模板匹配
尽量加上归一化操作
像素差值计算,模板在原图上滑动
① 模板匹配和卷积原理很像,模板在原图像上从原点开始滑动,计算模板与(图像被模板覆盖的地方)的差别程度(例如值127与值190的区别),这个差别程度的计算方法在opencv里有6种,然后将每次计算的结果放入一个矩阵里,作为结果输出。
② 假如原图形是AxB大小,而模板是axb大小,则输出结果的矩阵是(A-a+1)x(B-b+1)。
因为是类似于卷积
他5行的 你拿4行的去卷 只能卷2次 5-4+1 = 2
③ 模板匹配计算方式6种方式 ( 用归一化后的方式更好一些 ):
- TM_SQDIFF:计算平方不同,计算出来的值越小,越相关。
- TM_CCORR:计算相关性,计算出来的值越大,越相关。
- TM_CCOEFF:计算相关系数,计算出来的值越大,越相关。
- TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关。
- TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关。
- TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关。
methods = ['cv2.TM_CCOEFF','cv2.TM_CCOEFF_NORMED','cv2.TM_CCORR',
'cv2.TM_CCORR_NORMED','cv2.TM_SQDIFF','cv2.TM_SQDIFF_NORMED']
res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF) 可以拿标号数字表示不同方法
print(res.shape) # 返回的矩阵大小 (A-a+1)x(B-b+1)
min_val,max_val,min_loc,max_loc = cv2.minMaxLoc(res) # 返回模板匹配后最小值、最大值的位置
print(min_val) # cv2.TM_SQDIFF方法中,越小的值表示像素点的差异越小
print(max_val)
print(min_loc) # 当获得最小值对应的模板左上角的位置,加上模板自身的长、宽,可以在原图像中画出最匹配的区域
print(max_loc)
多模板匹配
img_rgb = cv2.imread('01_Picture/14_Mario.jpg')
img_gray = cv2.cvtColor(img_rgb,cv2.COLOR_BGR2GRAY)
print('img_gray.shape:',img_gray.shape)
template = cv2.imread('01_Picture/15_Mario_coin.jpg',0)
print('template.shape:',template.shape)
h, w = template.shape[:2]
# res 是 result 的简称
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED) # res 是返回每一个小块窗口得到的结果值
print('res.shape:',res.shape)
threshold = 0.8
# 取匹配程度大于 80% 的坐标
loc = np.where(res >= threshold) # np.where 使得返回 res 矩阵中值大于 0.8 的索引,即坐标
print('type(loc):',type(loc)) # loc 为元组类型
print('len(loc):',len(loc)) # loc 元组有两个值
print('len(loc[0]):',len(loc[0]),'len(loc[1]):',len(loc[1])) # loc 元组每个值 120 个元素
print('type(loc[0]):',type(loc[0]),'type(loc[1]):',type(loc[1])) # loc 元组每个值的类型为 numpy.array
print("loc[::-1]:",loc[::-1]) # loc[::-1] 表示顺序取反,即第二个 numpy.array 放在第一个 numpy.array 前面
i = 0
# zip函数为打包为元组的列表,例 a = [1,2,3] b = [4,5,6] zip(a,b) 为 [(1, 4), (2, 5), (3, 6)]
for pt in zip(*loc[::-1]): # 当用 *b 作为传入参数时, b 可以为列表、元组、集合,zip使得元组中两个 numpy.array 进行配对
bottom_right = (pt[0] + w, pt[1] + h)
cv2.rectangle(img_rgb, pt, bottom_right, (0,0,255),2)
i = i + 1
print('i:',i)
cv2.imshow('img_rgb',img_rgb)
cv2.waitKey(0)
直方图
① 图像直方图是把图像变为灰度图,分成一个一个像素点的值进行统计,如下图左所示。
② 直方图统计函数 cv2.calcHist(images,channels,mask,histSize,ranges)
- images:原图像的图像格式为 uint8 或 float32。当传入函数时应该用中括号 [] 括来传入,例如[img]
- channels:同样用中括号来传入,它会告诉函数统幅的哪幅灰度图的直方图。如果传入的图像是灰度图它的值就是 [0],如果是彩色图像,那么传入的参数可以是 [0]、[1]、[2],它们分别对应着 B、G、R 通道,每个通道的图像都是灰度图。
- mask:掩模图像。统计整幅图像的直方图时就把它设为 None。但是如果你想统计图像的某一部分区域的直方图的,你就制作一个掩模图像并使用它。
- histSize:BIN 的数目。也应用中括号括来。 相当于x轴坐标的数目
- ranges: 统计的像素值范围,常为 [0-256]。 每个x轴上坐标数值的范围
img = cv2.imread('01_Picture/01_Cat.jpg',0) # 0 表示灰度图
print((img.ravel()).shape)
histr = cv2.calcHist([img],[0],None,[256],[0,256])
plt.hist(img.ravel(),256) # img.ravel()将 img 拉成一维数组
plt.show()
img = cv2.imread('01_Picture/01_Cat.jpg')
color = ('b','g','r')
for i,col in enumerate(color):
histr = cv2.calcHist([img],[i],None,[256],[0,256])
print(histr.shape)
plt.plot(histr,color=col)
plt.xlim([0,256])
掩码
相当于截取
img = cv2.imread('01_Picture/01_Cat.jpg',0)
cv_show(img,'img')
print(img.shape[:2])
mask = np.zeros(img.shape[:2],np.uint8)
print(mask.shape)
mask[100:300,100:400] = 255 需要保留的地方
cv_show(mask,'mask')
masked_img = cv2.bitwise_and(img,img,mask=mask) # 与操作
cv_show(masked_img,'masked_img')
图像掩码直方图
equ = cv2.equalizeHist(img)
plt.hist(equ.ravel(),256)
plt.show()
更亮更显眼
自适应直方图均衡
分模块均衡,可能比全局更靠谱
需要衡量
img = cv2.imread('01_Picture/16_Clahe.jpg',0)
clahe = cv2.createCLAHE(clipLimit=2.0,tileGridSize=(8,8)) # 自适应均衡化方法生成出来
res_clahe = clahe.apply(img) # 方法应用到输入图片当中
res = np.hstack((img,equ,res_clahe))
cv_show(res,'res')
① 如上图所示,直方图均衡化,人脸石膏本来有一些特征,可能由于直方图均衡导致丢失一些细节。所以可能切分成几个小块,局部做直方图均衡化,会比较好。
② 切分成几个小块之后,可能会导致一个现象,每个格子都会产生一个边界,opencv是对每个格子的边界进行线性插值处理。
③ 直方图均衡化函数:cv2.createCLAHE(clipLimit,tileGridSize)
- clipLimit 颜色对比度的阈值。
- titleGridSize 进行像素均衡化的网格大小,即在多少网格下进行直方图的均衡化操作。