1.了解什么是轮廓。
学习寻找轮廓,绘制轮廓等
了解这些函数:cv.findContours(), cv.drawContours()
轮廓可以简单地解释为连接所有连续点(沿边界)的曲线,具有相同的颜色或强度。轮廓是形状分析和对象检测和识别的有用工具。
为了获得更好的准确性,请使用二进制图像。所以在找到轮廓之前,应用阈值或精明的边缘检测。
从 OpenCV 3.2 开始, findContours() 不再修改源图像。
在 OpenCV 中,寻找轮廓就像从黑色背景中寻找白色物体。所以请记住,要找到的对象应该是白色的,背景应该是黑色的。
a. findContours()Python中用法:
contours, hierarchy = cv.findContours( image, mode, method[, contours[, hierarchy[, offset]]] )
参数 | 描述 |
---|---|
src | 源,8 位单通道图像。非零像素被视为 1。零像素保持 0,因此图像被视为二值图。您可以使用 compare、inRange、threshold、adaptiveThreshold、Canny 和其他方法从灰度或彩色图像中创建二值图像。如果 mode 等于 RETR_CCOMP 或 RETR_FLOODFILL,则输入也可以是标签的 32 位整数图像 (CV_32SC1)。 |
contours | 检测到的轮廓。每个轮廓都存储为一个点向量(例如 std::vector<std::vectorcv::Point >)。 |
hierarchy | 可选的输出向量(例如 std::vectorcv::Vec4i),包含有关图像拓扑的信息。它的元素与轮廓的数量一样多。对于每个第 i 个轮廓轮廓[i],将元素hierarchy[i][0]、hierarchy[i][1]、hierarchy[i][2]和hierarchy[i][3]设置为0-基于相同层次级别的下一个和前一个轮廓的轮廓中的索引,分别是第一个子轮廓和父轮廓。如果轮廓 i 没有下一个、上一个、父级或嵌套轮廓,则hierarchy[i] 的相应元素将为负 |
dx | 导数x的阶数 |
dy | 导数y的阶数 |
mode | 轮廓检索模式,见RetrievalModes |
method | 轮廓近似方法,见ContourApproximationModes |
offset | 每个轮廓点移动的可选偏移量。如果轮廓是从图像 ROI 中提取的,然后应该在整个图像上下文中对其进行分析,这将非常有用。 |
findContours() 重载函数
python中用法:
contours, hierarchy = cv.findContours( image, mode, method[, contours[, hierarchy[, offset]]] )
这是一个重载的成员函数,为方便起见而提供。它与上述函数的不同之处仅在于它接受的参数。
b. drawContours()python中用法:
image = cv.drawContours( image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]] )
参数 | 描述 |
---|---|
image | 目标图像 |
contours | 所有输入轮廓。每个轮廓都存储为一个点向量。 |
contourIdx | 指示要绘制的轮廓的参数。如果为负,则绘制所有轮廓。 |
color | 轮廓的颜色。 |
thickness | 绘制轮廓线的厚度。如果它是负数(例如,thickness=FILLED),则绘制轮廓内部。 |
lineType | 线路连接。请参阅线型lineTypes |
hierarchy | 有关层次结构的可选信息。仅当您只想绘制一些轮廓时才需要它(请参阅 maxLevel )。 |
maxLevel | 绘制轮廓的最大级别。如果为 0,则仅绘制指定的轮廓。如果为 1,则该函数绘制等高线和所有嵌套的等高线。如果为 2,则函数绘制轮廓、所有嵌套轮廓、所有嵌套到嵌套轮廓,依此类推。仅当有可用的层次结构时才考虑此参数。 |
offset | 可选的轮廓移位参数。将所有绘制的轮廓移动指定的 o f f s e t = ( d x , d y ) offset=(dx,dy) offset=(dx,dy) 。 |
2. 绘制轮廓
- 绘制图像中的所有轮廓
cv.drawContours(img, contours, -1, (0,255,0), 3)
- 要绘制单个轮廓,比如第 4 个轮廓:
cv.drawContours(img, contours, 3, (0,255,0), 3)
- 但大多数时候,下面的方法会很有用:
cnt = contours[4]
cv.drawContours(img, [cnt], 0, (0,255,0), 3)
实例
img = cv.imread("../../file/photos/box.png")
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) #将图像转换为灰度图像
print(img.dtype)
ret, th = cv.threshold(gray, 55, 255, 0) # 对图像进行二值处理
#获取边缘轮廓列表
contours, hierarchy = cv.findContours(th, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
# cnt = contours[0]
# img = cv.cvtColor(img, cv.COLOR_GRAY2RGB)
img_contour = cv.drawContours(img, contours, -1, (0, 255, 0), 3) #在原图上画出轮廓
plt.subplot(121), plt.imshow(th, 'gray'), plt.title("Oringal")
plt.axis('off')#关闭坐标轴
plt.subplot(122), plt.imshow(img_contour, ), plt.title("img_contour")
plt.axis('off')
plt.show()
结果:
Contour Approximation Method
这是 cv.findContours 函数中的第三个参数。它实际上表示什么?
上面,我们说过轮廓是具有相同强度的形状的边界。它存储形状边界的 (x,y) 坐标。但它是否存储了所有坐标?这是由这种轮廓近似方法指定的。
如果传递 cv.CHAIN_APPROX_NONE,则存储所有边界点。但实际上我们需要所有的点吗?例如,您找到了一条直线的轮廓。你需要线上的所有点来代表那条线吗?不,我们只需要那条线的两个端点。这就是 cv.CHAIN_APPROX_SIMPLE 所做的。它去除所有冗余点并压缩轮廓,从而节省内存。
下面的矩形图像演示了这种技术。只需在轮廓数组中的所有坐标上绘制一个圆圈(以蓝色绘制)。第一张图显示了我用 cv.CHAIN_APPROX_NONE(734 点)得到的点,第二张图显示了用 cv.CHAIN_APPROX_SIMPLE 得到的点(只有 4 点)。看,它节省了多少内存!!!