[{"data":1,"prerenderedAt":233},["ShallowReactive",2],{"content-/handwritten-table-images-to-csv":3,"all-pages-for-dir":211,"related-/handwritten-table-images-to-csv":212,"og-image-/handwritten-table-images-to-csv":232},{"id":4,"title":5,"body":6,"category":191,"concepts":192,"description":193,"extension":194,"meta":195,"navigation":196,"ogImage":192,"path":197,"project_name":198,"published":199,"publishedAt":200,"seo":201,"source":202,"stem":203,"tags":204,"todo":192,"unpublished":199,"updatedAt":192,"__hash__":210},"pages/2026-09/2026-09-16/handwritten-table-images-to-csv.md","手書き数値表42枚のOCRで、マルチモーダルLLMをレート制限ごとに乗り換えた記録",{"type":7,"value":8,"toc":178},"minimark",[9,13,16,19,24,27,30,33,36,39,47,50,53,56,59,62,65,68,71,74,77,80,83,86,89,92,95,98,101,104,107,110,117,120,123,126,129,137,141,147,150,153,156,159],[10,11,12],"p",{},"地域密着のサービス業の記録を電子化する作業を続けている。\n紙に手書きで残っていた数値表を撮った画像が42枚あり、これを機械が読める CSV に起こしたい。\n前日は Gemini 3.1 Pro に読ませて9枚まで進み、429 が返ったところで止まっていた。",[10,14,15],{},"朝いちばんに思ったのは、モデルを最新の Flash に替えれば枚数が伸びるだろう、ということだった。\nレート制限も一晩たてば解除されているはずだ、と踏んでいた。",[10,17,18],{},"外れた。",[20,21,23],"h2",{"id":22},"一晩で枠は戻っているはずという読み","一晩で枠は戻っているはず、という読み",[10,25,26],{},"疎通の1発目が2分待っても返ってこない。\n枠が切れているのか、単にモデルが遅いのかを切り分けさせたら、制限はまだ効いたままだった。\n回復予定は3日後の朝。",[10,28,29],{},"待つと決めた。\nただし枠が開いた瞬間に動けるよう、Flash 対応と枠切れの早期検知をスクリプトへ入れさせておいた。\n枠が切れている今なら、その検知が本当に働くかを実地で確かめられる。\n走らせたら、想定どおり止まった。",[10,31,32],{},"ここで気になったのは、確かめ方そのものだった。\n叩いて確かめる行為は、それ自体が枠を1回食う。\n公表されているトークン単価と画像の実寸から計算できるなら、消費せずに見積もれるはずではないか。\nそう聞いてみた。",[20,34,35],{"id":35},"実測せずに枠を見積もれないか",[10,37,38],{},"画像は 2268x4032 で、ここからトークン数は出せる。\nただし肝心なのは、使っている CLI 側の枠が何で測られているかだ。\n公表仕様を当たらせたところ、試算とは別のものが引っかかった。",[10,40,41,42,46],{},"モデル一覧が「Gemini」と「Claude / GPT」で節に分かれている。\n枠も分かれているのではないか、という見立てが立った。\n叩かせたら、",[43,44,45],"strong",{},"Gemini 系だけが切れていて、Claude / GPT 系は生きていた","。",[10,48,49],{},"基準にしている1枚を Sonnet 4.6 に読ませると、136件すべてが正解データと一致した。\n2分弱で終わっている。\n残り33枚を投入した。\n1枚あたり約35秒で流れていく。",[10,51,52],{},"14枚を読んだところで、朝入れておいた枠切れ検知が働いて止まった。\nそれでも合計は23枚まで来ている。",[10,54,55],{},"途中、1枚だけ278件と突出した画像があった。\n他は130件前後なので、読み間違いを疑った。\n中身を確認させたら、同じ CSV を2回出力していただけだった。\n2ブロックは完全に一致していて、データとしては無傷だった。\nまとめスクリプトを重複ブロックに対応させて先へ進めた。",[20,57,58],{"id":58},"枠は世代ごとにも分かれているのか",[10,60,61],{},"Gemini 側の枠は、世代ごとにも分かれていた。\n3.8 Flash と 3.1 Pro は枯れているのに、3.7 と 3.6 の Flash は生きている。\n前日に立てた「アカウント共通の枠」という見立ては、そもそも粗すぎた。",[10,63,64],{},"3.7 Flash にも基準の1枚を読ませたら、こちらも136件すべてが一致した。\n残り19枚を回させた。",[10,66,67],{},"ここで実装ミスが出た。\n処理済みディレクトリの指定に Sonnet 分を入れ損ねていて、すでに読み終えた14枚を 3.7 Flash が読み直している。\n完走を待ってから直すことにした。\n捨てるつもりだった重複が、結果として別系統2モデルの突き合わせ材料になる。",[20,69,70],{"id":70},"一致率3分の1の正体",[10,72,73],{},"突き合わせてみると、件数は同じなのに一致が3分の1しかない画像があった。",[10,75,76],{},"最初に疑ったのは並び順だった。\nこの表は先頭の数字の昇順で印刷されているように見えるので、片方だけ並びが崩れているのではないか。\n機械的に全数チェックさせたら、この仮説は消えた。\n目視で作った正解データ自身にも同じ逆行が出てくるので、順序は判定基準に使えない。",[10,78,79],{},"順序を無視して中身だけを比較する形に切り替えた。\n結果は、1,883件すべてで中身が一致した。\n違いは並び順だけで、残る食い違いは1枚の7件にとどまった。\n独立に読ませた2モデルが同じ答えを返したのだから、読み取り精度の裏付けとしては強い。\nこの比較をその場かぎりにせず、正式なスクリプトとして残させた。",[10,81,82],{},"3.6 Flash も 3.7 と同じプールで、すでに枠が切れていた。\n使えるモデルが尽きて、35枚で打ち止めになった。\n通し番号は0から83まで連番で埋まっている。\n残り7枚は3日後の朝を待つしかない。",[20,84,85],{"id":85},"残り7枚をブラウザから入れる",[10,87,88],{},"画像ファイルのフルパスを聞いて、HEIC ではなく JPEG に変換したディレクトリを作らせた。\n42枚をまとめて変換し、全部が正常に読めることまで確認させている。",[10,90,91],{},"そこから1枚を自分でブラウザの AI Studio に貼り、出てきた CSV をセッションへ貼り付けた。\n突き合わせさせたら、手元にあった4つ（目視で作った正解データを含む）すべてと136件が一致した。\n一致率100%。",[10,93,94],{},"なら、この貼り付け作業自体を渡してしまえばいい。\n自分が手で貼るのと、ブラウザを操作させるのとで、手間は大して変わらない。\n気になったのはコンテキストのほうで、画像を7枚も扱ってセッションが重くならないかと聞いた。",[10,96,97],{},"杞憂だった。\n画像は読ませるのではなくブラウザへアップロードするだけで、返ってくるのはテキスト136行、1枚あたり2KB程度でしかない。\nむしろコピー＆ペーストより、DOM から直接抜いたほうが取りこぼしがないという。",[10,99,100],{},"自分が開いていたタブは触らせず、設定だけ揃えた新しいタブで進めさせた。\nファイル入力が隠れているので一時的に見えるようにする、バックグラウンドタブではメニューが開かないので前面に出す、といった細かい迂回が続く。",[20,102,103],{"id":103},"送信のたびに403が返る",[10,105,106],{},"画像を入れてプロンプトを送ったら、内部エラーが返った。\nOCR に不要な検索グラウンディングが干渉している疑いで切らせたが、2回とも同じところで落ちる。",[10,108,109],{},"ネットワークとコンソールを見させて、原因が出た。\n生成リクエストが 403 で、中身は権限がないという応答だった。\n画像がサーバー側に登録されていない疑いが出たので、アップロードの通信まで遡らせたら、そちらは200で成功している。",[10,111,112,113,116],{},"権限の伝播待ちを疑って、時間を置いてから再実行させたら通った。\n",[43,114,115],{},"アップロード直後の1回目は必ず403で、再実行すれば通る","。\n癖が分かってしまえば、あとは同じ手順の繰り返しだった。",[10,118,119],{},"7枚が順に片付いて、42枚すべてが揃った。\n通し番号は0から99まで欠けがなく、範囲外の数字も出ていない。\n合計5,666件で、既存の CSV より388件多い。\nつまり、これまで拾えていなかった行がその分あった。",[20,121,122],{"id":122},"差分277件の出どころ",[10,124,125],{},"上書きはしたくない。\n日付を入れた新しいファイルとして残し、目視で確認できる正式版を1本作ってほしい。\n差分も先に見ておいてほしい、と頼んだ。",[10,127,128],{},"差分を掘らせると、既存にしかない277件が特定の画像へ集中していた。\nその画像では、既存 CSV が通し番号を取り違えている。\nある番号に80件、その次の番号に27件が入っていて、手前にあるはずの番号が存在しない。\n件数の足し算も整合した。\n既存が正しくて新規が抜けているのではなく、既存側の誤りだった。",[10,130,131,132,136],{},"ところが、出てきたファイルを開いても、どれが正式版なのか分からない。\n比較用の列まで並んでいて、5000行を目で追う形になっていなかった。\n列を削ぎ落としたものを作り直させた。\nExcel で前のファイルを開いたままだったので、閉じずに済むよう頭に ",[133,134,135],"code",{},"final"," を付けた別ファイルにしてもらった。",[20,138,140],{"id":139},"_1行だけずれて見える","1行だけずれて見える",[10,142,143,144,146],{},"その ",[133,145,135],{}," を Excel で開いたら、見出しのあたりが1行ずれて見える。\nスクリーンショットを撮って貼り、ずれていないかと聞いた。",[10,148,149],{},"ずれではなく文字化けだった。\nBOM が無いので Excel が UTF-8 と認識せず、見出し行が壊れて表示されていた。\nデータ自体は最初から正しい。\n既存の CSV も BOM なしのままだった。",[10,151,152],{},"BOM を付けて CRLF に統一し、全行が6列になっていることを確認させた。\n確認の仕方も、画面を開いて目で見るのではなく Excel 自身に読ませる形にしてある。\n行数も列数も正しく、5667行×6列と出た。\nPowerShell 経由では日本語が化けるので、Excel が読んだ値を UTF-8 で書き出させて文字まで確かめた。",[10,154,155],{},"最後にコミットとプッシュまで回して、今日の分は終わり。",[20,157,158],{"id":158},"学び",[160,161,162,166,169,172,175],"ul",{},[163,164,165],"li",{},"利用枠はアカウント単位とは限らない。今回はモデルの系統（Gemini 系と Claude / GPT 系）でも、Gemini の世代（3.8 と 3.7）でも分かれていた。1つが429を返しても、全部が止まったとは限らない",[163,167,168],{},"同じ画像を別系統の2モデルへ独立に読ませると、それだけで精度の裏取りになる。今回の材料は、処理済み判定の実装ミスが生んだ読み直しだった",[163,170,171],{},"一致率が低いときは、データより先に比較の仕方を疑う。並び順の違いを中身の違いとして数えていた",[163,173,174],{},"ブラウザ越しに別の LLM を使わせても、返ってくるのがテキストならコンテキストはほとんど食わない。1枚2KBなら7枚でも軽い",[163,176,177],{},"Excel で開いた CSV の「行がずれて見える」は、BOM が無いだけのことがある。データを直す前に文字コードを見る",{"title":179,"searchDepth":180,"depth":180,"links":181},"",2,[182,183,184,185,186,187,188,189,190],{"id":22,"depth":180,"text":23},{"id":35,"depth":180,"text":35},{"id":58,"depth":180,"text":58},{"id":70,"depth":180,"text":70},{"id":85,"depth":180,"text":85},{"id":103,"depth":180,"text":103},{"id":122,"depth":180,"text":122},{"id":139,"depth":180,"text":140},{"id":158,"depth":180,"text":158},"dev",null,"手書きの数値表を撮った画像42枚をCSVに起こす作業。モデルの利用枠が尽きるたびに別系統のLLMへ乗り換え、2モデルの独立読み取りで精度を裏取りし、最後はブラウザ越しに残りを片付けた一日。","md",{},true,"/handwritten-table-images-to-csv","komatsu-sos",false,"2026-09-16T00:00:00.000Z",{"title":5,"description":193},"make-diary","2026-09/2026-09-16/handwritten-table-images-to-csv",[205,206,207,208,209],"OCR","マルチモーダルLLM","Gemini","CSV","レート制限","pXPFbDS6K9II0k9ZolsdxHYMDWZ94MTr5cbpDkXDKI0",[],[213,217,221,225,228],{"title":214,"path":215,"publishedAt":216},"表の画像をCSVに起こすOCRを、Claude CodeとGemini 3.1 Pro（agy）で突き合わせた記録","/image-table-ocr-two-model-crosscheck","2026-09-15T00:00:00.000Z",{"title":218,"path":219,"publishedAt":220},"税務アシスタント開発ログ - 新規クライアント対応・レシートOCR100件超・税区分トグル実装","/tax-assistant-tax-category-2026-02-20","2026-02-20T00:00:00.000Z",{"title":222,"path":223,"publishedAt":224},"手書き伝票のOCRを安いモデルで試した記録：DeepSeek と Gemini の同条件ベンチと、Gemini Flash での全件照合","/cheap-ocr-model-bench-process","2026-09-18T00:00:00.000Z",{"title":226,"path":227,"publishedAt":224},"手書き伝票のOCRで、DeepSeek V4.1 Flash と Gemini 3 Flash を同じプロンプトで比べた","/deepseek-vs-gemini-handwritten-slip-ocr",{"title":229,"path":230,"publishedAt":231},"税務アシスタント外貨建てレシート対応 - アルゼンチンペソの$問題からis_foreign_currencyフラグのフルスタック実装まで","/foreign-currency-receipt-2026-02-27","2026-02-27T00:00:00.000Z","https://log.eurekapu.com/og/blog/handwritten-table-images-to-csv.png?v=2026-09-16T00%3A00%3A00.000Z&title=%E6%89%8B%E6%9B%B8%E3%81%8D%E6%95%B0%E5%80%A4%E8%A1%A842%E6%9E%9A%E3%81%AEOCR%E3%81%A7%E3%80%81%E3%83%9E%E3%83%AB%E3%83%81%E3%83%A2%E3%83%BC%E3%83%80%E3%83%ABLLM%E3%82%92%E3%83%AC%E3%83%BC%E3%83%88%E5%88%B6%E9%99%90%E3%81%94%E3%81%A8%E3%81%AB%E4%B9%97%E3%82%8A%E6%8F%9B%E3%81%88%E3%81%9F%E8%A8%98%E9%8C%B2&author=Kei%20Komatsu&sig=06c7e9bdd4451f08",1789885490382]