# Doco 子项目 · P1 完工快照 · 交接给 P2(OCR 阶段)

> 起草:Opus 顾问(本对话)
> 日期:2026-06-15
> 用户:制片人刘通(《军事科技》栏目 TPS 中台主理人)
> 上一份:`doco_handoff_to_next_chat.md`(P1 调试中,bug 未解)
> 状态:**P1 dry-run 已通过自检与人工验收,P1 完工**

---

## 一、一句话总结

P1(视频双路拆分预处理)的抽帧→空白过滤→去重→关键帧流水线已跑通并通过全部验收,可进入 OCR 阶段。OCR 选型方向已更正为**本地部署 DeepSeek-OCR(经 Ollama)**。

---

## 二、P1 做了什么

- **输入**:一期节目视频(黑底白字字幕 + 干净人声)+ A 稿
- **本阶段产物**:`frames/`(308 张字幕关键帧)+ keyframes 数据;音轨/B 稿是后续阶段
- **流程**:ffmpeg 抽帧(1fps,crop 到下方 20% 全宽)→ 空白帧过滤 → dHash + IoU 去重 → 关键帧
- **demo 视频**:《现代防空反导大对决》(26 分钟)

---

## 三、本轮修复的 3 个 bug(都已修好、已验收)

交接前的核心卡点是「`is_blank_frame()` 读的图 ≠ frames/ 里的图」,自检反复中止。逐层挖下去,实际是三个独立 bug:

### bug 1 · off-by-one 索引错位(核心)
- **根因**:`extract_frames()` 用 `enumerate()` 从 0 开始编号(frame_index 0-based),但下游清理逻辑和自检逻辑都从文件名提取 1-based 编号(`frame_0008.png` → 8),两套编号差 1。
- **后果**:空白帧因编号错位被错误保留;自检重读图片取到正确像素,却去查 CSV 错误的行 → 报告不一致、中止。
- **不是**"读了不同目录的图"——所有判断函数始终从同一个 frames/ 读图,是编号体系不一致。
- **修复**:改为直接从文件名提取 1-based 编号。commit `bc5a30e`

### bug 2 · 去重计数器恒为 0
- **根因**:`decision_stats[...] = decision_stats.get(..., 0)` 缺了 `+ 1`,每次迭代没递增。
- **后果**:统计永远显示"筛掉 0 张 IoU + 0 张哈希",看起来像去重没生效。**但去重逻辑本身一直是对的**,只是计数显示错。
- **修复**:加 `+ 1`。commit `d55f029`

### bug 3 · 2 个失败的单元测试(均与本次修改无关的旧断言问题)
- `test_subtitle_frame`:切片比例写错(注释说 1% 实际取了 10%),修改前就在失败。改 `arr[:100,:10]` → `arr[:10,:10]`。
- `test_ratio_ok_but_max_brightness_too_low`:亮度阈值从 200→240 后,亮度 220 的像素不再算白像素,旧断言没同步。改断言为 `white_ratio == 0.0`。
- **修复**:commit `82973a7`,17/17 全过。

---

## 四、验收数据(已核对,数字闭环)

本次 dry-run 全帧去向(1620 帧):

| 类别 | 张数 |
|---|---|
| 空白帧(blank) | 227 |
| IoU 重复(duplicate-iou) | 321 |
| 哈希重复(duplicate-hash) | 764 |
| 首帧保留(kept) | 1 |
| 关键帧(keyframe-hash) | 307 |
| **合计** | **1620** ✅ |

- 最终关键帧 = 1 + 307 = **308 张**,与 `frames/` 实际文件数一致。
- 旁证:demo 这期约 300 条字幕,关键帧 308 ≈ 字幕数,基本一对一,说明去重准确(每条字幕保留一张、砍掉重复画面)。
- **人工验收**:制片人手动翻遍 frames/,确认无任何纯黑帧。
- **单元测试**:17/17 通过。

---

## 五、遗留小尾巴(不影响完工,有空再收)

- `test_ratio_ok_but_max_brightness_too_low` 这个测试名与它现在实际测的行为对不上了(白像素阈值提到 240 后,原本想测的"白像素够但亮度不够"场景已构造不出来,断言被改成了 white_ratio==0)。建议以后让 Cline 给这个测试改名或重构。纯属测试整洁度,不影响流水线正确性。

---

## 六、重要更正 · OCR 选型(覆盖 PRD v2 的 Q1 表述)

**PRD v2 写的"DeepSeek 有 OCR 模型 + Vision 识图模式(官方 API endpoint)"——这条事实有误,作废。**

