YOLOv8+GLM-4-9B多模态实践:智能视频摘要生成系统

你有没有过这样的经历?面对一段长达数小时的监控录像,需要快速找出关键事件,或者看完一部冗长的会议记录视频,只想了解核心结论。传统方法要么靠人眼一帧帧看,效率低下;要么用简单的剪辑工具,效果粗糙。现在,结合目标检测和长文本大模型,我们可以让机器自动“看懂”视频,并生成一份清晰、准确的文字摘要。

今天要聊的,就是如何用YOLOv8和GLM-4-9B这两个明星模型,搭建一套智能视频摘要生成系统。简单来说,YOLOv8负责“看”视频里有什么物体、人在做什么;GLM-4-9B则像一位经验丰富的观察员,把看到的信息组织成通顺、有逻辑的文字报告。这套组合拳,尤其在安防监控、会议记录、内容审核等场景下,能带来效率的质变。

1. 为什么需要智能视频摘要?

视频数据正在爆炸式增长,但人的时间和注意力是有限的。单纯存储视频意义不大,关键是把视频里的信息提炼出来。传统方法有几个明显的痛点:

人工处理效率瓶颈:靠人力观看和记录,速度慢、成本高,而且容易因疲劳出错。一段8小时的监控,看完可能就需要一整天。

关键信息容易遗漏:人在长时间观看时,注意力会分散,可能错过转瞬即逝的重要画面,比如某个角落的异常动作。

检索和回溯困难:即使视频存档了,日后想查找某个特定事件,也需要重新观看大量无关内容,就像大海捞针。

信息呈现不直观:视频本身是连续的图像流,不便于快速阅读、分享和归档。一份文字摘要则一目了然。

智能视频摘要系统就是为了解决这些问题。它不仅能7x24小时不间断工作,还能以结构化的文字形式输出核心内容,让视频数据真正变得可读、可查、可用。接下来,我们就看看怎么用技术实现它。

2. 技术核心:YOLOv8与GLM-4-9B如何协同工作?

这套系统的核心思想是“视觉感知”加“语言理解”的分工协作。你可以把它想象成两个人的配合:一个眼神犀利的侦察兵(YOLOv8)和一个文笔出色的书记员(GLM-4-9B)。

YOLOv8:快速精准的“侦察兵” YOLOv8是目前最流行的目标检测模型之一,它的特点是“快”和“准”。对于视频的每一帧(或关键帧),YOLOv8能迅速识别出画面中都有哪些物体(人、车、包、动物等),它们在哪(用边界框标出),以及置信度有多高。这相当于把视频流转化成了一连串带有时序标记的物体检测日志。

GLM-4-9B:善于归纳的“书记员” GLM-4-9B是智谱AI开源的对话大模型,特别值得一提的是它的“长文本”能力。它的GLM-4-9B-Chat-1M版本支持高达100万token(约200万汉字)的上下文。这意味着,它能够一次性阅读和理解非常长的文本序列。在我们的系统里,它接收来自YOLOv8的检测结果序列(可能包含成千上万个物体事件),然后像分析一篇超长报告一样,从中提炼出事件脉络、主要活动、异常点,并生成连贯的摘要。

工作流就像一条流水线

  1. 视频输入:系统接收原始视频。
  2. 关键帧提取与目标检测:先对视频抽帧(比如每秒1帧或根据场景变化动态抽帧),然后用YOLOv8对每一帧进行检测,得到“时间戳-物体列表”的数据。
  3. 时序信息融合:这不是简单罗列检测结果。我们需要把离散的检测框,按照时间顺序和空间关系串联起来,形成“事件”。比如,同一个人从画面左边走到右边,会被识别为一个“人员移动”事件,而不是多个孤立的人。
  4. 文本化与摘要生成:将融合后的事件序列,整理成一段描述性的文本提示(Prompt),喂给GLM-4-9B。例如:“以下是一段监控视频的物体检测记录,时间从00:00到08:00。请根据这些记录,生成一段概括性的视频内容摘要,描述主要活动、人员车辆进出、以及任何异常情况。”
  5. 摘要输出:GLM-4-9B输出最终的文字摘要。

