← ガイダンス一覧へ戻る

1画像テキスト抽出

元表現: 写真読み取り係 / StudyGuide ブロック詳細

写真に写っている英文を、後段が扱える1本のテキストにして返すブロック。11工程のうち、外部の言語モデルに任せているのは2つだけです。

抽象度 Lv.4(技術用語+システム上のやりとりまで/製品名・ライブラリ名は書かない)

1. 概要

写真に写っている英文を、後段が扱える1本のテキストにして返すブロック。 やっていることは「文字を読む」だけではない。何枚目が本編で何枚目が続きかを決め、 洋書か英語教材かを判定し、教材なら日本語を落とし、 自信が持てなければ設定を変えてもう一度読み直すところまでが、この1ブロックの中に入っている。

外部の言語モデルを使う4か所のうちの1つ。ただしモデルに投げているのは工程の一部で、 前後は決まった手順の処理で挟まれている。

2. 位置づけ

利用者(写真の送信) → 1. 画像テキスト抽出 → 人による確認・修正 → 2. 処理オーケストレーション

3. 処理内容(Lv.4)

4. なぜこの位置にあるか(設計意図)

ここより後ろには置けない。後段(2・3・6・8)はすべて「1本のテキスト」を前提に組まれている。 写真のままでは、文に分けることも、語句を数えることも、音声にすることもできない。

ここより前に人の確認を置くこともできない。読み取る前の写真を人が見ても、 機械が何をどう読み違えるかは分からない。だから確認画面は1番の直後にある。

やり直しをこのブロックの中で完結させているのも意図的。読み取りの品質が悪いまま先へ流すと、 誤字が読解ガイドにも単語テストにも音声にも伝わり、全部を作り直すことになる。 外に出す前に、このブロックの中で2回粘る作りになっている。

5. 詳細プロセス分解

工程主体やりとりの内容方向
1-1画面 → 入口処理写真1〜4枚と、紙面の種類の指定
1-2入口処理(内部)認証の確認と、枚数・形式・容量の検査
1-3入口処理 → 保存領域元画像を控えとして書き出し、鍵を受け取る
1-4入口処理 → 読み取り本体画像一式、枚数、紙面の種類
1-5読み取り本体(内部)向きの補正・白埋め・縮小・形式変換を1枚ずつ
1-6読み取り本体 → 読み取りモデル縮小版の画像+役割の指示+受け取る書式の指定
1-7読み取りモデル → 読み取り本体本文・続き・ページ番号・自信のない箇所
1-8読み取り本体(内部)種類の確定/続きの一文の連結/日本語除去/やり直し判定
1-9読み取り本体 → 読み取りモデル(必要なときだけ)縮小前の元画像で読み直し
1-10読み取り本体 → 入口処理確定した本文一式
1-11入口処理 → 画面本文・ページ番号・自信のない箇所・画像の鍵

各行を文章にすると

1-1 利用者が画面から写真を送る。同時に「おまかせ/洋書/英語教材」のどれかが一緒に送られる。何も選ばなければ「おまかせ」になる。

技術的には ブラウザは POST /api/extractmultipart/form-data で送る。これはファイルと文字を1つの送信にまとめる形式で、区切り文字列で各パートを分ける。画像のようなバイナリは application/json にそのままは入れられないため、フォームでファイルを送るときはこちらを使う。パートは images(画像ファイル、複数可)と contentModeautobookmaterial)の2種類。GETではなくPOSTなのは、送る量が大きく、かつサーバー側に処理を起こす(=副作用がある)操作だから。

1-2 入口処理は、まず送り主が認証を通っているかを確かめる。次に、枚数が1〜4枚か、画像の形式が受け付けられるものか、1枚が大きすぎないかを順に見る。このやりとりは外に出ない。 はそういう意味で、他の部品に何も送らず、自分の中だけで合否を決める。ここで弾かれた場合、読み取りは一度も始まらない。

技術的には まずリクエストの Cookie ヘッダから署名付きの値を取り出し、定数時間比較で照合する。無効なら本文を読む前に 401 Unauthorized を返す。続いて種類の指定が決められた3語か、枚数が1〜4枚か、各ファイルの種類が image/jpegimage/pngimage/webp のどれかか、サイズが12MB以下かを順に見る。1つでも外れたら 400 Bad Request400と401の使い分けは、400が「送ってきた内容が悪い」、401が「あなたが誰か分からない」。

