OpenCV轮廓检测实战:二值化与特征计算
统计对象为图中的像素点,把图像分解为一个一个的像素点进行统计横坐标 #像素值(0-255)纵坐标 #每一个像素点对应出现的次数原理:统计图像中每个灰度级(0-255)出现的次数,形成直方图。
课时十六:轮廓检测方法和检测结果
1.图像轮廓

模式(mode)建议使用RETR_TREE
为了提高准确率,使用二值图像
轮廓 #整体联通的边界,反映物体形状
边缘 #零散的线段,反映梯度变化
先输入一张图像(img),再转换为灰度图,再对图像数据进行二值化处理,得出结果
实战:


2.绘制轮廓
#传入绘制图像,轮廓,轮廓索引,颜色模式,线条厚度
#注意需要copy,否则原图会变
#阈值处理时建议使用127作为分界点
实战:
import cv2
# 假设 img 是已加载的图像
draw_img = img.copy()
# 1. 预处理:转为灰度图 + 二值化(轮廓检测的必要步骤)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 2. 检测轮廓,生成 contours 变量
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 3. 绘制轮廓(注意:建议在副本 draw_img 上绘制,避免修改原图)
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2)
# 显示结果(假设 cv_show 是你封装的显示函数)
cv_show(res, 'res')
# contours 轮廓是什么 -1 把所有轮廓都画进去(数字是几画进去几个轮廓,按顺序进行选择) (0,0,255)B G R用R(红色)画轮廓 2 线条宽度(建议2-3)
课时十七:轮廓特征与近似
1.轮廓特征
注:辅助函数计算轮廓特征时需要把各个轮廓都拿出来才能使用,eg:cnt=contours[0]获取第0个轮廓
面积计算调用cv2.contourArea(cnt)函数
周长计算调用cv2.arcLength(cnt,True)函数,Ture(表示闭合)
2.轮廓近似
曲线AB(AB连成一条直线),取曲线上一点C,使得C与直线AB距离最大(距离设为d)
若d<T(阈值),可以近似,曲线AB可以近似为直线AB

曲线AB(AB连成一条直线),取曲线上一点C,使得C与直线AB距离最大(距离设为d1),且连接AC,BC,曲线上一点距AC为d2
若d1>T(阈值),且d2<T,曲线AC可近似为直线AC(若不满足,继续画线,直到满足为止)
实战:



3.边界矩形
实战:



4.外接圆
实战:
# 读取图片 图像预处理(转灰度,二值化)检测轮廓得到cnt变量 选择目标轮廓 计算最小外接圆 在原图上绘制最小外接圆 显示结果


课时十八:模板匹配方法
1.读取图像

# 模板在原图像上从原点开始滑动,计算模板与图像被覆盖区域的差别程度,每次计算结果存入一个矩阵作为结果输出,假如原图像大小为A*B,模板大小为a*b,则输出结果的矩阵为(A-a+1)*(B-b+1)
# 匹配过程类似卷积操作,通过逐个像素点比较差异,计算像素值平方差或相关系数
2.模板匹配方法


3.读入效果示例


原始输入lena.jpg(263*263灰度图)
模板face.jpg(110*85人脸区域)
输出结果154*179矩阵
4.结果分析

使用函数cv2.minMaxLoc(res)返回四个值:
min_val:最小值
max_val:最大值
min_loc:最小值坐标
max_loc:最大值坐标
平方差方法:(SQDIFF)取最小值位置
相关方法:(CCORR/CCOEFF)取最大值位置
课时十九:匹配效果展示
1.操作步骤
(1)读取图像
读取原始图像和模板图像并转为灰度图(模板尺寸不大于原图)
(2)处理函数

调用cv2.matchTemplate(img,template,method)函数
img #待搜索图像(18/32位浮点型)
templ #搜索模板(同类型且不大于img)
method #匹配方法枚举值
归一化 #不同方法结果值范围差异较大,归一化结果在固定范围内,便于比较
实战:







2.匹配多个对象


# 读取原始图像和模板图像 转换为灰度图 获取模板的高度和宽度 进行模板匹配 设置匹配阈值 找出所有匹配位置 遍历匹配位置并绘制矩形框
使用归一化相关系数法(TM_CCOEFF_NORMED),值越接近1表示匹配度越高(0.8表示只保留匹配度大于80%的结果)
课时二十:直方图定义
1.定义

统计对象为图中的像素点,把图像分解为一个一个的像素点进行统计
横坐标 #像素值(0-255)纵坐标 #每一个像素点对应出现的次数
原理:统计图像中每个灰度级(0-255)出现的次数,形成直方图
2.方法
函数cv2.calcHist(images,channels,mask,histSize,ranges)
images:输入图像,格式为uint8/float32,传入函数时应用中括号[]括起来,eg:[img]
channels:通道索引,灰度图为[0],彩色图可选为[0][1][2]分别对应BGR三通道
mask:掩模图像,None表示统计整幅图像的直方图,统计图像某一部分就制作掩模图像并使用它(局部统计)
histSize:BIN数目,用中括号括起来,eg:[256]表示分成256个区间
ranges:像素值范围,通常为[0,256](包含0,但不包含256)

![]()
3.可视化操作
灰度图:


# matplotlib默认顺序RGB,Opencv默认顺序BGR
彩色图:


# 彩色图需分别计算BGR三个通道的直方图
# 使用for循环enumerate遍历颜色通道,i对应通道索引(0:蓝,1:绿,2:红)
课时二十一:均值化原理
1. 掩码操作


# 使用np.zeros(img.shape[:2], np.uint8)创建与图像大小相同的二维零矩阵 将需要保留的区域设置为255:mask[100:300, 100:400] = 255(图像大小要保持一致)使用img.shape获取原图尺寸
# 掩码只有两部分组成:0(黑色)和255(白色)
# 通过cv2.bitwise_and()执行与操作,保留掩码为255的区域
# 黑色区域(0值)会被完全过滤掉,相当于图像截取操作

原始图像:完整的灰度图
掩码图像:黑白分明的矩形区域
处理后图像:只保留掩码白色区域的图像内容
直方图对比:显示原始图像和掩码区域的像素分布差异
实战:


2.直方图均衡化

# 统计各灰度值的像素个数 计算每个灰度值的概率 计算累积概率 映射新灰度值
eg:
原始灰度值50(4个像素)→ 累积概率0.25 → 新值64
灰度值128(3个像素)→ 累积概率0.4375 → 新值112
最终效果:将"瘦高"的直方图分布变为"矮胖"的均衡分布
核心思想:通过累积概率函数实现灰度值重新分配,使像素值分布更均匀
知识小结
|
知识点 |
核心内容 |
操作要点 |
应用示例 |
|
掩码(Mask)原理 |
黑白二值矩阵(0/255)用于图像区域选择 |
1. 创建与图像同尺寸的无符号矩阵 2. 目标区域设为255 3. 与原始图像进行按位与操作 |
猫图像局部提取:掩码白色区域保留原图,黑色区域置零 |
|
直方图均衡化 |
通过概率分布映射改善图像对比度 |
1. 统计各灰度级出现频率 2. 计算累积分布函数 3. 映射到新灰度级(公式:新值=累积概率×255) |
狗图像处理:原集中分布的灰度值被拉伸到更广范围 |
|
掩码实操步骤 |
OpenCV实现流程 |
1. np.zeros()初始化 2. image.shape保持尺寸一致 3. 指定ROI区域赋值为255 |
演示代码展示掩码创建、图像叠加和效果对比 |
|
直方图对比分析 |
均衡化前后数据对比 |
1. 原始直方图呈现"瘦高"分布 2. 均衡后变为"矮胖"分布 3. 亮度通道明显改善 |
并排显示原始图像/直方图与处理后效果 |
|
技术要点 |
关键参数说明 |
1. 掩码必须为8位无符号整型 2. 直方图均衡化需先计算概率分布 3. 最终灰度值需四舍五入取整 |
代码演示中强调dtype=np.uint8的重要性 |
课时二十二:均衡化效果
1.均衡化

调用函数cv2.equalizeHist(img)实现直方图均衡化
效果对比 #均衡化后直方图分布从"尖峰状"变为"扁平状",原图中较少的灰度值(如小值区域)在均衡化后明显增多

