Skip to content

OCR表示安定化をバッファ方式から追跡方式(OcrTextTracker)へ置き換え#648

Draft
Freeesia wants to merge 26 commits into
masterfrom
codex/issue-643-ocr-tracking
Draft

OCR表示安定化をバッファ方式から追跡方式(OcrTextTracker)へ置き換え#648
Freeesia wants to merge 26 commits into
masterfrom
codex/issue-643-ocr-tracking

Conversation

@Freeesia

@Freeesia Freeesia commented Jul 19, 2026

Copy link
Copy Markdown
Owner

概要

OcrBufferFilter(直近フレームのOCR結果を平坦化して類似検索する方式)を廃止し、テキスト領域をフレーム間で継続追跡するOcrTextTrackerに置き換える。issue #643 の設計方針に基づく実装。

主な変更

追跡処理の新規実装(IOcrTextTracker / OcrTextTracker

  • OCR観測結果を論理的な「トラック」として管理し、位置・サイズ・アスペクト比・文字列類似度・移動量などから対応候補を評価。
  • 通常対応は最小コスト割当による1対1マッチングで確定し、候補の列挙順に依存しない安定した対応付けを実現。
  • 分割(1トラック→複数OCR結果)・統合(複数トラック→1OCR結果)を候補として検出し、一時的な変化では既存表示を維持しつつ、変化が継続した場合のみ論理トラックを再構成。
  • 回転したテキスト矩形(角度付き領域)の隣接判定・結合にも対応。
  • 対応しなかったトラックは一定期間「一時未認識(Dormant)」として表示を保持し、再認識時に同じトラックへ復元。保持期間超過で破棄。
  • 位置・サイズ・文字列をそれぞれ独立して安定化し、微小変動はノイズとして吸収、確定文字列は変更候補が一定回数継続した場合のみ更新。
  • 処理位置をIOcrModule.RecognizeAsyncの直後に変更(MainViewModelBase)。DIにはScopedで登録。

旧実装・関連機能の削除

  • OcrBufferFilterIFilterModule、Levenshtein類似度によるバッファ内重複検索・振動抑制・復元処理)を削除。
  • BasicOcrParamBufferSize / IsSuppressVibe / IsEnableRecoverと、対応する全言語のリソース文字列を削除。
  • TargetSettings.IsOneShotMode(One-shotモード)と関連コード(設定画面、初回キャプチャ制御、エラーレポート出力)を削除。

テスト

  • 新規テストプロジェクトWindowTranslator.Testsを追加。
  • 静止テキストのジッター、一時的な誤認識、分割・統合、文字列変更などのシナリオテストと、96フレーム×3シードのランダム化回帰テストを追加。
  • OcrBufferFilter相当ロジック(テスト内に再実装した比較用モデル)との比較で、安定化の一致率が基本シナリオで約80%→95%以上(14ポイント以上改善)、ランダム化シナリオでも平均10ポイント以上改善することを確認。

動作確認

  • dotnet test WindowTranslator.Tests: 53件すべて成功
  • 既存CI(build / license 等)は本ブランチで成功済み

Close #643

Freeesia added 26 commits July 17, 2026 01:37
@Freeesia

Copy link
Copy Markdown
Owner Author

@copilot 説明とタイトルを日本語で記載して。
翻訳するだけではなく、内容を理解して記載して。

Copilot AI commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

@copilot 説明とタイトルを日本語で記載して。
翻訳するだけではなく、内容を理解して記載して。

タイトルと説明を日本語に更新しました。バッファ関連設定の削除だけでなく、OCRテキスト追跡をOcrTextTrackerによる安定した追跡アルゴリズムに置き換えた点も含めて、変更内容を理解した上で記載しています。

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

