- 图像轮廓特征查找其实就是它的外接轮廓
- 应用
- 图像分割
- 形状分析
- 物体检测与识别
- 根据轮廓点进行,所以要先找到轮廓
- 先将图像进行灰度化->二值化处理
- 需要目标物体为白色,非目标物体为黑色(黑隐白显),请选择合适的二值化方式
- 有了轮廓点之后可以找到最上、最下、最左、最右的四个坐标:
,然后就可以绘制矩形了。
外接矩形
实现代码:boundingRect(轮廓点)
返回值:x,y,w,h
外接矩形主要有两种类型,如下图所示:
- 绿色矩形:普通外接矩形,不考虑旋转角度,仅需包含整个轮廓。可通过轮廓坐标中的最上、最下、最左、最右点确定其边界。
- 蓝色矩形:最小外接矩形,在保证完全包含轮廓的前提下,进一步优化以最小化面积。

代码示例:
import cv2 as cv
#读图
num=cv.imread("../images/num.png")
#灰度化处理
gray=cv.cvtColor(num,cv.COLOR_BGR2GRAY)
#二值化处理
_,binary=cv.threshold(gray,170,255,cv.THRESH_BINARY_INV)
# cv.imshow("binary",binary)
#轮廓查找
conts,hie=cv.findContours(binary,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
#轮廓特征查找:外接矩形
for cont in conts:
#(x,y)是轮廓左上角的坐标,w,h是宽和高
x,y,w,h=cv.boundingRect(cont)
# print(x,y,w,h)
#绘制外接矩形
cv.rectangle(num,(x,y),(x+w,y+h),(0,255,0),2)
#显示效果
cv.imshow("num",num)
cv.waitKey(0)
cv.destroyAllWindows()
这段代码的功能是:
- 读取数字图片并转换为灰度图
- 对灰度图进行二值化处理,突出数字区域
- 查找二值图中的轮廓
- 为每个轮廓绘制外接矩形框
- 最终显示带有矩形框标记的原图
主要用于数字识别前的预处理,通过轮廓检测来定位图片中的数字位置。
来看看效果:

最小外接矩形
最小外接矩形即图中蓝色框所示区域,其计算采用的是旋转卡壳算法。以下简要说明该算法的原理:
- 在上一章中,我们已经学习了凸包的概念:凸包是指包含点集中所有点的最小凸多边形,构成凸包的点称为凸包顶点。旋转卡壳算法正是基于这些凸包顶点进行计算的。
- 该算法的一个重要前提是:对于任意多边形P,其最小外接矩形必定至少有一条边与最小凸多边形的一条边重合。
假设某凸包图形如下所示:

根据前提条件,该凸多边形的最小外接矩形必然与多边形的某条边共线。因此,我们只需选择任意一条边作为起始边,沿逆时针方向依次计算各凸包顶点到起始边的距离,并记录其中距离最大的点即可。看图:

如图所示,我们从a、b两点构成的起始边出发,
- 首先找到与这条边距离最远的点e,该距离即为矩形的高度。
- 接下来确定矩形宽度:以向量为基准,计算所有凸包点在该向量上的投影长度,其中投影最长的点所对应的垂直线即为矩形右侧边界所在位置。
看图:

如上图所示,d点就是在向量上投影最长的凸包点,那么通过d点垂直于直线ab的直线就是矩形的右边界所在的直线。矩形的左边界的也是这么计算的,不同的是使用的向量不是而是
。
看图:
如图所示,h点作ab的垂线即为以ab为起始边计算所得矩形的左边界。该矩形的高度等于e点到直线ab的距离,宽度为h点在向量上的投影加上d点在向量上的投影,再减去ab的长度(加的时候ab加了两次哦)。计算公式为:


于是我们就有了以ab为起始边所构成的外接矩形的宽和高,这样就可以得到该矩形的面积。然后再以bc为起始边,并计算其外接矩形的面积。也就是说凸多边形有几个边,就要构建几次外接矩形,然后找到其中面积最小的矩形作为该凸多边形的最小外接矩形。
在OpenCV中,可以直接使用cv2.minAreaRect()来获取最小外接矩形,该函数只需要输入一个参数,就是凸包点的坐标,然后会返回最小外接矩形的中心点坐标、宽高以及旋转角度。通过返回的内容信息,即可绘制凸多边形的的最小外接矩形。
需要用到的API:
contours,hierarchy=cv.findContours(imge_np_thresh,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
contours为二值图像上查找所有的外部轮廓。
- rect = cv2.minAreaRect(cnt):传入的cnt参数为contours中的轮廓,可以遍历contours中的所有轮廓,然后计算出每个轮廓的小面积外接矩形
-
rect 用于计算轮廓的最小面积外接矩形,其结构一般包含以下属性:
- 中心点坐标 (x, y)
- 宽度 width
- 高度 height
- 旋转角度 angle
- cv2.boxPoints(rect).astype(int)
- cv2.boxPoints(rect)返回一个4×2的数组,其中每行包含一个点的(x,y)坐标值,所有点按顺时针或逆时针顺序排列。
-
该函数将最小外接矩形转换为边界框的四个角点坐标,并将结果转换为整数值。
-
cv2.drawContours(image, contours, contourIdx, color, thickness)
- image:原图像,一般为 numpy 数组,通常为灰度或彩色图像。
- contours:一个包含多个轮廓的列表,可以用上一个api得到的 [box]
-
color:轮廓的颜色,可以是 BGR 颜色格式的三元组,例如
(0, 0, 255)表示红色。 - thickness:轮廓线的粗细,如果是正数,则绘制实线;如果是 0,则绘制轮廓点;如果是负数,则填充轮廓内部区域。
-
代码示例:
import cv2 as cv
# 加载图片
num=cv.imread("../images/num.png")
# 灰度化处理
gray=cv.cvtColor(num,cv.COLOR_BGR2GRAY)
# 二值化处理
_,binary=cv.threshold(gray,120,255,cv.THRESH_BINARY_INV)
# 查找轮廓
conts,hie=cv.findContours(binary,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
# 绘制外接矩形
for cont in conts:
ret = cv.minAreaRect( cont)
print( ret)#center(x,y),长宽(w,h),旋转角度angle
dst = cv.boxPoints(ret).astype(int)#坐标要求为整数类型,原坐标类型为浮点数,需要进行类型转换
cv.drawContours(num,[dst],-1,(0,0,255),2)#[dst]因为绘制多个轮廓,所以需要传入一个列表
cv.imshow("num",num)
cv.waitKey(0)
cv.destroyAllWindows()
这段代码的功能是:
- 读取图片:加载名为"num.png"的图像
- 预处理:将彩色图像转换为灰度图,再进行二值化处理
- 轮廓检测:查找二值图像中的外部轮廓
- 绘制矩形:为每个轮廓绘制最小外接矩形框
- 显示结果:展示标注了矩形框的原图
主要用于数字或字符的轮廓识别和定位。
来看看结果:

最小外接圆
寻找最小外接圆使用的算法是Welzl算法。
Welzl算法的理论基础源自希尔伯特圆定理:对于平面上任意三个非共线的点,存在唯一的最小圆能够完全包含这三个点。该圆不仅是包含这三个点的最小覆盖圆,而且在所有可能的包含圆中具有最小的半径。
对于平面上任意 n 个不共圆的点,总存在唯一的最小覆盖圆包含这些点。
当平面上已有 n 个不共线(或不共圆)的点及其最小覆盖圆时,若新增第 n+1 个点且该点位于原最小覆盖圆外,则新的最小覆盖圆半径必然增大。此时,新点必须位于新的最小覆盖圆的圆周上。这是因为:
- 若新点在原圆内,不影响最小覆盖圆;
- 若新点在原圆外但不在圆周上,则通过新点与至少两个原圆周上的点可构造更大的圆,这必然大于原最小覆盖圆。因此,新点必须位于新的最小覆盖圆的圆周上。
基于此定理,最小覆盖圆的求解步骤如下:
- 初始选取3个点建立圆(不共线三点确定唯一圆;若共线则取最远两点作为直径);
- 遍历剩余点:
- 若点位于当前圆内,保持圆不变;
- 若点位于圆外,则根据定理,该点必在新圆圆周上。此时需枚举该点与之前各点的组合,寻找能包含所有点的最小圆(需三个点确定新圆)。
在OpenCV中,可直接调用cv2.minEnclosingCircle()函数计算最小外接圆。该函数接收点集坐标作为输入,返回圆心坐标和半径,即可绘制点集的最小外接圆。
需要使用的API说明:
cv2.minEnclosingCircle(points) -> (center, radius)
points:输入参数图片轮廓数据center:一个包含圆心坐标的二元组(x, y)。radius:浮点数类型,表示计算得到的最小覆盖圆的半径。
cv2.circle(img, center, radius, color, thickness)
img:输入图像,通常是一个numpy数组,代表要绘制圆形的图像。center:一个二元组(x, y),表示圆心的坐标位置。radius:整型或浮点型数值,表示圆的半径长度。color:颜色标识,可以是BGR格式的三元组(B, G, R),例如(255, 0, 0)表示红色。thickness:整数,表示圆边框的宽度。如果设置为-1,则会填充整个圆。
代码示例:
import cv2 as cv
import numpy as np
#读图
num=cv.imread("../images/num.png")
gray=cv.cvtColor(num,cv.COLOR_BGR2GRAY)
#二值化
_,binary=cv.threshold(gray,0,255,cv.THRESH_BINARY_INV)
#查找轮廓
conts,hie=cv.findContours(binary,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
#遍历轮廓
for cont in conts:
#查找最小外接圆
(x,y),r=cv.minEnclosingCircle(cont)
# print(x,y,r)
#数据类型转换
x,y,r=np.int_(x),np.int_(y),np.int_(r)
#绘制圆
cv.circle(num,(x,y),r,(0,255,0),2,cv.LINE_AA)
#显示效果
cv.imshow("num",num)
cv.waitKey(0)
cv.destroyAllWindows()
这段代码的功能是:
- 读取数字图像并转换为灰度图
- 对灰度图进行二值化处理(反色)
- 查找二值图像中的外部轮廓
- 遍历每个轮廓,计算其最小外接圆
- 在原图上绘制所有轮廓的最小外接圆
- 显示处理结果
主要用于数字识别前的图像预处理和特征提取。
结果对比:

直方图均衡化
先来理解一些基础概念
-
什么是直方图
直方图是一种数据统计方法,它将统计值分配到预先定义好的区间(bin)中。在直方图中,"bin"是一个核心概念,可译为"直条"或"组距",其数值代表从数据中提取的特征统计量。这些数据可以包括梯度、方向、色彩等各种特征值。
-
直方图:反映图像像素分布的统计图,横坐标就是图像像素的取值,纵坐标是该像素的个数。也就是对一张图像中不同像素值的像素个数的统计。
-
增加对比度:黑的更黑,白的更白。

绘制直方图
简单来说就是以像素值为横坐标,像素值的个数为纵坐标绘制一个统计图。
所用API为:
hist=cv.calcHist(images,channels,mask,histSize,rangs)
images:输入图像列表,可以是一幅或多幅图像(通常是灰度图像或者彩色图像的各个通道)。-
channels:一个整数列表,用于指定计算直方图的图像通道。对于灰度图像,使用[0];对于彩色图像,可选择[0]、[1]或[2],分别对应蓝色(B)、绿色(G)和红色(R)通道。
-
mask(可选):生成与输入图像尺寸相同的二值掩模图像,其中非零像素标记直方图计算区域;若为None则计算全图。 -
histSize:一个整数列表,也就是直方图的区间个数(BIN 的数目)。用中括号括起来,例如:[256]。 -
ranges:每维数据的取值范围,它是一个二维列表,每一维对应一个通道的最小值和最大值,例如对灰度图像可能是[0, 256]。
返回值hist是一个长度为255的数组,数组中的每个值表示图像中对应灰度等级的像素计数。
cv2.line(img, pt1, pt2, color, thickness)
- img:原始图像,即要在上面画线的numpy数组(一般为uint8类型)。
- pt1 和 pt2:分别为线段的起点和终点坐标,它们都是元组类型,例如
(x1, y1)和(x2, y2)分别代表线段两端的横纵坐标。 - color:线段的颜色,通常是一个包含三个元素的元组
(B, G, R)表示BGR色彩空间的像素值,也可以是灰度图像的一个整数值。 - thickness:线段的宽度,默认值是1,如果设置为负数,则线宽会被填充。
(先统计再绘图,这里注意图像的坐标原点在图像左上角,我们要将直方图从低端开始展示,这需要定点时有所斟酌)
来看一下代码示例:
import cv2 as cv
import numpy as np
#读图
cat=cv.imread("../images/lvbo.png")
#转灰度图
# gray=cv.cvtColor(cat,cv.COLOR_BGR2GRAY)
#统计像素
hist=cv.calcHist([cat],[1],mask=None,histSize=[256],ranges=[0,256])
# print( hist)
minval,maxval,minloc,maxloc=cv.minMaxLoc(hist)
print(minval,maxval,minloc,maxloc)
#定义绘制图像的高
h_max=np.int_(256)
#定义绘制的背景
back=np.zeros((400,256,3),dtype=np.uint8)
#遍历
for i in range(256):
#归一化
h=int(hist[i].item()*h_max/maxval)
#绘制直方图
cv.line(back,(i,400-h),(i,400),color=(0,255,0),thickness=1)
#显示
cv.imshow("back",back)
cv.waitKey(0)
cv.destroyAllWindows()
以下是对所选代码的逐行参数和意思解释:
- 图像预处理(注释状态)
- gray=cv.cvtColor(cat,cv.COLOR_BGR2GRAY)`: 将BGR彩色图像转换为灰度图的代码(当前被注释)
- 直方图计算
- hist=cv.calcHist([cat],[1],mask=None,histSize=[256],ranges=[0,256])`:
- [cat]: 输入图像列表
- [1]: 通道索引,这里选择绿色通道(1)
- `mask=None`: 不使用掩码
- `histSize=[256]`: 直方图bins数量为256
- `ranges=[0,256]`: 像素值范围0-255
- 直方图统计信息
- minval,maxval,minloc,maxloc=cv.minMaxLoc(hist)`: 获取直方图中的最小值、最大值及其位置
- print(minval,maxval,minloc,maxloc)`: 打印这些统计信息(在运行代码时要观察数据结构以便对其进行处理,这里不需要可以隐去)
- 绘制直方图参数设置
- h_max=np.int_(256)`: 设置直方图绘制的最大高度为256
- back=np.zeros((400,256,3),dtype=np.uint8)`: 创建400×256大小的黑色背景图像用于绘制直方图
- 直方图绘制循环
- for i in range(256)`: 遍历所有256个灰度级
- h=int(hist[i].item()*h_max/maxval)`: 计算第i个灰度级的归一化高度
- cv.line(back,(i,400-h),(i,400),color=(0,255,0),thickness=1)`: 在背景图上绘制绿色垂直线表示直方图条
- 图像显示
- cv.imshow("back",back)`: 显示绘制的直方图
- cv.waitKey(0)`: 等待按键输入
- cv.destroyAllWindows()`: 关闭所有显示窗口
这段代码主要用于分析图像的绿色通道直方图分布情况。
运行结果如图:

直方图均衡化
优质图像通常具有均匀分布的直方图,而直方图均衡化技术可以有效提升图像的全局亮度和对比度。
当图像整体偏亮时,其像素值普遍集中在较高区间。通过横向拉伸直方图,能够扩展像素值的分布范围,从而显著增强图像对比度。
具体实现过程包括:
- 统计图像各灰度级的像素数量、计算比例和累计比例,
- 最终将这些值重新映射到0-255区间(或其他指定范围)。
- 通过对比示例图像可以直观看出,经过处理的图像(后者)明显改善了原始图像(前者)存在的对比度不足、色调偏灰的问题。
直方图均衡化作用:
- 增强对比度
- 提高图像质量
来看一组对比图:



可以看到均衡化后图片的亮度和对比度效果明显好于原图。
自适应直方图均衡化
自适应直方图均衡化(Adaptive Histogram Equalization, AHE),通过调整图像像素值分布,使图像的对比度和亮度得到改善。
具体过程如下所示:

现有一张3×3的灰度图像,其像素值分布如上图所示。为进行直方图均衡化处理,我们首先需要统计每个像素值的出现次数、占比以及累计占比,具体统计结果如下图所示。
比例的计算
- 比例是指每个灰度值出现的次数占总像素数的比例。
- 公式:

- 例子:

累计比例的计算
- 累计比例是指从最小的灰度值开始,依次累加每个灰度值的比例。
- 公式:累计比例=前一个灰度值的累计比例+当前灰度值的比例
例子:

- 这些计算是直方图均衡化的基础,用于调整图像的灰度分布,提高对比度。
我们首先计算缩放范围(通常为0-255,即255减0),将其与累计比例相乘得到新的像素值。
例如,对于原始像素值为50的点,用其累计比例0.33乘以255得到84.15,取整后为84,并将该值填入原图像中像素值为50的位置。同理计算像素值100、210和255对应的新值,最终得到如下图所示的结果,完成基本的直方图均衡化过程。


所用API:dst = cv.equalizeHist(imgGray)
代码示例:
import cv2 as cv
img=cv.imread("../images/zhifang.png")
#print(img.shape)
gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
dst=cv.equalizeHist(gray)
cv.imshow("src",img)
cv.imshow("dst",dst)
cv.waitKey(0)
cv.destroyAllWindows()
imgGray是需要进行直方图均衡化的灰度图像,处理后返回优化后的图像。该方法特别适用于以下情况:当图像灰度分布不均匀,灰度值集中在较窄范围时,会导致图像细节模糊、对比度不足。但需注意的是,传统直方图均衡化可能会带来两个问题:一是引入噪声干扰,二是造成局部区域过度增强。这是由于该方法未能充分考虑图像的局部特征与全局对比度差异所致。效果对比如下图所示:

对比度受限的自适应直方图均衡化
很明显,因为全局调整亮度和对比度的原因,脸部太亮,大部分细节都丢失了。自适应均衡化就是用来解决这一问题的:它在每一个小区域内(默认8×8)进行直方图均衡化。当然,如果有噪点的话,噪点会被放大,需要对小区域内的对比度进行了限制,所以这个算法全称叫:对比度受限的自适应直方图均衡化(Contrast Limited Adaptive Histogram Equalization, CLAHE)。
其主要步骤为:
- 图像分块(Tiling):
- 图像首先被划分为多个不重叠的小块(tiles)。这样做的目的是因为在全局直方图均衡化中,单一的直方图无法反映图像各个局部区域的差异性。通过局部处理,AHE能够更好地适应图像内部的不同光照和对比度特性。(tiles 的 大小默认是 8x8)
- 计算子区域直方图:
- 对于每个小块,独立计算其内部像素的灰度直方图。直方图反映了该区域内像素值的分布情况。
- 子区域直方图均衡化:
- 对每个小块的直方图执行直方图均衡化操作。这涉及重新分配像素值,以便在整个小块内更均匀地分布。均衡化过程会增加低频像素的数量,减少高频像素的数量,从而提高整个小块的对比度。
- 对比度限制(Contrast Limiting):
- 如果有噪声的话,噪声会被放大。为了防止过大的对比度增强导致噪声放大,出现了限制对比度自适应直方图均衡化(CLAHE)。CLAHE会在直方图均衡化过程中引入一个对比度限制参数。当某一小块的直方图在均衡化后出现极端值时,会对直方图进行平滑处理(使用线性或非线性的钳制函数),确保对比度增强在一个合理的范围内。
- 重采样和邻域像素融合:
- 由于小块之间是不重叠的,直接拼接经过均衡化处理的小块会产生明显的边界效应。因此,在CLAHE中通常采用重采样技术来消除这种效应,比如通过双线性插值将相邻小块的均衡化结果进行平滑过渡,使最终图像看起来更为自然和平滑。
- 合成输出图像:
- 将所有小块均衡化后的结果整合在一起,得到最终的自适应直方图均衡化后的图像。
使用到的API:clahe = cv2.createCLAHE(clipLimit=None, tileGridSize=None)
- clipLimit(可选):对比度限制参数,用于控制直方图均衡化过程中对比度增强的程度。如果设置一个大于1的值(如2.0或4.0),CLAHE会限制对比度增强的最大程度,避免过度放大噪声。如果不设置,OpenCV会使用一个默认值。
- tileGridSize(可选):图像分块的大小,通常是一个包含两个整数的元组,如
(8, 8),表示将图像划分成8x8的小块进行独立的直方图均衡化处理。分块大小的选择会影响到CLAHE的效果以及处理速度。
创建CLAHE对象后,可以使用 .apply() 方法对图像进行CLAHE处理:
img=clahe.apply(image)
-
image:要均衡化的图像。
-
img均衡后的图像
代码示例:
import cv2 as cv
#读图
zhifang=cv.imread("../images/zhifang.png")
#灰度化
gray=cv.cvtColor(zhifang,cv.COLOR_BGR2GRAY)
#创建clahe对象
clahe=cv.createCLAHE(clipLimit=2.0,tileGridSize=(8,8))
#调用apply方法
dst=clahe.apply(gray)
cv.imshow("zhifang",zhifang)
cv.imshow("dst",dst)
cv.waitKey(0)
cv.destroyAllWindows()
这段代码的功能是:
- 读取名为"zhifang.png"的图像文件
- 将彩色图像转换为灰度图像
- 创建CLAHE(限制对比度自适应直方图均衡化)对象,设置对比度限制为2.0,网格大小为8×8
- 对灰度图像应用CLAHE处理,增强图像对比度
- 显示原始图像和处理后的图像结果
模板匹配
模板匹配
模板匹配:用模板图(通常是一个小图)在目标图像(通常是一个比模板图大的图片)中不断的滑动比较,通过某种比较方法来判断是否匹配成功,找到模板图所在的位置。
- 不会有边缘填充
- 类似于卷积,滑动比较,挨个比较像素
- 返回结果大小是:目标图大小--模板图大小--1
看图:

匹配方法
所用API:res=cv2.matchTemplate(image, templ, method)
- image:原图像,这是一个灰度图像或彩色图像(在彩色图像情况下,匹配将在每个通道上独立进行)。
- templ:模板图像,也是灰度图像或与原图像相同通道数的彩色图像。
- method:匹配方法,可以是以下之一:
- cv.TM_CCOEFF
- cv.TM_CCOEFF_NORMED
- cv.TM_CCORR
- cv.TM_CCORR_NORMED
- cv.TM_SQDIFF
- cv.TM_SQDIF_NORMED
这些方法决定了如何度量模板图像与原图像子窗口之间的相似度。
- 返回值res
- 函数在完成图像模板匹配后返回一个的res是一个结果矩阵,这个矩阵的大小与原图相同。
- 矩阵的每个元素表示原图像中相应位置与模板图像匹配的相似度
- 匹配方法不同,返回矩阵的值的含义也会有所区别。以下列举几种常用的匹配方法及其返回值含义
cv2.TM_SQDIFF或cv2.TM_SQDIFF_NORMED:返回值越接近0,表示匹配程度越好。最小值对应的最佳匹配位置。cv2.TM_CCORR或cv2.TM_CCORR_NORMED:返回值越大,表示匹配程度越好。最大值对应的最佳匹配位置。cv2.TM_CCOEFF或cv2.TM_CCOEFF_NORMED:返回值越大,表示匹配程度越好。最大值对应的最佳匹配位置。
平方差匹配
使用API:cv.TM_SQDIFF
以模板图与目标图所对应的像素值使用平方差公式来计算,其结果越小,代表匹配程度越高,计算过程如下:

注意:模板匹配过程皆不需要边缘填充,直接从目标图像的左上角开始计算。
归一化平方差匹配
所用API:cv.M_SQDIFF_NORMED
与平方差匹配类似,只不过需要将值统一到0-1,计算结果越小,代表匹配程度越高,计算过程举例如下:

相关匹配
所用API:cv.TM_CCORR
使用对应像素的乘积进行匹配,乘积结果越大其匹配程度越高,计算过程举例如下:

归一化相关匹配
所用API:cv.TM_CCORR_NORMED
与相关匹配类似,只不过是将其值统一到0到1之间,值越大,代表匹配程度越高,计算过程举例如下。

相关系数匹配
所用API:cv.TM_CCOEFF
这里需要先计算模板与目标图像的均值,然后通过每个像素与均值之间的差的乘积再求和来表示其匹配程度,1表示完美的匹配,-1表示最差的匹配,计算过程举例如下:

归一化相关系数匹配
所用API:cv.TM_CCOEFF_NORMED
也是将相关系数匹配的结果统一到0到1之间,值越接近1代表匹配程度越高,计算过程举例如下:

绘制匹配程度最高的点的轮廓
找的目标图像中匹配程度最高的点,我们可以设定一个匹配阈值来筛选出多个匹配程度高的区域。
所用API:
- loc=np.where(array>0.8):loc包含array中所有大于0.8的元素索引的数组
- np.where(condition) 是 NumPy 的一个函数,当条件为真时,返回满足条件的元素的索引。
- zip(*loc)
loc是解包操作,将loc中的多个数组拆开,作为单独的参数传递给zip。zip将这些数组按元素一一配对,生成一个迭代器,每个元素是一个元组,表示一个坐标点。
x=list([[1,2,3,4,3],[23,4,2,4,2]])
print(list(zip(*x))) # [(1, 23), (2, 4), (3, 2), (4, 4), (3, 2)]
来看一下示例代码:
import cv2 as cv
import numpy as np
#读图
game=cv.imread("../images/game.png")
temp=cv.imread("../images/temp.png")
#转灰度
gray_g=cv.cvtColor(game,cv.COLOR_BGR2GRAY)#目标图
gray_t=cv.cvtColor(temp,cv.COLOR_BGR2GRAY)#模板图
print(gray_g.shape,gray_t.shape)
h,w=gray_t.shape
#模板匹配:结果矩阵大小等于目标像素个数-模板像素个数+1
ret=cv.matchTemplate(gray_g,gray_t,cv.TM_CCOEFF_NORMED)
# print(ret.shape)
#设定匹配阈值
threshold=0.8
#获取结果矩阵中匹配上的结果的位置索引
loc=np.where(ret>= threshold)#(h,w)
print(loc)
#解包操作
iter=zip(*loc)
for pt in iter:
# print(pt)#获取匹配位置(原图中左上角第一个坐标)
right_bottom=(pt[1]+w,pt[0]+h)#获取匹配位置(原图中右下角第一个坐标)输入以(w,h)的格式
left_top=pt[::-1]
cv.rectangle(game,left_top,right_bottom,(0,255,0),2)
#显示效果
cv.imshow("game",game)
cv.waitKey(0)
cv.destroyAllWindows()
这段代码实现了模板匹配功能:
- 读取目标图像和模板图像并转换为灰度图
- 使用matchTemplate函数进行模板匹配,得到匹配结果矩阵
- 设定阈值筛选匹配度高的位置
- 在目标图像上绘制匹配区域的矩形框
- 显示匹配结果
主要用途是在大图中查找小图模板的位置。
看一下结果:

图中用轮廓绘制框出匹配值最高的点。

7917

被折叠的 条评论
为什么被折叠?



