小白也能懂!OpenCV 提取信用卡数字(附完整可运行代码)
大家好,今天咱们用 Python+OpenCV 做一个超实用的小项目 —— 自动提取信用卡上的数字!全程避开复杂术语,用大白话讲清楚每一步,哪怕是刚接触 OpenCV 的小白也能跟着跑通。
·
大家好,今天咱们用 Python+OpenCV 做一个超实用的小项目 —— 自动提取信用卡上的数字!全程避开复杂术语,用大白话讲清楚每一步,哪怕是刚接触 OpenCV 的小白也能跟着跑通。
先看效果
原始信用卡图片 → 经过图像处理 → 精准定位并提取数字区域,最终能识别出信用卡号(今天先做「定位数字区域」,识别数字下期讲)。
准备工作
1. 安装必备库
打开终端 / 命令行,输入以下命令安装 OpenCV(图像处理核心库):
bash
运行
pip install opencv-python
pip install numpy # OpenCV依赖的数值计算库
2. 核心思路(大白话版)
要提取信用卡数字,本质是「让数字从背景中凸显出来」,步骤拆解:
- 预处理:把图片调小、转成灰度图(减少颜色干扰);
- 增强对比:用「顶帽操作」让数字更清晰;
- 合并数字:用「闭操作」把分散的数字笔画连在一起;
- 二值化:把图片变成黑白(数字黑、背景白);
- 找轮廓:让程序识别出数字的边框位置;
- 筛选:只保留符合数字比例的区域(排除无效轮廓)。
完整可运行代码(复制即用)
先把代码完整复制下来,保存为credit_card_digit.py,后面逐行解释:
python
运行
import cv2
import numpy as np
# 自定义工具函数:显示图像(小白友好,避免重复写代码)
def cv_show(name, img):
cv2.imshow(name, img) # 显示图像窗口
cv2.waitKey(0) # 按任意键关闭窗口
cv2.destroyAllWindows()# 释放窗口资源
# 自定义工具函数:等比例调整图片大小(避免拉伸变形)
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)
return resized
# ----------------------核心代码开始----------------------
# 1. 读取并预处理图片(替换成你的信用卡图片路径)
image_path = "credit_card.jpg" # 改成你的图片路径!!!
image = cv2.imread(image_path)
if image is None:
print("❌ 错误:没找到图片,请检查路径是否正确!")
exit()
image = resize(image, width=300) # 统一调整图片宽度为300(方便后续处理)
cv_show("原始图片", image) # 显示原始图片
# 2. 转灰度图(去掉颜色,只保留明暗)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv_show("灰度图", gray)
# 3. 顶帽操作:突出亮的细节(数字),压暗背景
# 初始化卷积核(可以理解为「处理图片的小刷子」)
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3)) # 长方形刷子(适合数字的形状)
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
cv_show("顶帽操作后(数字更清晰)", tophat)
# 4. 闭操作:把数字的分散笔画连在一起(先膨胀再腐蚀)
closeX = cv2.morphologyEx(tophat, cv2.MORPH_CLOSE, rectKernel)
cv_show("闭操作后(数字连成块)", closeX)
# 5. 二值化:把图片变成纯黑纯白(数字黑,背景白)
# THRESH_OTSU:让程序自动找最佳阈值(不用手动调)
thresh = cv2.threshold(closeX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show("二值化后(黑白对比)", thresh)
# 6. 再做一次闭操作:加固数字区域(避免小缝隙)
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 正方形刷子
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)
cv_show("二次闭操作后", thresh)
# 7. 找轮廓:识别数字的边框
# cv2.RETR_EXTERNAL:只找最外层轮廓(避免数字内部的小轮廓)
cnts, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts_img = image.copy() # 复制原始图片,用来画轮廓
cv2.drawContours(cnts_img, cnts, -1, (0, 0, 255), 2) # 红色画出所有轮廓
cv_show("所有轮廓", cnts_img)
# 8. 筛选:只保留符合数字比例的轮廓
locs = [] # 存储数字区域的坐标
for c in cnts:
# 计算轮廓的外接矩形(x=横坐标,y=纵坐标,w=宽度,h=高度)
x, y, w, h = cv2.boundingRect(c)
ar = w / float(h) # 计算宽高比(数字的宽通常是高的2.5~4倍)
# 筛选条件:宽高比2.5~4,宽度40~55,高度10~20(根据你的图片调整)
if 2.5 < ar < 4.0 and (40 < w < 55) and (10 < h < 20):
locs.append((x, y, w, h)) # 符合条件的区域存起来
# 9. 按从左到右排序(信用卡数字是从左到右的)
locs = sorted(locs, key=lambda x: x[0])
print("✅ 找到的数字区域坐标:", locs)
# 10. 画出最终筛选后的数字区域(绿色框)
result_img = image.copy()
for (x, y, w, h) in locs:
cv2.rectangle(result_img, (x-2, y-2), (x+w+2, y+h+2), (0, 255, 0), 2)
cv_show("最终数字区域(绿色框)", result_img)
关键步骤解释(小白版)
1. 为什么要转灰度图?
彩色图片有 RGB 三个通道,处理起来复杂;灰度图只有「明暗」一个维度,程序更容易聚焦数字。
2. 顶帽操作是啥?
想象一下:信用卡背景是浅灰色,数字是白色。顶帽操作就像「把白色数字提起来,把灰色背景压下去」,让数字和背景的对比更强烈。
3. 闭操作有啥用?
信用卡数字的笔画可能有小缝隙(比如数字 8 中间的缝),闭操作先「膨胀」(把笔画变粗)再「腐蚀」(恢复原来的大小),刚好能把缝隙补上,让数字变成一个完整的块。
4. 筛选轮廓的条件怎么来的?
信用卡数字的宽通常是高的 2.5~4 倍,宽度大概 40~55 像素,高度 10~20 像素(如果你的图片大小不同,稍微调整这个数值就行)。
运行注意事项
- 替换图片路径:把代码里的
image_path = "credit_card.jpg"改成你自己的信用卡图片路径(建议用正面、清晰的图片); - 标红问题解决:如果代码标红,大概率是没装 OpenCV,重新运行
pip install opencv-python即可; - 图片找不到:如果提示「没找到图片」,检查路径是否正确(比如 Windows 路径要用
\\,比如C:\\images\\credit_card.jpg)。
更多推荐
所有评论(0)