Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3608540
  • 博文数量: 365
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2522
  • 用 户 组: 普通用户
  • 注册时间: 2019-10-28 13:40
文章分类

全部博文(365)

文章存档

2023年(8)

2022年(130)

2021年(155)

2020年(50)

2019年(22)

我的朋友

分类: Python/Ruby

2021-08-31 17:25:46

# 银行支票(从MICR字体中提取数字和符号提取)

# MICRMagnetic 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中提取数字和符号

# imageMICR E-13B字体图像(在代码下载中提供)。

# charCnts:包含参考图像中字符轮廓的列表

# minW:表示最小字符宽度的可选参数。这有助于当遇到23个小轮廓时,它们一起构成一个MICR字符。默认值为5像素的宽度。

# minH:最小字符高度。此参数是可选的,默认值为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:输入图像路径

# --referenceMICR 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字体图像,并进行预处理(转换灰度图、保持宽高比缩放为宽度400Octus二值化逆阈值化)外汇跟单gendan5.com以保证该图像以白色前景和黑色背景呈现,便于轮廓提取

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.434返回的轮廓不同,因此下一行代码来兼容不同版本及返回值

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)

阅读(1151) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~