随着人工智能视觉技术的飞速发展,目标检测已成为众多应用的核心,无论是工业自动化、安防监控,还是日常生活的图像分析。大型视觉推理模型,如 QVQ,凭借其强大的理解能力,为实现灵活多变的目标检测任务提供了新的可能。

本文将介绍一个简单的 Python 脚本,演示如何利用 QVQ 模型(通过兼容 OpenAI API 的接口,例如阿里云的灵骏智验 - DashScope)来实现对图像中特定目标的自动检测与分类,并将结果结构化输出。与传统的固定类别目标检测模型不同,借助 QVQ 模型的强大泛化能力,我们可以通过调整提示文本(Prompt),实现对各种“日常业务相关”对象的检测,而无需重新训练模型。

脚本功能概览

这个 Python 脚本主要完成以下任务:

  1. 扫描指定文件夹下的图片文件。
  2. 将每张图片编码为 Base64 字符串。
  3. 构建一个包含图片和描述检测任务的文本提示的消息体。
  4. 调用基于 QVQ 模型构建的 AI 服务 API。
  5. 处理模型的流式响应,分离模型的思考过程和最终的检测结果(JSON 格式)。
  6. 将思考过程和检测结果保存到对应的文本文件中。

核心代码与讲解

下面是实现上述功能的 Python 代码。请注意,与原始代码不同,我们在处理 API 凭证时采用了更安全的方式,避免了将密钥硬编码在脚本中。

import glob
import os
import base64
from openai import OpenAI # 需要安装 openai 库: pip install openai
import json # 引入 json 库用于可能的后续处理或验证

# --- 安全配置提醒 ---
# 为了保护您的 API 密钥,请勿将其硬编码在代码中。
# 推荐使用环境变量来存储敏感信息。
# 在运行脚本前,请设置以下环境变量:
# export QVQ_API_KEY='您的API密钥'
# export QVQ_BASE_URL='兼容OpenAI API的端点URL (如: https://dashscope.aliyuncs.com/compatible-mode/v1)'
#
# Windows 用户可以使用 set 命令 (cmd) 或 $env: (PowerShell)。

API_KEY = os.getenv("QVQ_API_KEY")
BASE_URL = os.getenv("QVQ_BASE_URL")

if not API_KEY or not BASE_URL:
    print("错误:API 密钥 (QVQ_API_KEY) 和基础 URL (QVQ_BASE_URL) 必须设置为环境变量。")
    print("请在运行脚本前设置相应的环境变量。")
    exit(1)

# --- 辅助函数:图片编码 ---
def encode_image(image_path):
    """将图片文件编码为 Base64 字符串"""
    try:
        with open(image_path, "rb") as image_file:
            return base64.b64encode(image_file.read()).decode("utf-8")
    except Exception as e:
        print(f"错误:无法编码图片文件 {image_path}: {e}")
        return None

# --- 主要处理逻辑 ---
# 使用 glob 查找指定目录下符合条件的图片文件 (例如: best_output_folder 下任意子目录的 jpg 文件)
output_key_frames = glob.glob("best_output_folder/*/*.jpg")

# 初始化 OpenAI 客户端
# 注意:即使对接的是兼容 OpenAI API 的第三方服务,通常也可以使用 openai 库,
# 但需要通过 base_url 参数指定服务地址。
try:
    client = OpenAI(
        api_key=API_KEY,
        base_url=BASE_URL
    )
except Exception as e:
    print(f"错误:初始化 OpenAI 客户端失败:{e}")
    print("请检查您的 BASE_URL 和 API_KEY 是否正确,并确保 openai 库已安装。")
    exit(1)

print(f"找到 {len(output_key_frames)} 张图片待处理。")
print("脚本将调用 QVQ 模型进行目标检测,并通过兼容 API 进行通信。")

