news 2026/3/14 17:17:13

Opencv总结2——图像金字塔与轮廓检测

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Opencv总结2——图像金字塔与轮廓检测

纯手打持续更新中~

1、轮廓检测(findContours)

cv2.findContours(img,mode,method)

mode:轮廓检索模式

  • RETR_EXTERNAL :只检索最外面的轮廓;

  • RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中;

  • RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;

  • RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次;(最常用这个)

method:轮廓逼近方法

  • CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。

  • CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。

输入图像的要求,必须是二值的!(更高的准确率)

img = cv2.imread('contours.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 这一步对图像二值处理 cv_show(thresh,'thresh')

binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

binary 为二值化之后的结果

contours为轮廓信息

hierarchy 表示层级

绘制轮廓(drawContours)

#传入绘制图像,轮廓,轮廓索引,颜色模式,线条厚度 # 注意需要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')

轮廓特征与近似(contourArea)

轮廓特征计算

cnt = contours[0] #面积 cv2.contourArea(cnt) #周长,True表示闭合的 cv2.arcLength(cnt,True)

轮廓近似

img = cv2.imread('contours2.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) 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')

这里用到了近似函数approxPolyDP

epsilon = 0.15*cv2.arcLength(cnt,True) # 0.15 越小是越貼合的 approx = cv2.approxPolyDP(cnt,epsilon,True) #cnt是轮廓,epsilon是指定需要比较的值,一般是周长的倍数 draw_img = img.copy() res = cv2.drawContours(draw_img, [approx], -1, (0, 0, 255), 2) cv_show(res,'res')

2、模板匹配(matchTemplate)

两张图片,现在是要看第一张图片是在第二张图片的哪一个部分。

把右边的图片分成九宫格,然后opencv会从左到右,从上到下进行一点点的匹配(逐像素匹配,像素点的差异),看看左边的图像是在右边的哪个位置。

模板匹配和卷积原理很像,模板在原图像上从原点开始滑动,计算模板与(图像被模板覆盖的地方)的差别程度,这个差别程度的计算方法在opencv里有6种,然后将每次计算的结果放入一个矩阵里,作为结果输出。假如原图形是AxB大小,而模板是axb大小,则输出结果的矩阵是(A-a+1)x(B-b+1)

模板匹配的代码(代码在notebook中运行):

# 模板匹配 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)

有以下6种差异计算方法:

  • 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']

cv2.minMaxLoc()是专门用于提取匹配结果矩阵(res)中极值(最小值 / 最大值)对应坐标的核心函数.

最小值就是根据需要匹配的图中,找到原图中的一个匹配点,后续根据h和w就能画出一个候选的方框。

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:39168.0 max_val:74403584.0 min_loc:(107, 89) max_loc:(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)

3、图像金字塔(pyrUp,pyrDown)

把图像组合成金字塔的形状,可以用于图像特征提取,每层的特征提取的结果组合在一起。

高斯金字塔:向下采样方法(缩小)用高斯核,然后把偶数列全部去掉。

高斯金字塔:向上采样方法(放大)

用相同的卷积核来卷积,可以想象10这个像素平均的分给了周围的像素点。

代码实验

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)

拉普拉斯金字塔

拉普拉斯金字塔的每一层 = 当前层的高斯图像 - 上采样后的下一层高斯图像。

down=cv2.pyrDown(img) down_up=cv2.pyrUp(down) l_1=img-down_up cv_show(l_1,'l_1')
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/12 22:48:33

自容式水听器是什么?偶信科技为您解答

在探索海洋的征途中,如何高效、可靠地获取水下声音信息,一直是科研与工程应用的核心课题。近年来,“自容式水听器”这一术语频繁出现在海洋观测、水声通信和环境监测等领域。那么,自容式水听器究竟是什么?它与传统水听…

作者头像 李华
网站建设 2026/3/13 10:56:54

有源蜂鸣器和无源区分选型的6个关键因素

如何选对蜂鸣器?有源 vs 无源,6个实战维度讲透关键差异你有没有遇到过这样的场景:产品快要量产了,突然发现报警音太单调,想换成“滴滴—嘟”这种变调提示,结果一看用的是有源蜂鸣器——换不了!只…

作者头像 李华
网站建设 2026/3/13 0:09:53

基于vue的订餐小程序毕设源码(源码+lw+部署文档+讲解等)

博主介绍:✌ 专注于VUE,小程序,安卓,Java,python,物联网专业,有18年开发经验,长年从事毕业指导,项目实战✌选取一个适合的毕业设计题目很重要。✌关注✌私信我✌具体的问题,我会尽力帮助你。一、…

作者头像 李华
网站建设 2026/3/14 1:33:56

工业控制系统中vivado2021.1安装操作指南

Vivado 2021.1 安装实战指南:为工业控制系统打造稳定FPGA开发环境 你有没有遇到过这样的情况?项目刚启动,团队急着要跑通第一个Zynq-7000的PL端逻辑,结果发现Vivado装不上——界面打不开、依赖报错、许可证加载失败……折腾三天两…

作者头像 李华