2026-05-18-20-35-32 修复结果视频关键帧空白
This commit is contained in:
@@ -12,7 +12,7 @@ from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import FileResponse, Response
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from backend.segmentation import METHOD_DESCRIPTIONS, compare_frame, segment_frame
|
||||
from backend.segmentation import METHOD_DESCRIPTIONS, compare_frame, overlay_mask, segment_frame
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
@@ -213,6 +213,7 @@ def _process_video(
|
||||
frame_index = 0
|
||||
selected_count = 0
|
||||
written_count = 0
|
||||
active_mask = None
|
||||
writer = None
|
||||
raw_video_path = job_path / f"{method}_overlay.raw.mp4"
|
||||
video_path = job_path / f"{method}_overlay.mp4"
|
||||
@@ -251,6 +252,7 @@ def _process_video(
|
||||
)
|
||||
)
|
||||
video_output = next(item for item in outputs if item.method == "fusion")
|
||||
active_mask = video_output.mask
|
||||
video_frame = video_output.overlay
|
||||
selected_count += 1
|
||||
elif should_process:
|
||||
@@ -267,8 +269,11 @@ def _process_video(
|
||||
frame_index,
|
||||
)
|
||||
)
|
||||
active_mask = video_output.mask
|
||||
video_frame = video_output.overlay
|
||||
selected_count += 1
|
||||
elif active_mask is not None:
|
||||
video_frame = overlay_mask(frame, active_mask)
|
||||
|
||||
if writer is None:
|
||||
height, width = frame.shape[:2]
|
||||
|
||||
@@ -78,9 +78,18 @@ def test_segment_video_and_compare_frame(tmp_path: Path):
|
||||
assert result_capture.isOpened()
|
||||
result_frames = int(result_capture.get(cv2.CAP_PROP_FRAME_COUNT) or 0)
|
||||
result_fps = float(result_capture.get(cv2.CAP_PROP_FPS) or 0)
|
||||
result_capture.set(cv2.CAP_PROP_POS_FRAMES, 1)
|
||||
ok, carried_overlay = result_capture.read()
|
||||
result_capture.release()
|
||||
assert result_frames == 18
|
||||
assert abs((result_frames / result_fps) - payload["duration"]) < 0.25
|
||||
assert ok
|
||||
yellow_pixels = (
|
||||
(carried_overlay[:, :, 1] > 140)
|
||||
& (carried_overlay[:, :, 2] > 140)
|
||||
& (carried_overlay[:, :, 0] < 150)
|
||||
).sum()
|
||||
assert yellow_pixels > 0
|
||||
|
||||
with video_path.open("rb") as handle:
|
||||
compare = client.post(
|
||||
|
||||
19
工程分析/实现方案-2026-05-18-20-35-32.md
Normal file
19
工程分析/实现方案-2026-05-18-20-35-32.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# 实现方案
|
||||
|
||||
开始时间:2026-05-18-20-35-32
|
||||
|
||||
## 后端
|
||||
|
||||
1. 在 `_process_video` 中维护最近一次成功分割得到的 `active_mask`。
|
||||
2. 命中抽帧关键帧时:
|
||||
- 执行分割。
|
||||
- 保存该帧原图、掩膜、叠加图。
|
||||
- 更新 `active_mask`。
|
||||
- 结果视频写入该关键帧叠加图。
|
||||
3. 未命中抽帧关键帧时:
|
||||
- 如果已有 `active_mask`,将最近掩膜叠加到当前原始帧上写入结果视频。
|
||||
- 如果还没有 `active_mask`,写入原始帧。
|
||||
|
||||
## 测试增强
|
||||
|
||||
- 在视频 API 测试中读取结果视频的非关键帧,检查其仍包含黄色叠加像素,避免结果视频在关键帧之间回到纯原图。
|
||||
30
工程分析/测试方案-2026-05-18-20-35-32.md
Normal file
30
工程分析/测试方案-2026-05-18-20-35-32.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# 测试方案
|
||||
|
||||
开始时间:2026-05-18-20-35-32
|
||||
|
||||
## 自动化测试
|
||||
|
||||
- `python3 -m compileall backend tests`
|
||||
- `node --check frontend/app.js`
|
||||
- `pytest -q`
|
||||
|
||||
## 接口验证
|
||||
|
||||
- 使用内置样例视频调用 `/api/segment`。
|
||||
- 检查第 40 帧的卡片叠加图存在黄色分割像素。
|
||||
- 检查输出结果视频第 40 帧附近存在黄色叠加像素。
|
||||
|
||||
## 浏览器验证
|
||||
|
||||
- 打开页面、加载样例、运行分割。
|
||||
- 点击下方“帧 40”。
|
||||
- 确认右侧视频不再显示纯原图,而是带有分割叠加。
|
||||
|
||||
## 执行结果
|
||||
|
||||
- `python3 -m compileall backend tests`:通过。
|
||||
- `node --check frontend/app.js`:通过。
|
||||
- `pytest -q`:5 passed。
|
||||
- API 样例分割:返回 12 个结果关键帧,包含第 40 帧。
|
||||
- 后端视频像素检查:结果视频第 39、40、41 帧分别检测到 4742、5851、5710 个黄色叠加像素。
|
||||
- Chrome headless 页面验证:点击“帧 40”后,右侧视频 `currentTime=3.3333`、`duration=6`,画面检测到 5824 个黄色叠加像素。
|
||||
12
工程分析/经验记录.md
12
工程分析/经验记录.md
@@ -151,3 +151,15 @@ B. 产生问题原因:后端把已抽取并分割的结果帧直接按固定 8
|
||||
C. 解决问题方案:后端改为按原视频 `source_fps` 写完整结果视频;抽中的帧写入分割叠加画面,未抽中的帧写入原始画面;`result_fps`、`result_duration`、`result_time` 与源视频时间轴保持一致。前端双视频 seek 同步改为同一时间点同步。
|
||||
|
||||
D. 后续如何避免问题:任何面向并排对照的视频结果,都应优先保持与源视频相同时间轴;抽帧结果可以作为下方帧卡片展示,但主视频播放器不应只由抽帧结果压缩拼接。
|
||||
|
||||
## 2026-05-18-20-35-32 结果视频关键帧附近空白
|
||||
|
||||
### 1. 点击第 40 帧后右侧结果视频看起来没有叠加
|
||||
|
||||
A. 具体问题:下方结果列表中第 40 帧存在分割结果,但点击后右侧完整结果视频画面可能显示为纯原图,用户感知为“空白”。
|
||||
|
||||
B. 产生问题原因:上一版结果视频为了保持 6 秒完整时间轴,只在被抽中的关键帧写入一帧叠加图,其他帧写原始图。浏览器 seek 到关键帧附近时可能显示相邻未抽中帧,因此叠加结果只闪一帧,难以稳定看到。
|
||||
|
||||
C. 解决问题方案:后端在生成完整结果视频时维护最近一次分割掩膜;关键帧写真实叠加图,非关键帧用最近掩膜叠加到当前原始帧后写入,从而在关键帧之间持续显示分割提示。
|
||||
|
||||
D. 后续如何避免问题:当主视频播放器用于人工查看结果时,抽帧算法的关键帧结果不能只显示单帧;应使用持续叠加、插值或显式关键帧段落,保证用户拖动时能稳定看到结果。
|
||||
|
||||
17
工程分析/需求分析-2026-05-18-20-35-32.md
Normal file
17
工程分析/需求分析-2026-05-18-20-35-32.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# 需求分析
|
||||
|
||||
开始时间:2026-05-18-20-35-32
|
||||
|
||||
## 用户问题
|
||||
|
||||
用户反馈:当前选择到第 40 帧时,右侧结果视频看起来是空白或没有分割叠加。
|
||||
|
||||
## 判断
|
||||
|
||||
第 40 帧的结果卡片和后端保存的 `frame_0040_overlay.png` 本身包含分割叠加,不是算法完全未输出。问题出在右侧完整时长结果视频:上一轮为了让右侧视频保持 6 秒,只在被抽中的关键帧写入叠加画面,未抽中的帧写入原始画面。浏览器 seek 到第 40 帧附近时,可能显示相邻未抽中帧,于是看起来像空白。
|
||||
|
||||
## 期望
|
||||
|
||||
- 结果视频保持与原始视频相同 6 秒时间轴。
|
||||
- 在关键帧附近拖动或点击时,不应闪回纯原图。
|
||||
- 下方帧卡片仍展示真实被分割的关键帧。
|
||||
Reference in New Issue
Block a user