You need to enable JavaScript to run this app.
导航
视觉理解
最近更新时间:2025.02.19 20:27:38首次发布时间:2024.11.07 21:54:45

部分大模型具备视觉理解能力,如当您传入图片时,大模型可以理解图片里的视觉信息,并结合这些信息完成如描述图片等图片相关任务。通过这篇教程,您可以学习到如何通过调用大模型 API 来识别传入图片里的信息。

应用场景

您可以在以下场景中使用模型的视觉理解能力。

场景

场景细分

描述

文字识别(OCR)

纯文本图像的文字抽取

抽取包含文本图片的文字内容,如:密集文本图片、文档截图等,并支持格式化输出文本内容。

日常图像的文字抽取

对日常手机拍摄的图片中,包含的文本文字抽取,如:菜单、路标、证件等。

表格图像的内容抽取

模型能够识别图表、表格类型图片的文字、数字等内容读取,并支持格式化输出文本内容。

图像问答

图片描述

描述图片中的内容,包括详细描述、简短描述以及为图像分类。

图像内容提问

对图片中包含的内容进行提问。

创作内容

文案创作

根据图像内容,创作文案、标题等,如:点评、小红书文案、朋友圈文案等。

教育

数学、自然等学科题目解答

分析题目、考点说明、解题思路、解题结果。

数学、自然等学科题目批改

对题目的回答批改。

生成代码

前端页面生成

根据前端页面设计图片信息,完成前端页面。

图表绘制

根据图片信息,完成图表实现。

支持模型

当前支持视觉理解的模型如下。

模型名称

模型版本

模型说明

上下文长度

Max Tokens
最大输出 token 长度

TPM
每分钟 token 处理数

RPM
每分钟处理请求数

免费额度
(token)

计费

Doubao-1.5-vision-pro-32k

250115

最新一代视觉理解模型,在单价不提升的同时,在多模态数据合成、动态分辨率、多模态对齐、混合训练上进行了全面的技术升级,进一步增强了模型在视觉推理、文字文档识别、细粒度信息理解、指令遵循方面的能力,并让模型的回复模式变得更加精简、友好。

32k

12k升级

5000k升级

30k升级

500k

视觉理解大模型

Doubao-vision-pro-32k

241028

强视觉理解能力,精准指令理解能力。

32k

4k

1200k

15k

500k

视觉理解大模型

Doubao-vision-lite-32k

241015

轻量级,单价低,速度快。

32k

4k

1200k

15k

500k

视觉理解大模型

前提条件
  • 获取 API Key 或者 Access Key,用于模型在线推理服务的调用鉴权。

  • 获取 Model ID 作为推理接入点 用于模型调用,请参考 获取 Model ID。(此方式目前仅支持 API Key 鉴权)

    说明

    如果您想更灵活的配置使用的大模型及其版本、限流、计费类型(前付费/后付费)、运行状态、调用监控等,推荐您使用 Endpoint ID 作为推理接入点 ,可以参考获取 Endpoint ID

快速开始

支持视觉理解的大模型当前支持在请求中传入图片链接,图片信息需要作为用户角色输入信息传给大模型,即"role": "user",下面是简单的视觉模型调用示例代码。

import os
# 通过 pip install volcengine-python-sdk[ark] 安装方舟SDK
from volcenginesdkarkruntime import Ark

# 替换 <Model> 为模型的Model ID
model="<Model>"

# 初始化Ark客户端,从环境变量中读取您的API Key
client = Ark(
    api_key=os.getenv('ARK_API_KEY'),
    )

# 创建一个对话请求
response = client.chat.completions.create(
    # 指定您部署了视觉理解大模型的推理接入点ID
    model = model,
    messages = [
        {
            # 指定消息的角色为用户
            "role": "user",  
            "content": [  
                # 文本消息,希望模型根据图片信息回答的问题
                {"type": "text", "text": "支持输入是图片的模型系列是哪个?"},  
                # 图片信息,希望模型理解的图片
                {"type": "image_url", "image_url": {"url":  "https://ark-project.tos-cn-beijing.volces.com/doc_image/ark_demo_img_1.png"}
                },
            ],
        }
    ],
)

