在计算轮廓时,有时仅需要一个接近轮廓的近似多边形。OpenCV提供了多种计算轮廓近似多边形的方法。

矩形包围框

形式一:

retval = cv2.boundingRect(array)
  • retval表示返回的矩形边界的左上角顶点的坐标值及矩形边界的宽度和高度
  • array为灰度图像或轮廓

形式二:

x, y, w, h = cv2.boundingRect( array )
  • 矩形边界左上角顶点的x坐标
  • 矩形边界左上角顶点的y坐标
  • 矩形边界的x方向的长度
  • 矩形边界的y方向的长度

例1:设计程序,显示函数cv2.boundingRect()不同形式的返回值。

import cv2
import numpy as np
img = cv2.imread(r'E:\Blog\OpenCV\Process\A1.png')

# =====处理为二值图=====
gray = cv2.cvtColor(img,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.RETR_LIST 对检测的轮廓不建立等级关系

# =====返回顶点及变长=====
x, y, w, h = cv2.boundingRect(contours[0])
print("顶点及长度的形式:")
print('x= ',x)
print('y= ',x)
print('w= ',w)
print('h= ',h)

# =====仅有一个返回值=====
rect = cv2.boundingRect(contours[0])
print('\n')
print('只返回一个值(元组) :')
print('rect= ',rect)

结果:

顶点及长度的形式:
x= 14
y= 14
w= 248
h= 151

只返回一个值(元组) :
rect= (14, 72, 248, 151)

 

例2:使用cv2.drawContours()绘制矩形包围框。

import cv2
import numpy as np
img = cv2.imread(r'E:\Blog\OpenCV\Process\A1.png')
cv2.imshow("IMG",img)
# =====处理为二值图=====
gray = cv2.cvtColor(img,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.RETR_LIST 对检测的轮廓不建立等级关系

# =====构造矩形边界=====
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(gray,[brcnt],-1,(255,255,255),2)

# =====显示矩形边界=====
cv2.imshow("RESULT",gray)
cv2.waitKey()

返回值

还可以使用绘制矩形的函数cv2.rectangle()实现:

image-20220211115616401

最小包围矩形框

语法:

retval = cv2.minAreaRect( curve )
  • retval表示返回的矩形特征信息

    该值的结构是(最小外接矩形的中心(x,y),(宽度,高度),旋转角度)

  • curve是轮廓

需要注意的是,retval的结构不符合cv2.drawContours()的参数结构要求。需要转换:

points = cv2.boxPoints( retval )
  • 这里的points是能够用于函数cv2.drawContours()参数的轮廓点

 

例:使用函数cv2.minAreaRect()计算图像的最小包围矩阵框。

import cv2
import numpy as np
img = cv2.imread(r'E:\Blog\OpenCV\Process\A1.png')
cv2.imshow("IMG",img)
# =====处理为二值图=====
gray = cv2.cvtColor(img,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.RETR_LIST 对检测的轮廓不建立等级关系

# ====返回最小方框====
rect = cv2.minAreaRect(contours[0])
print("返回值rect:\n",rect)

# ====方框格式转换====
points = cv2.boxPoints(rect)
print('转换后的points:\n',points)

points = np.int0(points)  #取整

cv2.drawContours(img,[points],0,(255,255,255),2)
cv2.imshow("RESULT",img)
cv2.waitKey()

运行结果:

返回值rect:
((137.5, 138.5), (141.55892944335938, 244.25555419921875), 77.6609115600586)
转换后的points:
[[ 3.068015 95.453865]
[241.68129 43.25721 ]
[271.93198 181.54614 ]
[ 33.31871 233.7428 ]]

显示结果

最小包围圆形

语法:

center, radius = cv2.minEnclosingCircle( points )
  • center返回的是圆心
  • radius返回的是半径
  • points是轮廓

例:使用函数cv2.minEnclosingCircle()计算图像的最小包围矩阵框。

import cv2
import numpy as np
img = cv2.imread(r'E:\Blog\OpenCV\Process\A4.png')
cv2.imshow("IMG",img)
# =====处理为二值图=====
gray = cv2.cvtColor(img,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.RETR_LIST 对检测的轮廓不建立等级关系

# ====返回最小包围圈====
(x,y),radius = cv2.minEnclosingCircle(contours[0])
center = (int(x),int(y))
radius = int(radius)

cv2.circle(img,center,radius,(255,255,255),2)
cv2.imshow("RESULT",img)
cv2.waitKey()

实现效果(最小包围圆)

最优拟合椭圆

函数cv2.fitEllipse( )可以用来构造最优拟合椭圆。语法:

retval = cv2.fitEllipse( points )
  • points为轮廓
  • retval值包含中心点、轴长度、椭圆的旋转角度

例:使用函数cv2.fitEllipse( )构造最优拟合椭圆。

import cv2
import numpy as np
img = cv2.imread(r'E:\Blog\OpenCV\Process\A4.png')
cv2.imshow("IMG",img)
# =====处理为二值图=====
gray = cv2.cvtColor(img,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.RETR_LIST 对检测的轮廓不建立等级关系

# ====返回最小包围椭圆====
ellipse = cv2.fitEllipse(contours[0])
print("ellipse = \n",ellipse)

cv2.ellipse(img,ellipse,(255,0,0),3)
cv2.imshow("RESULT",img)
cv2.waitKey()

结果:

ellipse =
((259.45526123046875, 151.544189453125), (88.27090454101562, 150.53924560546875), 87.91172790527344)

  • (259.45526123046875, 151.544189453125)是椭圆的中心
  • (88.27090454101562, 150.53924560546875)是椭圆的轴长度
  • 87.91172790527344是椭圆的旋转

椭圆拟合结果

最优直线拟合

在OpenCV中,函数cv2.fitLine()用来构造最优拟合直线。

语法:

line = cv2.fitLine( points, distType, param, reps, aeps )
  • points:轮廓
  • distType:距离类型。拟合直线时,要使输入点到拟合直线的距离最小,具体参数值如下表所示
  • param:距离参数,与所选的距离类型有关。当此参数被设置为0时,该函数会自动选择最优值
  • reps:用于表示拟合直线所需要的径向精度,通常为0.01
  • aeps:用于表示拟合直线所需要的角度精度,通常为0.01

距离类型参数

例:使用函数cv2.fitLine()构造最优拟合直线。

from turtle import right
import cv2
import numpy as np
img = cv2.imread(r'E:\Blog\OpenCV\Process\A4.png')
cv2.imshow("IMG",img)
# =====处理为二值图=====
gray = cv2.cvtColor(img,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.RETR_LIST 对检测的轮廓不建立等级关系

# ====返回最优直线====
rows, cols = img.shape[:2]
[vx, vy, x, y] = cv2.fitLine(contours[0], cv2.DIST_L2, 0, 0.01, 0.01)
lefty = int((-x*vy/vx)+y)
righty = int(((cols-x)*vy/vx)+y)
# ====绘制最优直线====
cv2.line(img,(cols-1,righty),(0,lefty),(0,255,0),2)

cv2.imshow("RESULT",img)
cv2.waitKey()

# 关于绘制的函数会在后面具体细说,此处了解即可。

运行结果

最小外包三角形

语法:

retval, triangle = cv2.minEnvlosingTriangle(points)
  • retval:最小外包三角形的面积
  • triangle:最小外包三角形的三个顶点集
  • points:轮廓

例:编写代码,构造最小外包三角形。

import cv2
img = cv2.imread(r'E:\Blog\OpenCV\Process\A5.png')
cv2.imshow("IMG",img)
# =====处理为二值图=====
gray = cv2.cvtColor(img,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.RETR_LIST 对检测的轮廓不建立等级关系

# ====返回外包三角形====
area, trg1 = cv2.minEnclosingTriangle(contours[0])
print("AREA=\n",area)
print("TRG1=\n",trg1)
# ====绘制外包三角形,原理是绘制三个线段===
for i in range(0,3):
    cv2.line(img, tuple(trg1[i][0]), tuple(trg1[(i+1)%3][0]),(255,0,255),2)

cv2.imshow("RESULT",img)
cv2.waitKey()

此处代码有问题,报错,网上查了查,说是因为不能是浮点型,但是没成功解决,此处留个念想。

OpenCV(4.5.5) -1: error: (-5:Bad argument) in function 'line' > Overload resolution failed: > - Can't parse 'pt1'. Sequence item with index 0 has a wrong type > - Can't parse 'pt1'. Sequence item with index 0 has a wrong type

逼近多边形

语法:

approxCurve = cv2.approxPolyDP( curve, epsilon, closed )
  • 用来构造指定精度的逼近多边形曲线
  • curve是轮廓
  • epsilon为精度,原始轮廓的边界点与逼近多边形边界之间的最大距离
  • closed是布尔类型的值,为True时,逼近多边形是封闭的,否则不是封闭的。

例:构造不同精度的逼近多边形。

import cv2
img = cv2.imread(r'E:\Blog\OpenCV\Process\A5.png')
cv2.imshow("IMG",img)
# =====处理为二值图=====
gray = cv2.cvtColor(img,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.RETR_LIST 对检测的轮廓不建立等级关系

# =====  epsilon = 0.1 * 周长  =======
adp = img.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 = img.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.02 * 周长  =======
adp = img.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()

python程序结果


博主个人公众号
版权声明 ▶ 本网站名称:陶小桃Blog
▶ 本文链接:https://www.52txr.cn/2022/OpenCV37.html
▶ 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行核实删除。
▶ 转载本站文章需要遵守:商业转载请联系站长,非商业转载请注明出处!!

最后修改:2022 年 05 月 27 日
如果觉得我的文章对你有用,请随意赞赏