LoginSignup
6
11

More than 5 years have passed since last update.

画像からOCRテキスト出すやつを高速化する

Last updated at Posted at 2019-02-23

環境

  • Windows 10
  • Python 3.7.1 (Anaconda3-2018.12-Windows-x86_64)
  • PyOCR
  • Tesseract OCR 4.0.0

画像をOCRするやつの並列化版

前回の画像をOCRして検索用のテキストファイルを作るだとシングルスレッドで処理するので遅かった。

標準ライブラリに入っているPoolというやつを使うとmapするときに並列処理できるらしいので、それに合わせてOCR処理を書き換えた。

pyocrとTesseract OCRが並列で呼び出しても問題ないIFになっているかどうか不安だったが、OCRする度にTesseract OCRを新たに呼び出す設計で問題なかった(少なくとも上記環境では)。

from PIL import Image
import sys

import pyocr
import pyocr.builders
import glob
import os

from multiprocessing import Pool
import multiprocessing as multi

def ocrimage(file):
    tools = pyocr.get_available_tools()
    if len(tools) == 0:
        print("No OCR tool found")
        sys.exit(1)
    # The tools are returned in the recommended order of usage
    tool = tools[0]
    print("Will use tool '%s'" % (tool.get_name()))
    # Ex: Will use tool 'libtesseract'

    langs = tool.get_available_languages()
    print("Available languages: %s" % ", ".join(langs))
    lang = langs[0]
    print("Will use lang '%s'" % (lang))
    ocrtext = tool.image_to_string(
        Image.open(file),
        lang="jpn",
        builder=pyocr.builders.TextBuilder(tesseract_layout=6)
    )
    return ocrtext

if __name__ == '__main__':

    njobs = 1
    if multi.cpu_count() > 2:
        njobs = multi.cpu_count() - 1

    ocrtext = ""
    filelist = []
    for p in glob.glob('./**', recursive=True): # ディレクトリとファイル取得
           if os.path.isfile(p):
               # ファイルの場合
               name,ext = os.path.splitext(p)
               if ".jpg" == ext or ".png" == ext or ".gif" == ext:
                   filelist.append(p)

    # 並列処理                
    process = Pool( njobs )
    ocrtexts = process.map(ocrimage, filelist)
    process.close()

    for index, p in  enumerate(filelist):
        ocrtext += p + ":\n" + ocrtexts[index] + "\n\n"

    f = open('ocrtext.txt', 'w', encoding='utf-8')
    f.write(ocrtext)
    f.close()

前回はRyzen7 2700X(8コア16スレッド)で1スレッドしか使ってなかったので約2500個の画像に1時間ぐらい掛かっていたが、今回のコードで10分程度に高速化した。

HyperThreadingなどのSMTはCPUの空きパイプラインを活用して5-20%程度性能を改善するらしいのだが、実コア数を超えた時点でPCの動作が重くなる。

multi.cpu_count()が返すのはどうも論理コア数らしいので、実コア数に合わせたい感はある。

論理コア数を全部使い切られるとPCが大変重くなって作業できなくなるので一応-1している。

余談だが、if __name__ == '__main__':を忘れたせいで1時間半損した。

2/27追記: なんか"UnicodeEncodeError: 'cp932' codec can't encode character ..."とエラーが出た。
書き込み時のエンコーディング指定忘れが原因だった。ちゃんとencoding='utf-8'をつけましょう…。

6
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
11