print(response.choices[0].message.content)

模型回复预览:

提供的表格信息可知,支持输入是图片的模型系列是Doubao-1.5-vision 。该模型系列在输入部分的“图像”列标注为“√”,表明其支持图像输入,而Doubao-1.5-pro和Doubao-1.5-lite均不支持图像输入。

使用说明

说明

处理完图片后,图片会从方舟服务器删除。方舟不会保留您提交的图片以及文本信息等用户数据来训练模型。

图片传入方式

图片 URL 或图片 Base64 编码。用图片 URL 方式时,需确保图片 URL 可被访问。

说明

如果您希望获得更低的时延和成本,可以使用TOS(火山引擎对象存储)存储图片并生成访问链接。方舟与TOS网络打通,可使用内网通信,具备好处:

  • 高速访问图片速度,降低模型回复的时延。
  • 降低公网访问图片产生的高昂流量成本。

视觉理解接口对话(Chat)-视觉 API是无状态的,如果您需要模型多次理解同一张图片,则每次请求时都需要传入该图片信息。

图片像素说明

  • 新版模型(Doubao-1.5-vision-pro-32k 250115 ):支持尺寸更加灵活的图片,传入图片满足下面条件:
    • 图片宽高长度(单位 px) :大于 14。
    • 图片总像素(宽×高,单位 px) :小于 3600万。
  • 旧版模型:( Doubao-vision-pro-32k 241028Doubao-vision-lite-32k 241025 )传入图片需满足下面条件。
    • 图片宽或高长度(单位 px):[10, 6000]。
    • 图片宽高比(宽÷高):[1/100,100]。

说明

在模型处理图片信息前,方舟会做压缩图片等预处理。您也可根据业务需求,自行压缩或裁剪图片,可减少图片传入时报错,降低 token 消耗以及降低 TTFT(首个 token 响应时间)。推荐减少的单张图片像素(宽×高)至下面范围内。

  • 新版模型:detail:low模式下,104万(1024×1024) px;detail:high模式下,401万(2048×1960) px。
  • 旧版模型:detail:low模式下,80万(896×896) px;detail:high模式下,320万(1792×1792) px。

其中detail:lowdetail:high,指低/高精度理解图像,具体说明请参见理解图像的深度控制
压缩图片示例代码,请参见应用案例:上传本地图片并进行图片分析

图片数量说明

单次请求传入图片数量,受到上传数量及模型上下文长度两个规格的限制。

  • 上传数量限制:即一次请求传入图片(包含历史信息中的图片)信息不能超过 50 张。
  • 模型上下文长度限制:模型上下文长度见模型规格,当输入信息过长,譬如传入图片较多,触发了模型上下文长度限制,如上下文 32k 的模型,图片超过 25 张,信息会被截断后处理。

举例说明:

  • 当图片质量很高,使用模型上下文窗口限制为 32k,每个图片转为 1312 个 token ,单轮可传入的图片数量为 min(32000 ÷ 1312 = 24 , 50) = 24 张。
  • 当图片细节和分辨率小,使用模型上下文窗口限制为 32k,每个图片转为 256 个 token,单轮可以传入的数量为 min(32000 ÷ 256 , 50) = 50 张。

说明

模型对图片理解以及内容回复的质量,受到同时处理图片信息量的影响。单次请求过多的图片张数会导致模型回复质量下滑,以及超出模型上下文限制,为了更好的回复质量,建议您合理控制传入图片的数量。

图片文件容量

上传的图片文件大小不能超过 10 MB。

token 用量说明

模型理解图片,会将图片转化为 token ,再进行推理计算。token 用量,根据图片宽高像素计算可得。图片转化 token 的公式为:

min(图片宽 * 图片高÷784, 单图 token 限制)

新版模型(Doubao-1.5-vision-pro-32k 250115)支持对图像有更精细理解:

  • detail:high模式下,单图 token 限制升至 5120 token。
  • detail:low模式下,单图 token 限制 1312 token。

旧版模型( Doubao-vision-pro-32k 241028Doubao-vision-lite-32k 241025) :

  • detail:high模式、detail:low模式,单图 token 限制均为 1312 token。

