前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >14: 轮廓特征

14: 轮廓特征

作者头像
CodecWang
发布2021-12-07 15:14:25
8460
发布2021-12-07 15:14:25
举报
文章被收录于专栏:CodecWangCodecWang

学习计算轮廓特征,如面积、周长、最小外接矩形等。图片等可到文末引用处下载。

目标

  • 计算物体的周长、面积、质心、最小外接矩形等
  • OpenCV函数:cv2.contourArea(), cv2.arcLength(), cv2.approxPolyDP()

教程

在计算轮廓特征之前,我们先用上一节的代码把轮廓找到:

代码语言:javascript
复制
import cv2
import numpy as np

img = cv2.imread('handwriting.jpg', 0)
_, thresh = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
image, contours, hierarchy = cv2.findContours(thresh, 3, 2)

# 以数字3的轮廓为例
cnt = contours[0]Copy to clipboardErrorCopied

为了便于绘制,我们创建出两幅彩色图,并把轮廓画在第一幅图上:

代码语言:javascript
复制
img_color1 = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
img_color2 = np.copy(img_color1)
cv2.drawContours(img_color1, [cnt], 0, (0, 0, 255), 2)Copy to clipboardErrorCopied

轮廓面积

代码语言:javascript
复制
area = cv2.contourArea(cnt)  # 4386.5Copy to clipboardErrorCopied

注意轮廓特征计算的结果并不等同于像素点的个数,而是根据几何方法算出来的,所以有小数。

如果统计二值图中像素点个数,应尽量避免循环,**可以使用cv2.countNonZero()**,更加高效。

轮廓周长

代码语言:javascript
复制
perimeter = cv2.arcLength(cnt, True)  # 585.7Copy to clipboardErrorCopied

参数2表示轮廓是否封闭,显然我们的轮廓是封闭的,所以是True。

图像矩

矩可以理解为图像的各类几何特征,详情请参考:[Image Moments]

代码语言:javascript
复制
M = cv2.moments(cnt)Copy to clipboardErrorCopied

M中包含了很多轮廓的特征信息,比如M['m00']表示轮廓面积,与前面cv2.contourArea()计算结果是一样的。质心也可以用它来算:

代码语言:javascript
复制
cx, cy = M['m10'] / M['m00'], M['m01'] / M['m00']  # (205, 281)Copy to clipboardErrorCopied

外接矩形

形状的外接矩形有两种,如下图,绿色的叫外接矩形,表示不考虑旋转并且能包含整个轮廓的矩形。蓝色的叫最小外接矩,考虑了旋转:

代码语言:javascript
复制
x, y, w, h = cv2.boundingRect(cnt)  # 外接矩形
cv2.rectangle(img_color1, (x, y), (x + w, y + h), (0, 255, 0), 2)Copy to clipboardErrorCopied
代码语言:javascript
复制
rect = cv2.minAreaRect(cnt)  # 最小外接矩形
box = np.int0(cv2.boxPoints(rect))  # 矩形的四个角点取整
cv2.drawContours(img_color1, [box], 0, (255, 0, 0), 2)Copy to clipboardErrorCopied

其中np.int0(x)是把x取整的操作,比如377.93就会变成377,也可以用x.astype(np.int)。

最小外接圆

外接圆跟外接矩形一样,找到一个能包围物体的最小圆:

代码语言:javascript
复制
(x, y), radius = cv2.minEnclosingCircle(cnt)
(x, y, radius) = np.int0((x, y, radius))  # 圆心和半径取整
cv2.circle(img_color2, (x, y), radius, (0, 0, 255), 2)Copy to clipboardErrorCopied

拟合椭圆

我们可以用得到的轮廓拟合出一个椭圆:

代码语言:javascript
复制
ellipse = cv2.fitEllipse(cnt)
cv2.ellipse(img_color2, ellipse, (255, 255, 0), 2)Copy to clipboardErrorCopied

形状匹配

cv2.matchShapes()可以检测两个形状之间的相似度,返回值越小,越相似。先读入下面这张图片:

代码语言:javascript
复制
img = cv2.imread('shapes.jpg', 0)
_, thresh = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
image, contours, hierarchy = cv2.findContours(thresh, 3, 2)
img_color = cv2.cvtColor(thresh, cv2.COLOR_GRAY2BGR)  # 用于绘制的彩色图Copy to clipboardErrorCopied

图中有3条轮廓,我们用A/B/C表示:

代码语言:javascript
复制
cnt_a, cnt_b, cnt_c = contours[0], contours[1], contours[2]
print(cv2.matchShapes(cnt_b, cnt_b, 1, 0.0))  # 0.0
print(cv2.matchShapes(cnt_b, cnt_c, 1, 0.0))  # 2.17e-05
print(cv2.matchShapes(cnt_b, cnt_a, 1, 0.0))  # 0.418Copy to clipboardErrorCopied

可以看到BC相似程度比AB高很多,并且图形的旋转或缩放并没有影响。其中,参数3是匹配方法,详情可参考:ShapeMatchModes,参数4是OpenCV的预留参数,暂时没有实现,可以不用理会。

形状匹配是通过图像的Hu矩来实现的(cv2.HuMoments()),大家如果感兴趣,可以参考:Hu-Moments

练习

  1. 前面我们是对图片中的数字3进行轮廓特征计算的,大家换成数字1看看。
  2. (选做)用形状匹配比较两个字母或数字(这相当于很简单的一个OCR噢)。

小结

常用的轮廓特征:

  • cv2.contourArea()算面积,cv2.arcLength()算周长,cv2.boundingRect()算外接矩。
  • cv2.minAreaRect()算最小外接矩,cv2.minEnclosingCircle()算最小外接圆。
  • cv2.matchShapes()进行形状匹配。

接口文档

引用

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-12-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目标
  • 教程
    • 轮廓面积
      • 轮廓周长
        • 图像矩
          • 外接矩形
            • 最小外接圆
              • 拟合椭圆
                • 形状匹配
                • 练习
                • 小结
                • 接口文档
                • 引用
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档