大家好,今天咱们用 Python+OpenCV 做一个超实用的小项目 —— 自动提取信用卡上的数字!全程避开复杂术语,用大白话讲清楚每一步,哪怕是刚接触 OpenCV 的小白也能跟着跑通。

先看效果

原始信用卡图片 → 经过图像处理 → 精准定位并提取数字区域,最终能识别出信用卡号(今天先做「定位数字区域」,识别数字下期讲)。

准备工作

1. 安装必备库

打开终端 / 命令行,输入以下命令安装 OpenCV(图像处理核心库):

bash

运行

pip install opencv-python
pip install numpy  # OpenCV依赖的数值计算库

2. 核心思路(大白话版)

要提取信用卡数字,本质是「让数字从背景中凸显出来」,步骤拆解:

  1. 预处理:把图片调小、转成灰度图(减少颜色干扰);
  2. 增强对比:用「顶帽操作」让数字更清晰;
  3. 合并数字:用「闭操作」把分散的数字笔画连在一起;
  4. 二值化:把图片变成黑白(数字黑、背景白);
  5. 找轮廓:让程序识别出数字的边框位置;
  6. 筛选:只保留符合数字比例的区域(排除无效轮廓)。

完整可运行代码(复制即用)

先把代码完整复制下来,保存为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 像素(如果你的图片大小不同,稍微调整这个数值就行)。

运行注意事项

  1. 替换图片路径:把代码里的image_path = "credit_card.jpg"改成你自己的信用卡图片路径(建议用正面、清晰的图片);
  2. 标红问题解决:如果代码标红,大概率是没装 OpenCV,重新运行pip install opencv-python即可;
  3. 图片找不到:如果提示「没找到图片」,检查路径是否正确(比如 Windows 路径要用\\,比如C:\\images\\credit_card.jpg)。
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