图片尺寸为 1280 px × 720 px,即宽为 1280 px,高为 720 px,传入模型图片 token 限制为 1312,则理解这张图消耗的 token 为1280×720÷784=1176,因为小于 1312,消耗 token 数为 1176 。
图片尺寸为 1920 px × 1080 px,即宽为 1920 px,高为 1080 px,传入模型图片 token 限制为 1312,则理解这张图消耗的 token 为1920×1080÷784=2645,因为大于 1312,消耗 token 数为 1312 。这时会压缩 token,即图片的细节会丢失部分,譬如字体很小的图片,模型可能就无法准确识别文字内容。

图片格式说明

支持的图片格式如下表,需注意文件后缀和图片格式需要匹配,即图片文件扩展名(URL 传入)、编码中图片格式声明(Base64 编码传入)需要与图片实际信息一致。

图片格式

文件扩展名

内容格式 Content Type

JPEG

.jpg, .jpeg

image/jpeg

PNG

.apng, .png

image/png

GIF

.gif

image/gif

WEBP

.webp

image/webp

BMP

.bmp

image/bmp

TIFF

.tiff, .tif

image/tiff

ICO

.ico

image/x-icon

DIB

.dib

image/bmp

ICNS

.icns

image/icns

SGI

.sgi

image/sgi

JPEG2000

.j2c, .j2k, .jp2, .jpc, .jpf, .jpx

image/jp2

说明

TIFF、 SGI、ICNS、JPEG2000 几种格式图片,需要保证和元数据对齐如在对象存储中正确设置文件元数据,否则会解析失败。详细见 使用视觉理解模型时,报错InvalidParameter?

API 参数字段说明

以下字段视觉理解暂不支持。

  • 不支持 functioncall,无tools字段。
  • 不支持设置频率惩罚系数,无 frequency_penalty字段。
  • 不支持设置存在惩罚系数,presence_penalty字段。
  • 不支持为每个请求生成多个 chatcompletions,只能生成 1 个,无n字段。

使用示例

多图像输入

API 可以支持接受和处理多个图像输入,这些图像可以通过图片可访问 URL 或图片转为 Base64 编码后输入,模型将结合所有传入的图像中的信息来回答问题。

import os
# 通过 pip install volcengine-python-sdk[ark] 安装方舟SDK
from volcenginesdkarkruntime import Ark

# 从环境变量中获取API Key
client = Ark(
    api_key=os.getenv('ARK_API_KEY'),
    )

response = client.chat.completions.create(
    # 替换 <Model> 为模型的Model ID
    model="<Model>",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "支持输入是图片的模型系列是哪个?同时,豆包应用场景有哪些?"},
                {"type": "image_url","image_url": {"url":  "https://ark-project.tos-cn-beijing.volces.com/doc_image/ark_demo_img_1.png"}},
                {"type": "image_url","image_url": {"url":  "https://ark-project.tos-cn-beijing.volces.com/doc_image/ark_demo_img_2.png"}},
            ],
        }
    ],
)

print(response.choices[0])

Base64 编码输入

如果你要传入的图片在本地,你可以将这个图片转化为 Base64 编码,然后提交给大模型。下面是一个简单的示例代码。

注意

传入 Base64 编码格式时,传入的格式如下:
data:image/<图片格式>;base64,<Base64编码>

  • 图片格式:jpegpnggif等,支持的图片格式详细见图片格式说明
  • Base64 编码:图片的 Base64 编码。
import base64
import os
# 通过 pip install volcengine-python-sdk[ark] 安装方舟SDK
from volcenginesdkarkruntime import Ark

# 初始化一个Client对象,从环境变量中获取API Key
client = Ark(
    api_key=os.getenv('ARK_API_KEY'),
    )

# 定义方法将指定路径图片转为Base64编码
def encode_image(image_path):
  with open(image_path, "rb") as image_file:
    return base64.b64encode(image_file.read()).decode('utf-8')

# 需要传给大模型的图片
image_path = "path_to_your_image.jpg"

# 将图片转为Base64编码
base64_image = encode_image(image_path)

