[{"data":1,"prerenderedAt":580},["ShallowReactive",2],{"content-/yomitoku-book-ocr-batch":3,"all-pages-for-dir":578,"og-image-/yomitoku-book-ocr-batch":579},{"id":4,"title":5,"body":6,"category":557,"description":558,"extension":559,"meta":560,"navigation":515,"path":561,"project_name":562,"published":563,"publishedAt":564,"seo":565,"stem":566,"tags":567,"todo":575,"updatedAt":576,"__hash__":577},"pages/2026-04/2026-04-28/yomitoku-book-ocr-batch.md","yomitoku で専門書5冊・1181ページを一気にOCRして Turso DB に1057チャンク登録した",{"type":7,"value":8,"toc":543},"minimark",[9,13,30,34,171,178,181,185,192,195,198,202,212,219,232,280,286,290,312,355,369,380,397,419,431,435,442,445,448,501,504,539],[10,11,5],"h1",{"id":12},"yomitoku-で専門書5冊1181ページを一気にocrして-turso-db-に1057チャンク登録した",[14,15,16,17,21,22,25,26,29],"p",{},"別リポジトリ book-knowledge-base で、日本語特化AI OCR の ",[18,19,20],"strong",{},"yomitoku"," を使って手元の専門書を一気に処理した。連結会計の入門書、税効果会計の教科書、財務数値ケース集、連結精算表の入門書、それから試運転として不動産業の漫画PDF。合計",[18,23,24],{},"1181ページ","を yomitoku に通し、抽出したテキストと図を ",[18,27,28],{},"Turso DB に1057チャンク登録","した。",[31,32,33],"h2",{"id":33},"処理した5冊",[35,36,37,60],"table",{},[38,39,40],"thead",{},[41,42,43,47,50,54,57],"tr",{},[44,45,46],"th",{},"#",[44,48,49],{},"種別",[44,51,53],{"align":52},"right","ページ数",[44,55,56],{"align":52},"DB登録チャンク",[44,58,59],{"align":52},"抽出図",[61,62,63,81,98,115,131,147],"tbody",{},[41,64,65,69,72,75,78],{},[66,67,68],"td",{},"1",[66,70,71],{},"不動産業の漫画（試運転10ページ）",[66,73,74],{"align":52},"10",[66,76,77],{"align":52},"-",[66,79,80],{"align":52},"12",[41,82,83,86,89,92,95],{},[66,84,85],{},"2",[66,87,88],{},"連結会計の入門書",[66,90,91],{"align":52},"352",[66,93,94],{"align":52},"270",[66,96,97],{"align":52},"121",[41,99,100,103,106,109,112],{},[66,101,102],{},"3",[66,104,105],{},"税効果会計の教科書",[66,107,108],{"align":52},"363",[66,110,111],{"align":52},"341",[66,113,114],{"align":52},"48",[41,116,117,120,123,126,129],{},[66,118,119],{},"4",[66,121,122],{},"財務数値ケース集",[66,124,125],{"align":52},"182",[66,127,128],{"align":52},"180",[66,130,77],{"align":52},[41,132,133,136,139,142,145],{},[66,134,135],{},"5",[66,137,138],{},"連結精算表の入門書",[66,140,141],{"align":52},"284",[66,143,144],{"align":52},"266",[66,146,77],{"align":52},[41,148,149,154,156,161,166],{},[66,150,151],{},[18,152,153],{},"計",[66,155],{},[66,157,158],{"align":52},[18,159,160],{},"1181",[66,162,163],{"align":52},[18,164,165],{},"1057",[66,167,168],{"align":52},[18,169,170],{},"181+",[14,172,173,174,177],{},"最初の漫画PDFは10ページのサンプルだけ流して、形式の確認に使った。",[18,175,176],{},"16秒で完走","した。1ページあたり1.6秒。Markdown 10ファイル + 図 12枚 + layout/ocr の可視化画像（バウンディングボックス付き）が一気に出てきた。",[14,179,180],{},"「漫画は縦書きセリフと効果音だらけで yomitoku が崩れるかもしれない」と身構えていたが、レイアウト認識は意外と素直に走った。コマ割りをブロックとして拾い、コマ内のセリフ枠を別ブロックとして識別して、横書きの説明テキストはそのまま読めた。縦書きセリフと効果音の擬音はさすがに崩れたが、地の文はほぼ拾えている。",[31,182,184],{"id":183},"漫画pdfのサマリー雛形を試作した","漫画PDFのサマリー雛形を試作した",[14,186,187,188,191],{},"漫画PDFで「冒頭の話を1話単位で要約してマークダウンに保存」という雛形を試した。第1話の冒頭8ページを yomitoku に通したあと、抽出したセリフとト書きを ",[18,189,190],{},"「物語の振り（フック）→ 問い → 答え → オチ」"," の4ブロックで抽象化したマークダウンを書いた。",[14,193,194],{},"書籍名は伏せておくが、「不動産業の主人公が現場で何かに気付く → なぜそうなるのかという問いが立つ → 答えが提示される → 次回への引きでオチ」という4部構造で第1話冒頭が綺麗に切れた。本一冊（数十話）を同じ雛形で要約していけば、章ごとのストーリーが横並びで読める索引になる。",[14,196,197],{},"サマリーの粒度は「ストーリーの骨格だけ残してセリフは捨てる」にした。セリフを残すと著作権の問題に近づくし、骨格だけ残しておけば「この話は何を言いたかったか」が3行で読める。",[31,199,201],{"id":200},"db登録時にぶつかった3つのエラー","DB登録時にぶつかった3つのエラー",[203,204,206,207,211],"h3",{"id":205},"_1-wal-ロックエラー-replace-で抜ける","1. WAL ロックエラー → ",[208,209,210],"code",{},"--replace"," で抜ける",[14,213,214,215,218],{},"連結会計の入門書をDB登録する途中で、",[208,216,217],{},"SQLITE_BUSY: database is locked"," が出て止まった。libSQL の Embedded Replica は、別のプロセスがWAL書き込み中だと一時的にロックされる。",[14,220,221,224,225,228,229,231],{},[18,222,223],{},"最初の対処",": 数秒待ってリトライ。多くの場合これで解消する。\n",[18,226,227],{},"それでも駄目だった時",": ",[208,230,210],{}," オプションを付けて再実行。途中まで入った中途半端なチャンクを上書きで再登録する形にした。",[233,234,239],"pre",{"className":235,"code":236,"language":237,"meta":238,"style":238},"language-bash shiki shiki-themes vitesse-light vitesse-light","# WAL ロックで途中失敗した場合\npnpm tsx scripts/register-book.ts --book=\"連結会計入門\" --replace\n","bash","",[208,240,241,250],{"__ignoreMap":238},[242,243,246],"span",{"class":244,"line":245},"line",1,[242,247,249],{"class":248},"sxvE3","# WAL ロックで途中失敗した場合\n",[242,251,253,257,261,264,268,272,275,277],{"class":244,"line":252},2,[242,254,256],{"class":255},"senZ8","pnpm",[242,258,260],{"class":259},"sdGka"," tsx",[242,262,263],{"class":259}," scripts/register-book.ts",[242,265,267],{"class":266},"snbK4"," --book=",[242,269,271],{"class":270},"sMJiu","\"",[242,273,274],{"class":259},"連結会計入門",[242,276,271],{"class":270},[242,278,279],{"class":266}," --replace\n",[14,281,282,283,285],{},"libSQL は SQLite 互換だが Embedded Replica モード特有の短時間ロックがあるので、バッチ処理の途中失敗は想定して ",[208,284,210],{}," を最初から用意しておく方がよかった。",[203,287,289],{"id":288},"_2-ファイル名の100が1ooに化けていた","2. ファイル名の「100」が「1OO」に化けていた",[14,291,292,293,296,297,304,305,308,309,311],{},"抽出した図ファイルのファイル名を一覧していたら、「",[208,294,295],{},"figure_100.png","」と書いたつもりが ",[18,298,299,300,303],{},"「",[208,301,302],{},"figure_1OO.png","」","（数字の0ではなく大文字のO、Unicode U+004F）になっていたケースを見つけた。シェルのワイルドカードで ",[208,306,307],{},"figure_1*.png"," と書くと拾えるが、",[208,310,295],{}," を文字列リテラルで指定すると当たらない。",[233,313,315],{"className":235,"code":314,"language":237,"meta":238,"style":238},"# 化けていない正規ファイル名を実物から確認\nls apps/web/public/figures/book-A/ | grep -E \"1[O0]{2}\"\n# → figure_1OO.png ← O U+004F が混入\n",[208,316,317,322,349],{"__ignoreMap":238},[242,318,319],{"class":244,"line":245},[242,320,321],{"class":248},"# 化けていない正規ファイル名を実物から確認\n",[242,323,324,327,330,334,337,340,343,346],{"class":244,"line":252},[242,325,326],{"class":255},"ls",[242,328,329],{"class":259}," apps/web/public/figures/book-A/",[242,331,333],{"class":332},"stQ0i"," |",[242,335,336],{"class":255}," grep",[242,338,339],{"class":266}," -E",[242,341,342],{"class":270}," \"",[242,344,345],{"class":259},"1[O0]{2}",[242,347,348],{"class":270},"\"\n",[242,350,352],{"class":244,"line":351},3,[242,353,354],{"class":248},"# → figure_1OO.png ← O U+004F が混入\n",[14,356,357,358,361,362,365,366,368],{},"OCR時にページ番号らしきものを連番化する処理で ",[208,359,360],{},"0"," が ",[208,363,364],{},"O"," に化けた可能性がある。実ファイル名を ",[208,367,326],{}," で吐き出して目視確認するまで気付けなかった。",[203,370,372,373,361,376,379],{"id":371},"_3-weboutputpublicfigures-が-gitignore-漏れで追跡されていた","3. ",[208,374,375],{},"web/.output/public/figures",[208,377,378],{},".gitignore"," 漏れで追跡されていた",[14,381,382,383,386,387,392,393,396],{},"Nuxt の ",[208,384,385],{},".output/public/figures"," 配下に yomitoku の抽出図がコピーされていたが、ここが ",[18,388,389,391],{},[208,390,378],{}," に入っていなかった","。",[208,394,395],{},"git status"," で181枚以上の図が「Untracked files」として並んでいて、コミット対象に紛れ込みそうになった。",[233,398,402],{"className":399,"code":400,"language":401,"meta":238,"style":238},"language-diff shiki shiki-themes vitesse-light vitesse-light","# apps/web/.gitignore\n+ .output/\n+ .output/public/figures/\n","diff",[208,403,404,409,414],{"__ignoreMap":238},[242,405,406],{"class":244,"line":245},[242,407,408],{},"# apps/web/.gitignore\n",[242,410,411],{"class":244,"line":252},[242,412,413],{},"+ .output/\n",[242,415,416],{"class":244,"line":351},[242,417,418],{},"+ .output/public/figures/\n",[14,420,421,424,425,427,428,430],{},[208,422,423],{},".output/"," 自体は Nuxt の生成物なので、ルートの ",[208,426,378],{}," で除外するのが筋。プロジェクト初期に ",[208,429,423],{}," を追加し忘れていたのが今回の遠因だった。",[31,432,434],{"id":433},"yomitoku-の処理速度","yomitoku の処理速度",[14,436,437,438,441],{},"GPU処理（CUDA）で ",[18,439,440],{},"1ページあたり約1.6秒","。1181ページを通した実測の合計時間は約30分（待機時間込み）。CPU処理だと10倍以上かかるので、GPUを積んだマシンで一気に流すのが正解だった。",[14,443,444],{},"バックグラウンド実行 + ScheduleWakeup（Windowsのタスクスケジューラからのスリープ復帰）の組み合わせで、夜間に自動で次の本を処理する運用に切り替えた。深夜にスリープから自動復帰してOCR → DB登録 → 次の本へ、という流れを5冊分パイプラインで回した。",[31,446,447],{"id":447},"学び",[449,450,451,458,467,477,495],"ul",{},[452,453,454,457],"li",{},[18,455,456],{},"yomitoku は漫画も意外と読める",": 縦書きセリフと効果音以外は、コマ内の横書きテキストと地の文をブロック単位で正しく拾う。「漫画は無理」と決めつけずに10ページサンプルで試す価値があった",[452,459,460,463,464,466],{},[18,461,462],{},"WAL ロックは少し待てば解消する",": libSQL Embedded Replica の短時間ロックは数秒で抜ける。バッチ処理側で ",[208,465,210],{}," を初手から用意しておけば、リトライが楽になる",[452,468,469,472,473,476],{},[18,470,471],{},"OCR成果物のファイル名は目視確認する",": 「100」が「1OO」になる程度の文字化けは、シェル展開では気付きにくい。",[208,474,475],{},"ls | grep"," で正規表現を当てて確認する習慣をつける",[452,478,479,487,488,491,492,494],{},[18,480,481,483,484,486],{},[208,482,423],{}," は最初から ",[208,485,378],{}," する",": Nuxt の生成物が181枚の画像と一緒に追跡対象になると、",[208,489,490],{},"git add"," の事故が起きる。プロジェクト初期化時に ",[208,493,423],{}," を入れる",[452,496,497,500],{},[18,498,499],{},"漫画サマリーは「振り→問い→答え→オチ」で骨格だけ残す",": セリフを残さず構造だけ抽出すると、章ごとの索引として使える形になる。本一冊全部やっても同じ雛形で読める",[31,502,503],{"id":503},"次にやりたいこと",[449,505,508,518,524,530],{"className":506},[507],"contains-task-list",[452,509,512,517],{"className":510},[511],"task-list-item",[513,514],"input",{"disabled":515,"type":516},true,"checkbox"," 残りの専門書（あと8冊ほど積んである）を同じパイプラインで一気に流す",[452,519,521,523],{"className":520},[511],[513,522],{"disabled":515,"type":516}," 漫画PDFの第2話以降を同じ「振り→問い→答え→オチ」雛形でサマリー化し、第1〜10話を横並びで読める索引ページを作る",[452,525,527,529],{"className":526},[511],[513,528],{"disabled":515,"type":516}," Turso DB に登録した1057チャンクに対して、全文検索（FTS5）のインデックスを張って書籍ナレッジベース側で横断検索を試す",[452,531,533,535,536,538],{"className":532},[511],[513,534],{"disabled":515,"type":516}," yomitoku の ",[208,537,210],{}," 失敗時のリトライをスクリプト側で自動化（最大3回、間隔3秒）して手動再実行を消す",[540,541,542],"style",{},"html pre.shiki code .sxvE3, html code.shiki .sxvE3{--shiki-default:#A0ADA0;--shiki-dark:#A0ADA0}html pre.shiki code .senZ8, html code.shiki .senZ8{--shiki-default:#59873A;--shiki-dark:#59873A}html pre.shiki code .sdGka, html code.shiki .sdGka{--shiki-default:#B56959;--shiki-dark:#B56959}html pre.shiki code .snbK4, html code.shiki .snbK4{--shiki-default:#A65E2B;--shiki-dark:#A65E2B}html pre.shiki code .sMJiu, html code.shiki .sMJiu{--shiki-default:#B5695977;--shiki-dark:#B5695977}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .stQ0i, html code.shiki .stQ0i{--shiki-default:#AB5959;--shiki-dark:#AB5959}",{"title":238,"searchDepth":252,"depth":252,"links":544},[545,546,547,554,555,556],{"id":33,"depth":252,"text":33},{"id":183,"depth":252,"text":184},{"id":200,"depth":252,"text":201,"children":548},[549,551,552],{"id":205,"depth":351,"text":550},"1. WAL ロックエラー → --replace で抜ける",{"id":288,"depth":351,"text":289},{"id":371,"depth":351,"text":553},"3. web/.output/public/figures が .gitignore 漏れで追跡されていた",{"id":433,"depth":252,"text":434},{"id":447,"depth":252,"text":447},{"id":503,"depth":252,"text":503},"dev","別リポジトリ book-knowledge-base で、日本語特化AI OCRの yomitoku を使って手元の専門書5冊（連結会計の入門書、税効果会計の教科書、財務数値ケース集、連結精算表の入門書、不動産業の漫画）を一気にOCRした。合計1181ページを yomitoku に通し、Markdown と図を抽出して Turso DB に1057チャンクとして登録。GPU処理で1ページあたり約1.6秒、漫画PDFも10ページ16秒で完走した。途中で WAL ロックエラーや「100」が「1OO」（U+004F のO）に化けたファイル名問題にぶつかったが、--replace オプションと実ファイル名再確認で抜けた。漫画PDFの第1話冒頭8ページでは「物語の振り→問い→答え→オチ」の構造でストーリーを抽象化するサマリー雛形を試作し、本一冊全部をかけても同じ形式で使える構造に組み立てた。","md",{},"/yomitoku-book-ocr-batch","book-knowledge-base",false,"2026-04-28T00:00:00.000Z",{"title":5,"description":558},"2026-04/2026-04-28/yomitoku-book-ocr-batch",[20,568,569,570,571,572,573,574],"OCR","Turso","libSQL","PDF","書籍","ナレッジベース","バッチ処理","memo",null,"kMMTl-4p2DNazEwZHCbhsztksmyaBX07HWVlY4aW8jg",[],"https://log.eurekapu.com/favicon.svg",1777533702989]