原始图像 #色彩较淡,细节模糊
均衡化图像 #对比度增强,整体更明亮
适用于需要增强图像整体对比度的场景,如医学影像、低光照图像处理

在人物面部图像中,均衡化使皮肤区域亮度提升,但可能丢失阴影细节
全局均衡化会导致:高光区域过曝(如面部反光)阴影细节消失(如五官轮廓)
2.自适应直方图均衡化

将图像划分为8×8的局部区域单独均衡化,避免全局处理导致的细节丢失
clipLimit=2.0# 对比度限制阈值
tileGridSize=(8,8)# 分块尺寸

A(原始):细节完整但对比度低
B(全局均衡):丢失纹理细节
C(自适应):保留细节同时提升对比度
适用于需要保留局部特征的图像 eg:人脸识别中的微表情 医学影像的病灶区域
知识小结
|
知识点 |
核心内容 |
操作演示 |
技术对比 |
|
直方图均衡化 |
改善图像像素分布不均匀问题 |
使用OpenCV的equalizeHist()函数处理猫图像 |
全局均衡化 vs 自适应均衡化 |
|
自适应均衡化 |
分块处理保留局部细节 |
CLAHE算法实现8×8网格处理 |
消除全局均衡化的细节丢失问题 |
课时二十三:频率变换结果
傅里叶变换

时域# 以时间为参照的分析方式,如早上7点吃早饭、8点挤地铁等随时间变化的事件(动态)
频域# 从更高视角观察事件的重复规律,如"每天吃早饭"、"工作日挤地铁"等周期性行为(静态)频域就像上帝视角,不关心具体时间点发生什么,只关注事件的重复规律
图像:


所有周期函数都能用正弦波叠加
低频部分(最前边):变化缓慢,振幅较大(如基础波形)
高频部分(最后边):变化剧烈,振幅较小(如细节部分)
课时二十四:低频和高频的使用
1.傅里叶变换的作用
(1)高频
定义: 图像中变化剧烈的灰度分量
特征:边界区域(如船与水的交界处)灰度值变化迅速 对应频域图像的外围区域
(2)低频
定义: 变化缓慢的灰度分量
特征:非边界区域(如茫茫大海)灰度值变化平缓 对应频域图像的中心区域
(3)处理函数
cv2.dft(): 执行傅里叶变换,变为频域
cv2.idft(): 执行逆傅里叶变换
# 输入图像需转换为np.float32格式 频率为0的部分初始在左上角,需通过shift变换到中心位置 cv2.dft()返回结果为双通道(实部+虚部),需转换为图像格式(0-255)显示
# 输入图像 将图像转换为np.float32格式 执行cv2.dft() 使用np.fft.fftshift()将低频值转换到中心位置 两通道转换将结果映射到0-255之间20*np.log(cv2.magnitude()) 可视化显示
实战:


# 中心低频(亮度高)外围高频(亮度低)离中心越近频率越低,离中心越远频率越高
2.滤波

(1)低通滤波器
保留低频信息,去除高频,使图像变模糊(边界信息减弱)
(2)高通滤波器
保留高频信息,去除低频,使图像细节增强(边界锐化)
(3)处理函数
# 输入图像 将图像转换为np.float32格式 执行cv2.dft() 使用np.fft.fftshift()将低频值转换到中心位置 创建掩膜将中心区域设为1,其余为0 逆变换还原图像
低通滤波器实战:


# 图像变得模糊,保留整体轮廓但弱化细节
高通滤波器实战:


# 主体内部区域变为空白,突出图像边缘和细节
# 分离低频/高频层次分明更直观
知识小结
|
知识点 |
核心内容 |
考试重点/易混淆点 |
难度系数 |
|
傅里叶变换基本概念 |
将图像从空间域转换到频域进行分析处理 |
频域中低频/高频的分布规律(低频集中在中心) |
⭐⭐⭐ |
|
低通滤波器原理 |
保留低频信息(图像平滑区域),过滤高频信息(边缘细节) |
应用效果:图像模糊化(如示例中帽子/脸部细节丢失) |
⭐⭐ |
|
高通滤波器原理 |
保留高频信息(边缘轮廓),过滤低频信息(平滑区域) |
应用效果:边界锐化(如示例仅保留人像轮廓) |
⭐⭐ |
|
OpenCV实现关键步骤 |
1. 强制转换为float32格式 2. fftshift中心化处理 3. 双通道结果转图像格式 |
易忽略点:逆变换前需ifftshift还原频域分布 |
⭐⭐⭐⭐ |
|
频域处理优势 |
在频域中分离低频/高频比空间域操作更高效 |
典型应用场景:噪声消除(低频)/ 边缘增强(高频) |
⭐⭐⭐ |
课时二十五:信用卡数字的识别
流程解析

