Skip to content

About

帳票画像の座標から読み上げ順を組み直し、スクリーンリーダーで読ませるデモ(space ocr API 利用)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

space ocr 読み上げデモ

帳票の画像を、スクリーンリーダーが意味の通る順で読める形に組み直すデモです。

同じ画像に対する3通りの読み取り結果を1画面に並べます。

欄 中身
A /ocr/text の出力をそのまま
B /ocr/markdown の出力を、Markdown の表記どおりに HTML へ変換したもの
C /ocr/fields と /ocr/markdown の cells の座標から、読み上げ順を組み直したもの

A と B で崩れている箇所は直していません。直すと比較になりません。


何をしているか

1. 座標から読み上げ順を組み直す(app/reorder.py)

/ocr/markdown は文書の見た目を素直に写します。そのため横に並んだ2つの表は「1つの8列の表」として返ります。

| 検査項目 | 今回 | 基準値 | 判定 | 検査項目 | 今回 | 基準値 | 判定 |
| 収縮期血圧 | 128 | 100~139 |  | 総コレステロール | 210 | 140~219 |  |
                                 ↑ ここから右は別の検査

目で見れば罫線で分かりますが、読み上げでは左右の別々の検査が1行に混ざります。座標を使うと機械的に分離できます。

  • 要素の並び順は再帰的な XY カットで決めます。横向きの空白帯で切れるなら切り、切れなければ縦向きの空白帯で切ります。請求書の下部(左に備考、右に合計欄)もこれで分かれます。
  • 表の中の段組は、列の中心座標の周期性で判定します。⚠️ 隙間の大きさでは判定できません——健診結果の見本では、いちばん広い隙間(110px)は段の境界ではなく、境界は90pxで2番目でした。列中心の差は 569.8 / 570.4 / 570.5 / 570.4 px(ずれ 0.65px)で、右半分が左半分の平行移動であることがはっきり出ます。

2. 読み上げる前に確認する(app/verify.py)

  • 同じ値が別の列に出ていないかを座標から判定します(「単価 60,000」と「金額 60,000」など)
  • evidence.source と ocr_confidence を見て、座標の確からしさを枠の描き分けに反映します
  • review.reasons を補助として足します
  • 検算——請求書は明細合計と小計・消費税・合計・請求金額の突き合わせ、健診表は今回値・基準値・判定記号の整合

3. 座標を目に見える形にする(app/render.py)

元画像の上に、cells の座標どおりの位置で、読み上げ順に並んだフォーカス可能な領域を置きます。

  • Tab を押すと、読み上げ順に移動します
  • 枠は CSS の :focus で描きます(JavaScript の同期処理を使っていないのでずれません)
  • 実線=値、破線=補完に使った列見出し。列見出しは値と離れた場所にあるので、「今回 128」という一文が画像上の2か所の合成であることが見えます

いちばん短い動かし方

pip install fastapi uvicorn jinja2 python-multipart pillow
python -m uvicorn app.main:app --port 8000

そのうえで、次のURLを開いてください。見本の解析から読み上げの再生まで自動で走ります。

http://localhost:8000/?doc=health_check&autoplay=1&delay=6&pace=3.5

doc は health_check(健康診断の結果表)か invoice(請求書)。 pace は1項目あたりの秒数、delay は再生を始めるまでの秒数です。

スクリーンリーダーをお持ちでない場合

画面の「この端末の音声で読む」ボタンを押すと、同じ文をブラウザの音声合成で読み上げます。読み上げ順が組み直されていることは、これで確認できます。

⚠️ ただし音声合成は文を読むだけです。見出しへの移動や、表の行と列をたどる操作(付録の「構造化した表」)は、スクリーンリーダーでないと確認できません。そこまで見ていただける場合は NVDA(無料・インストール不要のポータブル版あり)をお使いください。

🔴 スクリーンリーダーをお使いの方は、このボタンを押さないでください。読み上げが二重に鳴ります。


動かし方(詳細)

