【python】OpenCV—findContours

在这里插入图片描述



根据 mask 标签(跟原图一样大小的二值图),1)把 mask(cv2.addWeighted) 画在原图上,2)把 mask 轮廓(cv2.findContours)画在原图上

【1】 cv2.addWeighted

在这里插入图片描述
来自 Python-OpenCV 图像叠加or图像混合加权(cv2.addWeighted)

import numpy as np

img1 = cv2.imread("/home/Downloads/cat.jpg")
img2 = cv2.imread("/home/Downloads/1.bmp") # 颜色通道顺序为 BGR

img2[:,:,0] *= 0 # 蓝色通道为0
img2[:,:,1] *= 0 # 绿色通道为0
img2[:,:,2] *= 1 # 保留红色通道

imgadd = cv2.addWeighted(img1,1,img2,0.5,0)  # 原图权重1,mask 权重 0.5(调透明度)

cv2.imshow("image",imgadd)
cv2.waitKey(3000)

1.bmp
在这里插入图片描述
cat.jpg
在这里插入图片描述
效果图如下
在这里插入图片描述

【2】二值化获取边缘轮廓

cv2.threshold 是 OpenCV 库中的一个重要函数,用于对图像进行阈值化处理。阈值化处理是数字图像处理中常用的一种方法,可以将图像转换为二值图像,即图像中每个像素只有黑白两种颜色

具体介绍以及用法可以参考 【python】OpenCV—Tracking(10.2)

案例一

import cv2
import numpy as np

img1 = cv2.imread("/home/yanmeng/Downloads/cat.jpg")
img2 = cv2.imread("/home/yanmeng/Downloads/1.bmp") # BGR

gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) # 彩色图变灰度图
_,binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) # 灰度图变二值图

_, contours, _ = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 根据二值图找轮廓

cv2.drawContours(img1,contours,-1,(0,0,255),3) # 把轮廓画在原图上(0,0,255) 表示 RGB 三通道,红色

cv2.imshow("image",img1) # 显示原图
cv2.waitKey(3000)

注意 cv2.findContours 的返回值个数可能因 opencv 版本不同而不一样,我的版本是三个返回值,有的版本是两个

效果图如下
在这里插入图片描述
我们把其他的 mask 的轮廓也都画在原图上,有 1.bmp~4.bmp

import cv2

def bgr2binary(image): # 彩色转二值
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    _, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
    return binary

binarys = []

img = cv2.imread("/home/yanmeng/Downloads/cat.jpg") # 原图

for i in range(1,5): # 遍历 mask 图片
    mask_path = "/home/yanmeng/Downloads/{}.bmp".format(str(i))
    mask = cv2.imread(mask_path)
    binarys.append(bgr2binary(mask)) # 读 mask 并转换为二值图

