前言
在计算轮廓时,可能并不需要实际的轮廓,而仅需要一个接近于轮廓的近似多边形。OpenCV提供了多种计算轮廓近似多边形的方法。
矩形包围框
函数cv2.boundingRect()能够绘制轮廓的矩形边界。该函数的语法格式为:
retval = cv2.boundingRect( array )
式中:
- ● 返回值retval表示返回的矩形边界的左上角顶点的坐标值及矩形边界的宽度和高度。
- ● 参数array是灰度图像或轮廓。该函数还可以是具有4个返回值的形式:
该函数还可以是具有4个返回值的形式:
x, y, w, h = cv2.boundingRect( array )
这里的4个返回值分别表示:
- 矩形边界左上角顶点的x坐标。
- 矩形边界左上角顶点的y坐标。
- 矩形边界的x方向的长度。
- 矩形边界的y方向的长度。
【例1】设计程序,显示函数cv2.boundingRect()不同形式的返回值。根据题目的要求,编写代码如下:
import cv2
#---------------读取并显示原始图像------------------
o = cv2.imread('cc.bmp')
#---------------提取图像轮廓------------------
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255, cv2.THRESH_BINARY)
image, contours, hierarchy = cv2.findContours(binary,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
#---------------返回顶点及边长------------------
x, y, w, h = cv2.boundingRect(contours[0])
print("顶点及长宽的点形式:")
print("x=", x)
print("y=", y)
print("w=", w)
print("h=", h)
#---------------仅有一个返回值的情况------------------
rect = cv2.boundingRect(contours[0])
print("\n顶点及长宽的元组(tuple)形式:")
print("rect=", rect)
【例2】使用函数cv2.drawContours()绘制矩形包围框。根据题目的要求,编写代码如下:
# import cv2
# #---------------读取并显示原始图像------------------
# o = cv2.imread('aa1.jpeg')
# #---------------提取图像轮廓------------------
# gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
# ret, binary = cv2.threshold(gray,127,255, cv2.THRESH_BINARY)
# # image, contours, hierarchy = cv2.findContours(binary,
# # cv2.RETR_LIST,
# # cv2.CHAIN_APPROX_SIMPLE)
# contours, hierarchy = cv2.findContours(binary,
# cv2.RETR_LIST,
# cv2.CHAIN_APPROX_SIMPLE)
# #---------------返回顶点及边长------------------
# x, y, w, h = cv2.boundingRect(contours[0])
# print("顶点及长宽的点形式:")
# print("x=", x)
# print("y=", y)
# print("w=", w)
# print("h=", h)
# #---------------仅有一个返回值的情况------------------
# rect = cv2.boundingRect(contours[0])
# print("\n顶点及长宽的元组(tuple)形式:")
# print("rect=", rect)
import cv2
import numpy as np
# ---------------读取并显示原始图像------------------
o = cv2.imread('C:\\Users\\Administrator\\Desktop\\caonima.png')
cv2.imshow("original", o)
# ---------------提取图像轮廓------------------
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
# ---------------构造矩形边界------------------
x, y, w, h = cv2.boundingRect(contours[0])
brcnt = np.array([[[x, y]], [[x + w, y]], [[x + w, y + h]], [[x, y + h]]])
cv2.drawContours(o, [brcnt], -1, (255, 255, 255), 2)
# ---------------显示矩形边界------------------
cv2.imshow("result", o)
cv2.waitKey()
cv2.destroyAllWindows()
我试了很多图像 只有这张找到了轮廓包围框
- 左图是原始图像o。
- 右图是在原始图像外绘制矩形包围框后的结果。
【例3】使用函数cv2.boundingRect()及cv2.rectangle()绘制矩形包围框。函数cv2.rectangle()可以用来绘制矩形。
根据题目的要求,编写代码如下:
import cv2
o = cv2.imread('C:\\Users\\Administrator\\Desktop\\caonima.png')
cv2.imshow("original", o)
# ---------------提取图像轮廓------------------
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
# ---------------构造矩形边界------------------
x, y, w, h = cv2.boundingRect(contours[0])
cv2.rectangle(o, (x, y), (x + w, y + h), (255, 255, 255), 2)
# ---------------显示矩形边界------------------
cv2.imshow("result", o)
cv2.waitKey()
cv2.destroyAllWindows()
最小包围矩形框
函数cv2.minAreaRect()能够绘制轮廓的最小包围矩形框,其语法格式为:
retval =cv2.minAreaRect( points )
式中:
- 返回值retval表示返回的矩形特征信息。
- 该值的结构是(最小外接矩形的中心(x, y), (宽度,高度),旋转角度)。
- 参数points是轮廓。
需要注意,返回值retval的结构不符合函数cv2.drawContours()的参数结构要求。因此,必须将其转换为符合要求的结构,才能使用。函数cv2.boxPoints()能够将上述返回值retval转换为符合要求的结构。函数cv2.boxPoints()的语法格式是:
points = cv2.boxPoints( box )
式中:
- 返回值points,是能够用于函数cv2.drawContours()参数的轮廓点。
- 参数box是函数cv2.minAreaRect()返回值的类型的值。
【例4】使用函数cv2.minAreaRect()计算图像的最小包围矩形框。根据题目的要求,编写代码如下:
import cv2
import numpy as np
o = cv2.imread('C:\\Users\\Administrator\\Desktop\\caonima.png')
cv2.imshow("original", o)
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
rect = cv2.minAreaRect(contours[0])
print("返回值rect:\n", rect)
points = cv2.boxPoints(rect)
print("\n转换后的points:\n", points)
points = np.int32(points) #取整
image=cv2.drawContours(o, [points],0, (255,255,255),2)
cv2.imshow("result", o)
cv2.waitKey()
cv2.destroyAllWindows()
- 左图是原始图像o。
- 右图显示了最小包围矩形框。
同时,程序还会输出如下结果:
从输出结果可以看出:
- 返回值rect表示返回的矩形特征信息。该值的结构是(最小外接矩形的中心(x, y), (宽度,高度),旋转角度)
- 转换后的points是一些点,是能够用作函数cv2.drawContours()参数的轮廓点。
最小包围圆形
函数cv2.minEnclosingCircle()通过迭代算法构造一个对象的面积最小包围圆形。该函数的语法格式为:
center, radius = cv2.minEnclosingCircle( points )
式中:
- 返回值center是最小包围圆形的中心。
- 返回值radius是最小包围圆形的半径。
- 参数points是轮廓。
【例5】使用函数cv2.minEnclosingCircle()构造图像的最小包围圆形。
根据题目的要求,编写代码如下:
import cv2
o = cv2.imread('cc.bmp')
cv2.imshow("original", o)
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255, cv2.THRESH_BINARY)
image, contours, hierarchy = cv2.findContours(binary,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
(x, y), radius = cv2.minEnclosingCircle(contours[0])
center = (int(x), int(y))
radius = int(radius)
cv2.circle(o, center, radius, (255,255,255),2)
cv2.imshow("result", o)
cv2.waitKey()
cv2.destroyAllWindows()
- 左图是图像o。
- 右图是含有最小包围圆形的图像。
最优拟合椭圆
在OpenCV中,函数cv2.fitEllipse()可以用来构造最优拟合椭圆。该函数的语法格式是:
retval = cv2.fitEllipse( points )
式中:
- 返回值retval是RotatedRect类型的值。这是因为该函数返回的是拟合椭圆的外接矩形,retval包含外接矩形的质心、宽、高、旋转角度等参数信息,这些信息正好与椭圆的中心点、轴长度、旋转角度等信息吻合。
- 参数points是轮廓。
【例6】使用函数cv2.fitEllipse()构造最优拟合椭圆。
本例需要使用函数cv2.ellipse()根据函数cv2.fitEllipse()的返回值绘制最优拟合椭圆。
根据题目的要求,编写代码如下:
import cv2
o = cv2.imread('C:\\Users\\Administrator\\Desktop\\caonima.png')
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
cv2.imshow("original", o)
ellipse = cv2.fitEllipse(contours[0])
print("ellipse=", ellipse)
cv2.ellipse(o, ellipse, (0, 255, 0), 3)
cv2.imshow("result", o)
cv2.waitKey()
cv2.destroyAllWindows()
翻车了 家人们
最优拟合直线
在OpenCV中,函数cv2.fitLine()用来构造最优拟合直线,该函数的语法格式为:
line = cv2.fitLine( points, distType, param, reps, aeps )
式中line为返回值,是返回的最优拟合直线参数。式中的参数如下:
- points:轮廓。
- distType:距离类型。拟合直线时,要使输入点到拟合直线的距离之和最小,其类型如表1所示。
- param:距离参数,与所选的距离类型有关。当此参数被设置为0时,该函数会自动选择最优值。
- reps:用于表示拟合直线所需要的径向精度,通常该值被设定为0.01。
- aeps:用于表示拟合直线所需要的角度精度,通常该值被设定为0.01。
用处不大,代码就不搞了
最小外包三角形
在OpenCV中,函数cv2.minEnclosingTriangle()用来构造最小外包三角形。该函数的语法格式为:
retval, triangle = cv2.minEnclosingTriangle( points )
式中有两个返回值:
- retval:最小外包三角形的面积。
- triangle:最小外包三角形的三个顶点集。式中的参数points是轮廓。
式中的参数points是轮廓。
【例8】使用函数cv2.minEnclosingTriangle()构造最小外包三角形。根据题目的要求,编写代码如下:
import cv2
import numpy as np
# 读取图像
o = cv2.imread('C:\\Users\\Administrator\\Desktop\\caonima.png')
# 显示原图
cv2.imshow("original", o)
# 转为灰度图
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
# 二值化处理
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 查找轮廓
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# 计算最小外接三角形
area, trgl = cv2.minEnclosingTriangle(contours[0])
# 打印面积和三角形顶点
print("area =", area)
print("trgl:", trgl)
# 绘制三角形
for i in range(3):
pt1 = tuple(np.int32(trgl[i][0])) # 转为整数
pt2 = tuple(np.int32(trgl[(i + 1) % 3][0])) # 转为整数
cv2.line(o, pt1, pt2, (255, 255, 255), 2)
# 显示结果
cv2.imshow("result", o)
cv2.waitKey()
cv2.destroyAllWindows()
- 左图是图像o。
- 右图是包含最小外包三角形的图像。
逼近多边形
函数cv2.approxPolyDP()用来构造指定精度的逼近多边形曲线。该函数的语法格式为:
approxCurve = cv2.approxPolyDP( curve, epsilon, closed )
式中,返回值approxCurve为逼近多边形的点集。
式中的参数如下:
- curve是轮廓。
- epsilon为精度,原始轮廓的边界点与逼近多边形边界之间的最大距离。
- closed是布尔型值。该值为True时,逼近多边形是封闭的;否则,逼近多边形是不封闭的。
函数cv2.approxPolyDP()采用的是Douglas-Peucker算法(DP算法)。该算法首先从轮廓中找到距离最远的两个点,并将两点相连(见图1的(b))。接下来,在轮廓上找到一个离当前直线最远的点,并将该点与原有直线连成一个封闭的多边形,此时得到一个三角形,如图1中的( c )图所示。
将上述过程不断地迭代,将新找到的距离当前多边形最远的点加入到结果中。当轮廓上所有的点到当前多边形的距离都小于函数cv2.approxPolyDP()的参数epsilon的值时,就停止迭代。最终可以得到如图1的(f)图所示的处理结果。
通过上述过程可知,epsilon是逼近多边形的精度信息。通常情况下,将该精度设置为多边形总长度的百分比形式。
【例9】使用函数cv2.approxPolyDP()构造不同精度的逼近多边形。
根据题目的要求,编写代码如下:
import cv2
# ----------------读取并显示原始图像-------------------------------
o = cv2.imread('C:\\Users\\Administrator\\Desktop\\caonima.png')
o = cv2.resize(o, (300, 300)) # 调整大小到 300x300
cv2.imshow("original", o)
# ----------------获取轮廓-------------------------------
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
# ----------------epsilon=0.1*周长-------------------------------
adp = o.copy()
epsilon = 0.1 * cv2.arcLength(contours[0], True)
approx = cv2.approxPolyDP(contours[0], epsilon, True)
adp = cv2.drawContours(adp, [approx], 0, (0, 0, 255), 2)
cv2.imshow("result0.1", adp)
# ----------------epsilon=0.09*周长-------------------------------
adp = o.copy()
epsilon = 0.09 * cv2.arcLength(contours[0], True)
approx = cv2.approxPolyDP(contours[0], epsilon, True)
adp = cv2.drawContours(adp, [approx], 0, (0, 0, 255), 2)
cv2.imshow("result0.09", adp)
# ----------------epsilon=0.055*周长-------------------------------
adp = o.copy()
epsilon = 0.055 * cv2.arcLength(contours[0], True)
approx = cv2.approxPolyDP(contours[0], epsilon, True)
adp = cv2.drawContours(adp, [approx], 0, (0, 0, 255), 2)
cv2.imshow("result0.055", adp)
# ----------------epsilon=0.05*周长-------------------------------
adp = o.copy()
epsilon = 0.05 * cv2.arcLength(contours[0], True)
approx = cv2.approxPolyDP(contours[0], epsilon, True)
adp = cv2.drawContours(adp, [approx], 0, (0, 0, 255), 2)
cv2.imshow("result0.05", adp)
# ----------------epsilon=0.02*周长-------------------------------
adp = o.copy()
epsilon = 0.02 * cv2.arcLength(contours[0], True)
approx = cv2.approxPolyDP(contours[0], epsilon, True)
adp = cv2.drawContours(adp, [approx], 0, (0, 0, 255), 2)
cv2.imshow("result0.02", adp)
# ----------------等待释放窗口-------------------------------
cv2.waitKey()
cv2.destroyAllWindows()
- origin图是图像o。
- result0.1图显示了
epsilon=0.1
周长”的逼近多边形(实际上,在第1次的查找过程中仅仅找到了一条直线)。 - result0.09图显示了
epsilon=0.09
周长”的逼近多边形。 - result0.055图显示了
epsilon=0.055
周长”的逼近多边形。 - result0.05图显示了
epsilon=0.05
周长”的逼近多边形。 - result0.02图显示了
epsilon=0.02
周长”的逼近多边形。
从程序运行结果可以看出,在函数cv2.approxPolyDP()中通过参数epsilon可以控制逼近多边形的精度。需要注意:本节中使用的图像都是仅有一个轮廓的图像,处理的轮廓都是contours[0]。如果处理的原图像中有多个轮廓,则需要注意控制轮廓的索引,即contours[i]中的i值,使其指向特定的轮廓。