
OpenCV 图像金字塔、轮廓与模板匹配(学习笔记)
图像金字塔
初始化代码。
import cv2 #opencv读取的格式是BGR
import numpy as np
import matplotlib.pyplot as plt#Matplotlib是RGB
%matplotlib inline
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
高斯金字塔
在OpenCV中,高斯金字塔是一种图像处理技术,它用于将一幅图像分解成多个不同分辨率的图像版本,其中每个版本都是原始图像的模糊和缩小版本。
这个过程可以帮助我们在不同尺度上分析和处理图像。
高斯金字塔:向下采样方法(缩小)
高斯金字塔:向上采样方法(放大)
查看原始图片大小。
img=cv2.imread("AM.png")
cv_show(img,'img')
print (img.shape)
(442, 340, 3)
放大操作。(大小翻一倍)
up=cv2.pyrUp(img)
cv_show(up,'up')
print (up.shape)
(884, 680, 3)
缩小一倍。
down=cv2.pyrDown(img)
cv_show(down,'down')
print (down.shape)
(221, 170, 3)
放大两倍
up2=cv2.pyrUp(up)
cv_show(up2,'up2')
print (up2.shape)
(1768, 1360, 3)
放大后再缩小,我们会发现它与原有的图片相比比较模糊。
up=cv2.pyrUp(img)
up_down=cv2.pyrDown(up)
cv_show(np.hstack((img,up_down)),'up_down')
拉普拉斯金字塔
拉普拉斯金字塔的构建基于高斯金字塔,主要计算每一层与其下一层之间的差异,也就是高斯金字塔中的每一层图像与其下一层图像的差异。
将这些细节图像与高斯金字塔的上一层相应尺度的图像相加,就可以还原出原始图像。
down=cv2.pyrDown(img)
down_up=cv2.pyrUp(down)
l_1=img-down_up
cv_show(l_1,'l_1')
图像轮廓
通常我们再获取图像轮廓的时候,调用cv2.findContours(img,mode,method)
方法。img
:通常是二值化后的灰度图。mode
:轮廓检索模式。一共有以下四种模式:cv2.RETR_EXTERNAL
:只检索最外面的轮廓;cv2.RETR_LIST
:检索所有的轮廓,并将其保存到一条链表当中;cv2.RETR_CCOMP
:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;cv2.RETR_TREE
:检索所有的轮廓,并重构嵌套轮廓的整个层次;method
:轮廓逼近方法cv2.CHAIN_APPROX_NONE
:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。cv2.CHAIN_APPROX_SIMPLE
:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。
为了更高的准确率,使用二值图像。
img = cv2.imread('contours.png')
# 转成灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 设置阈值在127-255之间,进行二值化处理
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
cv_show(thresh,'thresh')
# 检测图像中的对象边界
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
绘制轮廓
#传入绘制图像,轮廓,轮廓索引,颜色模式,线条厚度
# 注意需要copy,要不原图会变。。。
draw_img = img.copy()
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2)
cv_show(res,'res')
绘制第一个。
draw_img = img.copy()
res = cv2.drawContours(draw_img, contours, 0, (0, 0, 255), 2)
cv_show(res,'res')
轮廓特征
计算第一个图像的面积和周长。
cnt = contours[0]
#面积
cv2.contourArea(cnt)
8500.5
#周长,True表示闭合的
cv2.arcLength(cnt,True)
437.9482651948929
轮廓近似
轮廓近似(Contour Approximation)是图像处理和计算机视觉中的一种技术,用于减少轮廓的顶点数目,以简化轮廓的表示。
img = cv2.imread('contours2.png')
# 二值化处理
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]
#对第一个图像轮廓进行绘制
draw_img = img.copy()
res = cv2.drawContours(draw_img, [cnt], -1, (0, 0, 255), 2)
cv_show(res,'res')
# 计算轮廓周长(弧长)乘以的值越小0.01轮廓绘制越清晰
epsilon = 0.01*cv2.arcLength(cnt,True)
# 用于对轮廓进行多边形逼近
approx = cv2.approxPolyDP(cnt,epsilon,True)
draw_img = img.copy()
res = cv2.drawContours(draw_img, [approx], -1, (0, 0, 255), 2)
cv_show(res,'res')
epsilon = 0.15*cv2.arcLength(cnt,True)
边界矩形
img = cv2.imread('contours.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]
# 使用矩形包裹
x,y,w,h = cv2.boundingRect(cnt)
# 绘制矩形框
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
cv_show(img,'img')
# 计算第一个图的面积
area = cv2.contourArea(cnt)
# 计算包裹这个图的矩形面积
x, y, w, h = cv2.boundingRect(cnt)
rect_area = w * h
extent = float(area) / rect_area
print ('轮廓面积与边界矩形比',extent)
轮廓面积与边界矩形比:0.5154317244724715
外接圆
使用圆进行包裹。
(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
img = cv2.circle(img,center,radius,(0,255,0),2)
cv_show(img,'img')
模板匹配
模板匹配和卷积原理很像,模板在原图像上从原点开始滑动,计算模板与(图像被模板覆盖的地方)的差别程度,这个差别程度的计算方法在opencv里有6种,然后将每次计算的结果放入一个矩阵里,作为结果输出。假如原图形是AxB大小,而模板是axb大小,则输出结果的矩阵是(A-a+1)x(B-b+1)
简单来讲:模板匹配就像在一张图片里找到一张小图片的位置,就像找到拼图的一块在整个拼图上的位置一样。
# 模板匹配
img = cv2.imread('lena.jpg', 0)
template = cv2.imread('face.jpg', 0)
h, w = template.shape[:2]
img.shape
(263, 263)
template.shape
(110, 85)
模板匹配模式 | 描述 |
---|---|
TM_SQDIFF | 计算平方不同,计算出来的值越小,越相关 |
TM_CCORR | 计算相关性,计算出来的值越大,越相关 |
TM_CCOEFF | 计算相关系数,计算出来的值越大,越相关 |
TM_SQDIFF_NORMED | 计算归一化平方不同,计算出来的值越接近0,越相关 |
TM_CCORR_NORMED | 计算归一化相关性,计算出来的值越接近1,越相关 |
TM_CCOEFF_NORMED | 计算归一化相关系数,计算出来的值越接近1,越相关 |
公式:https://docs.opencv.org/3.3.1/df/dfb/group__imgproc__object.html#ga3a7850640f1fe1f58fe91a2d7583695d
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)
res.shape
(154, 179)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
# 匹配结果矩阵中最小值,匹配结果矩阵中最大值,最小值的位置,最大值的位置
min_val, max_val, min_loc, max_loc
(39168.0, 74403584.0, (107, 89), (159, 62))
遍历所有的模式:
for meth in methods:
img2 = img.copy()
# 匹配方法的真值
method = eval(meth)
print (method)
res = cv2.matchTemplate(img, template, method)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
# 如果是平方差匹配TM_SQDIFF或归一化平方差匹配TM_SQDIFF_NORMED,取最小值
if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
top_left = min_loc
else:
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
# 画矩形
cv2.rectangle(img2, top_left, bottom_right, 255, 2)
plt.subplot(121), plt.imshow(res, cmap='gray')
plt.xticks([]), plt.yticks([]) # 隐藏坐标轴
plt.subplot(122), plt.imshow(img2, cmap='gray')
plt.xticks([]), plt.yticks([])
plt.suptitle(meth)
plt.show()
匹配多个对象
匹配多个金币。
img_rgb = cv2.imread('mario.jpg')
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread('mario_coin.jpg', 0)
h, w = template.shape[:2]
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.8
# 取匹配程度大于%80的坐标
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]): # *号表示可选参数
bottom_right = (pt[0] + w, pt[1] + h)
cv2.rectangle(img_rgb, pt, bottom_right, (0, 0, 255), 2)
cv2.imshow('img_rgb', img_rgb)
cv2.waitKey(0)
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739

