写真42枚をサブエージェント並列でOCR。前処理の作り直しと独立2回読みで精度を担保する
写真42枚をサブエージェント並列でOCR。前処理の作り直しと独立2回読みで精度を担保する
ダウンロードディレクトリに写真が42枚溜まっていた。 手持ちの印刷物を撮ったもので、どれも数字が並んだ一覧表が写っている。 全部OCRして数字のチェックまでやってほしい、とClaude Codeに投げた。 所要は10分か20分と返ってきた。 その程度で片づく作業だと思った。
終わってみると、その日はOCRの待ち時間ではなく前処理の書き直しでほとんど埋まった。
写真が何なのかを確かめる
同じ種類の一覧表は、別の形で既に作ってあった。 だから写真もその一部だろうと考えて、まず突き合わせさせた。
切り出して拡大すると、構造が違った。 既に作ってあった表とは別の付録で、セクションが連番で区切られ、1セクションあたりの行数も揃っていない。
だとすれば、元になった本のPDFに同じ付録が入っているかもしれない。 写真から起こすより、PDFから直接取るほうが確実だ。 探させた。 載っていなかった。
OCRの完了を待つ間に表の出所を調べさせていたので、手持ちの資料だと伝えた。 連番は途中で切れておらず、端から端まで一続きで揃っている。
この時点で、照合する相手がいないことが決まった。 デジタルの原本はなく、本のPDFにも同じ表はない。 OCRの結果が正しいかどうかをOCRの外側から確かめる手段がない状態で、この作業を始めることになる。
紙面の切り出し
OCRに渡す前に、紙面を切り出して傾きを直し、解像度を上げる前処理を書かせた。 ここで何度も止まった。
最初の版は紙全体を切り出してしまい、文字が小さいままだった。 彩度が低く明度が高い領域を先に紙面として検出し、そこから表を切り出す方式に変えさせた。
次は、表の領域を求める関数の初期値がバグで、どの画像でも常に全域を返していた。
その次は二値化の閾値が強すぎて、数字が消えて表のマーカー線だけが残っていた。 閾値を70%まで、幅を600pxまで上げると、紙と文字がきれいに分かれた。 照明のムラを補正する処理を足して、試した3枚とも表全体が取れた。
それでも42枚全体では自動切り出しが安定しない。 最後に判定基準そのものを変えさせた。 「黒い画素がどれだけあるか」ではなく、「1行を横になぞったとき白と黒が何回入れ替わるか」で表領域を決める。 文字の並びと塗りつぶしは、この数え方なら混ざらない。
42枚とも切り出しが揃ったのは、この変更のあとだった。
サブエージェント42本の並列OCR
写真の枚数だけエージェントを立てて並列で読ませた。 1回目は写真リストの渡し方でこけた。 引数が文字列化されて届いていたので、リストをスクリプトの中に直接持たせて投げ直した。
10〜20分という見積もりは外れた。 走らせてみると、完了は数時間先になった。 エージェントが1体あたり数十回、判読に迷ったセルを拡大して見に行っていたからだ。 速い処理を42本並べたのではなく、1枚に時間をかける処理を42本並べていた。
ネットが切れても次のセッションが読めば再開できるように、作業ログのドキュメントを先に作らせた。
結合と重複の除去
41枚が揃った時点で、残り1枚を待ちながら結合と検算を先に流させた。
まず落ちた。
OCRは判読できなかったセルを null で返してくるのに、スクリプトがその値を想定していなかった。
直して流し直すと、今度は重複が904件出た。 紙をずらしながら撮っているので、隣り合う写真は数行ぶん重なる。 隣接する行どうしの比較で消そうとしていたが、重なるのは1行ではなく数行のかたまりだった。 ブロック単位で畳むように直させた。
行の先頭が抜けたページ
42枚が出揃ったあと、エージェントの報告から前処理の欠陥が見つかった。 ページによっては、各行の先頭にあるはずの数字が入っていない。
傾き補正が紙の左端をキャンバスの外へ押し出したのだろう、と最初は考えた。 違った。 傾いたページに対して切り出し枠が狭すぎただけだった。 列の判定を緩めて横方向に余裕を持たせると、左端が全部入った。
42枚を作り直してもう一度OCRにかけた。 やり直しではあるが、1回目とは独立した2回目の読みにもなる。
数字の規則を検算に使えるか
2回目が走っている間に、1回目のデータで数字の性質を調べさせた。 各行の数字が一定の幅で増えていくのなら、その規則を検算に使える。
規則から外れた行が、あるセクションの先頭近くにあった。 拡大して確かめさせると、印刷物のほうが本当にそうなっていた。 昇順は厳密な法則ではなく、弱い手がかりにすぎない。
これで検算の当てが一つ消えた。 デジタルの原本はない。 本のPDFにもない。 数字の規則にも頼れない。 残ったのは、独立に2回読ませて一致を見ることだけだった。
2回目を待つ間に、1回目と2回目を突き合わせるスクリプトを書かせた。 あわせて、最終的なCSVと閲覧用HTMLを作るスクリプトも用意させた。 進捗を聞き返したところで、月の利用上限に当たってセッションが切れた。
この日に決まったこと
- 紙の写真から表を起こす仕事で時間を食うのは、OCRそのものではなく前処理だった。切り出しが決まるまでに書き直した回数のほうが、OCRを待った回数より多い。
- 判定基準を「量」から「切り替わりの回数」に変えたところで一気に安定した。同じ画像を見ていても、何を数えるかで結果が変わる。
- 原本がない資料では、中身の規則性を検算に使えるとは限らない。使えるかどうかは、規則を仮定したうえで外れ値を実物に当たって確かめるまで分からない。
- 42本の並列は速さのためではなく、1枚あたりに時間をかけるための構成だった。10〜20分という見積もりをそのまま受け取ったのは、その性質を読み違えていたからだ。
残っている作業
- 2回目のOCR結果を1回目と突き合わせる
- 一致しなかったセルを拡大して確定させる
- CSVと閲覧用HTMLを生成する