response = client.chat.completions.create(
  # 替换 <Model> 为模型的Model ID
  model="<Model>",
  messages=[
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "图片里讲了什么?",
        },
        {
          "type": "image_url",
          "image_url": {
          # 需要注意:传入Base64编码前需要增加前缀 data:image/{图片格式};base64,{Base64编码}:
          # PNG图片:"url":  f"data:image/png;base64,{base64_image}"
          # JEPG图片:"url":  f"data:image/jpeg;base64,{base64_image}"
          # WEBP图片:"url":  f"data:image/webp;base64,{base64_image}"
            "url":  f"data:image/<IMAGE_FORMAT>;base64,{base64_image}"
          },
        },
      ],
    }
  ],
)

print(response.choices[0])

理解图像的深度控制

你可以通过detail参数来控制模型理解图片的深度,或者说对图片细节的观察细致程度,以及返回速度,计费公式请参见token 用量说明

  • auto:默认情况下,模型将使用设置auto
  • 新版模型(Doubao-1.5-vision-pro-32k 250115):采用low模式。
  • 旧版模型(Doubao-vision-pro-32k 241028Doubao-vision-lite-32k 241025):根据图片分辨率,自行选择模式。
  • low:“低分辨率”模式,处理速度会提高,适合图片本身细节较少或者只需要模型理解图片大致信息或者对速度有要求的场景。
  • high:“高分辨率”模式,这代表模型会理解图片更多的细节,但是处理图片速度会降低,适合需要模型理解图像细节,图像细节丰富,需要关注图片细节的场景。
import os
# 可通过 pip install volcengine-python-sdk[ark] 安装方舟SDK 
from volcenginesdkarkruntime import Ark

# 初始化一个Client对象,从环境变量中获取API Key
client = Ark(
    api_key=os.getenv('ARK_API_KEY'),
    )

# 调用 Ark 客户端的 chat.completions.create 方法创建聊天补全请求
response = client.chat.completions.create(
    # 替换 <Model> 为模型的Model ID
    model="<Model>",
    messages=[
        {
            # 消息角色为用户
            "role": "user",
            "content": [
                # 文本类型的消息内容,询问图片里有什么
                {"type": "text", "text": "图片里有什么?"},
                {
                    "type": "image_url",
                    # 第一张图片链接及细节设置为 high
                    "image_url": {
                        # 您可以替换图片链接为您的实际图片链接
                        "url":  "https://ark-project.tos-cn-beijing.volces.com/doc_image/ark_demo_img_1.png",
                        "detail": "high"
                    }
                },
            ],
        }
    ],
)

print(response.choices[0])

使用BotChatCompletions接口

您可以使用BotChatCompletions间接调用配置视觉理解模型的bot,使应用具备理解图片、视频相关能力,处理如图片文字内容识别,根据图片绘制表格、开发前端代码等任务。

示例代码如下:

import os
from volcenginesdkarkruntime import Ark

client = Ark(
    api_key=os.environ.get("ARK_API_KEY"),
)

print("----- standard request -----")
completion = client.bot_chat.completions.create(
    model="<YOUR_BOT_ID>",
    messages = [
        {
            "role": "user",  # 指定消息的角色为用户
            "content": [  # 消息内容列表
                {"type": "text", "text": "支持输入是图片的模型系列是哪个?"},  # 文本消息
                {
                    "type": "image_url",  # 图片消息
                    # 图片的URL,需要大模型进行理解的图片链接
                    "image_url": {"url":  "https://ark-project.tos-cn-beijing.volces.com/doc_image/ark_demo_img_1.png"}
                },
            ],
        }
    ],
)
print(completion.choices[0].message.content)
print(completion.references)

图文混排

支持灵活的传入提示词和图片信息的方式,您可以任意调整传图图片和文本的顺序,模型会根据顺序返回处理信息的结果,示例如下。

import os
# 通过 pip install volcengine-python-sdk[ark] 安装方舟SDK
from volcenginesdkarkruntime import Ark

# 替换 <Model> 为模型的Model ID
model="<Model>"

# 初始化Ark客户端,从环境变量中读取您的API Key
client = Ark(
    api_key=os.getenv('ARK_API_KEY'),
    )