这个流程的关键在于第三步“时序信息融合”和第四步“提示词设计”,它们直接决定了摘要的质量。下面我们就进入实战环节,看看代码怎么写。

3. 动手搭建:从环境准备到代码实现

我们假设你已经有基本的Python开发环境,并且有一张支持CUDA的NVIDIA显卡(显存建议8G以上,因为要跑两个模型)。整个项目结构会相对清晰。

3.1 环境与模型准备

首先,安装核心的依赖库。我们使用Ultralytics的YOLOv8官方库和Transformers库来调用GLM-4-9B。

# 创建虚拟环境(可选)
python -m venv video_summary_env
source video_summary_env/bin/activate  # Linux/Mac
# video_summary_env\Scripts\activate  # Windows

# 安装依赖
pip install ultralytics opencv-python transformers torch torchvision
pip install accelerate  # 用于加速大模型加载
# 如果需要使用vLLM获得极速推理,可以安装,但对硬件要求更高
# pip install vllm

接下来是模型。YOLOv8的模型可以通过ultralytics库自动下载。GLM-4-9B的模型文件比较大(约18GB),我们可以从ModelScope或Hugging Face下载。这里以从ModelScope下载为例:

# 这是一个下载模型的示例脚本,你可以单独运行它
import os
from modelscope import snapshot_download

model_dir = snapshot_download('ZhipuAI/glm-4-9b-chat-1m', cache_dir='./models')
print(f"模型已下载至: {model_dir}")

由于模型体积大,下载可能需要较长时间。也可以先手动下载好,放在本地目录。

3.2 核心代码实现

我们创建一个主要的类 VideoSummaryGenerator 来串联整个流程。

import cv2
import torch
from ultralytics import YOLO
from transformers import AutoTokenizer, AutoModelForCausalLM
from typing import List, Dict, Any
import json
from datetime import timedelta