for binary_mask in binarys: # 遍历二值化后的 mask
    _, contours, _ = cv2.findContours(binary_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 找轮廓,并用红色标记出来
    cv2.drawContours(img,contours,-1,(0,0,255),3) # 把轮廓画在原图上

cv2.imshow("image",img) # 显示原图
cv2.waitKey(3000)

mask 具体形式如下所示

2.bmp
在这里插入图片描述3.bmp
在这里插入图片描述4.bmp
在这里插入图片描述
最终效果图

在这里插入图片描述

案例二

import cv2
image = cv2.imread('1.jpg')

img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

ret, thresh = cv2.threshold(img_gray, 230, 255, cv2.THRESH_BINARY)

thresh = cv2.bitwise_not(thresh)

cv2.imshow('Binary image', thresh)
cv2.waitKey(0)
cv2.imwrite('image_thres1.jpg', thresh)
cv2.destroyAllWindows()

cv2.bitwise_not 的详细介绍可以参考 【python】OpenCV—Paste Mask

输入图像

在这里插入图片描述
输出的二值化图片结果

取反前
在这里插入图片描述

取反后

在这里插入图片描述

我们绘制轮廓边缘,涉及到 cv2.findContourscv2.drawContours

cv2.findContours 的介绍可以参考 【python】OpenCV—Tracking(10.2)

import cv2
import numpy as np
np.set_printoptions(threshold=np.inf)

image = cv2.imread('1.jpg')

img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

ret, thresh = cv2.threshold(img_gray, 230, 255, cv2.THRESH_BINARY)
thresh = cv2.bitwise_not(thresh)


contours, hierarchy = cv2.findContours(image=thresh, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE)
for i in range(len(contours)):
    print(len(contours[i]))
    print(contours[i])
"""
2
944
850
930
1024
4
901
4
"""
print(hierarchy)
"""
[[[ 1 -1 -1 -1]
  [ 2  0 -1 -1]
  [ 3  1 -1 -1]
  [ 4  2 -1 -1]
  [ 6  3  5 -1]
  [-1 -1 -1  4]
  [-1  4  7 -1]
  [-1 -1 -1  6]]]
"""

image_copy = image.copy()
cv2.drawContours(image=image_copy, contours=contours, contourIdx=-1, color=(0, 255, 0), thickness=2,
                 lineType=cv2.LINE_AA)

cv2.imshow('Binary image', thresh)
cv2.waitKey(0)
cv2.imshow('None approximation', image_copy)
cv2.waitKey(0)
cv2.destroyAllWindows()

输出结果
在这里插入图片描述

【3】轮廓的层次结构

参考学习来自 OpenCV基础(13)基于OpenCV的轮廓检测

cv2.RETR_LIST:返回所有轮廓,不建立轮廓间的层级关系。

cv2.RETR_EXTERNAL:只返回最外层的轮廓。

cv2.RETR_CCOMP:返回所有轮廓,并重建嵌套轮廓的完整层次结构(二级)。

cv2.RETR_TREE:返回所有轮廓,并建立完整的层级关系。

轮廓层次结构表示为数组,数组又包含四个值。它表示为:[Next, Previous, First_Child, Parent]

输入图片
在这里插入图片描述

RETR_LIST

import cv2

image1 = cv2.imread('./simple.png')
img_gray1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
ret, thresh1 = cv2.threshold(img_gray1, 150, 255, cv2.THRESH_BINARY)
contours1, hierarchy1 = cv2.findContours(thresh1, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
image_copy1 = image1.copy()

colors = [[0, 0, 255],  # 红
          [0, 125, 255], # 橙
          [0, 255, 255], # 黄
          [0, 255, 0], # 绿
          [255, 0, 0],] # 蓝

for index, contour in enumerate(contours1):
    print(hierarchy1[0][index])
    cv2.drawContours(image_copy1, contour, -1, colors[index], 2, cv2.LINE_AA)

cv2.imshow('LIST', image_copy1)
cv2.waitKey(0)
#cv2.imwrite('contours_retr_list.jpg', image_copy1)
cv2.destroyAllWindows()

RETR_LIST轮廓检索方法不会在提取的轮廓之间创建任何父-子关系。因此,对于所有检测到的轮廓区域,First_Child和Parent索引位置值始终为-1。

在这里插入图片描述

[ 1 -1 -1 -1]
[ 2  0 -1 -1]
[ 3  1 -1 -1]
[ 4  2 -1 -1]
[-1  3 -1 -1]

RETR_EXTERNAL

import cv2

image1 = cv2.imread('./simple.png')
img_gray1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
ret, thresh1 = cv2.threshold(img_gray1, 150, 255, cv2.THRESH_BINARY)
contours1, hierarchy1 = cv2.findContours(thresh1, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
image_copy1 = image1.copy()

colors = [[0, 0, 255],
          [0, 125, 255],
          [0, 255, 255],
          [0, 255, 0],
          [255, 0, 0],]

for index, contour in enumerate(contours1):
    print(hierarchy1[0][index])
    cv2.drawContours(image_copy1, contour, -1, colors[index], 2, cv2.LINE_AA)

cv2.imshow('LIST', image_copy1)
cv2.waitKey(0)
cv2.imwrite('contours_retr_list.jpg', image_copy1)
cv2.destroyAllWindows()

RETR_EXTERNAL轮廓检索方法是一种非常有趣的方法。它只检测父轮廓,而忽略任何子轮廓。

在这里插入图片描述

[ 1 -1 -1 -1]
[ 2  0 -1 -1]
[-1  1 -1 -1]

RETR_CCOMP

import cv2

image1 = cv2.imread('./simple.png')
img_gray1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
ret, thresh1 = cv2.threshold(img_gray1, 150, 255, cv2.THRESH_BINARY)
contours1, hierarchy1 = cv2.findContours(thresh1, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
image_copy1 = image1.copy()

colors = [[0, 0, 255],
          [0, 125, 255],
          [0, 255, 255],
          [0, 255, 0],
          [255, 0, 0],]

for index, contour in enumerate(contours1):
    print(hierarchy1[0][index])
    cv2.drawContours(image_copy1, contour, -1, colors[index], 2, cv2.LINE_AA)

cv2.imshow('LIST', image_copy1)
cv2.waitKey(0)
cv2.imwrite('contours_retr_list.jpg', image_copy1)
cv2.destroyAllWindows()

RETR_EXTERNAL不同,RETR_CCOMP检索图像中的所有轮廓。除此之外,它还对图像中的所有形状或对象应用2级层次结构。

所有的外部轮廓将有层次1级

所有的内部轮廓将有层次2级

在这里插入图片描述

[ 1 -1 -1 -1]
[ 3  0  2 -1]
[-1 -1 -1  1]
[ 4  1 -1 -1]
[-1  3 -1 -1]

层级结构如下

在这里插入图片描述

RETR_TREE

import cv2

image1 = cv2.imread('./simple.png')
img_gray1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
ret, thresh1 = cv2.threshold(img_gray1, 150, 255, cv2.THRESH_BINARY)
contours1, hierarchy1 = cv2.findContours(thresh1, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
image_copy1 = image1.copy()

colors = [[0, 0, 255],
          [0, 125, 255],
          [0, 255, 255],
          [0, 255, 0],
          [255, 0, 0],]

for index, contour in enumerate(contours1):
    print(hierarchy1[0][index])
    cv2.drawContours(image_copy1, contour, -1, colors[index], 2, cv2.LINE_AA)

cv2.imshow('LIST', image_copy1)
cv2.waitKey(0)
cv2.imwrite('contours_retr_list.jpg', image_copy1)
cv2.destroyAllWindows()

就像RETR_CCOMP一样,RETR_TREE也检索所有的轮廓。它还创建了一个完整的层次结构,级别不限于1或2。每个轮廓可以有自己的层次结构,与它所处的级别一致,以及它所具有的相应的父子关系。

在这里插入图片描述

[ 3 -1  1 -1]
[-1 -1  2  0]
[-1 -1 -1  1]
[ 4  0 -1 -1]
[-1  3 -1 -1]

层次结构如下

在这里插入图片描述

不同轮廓检索方法的运行时比较

仅仅知道轮廓检索方法是不够的。您还应该了解它们的相对处理时间。下表比较了上面讨论的每个方法的运行时。

比较了不同方法的推理速度

在这里插入图片描述

从上表中可以得出一些有趣的结论:

RETR_LIST和RETR_EXTERNAL的执行时间最少,因为RETR_LIST没有定义任何层次结构,而RETR_EXTERNAL只检索父轮廓

RETR_CCOMP的执行时间是第二快的。它检索所有轮廓并定义一个两级层次结构。

RETR_TREE执行时间最长,因为它检索了所有的轮廓,并为每个父子关系定义了独立的层次结构级别。

虽然上述时间可能看起来不重要,但重要的是要意识到可能需要大量轮廓处理的应用程序的差异。同样值得注意的是,这个处理时间可能会有所不同,这取决于它们提取的轮廓的程度以及它们定义的层次级别。

【4】附录——应用

cv2.moments——图矩阵,可以提取出质心等

在这里插入图片描述
cv2.arcLength——轮廓面积

cv2.approxPolyDP——轮廓近似

cv2.convexHull——用于计算点集的凸包。凸包是一个最小的凸多边形,它能够包含点集中的所有点。这个函数在图像处理、计算机视觉和形状分析中非常有用,特别是在需要识别或分析物体轮廓时。

在这里插入图片描述

cv2.isContourConvex——检查曲线是否是凸的

cv2.minEnclosingCircle——最小闭合圆

在这里插入图片描述

cv2.fitEllipse——拟合椭圆

在这里插入图片描述

cv2.fitLine——拟合直线

在这里插入图片描述

参考学习来自 基于OpenCV的轮廓检测(1)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值