Opencv实现提取卡号(数字识别)
直接开始
实行方法
-
解析命令行参数:使用
argparse
库来解析命令行输入,确保用户提供了输入图像和模板图像的路径。 -
读取模板图像:使用
cv2.imread()
函数读取模板图像的路径,并显示原始图像。 -
图像预处理:
- 将图像转换为灰度图,以简化后续处理。
- 应用二值化操作(使用阈值10)将图像转换为二值图像(黑白图),并通过
cv2.THRESH_BINARY_INV
反转颜色,使前景(数字)为白色,背景为黑色。 - 显示预处理后的二值图像。
-
轮廓检测:
- 使用
cv2.findContours()
函数在二值图像上检测轮廓。这里只检测外部轮廓,并使用cv2.CHAIN_APPROX_SIMPLE
方法来简化轮廓形状。 - 在原始图像上绘制检测到的轮廓,并显示结果。
- 使用
-
轮廓排序:
- 使用自定义的
myutils.sort_contours()
函数对检测到的轮廓进行排序,这里假设该函数按照从左到右的顺序排序轮廓。
- 使用自定义的
自定义的myutils库
import cv2
def sort_contours(cnts, method='left to-right'):
reverse = False
i = 0
if method == 'right-to-left' or method == 'bottom-to-top':
reverse = True
if method == 'top-to-bottom' or method == 'bottom-to-top':
i = 1
boundingBoxes = [cv2.boundingRect(c) for c in cnts]
(cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),
key=lambda b: b[1][i], reverse=reverse))
return cnts, boundingBoxes
def resize(image,width=None,height=None ,inter=cv2.INTER_AREA):
dim = None
(h, w) = image.shape[:2]
if width is None and height is None:
return image
if width is None:
r = height / float(h)
dim = (int(w * r), height)
else:
r = width / float(w)
dim = (width, int(h * r))
resized = cv2.resize(image, dim, interpolation=inter) # 默认为cV2.INTER_AREA,即面积插值,适用于缩放图像。
return resized
-
数字模板提取:
- 遍历排序后的轮廓,对每个轮廓计算其外接矩形,并裁剪出相应的区域(ROI,Region of Interest)。
- 将每个裁剪出的ROI区域缩放到固定大小(57x88),以便于后续与输入图像中的数字进行匹配。
- 将每个缩放后的ROI存储到
digits
字典中,其中键为轮廓的索引,值为对应的数字模板图像。
接下来,你可能会想要使用这些数字模板与输入图像中的数字进行匹配,以确定输入图像中每个数字的具体值。这通常涉及到模板匹配技术,如使用cv2.matchTemplate()
函数。
- 读取输入图像。
- 对输入图像进行类似的预处理步骤。
- 在输入图像上检测可能的数字区域。
- 对每个检测到的数字区域,使用提取的模板进行匹配,以确定其值。
- 根据识别出的数字进行进一步的处理或分析(如确定信用卡类型等)。
import argparse
import cv2
import myutils
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
help="path to input image")
ap.add_argument("-t", "--template", required=True,
help="path to template OCR-A image")
args = vars(ap.parse_args())
#创建ArgumentParser对象来解析命令行参数。
#添加两个必需的参数:-i/--image(输入图像路径)和-t/--template(模板图像路径)。
#使用parse_args()解析命令行输入,并将结果转换为字典存储在args中。
FIRST_NUMBER = {
"3": "American Express",
"4": "Visa",
"5": "MasterCard",
"6": "Discover Card"
}
#定义一个字典,将信用卡号码的首位数字映射到对应的信用卡类型。
def cv_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(0)
#一个简单的函数,用于在窗口中显示图像,并等待用户按键。
img = cv2.imread(args["template"])
cv_show('img', img)
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv_show('ref', ref)
ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1]
cv_show('ref', ref)
# 计算轮廓:cv2.findcontours()函数接受的参数为二值图,
# 即黑白的(不是灰度图)CV2.RETR_EXTERNAL只检测外轮廓,
# CV2.CHAIN_APPROX_SIMPLE只保留终点坐标
_, refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, refCnts, -1, (0, 0, 255), 3)
cv_show('img', img)
#使用findContours函数检测二值图像中的轮廓。
#在原始图像上绘制检测到的轮廓。
#假设myutils.sort_contours函数存在,并按从左到右的顺序对轮廓进行排序。注意这里[0]可能是为了处理#sort_contours返回值的格式,具体取决于该函数的实现。
#refCnts = myutils.sort_contours(refCnts, method="left-to-right")[0]
digits = {}
for (i, c) in enumerate(refCnts): # 遍历每一个轮廓
(x, y, w, h) = cv2.boundingRect(c) # 计算外接矩形并且resize成合适大小
roi = ref[y:y + h, x:x + w]
roi = cv2.resize(roi, (57, 88)) # 缩放到指定的大小
digits[i] = roi # 每一个数字对应每一个模板
#遍历排序后的轮廓。
#对每个轮廓,计算其外接矩形,并裁剪出相应的区域(ROI)。
#将每个ROI缩放到固定大小(57x88)。
#将缩放后的ROI存储在digits字典中,键为轮廓的索引
代码效果: