Bank Check OCR
Bank check OCR with OpenCV and Python (Part I) - PyImageSearch
Bank check OCR with OpenCV and Python (Part I) - PyImageSearch
Learn how to detect and recognize bank check and routing numbers using OCR, Python, and computer vision + image processing (Part I).
www.pyimagesearch.com
※ 주 의: 2017년에 작성된 포스팅
STEP 1: 데이터 준비
1. 필요한 모듈 임포트
2. 글자 추출 함수 정의
파라미터 (이미지, 글자 컨투어, 최소 너비, 최소 높이)
파이썬에는 자바와 달리 hasNext 같은 메소드 없음, 그래서 예외처리로 다뤄야 함.
3. 반복문 시작
4. 최소 기준 확인 및 roi & loc 추출
5. 글자 아니면 기호라고 간주하고 처리
np.inf == infinite
6. 반복문 탈출 (roi & loc 반환)
7. 아규먼트 파싱
8. 정답지 만들기 part 1
9. 정답지 만들기 part 2
읽어들이고 전처리 실시 (흑백 전환, 크기 조정, 이진화)
결과:
STEP 2: 바운딩 박스 추출
10. 바운딩박스 그리는 간단한 방식
결과:
11. 바운딩박스 그리는 더 정교한 방식
코드 초반에 정의한 extract_digits_and_symbols 함수 활용함
결과:
cmd로 실행하기
코드 정리:
# import the necessary packages
from skimage.segmentation import clear_border
from imutils import contours
import numpy as np
import argparse
import imutils
import cv2
def extract_digits_and_symbols(image, charCnts, minW=5, minH=15):
# grab the internal Python iterator for the list of character
# contours, then initialize the character ROI and location
# lists, respectively
charIter = charCnts.__iter__()
rois = []
locs = []
# keep looping over the character contours until we reach the end
# of the list
while True:
try:
# grab the next character contour from the list, compute
# its bounding box, and initialize the ROI
c = next(charIter)
(cX, cY, cW, cH) = cv2.boundingRect(c)
roi = None
# check to see if the width and height are sufficiently
# large, indicating that we have found a digit
if cW >= minW and cH >= minH:
# extract the ROI
roi = image[cY:cY + cH, cX:cX + cW]
rois.append(roi)
locs.append((cX, cY, cX + cW, cY + cH))
# otherwise, we are examining one of the special symbols
else:
# MICR symbols include three separate parts, so we
# need to grab the next two parts from our iterator,
# followed by initializing the bounding box
# coordinates for the symbol
parts = [c, next(charIter), next(charIter)]
(sXA, sYA, sXB, sYB) = (np.inf, np.inf, -np.inf,
-np.inf)
# loop over the parts
for p in parts:
# compute the bounding box for the part, then
# update our bookkeeping variables
(pX, pY, pW, pH) = cv2.boundingRect(p)
sXA = min(sXA, pX)
sYA = min(sYA, pY)
sXB = max(sXB, pX + pW)
sYB = max(sYB, pY + pH)
# extract the ROI
roi = image[sYA:sYB, sXA:sXB]
rois.append(roi)
locs.append((sXA, sYA, sXB, sYB))
# we have reached the end of the iterator; gracefully break
# from the loop
except StopIteration:
break
# return a tuple of the ROIs and locations
return (rois, locs)
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
help="path to input image")
ap.add_argument("-r", "--reference", required=True,
help="path to reference MICR E-13B font")
args = vars(ap.parse_args())
# initialize the list of reference character names, in the same
# order as they appear in the reference image where the digits
# their names and:
# T = Transit (delimit bank branch routing transit #)
# U = On-us (delimit customer account number)
# A = Amount (delimit transaction amount)
# D = Dash (delimit parts of numbers, such as routing or account)
charNames = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0",
"T", "U", "A", "D"]
# load the reference MICR image from disk, convert it to grayscale,
# and threshold it, such that the digits appear as *white* on a
# *black* background
ref = cv2.imread(args["reference"])
ref = cv2.cvtColor(ref, cv2.COLOR_BGR2GRAY)
ref = imutils.resize(ref, width=400)
ref = cv2.threshold(ref, 0, 255, cv2.THRESH_BINARY_INV |
cv2.THRESH_OTSU)[1]
# find contours in the MICR image (i.e,. the outlines of the
# characters) and sort them from left to right
refCnts = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
refCnts = imutils.grab_contours(refCnts)
refCnts = contours.sort_contours(refCnts, method="left-to-right")[0]
# create a clone of the original image so we can draw on it
clone = np.dstack([ref.copy()] * 3)
# loop over the (sorted) contours
for c in refCnts:
# compute the bounding box of the contour and draw it on our
# image
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(clone, (x, y), (x + w, y + h), (0, 255, 0), 2)
# show the output of applying the simple contour method
cv2.imshow("Simple Method", clone)
# extract the digits and symbols from the list of contours, then
# initialize a dictionary to map the character name to the ROI
(refROIs, refLocs) = extract_digits_and_symbols(ref, refCnts,
minW=10, minH=20)
chars = {}
# re-initialize the clone image so we can draw on it again
clone = np.dstack([ref.copy()] * 3)
# loop over the reference ROIs and locations
for (name, roi, loc) in zip(charNames, refROIs, refLocs):
# draw a bounding box surrounding the character on the output
# image
(xA, yA, xB, yB) = loc
cv2.rectangle(clone, (xA, yA), (xB, yB), (0, 255, 0), 2)
# resize the ROI to a fixed size, then update the characters
# dictionary, mapping the character name to the ROI
roi = cv2.resize(roi, (36, 36))
chars[name] = roi
# display the character ROI to our screen
cv2.imshow("Char", roi)
cv2.waitKey(0)
# show the output of our better method
cv2.imshow("Better Method", clone)
cv2.waitKey(0)
cv2.waitKey(0)
# cmd
# $ python bank_check_ocr.py --image example_check.png \
# --reference micr_e13b_reference.png