DockerによるTesseractAPIサーバ

docker By Output不足なエンジニア

以前画像をJSONリクエストに乗せる方法を紹介しました。今回はHTTPリクエストで画像をもらい、TesseractOCRの実行結果を返却するAPIを作る方法を紹介します。シンプルなAPIなのでflaskで実装します。



Dockerfile

日本語可なTesseractをインストールするDockerfileの一例です。ついでにflaskも入れるので後述のrequirements.txtが必要になります。API部分はtesseract_api.pyで実装しています。

FROM python:3.7
USER root
ENV PYTHONUNBUFFERED 1

RUN apt-get update && \
    apt-get -y install locales \
    vim less \
    libboost-all-dev \
    tesseract-ocr \
    tesseract-ocr-jpn \
    libtesseract-dev
RUN localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
ENV TZ JST-9
ENV TERM xterm

RUN pip install --upgrade pip
RUN pip install --upgrade setuptools

### create and into workdir
RUN mkdir /flask
WORKDIR /flask
ADD requirements.txt /flask/
RUN pip install -r requirements.txt
RUN pip install pytesseract pyocr

ADD ./tesseract_api.py /flask/
CMD FLASK_APP=/flask/tesseract_api.py flask run --host=0.0.0.0 --debugger --reload

必要ライブラリ

requirements.txt は下記になります。※numpyは文字列化した画像を復元する際に使用するので、用途によっては不要です。

numpy
opencv-python
Flask

Tesseract API の実装

画像とTesseractOCRを実行したい矩形の座標領域、矩形領域の連番が下記フォーマットで渡されて、矩形領域に文字が含まれているか判定するAPIを実装します。(tesseract_api.py)
{“image” : “画像の文字列”, “rectangles” : [rect( = [x, y, width, height, uid] )}
画像差分から記入箇所判定で紙が歪んでいた場合は記入箇所以外にも差分として検出してしまうことが多いので、検出箇所に文字があるか判定する用途で作成したものです。これは抜粋なので本来はTesseractのオプションをJSONで指定したりヘルスチェックを入れたりする必要はあると思います。

from flask import Flask, request, json, jsonify
import base64
import numpy as np
import cv2
import pytesseract
from collections import namedtuple
import logging
# 矩形領域IDに文字があるか判定結果を格納するフォーマット
DetectResult = namedtuple('Result', ('uid', 'has_text'))

app = Flask(__name__)
# JSONにUnicode文字が含まれる可能性がある場合必要
app.config['JSON_AS_ASCII'] = False

api_logger = logging.getLogger('werkzeug')


@app.route('/text_detect', methods=['POST'])
def text_detect():
    app.logger.debug('text_detect')
    # 文字列から画像の復元
    posted = json.loads(request.get_json())
    img_as_text = posted['image']#.encode('utf-8')
    img_binary = base64.b64decode(img_as_text.encode('utf-8'))
    img_array = np.frombuffer(img_binary, dtype=np.uint8)
    img_from_text = cv2.imdecode(img_array, cv2.IMREAD_COLOR)

    rectangles = posted['rectangles']
    api_logger.info('rectangles:' + str(rectangles))
    # オプションは本来リクエストで変更したほうが良いが入力チェックは大変そう
    config = ('-l eng+jpn --oem 1 --psm 7')
    res_rectangles = []
    for rect in rectangles:
        x, y, width, height, uid = rect.values()
        clip_img = img_from_text[y:y+height, x:x+width]
        # Tesseract の呼び出し
        text = pytesseract.image_to_string(clip_img, config=config)
        if text == '':
            res_rectangles.append(DetectResult(uid, False))
        else:
            res_rectangles.append(DetectResult(uid, True))

    res_json = json.dumps(res_rectangles)
    app.logger.debug(res_json)
    return jsonify(json.loads(res_json))

これだけでTesseract OCRを実行するAPIサーバがどこにでも立てることができます。(実際のAPI実装はもっと真面目にやるべきです)。flaskも今回初めて使いましたが、かなり直感的で学習コスト少なくAPIが実装できそうな感じです。(ただしXXS対策などセキュリティは考慮してないのでそこは別)。

ちなみにnamedtupleはクラスにするほどでもないけど、ちょっとしたオブジェクトを作りたいときに結構便利です。「テスト駆動 Python」で初めて知りました。立ち読みレベルで理解できるので興味がある方は読んでみてください。(Amazonのリンク貼れるようにしようかな)


カテゴリー:docker,OpenCV,python,画像処理

Output不足なエンジニア

統計が好きになれず、機械学習やったら必然的に統計が必要になるだろうと思ったら想像以上に機械学習にハマる。数学は芸術なので商売にするつもりはないけど、DeepLearningは数学じゃないし商売にしたいと思っているところ。画像処理がメイン。