# 创建一个对话请求
response = client.chat.completions.create(
    model = model,
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "根据图片信息回答问题"},
                {"type": "text", "text": "支持输入是图片的模型系列是哪个?"},
                {"type": "image_url","image_url": {"url":  "https://ark-project.tos-cn-beijing.volces.com/doc_image/ark_demo_img_1.png"}},
                {"type": "text", "text": "豆包应用场景有哪些?"},
                {"type": "image_url","image_url": {"url":  "https://ark-project.tos-cn-beijing.volces.com/doc_image/ark_demo_img_2.png"}},
            ],
        }
    ],
)

print(response.choices[0].message.content)

最佳实践

下面介绍具体场景的应用案例,包括数据处理以及模型调用等端到端的案例。

上传本地图片进行分析

处理图片通常会与网络存储结合起来使用,下面介绍如何结合对象存储 TOS,来实现完整的图片处理流程。

代码流程

完整流程会分成以下步骤:

  1. 压缩图片。分辨率会影响 token 消耗数量,可以通过图片压缩,来节省网络、存储以及模型分析成本。
  2. 将压缩后的图片上传至对象存储 TOS,并为图片生成预签名 URL。
  3. 调用视觉理解大模型分析图片。

示例代码

  1. 安装依赖。

    pip install -r requirements.txt
    

    requirements.txt文本内容如下:

    Pillow
    volcengine-python-sdk[ark]
    tos
    
  2. 示例代码。

    import os
    import tos
    from PIL import Image
    from tos import HttpMethodType
    from volcenginesdkarkruntime import Ark
    
    # 从环境变量获取 AK/SK/APIKEY信息
    ak = os.getenv('VOLC_ACCESSKEY')
    sk = os.getenv('VOLC_SECRETKEY')
    api_key = os.getenv('ARK_API_KEY')
    # 配置视觉理解模型的 Model ID
    model_id = '<Model>'
    
    # 压缩前图片
    original_file = "original_image.jpeg"
    # 压缩后图片存放路径
    compressed_file = "comressed_image.jpeg"
    # 压缩的目标图片大小,300KB
    target_size = 300 * 1024
    
    # endpoint 和 region 填写Bucket 所在区域对应的Endpoint。
    # 以华北2(北京)为例,region 填写 cn-beijing。
    # 公网域名endpoint 填写 tos-cn-beijing.volces.com
    endpoint, region = "tos-cn-beijing.volces.com", "cn-beijing"
    # 对象桶名称
    bucket_name = "demo-bucket-test"
    # 对象名称,例如 images 下的 compressed_image.jpeg 文件,则填写为 images/compressed_image.jpeg
    object_key = "images/compressed_image.jpeg"
    
    def compress_image(input_path, output_path):
        img = Image.open(input_path)
        current_size = os.path.getsize(input_path)
    
        # 粗略的估计压缩质量,也可以从常量开始,逐步减小压缩质量,直到文件大小小于目标大小
        image_quality = int(float(target_size / current_size) * 100)
        img.save(output_path, optimize=True, quality=int(float(target_size / current_size) * 100))
    
        # 如果压缩后文件大小仍然大于目标大小,则继续压缩
        # 压缩质量递减,直到文件大小小于目标大小
        while os.path.getsize(output_path) > target_size:
            img = Image.open(output_path)
            image_quality -= 10
            if image_quality <= 0:
                break
            img.save(output_path, optimize=True, quality=image_quality)
        return image_quality
    
    def upload_tos(filename, tos_endpoint, tos_region, tos_bucket_name, tos_object_key):
        # 创建 TosClientV2 对象,对桶和对象的操作都通过 TosClientV2 实现
        tos_client, inner_tos_client = tos.TosClientV2(ak, sk, tos_endpoint, tos_region), tos.TosClientV2(ak, sk,
                                                                                                          tos_endpoint,
                                                                                                          tos_region)
        try:
            # 将本地文件上传到目标桶中, filename为本地压缩后图片的完整路径
            tos_client.put_object_from_file(tos_bucket_name, tos_object_key, filename)
            # 获取上传后预签名的 url
            return inner_tos_client.pre_signed_url(HttpMethodType.Http_Method_Get, tos_bucket_name, tos_object_key)
        except Exception as e:
            if isinstance(e, tos.exceptions.TosClientError):
                # 操作失败,捕获客户端异常,一般情况为非法请求参数或网络异常
                print('fail with client error, message:{}, cause: {}'.format(e.message, e.cause))
            elif isinstance(e, tos.exceptions.TosServerError):
                # 操作失败,捕获服务端异常,可从返回信息中获取详细错误信息
                print('fail with server error, code: {}'.format(e.code))
                # request id 可定位具体问题,强烈建议日志中保存
                print('error with request id: {}'.format(e.request_id))
                print('error with message: {}'.format(e.message))
                print('error with http code: {}'.format(e.status_code))
            else:
                print('fail with unknown error: {}'.format(e))
            raise e
    
    
    if __name__ == "__main__":
        print("----- 压缩图片 -----")
        quality = compress_image(original_file, compressed_file)
        print("Compressed Image Quality: {}".format(quality))
    
        print("----- 上传至TOS -----")
        pre_signed_url_output = upload_tos(compressed_file, endpoint, region, bucket_name, object_key)
        print("Pre-signed TOS URL: {}".format(pre_signed_url_output.signed_url))
    
        print("----- 传入图片调用视觉理解模型 -----")
        client = Ark(api_key=api_key)
    
        # 图片输入:
        response = client.chat.completions.create(
            # 配置推理接入点
            model=model_id,
            messages=[
                {
                    "role": "user",
                    "content": [
                        {"type": "text", "text": "Which is the most secure payment app according to Americans?"},
                        {
                            "type": "image_url",
                            "image_url": {
                                "url": pre_signed_url_output.signed_url
                            }
                        },
                    ],
                }
            ],
        )
    
        print(response.choices[0])
    

