OpenCV 图像金字塔、轮廓与模板匹配(学习笔记) 电脑版发表于:2024/1/16 15:39 ![](https://img.tnblog.net/arcimg/hb/3c9a034e352c4476b4ec6d8cc07a0263.png) >#OpenCV 图像金字塔、轮廓与模板匹配(学习笔记) [TOC] ## 图像金字塔 tn2>初始化代码。 ```python 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() ``` ### 高斯金字塔 tn2>在OpenCV中,高斯金字塔是一种图像处理技术,它用于将一幅图像分解成多个不同分辨率的图像版本,其中每个版本都是原始图像的模糊和缩小版本。 这个过程可以帮助我们在不同尺度上分析和处理图像。 ![](https://img.tnblog.net/arcimg/hb/a179c9a293e64e82a8a4400d7f7cf8ce.png) tn2>高斯金字塔:向下采样方法(缩小) ![](https://img.tnblog.net/arcimg/hb/0c84b7844a174048ad53abe9e563a9d4.png) tn2>高斯金字塔:向上采样方法(放大) ![](https://img.tnblog.net/arcimg/hb/506b42d96bbb49ef926df146c4290fc0.png) tn2>查看原始图片大小。 ```python img=cv2.imread("AM.png") cv_show(img,'img') print (img.shape) ``` ![](https://img.tnblog.net/arcimg/hb/97f77490ee3c4be78e883ad47da19297.png) >(442, 340, 3) tn2>放大操作。(大小翻一倍) ```python up=cv2.pyrUp(img) cv_show(up,'up') print (up.shape) ``` ![](https://img.tnblog.net/arcimg/hb/dfe33989c07a4e2a91fd3cd14f2d9e6d.png) >(884, 680, 3) tn2>缩小一倍。 ```python down=cv2.pyrDown(img) cv_show(down,'down') print (down.shape) ``` ![](https://img.tnblog.net/arcimg/hb/aa6c096cd44a42dd81c8d8248f542bd7.png) >(221, 170, 3) tn2>放大两倍 ```python up2=cv2.pyrUp(up) cv_show(up2,'up2') print (up2.shape) ``` ![](https://img.tnblog.net/arcimg/hb/f2a57d7cccd240e6ac906f7d2f0ea967.png) >(1768, 1360, 3) tn2>放大后再缩小,我们会发现它与原有的图片相比比较模糊。 ```python up=cv2.pyrUp(img) up_down=cv2.pyrDown(up) cv_show(np.hstack((img,up_down)),'up_down') ``` ![](https://img.tnblog.net/arcimg/hb/903ec85858a64e1ba32412dd424bfc3f.png) ### 拉普拉斯金字塔 tn2>拉普拉斯金字塔的构建基于高斯金字塔,主要计算每一层与其下一层之间的差异,也就是高斯金字塔中的每一层图像与其下一层图像的差异。 将这些细节图像与高斯金字塔的上一层相应尺度的图像相加,就可以还原出原始图像。 ![](https://img.tnblog.net/arcimg/hb/ddd7a7af8d5849bf8fc8ff86eee32c46.png) ```python down=cv2.pyrDown(img) down_up=cv2.pyrUp(down) l_1=img-down_up cv_show(l_1,'l_1') ``` ![](https://img.tnblog.net/arcimg/hb/d626b65230ad45739690d94ef7c32225.png) ## 图像轮廓 tn2>通常我们再获取图像轮廓的时候,调用`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`:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。 ![](https://img.tnblog.net/arcimg/hb/46640391eb6c4f059fd1ac71f269705b.png) tn2>为了更高的准确率,使用二值图像。 ```python 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') ``` ![](https://img.tnblog.net/arcimg/hb/158dcfbcaf83488ba86ebf7033a4576f.png) ```python # 检测图像中的对象边界 contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) ``` ### 绘制轮廓 ```python #传入绘制图像,轮廓,轮廓索引,颜色模式,线条厚度 # 注意需要copy,要不原图会变。。。 draw_img = img.copy() res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2) cv_show(res,'res') ``` ![](https://img.tnblog.net/arcimg/hb/6d377465fb8742b9ae09b6428a6a7db9.png) tn2>绘制第一个。 ```python draw_img = img.copy() res = cv2.drawContours(draw_img, contours, 0, (0, 0, 255), 2) cv_show(res,'res') ``` ![](https://img.tnblog.net/arcimg/hb/177320e392bc49b2bc38e6ba8a0f483e.png) ### 轮廓特征 tn2>计算第一个图像的面积和周长。 ```python cnt = contours[0] #面积 cv2.contourArea(cnt) ``` >8500.5 ```python #周长,True表示闭合的 cv2.arcLength(cnt,True) ``` >437.9482651948929 ### 轮廓近似 tn2>轮廓近似(Contour Approximation)是图像处理和计算机视觉中的一种技术,用于减少轮廓的顶点数目,以简化轮廓的表示。 ![](https://img.tnblog.net/arcimg/hb/f3afa7ef8fc14e20bc7c933b2b94e9e5.png) ```python 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') ``` ![](https://img.tnblog.net/arcimg/hb/1a2d2927d7bb4b2e87599bcb4cf60a7b.png) ```python # 计算轮廓周长(弧长)乘以的值越小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') ``` ![](https://img.tnblog.net/arcimg/hb/833177c3c8ee4bf98823d26ca1ad4859.png) ```python epsilon = 0.15*cv2.arcLength(cnt,True) ``` ![](https://img.tnblog.net/arcimg/hb/b73a812df96149c29382f1e3e702a3cf.png) ### 边界矩形 ```python 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') ``` ![](https://img.tnblog.net/arcimg/hb/133a97fcb8f943bfbab5909f3d460e0e.png) ```python # 计算第一个图的面积 area = cv2.contourArea(cnt) # 计算包裹这个图的矩形面积 x, y, w, h = cv2.boundingRect(cnt) rect_area = w * h extent = float(area) / rect_area print ('轮廓面积与边界矩形比',extent) ``` >轮廓面积与边界矩形比:0.5154317244724715 ### 外接圆 tn2>使用圆进行包裹。 ```python (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') ``` ![](https://img.tnblog.net/arcimg/hb/2f6d5d56aa5440789cbcea0f3359c06d.png) ## 模板匹配 tn2>模板匹配和卷积原理很像,模板在原图像上从原点开始滑动,计算模板与(图像被模板覆盖的地方)的差别程度,这个差别程度的计算方法在opencv里有6种,然后将每次计算的结果放入一个矩阵里,作为结果输出。假如原图形是AxB大小,而模板是axb大小,则输出结果的矩阵是(A-a+1)x(B-b+1) tn>简单来讲:模板匹配就像在一张图片里找到一张小图片的位置,就像找到拼图的一块在整个拼图上的位置一样。 ```python # 模板匹配 img = cv2.imread('lena.jpg', 0) template = cv2.imread('face.jpg', 0) h, w = template.shape[:2] ``` ```python img.shape ``` >(263, 263) ```python template.shape ``` >(110, 85) | 模板匹配模式 | 描述 | | ------------ | ------------ | | TM_SQDIFF|计算平方不同,计算出来的值越小,越相关| | TM_CCORR|计算相关性,计算出来的值越大,越相关| | TM_CCOEFF|计算相关系数,计算出来的值越大,越相关| | TM_SQDIFF_NORMED|计算归一化平方不同,计算出来的值越接近0,越相关| | TM_CCORR_NORMED|计算归一化相关性,计算出来的值越接近1,越相关| | TM_CCOEFF_NORMED|计算归一化相关系数,计算出来的值越接近1,越相关| tn2>公式:https://docs.opencv.org/3.3.1/df/dfb/group__imgproc__object.html#ga3a7850640f1fe1f58fe91a2d7583695d ```python methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR', 'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED'] ``` ```python res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF) res.shape ``` >(154, 179) ```python 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)) tn2>遍历所有的模式: ```python 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() ``` ![](https://img.tnblog.net/arcimg/hb/b84f0dab06f6441e877d814fb98285de.png) ![](https://img.tnblog.net/arcimg/hb/22d38d8e72094dad8290608249a403f9.png) ![](https://img.tnblog.net/arcimg/hb/5f02dcb619954ac28a2d7a819d82a4c3.png) ![](https://img.tnblog.net/arcimg/hb/49c1c6453233487b98214b170baa6d5b.png) ![](https://img.tnblog.net/arcimg/hb/314ad8ec594546eda77c728ece7aca28.png) ![](https://img.tnblog.net/arcimg/hb/360399b8c1024d52a6564bee3c70c50e.png) ### 匹配多个对象 tn2>匹配多个金币。 ```python 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) ``` ![](https://img.tnblog.net/arcimg/hb/9e69b44ecb1b472aa1a6461974343453.png)