文章目录
- 前言
-
- [【计算机视觉】OpenCV 图像处理阈值处理 + 图像编辑 + 噪声滤波 + 图像运算(https://blog.csdn.net/m0_66822255/article/details/161371374?spm=1001.2014.3001.5501)](#【计算机视觉】OpenCV 图像处理阈值处理 + 图像编辑 + 噪声滤波 + 图像运算)
- 一、轮廓检测基础
-
- 1.概念
- [2. 轮廓检测的前置条件](#2. 轮廓检测的前置条件)
- [3. 完整代码](#3. 完整代码)
- [4. 代码详解](#4. 代码详解)
- 二、轮廓特征筛选与外接图形绘制
-
- [1. 概念](#1. 概念)
- [2. 完整代码](#2. 完整代码)
- [3. 代码详解](#3. 代码详解)
- 三、轮廓近似(多边形逼近)
-
- 1.概念
- [2. 完整代码](#2. 完整代码)
- [3. 代码详解](#3. 代码详解)
前言
在计算机视觉领域,OpenCV 是最经典、最常用的开源图像处理库,无论是基础的图像变换、噪声处理,还是进阶的目标检测、图像分割,都离不开 OpenCV 的核心操作。本次案例全覆盖轮廓检测、轮廓特征筛选、外接圆 / 外接矩形绘制、轮廓近似四大核心模块,搭配逐行代码解析 + 原理详解 + 效果说明。
如果对计算机视觉有兴趣但是没有了解的读者,可以先观看博主的第一篇关于计算机视觉入门的文章。
【计算机视觉】OpenCV 图像处理阈值处理 + 图像编辑 + 噪声滤波 + 图像运算
一、轮廓检测基础
1.概念
轮廓检测是图像分割与目标识别的核心技术,核心逻辑是:在二值图像中,通过边缘点的连接,勾勒出目标物体的边界轮廓,实现前景物体的定位与分离。
简单来说:就是把图像中不同物体的 "外边框" 找出来,后续才能进行物体计数、特征分析等操作。
2. 轮廓检测的前置条件
轮廓检测必须在二值图像上进行,所以完整流程是:
彩色图 → 灰度图 → 二值化处理 → 轮廓检测
3. 完整代码
轮廓的检索模式,主要有四种方式:
- cv2.RETR_EXTERNAL:只检测外轮廓,所有子轮廓被忽略
- cv2.RETR_LIST:检测的轮廓不建立等级关系,所有轮廓属于同一等级
- cv2.RETR_CCOMP:返回所有的轮廓,只建立两个等级的轮廓。一个对象的外轮廓为第1级组织结构。而对象内部中空洞的轮廓为第2级组织结构,空洞中的任何对象的轮廓又是第1级组织结构
- cv2.RETR_TREE:返回所有的轮廓,建立一个完整的组织结构的轮廓。
代码完整且可直接运行,有需要可自取
c
import cv2
py = cv2.imread('python.png')
py_new = cv2.resize(py,None,fx=0.3,fy=0.3)
py_gray = cv2.cvtColor(py_new,cv2.COLOR_BGR2GRAY)
cv2.imshow('py_b',py_gray)
cv2.waitKey(0)
ret,py_binary = cv2.threshold(py_gray,120,255,cv2.THRESH_BINARY)
cv2.imshow('py_binary',py_binary)
cv2.waitKey(0)
contours = cv2.findContours(py_binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)[-2]
print(len(contours))
image_copy = py_new.copy()
cv2.drawContours(image=image_copy,contours=contours,contourIdx=-1,color=(0,255,0),thickness=2)
# 查看检测到了多少个轮廓
print("检测到的轮廓总数:", len(contours))
cv2.imshow('Contours_show',image_copy)
cv2.waitKey(0)
cv2.destroyAllWindows()
4. 代码详解
在编写代码前,我们需要先安装核心工具包:
c
pip install opencv-python numpy -i https://pypi.tuna.tsinghua.edu.cn/simple/
首先导入 OpenCV 库,读取本地图像并做基础预处理:
- cv2.imread():读取彩色图像,参数为图片路径;
- cv2.resize():按比例缩放图像,fx/fy 为宽高缩放比例,避免图像过大导致轮廓检测混乱;
- cv2.cvtColor():将彩色图像转为灰度图,cv2.COLOR_BGR2GRAY 表示 BGR 转灰度,二值化处理必须使用灰度图。
c
import cv2
py = cv2.imread('python.png')
py_new = cv2.resize(py,None,fx=0.3,fy=0.3)
py_gray = cv2.cvtColor(py_new,cv2.COLOR_BGR2GRAY)
调用 cv2.threshold() 将灰度图转为黑白二值图。
这涉及到图像的阈值处理,博主也有相关介绍的文章,感兴趣的读者也可以去看看,这里不过多赘述,仅说明方法。
c
ret,py_binary = cv2.threshold(py_gray,120,255,cv2.THRESH_BINARY)
这是轮廓检测核心函数,会返回三个值,轮廓列表、层次结构等,-2 表示只取轮廓列表;
轮廓检索模式cv2.RETR_TREE:建立轮廓的完整层次结构.
轮廓逼近方法 cv2.CHAIN_APPROX_NONE:保留轮廓上的所有点。
c
contours = cv2.findContours(py_binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)[-2]
绘制轮廓
- image_copy:在原图副本上绘制轮廓,避免修改原图;
- contourIdx=-1:表示绘制所有轮廓,若指定索引则只绘制对应轮廓;
- color=(0, 255, 0):轮廓颜色,这里是绿色;代表BGR通道。
- thickness=2:轮廓线的粗细。
c
image_copy = py_new.copy()
cv2.drawContours(image=image_copy,contours=contours,contourIdx=-1,color=(0,255,0),thickness=2)
程序运行结束后关闭窗口。
c
cv2.imshow('Contours_show', image_copy)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行结果:

二、轮廓特征筛选与外接图形绘制
1. 概念
轮廓检测会返回大量轮廓(包括噪声、小斑点等无效轮廓),需要通过轮廓面积筛选有效目标;同时可以为轮廓绘制外接圆、外接矩形,用于目标定位与特征分析。
2. 完整代码
c
import cv2
py = cv2.imread('python.png')
py_new = cv2.resize(py,None,fx=0.3,fy=0.3)
py_gray = cv2.cvtColor(py_new,cv2.COLOR_BGR2GRAY)
cv2.imshow('py_b',py_gray)
cv2.waitKey(0)
ret,py_binary = cv2.threshold(py_gray,120,255,cv2.THRESH_BINARY)
cv2.imshow('py_binary',py_binary)
cv2.waitKey(0)
contours = cv2.findContours(py_binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)[-2]
print(len(contours))
area_0 = cv2.contourArea(contours[0])
print(area_0)
area_1 = cv2.contourArea(contours[1])
print(area_1)
length = cv2.arcLength(contours[0],closed=True)
print(length)
a_list = []
for i in contours:
if cv2.contourArea(i)>4000:#做筛选,面积大于4000的轮廓
a_list.append(i)
image_copy = py_new.copy()
image_copy = cv2.drawContours(image=image_copy,contours=a_list,contourIdx=-1,color=(0,255,0),thickness=3)
cv2.imshow('Contours_show_4000',image_copy)
cv2.waitKey(0)
sortcnt = sorted(contours,key=cv2.contourArea,reverse=True)[0]
image_contours = cv2.drawContours(image_copy,[sortcnt],contourIdx=-1,color=(255,0,0),thickness=3)
cv2.imshow('image_contours',image_contours)
cv2.waitKey(0)
#外接圆,外接矩阵
cnt = contours[5]
(x,y),r = cv2.minEnclosingCircle(cnt)
py_new_circle = cv2.circle(py_new,(int(x),int(y)),int(r),(0,255,0),3)
cv2.imshow('py_new_circle',py_new_circle)
cv2.waitKey(0)
x,y,w,h = cv2.boundingRect(cnt)
py_new_rectangle = cv2.rectangle(py_new,(x,y),(x+w,y+h),(0,255,0),3)
cv2.imshow('py_new_rectangle',py_new_rectangle)
cv2.waitKey(0)
3. 代码详解
上方一段代码和轮廓检测是一样的,这里不过多赘述。
这里我们检测轮廓的面积,周长大小,并输出。
面积大小用于代码筛选的条件。
c
area_0 = cv2.contourArea(contours[0])
print(area_0)
area_1 = cv2.contourArea(contours[1])
print(area_1)
length = cv2.arcLength(contours[0],closed=True)
print(length)
这里设立for循环遍历语句,目的是筛选出轮廓大小大于4000的轮廓。(单位为像素)
再将筛选出来的轮廓添加到列表中。
c
a_list = []
for i in contours:
if cv2.contourArea(i)>4000:
a_list.append(i)
我们这里通过copy的方法,复制原图像,之后的操作都在复制图像上操作,而不会对原图像有影响。
对复制图像进行轮廓检测,并输出图像。
c
image_copy = py_new.copy()
image_copy = cv2.drawContours(image=image_copy,contours=a_list,contourIdx=-1,color=(0,255,0),thickness=3)
cv2.imshow('Contours_show_4000',image_copy)
cv2.waitKey(0)
按面积排序,找到最大轮廓。以轮廓的面积为关键字,降序排列;后输出图像。
这里用到的颜色为蓝色,便于后面区分。
c
sortcnt = sorted(contours,key=cv2.contourArea,reverse=True)[0]
image_contours = cv2.drawContours(image_copy,[sortcnt],contourIdx=-1,color=(255,0,0),thickness=3)
cv2.imshow('image_contours',image_contours)
cv2.waitKey(0)
我们设cnt为第六个轮廓,绘制外接圆。
要绘制出圆,我们需要圆心坐标和半径r。
cv2.minEnclosingCircle(cnt):计算轮廓的最小外接圆,返回圆心坐标 (x, y) 和半径 r;
同时,cv2.circle():在图像上绘制圆形,参数为图像、圆心、半径、颜色、线宽。
c
cnt = contours[5]
(x,y),r = cv2.minEnclosingCircle(cnt)
py_new_circle = cv2.circle(py_new,(int(x),int(y)),int(r),(0,255,0),3)
cv2.imshow('py_new_circle',py_new_circle)
cv2.waitKey(0)
绘制外接矩形,我们需要的是左上角坐标和右下角坐标。从代码中,我们可以得到左上角坐标(x,y),和宽w,高h。
在这里我们需要注意的是在pycharm中的坐标系与我们在数学中的形态是不同的。

c
x,y,w,h = cv2.boundingRect(cnt)
py_new_rectangle = cv2.rectangle(py_new,(x,y),(x+w,y+h),(0,255,0),3)
cv2.imshow('py_new_rectangle',py_new_rectangle)
cv2.waitKey(0)
运行结果:

三、轮廓近似(多边形逼近)
1.概念
轮廓近似是用更少的点来近似表示轮廓,核心逻辑是通过设置近似精度 epsilon,将复杂轮廓简化为多边形轮廓,保留关键形状特征,减少轮廓点数量,提高后续处理效率。
2. 完整代码
c
import cv2
phone = cv2.imread('phone.png')
phone_gray = cv2.cvtColor(phone,cv2.COLOR_BGR2GRAY)
#二值化
ret,phone_thresh = cv2.threshold(phone_gray,120,255,cv2.THRESH_BINARY)
contours=cv2.findContours (phone_thresh, cv2.RETR_TREE, cv2. CHAIN_APPROX_NONE)[-2]
epsilon = 0.01 * cv2.arcLength(contours[0],True)
approx = cv2.approxPolyDP(contours[0],epsilon,True)
print(contours[0].shape)
print(approx.shape)
phone_new = phone.copy()
image_contours = cv2.drawContours(phone_new,[approx],contourIdx=-1,color=(0,255,0),thickness=3)
cv2.imshow('phone',phone)
cv2.waitKey(0)
cv2.imshow('image_contours',image_contours)
cv2.waitKey(0)
3. 代码详解
在对图像做完预处理之后,计算轮廓周长
- cv2.arcLength(contours0, True):计算轮廓的周长,True 表示轮廓是封闭的;
- epsilon:近似精度,这里取轮廓周长的 1%,值越小,近似后的轮廓越接近原轮廓;值越大,轮廓越简化。
c
contours=cv2.findContours (phone_thresh, cv2.RETR_TREE, cv2. CHAIN_APPROX_NONE)[-2]
epsilon = 0.01 * cv2.arcLength(contours[0],True)
轮廓近似核心函数cv2.approxPolyDP(),有以下几个参数需要注意,其他的参数可暂时用默认。
- contours0:需要近似的原轮廓;
- epsilon:近似精度,即轮廓上的点到近似多边形的最大距离;
- True:表示近似后的轮廓是封闭的。
c
approx = cv2.approxPolyDP(contours[0],epsilon,True)
打印第一个轮廓的形状,后输出轮廓的格式,一般是(点数, 1, 2)
然后打印打印近似后的经过 approxPolyDP 简化后的轮廓的形状,输出会比原来小很多。
最后在复制的图上,用绿色线画出简化后的轮廓。
c
print(contours[0].shape)
print(approx.shape)
phone_new = phone.copy()
image_contours = cv2.drawContours(phone_new,[approx],contourIdx=-1,color=(0,255,0),thickness=3)
cv2.imshow('phone',phone)
cv2.waitKey(0)
cv2.imshow('image_contours',image_contours)
cv2.waitKey(0)
代码输出:
第一行输出是原轮廓。
第二行是简化后的轮廓。
c
(759, 1, 2)
(8, 1, 2)
输出结果:

我们观察可以发现简化后的轮廓共8个点,符合结果。

之后也可以尝试不同的近似精度对图像,对轮廓的影响。