OpenCV 例程200篇 总目录-202206更新
在对实际图像进行轮廓查找时,得到的轮廓数量很多。获取轮廓后,通常基于轮廓的特征进行筛选、识别和处理。例如,基于轮廓的周长和面积对轮廓进行筛选,然后绘制筛选的目标轮廓或其最小外接矩形。
对象的宽高比(Aspect Ratio),是指对象垂直边界矩形的宽度与高度的比值,是对象/轮廓的重要特征。
通过对象/轮廓的垂直矩形边界框,可以计算宽高比:
A s p e c t R a t i o = W i d t h ( B o u n d i n g R e c t ) H e i g h t ( B o u n d i n g R e c t ) AspectRatio = \frac{Width_{(BoundingRect)}}{Height_{(BoundingRect)}} AspectRatio=Height(BoundingRect)Width(BoundingRect)
例程:
# 轮廓的宽高比 (Aspect Ratio)
x, y, wv, hv = cv2.boundingRect(cnt) # 轮廓外接垂直矩形的左上顶点 (x,y), 宽度 w, 高度 h
aspect_ratio = round(wv/hv, 2) # 轮廓外接垂直矩形的宽高比
注意:
对象的面积(Extent),是指对象面积与垂直边界矩形面积的比值。
通过轮廓面积和垂直边界矩形面积,可以计算面积比:
E x t e n t = A r e a ( O b j e c t ) A r e a ( B o u n d i n g R e c t ) Extent = \frac{Area_{(Object)}}{Area_{(BoundingRect)}} Extent=Area(BoundingRect)Area(Object)
例程:
# 轮廓的面积比 (Extent)
x, y, wv, hv = cv2.boundingRect(cnt) # 轮廓外接垂直矩形的左上顶点 (x,y), 宽度 w, 高度 h
rect_area = wv * hv # 轮廓外接垂直矩形的面积
cnt_area = cv2.contourArea(cnt) # 轮廓的面积
extent = round(cnt_area/rect_area, 2) # 轮廓的面积比
注意:
对象的坚实度(Solidity),是指对象面积与其凸包面积的比值。
通过轮廓面积和凸包面积,可以计算坚实度:
S o l i d i t y = A r e a ( O b j e c t ) A r e a ( C o n v e x H u l l ) Solidity = \frac{Area_{(Object)}}{Area_{(ConvexHull)}} Solidity=Area(ConvexHull)Area(Object)
例程:
# 轮廓的坚实度 (Solidity)
cnt_area = cv2.contourArea(cnt) # 轮廓的面积
hull = cv2.convexHull(cnt) # 轮廓的凸包,返回凸包顶点集
hull_area = cv2.contourArea(hull) # 凸包的面积
solidity = round(cnt_area/hull_area, 2) # 轮廓的坚实度
轮廓的等效直径(Equivalent diameter), 是指与轮廓面积相等的圆形的直径 。
通过轮廓面积,可以计算轮廓的等效直径:
D e q u = 4 ∗ A r e a ( O b j e c t ) / π D_{equ} = \sqrt{4 * Area_{(Object)} / {\pi}} Dequ=4∗Area(Object)/π
例程:
# 轮廓的等效直径 (Equivalent diameter)
cnt_area = cv2.contourArea(cnt) # 轮廓的面积
dEqu = round(np.sqrt(4*cnt_area/np.pi), 2) # 轮廓的等效直径
注意:
轮廓的方向(Orientation), 是指物体指向的角度。
通过函数 cv2.fitEllipse() 可以得到轮廓的最优拟合椭圆,并返回椭圆的中心点、轴长和旋转角度:
例程:
# 轮廓的方向 (Orientation)
ellipse = cv2.fitEllipse(cnt) # 椭圆中心点 (x,y), 长轴短轴长度 (a,b), 旋转角度 ang
angle = round(ellipse[2], 1) # 轮廓的方向, 指椭圆长轴 a 与水平方向的夹角
注意:
图像掩模(Mask)也常被写成“图像掩膜”,或称为掩码、掩像、模板、遮罩,有的资料中甚至译成“面具”。图像处理时,可以只对掩模区域进行处理,或者只对非掩模区域进行处理。
轮廓的掩模是轮廓区域的掩模图像,背景为黑色、轮廓区域为白色。
轮廓的掩模可以通过绘制轮廓 cv2.drawContours() 函数的内部填充功能(“CV_FILLED”)实现。
轮廓的像素点(Pixelpoints),是指轮廓区域内的所有像素点。
轮廓的像素点可以由轮廓掩模的非 0 筛选(切片)获得,如通过 Numpy 函数 np.nonzero() 或 OpenCV 函数 cv2.findNonZero() 实现。
例程:
# 轮廓的掩模和像素点 (Mask)
maskCnt = np.zeros(gray.shape, np.uint8) # 背景区域置为黑色
cv2.drawContours(maskCnt, [cnt], 0, 255, -1) # 轮廓区域置为白色
pixelsNP = np.transpose(np.nonzero(maskCnt)) # (15859, 2)
pixelsCV = cv2.findNonZero(maskCnt) # (15859, 1, 2)
注意:
轮廓中像素点的灰度值的最大值、最小值及其位置,可以由函数 **cv.minMaxLoc() ** 通过掩模图像获得。
函数说明:
cv.minMaxLoc(src[, mask]) → minVal, maxVal, minLoc, maxLoc
对于灰度图像或二维数组,查找最大值、最小值及其位置。
参数说明:
例程:
# 轮廓的掩模
maskCnt = np.zeros(gray.shape, np.uint8) # 背景区域置为黑色
cv2.drawContours(maskCnt, [cnt], 0, 255, -1) # 轮廓区域置为白色
# 轮廓的最大值/最小值及其位置
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(gray, mask=maskCnt) # 必须用灰度图像
print(min_val, max_val, min_loc, max_loc) # 0.0 255.0 (468, 291) (379, 357)
注意:
轮廓中像素点的颜色均值或灰度值均值,可以由函数 **cv.mean() ** 对掩模图像计算。
函数说明:
cv.mean(src[, mask]) → retval
对于单通道或多通道图像,计算整个图像或掩模区域的像素值的均值。
参数说明:
例程:
# 轮廓的掩模
maskCnt = np.zeros(gray.shape, np.uint8) # 背景区域置为黑色
cv2.drawContours(maskCnt, [cnt], 0, 255, -1) # 轮廓区域置为白色
# 轮廓的灰度均值和颜色均值
gray_mean = cv2.mean(gray, maskCnt) # (mg, 0.0, 0.0, 0.0)
img_mean = cv2.mean(img, maskCnt) # (mR, mG, mB, 0.0)
注意:
极点是指对象的最左侧、最右侧、最顶部、最底部的点。
查找轮廓的极端点,可以通过轮廓边界点集 contours[i],对所有边界点的横坐标、纵坐标分别查找最大值、最小值及其位置。
由轮廓上下左右的极端点位置,就可以得到轮廓的垂直矩形边界框的顶点坐标和宽度高度。
例程:
# 轮廓的极端点位置
leftmost = tuple(cnt[cnt[:, :, 0].argmin()][0]) # cnt[:,:,0], 所有边界点的横坐标
rightmost = tuple(cnt[cnt[:, :, 0].argmax()][0])
topmost = tuple(cnt[cnt[:, :, 1].argmin()][0]) # cnt[:,:,1], 所有边界点的纵坐标
bottommost = tuple(cnt[cnt[:, :, 1].argmax()][0])
# 12.6 轮廓的基本属性
img = cv2.imread("../images/seagull01.png", flags=1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度图像
# HSV 色彩空间图像分割
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 将图片转换到 HSV 色彩空间
lowerBlue, upperBlue = np.array([100, 43, 46]), np.array([124, 255, 255]) # 蓝色阈值
segment = cv2.inRange(hsv, lowerBlue, upperBlue) # 背景色彩图像分割
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) # (5, 5) 结构元
binary = cv2.dilate(cv2.bitwise_not(segment), kernel=kernel, iterations=3) # 图像膨胀
# 寻找二值化图中的轮廓
# binary, contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # OpenCV3
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # OpenCV4~
# 绘制全部轮廓,contourIdx=-1 绘制全部轮廓
imgCnts = img.copy()
imgCnts = cv2.drawContours(imgCnts, contours, -1, (255,255,255), -1) # 绘制全部轮廓, 内部填充
print("len(contours) =", len(contours)) # 所有轮廓的列表
cnt = contours[1]
# 轮廓的垂直矩形边界框
# boundingBoxes = [cv2.boundingRect(cnt) for cnt in contours] # 所有轮廓的外接垂直矩形
xv, yv, wv, hv = cv2.boundingRect(cnt) # 矩形左上顶点的坐标 x, y, 矩形宽度 w, 高度 h
print("Vertical rectangle: (x,y)={}, (w,h)={}".format((xv, yv), (wv, hv)))
cv2.rectangle(imgCnts, (xv,yv), (xv+wv,yv+hv), (0, 0, 255), 2) # 绘制垂直矩形边界框
# 轮廓的宽高比 (Aspect Ratio)
xv, yv, wv, hv = cv2.boundingRect(cnt) # 轮廓外接垂直矩形的左上顶点 (x,y), 宽度 w, 高度 h
aspect_ratio = round(wv/hv, 2) # 轮廓外接垂直矩形的宽高比
print("Vertical rectangle: w={}, h={}".format(wv,hv))
print("Aspect ratio:", aspect_ratio)
# 轮廓的面积比 (Extent)
xv, yv, wv, hv = cv2.boundingRect(cnt) # 轮廓外接垂直矩形的左上顶点 (x,y), 宽度 w, 高度 h
rect_area = wv * hv # 轮廓外接垂直矩形的面积
cnt_area = cv2.contourArea(cnt) # 轮廓的面积
extent = round(cnt_area/rect_area, 2) # 轮廓的面积比
print("Area of cnt:", cnt_area)
print("Area of VertRect:", rect_area)
print("Extent(area ratio):", extent)
# 轮廓的坚实度 (Solidity)
cnt_area = cv2.contourArea(cnt) # 轮廓的面积
hull = cv2.convexHull(cnt) # 轮廓的凸包,返回凸包顶点集
hull_area = cv2.contourArea(hull) # 凸包的面积
solidity = round(cnt_area/hull_area, 2) # 轮廓的坚实度
print("Area of cnt:", cnt_area)
print("Area of convex hull:", hull_area)
print("Solidity(area ratio):", solidity)
# 轮廓的等效直径 (Equivalent diameter)
cnt_area = cv2.contourArea(cnt) # 轮廓的面积
dEqu = round(np.sqrt(4*cnt_area/np.pi), 2) # 轮廓的等效直径
print("Area of cnt:", cnt_area)
print("Equivalent diameter:", dEqu)
# 轮廓的方向 (Orientation)
ellipse = cv2.fitEllipse(cnt) # 椭圆中心点 (x,y), 长轴短轴长度 (a,b), 旋转角度 ang
angle = round(ellipse[2], 1) # 轮廓的方向, 指椭圆长轴与水平方向的夹角
print("Orientation of cnt: {}".format(angle))
# 轮廓的掩模和像素点 (Mask)
maskCnt = np.zeros(gray.shape, np.uint8) # 背景区域置为黑色
cv2.drawContours(maskCnt, [cnt], 0, 255, -1) # 轮廓区域置为白色
pixelsNP = np.transpose(np.nonzero(maskCnt)) # (15859, 2): (y, x)
pixelsCV = cv2.findNonZero(maskCnt) # (15859, 1, 2): (x, y)
print("pixelsNP: {}, pixelsCV: {}".format(pixelsNP.shape, pixelsCV.shape))
# 轮廓的最大值/最小值及其位置
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(gray, mask=maskCnt) # 必须用灰度图像
print("Minimum value is {} at Pos{}".format(min_val, min_loc))
print("Maximum value is {} at Pos{}".format(max_val, max_loc))
# 轮廓的灰度均值和颜色均值
gray_mean = cv2.mean(gray, maskCnt) # (mg, 0, 0, 0)
img_mean = cv2.mean(img, maskCnt) # (mR, mG, mB, 0)
print("gray_mean: {:.1f} \nimg_mean: ({:.1f}, {:.1f}, {:.1f})"
.format(gray_mean[0], img_mean[0], img_mean[1], img_mean[2]))
# 轮廓的极端点位置
leftmost = tuple(cnt[cnt[:, :, 0].argmin()][0]) # cnt[:,:,0], 所有边界点的横坐标
rightmost = tuple(cnt[cnt[:, :, 0].argmax()][0])
topmost = tuple(cnt[cnt[:, :, 1].argmin()][0]) # cnt[:,:,1], 所有边界点的纵坐标
bottommost = tuple(cnt[cnt[:, :, 1].argmax()][0])
print("Left most is {} at Pos{}".format(leftmost[0], leftmost))
print("Right most is {} at Pos{}".format(rightmost[0], rightmost))
print("Top most is {} at Pos{}".format(topmost[1], topmost))
print("Bottom most is {} at Pos{}".format(bottommost[1], bottommost))
for point in [leftmost, rightmost, topmost, bottommost]:
cv2.circle(imgCnts, point, 5, (0, 0, 255), -1) # 在轮廓的极端点上绘制圆点
plt.figure(figsize=(9, 6))
plt.subplot(131), plt.axis('off'), plt.title("Origin")
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.subplot(132), plt.axis('off'), plt.title("Tree contour")
plt.imshow(cv2.cvtColor(imgCnts, cv2.COLOR_BGR2RGB))
plt.subplot(133), plt.axis('off'), plt.title("Contour mask")
plt.imshow(cv2.cvtColor(maskCnt, cv2.COLOR_BGR2RGB))
plt.tight_layout()
plt.show()
运行结果:
len(contours) = 6
Vertical rectangle: (x,y)=(168, 173), (w,h)=(132, 156)
Vertical rectangle: w=132, h=156
Aspect ratio: 0.85
Area of cnt: 9302.5
Area of VertRect: 20592
Extent(area ratio): 0.45
Area of cnt: 9302.5
Area of convex hull: 12546.5
Solidity(area ratio): 0.74
Area of cnt: 9302.5
Equivalent diameter: 108.83
Orientation of cnt: 155.4
pixelsNP: (9551, 2), pixelsCV: (9551, 1, 2)
Minimum value is 0.0 at Pos(204, 179)
Maximum value is 255.0 at Pos(233, 220)
gray_mean: 111.2
img_mean: (127.8, 113.6, 100.1)
Left most is 168 at Pos(168, 256)
Right most is 299 at Pos(299, 322)
Top most is 173 at Pos(203, 173)
Bottom most is 328 at Pos(288, 328)
(本节完)
版权声明:
youcans@xupt 原创作品,转载必须标注原文链接:(https://blog.csdn.net/youcans/article/details/125112262)
Copyright 2022 youcans, XUPT
Crated:2022-5-28
欢迎关注 『youcans 的 OpenCV 例程 200 篇』 系列,持续更新中
欢迎关注 『youcans 的 OpenCV学习课』 系列,持续更新中
更多内容,请见:
【OpenCV 例程200篇 总目录-202206更新】
194.寻找图像轮廓(cv.findContours)
195.绘制图像轮廓(cv.drawContours)
196.图像的矩和不变矩(cv.moments)
197.轮廓的基本特征
198.基于不变矩的形状相似性检测
199.轮廓的外接边界框
200.轮廓的基本属性