# 遍历找到的图片文件
for output_key_frame in output_key_frames:
    print(f"\n正在处理图片:{output_key_frame}")
    base64_image = encode_image(output_key_frame)

    if base64_image is None:
        print(f"跳过图片 {output_key_frame} 由于编码失败。")
        continue # 如果图片编码失败,跳过当前文件

    reasoning_content = ""
    answer_content = ""
    is_answering = False # 标志,用于判断何时开始接收最终的 JSON 回复内容

    # --- 定义检测任务的 Prompt ---
    # 这是核心部分,通过修改这里的文本来指定要检测的目标类型。
    # 我们将原始的“飞机内外部标识”替换为更通用的描述。
    # IMPORTANT: 请根据你的实际“日常业务”需求,将“其他日常业务相关标识或对象”
    # 替换为更具体的描述,例如:“图像中所有的交通标志”、“商品包装上的标签”等。
    detection_prompt = """你需要完成目标检测任务,检测图像中所有的其他日常业务相关标识或对象,并按照以下分类:
0: 清晰 (Clear)
1: 模糊 (Blurred)(由于拍摄原因导致模糊)
2: 褪色(由于标识本身老化导致模糊)
3: 缺失 (Missing)(标识不完整)
4: 遮盖 (Covered)

要求返回 **严格的 JSON 格式**,结构如下:
[
    {"bbox_2d": [中心x, 中心y, 宽w, 高h], "label": "类别"},
    ...
]

注意事项:
- bbox_2d四个数值是归一化到0-1之间的小数。
- label字段的值需要带上编号和名称,如"0: 清晰 (Clear)"。
- 只返回JSON,不要添加任何额外文字解释或其他内容。

示例参考:
[
    {"bbox_2d": [0.037932, 0.489878, 0.075865, 0.024334], "label": "0: 清晰 (Clear)"},
    {"bbox_2d": [0.485701, 0.402788, 0.220856, 0.099898], "label": "2: 褪色"},
    {"bbox_2d": [0.624589, 0.654133, 0.209472, 0.096696], "label": "2: 褪色"},
    {"bbox_2d": [0.814139, 0.649010, 0.137750, 0.055712], "label": "2: 褪色"}
]"""


    # --- 调用模型 API ---
    try:
        completion = client.chat.completions.create(
            model="qvq-max", # 指定使用的 QVQ 模型版本
            messages=[
                {
                    "role": "user",
                    "content": [
                        {
                            "type": "image_url",
                            "image_url": {"url": f"data:image/png;base64,{base64_image}"}, # 使用 data URL 格式发送 Base64 图片
                        },
                        {"type": "text", "text": detection_prompt} # 发送包含检测任务的文本提示
                    ],
                }
            ],
            stream=True, # 启用流式处理,可以实时接收模型响应
        )

        print("\n" + "=" * 20 + "模型思考过程 (Reasoning)" + "=" * 20 + "\n")

        # --- 处理流式响应 ---
        for chunk in completion:
            if not chunk.choices:
                # 处理 chunk 中可能包含的 usage 信息或错误
                if hasattr(chunk, 'usage') and chunk.usage:
                    print("\nUsage Info:", chunk.usage)
                else:
                    print("\n接收到空或异常 chunk:", chunk)
                continue # 跳过当前 chunk

            delta = chunk.choices[0].delta
            # 根据 delta 对象中的属性来判断是思考过程还是实际内容
            # 注意:不同的 API 实现可能对 'reasoning_content' 或其他属性的支持有所不同
            if hasattr(delta, 'reasoning_content') and delta.reasoning_content is not None:
                # 打印并累积思考过程
                print(delta.reasoning_content, end='', flush=True)
                reasoning_content += delta.reasoning_content
            elif delta.content is not None: # 这是模型的实际回复内容(期望是 JSON)
                 if delta.content != "" and is_answering is False:
                    # 第一次接收到实际内容时打印分隔符
                    print("\n" + "=" * 20 + "检测结果 (Answer JSON)" + "=" * 20 + "\n")
                    is_answering = True
                 # 打印并累积最终回复内容
                 print(delta.content, end='', flush=True)
                 answer_content += delta.content

        # --- 保存输出结果 ---
        # 根据输入图片路径生成输出文件路径
        base_name = os.path.splitext(os.path.basename(output_key_frame))[0]
        second_last_dir = os.path.basename(os.path.dirname(output_key_frame))
        save_dir = os.path.join("best_output_folder_texts", second_last_dir)
        os.makedirs(save_dir, exist_ok=True) # 创建保存目录,如果不存在则创建

        # 保存思考过程
        reasoning_file_path = os.path.join(save_dir, f"{base_name}_reasoning.txt")
        with open(reasoning_file_path, "w", encoding="utf-8") as f:
            f.write(reasoning_content)

        # 保存检测结果 (JSON)
        answer_file_path = os.path.join(save_dir, f"{base_name}_answer.txt")
        with open(answer_file_path, "w", encoding="utf-8") as f:
             # 可选:尝试验证 JSON 格式是否正确
             try:
                 json.loads(answer_content)
                 with open(answer_file_path, "w", encoding="utf-8") as f:
                     f.write(answer_content)
             except json.JSONDecodeError:
                 print(f"\n警告:图片 {output_key_frame} 的回复内容不是有效的 JSON 格式。")
                 # 即使不是有效 JSON 也保存,便于排查问题
                 with open(answer_file_path, "w", encoding="utf-8") as f:
                     f.write("ERROR: Invalid JSON response\n" + answer_content)


        print(f"\n保存完成:\n  - 思考过程: {reasoning_file_path}\n  - 检测结果: {answer_file_path}")

    except Exception as e:
        print(f"\n处理图片 {output_key_frame} 时发生错误:{e}")
        # 可以在这里选择记录错误信息到文件或进行其他处理

