构建IPC设备的智能语义检索系统

更新时间:2025-04-29 09:35:43

使用 OSS 的数据索引和视频截帧功能,可以为网络摄像机(IPC)设备采集的视频构建一个智能语义检索系统,对采集的视频进行截帧并实现语义检索,适用于智能安防等场景。

方案概览

image

搭建智能语义检索系统,只需两步:

  1. 配置视频Bucket:创建用于存储原始 IPC 设备采集的视频文件的 Bucket,并上传待处理的视频文件,基于预设的时间间隔提取视频关键帧,为后续检索提供有力支持。

  2. 配置截帧Bucket:创建用于存储从原始视频中截取关键帧图像的Bucket,并开启向量检索功能,以支持基于自然语言描述的智能检索。

方案优势

  • 智能媒体处理:结合OSS与智能媒体管理(IMM)实现大规模视频数据的处理与分析,对上传至 OSS 的大规模视频数据进行自动化处理,提取关键帧图像、生成元数据,并为后续语义化检索提供支持。

  • 语义化检索:支持基于自然语言描述和多条件组合的精准检索,能够快速定位目标画面,满足复杂场景下的检索需求。实现更精准的事件匹配,减少人工筛选成本。

  • 多模态支持:提供视频、图像、文本等多模态数据统一管理与跨模态检索能力。依托阿里云 OSS 和 IMM,开箱即用,显著降低技术门槛与运维成本。

  • 横向扩展:OSS 的分布式存储架构支持动态扩容,轻松应对海量数据增长;计算资源可根据负载动态扩展,确保系统性能稳定。

1. 配置视频Bucket

1.1 创建Bucket上传设备采集视频

  1. 登录OSS管理控制台

  2. 进入Bucket列表页面,并点击创建Bucket

  3. 创建Bucket页面,填写Bucket名称(建议使用业务相关的名称,如ipc-videos-oss-metaquery-demo),其余参数可保持默认配置。

  4. 单击完成创建,在创建成功的页面,点击进入Bucket

  5. 文件列表页面,点击上传文件 > 扫描文件选择待上传的视频文件(如视频A.mp4),其余参数保留默认配置,点击上传文件

1.2 绑定智能媒体管理(IMM)项目

视频截帧是IMM提供的数据处理能力,使用此功能前需要先将您刚创建的视频BucketIMM项目进行绑定。​

  1. 在左侧导航栏,选择数据处理 > 媒体处理,在弹出的绑定页面,依次开通授权相关服务后,点击Bucket绑定IMM project按钮。

  2. 绑定智能媒体管理项目对话框中,选择创建新的Project,并输入项目名称(建议使用业务相关名称,如video-snapshot),点击确认进行绑定。系统会自动在存储空间所在的地域创建对应的IMM项目,并将其与当前存储空间绑定。

1.3 创建视频截帧样式

创建截帧样式,对输入视频按照固定时间间隔进行截帧处理直至视频结束,并将输出的关键帧图片以原始尺寸保存为 JPG 格式。

  1. 仍在媒体处理页面,选择视频处理 > 视频截帧,单击创建样式

  2. 创建样式面板中,依此完成如下配置:

    1. 样式名称: 建议使用与业务相关的名称(例如:ipc-snap)。

    2. 输出格式: 选择 jpg 格式。

    3. 截帧模式: 选择固定间隔截帧,并填写间隔时间(例如:1000 毫秒,即每秒截取一帧)。

    4. 其他参数保持默认。

  3. 单击确定完成样式配置。

image

1.4 执行视频截帧

​基于上述创建的截帧样式,对上传至视频Bucket的视频文件进行截帧处理,生成关键帧图像。以下是利用 OSS Python SDK 执行视频截帧的示例代码:

环境准备

在使用 Python SDK 进行视频截帧操作前,请确保已配置好开发环境和访问凭证,详细的配置流程请参考快速入门

示例代码

# -*- coding: utf-8 -*-
import base64
import argparse
import alibabacloud_oss_v2 as oss


