1 几何图形检测介绍
1.1 轮廓(contours)
什么是轮廓,简单说轮廓就是一些列点相连组成形状、它们拥有同样的颜色、轮廓发现在图像的对象分析、对象检测等方面是非常有用的工具,在OpenCV
中使用轮廓发现相关函数时候要求输入图像是二值图像,这样便于轮廓提取、边缘提取等操作。轮廓发现的函数与参数解释如下:
函数原型:
findContours(image, mode, method, contours=None, hierarchy=None, offset=None)
参数:
image输入/输出的二值图像
mode 迒回轮廓的结构、可以是List、Tree、External
method 轮廓点的编码方式,基本是基于链式编码
contours 迒回的轮廓集合
hieracrchy 迒回的轮廓层次关系
offset 点是否有位移
1.2
多边形逼近
多边形逼近,是通过对轮廓外形无限逼近,删除非关键点、得到轮廓的关键点,不断逼近轮廓真实形状的方法,OpenCV中多边形逼近的函数与参数解释如下:
函数原型:
approxPolyDP(curve, epsilon, closed, approxCurve=None)
参数:
curve 表示输入的轮廓点集合
epsilon 表示逼近曲率,越小表示相似逼近越厉害
close 是否闭合
1.3
几何距计算
图像几何距是图像的几何特征,高阶几何距中心化之后具有特征不变性,可以产生Hu距输出,用于形状匹配等操作,这里我们通过计算一阶几何距得到指定轮廓的中心位置,计算几何距的函数与参数解释如下:
函数原型:
moments(array, binaryImage=None)
参数:
array表示指定输入轮廓
binaryImage默认为None
2 基于opencv实现几何图形检测
整个代码实现分为如下几步完成
- 加载图像,
- 图像二值化
- 轮廓发现
- 几何形状识别
- 测量周长、面积、计算中心
- 颜色提取
完整代码:
import cv2 as cv
import numpy as np
class ShapeAnalysis:
def __init__(self):
self.shapes = {'triangle': 0, 'rectangle': 0, 'polygons': 0, 'circles': 0}
def analysis(self, frame):
h, w, ch = frame.shape
result = np.zeros((h, w, ch), dtype=np.uint8)
# 二值化图像
print("start to detect lines...\n")
gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU)
cv.imshow("input image", frame)
contours, hierarchy = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
for cnt in range(len(contours)):
# 提取与绘制轮廓
cv.drawContours(result, contours, cnt, (0, 255, 0), 2)
# 轮廓逼近
epsilon = 0.01 * cv.arcLength(contours[cnt], True)
approx = cv.approxPolyDP(contours[cnt], epsilon, True)
# 分析几何形状
corners = len(approx)
shape_type = ""
if corners < 3:
continue
if corners == 3:
count = self.shapes['triangle']
count = count + 1
self.shapes['triangle'] = count
shape_type = "三角形"
if corners == 4:
count = self.shapes['rectangle']
count = count + 1
self.shapes['rectangle'] = count
shape_type = "矩形"
if corners >= 10:
count = self.shapes['circles']
count = count + 1
self.shapes['circles'] = count
shape_type = "圆形"
if 4 < corners < 10:
count = self.shapes['polygons']
count = count + 1
self.shapes['polygons'] = count
shape_type = "多边形"
# 求解中心位置
mm = cv.moments(contours[cnt])
if mm['m00'] == 0:
continue
cx = int(mm['m10'] / mm['m00'])
cy = int(mm['m01'] / mm['m00'])
cv.circle(result, (cx, cy), 3, (0, 0, 255), -1)
# 颜色分析
color = frame[cy][cx]
color_str = "(" + str(color[0]) + ", " + str(color[1]) + ", " + str(color[2]) + ")"
# 计算面积与周长
p = cv.arcLength(contours[cnt], True)
area = cv.contourArea(contours[cnt])
print("周长: %.3f, 面积: %.3f 颜色: %s 形状: %s " % (p, area, color_str, shape_type))
cv.imshow("Analysis Result", result)
print("triangle: ", self.shapes['triangle'])
print("rectangle: ", self.shapes['rectangle'])
print("polygons: ", self.shapes['polygons'])
print("circles: ", self.shapes['circles'])
return self.shapes
if __name__ == "__main__":
src = cv.imread("./data/jihe2.png")
ld = ShapeAnalysis()
ld.analysis(src)
cv.waitKey(0)
cv.destroyAllWindows()
原始图像:
运行结果显示:
周长: 553.973, 面积: 11687.500 颜色: (190, 146, 112) 形状: 矩形
周长: 479.556, 面积: 14743.500 颜色: (232, 162, 0) 形状: 矩形
周长: 543.144, 面积: 18333.500 颜色: (21, 0, 136) 形状: 多边形
周长: 341.421, 面积: 7593.000 颜色: (164, 73, 163) 形状: 多边形
周长: 761.796, 面积: 13221.500 颜色: (87, 122, 185) 形状: 圆形
周长: 594.257, 面积: 24703.500 颜色: (39, 127, 255) 形状: 圆形
周长: 592.000, 面积: 14508.000 颜色: (36, 28, 237) 形状: 矩形
周长: 432.291, 面积: 11939.500 颜色: (204, 72, 63) 形状: 多边形
周长: 280.451, 面积: 4917.000 颜色: (190, 146, 112) 形状: 多边形
周长: 531.588, 面积: 10587.000 颜色: (164, 73, 163) 形状: 矩形
周长: 405.262, 面积: 4172.500 颜色: (76, 177, 34) 形状: 圆形
周长: 370.191, 面积: 9746.000 颜色: (232, 162, 0) 形状: 圆形
周长: 486.000, 面积: 14762.000 颜色: (232, 162, 0) 形状: 矩形
triangle: 0
rectangle: 5
polygons: 4
circles: 4