本PRは、従来のフレームバッファ方式(OcrBufferFilter)を廃止し、OCR直後にテキスト領域を継続追跡して安定化する方式(OcrTextTracker)へ置き換えることで、OCR表示の安定性と性能を改善しつつ、不要になった設定項目・リソースを整理し、追跡品質をテストで担保する変更です(Issue #643対応)。

Changes:

  • OcrBufferFilter を削除し、IOcrTextTracker / OcrTextTracker を追加して OCR 後段に追跡処理を導入
  • One-shot モードおよびバッファ関連パラメータ(BufferSize, IsSuppressVibe, IsEnableRecover)を削除し、設定/UI/リソース/Designer を整理
  • 追跡精度・性能特性を検証するテストプロジェクト(WindowTranslator.Tests)を追加

Reviewed changes

Copilot reviewed 59 out of 61 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
WindowTranslator/Program.cs IOcrTextTracker のDI登録を追加
WindowTranslator/Modules/Main/MainViewModelBase.cs OCR直後に追跡処理を挿入し、表示ONで追跡状態をリセット
WindowTranslator/Modules/Ocr/IOcrTextTracker.cs 追跡処理インターフェースを新規追加
WindowTranslator/Modules/Ocr/OcrTextTracker.cs テキスト領域の1対1割当て+分割/統合/復帰を含む追跡ロジックを新規追加
WindowTranslator/Modules/Ocr/OcrBufferFilter.cs 旧バッファ方式フィルターを削除
WindowTranslator/FilterPriority.cs OcrBufferFilter の優先度定義を削除
WindowTranslator/Modules/Settings/AllSettingsViewModel.cs One-shot設定の保存/表示を削除
WindowTranslator/Modules/ErrorReport/ErrorReportViewModel.cs エラーレポート出力から One-shot 設定を削除
WindowTranslator/Properties/Resources.resx One-shot のリソースキー削除
WindowTranslator/Properties/Resources.en.resx 同上(英語)
WindowTranslator/Properties/Resources.ar.resx 同上(アラビア語)
WindowTranslator/Properties/Resources.cs.resx 同上(チェコ語)
WindowTranslator/Properties/Resources.de.resx 同上(ドイツ語)
WindowTranslator/Properties/Resources.es.resx 同上(スペイン語)
WindowTranslator/Properties/Resources.fa.resx 同上(ペルシア語)
WindowTranslator/Properties/Resources.fil.resx 同上(フィリピン語)
WindowTranslator/Properties/Resources.fr.resx 同上(フランス語)
WindowTranslator/Properties/Resources.hi.resx 同上(ヒンディー語)
WindowTranslator/Properties/Resources.hu.resx 同上(ハンガリー語)
WindowTranslator/Properties/Resources.id.resx 同上(インドネシア語)
WindowTranslator/Properties/Resources.ko.resx 同上(韓国語)
WindowTranslator/Properties/Resources.ms.resx 同上(マレー語)
WindowTranslator/Properties/Resources.pl.resx 同上(ポーランド語)
WindowTranslator/Properties/Resources.pt-BR.resx 同上(ポルトガル語)
WindowTranslator/Properties/Resources.ru.resx 同上(ロシア語)
WindowTranslator/Properties/Resources.th.resx 同上(タイ語)
WindowTranslator/Properties/Resources.tr.resx 同上(トルコ語)
WindowTranslator/Properties/Resources.vi.resx 同上(ベトナム語)
WindowTranslator/Properties/Resources.zh-CN.resx 同上(中国語・簡体)
WindowTranslator/Properties/Resources.zh-TW.resx 同上(中国語・繁体)
WindowTranslator/Properties/Resources.Designer.cs One-shot のプロパティを削除し生成コードを同期
WindowTranslator.Abstractions/Modules/IOcrModule.cs BasicOcrParam からバッファ関連パラメータを削除
WindowTranslator.Abstractions/UserSettings.cs TargetSettings.IsOneShotMode を削除
WindowTranslator.Abstractions/Properties/Resources.resx バッファ関連キーを削除
WindowTranslator.Abstractions/Properties/Resources.en.resx 同上(英語)
WindowTranslator.Abstractions/Properties/Resources.ar.resx 同上(アラビア語)
WindowTranslator.Abstractions/Properties/Resources.cs.resx 同上(チェコ語)
WindowTranslator.Abstractions/Properties/Resources.de.resx 同上(ドイツ語)
WindowTranslator.Abstractions/Properties/Resources.es.resx 同上(スペイン語)
WindowTranslator.Abstractions/Properties/Resources.fa.resx 同上(ペルシア語)
WindowTranslator.Abstractions/Properties/Resources.fil.resx 同上(フィリピン語)
WindowTranslator.Abstractions/Properties/Resources.fr.resx 同上(フランス語)
WindowTranslator.Abstractions/Properties/Resources.hi.resx 同上(ヒンディー語)
WindowTranslator.Abstractions/Properties/Resources.hu.resx 同上(ハンガリー語)
WindowTranslator.Abstractions/Properties/Resources.id.resx 同上(インドネシア語)
WindowTranslator.Abstractions/Properties/Resources.ko.resx 同上(韓国語)
WindowTranslator.Abstractions/Properties/Resources.ms.resx 同上(マレー語)
WindowTranslator.Abstractions/Properties/Resources.pl.resx 同上(ポーランド語)
WindowTranslator.Abstractions/Properties/Resources.pt-BR.resx 同上(ポルトガル語)
WindowTranslator.Abstractions/Properties/Resources.ru.resx 同上(ロシア語)
WindowTranslator.Abstractions/Properties/Resources.th.resx 同上(タイ語)
WindowTranslator.Abstractions/Properties/Resources.tr.resx 同上(トルコ語)
WindowTranslator.Abstractions/Properties/Resources.vi.resx 同上(ベトナム語)
WindowTranslator.Abstractions/Properties/Resources.zh-CN.resx 同上(中国語・簡体)
WindowTranslator.Abstractions/Properties/Resources.zh-TW.resx 同上(中国語・繁体)
WindowTranslator.Abstractions/Properties/Resources.Designer.cs バッファ関連プロパティを削除し生成コードを同期
WindowTranslator.Tests/WindowTranslator.Tests.csproj テストプロジェクトを新規追加
WindowTranslator.Tests/OcrTextTrackerAccuracyTests.cs 追跡の精度/性能/互換性を検証するテストを追加
WindowTranslator.Tests/OcrTrackingAccuracyScenarios.cs シナリオベースの精度計測データを追加
WindowTranslator.Tests/RandomizedOcrTrackingAccuracyScenarios.cs 乱数シナリオ生成・精度評価を追加
WindowTranslator.sln テストプロジェクトをソリューションに追加
Files not reviewed (2)
  • WindowTranslator.Abstractions/Properties/Resources.Designer.cs: Generated file
  • WindowTranslator/Properties/Resources.Designer.cs: Generated file

Comment thread WindowTranslator.Tests/OcrTextTrackerAccuracyTests.cs
Comment thread WindowTranslator.Tests/OcrTextTrackerAccuracyTests.cs
Comment thread WindowTranslator.Tests/OcrTextTrackerAccuracyTests.cs
Comment thread WindowTranslator.Tests/OcrTextTrackerAccuracyTests.cs
Comment thread WindowTranslator.Tests/OcrTextTrackerAccuracyTests.cs
@Freeesia Freeesia changed the title Enhance OCR tracking stability, performance, and testing OCR表示安定化をバッファ方式から追跡方式(OcrTextTracker)へ置き換え Jul 19, 2026

@Freeesia Freeesia left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OcrTextTracker.csをレビューしました。

全体として非常に良くできた実装だと思います。

  • 1対1割当はハンガリアン法(JV法)による最小コスト割当で、候補の列挙順に依存しない決定的な対応付けになっている
  • 分割・統合・休眠復元はビットマスクDP(SelectStructureComponent)で資源数最大→スコア最大の順に厳密に選択され、状態数もMaxStructureSelectionStatesで制限済み
  • lockによるスレッド保護、Resetでの状態全消去、RemoveTrackTreeでの休眠子トラックの連鎖削除など、ライフサイクル管理も一貫している
  • 組合せ生成はMaxStructureCandidates=6で上限が抑えられており爆発しない

明確なバグは見つかりませんでした。インラインで数点、性能と一貫性についてコメントしています。

return -1;
}