def video_processor():
    # OSS配置
    args = argparse.Namespace(
        region='cn-beijing',
        target_bucket='ipc-frames-oss-metaquery-demo',    
        bucket='ipc-videos-oss-metaquery-demo',     
        endpoint='https://oss-cn-beijing.aliyuncs.com'
    )
    
    # 配置OSS客户端
    credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    cfg = oss.config.load_default()
    cfg.credentials_provider = credentials_provider
    cfg.region = args.region
    cfg.endpoint = args.endpoint
    client = oss.Client(cfg)

    # 视频文件列表 替换为您需要处理的视频
    videos = [('视频A.mp4', '2025-04-07'), 
              ('视频B.mp4', '2025-04-07'), 
              ('视频C.mp4', '2025-04-07')]
    
    for video, date in videos:
        # 将截取的图片分日期放入目标文件夹
        target_key = f"{date}/{video}/{{index}}.{{autoext}}"
        
        # 填入配置好的视频样式
        style = 'style/ipc-snap'
        
        # 对目标存储空间名和文件名进行URL安全的Base64编码
        target_bucket_base64 = base64.urlsafe_b64encode(args.target_bucket.encode()).decode()
        target_key_base64 = base64.urlsafe_b64encode(target_key.encode()).decode()
        
        # 构建处理指令,包含样式和保存位置
        process = f"{style}|sys/saveas,b_{target_bucket_base64},o_{target_key_base64}"
        
        try:
            # 发送异步处理请求
            result = client.async_process_object(oss.AsyncProcessObjectRequest(
                bucket=args.bucket,
                key=video,
                process=process
            ))
            
            print(f"Status Code: {result.status_code}")
            print(f"RequestId: {result.request_id}")
            print(f"TaskId: {result.task_id}")
            print(f"EventId: {result.event_id}")
            print(f"ProcessRequestId: {result.process_request_id}")
            
        except Exception as e:
            print(f"Error: {e}")

if __name__ == "__main__":
    video_processor()

2. 配置截帧Bucket

2.1 创建Bucket

创建用于存储从IPC视频中提取的关键帧图片的 Bucket,这样,原始视频和关键帧图像将分别存储在不同的 Bucket 中,优化数据管理效率。

  1. 进入Bucket列表页面,然后单击创建Bucket

  2. 创建Bucket页面,填写Bucket名称(建议使用业务相关的名称,如ipc-frames-oss-metaquery-demo)。其余参数可保持默认配置。

  3. 单击完成创建,在创建成功的页面,点击进入Bucket

2.2 开启向量检索功能

为截帧Bucket开启向量检索功能,支持对关键帧图片进行基于自然语言描述和多条件组合的精准检索。

  1. 在左侧导航栏, 选择文件管理 > 数据索引

  2. 数据索引页面,单击立即开启

  3. 选择向量检索,单击确认开启

说明

构建元数据索引需要等待一定的时间,具体等待时长取决于BucketObject的数量。若开启时间过久可通过刷新来查看开启状态。

image

image

结果验证

您只需输入描述性文字,例如停着车的院子,系统便会返回与描述相符的关键帧图像。

  1. Bucket 列表页面,点击您的截帧Bucket名称。

  2. 文件列表页面,确认截帧图片已生成。

  3. 在左侧导航栏, 选择文件管理 > 数据索引

  4. 数据索引页面,检索内容中输入停着车的院子,在多媒体类型中勾选图片,点击立即查询

  5. 复制查询到的文件路径,返回文件列表页面,输入复制的文件路径进行搜索,即可查看到符合描述的截帧图片。

2025-04-08_16-21-10 (2)

应用于生产环境

当前智能语义检索系统已经完成构建,为了将该系统无缝集成到您的产品并应用于生产环境,您可以通过调用消费端应用接口,利用自然语言描述进行检索,快速定位目标画面或事件片段。

以下为消费端示例代码,展示如何构建符合 OSS MetaQuery 规范的 XML 请求,获取检索结果:

示例代码

# -*- coding: utf-8 -*-
import argparse
import alibabacloud_oss_v2 as oss
# 解析XML响应
import xml.etree.ElementTree as ET
import json 
from datetime import datetime 

def get_search_conditions():
    """获取用户的多条件输入"""
    print("请输入语义描述(示例:停着车的院子)")
    query = input("> 语义关键词: ").strip()
    while not query:
        print("语义关键词不能为空!")
        query = input("> 语义关键词: ").strip()
    return query  

def build_metaquery_xml(query):
    """构建符合OSS规范的MetaQuery XML"""
    xml_parts = [f'<Query>{query}</Query>']

    meta_query_xml = f'''<MetaQuery>
    {"".join(xml_parts)}
</MetaQuery>'''
    return meta_query_xml  # 编码为UTF-8字节流


def format_result(key, pre_url):
    """格式化单个搜索结果"""
    return f""" 文件路径:{key}
 文件地址:{pre_url}
-----------------------"""

def semantic_search():
    # 直接赋值给命令行参数
    args = argparse.Namespace(
        region='cn-beijing',  # 替换为你的区域
        bucket='ipc-frames-oss-metaquery-demo',  # 替换为你的存储空间名称
        endpoint='https://oss-cn-beijing.aliyuncs.com',  # 替换为你的endpoint,如果不需要可以留空或删除
    )

    # 初始化OSS客户端
    credentials = oss.credentials.EnvironmentVariableCredentialsProvider()
    cfg = oss.config.load_default()
    cfg.credentials_provider = credentials
    cfg.region = args.region
    if args.endpoint:
        cfg.endpoint = args.endpoint
    client = oss.Client(cfg)

    # 获取用户输入
    query = get_search_conditions()  # 只获取查询
    # 构建请求
    try:
        # 构造请求体(XML 数据)
        data_str = build_metaquery_xml(query)  # 不再传递条件

        # 定义操作输入
        req = oss.OperationInput(
            op_name='DoMetaQuery',  # 自定义操作名称
            method='POST',            # HTTP 方法
            parameters={              # 查询参数
                'metaQuery': '',
                'mode': 'semantic',
                'comp': 'query',
            },
            headers=None,             # 自定义请求头(可选)
            body=data_str.encode("utf-8"),  # 请求体(编码为 UTF-8 字节流)
            bucket=args.bucket,       # 目标 Bucket 名称
        )

        # 调用泛化接口执行操作
        resp = client.invoke_operation(req)

    except oss.exceptions.ServiceError as e:
        print(f" 服务端错误:{e.message}")
        return
    
    root = ET.fromstring(resp.http_response.content.decode('utf-8'))
     # 查找所有File元素
    files = root.findall('.//File')
    
    print(f"\n 共找到 {len(files)} 个匹配结果:")
    


    for i, file in enumerate(files, 1):
        # 获取文件名
        key_element = file.find('Filename')
        if key_element is None:
            continue  # 如果没有找到文件名,则跳过当前循环迭代
        key = key_element.text
        
        
        # 生成预签名URL
        pre_url = client.presign(
            oss.GetObjectRequest(
                bucket=args.bucket,  # 指定存储空间名称
                key=key,        # 指定对象键名
            )
        )
        
        print(format_result(key, pre_url.url))
        
    

if __name__ == "__main__":
    semantic_search()

运行该程序后,您可以输入描述性文字(例如停着车的院子)进行查询。系统根据数据索引,返回包含截帧图片路径、URL链接的检索结果,您可以直接通过URL链接查看图片详情。

共找到 4 个匹配结果:
文件路径:2025-04-07/视频A/2.jpg
文件地址:https://ipc-frames-oss-metaquery-demo.oss-cn-beijing.aliyuncs.com/2025-04-07/%E8%A7%86%E9%A2%91A/2.jpg?x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-date=20250407T025024Z&x-oss-expires=900&x-oss-credential=LTAI********************%2F20250407%2Fcn-beijing%2Foss%2Faliyun_v4_request&x-oss-signature=d2d849c56e230f6beaf199ee1ca756bb99b3f6a14ce64c1fa710127149375fac
------------------------
文件路径:2025-04-07/视频C/2.jpg
文件地址:https://ipc-frames-oss-metaquery-demo.oss-cn-beijing.aliyuncs.com/2025-04-07/%E8%A7%86%E9%A2%91C/2.jpg?x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-date=20250407T025024Z&x-oss-expires=900&x-oss-credential=LTAI********************%2F20250407%2Fcn-beijing%2Foss%2Faliyun_v4_request&x-oss-signature=1b3b456a32f9d0823dcfe5408d156df74e138a194bc06095877e00dd8079511e
------------------------
文件路径:2025-04-07/视频C/1.jpg
文件地址:https://ipc-frames-oss-metaquery-demo.oss-cn-beijing.aliyuncs.com/2025-04-07/%E8%A7%86%E9%A2%91C/1.jpg?x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-date=20250407T025024Z&x-oss-expires=900&x-oss-credential=LTAI********************%2F20250407%2Fcn-beijing%2Foss%2Faliyun_v4_request&x-oss-signature=1fc66719db2bbe611bc86a63a863d7f94ad9aebf14ccfaff8579938094ace3ef
------------------------
文件路径:2025-04-07/视频A/1.jpg
文件地址:https://ipc-frames-oss-metaquery-demo.oss-cn-beijing.aliyuncs.com/2025-04-10/%E8%A7%86%E9%A2%91A/1.jpg?x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-date=20250407T025024Z&x-oss-expires=900&x-oss-credential=LTAI********************%2F20250407%2Fcn-beijing%2Foss%2Faliyun_v4_request&x-oss-signature=4dd7f808872b9d4c988efe78a09cb91fd2accbb3e9c12df7cdbdf60bdcc2e7a7
------------------------

了解更多

为截帧图添加标签

为了实现更高效的检索,您可以利用 OSS 的对象标签功能,在关键帧图片添加元数据标签。例如,来标识视频的来源或业务场景信息,您便能在海量图片中快速定位目标资源。

假设系统中有三个待分析的视频文件(如视频A.mp4视频B.mp4视频C.mp4)如下:

视频A.mp4

视频B.mp4

视频C.mp4

example

example (1)

example

后院视频,标记为camara-a拍摄

售货视频,标记为camara-b拍摄

后院视频,和视频A内容近似,标记为camara-c拍摄

以下代码示例展示了如何在上传视频之后,通过 OSS SDK 为每个文件添加标签,您只需根据需求调整标签内容,就能实现自动化标签管理,从而为后续组合检索提供支持。

示例代码

import argparse
import alibabacloud_oss_v2 as oss
from alibabacloud_oss_v2.models import GetObjectTaggingRequest, PutObjectTaggingRequest, Tagging, TagSet, Tag

def list_and_process_frames(client, bucket, prefix, tags):
    """列出并处理指定前缀下的所有文件"""
    try:
        # 创建列举对象请求
        list_request = oss.models.ListObjectsRequest(
            bucket=bucket,
            prefix=prefix
        )
        
        # 获取对象列表并处理
        result = client.list_objects(list_request)
        if result.contents:
            for obj in result.contents:
                apply_tags_to_frame(client, bucket, obj.key, tags)
                    
    except Exception as e:
        print(f"处理文件夹失败: {str(e)}")

def apply_tags_to_frame(client, bucket, frame_key, tags):
    """为截帧图片添加标签"""
    try:
        # 构建标签对象
        tagging = Tagging(
            version=1,
            tag_set=TagSet(tags=tags)
        )
        
        # 创建更新标签请求
        put_tag_request = PutObjectTaggingRequest(
            bucket=bucket,
            key=frame_key,
            tagging=tagging
        )
        
        # 更新对象标签
        result = client.put_object_tagging(put_tag_request)
        
        # 将标签转换为字符串以便打印
        tags_str = '&'.join([f"{tag.key}={tag.value}" for tag in tags])
        print(f"成功为图片 {frame_key} 添加标签: {tags_str}")
        return True
    except Exception as e:
        print(f"为图片添加标签失败: {str(e)}")
        return False