# 输入银行卡图像,识别并输出卡号数字序列及其对应位置,类似车牌识别系统
# 必须使用与目标字体风格一致的模板(如银行卡专用字体)包含完整数字集0-9 每个数字需单独提取保存
# 模板匹配将待识别区域与所有模板数字进行相似度计算(采用平方差等匹配方法)相似度最高的模板数字即为识别结果
# 输入图像 转换为灰度图 二值化处理 轮廓检测(根据长宽比过滤非数字轮廓(如文字logo)(银行卡数字具有特定长宽比例特征) 保留符合数字特征的轮廓) 外接矩形获取
# 将检测区域resize到与模板相同尺寸 保证匹配时的尺度一致性
知识小结
|
知识点 |
核心内容 |
考试重点/易混淆点 |
难度系数 |
|
模板匹配原理 |
通过对比目标区域与预存模板的相似度(如平方差计算)识别数字 |
需区分模板匹配与特征匹配的适用场景 |
⭐⭐⭐ |
|
轮廓检测应用 |
提取数字外轮廓后生成外接矩形,用于标准化匹配区域 |
注意过滤非数字轮廓(如文字/logo)的长宽比筛选 |
⭐⭐ |
|
预处理流程 |
灰度转换→二值化→形态学操作(开运算/闭运算)优化轮廓质量 |
形态学操作顺序对结果影响显著 |
⭐⭐⭐⭐ |
|
银行卡数字识别步骤 |
1. 模板数字分离 2. 输入图像区域检测 3. 轮廓匹配与Resize 4. 分组输出 |
四分组逻辑需与卡号标准格式对齐 |
⭐⭐⭐ |
|
项目实战难点 |
字体特异性(如银行卡专用字体需定制模板) |
模板字体一致性决定匹配准确率 |
⭐⭐⭐⭐ |
课时二十六:环境配置和预处理
1.模板匹配

指定输入图像(-i)和模板图像(-t)两个参数,通过命令行执行时使用-i和-t参数传递
-i #指定输入图像的路径,如信用卡图像
-t #指定模板图像的路径,包含待匹配的数字模板
2.环境配置
需要支持debug功能的IDE,不限定具体工具 eg:Eclipse(支持多语言:Python/Java/C)PyCharm Jupyter Notebook(作者使用的是PyCharm)
3.参数配置
右键点击.py文件选择"Run As" 选择"Run Configurations" 在"Program arguments"中输入参数:
-i images/credit_card_03.png -t images/ocr_a_reference.png 点击Apply保存配置
配置完成后直接运行,验证是否能正常输出结果
4.处理流程
(1) 灰度处理和二值处理



- 灰度转换:将彩色模板图像转换为灰度图,调用cv2.cvtColor()cv2.cvtColor()cv2.cvtColor()函数,参数为cv2.COLOR_BGR2GRAY
- 二值化处理:对灰度图进行阈值处理,调用cv2.threshold()cv2.threshold()cv2.threshold()函数,参数为cv2.THRESH_BINARY_INV(反二值化),阈值设为10,最大值255
- 处理原因:轮廓检测需要输入二值图像,且原图背景为白色(255),数字为黑色(0),反二值化后数字变为白色(255)
(2) 检测轮廓

- 轮廓检测:调用cv2.findContours()cv2.findContours()cv2.findContours()函数,参数为:
-
cv2.RETR_EXTERNAL:只检测外轮廓
-
cv2.CHAIN_APPROX_SIMPLE:仅保留轮廓终点坐标
-
- 外接矩形:检测到轮廓后计算每个数字的外接矩形,用于后续模板匹配
- 轮廓排序:使用myutils.sortcontours()方法按从左到右顺序排列数字轮廓
(3)模板匹配

