OCRでレポート作成をサボる

OCRでレポート作成をサボる

大学のレポートは本当に面倒! こう思ったのはたしか学部3年の春のことでした。

それまではマメに実験冊子を見てタイピングしながら実験の手順とかを書いていましたが、「さすがにもうやりたくない!」となった私はついにこう考えました。

そうだ、退屈なことはPythonにやらせよう。

対象とする読者、しない読者

このページでは以下の全てに該当する読者を対象としています。

  • ある程度Pythonが読めるし、動かないプログラムを見つけたらちゃんと自分で検索する
  • タイピングがそれなりにできる
  • レポートを書くのがダルい

逆に対象としていない読者は以下のとおりです。

  • Pythonが書けないし読めない
  • タイピングができない
  • レポートを書くのは別にダルくない
  • レポートをサボるのに抵抗感があるというか罪悪感がある
  • 他人にレポートを書かせる

つまり、これはほんの一例ですが「Pythonが書けないし読めないし、タイピングもできないし他人にレポートを書かせるのにその対価を払わないようなやつ(こういうやつ、本当に大学に1人はいる)」は対象としていません。

何をするのか

具体的にはこんな感じのフローで

  1. 大学から配布された実験手順書をiPhoneで撮る
  2. AirDropでmacに写真を送る
  3. Pythonで画像を読み込んで文字認識させる
  4. 文字認識は絶対ではないので、間違っている箇所を確認して適宜削除する

おおよそこんな感じです。

まあ、1と2が相当面倒な作業なのですが、さておきという感じです。スキャナーは持っていないので、手段としてはいちばんお金がかからないAirDropしかないです。

ロボティクスとか勉強していたら、ロボットを組んでページめくりロボットとカメラでもっと効率良くできるかもしれませんが、私は所詮、情報系なので、そんなことはできない()

文字認識の技術にはOCR(光学的文字認識)を使います、そしてレポート作成をサボります! なんか漢字にするとカッコいい笑

ちなみにOCRはただのパターンマッチングなのでAIではないです。

ただし、大学で課されるレポートというのは「タイピングを練習する」「論文などで画像や表、文章ブロックなどをどのように配置するのかを考える力を養う」ためにあるので、本来のレポートの在り方とは、かけ離れますのでその辺はちゃんと留意してください。

1. 大学から配布された実験手順書をiPhoneで撮る

サンプル画像としてはこれを使います。

HEIC画像は例として載せられなかったので、代わりにjpegを載せることにします。

2. AirDropでmacに写真を送る

ここはそのままです。

Androidのことは知らないので、「Androidで画像をPCへ送りたい!」という人は検索しましょう。

3. Pythonで画像を読み込んで文字認識させる

ついにプログラムを動かす段階に入りました! それでは動かしてみましょう!

python main.py Users/*/Downloads/

結果はこのようになります。

sample.HEIC
  sample.jpeg
4第3回:2次元フーリエ変換と周波数フィルタリング
3回目の実験の日的は,画像の2次元フーリエ変換と周波数フィルタリングを実装し,入カ像やそのパラメータを変化させながら変換結果を評価することで,フーリ工変換と周波数領での画像処理について理解を深めることである.

文字認識の精度を上げるために、グレースケールに落とし込み二値化しました。

また、iPhoneで撮った画像はおおかたHEIC形式なので、HEICをJPEGに変換する処理も追加しています。このQiitaの記事を参考にしています。

コードは後述。

4. 文字認識は絶対ではないので、間違っている箇所を確認して適宜削除する

OCRのおかげで文字を簡単に認識できますが、残念ながら100%文字を認識してくれるわけではないです。

特に理系のレポート作成では数式が多く記述されていることがあります。この場合、OCRでは認識してくれないので、結局自分で手入力することになります。

本当はこの部分もラクにしたかった!

コード

とりあえずコードとしてはこのようになります。

from PIL import Image
from pyocr import get_available_tools
from pyocr.builders import TextBuilder
from glob import glob
import sys
import numpy as np
import re
import subprocess


image_to_string = get_available_tools()[0].image_to_string
image_to_string.__defaults__ = ('jpn', TextBuilder())


def get_text(fname: str, threshold: int=128) -> str:

    fname_ = fname.rsplit('.')[0] + '.jpeg'

    subprocess.call(f'sips --setProperty format jpeg {fname} --out {fname_}'.split(' '))

    im = np.array(Image.open(fname_).convert('L'))
    im[im < threshold] = 0
    im[im !=        0] = 255

    text = image_to_string(Image.fromarray(im))
    # 変な文字が出てくるから後処理
    dic = {
        "⓪":  "0", "①":  "1", "②":  "2", "③":  "3", "④":  "4", "⑤":  "5", "⑥":  "6", "⑦":  "7", "⑧":  "8", "⑨":  "9",
        "⑩": "10", "⑪": "11", "⑫": "12", "⑬": "13", "⑭": "14", "⑮": "15", "⑯": "16", "⑰": "17", "⑱": "18", "⑲": "19",
        "⑳": "20", "㉑": "21", "㉒": "22", "㉓": "23", "㉔": "24", "㉕": "25", "㉖": "26", "㉗": "27", "㉘": "28", "㉙": "29",
        "㉚": "30", "㉛": "31", "㉜": "32", "㉝": "33", "㉞": "34", "㉟": "35", "㊱": "36", "㊲": "37", "㊳": "38", "㊴": "39",
        "㊵": "40", "㊶": "41", "㊷": "42", "㊸": "43", "㊹": "44", "㊺": "45", "㊻": "46", "㊼": "47", "㊽": "48", "㊾": "49",
	"㊿": "50",
        ',' : ',', '\.' : '.', ' ': '', '\n[^\n]': ''
    }
    for key, val in dic.items():
        text = re.sub(key, val, text)

    return text


if __name__ == '__main__':

    text = '' 
    download_path = sys.argv[1]
    for fname in sorted(glob(f'/{download_path}/*HEIC')):
        text += get_text(fname)

    print(text)

さいごに

レポートを書くスキルは大学生には必須です。

しかし、時には文明の利器に頼ることも大切です。