def frame_tags():
    # OSS配置
    args = argparse.Namespace(
        region='cn-beijing',   
        frame_bucket='ipc-frames-oss-metaquery-demo',     
        endpoint='https://oss-cn-beijing.aliyuncs.com'
    )
    
    # 配置OSS客户端
    credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    cfg = oss.config.load_default()
    cfg.credentials_provider = credentials_provider
    cfg.region = args.region
    cfg.endpoint = args.endpoint
    client = oss.Client(cfg)

    # 配置您的存储帧路径前缀
    videos = [
        {
            'frame_prefix': '2025-04-07/视频A.mp4/' 
        },    
        {
            'frame_prefix': '2025-04-07/视频B.mp4/'
        },
        {
            'frame_prefix': '2025-04-07/视频C.mp4/'
        }
    ]

    # 处理每个视频的截帧
    for video in videos:


        # 从 frame_prefix 中提取视频名称部分,例如 '2025-04-07/视频A.mp4/' -> '视频A.mp4'
        prefix_parts = video['frame_prefix'].strip('/').split('/')
        video_filename = prefix_parts[-1] if prefix_parts else '' # 取最后一个非空部分,例如 '视频A.mp4'

        # 去掉文件扩展名,例如 '视频A.mp4' -> '视频A'
        video_name_base = video_filename.split('.')[0] if '.' in video_filename else video_filename

        print(f"\n开始处理视频 {video_filename} 的截帧") # 打印原始提取的文件名

        tags = [
            # 使用去除扩展名后的视频名称的最后一个字符设置摄像机标识
            Tag(key='camera', value=f'camera-{video_name_base[-1].lower()}' if video_name_base else 'camera-unknown'),
            # Tag(key='category', value='video_monitoring')       # 添加实际业务分类信息
        ]

        # 处理该视频的所有截帧
        list_and_process_frames(client, args.frame_bucket, video['frame_prefix'], tags)

        print(f"完成视频 {video_filename} 的截帧处理\n") # 打印原始提取的文件名

if __name__ == "__main__":
    frame_tags()

​以下提供消费端应用示例代码,利用描述性文字并结合其他条件(如标签)进行组合查询,获取检索结果:

示例代码

# -*- coding: utf-8 -*-
import argparse
import alibabacloud_oss_v2 as oss
# 解析XML响应
import xml.etree.ElementTree as ET
import json
from datetime import datetime
# --- 新增导入 ---
from alibabacloud_oss_v2.models import GetObjectTaggingRequest
# --- 新增导入结束 ---

def get_inputs():
    """获取用户的语义查询(必选)和筛选条件(可选)"""
    # 1. 获取必选的语义查询
    print(" 请输入语义描述(示例:停着车的院子)")
    query = input("> 语义关键词: ").strip()
    while not query:
        print(" 语义关键词不能为空!")
        query = input("> 语义关键词: ").strip()

    conditions = [] # 初始化筛选条件列表
    target_tag = None # --- 新增:用于存储目标标签 ---

    # 2. 获取可选的标签筛选
    print("\n (可选)请输入标签筛选条件(将用于客户端过滤,示例:camera=camera-a,按回车跳过)") # 修改提示
    tag_input = input("> 标签 (格式 key=value): ").strip()
    if tag_input:
        if '=' in tag_input:
            # --- 修改:不再添加到 conditions,而是存到 target_tag ---
            # conditions.append({
            #     'field': 'Tags',
            #     'value': tag_input,
            #     'op': 'eq'
            # })
            target_tag = tag_input
            print(f" 信息:标签 '{target_tag}' 将在获取结果后于客户端进行过滤。")
            # --- 修改结束 ---
        else:
             print(" 标签格式不正确,已忽略。请使用 'key=value' 格式。")


    # 3. 获取可选的路径前缀筛选
    print("\n (可选)请输入路径前缀筛选(示例:YYYY-MM-DD,按回车跳过)")
    date_prefix = input("> 路径前缀: ").strip()
    if date_prefix:
        conditions.append({
            'field': 'Filename',
            'value': date_prefix,
            'op': 'prefix'
        })

    # --- 修改:返回 target_tag ---
    return query, conditions, target_tag
    # --- 修改结束 ---