class VideoSummaryGenerator:
    def __init__(self, yolo_model_path='yolov8n.pt', glm_model_path='./models/glm-4-9b-chat-1m'):
        """
        初始化生成器
        :param yolo_model_path: YOLOv8模型路径,可以是官方名称如 'yolov8n.pt',会自动下载
        :param glm_model_path: GLM-4-9B模型本地路径
        """
        # 初始化YOLOv8模型(使用GPU如果可用)
        self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
        print(f"使用设备: {self.device}")
        
        self.yolo_model = YOLO(yolo_model_path).to(self.device)
        
        # 初始化GLM-4-9B的tokenizer和model
        print("正在加载GLM-4-9B模型,这可能需要一些时间...")
        self.tokenizer = AutoTokenizer.from_pretrained(
            glm_model_path, 
            trust_remote_code=True  # GLM需要这个参数
        )
        self.llm_model = AutoModelForCausalLM.from_pretrained(
            glm_model_path,
            torch_dtype=torch.bfloat16,  # 使用BF16节省显存
            low_cpu_mem_usage=True,
            trust_remote_code=True
        ).to(self.device).eval()
        print("模型加载完毕!")
        
    def extract_key_frames(self, video_path: str, interval_sec: int = 2) -> List[Dict]:
        """
        从视频中按时间间隔提取关键帧,并进行目标检测
        :param video_path: 视频文件路径
        :param interval_sec: 抽帧间隔(秒)
        :return: 包含时间戳和检测结果的列表
        """
        cap = cv2.VideoCapture(video_path)
        fps = cap.get(cv2.CAP_PROP_FPS)
        frame_interval = int(fps * interval_sec)
        
        frame_count = 0
        detection_results = []
        
        while cap.isOpened():
            ret, frame = cap.read()
            if not ret:
                break
                
            # 按间隔抽帧
            if frame_count % frame_interval == 0:
                # 计算当前时间戳
                timestamp_sec = frame_count / fps
                timestamp_str = str(timedelta(seconds=int(timestamp_sec)))
                
                # 使用YOLOv8进行检测
                # YOLO返回的结果列表,我们取第一个(因为只传了一张图)
                results = self.yolo_model(frame, verbose=False)[0]
                
                # 解析检测结果
                detections = []
                if results.boxes is not None:
                    for box, cls, conf in zip(results.boxes.xyxy, results.boxes.cls, results.boxes.conf):
                        class_name = self.yolo_model.names[int(cls)]
                        detections.append({
                            'class': class_name,
                            'confidence': float(conf),
                            'bbox': box.cpu().numpy().tolist()  # [x1, y1, x2, y2]
                        })
                
                # 只保留置信度较高的检测结果,减少噪音
                high_conf_detections = [d for d in detections if d['confidence'] > 0.5]
                
                if high_conf_detections:  # 如果这一帧有检测到物体,才记录
                    detection_results.append({
                        'timestamp': timestamp_str,
                        'timestamp_sec': timestamp_sec,
                        'detections': high_conf_detections
                    })
                
                print(f"已处理 {timestamp_str} 处的帧,检测到 {len(high_conf_detections)} 个物体")
            
            frame_count += 1
        
        cap.release()
        return detection_results
    
    def fuse_temporal_events(self, detection_results: List[Dict]) -> str:
        """
        将时序检测结果融合成事件描述文本
        这是一个简化的版本,实际中可以更复杂(如跟踪同一物体)
        :param detection_results: extract_key_frames的输出
        :return: 给大模型的提示文本
        """
        event_descriptions = []
        
        # 按时间顺序处理
        for i, result in enumerate(detection_results):
            time_str = result['timestamp']
            detections = result['detections']
            
            if not detections:
                continue
                
            # 统计当前帧的主要物体
            class_counter = {}
            for det in detections:
                cls = det['class']
                class_counter[cls] = class_counter.get(cls, 0) + 1
            
            # 生成该时刻的简要描述
            desc_parts = []
            for cls, count in class_counter.items():
                if count == 1:
                    desc_parts.append(f"1个{cls}")
                else:
                    desc_parts.append(f"{count}个{cls}")
            
            if desc_parts:
                event_descriptions.append(f"[时间 {time_str}] 画面中出现: {', '.join(desc_parts)}")
        
        # 将所有事件描述合并成一段文本
        events_text = "\n".join(event_descriptions)
        
        # 构建给GLM的提示词
        prompt = f"""你是一个专业的视频内容分析员。以下是一段监控视频按时间顺序提取的关键画面描述:

{events_text}

请根据以上时序观察,生成一段流畅、连贯的视频内容摘要。摘要需要:
1. 概括视频中发生的主要活动。
2. 描述人员、车辆等物体的进出和移动趋势。
3. 指出任何值得注意的异常或重点事件。
4. 使用客观、简洁的语言。

视频摘要:"""
        
        return prompt
    
    def generate_summary(self, prompt: str, max_new_tokens: int = 500) -> str:
        """
        使用GLM-4-9B生成摘要
        :param prompt: 构建好的提示词
        :param max_new_tokens: 生成文本的最大长度
        :return: 生成的摘要文本
        """
        # 使用chat模板格式(GLM推荐的方式)
        messages = [{"role": "user", "content": prompt}]
        
        # 应用聊天模板并tokenize
        inputs = self.tokenizer.apply_chat_template(
            messages,
            add_generation_prompt=True,
            tokenize=True,
            return_tensors="pt",
            return_dict=True
        )
        inputs = {k: v.to(self.device) for k, v in inputs.items()}
        
        # 生成参数
        gen_kwargs = {
            "max_new_tokens": max_new_tokens,
            "do_sample": True,  # 启用采样使文本更多样
            "top_p": 0.8,       # nucleus sampling参数
            "temperature": 0.7, # 温度参数,控制随机性
        }
        
        # 生成摘要
        with torch.no_grad():
            outputs = self.llm_model.generate(**inputs, **gen_kwargs)
            # 解码生成的token(跳过输入部分)
            generated_ids = outputs[:, inputs['input_ids'].shape[1]:]
            summary = self.tokenizer.decode(generated_ids[0], skip_special_tokens=True)
        
        return summary
    
    def process_video(self, video_path: str) -> Dict[str, Any]:
        """
        处理视频的主流程
        :param video_path: 视频文件路径
        :return: 包含原始检测结果和最终摘要的字典
        """
        print("步骤1: 提取关键帧并进行目标检测...")
        detections = self.extract_key_frames(video_path, interval_sec=3)  # 每3秒一帧
        
        print(f"步骤1完成,共分析 {len(detections)} 个关键时间点。")
        
        print("步骤2: 融合时序信息,构建提示词...")
        prompt = self.fuse_temporal_events(detections)
        
        print("步骤3: 使用GLM-4-9B生成视频摘要...")
        summary = self.generate_summary(prompt)
        
        print("处理完成!")
        return {
            'detection_logs': detections,
            'generated_summary': summary
        }

