OpenCV画像をJSONリクエストに乗せる

OpenCV By Output不足なエンジニア

OCRエンジンとしてTesseractは気軽に使用可能ですが、pipだけでは使えずTesseractのインストールが必要になり、環境ごとに準備するのは面倒です。なのでDockerでAPIサーバを作ってOCRをAPI実行できるようにしておくと便利です。
OpenCVで読み込んだ画像をJSON文字列にしてHTTP通信でやり取りして意外とハマったのでメモを残します。



手順

画像を読み込んだら文字列にしてJSONに入れれば良いのですが、画像を文字列にするにはまずバイナリ文字列( byte 型)にしてそれをデコードすることで文字列にします。そしてJSONを送信して、受信側が再度バイナリ文字列にして画像として復元することで画像をAPIでやり取りすることができます。
※単純に画像だけ送る場合はバイナリデータとして送ることもできますが、画像名や座標情報などその他情報も合わせて送る場合はJSONが扱いやすいですし、何よりJSONでのやり取りが一般的なので。ちなみにtegakiというOCRエンジンは画像をJSONにしてやり取りしています。

画像のバイナリ文字列化・文字列化

画像をバイナリ文字列に変換するソースは下記です。ここは特に問題ないと思いますが、OpenCVの画像はOpenCVの関数を用いてbufferという中間変数に変換しましょう。ここを普通にbase64で変換しようとして、受信側で復元できずハマりました。

# img は cv2.imread などで得られる画像を読み込んだnumpy.ndarray
_, buffer = cv2.imencode('.jpg', img)
# base64のバイナリ文字列をdecodeで文字列に変換
img_as_text = base64.b64encode(buffer).decode('utf-8')

受信側でOpenCVの画像に復元

受信側は文字列をバイナリ文字列に変換後、numpy.frombufferで一度配列にしてからimdecodeします。

# flask でPostを受信した状況を想定しています
posted = json.loads(request.get_json())
img_as_text = posted['image']
# バイナリ文字列に変換
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)

これで受信側で画像を復元することができます。当然画像データを文字列にするとかなりの文字数になるので、printやログ出力すると画面出力に時間がかかります。APIの設定として、受信データをそのままログ出力するのではなく、画像文字列以外をログに出力するなどの注意が必要です。

画像の劣化までは検証していませんが、HTTP通信で画像をやり取りすることができるので、APIの自由度が高まりました。flaskを使えばAPIサーバは簡単に作れるので、tesseract API コンテナを作ることも可能です。API サーバは次回記事にします。


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

Output不足なエンジニア

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