随着 GPT-Vision API 的出现,许多很酷的功能被解锁。小飞侠现在可以使用 GPT 来理解图像的内容,并提示它以小飞侠选择的任何方式描述图像。
Python 中的基本 GPT-Vision API 调用相对简单。首先,将图像转换为 base64 字符串,然后传递给 GPT-Vision。你还可以添加自定义提示。
from openai import OpenAI import base64 client = OpenAI() def convert_to_base64(image_path): with open(image_path, "rb") as image_file: encoded_string = base64.b64encode(image_file.read()).decode() return encoded_string def image_vision(prompt): response = client.chat.completions.create( model="gpt-4-vision-preview", messages=prompt, max_tokens=300, ) print(response.choices[0].message.content) base64image = convert_to_base64('k2.png') prompt = [ { "role": "user", "content": [ {"type": "text", "text": "Describe this image in detail."}, { "type": "image_url", "image_url": { "url": f"data:image/png;base64,{base64image}", }, }, ], } ], image_vision(prompt)
在这种情况下,人工智能会以令人震惊的准确细节做出回应:
这是一幅令人惊叹的雪山在湛蓝的天空下巍然耸立的图像。山峰陡峭,山脊明显,山面被冰雪覆盖,表明海拔高,气候寒冷。在山峰下方,大片的积雪覆盖着山坡,其间散布着岩石露头,积雪较薄或没有积雪。
在前景中,有一片平坦的广阔地带,似乎是谷底或高原,大部分被雪覆盖,有深色裸露的地面或岩石。这个区域将观众的视线引向山脚。
阳光照亮了场景,投射阴影并突出了山腰的轮廓,增强了质感并强调了崎岖的地形。晴朗的天空和无云表明环境平静祥和,尽管海拔高度可能意味着空气稀薄,温度低。
整体印象是自然美景、壮丽和高山环境的严酷。图像可以是喜马拉雅山脉、安第斯山脉或其他以高山和冰川地形而闻名的主要山脉中的一座山。
我想在这里解决的问题是,GPT-Vision 真的能理解视频吗?它绝对可以(但有一些限制)。我会告诉你怎么做!
提示 GPT-Vision 理解视频内容
在开始之前,请确保已安装所需的库:
pip install openai moviepy opencv-python
现在,让小飞侠进行导入:
import moviepy.editor as mp
import base64
from openai import OpenAI
import shutil
import cv2
import os
client = OpenAI()
上面的代码假定你已将 API 密钥保存为操作系统中的环境变量。如果你还没有这样做,你可以简单地将其更改为:
client = OpenAI(api_key='your_api_key_goes_here')
让小飞侠设置一个简单的 GPT-Vision 调用函数,小飞侠可以在其中插入一个提示对象:
def base64_vision(prompt):
response = client.chat.completions.create(
model="gpt-4-vision-preview",
messages=prompt,
max_tokens=300,
)
print(response.choices[0].message.content)
将视频分成单独的帧
在将视频数据传递到 GPT-Vision 之前,小飞侠需要将其拆分为帧。此拆分功能会压缩视频,以你可以在 main 函数中更改的间隔获取帧,并将帧保存在工作目录中的文件夹中。另一个函数会将它们转换为 base64 以传递给 GPT。
def extract_frames(video_path, interval=1): # Check if the 'FRAMES' directory exists, if not, create it if os.path.exists('FRAMES'): # Delete all files in the 'FRAMES' directory for filename in os.listdir('FRAMES'): file_path = os.path.join('FRAMES', filename) try: if os.path.isfile(file_path) or os.path.islink(file_path): os.unlink(file_path) elif os.path.isdir(file_path): shutil.rmtree(file_path) except Exception as e: print(f'Failed to delete {file_path}. Reason: {e}') # Open the video file video = cv2.VideoCapture(video_path) # Check if video opened successfully if not video.isOpened(): print("Error opening video file") return # Get the frame rate of the video fps = video.get(cv2.CAP_PROP_FPS) # Calculate the frame number to skip frame_skip = int(fps * interval) frame_count = 0 while True: # Read a frame success, frame = video.read() # If frame read successfully and it's the correct interval if success and frame_count % frame_skip == 0: # Save the frame frame_filename = f'FRAMES/frame_{frame_count}.jpg' cv2.imwrite(frame_filename, frame) print(f'Saved {frame_filename}') if not success: break frame_count += 1 # Release the video capture object video.release() cv2.destroyAllWindows() def convert_to_base64(image_path): with open(image_path, "rb") as image_file: encoded_string = base64.b64encode(image_file.read()).decode() return encoded_string
从视频中抓取和转录音频
现在,让小飞侠构建实际的音频提取功能。这是通过一个快速的 OpenAI Whisper 函数和另一个加载视频并将其提取到工作目录中的音频文件中的函数来完成的:def whisper(audio):
audio_file = open(audio, "rb")
transcript = client.audio.transcriptions.create(
model="whisper-1",
file=audio_file
)
return transcript.text
def extract_audio(video_path):
# Load the video file
video = mp.VideoFileClip(video_path)
# Extract audio
audio = video.audio
# Save the audio
if not audio:
transcript = "There is no audio for this video"
print(transcript)
return transcript
else:
audio.write_audiofile("video_audio.mp3")
transcript = whisper("video_audio.mp3")
print(f"Whisper video transcript: {transcript}")
return transcript
我要说的是,这不适用于识别音乐或人类语言以外的任何声音。希望有一天小飞侠能有办法理解任何类型的音频!
运行完整的视频装备
让小飞侠把所有这些放在一个完整的示例运行中。以下代码允许你更改一些关键项目:
-
你可以切换帧分配器保存帧的频率——即,如果你愿意每半秒保存一次,你可以输入“interval=.5” 一次输入太多帧是可能的;我建议一次传递少于 100 帧
-
你可以更改视频的路径 - 在这种情况下,它只是“video.mp4”
-
你可以改变你的 GPT 提示,使其具有不同的行为——通常,要求它简单地描述视频效果最好
-
你可以更改视频的 X 分辨率——这可以进行调整以为 AI 提供更多或更少的细节;我建议将其保留在 480
代码如下:
现在,小飞侠可以运行这个脚本,AI 会为小飞侠解释视频!
演示视频装备
对于这个例子,我想看看 AI 是否能理解是什么让这个视频变得有趣:
def video_GPT():
frames_directory = 'FRAMES'
base64frames = []
# Check if the directory exists
if not os.path.exists(frames_directory):
os.makedirs(frames_directory)
print(f"Created {frames_directory} Directory.")
else:
# Clear all files in the directory
for filename in os.listdir(frames_directory):
file_path = os.path.join(frames_directory, filename)
if os.path.isfile(file_path):
os.remove(file_path)
video_path = 'video.mp4' # Replace with your video file path
extract_frames(video_path, interval=.5) # How many seconds between frames
transcript = extract_audio(video_path) # Extract audio
for filename in sorted(os.listdir(frames_directory)):
file_path = os.path.join(frames_directory, filename)
if os.path.isfile(file_path):
encoded_image = convert_to_base64(file_path)
base64frames.append(encoded_image)
prompt = [
{
"role": "user",
"content": [
f"Explain what is happening in this sequence of frames, and do it concisely."
f"Here is the transcript of the video audio in case that helps you:{transcript}",
*map(lambda x: {"image": x, "resize": 480}, base64frames),
],
},
]
base64_vision(prompt)
if __name__ == '__main__':
video_GPT()
https://packaged-media.redd.it/y2kf79rf6m5b1/pb/m2-res_720p.mp4?m=DASHPlaylist.mpd&v=1&e=1701316800&s=6224b947b48644f07137c89fde54a9b2c5886c75#t=0
我将这个视频下载到我的工作目录中,剩下的工作由脚本完成。它刮掉了帧和音频,然后 GPT-Vision 发挥了它的魔力。以下是它不得不说的话:
一连串画面中的幽默感来自人与狗之间“赛跑”吃地上零食的意外结果。这个人正在小心翼翼地摆放零食,并指示狗等到数到三。然而,一旦它们开始,狗很快就会吃掉所有的零食,包括那些在人面前的零食,他大吃一惊,空手而归。由于该人徒劳地试图与狗的本能和快速进食竞争,以及他们意识到他们“没有机会”对抗狗的速度和注意力,喜剧效果更加突出。
这是一个完全准确的评估;它给我留下了深刻的印象。我用一个更微妙、更“复杂”的视频再次尝试(if only I have rizz like that : r/funnyvideos),其幽默更难理解:
序列中的幽默来自喜剧的时机和对期望的颠覆。起初,父亲似乎对男朋友对音乐的兴趣不屑一顾,质疑音乐的效用。当父亲问男朋友是否有任何“有用”的兴趣时,紧张局势升级,男朋友回答说他正在学习成为一名神经外科医生,这是一个备受尊敬和有用的职业。父亲的反应从怀疑转变为印象深刻,但当男朋友错误地称赞烩饭比他祖母的更好吃时,他的骄傲是短暂的,不知道父亲自己做的。在一系列紧张的时刻之后,意想不到的失礼传递了妙语,在男友的成就和他的社交失态之间形成了喜剧的对比。
显然,人工智能完全错过了这个视频的重点,即使有成绩单。显然,这是一个艰难的考验,但我有点失望……