查证结果(2026-06-15,DeepSeek 官方文档):
- DeepSeek 官方 API 只有 `deepseek-v4-flash` / `deepseek-v4-pro` 两个模型,**纯文本,不收图像输入**(功能仅:文本对话 / Tool Calls / JSON 输出 / FIM)。Cline 里 DeepSeek 预设没有 image 选项是正常的,不是配置漏项。
- `DeepSeek-OCR`(3B,中文文档识别强)是**开源模型,只能本地部署**(vLLM 或 Ollama),**没有现成云端 API**,需自备 GPU。
- 因此制片人手上的 DeepSeek API key **调不了 OCR**。

**新方向(已与制片人确认)**:本地部署 DeepSeek-OCR,经 **Ollama** 运行。
- 理由:制片人有 RTX 4090D 24GB(跑 3B OCR 模型富余);零调用成本、不限量;栏目全年 52 期常态化使用,长期划算;**且制片人另有一个离线小机房,只能用本地方案,云端 API 调不通**。
- Windows 上优先 Ollama(原生支持、免 WSL),不碰 vLLM(Windows 上一般要 WSL,门槛高)。

> 候选备选(若本地部署受阻):MiniMax-VL 云端 API、讯飞 OCR。但离线小机房只能本地。

---

## 七、下一步(P2 / OCR 阶段)

1. **本地部署 DeepSeek-OCR(Ollama)**:在制片人栏目主机上搭起来,确认能识别黑底白字字幕关键帧;之后同样流程部署到离线小机房。
2. **OCR 适配层 `ocr_adapter.py`**:把 308 张关键帧批量送入本地 OCR 服务,产出带 `[Nm Ns]` 时间戳的 **B 稿 txt**(格式必须与旧版完全一致,下游 P3 才零侵入)。
3. **去掉 `--dry-run`**,跑正式 OCR,产出 B 稿 + audio_16k.wav + keyframes.json,P1 整体产物齐活。
4. 之后才进 P2 的讯飞 ASR 适配、P3 的 Claude 融合层。

---

## 八、关键决策(已锁,沿用)

| 项 | 值 |
|---|---|
| 字幕裁切区域 | `crop=iw:ih*0.2:0:ih*0.8`(全宽 × 下方 20%),写死,全年 52 期一致 |
| 抽帧密度 | 1fps |
| 哈希算法 | dHash 阈值 5,pHash 阈值 2 |
| IoU 保底阈值 | 0.95 |
| 空白帧双条件 | `max_brightness >= 240` AND `white_pixel_ratio >= 0.005` |
| 凭证管理 | doco 自管 `doco/.env`,主仓库根 `docs/api_credentials_inventory.md` 只记元信息 |
| **OCR 提供方** | **本地 DeepSeek-OCR(经 Ollama)** ← 本次更正,原 PRD 的"DeepSeek 云端 Vision API"作废 |
| ASR 提供方 | 讯飞录音文件转写标准版(P2 才用) |
| AI 融合层 | Claude Sonnet 4.6(P3 才用) |
| 抽帧/抽音轨 | subprocess 调系统 ffmpeg,不引入 ffmpeg-python |
| Git workflow | 按宪法直接推 main,不开分支 |

---

## 九、环境信息

- Windows + Python 3.12.10 + `.venv`(主项目共享虚拟环境)
- VS Code + Cline 插件(编程模型已从 Minimax M3 换为 **deepseek-v4-pro**,响应更快)
- 仓库根:`E:\tps-dashboard\`(VS Code 必须打开仓库根,不能打开子目录)
- 主仓库:`simonkoson/tps-dashboard`(自部署 git,localhost:3000)
- ffmpeg:winget 安装,系统 PATH
- GPU:**NVIDIA RTX 4090 D 24GB**(本地 OCR 部署用)
- 另有一台**离线小机房**(无外网,本地部署的主要落地环境之一)
- 正确 CLI:`doco split --episode-id XXX --input-video XXX --output-dir XXX [--dry-run]`

---

## 十、本轮 commit(按时间)

| commit | 内容 |
|---|---|
| `bc5a30e` | extract_frames:frame_index 从 0-based enumerate 改为 1-based 文件名提取(核心 bug) |
| `d55f029` | decision_stats 计数加 `+ 1`(去重计数器恒为 0) |
| `82973a7` | 修复 2 个测试断言(切片比例 + 阈值适配),17/17 通过 |

---

*P1 至此完工。下一轮从「Ollama 本地部署 DeepSeek-OCR」开始。*
