目录
一、图像特征轮廓查找
图像轮廓特征查找其实就是他的外接轮廓。
先灰度化、二值化。目标物体白色,非目标物体黑色
外界矩形
外接矩形可根据获得到的轮廓坐标中最上、最下、最左、最右的点的坐标来绘制外接矩形
python
# 查找轮廓
conts,th = cv.findContours(binary,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
# 获取外接矩阵点
for i in conts:
x,y,w,h = cv.boundingRect(i) # 获取矩阵坐标和宽高
cv.rectangle(img,(x,y),(x+w,y+h),(0,0,255),2,cv.LINE_AA) # 绘制矩形
一次只能画出一个轮廓的外界矩形,而查找轮廓可能返回多个轮廓的坐标,所以用for循环,一次取出一个轮廓的坐标,找出最上面,最下面,最左面,最右面的值组合成四个矩阵顶点坐标,画出矩阵。
最小外界矩形
寻找最小外接矩形使用的算法叫做旋转卡壳法,是基于凸包点进行的。
凸多边形的最小外接矩形与凸多边形的某条边是共线的。因此我们只需要以其中的一条边为起始边,然后按照逆时针方向计算每个凸包点与起始边的距离,并将距离最大的点记录下来。
把凸包点在起始边上的所有投影中的两个最远端点的长度作为宽,凸包点到该线的最大垂直距离作为高,可计算面积,逆时针旋转,找出面积最小的宽和高就可以画出最小矩阵
python
# 查找轮廓
conts,th = cv.findContours(binary,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
# 轮廓特征查找:最小外接矩形 (x,y)w,h angle
# result = cv.minAreaRect(conts[0])
# 循环遍历每一条轮廓
for i in conts:
result = cv.minAreaRect(i)
# 处理点坐标
box = cv.boxPoints(result).astype(np.int32)
# 绘制矩形
cv.drawContours(num,[box],-1,(0,0,255),2)
最小外界圆
希尔伯特圆定理表明,对于平面上的任意三个不在同一直线上的点,存在一个唯一的圆同时通过这三个点,且该圆是最小面积的圆
先取3个点建立一个圆(不共线的三个点即可确定一个圆,如果共线就取距离最远的两个点作为直径建立圆),然后遍历剩下的所有点,对于遍历到的点P来说:
- 如果该点在圆内,那么最小覆盖圆不变。
- 如果该点在圆外,根据上述定理,该点一定在想要求得的最小覆盖圆的圆周上,又因为三个点才能确定一个圆,所以需要枚举P点之前的点来找其余的两个点。当找到与P点组成的圆能够将所有点都包含在圆内或圆上,该圆就是这些点的最小外接圆。
python
# 查找轮廓
conts,th = cv.findContours(binary,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
# 轮廓特征查找:最小外接圆 (x,y) r
# cents,r = cv.minEnclosingCircle(conts[0])
for i in range(len(conts)):
cents,r = cv.minEnclosingCircle(conts[i])
# 绘制圆
cv.circle(img,(int(cents[0]),int(cents[1])),int(r),(0,0,255),2)
二、直方图均衡化
直方图:反映图像像素分布的统计图,横坐标就是图像像素的取值,纵坐标是该像素的个数。也就是对一张图像中不同像素值的像素个数的统计。
增加对比度:黑的更黑,白的更白。
绘制直方图
统计像素
hist=cv2.calcHist(images, channels, mask, histSize, ranges)
- iages:图像
- channels:指示在每个图像上计算直方图的通道编号,灰度图对应[0],彩色图[0][1][2]对应B、R、G
- mask:掩膜
- histSize:整数列表,表示直方图的区间个数
- ranges:二维列表,表示每维数据的取值范围,每一维对应一个通道的最小值和最大值
获取直方图的最小值、最大值及其对应最小值的位置索引、最大值的位置索引
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(hist)
cv2.line(img, pt1, pt2, color, thickness)
img:原始图像
pt1 和 pt2:分别为线段的起点和终点坐标,分别代表线段两端的横纵坐标。
color :线段的颜色,通常是一个包含三个元素的元组
(B, G, R)
表示BGR色彩空间的像素值,也可以是灰度图像的一个整数值。thickness:线段的宽度,默认值是1,如果设置为负数,则线宽会被填充。
python
# 统计像素 返回值: 数组 (各个元素的个数)
hist = cv.calcHist([bg],[1],None, [256], [0,256])
# 获取直方图的最小值、最大值、对应的位置索引 [列,行]:与hist索引取值方式相反
minval,maxval,minloc,maxloc = cv.minMaxLoc(hist)
# print(minval,maxval,minloc,maxloc)
# 定义直方图的高
h_hist = np.int32(256)
# 循环拿像素个数
for i in range(256):
l = int(hist[i].item()*h_hist/maxval)
point1 = (i,256-l)
point2 = (i,256)
cv.line(black,point1,point2,(255,0,0),1)
直方图均衡化
一副效果好的图像通常在直方图上的分布比较均匀,直方图均衡化就是用来改善图像的全局亮度和对比度。
过亮的图形像素值一般都很大,对比度不高,把把像素值对应的直方图横向拉伸,映射到0-255区间内,就会让像素分布更加均匀,亮的地方更亮,暗的地方更暗,提高对比度
自适应直方图均衡化
选取一定范围的像素,统计其每个像素值的个数、比例以及其累计比例,将要缩放的范围乘以累计比例,得到新的像素值,并将新的像素值放到对应的位置上,类似于按比例加权,但由于是累计比例,像素越低比例数越小,像素越高的比例数越大,就会让像素值的差距拉大,从而让像素值整体更加均匀。
缺点;直方图均衡化方法没有考虑到图像的局部特征和全局对比度的差异,会引入噪声,并导致图像中出现过度增强的区域。
对比度受限的自适应
对小区域内的对比度进行了限制
单一的直方图无法反映图像各个局部区域的差异性,把图形分为不重叠的小块,对每个区域单独计算直方图,单独进行直方图均衡化操作,CLAHE会在直方图均衡化过程中引入一个对比度限制参数,防止出现极值,从而防止噪声扩大,由于小块之间是不重叠的,直接拼接经过均衡化处理的小块会产生明显的边界效应。因此,在CLAHE中通常采用重采样技术来消除这种效应。
创建clahe对象
clahe = cv2.createCLAHE(clipLimit=None, tileGridSize=None)
使用
.apply()
方法对图像进行CLAHE处理:img=clahe.apply(image)
python
# 自适应直方图均衡化
dst = cv.equalizeHist(img)
# 对比度受限的自适应直方图均衡化
# 创建clahe对象
clahe = cv.createCLAHE(clipLimit=2.0,tileGridSize=(8,8))
# 使用clahe调用apply()方法
dst1 = clahe.apply(img)
三、模板匹配
模板匹配就是用模板图(通常是一个小图)在目标图像(通常是一个比模板图大的图片)中不断的滑动比较,通过某种比较方法来判断是否匹配成功,找到模板图所在的位置。
没有边缘填充
匹配方法
res=cv2.matchTemplate(image, templ, method)
image:原图像,这是一个灰度图像或彩色图像。
templ:模板图像,也是灰度图像或与原图像相同通道数的彩色图像。
method:匹配方法,可以是以下之一:
cv2.TM_CCOEFF
cv2.TM_CCOEFF_NORMED
cv2.TM_CCORR
cv2.TM_CCORR_NORMED
cv2.TM_SQDIFF
cv2.TM_SQDIFF_NORMED
这些方法决定了如何度量模板图像与原图像子窗口之间的相似度。
返回值res
函数在完成图像模板匹配后返回一个结果矩阵,这个矩阵的大小与原图像不相同。矩阵的每个元素表示原图像中相应位置与模板图像匹配的相似度。
匹配方法不同,返回矩阵的值的含义也会有所区别。以下是几种常用的匹配方法及其返回值含义:
cv2.TM_SQDIFF
或cv2.TM_SQDIFF_NORMED
:返回值越接近0,表示匹配程度越好。最小值对应的最佳匹配位置。
cv2.TM_CCORR
或cv2.TM_CCORR_NORMED
:返回值越大,表示匹配程度越好。最大值对应的最佳匹配位置。
cv2.TM_CCOEFF
或cv2.TM_CCOEFF_NORMED
:返回值越大,表示匹配程度越好。最大值对应的最佳匹配位置。
平方差匹配:cv2.TM_SQDIFF
归一化平方差匹配:cv2.TM_SQDIFF_NORMED
相关匹配:cv2.TM_CCORR
归一化相关匹配:cv2.TM_CCORR_NORMED
相关系数匹配:cv2.TM_CCOEFF
归一化相关系数匹配:cv2.TM_CCOEFF_NORMED
绘制轮廓
找目标图像中匹配程度最高的点,可以设定一个匹配阈值来筛选出多个匹配程度高的区域。
- loc=np.where(array > 0.8) #loc包含array中所有大于0.8的元素索引的数组
np.where(condition) 是 NumPy 的一个函数,当条件为真时,返回满足条件的元素的索引。
zip(*loc)
*loc
是解包操作,将loc
中的多个数组拆开,作为单独的参数传递给zip
。
zip
将这些数组按元素一一配对,生成一个迭代器,每个元素是一个元组,表示一个坐标点。
四、霍夫变换
用于检测图像中的直线、圆等几何形状。基本思想就是将图像空间中的点映射到参数空间 中,通过在参数空间中寻找累计最大值实现对特定形状的检测。
霍夫直线变换
对于一条直线(不垂直于x轴的直线),都可以用y=k x+b来表示,此时,x和y是横纵坐标,k和b是一个固定参数,以k和b 为横纵坐标,x和y为固定参数,也就是说k和b成了自变量和因变量,对应霍夫空间的一个点,对于x=1这种直线(垂直于x轴)来说,y已经不存在了,斜率无穷大,无法映射到霍夫空间中去,就将直角坐标系转化为极坐标系,然后通过极坐标系与霍夫空间进行相互转化
lines=cv2.HoughLines(image, rho, theta, threshold)
image
:输入图像,通常为二值图像,其中白点表示边缘点,黑点为背景。
rho
:r的精度,以像素为单位,表示霍夫空间中每一步的距离增量, 值越大,考虑越多的线。
theta
:角度θ的精度,通常以弧度为单位,表示霍夫空间中每一步的角度增量。值越小,考虑越多的线。
threshold
:累加数阈值,只有累积投票数超过这个阈值的候选直线才会被返回。返回值:
cv2.HoughLines
函数返回一个二维数组,每一行代表一条直线在霍夫空间中的参数(rho, theta)
。统计概率霍夫直线变换
标准霍夫变换 ,它会计算图像中的每一个点,计算量比较大,另外它得到的是整一条线(r和θ),并不知道原图中直线的端点,提出统计概率霍夫直线变换
获取到直线之后,会检测原图中在该直线上的点,并获取到两侧的端点坐标,然后通过两个点的坐标来计算该直线的长度,通过直线长度与最短长度阈值的比较来决定该直线要不要被保留。
lines=cv2.HoughLinesP(image, rho, theta, threshold, lines=None, minLineLength=0, maxLineGap=0)
霍夫圆变换
霍夫圆变换跟直线变换类似,它可以从图像中找出潜在的圆形结构,并返回它们的中心坐标和半径
circles=cv2.HoughCircles(image, method, dp, minDist, param1, param2)
image
:输入图像,通常是灰度图像。
method
:使用的霍夫变换方法,可以是cv2.HOUGH_GRADIENT
dp
:累加器分辨率与输入图像分辨率之间的降采样比率,用于加速运算但不影响准确性。设置为1表示霍夫梯度法中累加器图像的分辨率与原图一致
minDist
:检测到的圆心之间的最小允许距离,以像素为单位。
param1
和param2
:这两个参数是在使用cv2.HOUGH_GRADIENT
方法时的特定参数,分别为:
param1
(可选):阈值1,决定边缘强度的阈值。
param2
:阈值2,控制圆心识别的精确度。返回值:
cv2.HoughCircles
返回一个二维numpy数组,包含了所有满足条件的圆的参数。
五、图形亮度变换
线性变换
使用
cv2.addWeighted()
函数,可以对图像的像素值进行加权平均,进而改变图像的整体亮度。亮度增益可以通过向每个像素值添加一个正值来实现。cv2.addWeighted(src1, alpha, src2, beta, gamma)
src1
:第一张输入图像,它将被赋予权重alpha
。
alpha
:第一个输入图像的权重。
src2
:第二张输入图像,它将被赋予权重beta
。
beta
:第二个输入图像的权重。
gamma
:一个标量,将被添加到权重求和的结果上,可用于调整总体亮度。直接像素值修改
如果只需要增加或减少固定的亮度值,可以直接遍历图像像素并对每个像素值进行加减操作。
使用的API:
numpy.clip(a, a_min, a_max)
用于对数组中的元素进行限定,将超出指定范围的元素值截断至指定的最小值和最大值之间
a
:输入数组。
a_min
:指定的最小值,数组中所有小于a_min
的元素将被替换为a_min
。
a_max
:指定的最大值,数组中所有大于a_max
的元素将被替换为a_max
。
六、形态学变换
腐蚀
腐蚀操作就是使用核在原图(二值化图)上进行从左到右、从上到下的滑动。在滑动过程中,令核值为1的区域与被核覆盖的对应区域进行相乘,得到其最小值,该最小值就是卷积核覆盖区域的中心像素点的新像素值,接着继续滑动。区域内只要有0(黑色区域),区域全部替换为0,白色区域就会被压缩,
膨胀
膨胀与腐蚀刚好相反,区域内取最大值,只要有255(白色区域),区域全部替换为255,黑色区域会被压缩
开运算
开运算是先腐蚀后膨胀,其作用是:分离物体,消除小区域。
特点:消除噪点,去除小的干扰块,而不影响原来的图像
闭运算
闭运算与开运算相反,是先膨胀后腐蚀,作用是消除/"闭合"物体里面的孔洞。
特点:可以填充闭合区域
礼帽运算
原图像与"开运算"的结果图之差
礼帽运算用来分离比邻近点亮一些的斑块。
黑帽运算
黑帽运算为"闭运算"的结果图与原图像之差。
黑帽运算用来分离比邻近点暗一些的斑块
形态学梯度
形态学梯度值是该像素点在膨胀后的图像值与其在腐蚀后的图像值之差。这样得到的结果通常能够强化图像的边缘信息,并且对噪声有一定的抑制作用
python
import cv2 as cv
import numpy as np
car = cv.imread("../images/car.png",cv.IMREAD_GRAYSCALE)
# 定义核
kernel = np.ones((5,5),np.uint8)
# 腐蚀
erosion = cv.erode(car,kernel,iterations=1) # 迭代次数
cv.imshow("car",car)
cv.imshow("erosion",erosion)
# 膨胀
dilation = cv.dilate(car,kernel,iterations=1)
cv.imshow("dilation",dilation)
# 开运算
opening = cv.morphologyEx(car,cv.MORPH_OPEN,kernel)
cv.imshow("opening",opening)
# 闭运算
closing = cv.morphologyEx(car,cv.MORPH_CLOSE,kernel)
cv.imshow("closing",closing)
# 礼帽运算 原图与开运算的差
tophat = cv.morphologyEx(car,cv.MORPH_TOPHAT,kernel)
cv.imshow("tophat",tophat)
# 黑帽运算 闭运算与原图之间的差
blackhat = cv.morphologyEx(car,cv.MORPH_BLACKHAT,kernel)
cv.imshow("blackhat",blackhat)
# 形态学梯度 膨胀和腐蚀之差
gradient = cv.morphologyEx(car,cv.MORPH_GRADIENT,kernel)
cv.imshow("gradient",gradient)
cv.waitKey(0)
cv.destroyAllWindows()
总结
本文介绍了图像处理中的多个关键技术:1. 轮廓特征提取方法,包括外接矩形、最小外接矩形和最小外接圆的实现原理与OpenCV应用;2. 直方图均衡化及其改进算法CLAHE的原理与实现;3. 模板匹配的方法与参数选择;4. 霍夫变换在直线和圆检测中的应用;5. 图像亮度调整的两种方法;6. 形态学变换(腐蚀、膨胀、开闭运算等)的原理和实际应用。这些技术共同构成了计算机视觉中的基础图像处理工具集,对图像分析具有重要意义。