Skip to content

Commit 87b8a1e

Browse files
committed
feat(ocr): 视频会话全帧 OCR 下线——关键帧纯图(v0.12.0 M5·帧侧)
live_frame_process: - 全帧分支跳过 recognize_image_timeout(视频会话不再全帧识别画面要点)—— 关键帧纯图归档(handle_full_frame 空块走 grid-change 触发,存图不识别) - 保留 is_subtitle 路径不动(本地 OCR 只做字幕) - 区域路径无可用产出时不再整帧 OCR 兜底(回退为关键帧纯图归档; 真实画面要点经 vision-exp 精修提取) - 前台非目标窗口期间不归档(REQ-157) 依赖前置:live_keyframes 存图触发解耦(grid_diff 变化 + 2s 防抖,已提交)。 真机说明:需实拍验证——视频会话关键帧为纯图(无 OCR 文字)、画面要点经精修提取。
1 parent cc3a7c0 commit 87b8a1e

1 file changed

Lines changed: 41 additions & 54 deletions

File tree

‎app/src-tauri/src/live_frame_process.rs‎

Lines changed: 41 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -414,68 +414,43 @@ pub fn process_frame(
414414
&grid, last_changed_texts, tier,
415415
);
416416
} else {
417-
// 区域路径无可用产出(误判/空白区域/垃圾块)→ 整帧 OCR 兜底
418-
// (结构性回退链:误判/空白区域不得阻断全帧识别——真实画面文字必须仍有出口)
419-
// H2 修复:改用有界等待变体——引擎卡死时本帧超时计入 ocr_err 后
420-
// 继续下一帧(降级不阻塞整条管线)
421-
match engines.recognize_image_timeout(rgb, crate::engine::OCR_REQUEST_TIMEOUT) {
422-
Ok(blocks) => {
423-
stats.ocr_err += failed_regions as u64;
424-
stats.ocr_ok += 1;
425-
trigger.last_ocr_at = Instant::now();
426-
trigger.last_full_ocr_at = Instant::now();
427-
crate::live_keyframes::handle_full_frame(
428-
&frame, &blocks, db, app, session_id, last_full_texts, frame_samples,
429-
last_archived_text, last_archived_at, image_store, ocr_input_hash,
430-
ocr_input_dhash, ui_junk, screen_tracker, layout_changed,
431-
&grid, last_changed_texts, tier,
432-
);
433-
}
434-
Err(e) => {
435-
stats.ocr_err += 1 + failed_regions as u64;
436-
eprintln!("[ScreenWorker] 整帧 OCR 兜底失败(区域 OCR 亦无产出): {}", e);
437-
}
417+
// v0.12.0 M5(视频会话全帧 OCR 下线):区域路径无可用产出时不再整帧
418+
// OCR 兜底——关键帧纯图归档(handle_full_frame 空块走 grid-change 触发,
419+
// 存图不识别;真实画面要点经 vision-exp 精修提取)
420+
if !roi_tracker.foreground_foreign() {
421+
crate::live_keyframes::handle_full_frame(
422+
&frame, &[], db, app, session_id, last_full_texts, frame_samples,
423+
last_archived_text, last_archived_at, image_store, ocr_input_hash,
424+
ocr_input_dhash, ui_junk, screen_tracker, layout_changed,
425+
&grid, last_changed_texts, tier,
426+
);
438427
}
439428
}
440-
} else {
429+
} else if is_subtitle {
430+
// 字幕区:既有路径不变(v0.12.0 M5 只下线全帧画面要点 OCR)
441431
// H2 修复:同上有界等待——超时时走 Err 分支计 ocr_err 并继续下一帧
442432
match engines.recognize_image_timeout(rgb, crate::engine::OCR_REQUEST_TIMEOUT) {
443433
Ok(blocks) => {
444434
stats.ocr_ok += 1;
445435
// 成功识别即刷新 OCR 时刻(无论是否产出文本——防漏检兜底周期基准)
446436
trigger.last_ocr_at = Instant::now();
447-
if is_subtitle {
448-
// M2/REQ-037:bbox 回喂 ROI 跟踪器(锁定/失效判定;
449-
// 裁剪图坐标系 + 原点平移 + TD-046 缩放比反算;
450-
// REQ-084:前台非目标窗口期间 feed_ocr 内部冻结)
451-
let boxes: Vec<crate::types::TextBox> =
452-
blocks.iter().filter_map(|b| b.bbox).collect();
453-
roi_tracker.feed_ocr(&boxes, crop_origin, ocr_input_scale);
454-
// REQ-084:前台切换期间其他窗口内容不得进字幕投票器
455-
if !roi_tracker.foreground_foreign() {
456-
// REQ-087(ADR-011):UI 面板活跃期 → 字幕源头丢弃
457-
// (控制栏/弹窗文本不得进投票器/落段;原料层不动可复查)
458-
if trigger.panel.is_active() {
459-
stats.panel_filtered += 1;
460-
} else {
461-
handle_subtitle_frame(
462-
&frame, &blocks, voter, &mut trigger.scrolling,
463-
last_frame_text, last_preview, db, app,
464-
session_id, subtitle_segments, ui_junk, stats,
465-
);
466-
}
467-
}
468-
} else {
469-
// 全帧 OCR 成功时刻:带外触发冷却基准(ADR-011)
470-
trigger.last_full_ocr_at = Instant::now();
471-
// v0.7.3(REQ-157):前台非目标窗口期间画面要点不落库
472-
// (复用 REQ-084 字幕先例——根治其他窗口内容混入屏卡)
473-
if !roi_tracker.foreground_foreign() {
474-
crate::live_keyframes::handle_full_frame(
475-
&frame, &blocks, db, app, session_id, last_full_texts, frame_samples,
476-
last_archived_text, last_archived_at, image_store, ocr_input_hash,
477-
ocr_input_dhash, ui_junk, screen_tracker, None,
478-
&grid, last_changed_texts, tier,
437+
// M2/REQ-037:bbox 回喂 ROI 跟踪器(锁定/失效判定;
438+
// 裁剪图坐标系 + 原点平移 + TD-046 缩放比反算;
439+
// REQ-084:前台非目标窗口期间 feed_ocr 内部冻结)
440+
let boxes: Vec<crate::types::TextBox> =
441+
blocks.iter().filter_map(|b| b.bbox).collect();
442+
roi_tracker.feed_ocr(&boxes, crop_origin, ocr_input_scale);
443+
// REQ-084:前台切换期间其他窗口内容不得进字幕投票器
444+
if !roi_tracker.foreground_foreign() {
445+
// REQ-087(ADR-011):UI 面板活跃期 → 字幕源头丢弃
446+
// (控制栏/弹窗文本不得进投票器/落段;原料层不动可复查)
447+
if trigger.panel.is_active() {
448+
stats.panel_filtered += 1;
449+
} else {
450+
handle_subtitle_frame(
451+
&frame, &blocks, voter, &mut trigger.scrolling,
452+
last_frame_text, last_preview, db, app,
453+
session_id, subtitle_segments, ui_junk, stats,
479454
);
480455
}
481456
}
@@ -485,6 +460,18 @@ pub fn process_frame(
485460
eprintln!("[ScreenWorker] OCR 识别失败(下帧重试): {}", e)
486461
}
487462
}
463+
} else {
464+
// v0.12.0 M5(视频会话全帧 OCR 下线):全帧画面要点不再识别——关键帧纯图
465+
// 归档(handle_full_frame 空块走 grid-change 触发,存图不识别;真实画面
466+
// 要点经 vision-exp 精修提取)。前台非目标窗口期间不归档(REQ-157)。
467+
if !roi_tracker.foreground_foreign() {
468+
crate::live_keyframes::handle_full_frame(
469+
&frame, &[], db, app, session_id, last_full_texts, frame_samples,
470+
last_archived_text, last_archived_at, image_store, ocr_input_hash,
471+
ocr_input_dhash, ui_junk, screen_tracker, layout_changed,
472+
&grid, last_changed_texts, tier,
473+
);
474+
}
488475
}
489476
}
490477

0 commit comments

Comments
 (0)