double text = TextSimilarity(track.ConfirmedText, observation.SourceText);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

【性能】ここは「アクティブトラック×観測」の全ペアで到達するパスで、早期リターン(L472)は overlap == 0 かつ距離ゲート超過の場合だけなので、近接するペアはほぼ毎回Levenshteinを計算することになります。

TrySelectCombinedText側にはCanReachTextSimilarityによる文字列長ベースの事前除外があるのに対し、こちらには無いのが非対称です。テキスト量の多い画面(一覧UIなど)では毎フレームここが支配的になり得るので、長さ比が閾値に届かないペアはLevenshteinをスキップする同様のゲートを入れておくと安心です(L490の text < 0.15 判定は長さ比だけで先に棄却できるケースが多いはず)。

private static bool IsStrongOneToOneCandidate(MatchCandidate candidate)
=> candidate.Score >= StrongOneToOneScore
&& NormalizeText(candidate.Tracks[0].ConfirmedText)
== NormalizeText(candidate.Combined.SourceText);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

【一貫性】強い1対1判定ではNormalizeText(NFKC+空白圧縮)で比較しているのに対し、ScoreAssignment内のTextSimilarity(L477)は生文字列同士のLevenshteinです。

OCRでは全角/半角や空白の揺れが頻出するので、正規化前提のここでは一致扱いされるのに、スコア計算側では類似度が下がってMinimumAssignmentScoreを割る、という非対称が起きえます。TextSimilarity側も正規化後の文字列で比較するか、少なくともConfirmedTextを正規化済みで保持する形に揃えることを検討してもよさそうです。

.Where(track => (!track.IsDormant && track.MissedFrames > MaxMissedFrames)
|| (track.IsDormant
&& !matchedTracks.Contains(track)
&& timestamp - track.DormantSince > dormantRetention))

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

【確認】休眠中の子トラックはDormantSinceが更新されないため、統合された親が安定して表示され続けている間も、復元候補(Restore)に一度も選ばれなければ5秒(dormantRetention)で削除されます。

その後に親が再び分割された場合、子トラックのID・確定文字列は失われ新規トラック扱いになりますが、これは意図した設計でしょうか?「親が生きている間は子を保持し続ける」(=期限判定を親の生存に連動させる)方が、長時間表示後の分割で翻訳キャッシュや表示の連続性を保てる気がします。メモリ保持量とのトレードオフなので、現状の挙動が意図的ならこのままで問題ありません。

}
selected.AddRange(SelectStructureComponent(component));
}
return OrderStructureCandidates(selected).ToList();

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

【nit】SelectStructureComponentの先頭(L730)でもOrderStructureCandidatesを呼んでいるため、コンポーネント分解→選択のパスではソートが二重に実行されます。正しさには影響しませんが、ResourceCount降順など8キーの複合ソートなので、候補が多いフレームでは無駄が気になるところです。ソート済み配列をSelectStructureComponentへ渡す形にすれば1回で済みます。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OCR表示安定化をフレームバッファ方式から追跡方式へ置き換える

3 participants