元表現: 写真読み取り係 / StudyGuide ブロック詳細
写真に写っている英文を、後段が扱える1本のテキストにして返すブロック。11工程のうち、外部の言語モデルに任せているのは2つだけです。
抽象度 Lv.4(技術用語+システム上のやりとりまで/製品名・ライブラリ名は書かない)写真に写っている英文を、後段が扱える1本のテキストにして返すブロック。 やっていることは「文字を読む」だけではない。何枚目が本編で何枚目が続きかを決め、 洋書か英語教材かを判定し、教材なら日本語を落とし、 自信が持てなければ設定を変えてもう一度読み直すところまでが、この1ブロックの中に入っている。
外部の言語モデルを使う4か所のうちの1つ。ただしモデルに投げているのは工程の一部で、 前後は決まった手順の処理で挟まれている。
利用者(写真の送信) → 1. 画像テキスト抽出 → 人による確認・修正 → 2. 処理オーケストレーション
POST /api/extract(受け取り multipart/form-data/返り application/json)。詳しくは5章の各工程に書いた。400ここより後ろには置けない。後段(2・3・6・8)はすべて「1本のテキスト」を前提に組まれている。 写真のままでは、文に分けることも、語句を数えることも、音声にすることもできない。
ここより前に人の確認を置くこともできない。読み取る前の写真を人が見ても、 機械が何をどう読み違えるかは分からない。だから確認画面は1番の直後にある。
やり直しをこのブロックの中で完結させているのも意図的。読み取りの品質が悪いまま先へ流すと、 誤字が読解ガイドにも単語テストにも音声にも伝わり、全部を作り直すことになる。 外に出す前に、このブロックの中で2回粘る作りになっている。
| 工程 | 主体 | やりとりの内容 | 方向 |
|---|---|---|---|
| 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/extract へ multipart/form-data で送る。これはファイルと文字を1つの送信にまとめる形式で、区切り文字列で各パートを分ける。画像のようなバイナリは application/json にそのままは入れられないため、フォームでファイルを送るときはこちらを使う。パートは images(画像ファイル、複数可)と contentMode(auto/book/material)の2種類。GETではなくPOSTなのは、送る量が大きく、かつサーバー側に処理を起こす(=副作用がある)操作だから。
1-2 入口処理は、まず送り主が認証を通っているかを確かめる。次に、枚数が1〜4枚か、画像の形式が受け付けられるものか、1枚が大きすぎないかを順に見る。このやりとりは外に出ない。↻ はそういう意味で、他の部品に何も送らず、自分の中だけで合否を決める。ここで弾かれた場合、読み取りは一度も始まらない。
技術的には まずリクエストの Cookie ヘッダから署名付きの値を取り出し、定数時間比較で照合する。無効なら本文を読む前に 401 Unauthorized を返す。続いて種類の指定が決められた3語か、枚数が1〜4枚か、各ファイルの種類が image/jpeg/image/png/image/webp のどれかか、サイズが12MB以下かを順に見る。1つでも外れたら 400 Bad Request。400と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 OK と application/json で返す。中身は、紙面の種類・ページ番号・本文・自信のない箇所の配列・画像の鍵の配列。ブラウザはこれを受け取って確認画面を描く。
← 横にスクロールできます / 縦の点線=その担当が動いている時間、横矢印=やりとり、折り返し=自分の中だけで終わる処理
| どこで失敗するか | 何が起きるか |
|---|---|
| 1-2 の検査 | 400。読み取りは始まらない。画面に理由(枚数・形式・容量のどれか)が返る。認証が無ければ 401 |
| 1-6/1-7 の読み取り | 500「写真から英文を読み取れませんでした」。1-3で保存した画像は残る |
| 1-8 で本文が空 | 500「写真から英語の本文を読み取れませんでした」 |
| 1-9 でも一文が閉じない | 500。「次のページが正しく選ばれているか」を促す専用の文言が返る |
| いずれの場合も | 2番以降は一切走らない。ページは作られない |
画面側でこれらのエラーがどう見えるか(やり直しの導線があるか)は未確認。
※ この解説は、2026年8月14日時点の studyguide のプログラムを読んで作成しました。
※ 抽象度は Lv.4 で統一しています。通信の方式・データの形式・認証のしくみ・ステータスコードまでは書き、製品名やライブラリ名、関数名までは踏み込みません(そこは Lv.5)。
※ 工程の番号は全体図の番号と対応しています。全体図はこちら。