作者:梅雅达编程笔记

收藏了一个 1 小时的 Python 教学视频,想着"周末好好看"。结果周末到了,打开视频,看了 5 分钟觉得太慢,拖了一下进度条,又觉得跳太多了怕漏掉关键内容……反反复复折腾了 20 分钟,啥也没学到。最后安慰自己"收藏了就是学了",然后继续刷短视频。

这个视频其实就讲了 3 个知识点。如果有个 30 秒就能看完的图文摘要就好了。

今天这篇就写这么一套工具:给它一个视频文件,它自动帮你截取画面变化的关键帧、把语音全部转成文字、让 AI"看"关键帧理解画面里展示了什么,最后综合文字和画面,生成一份带时间戳的图文笔记。全程自动,你只需要一行命令。

视频为什么这么"难啃"

文字有摘要,图片有缩略图,但视频没有。传统方案无非两种:

方案 A:人工看完全片,手写笔记。 1 小时视频至少花 1.5 小时(还得暂停记笔记)。慢、累、不可持续。

方案 B:自动截取等间隔截图 + 字幕。 很多播放器有这个功能——每隔 N 秒截一张图。但你仔细看就知道,大多数截图是"废话画面":讲师在喝水、PPT 停在同一页、画面没有变化。

问题的核心是:视频的信息密度不均匀。一个 1 小时的教学视频,真正有新信息的内容可能只占 20%——剩下的全是过渡、重复、闲聊。你需要的不是"均匀截图",而是"智能抓重点"。

AI 视频摘要的思路就是四件事:

  • 关键帧提取:不是等间隔截,而是在"画面发生变化"的时候才截——切 PPT 了、换场景了、出现新代码了,这些才是重点时刻
  • 语音转文字:把讲师说的每句话都记下来(Day05 学的 Whisper,今天直接复用)
  • 视觉模型看关键帧:光有文字不够,视频里很多信息是"写在画面上"的——代码截图、图表、公式、板书。视觉模型能"看懂"这些
  • LLM 综合整理:把文字 + 画面信息合在一起,生成结构化的摘要笔记

原理:三步拼出一个视频摘要

整个流程画成一张图:

视频文件
  │
  ├──→ FFmpeg 提取音轨 ──→ Whisper 语音转文字 ──→ 带时间戳的全文
  │
  ├──→ FFmpeg 场景检测 ──→ 关键帧图片序列
  │                            │
  │                      GLM-4V-Flash 视觉分析
  │                            │
  │                      每帧的文字描述
  │
  └──────────────┬──────────────────┘
                 │
          glm-4.5-air 综合总结
                 │
          结构化图文摘要

FFmpeg 场景检测(Scene Detection)

FFmpeg 内置了一个 select 滤镜,配合 scene 检测器,可以自动识别"画面什么时候发生了显著变化"。原理很简单:计算相邻两帧的像素差异(直方图差值),差异超过阈值就认为"场景切换了",把这一帧截下来。

ffmpeg -i video.mp4 -vf "select='gt(scene,0.3)'" -fps_mode vfr frame_%04d.jpg

这一行命令就能把视频里所有"画面变化"的瞬间截取出来。阈值 0.3 是个经验值——越小越敏感(截的帧越多),越大越迟钝(只截大变化)。

Whisper 语音转文字

Day05 详细讲过,这里直接复用。核心就一句话:

from faster_whisper import WhisperModel
model = WhisperModel("small", device="cpu", compute_type="int8")
segments, info = model.transcribe("audio.wav", language="zh")

GLM-4V-Flash 视觉分析 + glm-4.5-air 总结

关键帧截下来之后,用 GLM-4V-Flash(永久免费的视觉模型)逐张分析"画面里有什么",然后把画面描述 + 语音转文字的结果一起交给 glm-4.5-air,让它生成结构化摘要。

为什么需要视觉模型?因为视频里很多东西是"写在画面上"的——PPT 标题、代码片段、图表数据。纯靠语音转文字,这些信息会丢失。

动手实战:完整 Pipeline

环境准备

# Python 依赖
pip install faster-whisper requests

