前提・実現したいこと
レシートを読み込み,合計金額を抽出したいです。
python3系でtesseractを用いてOCRし,レシートの内容をテキストファイルにしようとしていたのですが,合計という文字がどうしても認識できません。
文字が潰れない限りで2値化も行っているのですが,そもそも元のレシートにおいて合計という文字が他の文字より大きく横長に書いてあることが多いためだと思います。実際,部首とそれ以外のところが別の文字として認識されてしまいます。
発生している問題・エラーメッセージ
文字認識した結果が以下のようになります。
""""
小 計 \③⑥0
刊 - が か ト * 社 ざ \①0
畑 言 一 \ ③ ⑤ 0
"""
畑言 一というところが本来,合計,と表示されるところです。
該当のソースコード
import cv2
import matplotlib.pyplot as plt
import numpy as np
import os
#画像を表示させる関数
def show_img(img):
plt.figure()
tmp = np.tile(img.reshape(img.shape[0],img.shape[1], -1),reps=3)
plt.imshow(tmp)
plt.show()
#画像をトリミングする関数の定義
def transform_by4(img, points):
""" 4点を指定してトリミングする。 """
points = sorted(points, key=lambda x:x[1]) # yが小さいもの順に並び替え。
top = sorted(points[:2], key=lambda x:x[0]) # 前半二つは四角形の上。xで並び替えると左右も分かる。
bottom = sorted(points[2:], key=lambda x:x[0], reverse=True) # 後半二つは四角形の下。同じくxで並び替え。
points = numpy.array(top + bottom, dtype='float32') # 分離した二つを再結合。
width = max(numpy.sqrt(((points[0][0]-points[2][0])**2)*2), numpy.sqrt(((points[1][0]-points[3][0])**2)*2))
height = max(numpy.sqrt(((points[0][1]-points[2][1])**2)*2), numpy.sqrt(((points[1][1]-points[3][1])**2)*2))
dst = numpy.array([
numpy.array([0, 0]),
numpy.array([width-1, 0]),
numpy.array([width-1, height-1]),
numpy.array([0, height-1]),
], numpy.float32)
trans = cv2.getPerspectiveTransform(points, dst) # 変換前の座標と変換後の座標の対応を渡すと、透視変換行列を作ってくれる。 return cv2.warpPerspective(img, trans, (int(width), int(height))) # 透視変換行列を使って切り抜く。
def convert(im, filename):
im = cv2.imread(im)
im_size = im.shape[0] * im.shape[1]
#輪郭を抽出して切り取るための二値化
im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
cv2.imwrite(filename + "_gray.jpg", im_gray)
print(filename + "_gray.jpg")
im_blur = cv2.fastNlMeansDenoising(im_gray) #画像のノイズ除去 im_th = cv2.adaptiveThreshold(im_blur, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 15, 5) #画像の二値化 show_img(im_th) cv2.imwrite(filename + "_th.jpg", im_th) print (filename+'_th.jpg')
#輪郭を抽出
img, cnts, hierarchy = cv2.findContours(im_th, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) #輪郭はリスト内にnumpy配列で出力される
cnts.sort(key = cv2.contourArea, reverse=True ) #面積の大きい順のsort
cnt = cnts[1]
img = cv2.drawContours(img, [cnt], -1, (0,255,0), 3)
cv2.imwrite(filename+"_drawcont.jpg", img)
im_line = im.copy()
print("finish")
warp = None flag = 1 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 flag: print(approx) warp = approx.copy() flag = 0 #一番大きな輪郭をwarpに組み込む 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)) #レシートっぽい輪郭の面積を算出 #一定以上の大きさならトリミング area = cv2.contourArea(warp) print("area = ", area) if area > im_size//5: print("now cutting.....") im_rect = transform_by4(im, warp[:,0,:]) #レシートの輪郭で切り取る。 cv2.imwrite(filename+'_rect.jpg', im_rect) else: return im #切り取ったら画像を表示する plt.figure() plt.imshow(im_line) cv2.imwrite(filename+'_line.jpg', im_line) print("warp = \n",warp[:, 0, :]) print(filename + '_rect.jp') #今度は文字抽出のためのグレースケール化 im_rect_gray = cv2.cvtColor(im_rect, cv2.COLOR_BGR2GRAY) cv2.imwrite(filename + "_rect_gray.jpg", im_rect_gray) print(filename + "_rect_gray.jpg") show_img(im_rect_gray) #2値化 im_rect_blur = cv2.fastNlMeansDenoising(im_rect_gray) im_rect_th = cv2.adaptiveThreshold(im_rect_blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2) rect_th_filename = "{:s}_rect_th.jpg".format(filename) cv2.imwrite(rect_th_filename, im_rect_th) print (filename+'_rect_th.jpg') show_img(im_rect_th) return (im_rect_th)
#実際に文字認識を行う
from PIL import Image
import sys
import pyocr
import pyocr.builders
def detect_txt(file):
tools = pyocr.get_available_tools() if len(tools) == 0: print("No OCR tool found") sys.exit(1) tool = tools[0] txt = tool.image_to_string( # ここでOCRの対象や言語,オプションを指定する Image.open('reciet1_rect_th.jpg'), lang='jpn', builder=pyocr.builders.TextBuilder() ) txt = open("out.txt").read().replace('\', '¥') print(txt)
#実行
if name == 'main':
detect_txt(convert("./reciet1.jpg", "reciet1"))
python
補足情報(FW/ツールのバージョンなど)
独学で勉強している初心者であり,teratailなどの質問場所を利用するのも初めてなので,ルールなど詳しくないのでご無礼ありましたら申し訳ございません。
あなたの回答
tips
プレビュー