对视频内容进行内容理解

当您需要对视频内容进行理解时,可以对视频进行抽帧,生成若干图片后,再将图片结合文本作为提示词内容,请求视频理解的模型服务。下面是使用 Python 脚本实现的方案,供您参考。

代码流程

  1. 视频文件读取:从本地路径读取视频文件。
  2. 对视频进行抽帧,可以支持 2 种策略:
    1. CONSTANT_INTERVAL:固定时间间隔抽帧,如 1s 一帧,时间间隔可以调整。
    2. EVEN_INTERVAL:固定总帧数均匀抽帧,如不管视频时长,总共抽 20 帧,抽取的总帧数可以调整。
  3. 压缩帧图像:为了降低网络传输时延,以及减少 token 用量,将图像等比例压缩至 640*480 以内。
  4. 请求视觉理解模型:组装请求体,包含提示词(Prompt)及所有帧图像的 Base64,并使用方舟ChatCompletion 接口调用模型服务。

示例代码

1.环境配置

在运行脚本前,您需要配置好 API Key、获取Model ID,具体方法请参见。

创建推理接入点时,请选择支持视觉理解的大模型,具体可参见支持的模型

运行脚本前,请先在终端中安装依赖包。

pip install opencv-contrib-python
pip install "volcengine-python-sdk[ark]"

2.实现脚本以及配置

配置您的变量,包括:

  • <VIDEO_FILE_PATH>替换您的视频文件的路径。
  • <Model>替换为模型的Model ID。
  • prompt = "描述视频内容"配置您的文本提示,这里仅做示例使用。
  • 选择您的抽帧策略。
...
# 1. CONSTANT_INTERVAL:固定时间间隔抽帧,可以调整时间间隔
# 2. EVEN_INTERVAL:固定总帧数均匀抽帧,可以调整总帧数
        extraction_strategy=Strategy.CONSTANT_INTERVAL,
        # extraction_strategy=Strategy.EVEN_INTERVAL,
...

完整的 Python 脚本

from typing import Optional
# 导入OpenCV库,用于视频处理
import cv2
# 导入时间库,用于计时
import time
# 导入文件操作库,用于删除目录
import shutil
# 导入枚举库,用于定义抽帧策略
from enum import Enum

# 导入Base64编码库,用于将图片转换为Base64编码
import base64
# 导入操作系统库,用于文件路径操作
import os