- 匹配过程:对信用卡图像中的每个数字区域与模板进行匹配
- 结果验证:示例中正确识别出信用卡类型为MasterCard,卡号为5412751234567890
课时二十七:模板处理方法
1.读入模板图像

- 图像特性:模板图像边界为白色(像素值255),数字为黑色
- 读取方法:调用cv2.imread()函数读取参数指定的模板路径
2.转为灰度图

使用cv2.cvtColor()函数进行BGR到灰度的转换
# 灰度图仍保留原始图像的明暗关系,但仅含单通道
3.二值化处理

- 阈值选择:阈值设为10,低于阈值设为0(黑),高于阈值设为255(白)
- 反二值化:使用THRESH_BINARY_INV使数字区域变为白色(255),便于轮廓检测
# 轮廓检测函数cv2.findContours()要求输入必须是二值图像
4.轮廓检测

- 关键参数:使用cv2.findContours()时,第一个参数需要传入图像的.copy()副本,因为原始图像后续还需要使用。
- 轮廓模式:参数cv2.RETR_EXTERNAL表示只检测外轮廓,内轮廓对数字识别任务无用。
- 轮廓近似方法:使用cv2.CHAIN_APPROX_SIMPLE来保留轮廓的关键坐标点。
- 绘制技巧:cv2.drawContours()中参数-1表示绘制所有检测到的轮廓,颜色(0,0,255)为红色线条,线宽3像素。
- 验证方法:通过np.array(refCnts).shape检查轮廓数量是否正确(信用卡数字应有10个轮廓)
5.轮廓排序

- 排序必要性:检测到的轮廓顺序不一定对应数字0-9的自然顺序,需要手动排序。
- 核心步骤:
- 计算每个轮廓的外接矩形cv2.boundingRect()
- 提取矩形左上角坐标(x,y)
- 根据x坐标进行从左到右排序
- 实现细节:
- 外接矩形返回值为(x,y,w,h)四元组
- 使用myutils.sort_contours()自定义排序函数
- 排序后轮廓索引即对应数字值(0轮廓对应数字0)
- 验证方法:排序后应确保轮廓顺序与数字显示顺序完全一致
6.模板匹配
- 模板提取:
- 对每个排序后的轮廓获取外接矩形
- 使用矩形坐标从原图截取ROI区域:ref[y:y+h, x:x+w]
- 统一resize到标准尺寸(57,88)
- 字典存储:
- 创建digits字典存储数字模板
- 键为数字(0-9),值为对应的ROI图像
# 必须使用.copy()保留原始图像 每个数字模板尺寸需保持一致
# 轮廓检测 轮廓排序 提取模板 构建数字模板字典
课时二十八:输入数据和识别结果
1.轮廓检测
(1)绘制轮廓

# 图像预处理(读取图像,转换为灰度图,礼帽操作,Sobel算子,闭操作,二值化,闭操作)使用cv2.drawContours()函数,参数设置为-1表示绘制所有轮廓,(0,0,255)表示用红色绘制
# 通过cv2.boundingRect()获取轮廓的外接矩形参数(x,y,w,h) 经过长宽比和尺寸双重筛选后,仅保留4个有效数字区域轮廓
(2)轮廓排序

# 检测到的4个数字轮廓需要按实际排列顺序(从左到右)进行排序 排序后轮廓编号1-4对应实际卡号的数字顺序
2.模板匹配
遍历轮廓数字

在大轮廓中分割出四个小轮廓 对每个小轮廓与0-9的模板数字进行匹配 使用红色标记处理区域以提高可视性
3.结果显示

用红色矩形框标记识别区域 在框上方显示识别结果 最终输出信用卡类型和完整卡号
实战:

