使用Python,OpenCV進行銀行支票數字和符號的OCR

專注的阿熊發表於2021-08-31

# 銀行支票(從 MICR 字型中提取數字和符號提取)

# MICR Magnetic Ink Character Recognition 磁性墨水字元識別)是一種用於處理文件的金融行業技術。

# 經常會在對賬單和支票的底部發現這種 E-13B 格式的磁性墨水。

# USAGE

# python bank_check_ocr.py --image images/example_check.jpg --reference images/micr_chars.png

# 匯入必要的包

from skimage.segmentation import clear_border # pip install -U scikit-image

from imutils import contours # pip install --upgrade imutils

import numpy as np # pip install numpy

import argparse

import imutils

import cv2

# MICR 中提取數字和符號

# image MICR E-13B 字型影像(在程式碼下載中提供)。

# charCnts :包含參考影像中字元輪廓的列表

# minW :表示最小字元寬度的可選引數。這有助於當遇到 2 3 個小輪廓時,它們一起構成一個 MICR 字元。預設值為 5 畫素的寬度。

# minH :最小字元高度。此引數是可選的,外匯跟單gendan5.com預設值為 15 畫素。 rational 的用法與 minW 相同

def extract_digits_and_symbols(image, charCnts, minW=5, minH=15):

     # 獲取字元輪廓列表的內建 Python 迭代器

     # 並分別初始化存放 ROI 和位置的列表

     charIter = charCnts.__iter__()

     rois = []

     locs = []

     # Python 迭代器沒有 Java 等語言中的“ hasNext ”方法——相反,當 iterable 物件中沒有更多項時, Python 將丟擲異常。

     # 因此在函式中使用 try-catch 塊來解釋此異常。

     # 保持遍歷字元輪廓,知道到達 list 末尾

     while True:

         try:

             # list 獲取下一個字元輪廓,計算邊界框,初始化 ROI

             c = next(charIter)

             (cX, cY, cW, cH) = cv2.boundingRect(c)

             roi = None

             # 檢查是否邊界框寬度、高度足夠大,代表著其是否是數字,還是需要合併 3 個輪廓為 1 個的符號區

             if cW >= minW and cH >= minH:

                 # 提取 ROI

                 roi = image[cY:cY + cH, cX:cX + cW]

                 rois.append(roi)

                 locs.append((cX, cY, cX + cW, cY + cH))

             # 反之,認為其是符號區的特殊符號之一(需要 3 個合併一個的部分)

             # MICR 符號區包含 3 部分,需要合併

             else:

                 # 從迭代器提取接下來的倆部分,然後計算邊界框

                 parts = [c, next(charIter), next(charIter)]

                 (sXA, sYA, sXB, sYB) = (np.inf, np.inf, -np.inf,

                                         -np.inf)

                 # 遍歷每一部分

                 for p in parts:

                     # 計算每一部分的邊界框,並更新紀薄變數

                     (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)

                 # 提取 ROI

                 roi = image[sYA:sYB, sXA:sXB]

                 rois.append(roi)

                 locs.append((sXA, sYA, sXB, sYB))

         # 優雅的退出迴圈

         except StopIteration:

             break

     # 返回區域 rois 和位置 locs 的元祖

     return (rois, locs)

# 構建命令列引數及解析

# --image :輸入影像路徑

# --reference MICR E-13B 字型參考影像

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())

# 為每個字元和符號建立參考字元名稱 list ,和它們在圖片中出現的順序一樣

# T = Transit 劃界銀行分行路由中轉

# U = On-us 界定客戶賬號

# A = Amount 界定交易金額

# D = Dash 分隔數字的各個部分,如路由或帳戶

# 由於 OpenCV 不支援 unicode 繪圖字元,需要定義“ T ”表示傳輸,“ U ”表示客戶帳號,“ A ”表示數量,“ D ”表示破折號。

charNames = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0",

              "T", "U", "A", "D"]

# 從磁碟載入 MICR 字型影像,並進行預處理(轉換灰度圖、保持寬高比縮放為寬度 400 Octus 二值化逆閾值化)以保證該影像以白色前景和黑色背景呈現,便於輪廓提取

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]

# MICR 影像查詢字元的輪廓線,並從左到右排序輪廓

refCnts = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL,

                            cv2.CHAIN_APPROX_SIMPLE)

# 注意: OpenCV 2.4 3 4 返回的輪廓不同,因此下一行程式碼來相容不同版本及返回值

refCnts = imutils.grab_contours(refCnts)

refCnts = contours.sort_contours(refCnts, method="left-to-right")[0]

# 複製一個原始影像

clone = np.dstack([ref.copy()] * 3)

# 遍歷排序過的輪廓

for c in refCnts:

     # 計算邊界框,並繪製邊界框綠色在影像上

     (x, y, w, h) = cv2.boundingRect(c)

     cv2.rectangle(clone, (x, y), (x + w, y + h), (0, 255, 0), 2)

# 展示應用簡單輪廓方法的結果

cv2.imshow("Simple Method", clone)

cv2.waitKey(0)

# 使用更高階的方法:從輪廓列表中提取數字和符號,然後初始化一個字典來匹配字元名稱和 ROI

(refROIs, refLocs) = extract_digits_and_symbols(ref, refCnts,

                                                 minW=10, minH=20)

chars = {}

# 重新初始化克隆影像以便於繪製輪廓

clone = np.dstack([ref.copy()] * 3)

# 遍歷參考 list 的名稱、區域及位置

for (name, roi, loc) in zip(charNames, refROIs, refLocs):

     # 在輸出影像上繪製字元邊界框

     (xA, yA, xB, yB) = loc

     cv2.rectangle(clone, (xA, yA), (xB, yB), (0, 255, 0), 2)

     # 縮放 ROI 區域為固定大小 36*36 ,然後更新匹配字元名稱和 ROI 影像的字典

     roi = cv2.resize(roi, (36, 36))

     chars[name] = roi

     # 展示字元 ROI 到螢幕

     cv2.imshow("Char", roi)

     cv2.waitKey(0)

# 展示更高階方法提取的效果圖

cv2.imshow("Better Method", clone)

cv2.waitKey(0)


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69946337/viewspace-2789729/,如需轉載,請註明出處,否則將追究法律責任。

相關文章