def build_metaquery_xml(query, conditions):
    """构建符合OSS规范的MetaQuery XML (包含语义查询和可选筛选)"""
    # 始终包含语义查询部分
    xml_parts = [f'<Query>{query}</Query>']

    # 添加可选的筛选条件 - 在语义模式下,仅支持部分字段(如此处的 Filename)
    for cond in conditions:
        # 在 semantic 模式下跳过 Tags 字段的 SimpleQuery 构建 (虽然我们已经不在 conditions 里加 Tags 了,但保留以防万一)
        if cond['field'] == 'Tags':
            # print(f" 信息:由于处于语义搜索模式,标签筛选条件 '{cond['value']}' 将被忽略。") # 这行不再需要
            continue

        json_query = json.dumps({
            "Field": cond['field'],
            "Value": cond['value'],
            "Operation": cond['op']
        }, ensure_ascii=False)
        xml_parts.append(f'<SimpleQuery>{json_query}</SimpleQuery>')

    # 组合成完整的 MetaQuery XML
    meta_query_xml = f'''<MetaQuery>
    {"".join(xml_parts)}
</MetaQuery>'''
    return meta_query_xml

def format_result(key, pre_url):
    """格式化单个搜索结果"""
    return f""" 文件地址:{pre_url}
 文件路径:{key}
-----------------------"""

def perform_search():
    # 直接赋值给命令行参数
    args = argparse.Namespace(
        region='cn-beijing',  # 替换为你的区域
        bucket='ipc-frames-oss-metaquery-demo',  # 替换为你的存储空间名称
        endpoint='https://oss-cn-beijing.aliyuncs.com',  # 替换为你的endpoint
    )

    # 初始化OSS客户端
    credentials = oss.credentials.EnvironmentVariableCredentialsProvider()
    cfg = oss.config.load_default()
    cfg.credentials_provider = credentials
    cfg.region = args.region
    if args.endpoint:
        cfg.endpoint = args.endpoint
    client = oss.Client(cfg)

    # 获取用户输入 (语义查询 + 可选条件 + 目标标签)
    # --- 修改:接收 target_tag ---
    query, conditions, target_tag = get_inputs()
    # --- 修改结束 ---

    # 构建请求
    try:
        # 构造请求体(XML 数据)
        # --- 只传入 conditions (不含Tags) ---
        data_str = build_metaquery_xml(query, conditions)
        
        print(f"\n 生成的 MetaQuery XML:\n{data_str}")

        # 定义操作输入
        req = oss.OperationInput(
            op_name='DoMetaQuery',
            method='POST',
            parameters={
                'metaQuery': '',
                'mode': 'semantic', # <-- 重新加入 semantic 模式
                'comp': 'query',
            },
            headers=None,
            body=data_str.encode("utf-8"),
            bucket=args.bucket,
        )

        # 调用泛化接口执行操作
        print("\n 发送 DoMetaQuery 请求...")
        resp = client.invoke_operation(req)
        print(f"\n 请求成功,HTTP 状态码: {resp.http_response.status_code}")
    # --- 修改异常类型 ---
    except oss.exceptions.ServiceError as e:
        print(f" 服务端错误 (ServiceError):{e.message}")
        print(f"   - HTTP Status Code: {e.status_code}")
        print(f"   - Error Code: {e.error_code}")
        print(f"   - Request ID: {e.request_id}")
        return
    except oss.exceptions.ClientError as e: # 添加对客户端错误的捕获
        print(f" 客户端或网络错误 (ClientError):{e.message}")
        return
    except Exception as e: # 捕获其他可能的异常
        print(f" 未知错误:{e}")
        import traceback
        traceback.print_exc() # 打印完整的 traceback
        return

    # --- 修改结束 ---

    # 解析和处理结果...
    final_results_count = 0 # --- 新增:计数器,用于统计最终符合条件的结果 ---
    try:
        root = ET.fromstring(resp.http_response.content.decode('utf-8'))
        # 查找所有File元素
        files = root.findall('.//File')

        print(f"\n 从OSS获取到 {len(files)} 个初步匹配结果,开始进行客户端标签过滤...")

        if not files:
            # 检查是否有 NextContinuationToken,可能需要分页
            next_token_elem = root.find('.//NextContinuationToken')
            if next_token_elem is not None and next_token_elem.text:
                print(" 提示:可能还有更多结果,当前实现未处理分页。")
            print("\n 没有初步匹配结果。")
            return # 没有文件时提前返回


        for i, file in enumerate(files, 1):
            # 获取文件名
            key_element = file.find('Filename')
            if key_element is None:
                print(f" 警告:第 {i} 个初步结果缺少 Filename 字段,已跳过。")
                continue
            key = key_element.text

            # --- 客户端标签过滤 ---
            if target_tag:
                try:
                    tagging_req = GetObjectTaggingRequest(bucket=args.bucket, key=key)
                    tagging_resp = client.get_object_tagging(tagging_req)
                    # 检查返回的标签集是否包含目标标签
                    tag_found = False
                    target_k, target_v = target_tag.split('=', 1)

                    # --- 修改:使用 .tags 而不是 .tag ---
                    if tagging_resp.tag_set and tagging_resp.tag_set.tags: # 使用 .tags
                        for tag in tagging_resp.tag_set.tags: # 使用 .tags
                    # --- 修改结束 ---
                            if tag.key == target_k and tag.value == target_v:
                                tag_found = True
                                break
                    if not tag_found:
                        continue # 标签不匹配,跳过此文件
                except oss.exceptions.ServiceError as tag_err:
                     if tag_err.status_code == 404 and tag_err.error_code == 'NoSuchTagSet':
                         continue
                     else:
                        print(f" 警告:获取文件 '{key}' 的标签时出错: {tag_err.error_code} - {tag_err.message},已跳过。")
                        continue
                except Exception as tag_e:
                    print(f" 警告:获取或处理文件 '{key}' 的标签时发生未知错误: {tag_e},已跳过。")
                    # --- 添加 traceback 方便调试 ---
                    import traceback
                    traceback.print_exc()
                    # --- 添加结束 ---
                    continue
            # --- 客户端标签过滤结束 ---

            # --- 如果通过了标签过滤(或者没有设置标签过滤),则处理并打印结果 ---
            final_results_count += 1 # 增加最终结果计数
            print(f"\n[{final_results_count}] 文件 '{key}' 符合所有条件:") # 打印最终结果编号

            # 生成预签名URL
            try:
                pre_url = client.presign(
                    oss.GetObjectRequest(
                        bucket=args.bucket,
                        key=key,
                    )
                )
                print(format_result(key, pre_url.url))
            except Exception as presign_e:
                print(f" 警告:为文件 '{key}' 生成预签名URL时出错: {presign_e}")
                print(format_result(key, "[无法生成URL]"))
        # --- 循环结束后打印最终统计 ---
        print(f"\n 客户端过滤完成,共找到 {final_results_count} 个最终匹配结果。")

    except ET.ParseError as xml_e:
        print(f" 错误:解析OSS响应XML时出错 - {xml_e}")
    except Exception as parse_e:
        print(f" 错误:处理结果时发生意外错误 - {parse_e}")