1-3 検査を通った画像を、そのまま保存領域へ書き出す。これは読み取りのためではなく、あとから元の紙面を見返すための控え。書き出すと画像ごとに鍵(呼び出すための名前)が返り、これが最後まで持ち回られる。

技術的には 1回の送信ごとにランダムなID(UUID)を1つ発行し、それと連番と拡張子から保存名を組み立てる。書き込みは、隣に一時ファイルを作って書き切ってから 名前を付け替えて(rename)本番の名前に差し替える方式。名前の付け替えは途中の状態が存在しない操作なので、読み手が半分だけ書かれたファイルを読むことがない。ここで付いた名前が「鍵」として返る。

1-4 入口処理が読み取り本体を呼ぶ。渡すのは画像一式と、枚数と、紙面の種類の指定。「何枚あるか」を別に渡しているのは、続き用ページの扱いを枚数で決めるため。

技術的には ここから先はHTTPではなく、同じサーバープロセスの中での関数呼び出し。ネットワークを通らないので、形式の変換も認証のやり直しも起きない。渡すのは画像のバイト列と種類、枚数、紙面の種類の指定。

1-5 読み取り本体は、モデルへ送る前に画像を整える。横倒しの写真を起こし、透けている部分を白で埋め、長辺を決まった大きさまで縮め、形式をそろえる。これも自分の中だけの処理。1枚ずつ順番にやっているのは、複数枚を同時に処理してPC全体の負荷を一気に上げないため。

技術的には EXIF(写真に埋め込まれた撮影情報)の向きタグを読んで回転を打ち消し、透明な部分を白で合成し、長辺が2048ピクセルに収まるよう縮小(元が小さければ拡大はしない)、品質90のJPEGに再符号化する。色情報の間引き(クロマサブサンプリング)は 4:4:4=間引かない設定。文字は色の境目で読み取るので、色情報を半分に減らす一般的な設定だと細い字がにじむため。1枚ずつ直列に処理して、CPUとメモリの山を低く抑えている。

1-6 整えた画像を、役割の指示と一緒に外部の読み取りモデルへ送る。役割の指示とは「洋書なら最後の1枚だけが続き用、それ以前が本編」「教材なら全部が本編で日本語は入れない」といった、紙面の種類ごとの読み方の説明。このとき受け取る書式もあらかじめ指定する。自由な文章ではなく、決まった項目に分けて返させる。

技術的には HTTPSでJSONを送る。画像は base64(3バイトを4文字に置き換える符号化。約1.33倍に膨らむ)にして data:image/jpeg;base64,... という形で本文に埋め込む。同時に返してほしいJSONの型も送る。型で縛るので返答は必ずその形になり、自由な文章を正規表現で切り出すような不安定な処理が要らない。1回目は「推論にかける手間=低」「画像の細かさ=高」。

1-7 モデルから、本文・続き部分・ページ番号・自信の持てなかった箇所が返る。「自信のない箇所」を返させているのが要点で、これが後の判断材料になる。

技術的には 型どおりのJSONが返る。項目は、本文・続き・ページ番号・真偽値(最後が文の途中で切れているか)・配列(自信のない箇所)。型に合わない返答はこの時点で解析に失敗するので、想定外の形が次の工程へ流れることはない。

1-8 返ってきたものを、決まった手順で組み立て直す。紙面の種類を確定し(画面で明示されていればモデルの判定より優先する)、続き用ページからは最後の未完の一文を完成させる分だけを足し、教材なら日本語を機械的に取り除く。そのうえで、自信のない箇所があるか、判読不能の印が混ざっていないか、最後の文がきちんと終わっているかを見て、やり直すかどうかを決める。

技術的には 日本語の除去は、正規表現でひらがな・カタカナ・漢字を文字の種類ごとにまとめて指定して置き換える。文が終わっているかの判定も正規表現で、. ! ? のあとに閉じ括弧や引用符が続く形まで見る。続きの一文の切り出しには言語対応の文分割器を使い、最初の1文だけを取る。Mr. のように略語のピリオドがあるので、単純に「.で切る」やり方では壊れてしまうため。

1-9 やり直すと決めた場合だけ、もう一度モデルへ送る。今度は縮小していない元の画像を使い、読み取りの力を一段上げる。この矢印は毎回は出ない。2回目でも最後の一文が閉じなければ、そこで止めてエラーにする。

技術的には 縮小前の元画像を、「推論にかける手間=中」「画像の細かさ=原寸」で送り直す。1回目の結果は捨てて、組み立てを最初からやり直す(差分を直すのではない)。だからこの経路に入ると、時間も費用もおよそ2回分かかる。

