文章目录
根据 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.findContours
、cv2.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)