# FFmpeg(系统级工具)
# Windows: 去 https://ffmpeg.org/download.html 下载,加入 PATH
# macOS:
brew install ffmpeg
# Ubuntu:
sudo apt install ffmpeg

验证安装:

ffmpeg -version        # 能看到版本号就行
ffprobe -version       # ffprobe 用来获取视频信息

第一步:提取音轨

视频文件里的音频不是独立文件,而是和视频流封装在一起的。我们先用 FFmpeg 把音频"抽"出来:

import subprocess

def extract_audio(video_path, output_dir):
    audio_path = f"{output_dir}/audio_track.wav"
    
    cmd = [
        "ffmpeg", "-y",
        "-i", video_path,
        "-vn",                    # 不要视频,只要音频
        "-acodec", "pcm_s16le",   # 16bit PCM 编码
        "-ar", "16000",           # 16kHz 采样率(Whisper 推荐)
        "-ac", "1",               # 单声道
        audio_path
    ]
    
    subprocess.run(cmd, capture_output=True, text=True, check=True)
    return audio_path

16kHz 单声道 WAV 是 Whisper 的"标准输入格式",这个采样率对人声足够用了。

第二步:关键帧提取

这是整个 Pipeline 最有意思的一步。

def extract_keyframes(video_path, output_dir, threshold=0.3, max_frames=30):
    frames_dir = f"{output_dir}/keyframes"
    os.makedirs(frames_dir, exist_ok=True)
    
    cmd = [
        "ffmpeg", "-y",
        "-i", video_path,
        "-vf", f"select='gt(scene\\,{threshold})',scale=1280:-1",
        "-fps_mode", "vfr",           # 可变帧率,只输出选中的帧(ffmpeg 9 已移除 -vsync vfr)
        "-pix_fmt", "yuvj420p",       # 关键帧 JPEG 输出,避免 mjpeg 编码器 limited-range YUV 报错
        "-q:v", "2",                  # JPEG 高质量
        "-frames:v", str(max_frames), # 最多截 max_frames 帧
        f"{frames_dir}/frame_%04d.jpg"
    ]
    
    subprocess.run(cmd, capture_output=True, text=True, check=True)
    
    # 收集截到的帧
    frame_files = sorted([
        f"{frames_dir}/{f}" for f in os.listdir(frames_dir)
        if f.endswith(".jpg")
    ])
    
    return frame_files

几个参数解释:

  • select='gt(scene,0.3)':场景差异 > 0.3 才截取。0.3 是个不错的起点
  • scale=1280:-1:截出来的帧缩放到宽度 1280px,高度自动。这样发给视觉模型不会太大
  • -fps_mode vfr:只输出被选中的帧,不输出"空白帧"(ffmpeg 9 起 -vsync vfr 已移除,用这个替代)
  • -frames:v 30:最多 30 帧,防止长视频截几百张图

如果场景检测没截到帧怎么办? 比如视频全程画面几乎没变(纯音频播客配一张静态图),这时候需要退化为等间隔截取:

def extract_frames_interval(video_path, frames_dir, max_frames=30):
    """退化方案:等间隔截取"""
    # 用 ffprobe 获取视频时长
    result = subprocess.run(
        ["ffprobe", "-v", "error", "-show_entries", "format=duration",
         "-of", "default=noprint_wrappers=1:nokey=1", video_path],
        capture_output=True, text=True
    )
    duration = float(result.stdout.strip())
    
    interval = duration / (max_frames + 1)
    frame_files = []
    
    for i in range(max_frames):
        timestamp = interval * (i + 1)
        output = f"{frames_dir}/frame_{i+1:04d}.jpg"
        subprocess.run([
            "ffmpeg", "-y", "-ss", str(timestamp),
            "-i", video_path, "-vframes", "1",
            "-q:v", "2", "-vf", "scale=1280:-1",
            "-pix_fmt", "yuvj420p",   # 兼容 mjpeg 编码器
            output
        ], capture_output=True)
        
        if os.path.exists(output):
            frame_files.append(output)
    
    return frame_files

第三步:语音转文字

直接复用 Day05 的代码:

from faster_whisper import WhisperModel