if __name__ == "__main__":
    perform_search()

运行该程序后,如需筛选出包含停放车辆的院子的视频内容,您可以:

  1. 在描述性字段输入检索关键词:停着车的院子

  2. 设置标签筛选条件:camera = camera-a

在当前视频文件中,视频 A 与视频 C 均拍摄了符合停着车的院子的描述场景,但由于设置了标签筛选(仅保留camera-a标记的视频检索结果),最终检索结果仅包含视频A。

发送 DoMetaQuery 请求...

请求成功,HTTP 状态码: 200

从OSS获取到 4 个初步匹配结果,开始进行客户端标签过滤...

[1] 文件 '2025-04-07/视频A/2.jpg' 符合所有条件:
 文件地址:https://ipc-frames-oss-metaquery-demo.oss-cn-beijing.aliyuncs.com/2025-04-07/%E8%A7%86%E9%A2%91A/2.jpg?x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-date=20250407T025459Z&x-oss-expires=900&x-oss-credential=LTAI********************%2F20250407%2Fcn-beijing%2Foss%2Faliyun_v4_request&x-oss-signature=5e9d684ec141f9570f2f95713de492ac8291a39d1d655e4bb4c4d35b40d1e554
 文件路径:2025-04-07/视频A/2.jpg
-----------------------

