おはようございます。まさきんです。
我が家は子どもが複数います。それぞれ通っているドリルや教材が違います。プリントの山を毎回手で仕分けするのが、地味に負担でした。
まとめてスキャンして、あとの仕分けはAIに任せる。そんな仕組みを作ってみました。今回は実際に使っているコードやルールまで、具体的に紹介します。
きっかけは「仕分けだけで時間が溶ける」こと
終わったドリルやプリントは、子どもの数だけ種類があります。科目も学年もバラバラです。まとめて置いておくと、結局どれが誰のものか分からなくなります。
一枚ずつ手でめくって確認するのは、単純作業ですが時間がかかります。なので、この「仕分け」の部分だけ自動化できないかと考えました。
仕組みの全体像
流れはシンプルです。次の3段階に分けています。
- 終わったプリントをまとめてスキャンし、PDFにする
- PDFを1ページずつ画像に変換し、AIにヘッダーやレイアウトの特徴から「誰の・どの科目の」プリントかを判定させる
- 判定結果にもとづいて、子ども別・科目別のツールに反映する
ポイントは、スキャンの時点では仕分けを考えなくていいことです。まとめて読み取ってしまい、判断はあとからAIに任せます。
PDFをページごとの画像に変換する
スキャンしたPDFは、そのままAIに読ませてもうまく認識できないことがあります。特に手書きの採点結果が入ったスキャンPDFは、文字レイヤーを持たないただの画像の集合であることが多いからです。
なので、まずPyMuPDF(fitz)でページを1枚ずつPNG画像に変換しています。
import fitz
doc = fitz.open("scan.pdf")
for i in range(doc.page_count):
pix = doc[i].get_pixmap(matrix=fitz.Matrix(2, 2)) # 2倍解像度
pix.save(f"page_{i+1}.png")
Matrix(2, 2)で解像度を2倍にしてから画像化すると、AIが文字やチェック欄を読み取る精度が上がります。手書きの採点結果は特に、解像度が低いと見落としが増えます。
つまずきやすい落とし穴: 座標の単位
チェック欄だけをズームして確認したいとき、get_pixmapにclip引数で範囲を指定できます。ここで一つ罠があります。clipの座標は**PDFのポイント座標(元のページサイズ)**で指定するもので、拡大後のピクセル座標ではありません。
# 2倍・4倍にズームしても、clipの数値はページの実寸(A4ならおよそ590×836pt)のまま指定する
pix = doc[i].get_pixmap(matrix=fitz.Matrix(4, 4), clip=fitz.Rect(0, 140, 589, 600))
倍率をかけたからといってclipの数値まで大きくしてしまうと、範囲が画像の外に出てしまい空振りします。ここを混同すると「なぜかクロップした画像が真っ白」という現象にハマるので、最初に知っておくと時間の節約になります。
判定のロジック
AIに渡している指示を、一般化して紹介します。
以下はスキャンした学習プリントの画像です。
次のヒントをもとに、どの子ども・どの科目のプリントかを判定してください。
- ページ上部のロゴや見出しのデザイン
- 罫線やレイアウトの形式(マス目の大きさ、行間など)
- 記載されている単元名や問題形式
- 名前欄に記載された名前
判定結果は「子どもID」「科目」「単元」の3項目で返してください。
判断がつかない場合は「不明」と回答し、無理に決めつけないこと。
実際の仕分けでは、見出しの文言・レイアウトの特徴・名前欄の3つを組み合わせた「識別表」を先に作っておき、AIにはその表と照合させる形にしています。例えば「漢字の書き取りドリルで、右上に学年名が入っている」と「計算ドリルで、各問の左下に小さく問題タイプの記号が印字されている」は、見た目だけでほぼ一意に見分けがつきます。表にしておくことで、AIの判定がブレにくくなりました。
仕分けの手間を仕組みで減らす発想は、プリント以外にも使えるかもしれません。放置しがちな携帯料金プランの見直しも、似たような話だと感じています。
採点のルールは「チェックだけ」で統一
複数の子どもの教材を扱ううえで、もうひとつ決めていることがあります。採点の基準を、子どもによらず統一することです。
具体的には、各問題の左(または左上)にあるチェック欄が塗りつぶされているかどうかだけを、正解・不正解の判断材料にしています。
- チェック欄が塗られている → まちがい(復習対象)
- チェック欄が白いまま → 正解
- 答えの近くにある赤丸(◯)は「正解の確認マーク」であって、不正解の印ではない
- 赤ペンで書き直された解答は親の添削であって、子どもの答えではない。赤字の有無で正誤を判断しない
文字の丁寧さや、消しゴムで直した跡は判断に含めません。理由は単純です。チェックが入っているかどうかは機械的に読み取れますが、手書きの丁寧さを評価に混ぜると、途端に基準がぶれます。
なので、「正誤の判断はチェック欄の塗りつぶしだけで行い、字のうまい下手や赤字の訂正は判断材料にしない」というルールを、最初にAIへの指示として明文化しています。この一文があるかないかで、判定のブレ方がかなり変わりました。
チェック欄の見落としを防ぐ拡大の手順
スキャン画像全体を縮小したまま見ると、チェック欄の塗りつぶしがつぶれて見えないことがあります。「全部書けているから全問正解」と早合点しないよう、次の手順を必ず踏んでいます。
- ページ全体を確認し、大まかなレイアウトを把握する
- チェック欄の列だけを切り出して拡大する(先述の
clipを使う) - 拡大画像で1問ずつ、塗られているかどうかを確認する
- 判定に迷うチェック欄や、そもそも文字が読めない箇所は、推測せずに人に確認してもらう
サムネイルサイズの縮小画像だけで判断を済ませると、塗りつぶしの見落としが起きやすいと実感しています。
科目によって「間違いの単位」を変えている
もう一つ工夫している点があります。間違いを記録する単位を、科目ごとに変えていることです。
| 科目 | 記録する単位 | 具体例 |
|---|---|---|
| 漢字・ひらがなの練習 | 1文字単位 | 「校」「ぬ」のように文字そのものをキーにする |
| 算数 | 問題のパターン単位 | 「単位換算」「あまりのある割り算」「位取り」のように、問題タイプをキーにする |
| 理科・社会 | テーマ+小項目単位 | 「テーマ名:セクション:番号」のように階層化したキーにする |
同じ「間違えた問題」でも、科目によって振り返るべき粒度が違います。漢字を単元単位でまとめてしまうと、次の復習の精度が落ちます。逆に、算数の「パターン」を1問ごとの粒度にしてしまうと、復習の対象が絞り込みにくくなります。
算数の場合、各問題の隅に小さく問題タイプを表す記号を印字しておき、採点結果を読み取るときはその記号単位でまちがいを集計しています。同じタイプの問題が複数あって1問でもまちがっていれば、そのタイプ全体を「要復習」として扱う、という単純なルールです。
理科・社会は、プリント末尾の復習コーナーが当日のメインテーマとは別の単元から出題されていることがあるため、設問の文面をテーマ一覧と照合してから、どのテーマの復習かを特定するようにしています。決め打ちせず、必ず照合してから記録する部分です。
この仕組みは、もともと別の目的で作っていた「間違えた項目を記録して繰り返し出題する」しくみを転用したものです。目的が変わっても、土台のロジックはそのまま使えました。
実行環境をまたぐ構成にしている理由
この仕組みは、AIとのやり取り自体はクラウドのサンドボックス環境で行い、実際のファイル操作や状態管理は自宅のパソコン側の常駐プロセスに任せる、という2段構成にしています。
理由は単純で、採点結果を保存するデータベースやドリル生成用のスクリプトが、自宅のパソコンにしか置いていないからです。クラウド側から自宅側へは、簡単なコマンドを送るだけの軽量なクライアントスクリプトを経由してアクセスします。
# 例: 判定結果をもとに、まちがえたキーを反映する
python3 tools/kanji_drill/kanji_bridge_client.py mark-miss 校庭 音楽
python3 tools/kanji_drill/kanji_bridge_client.py mark-correct 教室
# 反映前に、自宅側の常駐プロセスが生きているか確認する
python3 tools/kanji_drill/kanji_bridge_client.py health
このhealthコマンドを反映の直前に必ず挟むようにしています。自宅側のプロセスが落ちていることに気づかずmark-missを送り続けると、コマンドは成功したように見えて実は何も反映されていない、という事故につながるためです。
やってみて感じたこと
一番助かっているのは、「誰の・どの科目か」を確認する作業がなくなったことです。まとめてスキャンして、あとは結果を見るだけになりました。
ただ、AIの判定を完全に信頼しているわけではありません。「不明」という判定が出た分や、チェック欄の判定に迷った分は、必ず自分の目で確認するようにしています。無理に仕分けさせるより、分からないものは分からないと言わせるほうが、結果的に信頼できる仕組みになると感じています。
こういう人に向いている
- 複数の子どものプリントやドリルが混在して、仕分けに時間を取られている人
- 採点基準を「見た目の丁寧さ」ではなく、機械的なルールに統一したい人
- PyMuPDFやちょっとしたブリッジスクリプトを組む程度の実装に、抵抗がない人
まとめ
複数の子どもの教材は、量が増えるほど仕分けの負担が大きくなります。スキャンとAIによる仕分けを組み合わせることで、その部分だけ切り出して自動化できました。
座標の単位や採点基準の明文化といった地味な工夫が、実は精度を安定させるうえで一番効いていると感じています。仕組み化を考えるときは、こうした「判断基準そのものを決める」作業が案外大事なのかもしれません。
仕組み化で減らせる負担は、プリントの仕分けだけではないのかもしれません。次は携帯料金のプランを、同じように見直してみようと思っています。
本記事にはアフィリエイト広告を含みます。当サイトのリンクを経由して商品・サービスへお申し込みいただいた場合、提携先企業から成果報酬を受け取ることがあります。 また、運営者は楽天グループに所属する社員であり、紹介プログラムを通じた報酬を受け取る場合があります。記事内容・評価は広告の有無にかかわらず、運営者の実体験と調査に基づき作成していますが、 上記の関係性をご理解のうえお読みください。詳しくは免責事項・アフィリエイト表記をご覧ください。