# 使用示例
if __name__ == "__main__":
    # 初始化生成器(首次运行会自动下载yolov8n模型)
    generator = VideoSummaryGenerator(
        yolo_model_path='yolov8n.pt',  # 可以用 yolov8s.pt, yolov8m.pt 等更大模型提高精度
        glm_model_path='./models/ZhipuAI/glm-4-9b-chat-1m'  # 替换为你的实际路径
    )
    
    # 处理视频
    result = generator.process_video('your_video.mp4')  # 替换为你的视频路径
    
    print("\n" + "="*50)
    print("生成的视频摘要:")
    print("="*50)
    print(result['generated_summary'])
    
    # 可选:将结果保存为JSON文件
    with open('video_summary_result.json', 'w', encoding='utf-8') as f:
        # 注意:detection_logs中有numpy数组,需要转换
        import numpy as np
        def default_serializer(obj):
            if isinstance(obj, np.ndarray):
                return obj.tolist()
            raise TypeError(f"Object of type {type(obj)} is not JSON serializable")
        
        json.dump(result, f, ensure_ascii=False, indent=2, default=default_serializer)
    print("结果已保存至 video_summary_result.json")

这段代码是一个完整的、可运行的示例。它做了几件关键事:

  1. 按时间抽帧:避免处理每一帧,提高效率。
  2. 用YOLOv8检测物体:得到每帧有什么。
  3. 融合成事件文本:把离散的检测框变成时间线描述。
  4. 调用GLM-4-9B生成摘要:让大模型做最终的归纳和写作。

你可以直接复制这段代码,修改视频路径和模型路径,就能跑起来看看效果。

4. 在安防监控场景的落地案例

理论说得再好,不如实际案例有说服力。假设我们有一个小区出入口的监控视频,时长约30分钟。传统保安需要全程盯守或事后回放,而我们的系统可以自动输出这样一份摘要:

视频摘要(出入口监控,时间 14:00-14:30): 本时段内,出入口总体活动平稳。视频开始阶段(14:00-14:10),共有3辆私家车和1辆快递三轮车驶入,同时有2位行人步行进入小区。14:15左右,出现一个值得注意的情况:一名身着深色外套的人员在闸机口徘徊约2分钟,期间多次尝试跟随其他居民进入,但未成功,随后离开。14:20至14:25,车流量有所增加,连续有5辆汽车驶出。14:28,一名儿童在门口玩耍时险些跑到车行道,被随行大人及时拉回。整体而言,除14:15的异常徘徊行为外,未发现其他安全事件。

这份摘要立刻让管理者抓住了重点:有一个需要关注的徘徊人员,以及一个儿童的安全隐患。它比单纯的“有车有人”的检测日志有用得多,因为它讲述了“故事”。