def transcribe_audio(audio_path):
    model = WhisperModel("small", device="cpu", compute_type="int8")
    
    segments, info = model.transcribe(
        audio_path,
        language="zh",
        vad_filter=True,  # 自动跳过静音段
    )
    
    full_text = ""
    transcript_segments = []
    
    for seg in segments:
        text = seg.text.strip()
        if text:
            transcript_segments.append({
                "start": round(seg.start, 1),
                "end": round(seg.end, 1),
                "text": text,
            })
            full_text += text
    
    return {"full_text": full_text, "segments": transcript_segments}

第四步:视觉模型分析关键帧

把每张关键帧发给 GLM-4V-Flash,让它描述画面内容:

import base64
import requests

def analyze_keyframes(frame_files, frame_timestamps):
    """逐帧调用 GLM-4V-Flash,返回 [{time_str, description}, ...]"""
    descriptions = []
    for frame_path, timestamp in zip(frame_files, frame_timestamps):
        with open(frame_path, "rb") as f:
            img_b64 = base64.b64encode(f.read()).decode()
        mm, ss = int(timestamp // 60), int(timestamp % 60)
        time_str = f"{mm:02d}:{ss:02d}"
        resp = requests.post(
            "https://open.bigmodel.cn/api/paas/v4/chat/completions",
            headers={"Authorization": f"Bearer {os.environ['GLM_API_KEY']}"},
            json={"model": "glm-4v-flash",
                  "messages": [{"role": "user", "content": [
                      {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{img_b64}"}},
                      {"type": "text", "text": f"这是视频在 {time_str} 时刻的画面,用一句话描述画面内容。"}]}],
                  "max_tokens": 100},
            timeout=30,
        )
        desc = resp.json()["choices"][0]["message"]["content"].strip()
        descriptions.append({"time_str": time_str, "description": desc})
    return descriptions

GLM-4V-Flash 是永久免费的视觉模型,支持图片理解。这里我们只让它"一句话描述",控制 token 消耗。

第五步:LLM 生成结构化摘要

最后,把语音转文字 + 关键帧描述一起交给 glm-4.5-air,让它生成结构化摘要:

def generate_summary(transcript, frame_descriptions):
    """语音转文字 + 关键帧描述 → glm-4.5-air 生成结构化摘要"""
    frame_text = "\n".join(
        f"[{fd['time_str']}] {fd['description']}" for fd in frame_descriptions)

    prompt = f"""根据以下视频的语音转文字内容和关键帧画面描述,生成结构化图文摘要笔记。
## 语音转文字内容
---
{transcript["full_text"][:8000]}
---
## 关键帧画面描述
---
{frame_text}
---
请输出 JSON 格式:{{"title": "标题(15字内)", "one_line_summary": "一句话总结",
"timeline": [{{"time": "00:00", "topic": "主题", "detail": "要点"}}],
"key_points": ["要点1","要点2"], "conclusion": "核心结论", "tags": ["标签1"]}}"""

    resp = requests.post(
        "https://open.bigmodel.cn/api/paas/v4/chat/completions",
        headers={"Authorization": f"Bearer {os.environ['GLM_API_KEY']}"},
        json={"model": "glm-4.5-air", "messages": [{"role": "user", "content": prompt}],
              "temperature": 0.3, "max_tokens": 2000},
        timeout=60,
    )
    content = resp.json()["choices"][0]["message"]["content"]
    if "```json" in content:
        content = content.split("```json")[1].split("```")[0]
    return json.loads(content.strip())

把 Pipeline 串起来

把所有步骤串成一行命令可调用的 Pipeline:

def video_summary_pipeline(video_path, output_dir=None):
    """一行命令跑完:视频 → 图文摘要"""
    video_path = os.path.abspath(video_path)
    
    if output_dir is None:
        video_name = Path(video_path).stem
        output_dir = f"{os.path.dirname(video_path)}/{video_name}_summary"
    os.makedirs(output_dir, exist_ok=True)
    
    # Step 1: 提取音轨
    audio_path = extract_audio(video_path, output_dir)
    
    # Step 2: 关键帧提取
    frame_files = extract_keyframes(video_path, output_dir)
    if not frame_files:
        frame_files = extract_frames_interval(video_path, f"{output_dir}/keyframes")
    
    # Step 3: 语音转文字
    transcript = transcribe_audio(audio_path)
    
    # Step 4: 视觉模型分析关键帧
    frame_descriptions = analyze_keyframes(frame_files, frame_timestamps)
    
    # Step 5: LLM 生成摘要
    summary = generate_summary(transcript, frame_descriptions)
    
    # Step 6: 导出 Markdown
    export_markdown(summary, transcript, frame_descriptions, frame_files, output_dir)
    
    return summary

运行:

python day08_video_summary.py lecture.mp4

输出目录结构:

lecture_summary/
├── video_summary.md       ← 图文笔记(直接能看)
├── summary_result.json    ← 结构化数据(程序能用)
├── audio_track.wav        ← 提取的音轨
└── keyframes/             ← 关键帧图片
    ├── frame_0001.jpg
    ├── frame_0002.jpg
    └── ...

完整的可运行代码见配套文件 day08_video_summary.py。

实测运行结果

用一段 15 秒的测试视频(sample_lecture.mp4,testsrc 画面 + 正弦音轨)跑完整条流水线,控制台输出如下:

Day08 视频摘要流水线真实运行截图

这次是真实调用智谱 API:音轨提取 ✓ → 场景检测未检出硬切(测试视频画面连续变化),自动退化为等间隔截取 30 帧 ✓ → Whisper 转录运行正常(该测试音轨是正弦波、无人声,所以转录 0 字;换成真人语音视频就能出完整字幕)→ glm-4v-flash 逐帧分析 30 个关键帧 ✓ → glm-4.5-air 生成结构化摘要 ✓ → 导出 video_summary.md。总耗时 75 秒。

环境提示:FFmpeg 需要 5.1 以上版本。ffmpeg 9 起 -vsync vfr 已移除,代码里已改用 -fps_mode vfr;关键帧 JPEG 输出加 -pix_fmt yuvj420p 避免 mjpeg 编码器对 limited-range YUV 报错。文本模型用 .env 里的 GLM_REPORTER_MODEL=glm-4.5-air,视觉模型用免费的 glm-4v-flash。

这套流水线花多少钱

项目方案成本
关键帧提取FFmpeg 场景检测(开源)¥0
语音转文字faster-whisper 本地运行¥0
关键帧视觉分析GLM-4V-Flash(永久免费)¥0
结构化摘要生成glm-4.5-air(永久免费)¥0
图片去重(可选)Pillow + imagehash(开源)¥0
本篇总计¥0

整条 Pipeline 跑一个 1 小时视频的成本是零。API 消耗估算:约 30 次视觉模型调用(每次约 100 token)+ 1 次文本模型调用(约 2000-3000 token),全在免费额度内。时间成本:1 小时视频,CPU 环境下大约需要 3-5 分钟(主要耗时在 Whisper 转录),有 GPU 的话 1-2 分钟。

从"能用"到"好用"

关键帧智能去重

场景检测有时候会截到非常相似的两帧(比如 PPT 上只改了一个字)。加一步视觉去重:

def deduplicate_frames(frame_files, similarity_threshold=0.9):
    """用图片哈希去重,删除相似的关键帧"""
    from PIL import Image
    import imagehash
    
    unique_frames = []
    last_hash = None
    
    for fp in frame_files:
        img = Image.open(fp)
        h = imagehash.phash(img)
        
        if last_hash is None or h - last_hash > 5:  # 汉明距离 > 5 算不同
            unique_frames.append(fp)
            last_hash = h
    
    return unique_frames

长视频分段处理

1 小时以上的视频,Whisper 处理会比较慢。可以先切分再并行处理:

def split_audio(audio_path, output_dir, chunk_seconds=600):
    """把长音频切成 10 分钟一段"""
    os.makedirs(output_dir, exist_ok=True)
    
    cmd = [
        "ffmpeg", "-y", "-i", audio_path,
        "-f", "segment",
        "-segment_time", str(chunk_seconds),
        "-c", "copy",
        f"{output_dir}/chunk_%03d.wav"
    ]
    subprocess.run(cmd, capture_output=True)
    
    chunks = sorted([
        f"{output_dir}/{f}" for f in os.listdir(output_dir)
        if f.endswith(".wav")
    ])
    return chunks

批量处理视频文件夹

工作室场景:甲方发来 20 个教学视频,要全部生成摘要。

def batch_video_summary(video_dir, output_base_dir):
    """批量处理文件夹中的所有视频"""
    video_exts = {".mp4", ".avi", ".mkv", ".mov", ".flv", ".wmv"}
    
    videos = [
        f for f in os.listdir(video_dir)
        if os.path.splitext(f)[1].lower() in video_exts
    ]
    
    print(f"找到 {len(videos)} 个视频文件")
    
    for i, video in enumerate(videos, 1):
        print(f"\n{'='*60}")
        print(f"[{i}/{len(videos)}] {video}")
        print(f"{'='*60}")
        
        video_path = os.path.join(video_dir, video)
        output_dir = os.path.join(output_base_dir, 
                                  os.path.splitext(video)[0])
        
        try:
            video_summary_pipeline(video_path, output_dir)
        except Exception as e:
            print(f"❌ 处理失败:{e}")

顺带看看:2026 的 AI 视频生成

这篇做的是"视频→摘要"(理解视频),反过来"文字/图片→视频"(生成视频)在 2026 年也在爆发式增长,简单聊几个:

Runway Gen-3 / Gen-4.5: 目前专业级 AI 视频生成的标杆。Gen-3 Alpha 的核心优势是"导演级控制"——Motion Brush 2.0 可以精确控制画面中每个区域的运动方向和速度,Act One 能做面部表情捕捉和迁移,摄像机控制支持推拉摇移等专业术语。2025 年底推出的 Gen-4.5 进一步支持原生 4K 输出。在影视广告领域,Runway 已经是很多工作室的标配工具。

可灵 AI(Kling): 快手旗下的 AI 视频生成平台,2026 年用户突破 6000 万,年化收入超过 3 亿美元。最新的 Kling VIDEO 3.0 Omni 实现了"文本+图片+音频"统一生成——一段视频自带同步对话和环境音效,支持 5 种语言的精准口型同步。AI Director 功能可以自动把一段长 prompt 拆分成多机位镜头,直接生成完整场景而不是单个片段。$6.99/月起步,性价比很高。

行业趋势: Netflix 2026 年二季度财报披露,今年已有约 300 部作品在制作中使用了生成式 AI,主要集中在后期制作环节。国内爱奇艺也上线了首部获发行许可证的 AIGC 网络故事片。AI 视频生成正在从"好玩的玩具"变成"真正的生产力工具"。

不过这两件事方向是互补的——"理解视频"和"生成视频"迟早会合流。下一篇(Day09)就把图文生成和视频摘要的能力结合起来,做一套完整的小红书内容生产流水线。

想练手可以改这些

按难度分三档:

入门级:

  1. 调整场景检测阈值(0.1 / 0.3 / 0.5),对比同一个视频截出来的关键帧数量和质量
  2. 把摘要的 Markdown 输出改成 HTML 格式,加上图片预览,做得更漂亮

进阶级:

  1. 给 Pipeline 加一个"精华片段剪辑"功能:根据摘要中的 key_points 时间戳,用 FFmpeg 自动剪出每段要点的前后 10 秒
  2. 实现"多视频对比摘要":喂进去同一主题的 3 个视频,生成一份综合笔记

挑战级:

  1. 把关键帧分析改成"多图批量发送"(GLM-4V-Flash 支持单条消息多张图片),减少 API 调用次数
  2. 搭一个本地 Web 界面:上传视频 → 预览关键帧 → 编辑摘要 → 导出 PDF

参考链接

  • FFmpeg 场景检测文档:FFmpeg 官方文档(select 滤镜)
  • faster-whisper:GitHub 仓库 SYSTRAN/faster-whisper
  • 智谱 BigModel(GLM-4V-Flash 免费):智谱开放平台
  • GLM-4V-Flash 视觉模型文档:智谱开放平台
  • Runway Gen-3:Runway 官网
  • 可灵 AI(Kling):可灵 AI 官网
  • 本篇配套代码:day08_video_summary.py

把这套代码里的视频路径换成你自己的文件跑一遍,10 分钟的视频 30 秒就能看完。遇到问题,评论区见。

Logo

邀请您加入社区

更多推荐