必要なもの

  • Python 3.12 以上
  • pip install fastapi uvicorn jinja2 python-multipart pillow
    • pillow は見本画像の生成にだけ使います
    • 🔵 読み上げ順の再構成(app/reorder.py)と検証(app/verify.py)は標準ライブラリだけで動きます

APIキーの設定

cp .env.example .env
# .env を開いて SPACE_OCR_API_KEY を入れる

🔴 .env は公開物に含めません(.gitignore 済み)。コードにキーを書いている箇所はありません。

応答の固定(動画を撮るときだけ)

.env に SPACE_OCR_CACHE=1 を置くと、画像のハッシュをキーに応答を .cache/ へ保存して使い回します。既定は 0(毎回APIを呼びます)。

⚠️ これは動画を撮るためだけの設定です。同じ画像を投げても応答が回ごとに変わるため、撮り直すたびに内容が変わってしまうためです(詳細はフィードバック文書)。応答そのものには手を加えていません。

python src/freeze_saved_response.py      # 保存済みの正常な応答を .cache に入れる
python src/pick_good_response.py health_check 12          # 当たりを引いて .cache に固定する
python src/pick_good_response.py health_check 12 measure  # 固定せず、安定性を数えるだけ
python tools/summarize_gacha.py                           # 抽選ログを表にまとめる
python src/pick_good_response.py invoice 10   # または、通った回を引いて固定する

起動

python -m uvicorn app.main:app --reload --port 8000

http://localhost:8000 を開きます。

使い方

  1. 書類の種類を選ぶ(請求書 / 健康診断の結果表)
  2. 画像を選ぶ(ドラッグ&ドロップも可)か、「見本を使って解析する」を押す
  3. 5〜20秒で結果が出ます(3エンドポイントを並列で呼びます)
  4. C-1 の画像の中を Tab で送ると、読み上げ順に枠が移ります

見本

samples/ に3枚あります。実在の個人情報・機密情報は使っていません。すべて架空です。

見本 何を確かめるためのものか
sample_invoice.png 罫線のある明細表。同じ数字を意図的に重ねてある(「ご請求金額 132,000」と「合計 132,000」、明細1行目の「単価 60,000」と「金額 60,000」)。下部は左に備考・右に合計欄で、要素レベルの2段組になっている
sample_health.png 2段組の検査結果。判定記号 H / L が値と別のセルにある。「38」を左右の段にまたいで2回、「40」を基準値の中で2回重ねてある
sample_meter.png 検針票。ラベルが上・値が下の箱が横に並ぶ。単位(kWh)が値と離れ、マイナスの金額を含む。引き算の検算(今回指示数 − 前回指示数 = ご使用量)が立つ

生成し直す場合:

python samples/make_sample_invoice.py
python samples/make_sample_health.py
python samples/make_sample_meter.py

枠の位置を確かめる

画面の枠が正しい位置に出ているかは、同じ座標を画像に焼いて確認できます。

python tools/draw_boxes.py health all   # 全単位の主枠
python tools/draw_boxes.py health 9     # 9番目の 主枠+値+列見出し

ファイル

app/
  main.py       FastAPI(画面と /analyze)
  spaceocr.py   APIクライアント(3エンドポイントを並列で呼ぶ)
  reorder.py    🔵読み上げ順の再構成(標準ライブラリのみ)
  verify.py     🔵※要確認の判定と検算(標準ライブラリのみ)
  render.py     A / B / C の HTML 生成
  fieldspec/    書類ごとの項目宣言(新しい書類を足すときはここに1枚足す)
  templates/    画面
  static/       CSS と JavaScript
samples/        見本画像とその生成コード
tools/          枠の位置を確かめる検証用スクリプト

書類を足すときに書き換えるのは fieldspec/ の JSON 1枚と、検算のルールだけです。読み上げ順の再構成は書類ごとに分岐させていません(分岐させると、その帳票専用の処理になってしまうため)。


モグテックラボ 守井 清吾(工学博士・情報工学)

About

帳票画像の座標から読み上げ順を組み直し、スクリーンリーダーで読ませるデモ(space ocr API 利用)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages