よしたく blog

ほぼ週刊で記事を書いています

画像処理ライブラリOpenCVを使って、画像からレシートを切り出してみた

Pythonの画像処理ライブラリであるOpenCVを使って、レシートをトリミングしてみました。 今回はその時のメモです。 手順通りにやればほぼほぼうまくいくと思います。

環境

準備

# 仮想環境を作成
python3 -m venv opencv-demo

#仮想環境の立ち上げ
cd opencv-demo
source bin/activate

# opencv-pythonのインストール
# 41.2MBほどあるので注意
pip install opencv-python

# Python3の起動
python3

opencv-demo配下にトリミングしたいreceipt.jpgを配置します。 今回はネットで拾ったこの画像がやりやすかったので、使わせていただきます。

下のソースコードを実行します。

import os
import numpy as np
import cv2

filename = "receipt"
imp_filename = "receipt.jpg"

def transform_by4(img, points):
    points = sorted(points, key=lambda x:x[1])
    top = sorted(points[:2], key=lambda x:x[0])
    bottom = sorted(points[2:], key=lambda x:x[0], reverse=True)
    points = np.array(top + bottom, dtype='float32')
    width = max(np.sqrt(((points[0][0]-points[2][0])**2)*2), np.sqrt(((points[1][0]-points[3][0])**2)*2))
    height = max(np.sqrt(((points[0][1]-points[2][1])**2)*2), np.sqrt(((points[1][1]-points[3][1])**2)*2))
    dst = np.array([
        np.array([0, 0]),
        np.array([width-1, 0]),
        np.array([width-1, height-1]),
        np.array([0, height-1]),
    ], np.float32)
    trans = cv2.getPerspectiveTransform(points, dst)
    return cv2.warpPerspective(img, trans, (int(width), int(height)))

im = cv2.imread(imp_filename)

im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
cv2.imwrite(filename+'_gray.jpg', im_gray)

im_blur = cv2.fastNlMeansDenoising(im_gray)
im_th = cv2.adaptiveThreshold(im_blur, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 15, 5)
cv2.imwrite(filename+'_th.jpg', im_th)

cnts = cv2.findContours(im_th, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[1]
cnts.sort(key=cv2.contourArea, reverse=True)

im_line = im.copy()
warp = None

for c in cnts[1:]:
    arclen = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02*arclen, True)
    if len(approx) == 4:
        cv2.drawContours(im_line, [approx], -1, (0, 0, 255), 2)
        if warp is None:
            warp = approx.copy()
    else:
        cv2.drawContours(im_line, [approx], -1, (0, 255, 0), 2)
    for pos in approx:
        cv2.circle(im_line, tuple(pos[0]), 4, (255, 0, 0))

im_rect = transform_by4(im, warp[:,0,:])
cv2.imwrite(filename+'_rect.jpg', im_rect)

receipt_rect.jpgができあがって、 無事にレシートだけがトリミングできました!

今回は、OpenCVを使って、レシートをトリミングすることをやってみました 次回は、トリミングされた画像から、OCRをして文字を取得してみようと思います。

追記】 次回の記事です!!!

yoshitaku-jp.hatenablog.com