在实际部署时,我们还可以针对特定场景优化:

  • 自定义检测类别:如果只关心人和车,可以在YOLO中过滤掉其他类别,减少干扰。
  • 优化提示词(Prompt):给GLM的指令可以更具体。比如:“请以小区保安日报的形式撰写摘要,重点报告陌生人员徘徊、车辆异常停留、物品遗留、人员聚集等四类情况。”
  • 设置报警阈值:当GLM生成的摘要中出现“异常”、“徘徊”、“冲突”等关键词时,系统可以自动标记该视频段,并通知人工复核。

5. 实践经验与优化建议

在实际搭建和测试过程中,我总结了一些经验,可能对你有帮助:

关于性能与精度的平衡

  • 抽帧间隔:间隔太短(如每秒)处理慢,间隔太长(如10秒)可能漏掉关键瞬间。对于普通监控,2-5秒是个不错的起点。对于高速变化的场景(如交通路口),可能需要更短间隔或使用动态抽帧算法(检测到画面变化大时才分析)。
  • YOLO模型选择yolov8n.pt(纳米型)最快,但精度最低;yolov8x.pt(巨型)最准,但最慢。对于视频摘要,yolov8s.ptyolov8m.pt通常在速度和精度间取得较好平衡。
  • GLM推理优化:如果摘要生成速度是瓶颈,可以考虑:
    • 使用 vLLM 后端进行推理,它能极大提升吞吐量。
    • 将模型量化(如转为INT8或INT4),可以显著减少显存占用和加速,但可能会轻微损失精度。

提升摘要质量的技巧

  1. 给GLM“喂”更好的信息:在fuse_temporal_events函数里,我们可以做得更智能。比如,加入简单的跟踪算法,把同一物体在不同帧的出现关联起来,形成“人物A从东侧走到岗亭”这样的描述,而不是“第1帧有个人,第5帧有个人”。
  2. 分阶段摘要:对于超长视频(如24小时监控),可以先按小时或按事件分段生成摘要,再让GLM对分段摘要进行二次总结,形成日报或周报。
  3. 提供领域知识:在提示词中加入场景背景。例如:“这是一个银行ATM间的监控,请特别关注是否有人员长时间停留、遮挡摄像头、遗留物品等行为。”
  4. 后处理与格式化:要求GLM以特定格式输出,比如“时间:... 事件:... 风险等级:...”,便于后续程序自动解析和入库。

可能遇到的坑与解决思路

  • 显存不足(OOM):这是最常见的问题。解决方案:1) 使用更小的YOLO和GLM模型;2) 启用GPU内存清理;3) 使用模型量化;4) 将检测和摘要生成分两步进行,释放YOLO模型后再加载GLM模型。
  • 摘要内容空洞或重复:这通常是因为喂给GLM的检测日志太稀疏或噪音太多。可以尝试:1) 降低YOLO的置信度阈值,捕捉更多物体;2) 在融合事件时,加入一些简单的逻辑推理,比如“如果连续多帧检测到多人,则记为‘人群聚集’”。
  • 处理速度慢:视频摘要不是实时系统,对延迟有一定容忍度。如果必须加速,可以考虑:1) 使用多进程,将视频分块并行处理;2) 在抽帧后,将帧图片批量送入YOLO,而不是一帧一送;3) 对于GLM,使用流式生成,让用户先看到部分结果。

整体用下来,YOLOv8和GLM-4-9B的搭配确实能打开一扇新的大门,让机器对视频内容的理解从“看到了什么”进化到“发生了什么”。虽然现在的系统还有优化空间,比如事件融合的算法可以更精巧,但对于很多需要从海量视频中提取信息的场景来说,已经是一个强有力的工具。

如果你正准备尝试,我的建议是从一个小而具体的场景开始,比如一段10分钟的会议室录像,或者自家门口的监控。先跑通整个流程,看看生成的摘要有没有抓住重点,然后再去调整抽帧策略、优化提示词。这个过程本身,就是理解和驾驭这些强大模型的最好方式。

获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