# 通过 pip install volcengine-python-sdk[ark] 安装方舟SDK
from volcenginesdkarkruntime._exceptions import ArkAPIError
# 导入方舟SDK,用于调用方舟API
from volcenginesdkarkruntime import Ark

# 定义抽帧策略枚举类
class Strategy(Enum):
    # 固定间隔抽帧策略,例如每1秒抽一帧
    CONSTANT_INTERVAL = "constant_interval"
    # 均匀间隔抽帧策略,根据设定的最大帧数均匀从视频全长度抽取
    EVEN_INTERVAL = "even_interval"

def preprocess_video(
        video_file_path: str,
        output_dir: str,
        extraction_strategy: Optional[Strategy] = Strategy.EVEN_INTERVAL,
        interval_in_seconds: Optional[float] = 1,
        max_frames: Optional[int] = 10,
        keyframe_naming_template: str = "frame_{:04d}.jpg",
) -> list[str]:
    """将视频按照指定策略抽帧
    参数:
        video_file_path (str): 视频文件路径
        output_dir (str): 输出目录
        extraction_strategy (Optional[Strategy], optional): 抽帧策略。
             固定间隔 比如 1s 抽一帧 或
             均匀间隔 根据设定的最大帧数 均匀从视频全长度均匀抽取
             默认固定间隔 1s 抽一帧
        interval_in_seconds (Optional[float], optional): 固定间隔抽帧的间隔时间. 默认 1s 抽一帧
        max_frames (Optional[int], optional): 最大抽帧帧数. 默认 10 帧
        keyframe_naming_template (_type_, optional): 抽帧图片命名模板
    返回:
        list[str]: 抽帧图片路径列表
    """
    # 检查输出目录是否存在,如果不存在则创建
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    # 使用OpenCV打开视频文件
    cap = cv2.VideoCapture(video_file_path)
    # 获取视频的帧率
    fps = cap.get(cv2.CAP_PROP_FPS)
    # 获取视频的总帧数
    length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

    # 根据策略选择抽帧间隔
    if extraction_strategy == Strategy.CONSTANT_INTERVAL:
        # 计算固定间隔抽帧的帧间隔
        frame_interval = int(fps * interval_in_seconds)
    elif extraction_strategy == Strategy.EVEN_INTERVAL:
        # 计算均匀间隔抽帧的帧间隔
        frame_interval = int(length / max_frames)
    else:
        # 如果策略无效,抛出异常
        raise ValueError("Invalid extraction strategy")
    # 初始化帧计数器
    frame_count = 0
    # 初始化关键帧列表
    keyframes = []
    # 循环读取视频帧
    while True:
        # 读取一帧
        ret, frame = cap.read()
        # 如果读取失败,跳出循环
        if not ret:
            break
        # 如果当前帧是关键帧
        if frame_count % frame_interval == 0:
            # 生成关键帧的文件名
            image_path = os.path.join(
                output_dir, keyframe_naming_template.format(len(keyframes))
            )
            # 将关键帧保存为图片
            cv2.imwrite(
                image_path,
                frame,
            )
            # 将关键帧路径添加到列表中
            keyframes.append(image_path)
        # 增加帧计数器
        frame_count += 1
        # 如果关键帧数量达到最大值,跳出循环
        if len(keyframes) >= max_frames:
            break

    print("【视频抽帧完成】")
    print("【抽取帧数】", len(keyframes))
    # 返回关键帧路径列表
    return keyframes

def resize(image):
    """
    调整图片大小以适应指定的尺寸。
    参数:
        image (numpy.ndarray): 输入的图片,格式为numpy数组。
    返回:
        numpy.ndarray: 调整大小后的图片。
    """
    # 获取图片的原始高度和宽度
    height, width = image.shape[:2]
    # 根据图片的宽高比确定目标尺寸
    if height < width:
        target_height, target_width = 480, 640
    else:
        target_height, target_width = 640, 480
    # 如果图片尺寸已经小于或等于目标尺寸,则直接返回原图片
    if height <= target_height and width <= target_width:
        return image
    # 计算新的高度和宽度,保持图片的宽高比
    if height / target_height < width / target_width:
        new_width = target_width
        new_height = int(height * (new_width / width))
    else:
        new_height = target_height
        new_width = int(width * (new_height / height))
    # 调整图片大小
    return cv2.resize(image, (new_width, new_height))