print("\n所有图片处理完成。")

如何使用

  1. 安装依赖:
    pip install openai
    
  2. 准备图片: 将待检测的图片文件(例如 .jpg 格式)放入一个按照 best_output_folder/*/*.jpg 结构组织的文件夹中。
  3. 设置环境变量: 在运行脚本的终端中设置 QVQ_API_KEYQVQ_BASE_URL 环境变量。这是确保 API 密钥安全的关键步骤。请替换示例中的占位符为您的实际密钥和端点 URL。
    • Linux/macOS Bash:
      export QVQ_API_KEY='sk-your-key-here'
      export QVQ_BASE_URL='https://dashscope.aliyuncs.com/compatible-mode/v1' # 替换为您的实际兼容 API 端点
      
    • Windows Command Prompt (cmd):
      set QVQ_API_KEY=sk-your-key-here
      set QVQ_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1 # 替换为您的实际兼容 API 端点
      
    • Windows PowerShell:
      $env:QVQ_API_KEY='sk-your-key-here'
      $env:QVQ_BASE_URL='https://dashscope.aliyuncs.com/compatible-mode/v1' # 替换为您的实际兼容 API 端点
      
  4. 修改 Prompt: 务必修改脚本中 detection_prompt 变量的值,"其他日常业务相关标识或对象" 替换为你实际想要检测的具体目标描述。Prompt 的清晰度和准确性直接影响模型的检测效果。
  5. 运行脚本:
    python your_script_name.py
    
    (请将 your_script_name.py 替换为你保存脚本的文件名)

通过这个脚本,我们展示了如何利用 QVQ 这样强大的多模态模型,通过简单的 API 调用和灵活的文本提示,实现定制化的图像目标检测任务。这种方法避免了为每个新类别的对象单独训练模型的繁琐过程,极大地提高了开发效率和模型的应用范围。通过安全地管理 API 凭证并根据需求调整提示文本,你可以将这个框架轻松应用于各种“日常业务”中的图像分析场景。

Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