[2] 文件 '2025-04-07/视频A/1.jpg' 符合所有条件:
 文件地址:https://ipc-frames-oss-metaquery-demo.oss-cn-beijing.aliyuncs.com/2025-04-07/%E8%A7%86%E9%A2%91A/1.jpg?x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-date=20250407T025459Z&x-oss-expires=900&x-oss-credential=LTAI********************%2F20250407%2Fcn-beijing%2Foss%2Faliyun_v4_request&x-oss-signature=1c13310abdd764e8ac9f67e27a179f02949eaf47c4ed68358eefcb1965a6dd1e
 文件路径:2025-04-07/视频A/1.jpg
-----------------------
 客户端过滤完成,共找到 2 个最终匹配结果。

配置截帧事件通知

在实际应用中,视频截帧处理是以异步请求形式提交的,即在返回处理结果时无法直接获得截帧的处理结果(如处理成功或失败等关键信息)。如果您需要获取视频截帧的处理结果,建议结合轻量消息队列SMQ(原MNS),配置事件通知,即可以在截帧处理完成后收到即时通知,无需重复查询任务状态。

实现截帧事件通知,您需要先参考主题模型快速入门创建一个与您的视频Bucket位于同一地域的消息主题。​在视频截帧时,您可以通过以下代码在视频截帧时配置事件通知,其中的主题名称需经过URL安全的Base64编码处理。​例如,消息主题名称为test-topic,则编码后的名称为dGVzdC10b3BpYw。 ​

示例代码

# -*- coding: utf-8 -*-
import base64
import argparse
import alibabacloud_oss_v2 as oss


def video_processor():
    # OSS配置
    args = argparse.Namespace(
        region='cn-beijing',
        target_bucket='ipc-frames-oss-metaquery-demo',    
        bucket='ipc-videos-oss-metaquery-demo',     
        endpoint='https://oss-cn-beijing.aliyuncs.com'
    )
    
    # 配置OSS客户端
    credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
    cfg = oss.config.load_default()
    cfg.credentials_provider = credentials_provider
    cfg.region = args.region
    cfg.endpoint = args.endpoint
    client = oss.Client(cfg)

    # 视频文件列表 替换为您需要处理的视频
    videos = [('视频A.mp4', '2025-04-07'), 
              ('视频B.mp4', '2025-04-07'), 
              ('视频C.mp4', '2025-04-07')]
    
    for video, date in videos:
        # 将截取的图片分日期放入目标文件夹
        target_key = f"{date}/{video}/{{index}}.{{autoext}}"
        
        # 填入配置好的视频样式
        style = 'style/ipc-snap'
        
        # 对目标存储空间名和文件名进行URL安全的Base64编码
        target_bucket_base64 = base64.urlsafe_b64encode(args.target_bucket.encode()).decode()
        target_key_base64 = base64.urlsafe_b64encode(target_key.encode()).decode()
        
        # 构建处理指令,包含样式和保存位置,最后将转换结果通知发送到主题名称为test-topic的SMQ。
        process = f"{style}|sys/saveas,b_{target_bucket_base64},o_{target_key_base64}/notify,topic_dGVzdC10b3BpYw"
        
        try:
            # 发送异步处理请求
            result = client.async_process_object(oss.AsyncProcessObjectRequest(
                bucket=args.bucket,
                key=video,
                process=process
            ))
            
            print(f"Status Code: {result.status_code}")
            print(f"RequestId: {result.request_id}")
            print(f"TaskId: {result.task_id}")
            print(f"EventId: {result.event_id}")
            print(f"ProcessRequestId: {result.process_request_id}")
            
        except Exception as e:
            print(f"Error: {e}")

if __name__ == "__main__":
    video_processor()
  • 本页导读 (1)
  • 方案概览
  • 方案优势
  • 1. 配置视频Bucket
  • 1.1 创建Bucket并上传设备采集视频
  • 1.2 绑定智能媒体管理(IMM)项目
  • 1.3 创建视频截帧样式
  • 1.4 执行视频截帧
  • 2. 配置截帧Bucket
  • 2.1 创建Bucket
  • 2.2 开启向量检索功能
  • 结果验证
  • 应用于生产环境
  • 了解更多
  • 为截帧图添加标签
  • 配置截帧事件通知