# 定义方法将指定路径图片resize到合适大小并转为Base64编码
def encode_image(image_path: str) -> str:
    """
    将指定路径的图片进行编码
    参数:
        image_path (str): 图片文件的路径
    返回:
        str: 编码后的图片字符串
    """
    # 读取图片
    image = cv2.imread(image_path)
    # 调整图片大小
    image_resized = resize(image)
    # 将图片编码为JPEG格式
    _, encoded_image = cv2.imencode(".jpg", image_resized)
    # 将编码后的图片转换为Base64字符串
    return base64.b64encode(encoded_image).decode("utf-8")

def construct_messages(image_paths: list[str], prompt: str) -> list[dict]:
    """
    构造包含文本和图像的消息列表。
    参数:
        image_paths (list[str]): 图像文件路径列表。
        prompt (str): 文本提示。
    返回:
        list[dict]: 包含文本和图像的消息列表。
    """
    print("【组装请求参数】")
    # 初始化消息内容列表,包含一个文本消息
    content = [
        {
            "type": "text",
            "text": prompt,
        },
    ]
    # 遍历图像路径列表
    for image_path in image_paths:
        # 为每个图像路径构造一个图像URL消息
        content.append(
            {
                "type": "image_url",
                "image_url": {
                    # 使用Base64编码将图像转换为数据URL
                    "url": f"data:image/jpeg;base64,{encode_image(image_path)}",
                    # 指定图像细节级别为低
                    "detail":"low"
                },
            }
        )
    # 返回包含文本和图像的消息列表
    return [
        {
            "role": "user",
            "content": content,
        }
    ]

if __name__ == "__main__":
    # 替换为您的视频文件的路径
    video_path = "<VIDEO_FILE_PATH>"
    # 环境变量中读取的API Key,请提前配置好您的API Key
    ark_api_key = os.environ.get("ARK_API_KEY")
    # 替换 <Model> 为模型的Model ID
    model_id = "<Model>"
    # 替换为您的文本提示,这里仅做示例使用
    prompt = "描述视频内容"
    since = time.time()
    # 删除历史帧
    if os.path.exists("videoFrames"):
        shutil.rmtree("videoFrames")
        
    selected_images = preprocess_video(
        video_file_path=video_path,
        output_dir="videoFrames",
        
        # 两种抽帧策略
        # 1. CONSTANT_INTERVAL:固定时间间隔抽帧,可以调整时间间隔
        # 2. EVEN_INTERVAL:固定总帧数均匀抽帧,可以调整总帧数
        extraction_strategy=Strategy.CONSTANT_INTERVAL,
        # extraction_strategy=Strategy.EVEN_INTERVAL,
        
        # 固定抽帧策略时对应的抽帧间隔
        interval_in_seconds=1,

        # 视频抽取的最大帧数
        max_frames=20
    )

    print("【帧图像压缩完成】")
    preprocess_time_cost = time.time() - since

    client = Ark(api_key=ark_api_key)
    since = time.time()
    try:
        response = client.chat.completions.create(
            model=model_id,
            messages=construct_messages(
                selected_images,
                prompt,
            )
        )

        print("【视频理解结果】\n", response.choices[0].message.content)
        print("\n【总tokens消耗】", response.usage.total_tokens)
        print("- 输入tokens:", response.usage.prompt_tokens)
        print("- 输出tokens:", response.usage.completion_tokens)

    except ArkAPIError as e:
        print(e)

    api_time_cost = time.time() - since
    print("\n【全链路整体耗时】", round(preprocess_time_cost + api_time_cost, 2))
    print("- 视频预处理耗时:", round(preprocess_time_cost, 2))
    print("- 结果生成耗时:", round(api_time_cost, 2))

相关文档
  • 对话(Chat)-视觉 API:视觉理解 API 参数说明,调用模型的视觉理解能力,模型调用中出现错误,可参考视觉理解 API 参数说明。

常见问题

使用视觉理解模型时,报错InvalidParameter?