使用字节豆包大模型api识别本地或网络图片中文字内容返回给我

识别的结果还是挺准确的,而且支持本地或者网络图片的内容识别,官方文档也有说明:www.volcengine.com/docs/82379/...,使用的视觉理解这个接口,

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

官方使用示例:

ini 复制代码
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)

本地或网络图像识别

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

示例代码:

python 复制代码
import base64
from openai import OpenAI

# 请确保您已将 API Key 存储在环境变量 ARK_API_KEY 中
# 初始化Ark客户端,从环境变量中读取您的API Key
client = OpenAI(
    # 此为默认路径,您可根据业务所在地域进行配置
    base_url="https://ark.cn-beijing.volces.com/api/v3",
    # 从环境变量中获取您的 API Key。此为默认方式,您可根据需要进行修改
    api_key='key',
)


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


# 需要传给大模型的图片
image_path = "down2.png"

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

response = client.chat.completions.create(
    # 指定您创建的方舟推理接入点 ID,此处已帮您修改为您的推理接入点 ID
    model="doubao-1-5-vision-pro-32k-250115",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "请返回这种图片中橙色的单词给我"},
                {
                    "type": "image_url",
                    "image_url": {
                        "url": f"data:image/png;base64,{base64_image}"
                    },
                },
            ],
        }
    ],
)

print(response.choices[0])
print("content: ", response.choices[0].message.content)

多图像输入

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

python 复制代码
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 编码,然后提交给大模型。下面是一个简单的示例代码。

python 复制代码
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])

图文混排

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

python 复制代码
import os
# 安装方舟SDK, 可参考文档安装 https://www.volcengine.com/docs/82379/1399008#%E6%96%B9%E8%88%9F-python-sdk
from volcenginesdkarkruntime import Ark

# 初始化Ark客户端,从环境变量中读取您的API Key
client = Ark(
    api_key=os.getenv('ARK_API_KEY'),
    )
# 创建一个对话请求
response = client.chat.completions.create(
    # 替换 <Model> 为模型的Model ID
    model="<Model>",
        messages=[
        {
            "role": "system",
            "content": [
                {"type": "text", "text": "下面人物是目标人物"},
                {
                    "type": "image_url",
                    "image_url": {
                        "url": "https://ark-project.tos-cn-beijing.volces.com/doc_image/target.png"
                    },
                },
                {"type": "text", "text": "请确认下面图片中是否含有目标人物"},
            ],
        },
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "图片1中是否含有目标人物"},
                {
                    "type": "image_url",
                    "image_url": {
                        "url": "https://ark-project.tos-cn-beijing.volces.com/doc_image/scene_01.png"
                    },
                },
                {"type": "text", "text": "图片2中是否含有目标人物"},
                {
                    "type": "image_url",
                    "image_url": {
                        "url": "https://ark-project.tos-cn-beijing.volces.com/doc_image/scene_02.png"
                    },
                },
                
            ],
        },
    ],
)

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

最佳实践

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

上传本地图片进行分析

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

代码流程

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

  1. 压缩图片。分辨率会影响 token 消耗数量,可以通过图片压缩,来节省网络、存储以及模型分析成本。
  2. 将压缩后的图片上传至对象存储 TOS,并为图片生成预签名 URL。
  3. 调用视觉理解大模型分析图片。
python 复制代码
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 接口调用模型服务。

完整的 Python 脚本

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))
相关推荐
木卯1 分钟前
5种创建型设计模式笔记(Python实现)
python·设计模式
张琪杭18 分钟前
pytorch tensor创建tensor
人工智能·pytorch·python
星星点点洲28 分钟前
【RAG】RAG 系统的基本搭建流程(ES关键词检索示例)
python·elasticsearch
带娃的IT创业者1 小时前
《Python实战进阶》No18: 使用 Apache Spark 进行分布式计算
python·spark·apache
Tomorrow'sThinker1 小时前
Python零基础学习第三天:函数与数据结构
开发语言·windows·python
元媛媛1 小时前
Python - 轻量级后端框架 Flask
开发语言·python·flask
疏狂难除1 小时前
基于Rye的Django项目通过Pyinstaller用Github工作流简单打包
后端·python·django
囚~徒~2 小时前
flask 接口文档自动化
python·flask·自动化
行码棋2 小时前
【Python】omegaconf 用法详解
开发语言·python
SomeB1oody2 小时前
【Python机器学习】1.6. 逻辑回归理论(基础):逻辑函数、逻辑回归的原理、分类任务基本框架、通过线性回归求解分类问题
人工智能·python·机器学习·分类·逻辑回归·线性回归