1-10 確定した本文一式を入口処理へ返す。

技術的には 関数の戻り値として返るだけ。ここでも通信は発生しない。

1-11 入口処理が画面へ返す。本文とページ番号のほかに、自信のなかった箇所と、1-3で保存した画像の鍵も一緒に返る。鍵を返すのは、このあと2番へ進むときに「どの写真から作ったページか」を持ち回るため。

技術的には 200 OKapplication/json で返す。中身は、紙面の種類・ページ番号・本文・自信のない箇所の配列・画像の鍵の配列。ブラウザはこれを受け取って確認画面を描く。

← 横にスクロールできます / 縦の点線=その担当が動いている時間、横矢印=やりとり、折り返し=自分の中だけで終わる処理

画面
入口処理
読み取り本体
読み取りモデルAI利用
保存領域
1-1 写真1〜4枚と、紙面の種類の指定
1-2 認証の確認と、枚数・形式・容量の検査
1-3 元画像を控えとして書き出し、鍵を受け取る
1-4 画像一式、枚数、紙面の種類
1-5 向きの補正・白埋め・縮小・形式変換を1枚ずつ
1-6 縮小版の画像+役割の指示+受け取る書式の指定
1-7 本文・続き・ページ番号・自信のない箇所
1-8 種類の確定/続きの一文の連結/日本語除去/やり直し判定
1-9 (必要なときだけ)縮小前の元画像で読み直し
1-10 確定した本文一式
1-11 本文・ページ番号・自信のない箇所・画像の鍵

6. この分解から見えること

  • モデルに任せているのは11工程のうち2つだけ(1-6/1-7、必要なとき1-9)。 残りは検査・整形・組み立て・判定で、すべて決まった手順の処理。 「AIが写真を読んでいる」と一言で言うと見えなくなるが、実際にはAIの前後を機械で固めている。
  • 自信のなさを、モデル自身に申告させて使っている。1-7で「自信のない箇所」を返させ、 1-8でそれを見てやり直すかどうかを決める。人が確認画面で気付く前に、機械が一度自分で疑う仕組み。
  • 1回目はわざと手を抜いている。1-5で縮小した画像を、弱い設定で読む。 そのぶん速くて安い。ダメだったときだけ元画像と強い設定に切り替える。 毎回いちばん強い設定で読む作りに比べ、普通に読める紙面の速さを優先した設計になっている。
  • 元画像の保存(1-3)は、読み取りが失敗しても先に済んでいる。 順番がこうなっているので、読み取りに失敗した写真も保存領域には残る。 これが「使われなかった画像が溜まる」原因になり得る。
  • 紙面の種類の判定には、人が上書きできる余地が残されている。1-8で、 画面での明示指定がモデルの自動判定に優先する。自動判定が外れる紙面があることを前提にした作り。

7. 失敗時の挙動(未確認あり)

どこで失敗するか何が起きるか
1-2 の検査400。読み取りは始まらない。画面に理由(枚数・形式・容量のどれか)が返る。認証が無ければ 401
1-6/1-7 の読み取り500「写真から英文を読み取れませんでした」。1-3で保存した画像は残る
1-8 で本文が空500「写真から英語の本文を読み取れませんでした」
1-9 でも一文が閉じない500。「次のページが正しく選ばれているか」を促す専用の文言が返る
いずれの場合も2番以降は一切走らない。ページは作られない

画面側でこれらのエラーがどう見えるか(やり直しの導線があるか)は未確認

8. 未確認事項

  • 1-3で保存した元画像は、そのあとページが作られなかった場合に消えるのか(掃除の仕組みは見つからなかった)
  • 「自信のない箇所」が画面でどう表示され、人がどこまで気付けるようになっているか
  • 2回目の読み直しが実際にどれくらいの割合で起きているか(記録が残っているかを含めて未確認)
  • 1枚あたりの上限を超えたときの案内が、利用者に分かる言葉になっているか

※ この解説は、2026年8月14日時点の studyguide のプログラムを読んで作成しました。

※ 抽象度は Lv.4 で統一しています。通信の方式・データの形式・認証のしくみ・ステータスコードまでは書き、製品名やライブラリ名、関数名までは踏み込みません(そこは Lv.5)。

※ 工程の番号は全体図の番号と対応しています。全体図はこちら

akuramochi1.com — personal tools for learning and thinking.