知识小结
|
知识点 |
核心内容 |
考试重点/易混淆点 |
难度系数 |
|
形态学操作 |
使用不同大小的核进行图像预处理(9×3和5×5核) |
核大小的选择依据实际任务需求 |
⭐⭐⭐ |
|
顶帽操作 |
突出图像中更明亮的区域(如字体区域) |
与底帽操作的区别及应用场景 |
⭐⭐ |
|
Sobel算子 |
仅使用x方向梯度比xy联合效果更好 |
梯度方向选择的实验验证 |
⭐⭐⭐⭐ |
|
开闭运算 |
先膨胀后腐蚀(闭运算)使离散区域连成整体 |
开闭运算的先后顺序差异 |
⭐⭐⭐ |
|
双峰阈值法 |
使用OTSU自动确定最佳阈值 |
与传统固定阈值法的对比 |
⭐⭐⭐⭐ |
|
轮廓检测 |
通过长宽比过滤无效轮廓(信用卡数字区域) |
轮廓层级关系的理解 |
⭐⭐⭐⭐ |
|
模板匹配 |
使用57×88标准化尺寸进行数字识别 |
匹配方法选择(相关系数法) |
⭐⭐⭐⭐ |
|
ROI扩展技巧 |
轮廓坐标±5像素的边界扩展方法 |
扩展量与识别精度的平衡 |
⭐⭐ |
|
多级轮廓处理 |
先定位数字组再识别单个数字的两阶段策略 |
轮廓排序算法的实现 |
⭐⭐⭐⭐ |
|
实际应用验证 |
通过更换信用卡图片测试算法泛化能力 |
模板通用性与场景适应性 |
⭐⭐⭐ |
课时二十九:文档轮廓提取
1.边缘检测

- 读取图像转为灰度图:使用cv2.cvtColor()函数将图像从BGR转为灰度
- 高斯滤波:用cv2.GaussianBlur()去除噪声,常用(5,5)核大小
- Canny边缘检测:设置双阈值75和200进行边缘提取
2.获取轮廓

- 轮廓近似方法:
- 使用cv2.approxPolyDP()进行多边形近似
- 精度控制:取轮廓周长的2%作为近似阈值
- 四点判断:当近似结果为4个点时即为目标矩形轮廓
- 轮廓筛选:
- 按面积或周长排序取前5个轮廓
- 通过绿色线条绘制最终轮廓cv2.drawContours()
3.透视变换

- 变换原理:
- 包含平移、旋转、翻转等操作的复合变换
- 使用cv2.getPerspectiveTransform()函数计算变换矩阵
- 通过cv2.warpPerspective()执行实际变换
- 坐标处理:
- 使用order_points()函数规范坐标顺序(左上、右上、右下、左下)
- 变换后保持内容不变,仅改变呈现角度
- 尺寸计算:
- 通过欧式距离公式计算变换后图像的宽度和高度
- 取最大宽度和高度作为输出尺寸
4.文字识别
- OCR工具:
- 使用Tesseract OCR进行字符识别
- 支持中英文、数字混合识别
- 需单独安装配置OCR引擎
- 后处理:
- 对变换结果可进行二次图像增强
- 包括二值化、去噪等操作提高识别率
- 识别结果可结构化输出为文本数据
5.应用操作
(1)购物小票扫描


- 处理难点:
- 任意角度拍摄导致的形变
- 复杂背景下的轮廓提取
- 小票内容的精确识别
- 解决方案:
- 通过边缘检测+轮廓近似定位小票区域
- 透视变换校正图像角度
- OCR识别商品信息和价格
(2)阅读理解文档扫描


-
特殊处理:
- 多段落文本的分区域处理
- 保持原始文档的版式结构
- 针对印刷体优化识别参数
(3)文档轮廓提取
- 调试技巧:
- 使用比例参数保持原图坐标关系
- 分步骤可视化中间结果
- 关键点坐标的规范化处理
- 参数调整:
- 边缘检测阈值的动态调整
- 轮廓近似精度的优化
- 输出尺寸的自适应计算
课时三十:原始与变换公式 透视变换结果
1.透视变换

原始坐标:通过轮廓检测获得的四个点(a,b,c,d)
目标坐标:期望输出的标准矩形四点(e,f,g,h)
# 由于原始图像可能经过resize操作,需要通过ratio将检测到的坐标点还原到原始图像尺寸
# 需要8个方程求解3×3变换矩阵,每组坐标点提供2个方程,因此需要4组坐标点
# 按顺序排列为:左上(tl)、右上(tr)、右下(br)、左下(bl)
2.变换结果


- 灰度化:将结果转换为单通道灰度图像
- 二值化:使用阈值处理保留有效信息
- 结果显示:对比展示原始图像和变换后结果
更多推荐
所有评论(0)