一、图像轮廓
图像轮廓是指将边缘连接起来形成的一个整体,用于后续计算。
# 轮廓发现:基于阈值化
def contours_demo(image):
# dst = cv.GaussianBlur(image,(3,3),0)
# gray = cv.cvtColor(dst, cv.COLOR_RGB2GRAY)
# ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
# cv.imshow('binary image', binary)
binary = edge_demo(image)
# findContours:发现轮廓;
"""
findContours(image, mode, method, contours=None, hierarchy=None, offset=None)
image:8位单通道图像,所有非零值被处理为1,所有零值保持不变
mode:轮廓检索模式,如RETR_EXTERNAL只检测外轮廓
method:轮廓的近似方法,如CHAIN_APPROX_SIMPLE压缩水平方向、垂直方向、对角线方向的元素,只保留该方向上的终点坐标。
contours:检测到的轮廓。每条轮廓线存储为一个点的向量
hierarchy:可选输出向量(如std::vector),包含图像拓扑信息。它有和等高线的数量一样多
[1,-1,-1,-1]:1表示第一个轮廓;第2位-1表示前一个轮廓不存在;第3位-1表示不存在子轮廓;第4位-1表示不存在父轮廓
offset:每个轮廓点移动的可选偏移量。这是有用的从图像感兴趣区域提取轮廓线,然后在整个图像环境中进行分析。
"""
contours, heriachy = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
for i, contour in enumerate(contours):
# drawContours:绘制轮廓;contours:所有的轮廓;-1:像素,把轮廓全部填充
"""
drawContours(image, contours, contourIdx, color, thickness=None, lineType=None, hierarchy=None, maxLevel=None, offset=None)
image:待绘制轮廓的图像
contours:需要绘制的轮廓
contourIdx:需要绘制的边缘索引。一个整数或零表示绘制对应索引号的轮廓,负数表示绘制全部轮廓
color:绘制的颜色,用BGR格式表示
thickness:可选,表示绘制轮廓时所用画笔的粗细,-1表示绘制实心轮廓
lineType:可选,表示绘制轮廓时所用的线型
hierarchy:对应函数cv.findContours所输出的层次信息
maxLevel:控制所绘制轮廓层次的深度。非零整数表示绘制最高层及以下的相同数量层级的轮廓
offset:偏移参数,使轮廓偏移到不同位置展示出来
"""
cv.drawContours(image, contours, i, (0, 0, 255), 2)
print(i)
cv.imshow('detect_contours', image)
二、对象测量
1、moments函数
(1)cv.moments所返回的特征值能够用来比较两个轮廓是否相似。零阶矩“m00”表示一个轮廓的面积。
(2)中心矩通过减去均值获取平移不变性,从而忽略两个对象的位置关系,帮助比较不同位置上两个对象的一致性。
(3)归一化中心矩通过除以物体总尺寸获得缩放不变性,其提取的属性值具有平移不变性和缩放不变性,得以比较。
2、逼近多边形
原理:首先从轮廓中找到距离最远的两个点,将两点相连。接着,在轮廓上找到一个离当前直线最远的点,将该点与原有直线连成一个封闭的多边形,得到一个三角形。不断迭代,直到轮廓上所有的点到当前多边形的距离都小于函数approxPolyDP的epsilon的值。
# 轮廓分析:计算每个轮廓的弧长与面积,像素单位
def measure_object(image):
gray = cv.cvtColor(image, cv.COLOR_RGB2GRAY)
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
cv.imshow('binary image', binary)
dst = cv.cvtColor(binary, cv.COLOR_GRAY2RGB)
contours, hireachy = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
for i, contour in enumerate(contours):
# contourArea:计算轮廓的面积
area = cv.contourArea(contour)
print('contour area : %s'%area)
# boundingRect:绘制轮廓的矩形边界,返回矩形边界的左上角顶点坐标值及矩形边界的宽度和高度。
x, y, w, h = cv.boundingRect(contour)
# 宽、高比=宽度/高度
rate = min(w, h) / max(w, h)
print('rectangle rate : %s'% rate)
# moments:获取轮廓特征
mm = cv.moments(contour)
print(type(mm))
# 得到矩形中心位置
cx = mm['m10'] / mm['m00']
cy = mm['m01'] / mm['m00']
# 对每个轮廓在原来的位置上绘制,绘制中心点
cv.circle(image, (np.int_(cx), np.int_(cy)), 3, (0, 255, 255), -1)
# 对每个轮廓绘制外接矩形
cv.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2)
cv.imshow('rectangle image', image)
# approxPolyDP:获取轮廓的多边形拟合结果;epsilon4:越小越折线越逼近真实形状;closed:是否为闭合区域
"""
approxPolyDP(curve, epsilon, closed, approxCurve=None)
curve:轮廓
epsilon:精度,原始轮廓的边界点与逼近多边形边界之间的最大距离
closed:true表示多边形是封闭的,false表示多边形是不封闭的
approxCurve:逼近多边形的点集
"""
approxCurve = cv.approxPolyDP(contour, 4, True) # 4才能区别出三边形和四边形
print(approxCurve.shape)
# 轮廓拟合
if approxCurve.shape[0] == 4:
cv.drawContours(dst, contours, i, (0, 255, 0), 2)
if approxCurve.shape[0] == 7:
cv.drawContours(dst, contours, i, (0, 0, 255), 2)
if approxCurve.shape[0] == 11:
cv.drawContours(dst, contours, i, (0, 165, 255), 2)
if approxCurve.shape[0] == 15:
cv.drawContours(dst, contours, i, (128, 0, 128), 2)
if approxCurve.shape[0] == 16:
cv.drawContours(dst, contours, i, (255, 0, 0), 2)
if approxCurve.shape[0] == 21:
cv.drawContours(dst, contours, i, (128, 128, 0), 2)
cv.imshow('measure_object', dst) # dst:注意返回值
>>contour area : 7950.5
rectangle rate : 0.992
<class 'dict'>
(4, 1, 2)
contour area : 16218.5
rectangle rate : 0.5625
<class 'dict'>
(11, 1, 2)
contour area : 17186.5
rectangle rate : 1.0
<class 'dict'>
(7, 1, 2)
contour area : 19246.5
rectangle rate : 0.9875776397515528
<class 'dict'>
(21, 1, 2)
contour area : 18078.0
rectangle rate : 0.9934640522875817
<class 'dict'>
(15, 1, 2)
contour area : 20040.0
rectangle rate : 0.9937888198757764
<class 'dict'>
(16, 1, 2)