(三)OpenCV中的图像处理之轮廓

2023-05-16

注释:本文翻译自OpenCV3.0.0 document->OpenCV-Python Tutorials,包括对原文档种错误代码的纠正

该章节分为以下四个小节:

(一)     Contours:Getting Started(轮廓:开始)

(二)     Contours Features(轮廓特征)

(三)     Contours Properties(轮廓属性)

(四)     Contours:More Functions(轮廓:更多方法)

(五)     Contours Hierarchy(轮廓分级)


第一小节:Contours:GettingStarted

1.目标:

  • 明白什么是轮廓
  • 学会找到这些轮廓,绘制轮廓
  • 学习这些函数:cv2.findContours(),cv2.drawContours()

2.什么是轮廓

轮廓可以简单地解释为连接所有连续点(沿着边界),具有相同颜色或强度的曲线。轮廓是形状分析和物体检测和识别的有用工具。

  •  为了更高的准确率,使用二值图像。在寻找轮廓之前,应用阈值或canny边缘检测
  • findContours函数修改源图像,所有想要在找到轮廓后保存源图像,提前把源图像赋值给其它变量
  • 再OpenCV中,查找轮廓就像从黑色背景中找到白色物体,所以记住,找到的物体应该是白色的,背景应该是黑色的。

下面的栗子:在二值图像中找到轮廓

'''
Opencv中的轮廓:
demo1
'''
import cv2

img = cv2.imread('2.jpg')
# 图像灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 3*3核的高斯滤波
gray = cv2.GaussianBlur(gray, (3, 3), 0)
# canny边缘检测
gray = cv2.Canny(gray, 100, 300)

ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# binary是最后返回的二值图像
#findContours()第一个参数是源图像、第二个参数是轮廓检索模式,第三个参数是轮廓逼近方法
#输出是轮廓和层次结构,轮廓是图像中所有轮廓的python列表,每个单独的轮廓是对象边界点的(x,y)坐标的Numpy数组
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, contours, -1, (0, 0, 255), 1)
cv2.imshow("1", binary)
cv2.imshow("win10", img)

cv2.waitKey(0) & 0xFF

结果:


3.怎样绘制轮廓

Cv2.drawContours()用来绘制轮廓,只要提供了边界点它也可以用来绘制各种形状。它的第一个边界是源图像,第二个参数是Python列表传递的轮廓,第三个参数是轮廓的索引(在绘制单个轮廓时有用,在绘制多个轮廓时传递-1),剩余的参数是颜色、厚度等。

绘制图像中的所有轮廓:


cv2.drawContours(img, contours, -1, (0,255,0), 3)  

绘制个别轮廓,如第四轮廓:


cv2.drawContours(img, contours, 3, (0,255,0), 3)  

但是通常情况下,下面的方式更有用:


cnt = contours[4]  

cv2.drawContours(img, [cnt], 0, (0,255,0), 3)  

4.轮廓近似法

轮廓近似法是cv2.findContours()中第三个参数。

前面,我们说轮廓是具有相同强度的形状的边界。它存储形状边界的(x,y)坐标,但是它是否存储所有坐标?这是通过这种轮廓法来逼近的。

如果传递的参数为cv2.CHIAN_APPROX_NONE,则会存储所有边界。但实际情况下我们可能并不需要所有的点,比如发现一条直线的轮廓,所需要的只是这条线的两个端点。这就是cv2.CHAIN_APPROX_SIMPLE所做的,它删除了所有冗余的点并压缩轮廓,从而大大节省了内存。

在下面矩形图像中显示了这种技术,在轮廓数组上的所有坐标上绘制一个圆,第一个用cv2.CHIAN_APPROX_NONE绘制(734个点),第二个用cv2.CHAIN_APPROX_SIMPLE(4个点)绘制。



第二小节:ContoursFeatures(轮廓特征)

5.目标

  • 找不同的轮廓特征,如:面积、周长、重心、边框
  • 学会关于轮廓的方法

6.时刻

图像时刻可以帮助你计算一些特征,如物体的质心、物体的面积等。关于图像时刻的概念,参考维基百科。

函数cv2.moments()给出了计算所有时刻值的字典,详细如下:


import cv2  

import numpy as np  

  

img = cv2.imread('star.jpg',0)  

ret,thresh = cv2.threshold(img,127,255,0)  

binary,contours,hierarchy = cv2.findContours(thresh, 1, 2)  

cnt = contours[0]  

M = cv2.moments(cnt)  

print M  

然后可以提取有用的信息,如面积、质心等。质心由下面关系式给出:

     

cx = int(M['m10']/M['m00'])  

cy = int(M['m01']/M['m00'])  

7.轮廓区域

轮廓区域由cv2.contourArea()计算,或者由时刻中的M[‘m00’]得到:


area = cv2.contourArea(cnt)  

8.轮廓周长

也称为轮廓弧长,由cv2.arcLength()计算得到,第二个参数指定shape是封闭轮廓(True)或仅仅是曲线。


perimeter = cv2.arcLength(cnt,True)  

9.轮廓近似

如果图像没有明确的轮廓,使用cv2.approxPolyDP近似此轮廓。


epsilon = 0.1*cv2.arcLength(cnt,True)  

approx = cv2.approxPolyDP(cnt,epsilon,True)  

10.Convex Hull

ConvexHull将看起来类似于轮廓近似,但它不是(两者可能在某些情况下提供相同的结果)。这里,cv2.convexHull()函数检查一个曲线的凸性缺陷并进行修正。一般来说,凸曲线是总是凸出的或至少平坦的曲线。如果内部膨胀,则称为凸面缺陷。例如,检查下面的图像。红线显示手的凸包。双面箭头标记显示凸度缺陷,这是船体与轮廓的局部最大偏差。

有必要去解释以下这个函数的参数:


hull = cv2.convexHull(points[, hull[, clockwise[, returnPoints]]  

参数详情:

  • Points:是传入的轮廓;
  • Hull:是输出,通常我们避免它;
  • Clockwise:方向标志。如果是true,则顺时针输出凸包,否则逆时针方式输出;
  • ReturnPoints:默认情况下为true。它返回hull点的坐标,如果是false,则返回与hull点对应的轮廓点的索引。

所以要得到如上图所示的凸包,下面这条语句就足够了:

hull = cv2.convexHull(cnt)

但是如果你想找到凸面缺陷,你需要传递returnPoints=False。为了理解它,我们将采用上面的矩形图像。首先,我发现它的轮廓为cnt。现在我发现它的凸包带有returnPoints=True,我得到了以下值:[[[234202]],[[51202]],[[5179]],[[23479]]]这是四角矩形点。现在,如果对returnPoints=False做同样的处理,我会得到如下结果:[[129],[67],[0],[142]]。这些是轮廓中相应点的指数。例如,检查第一个值:cnt[129]=[[234,202]],它与第一个结果相同(其它的点也是如此)。


11.检查凸度

这里有一个函数可以检查曲线是否凸起,cv2.isContourConvex().返回值为True或False.

k = cv2.isContourConvex(cnt)


12.边界矩形

1)直线边界矩形

它是一个直的矩形,不考虑对象的旋转。所以边界矩形的面积不会最小。这个矩形由函数cv2.boundingRect()发现。

设(x,y)为矩形的左上角坐标,(w,h)为其宽度和高度.

x,y,w,h = cv2.boundingRect(cnt)

cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)

2)旋转矩形

这里,边界矩形是以最小面积绘制的,所以它也考虑旋转。使用的函数是cv2.minAreaRect().它返回一个Box2D的结构,使用包含以下结构——(中心(x,y),(宽度,高度),旋转角度)。但要绘制这个矩形,我们需要矩形的四个角。它是通过函数cv2.boxPoints()获得的。

rect = cv2.minAreaRect(cnt)

box = cv2.boxPoints(rect)

box = np.int0(box)

cv2.drawContours(img,[box],0,(0,0,255),2)

两种边界矩形都在下图中显示,绿色是一般的边界矩形,红色的是旋转边界矩形


13.最小封闭圈

接下来我们使用函数cv2.minEnclosingCircle()找到对象的外接圆。它是一个以最小面积完全覆盖物体的圆圈。

(x,y),radius = cv2.minEnclosingCircle(cnt)

center = (int(x),int(y))

radius =int(radius)

cv2.circle(img,center,radius,(0,255,0),2)

14.拟合椭圆

接下来将一个椭圆拟合到一个对象,它返回椭圆被刻在其中的旋转矩形。

ellipse = cv2.fitEllipse(cnt)

cv2.ellipse(img,ellipse,(0,255,0),2)

15.拟合线条

同样我们可以用一条线来拟合一组点。下图包含一组白点,可以近似为一条线。

rows,cols = img.shape[:2]

[vx,vy,x,y] = cv2.fitLine(cnt, 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)


第三小节:ContoursProperties(轮廓属性)

1.目标:

  • 学会找到不同轮廓的属性,如solidity,mean intensity等
  • 学习提取像Solidity、Equivalent Diameter, Mask image,Mean Intensity等对象的一些常用属性

2.纵横比(Aspect Ratio)

 它是对象矩阵的宽高比:



x,y,w,h = cv2.boundingRect(cnt)  

aspect_ratio = float(w)/h  

3.范围(Extent)

范围是轮廓面积和边界矩形面积的比率:



area = cv2.contourArea(cnt)  

x,y,w,h = cv2.boundingRect(cnt)  

rect_area = w*h  

extent = float(area)/rect_area  

4.密实度(Solidity)

密实度是轮廓面积与其凸包面积的比率:


area = cv2.contourArea(cnt)  

hull = cv2.convexHull(cnt)  

hull_area = cv2.contourArea(hull)  

solidity = float(area)/hull_area  

5.等效直径(EquivalentDiameter)

等效直径是与轮廓面积相同的圆的直径



area = cv2.contourArea(cnt)  

equi_diameter = np.sqrt(4*area/np.pi)  

6.方向(Orientation)

方向是物体指向的角度。以下方法也给出主轴和副轴长度。


(x,y),(MA,ma),angle = cv2.fitEllipse(cnt)  

7.蒙版和像素点(Maskand Pixel Points)

在某些情况下,我们可能需要包含该对象的所有点。它可以用以下方法做到:


mask = np.zeros(imgray.shape,np.uint8)  

cv2.drawContours(mask,[cnt],0,255,-1)  

pixelpoints = np.transpose(np.nonzero(mask))  

#pixelpoints = cv2.findNonZero(mask)  

这里有两个方法,一个使用Numpy函数,另一个使用OpenCV函数(最后一行注释)来执行相同操作。结果是一样的,但略有不同。Numpy以(行,列)格式给出坐标,而OpenCV以(x,y)格式给出坐标。所以基本上答案会互换。请注意,row=x和column=y.


8.最大最小值和它们的位置(maximumvalue,minimum value and their locations)

我们可以使用蒙版图像来找到这些参数。


min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(imgray,mask = mask)  

9.平均颜色或平均强度(meanColor or mean Intensity)

在这里,我们可以找到一个物体的平均颜色。或者它可以是灰度模式下物体的平均强度。我们再次使用相同的掩膜来做到这一点。


mean_val = cv2.mean(im,mask = mask)  

10.极值点(ExtremePoints)

极值点表示对象的最上面、最下面、最左边和最右边点。


leftmost = tuple(cnt[cnt[:,:,0].argmin()][0])  

rightmost = tuple(cnt[cnt[:,:,0].argmax()][0])  

topmost = tuple(cnt[cnt[:,:,1].argmin()][0])  

bottommost = tuple(cnt[cnt[:,:,1].argmax()][0])  


第四小节:Contours:More Functions (轮廓:更多方法)

1.目标:

  • 如何找到凸起缺陷
  • 寻找从一个点到多个点的最短距离
  • 匹配不同的形状

2.凸起缺陷

我们在第二章看到什么是凸包,关于轮廓。物体与该hull之间的任何偏差均可视为凸度缺陷。

OpenCV提供了一个现成的函数来查找这个,cv2.convexityDefects().基本的函数调用如下:


hull = cv2.convexHull(cnt,returnPoints = False)  

defects = cv2.convexityDefects(cnt,hull)  

Note:请记住,我们必须在找到凸包时通过returnPoints=False,以便找到凹凸缺陷。

它返回一个数组,其中每行包含这些值——[起点,终点,最远点,到最远点的近似距离]。我们可以使用图像对其进行可视化。我们绘制一条连接起点和终点的线,然后在最远点绘制一个圆。记得前三个返回的时cnt的索引。所以我们必须从cnt中提取这些点。

示例代码:

# -*- coding: utf-8 -*-
import cv2
import numpy as np

img = cv2.imread('13.png')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(img_gray, 127, 255, 0)
binary, contours, hierarchy = cv2.findContours(thresh, 2, 1)
cnt = contours[0]

hull = cv2.convexHull(cnt, returnPoints=False)
defects = cv2.convexityDefects(cnt, hull)

for i in range(defects.shape[0]):
    s, e, f, d = defects[i, 0]
    start = tuple(cnt[s][0])
    end = tuple(cnt[e][0])
    far = tuple(cnt[f][0])
    cv2.line(img, start, end, [0, 255, 0], 2)
    cv2.circle(img, far, 5, [0, 0, 255], -1)

cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

结果如下:


3.点多边形测试

该函数可查找图像中某点与轮廓之间的最短距离。当点在轮廓之外时,它返回负值的距离,点在内部时返回正值,如果点在轮廓上则返回零。

例如,我们可以按如下方式检查点(50,50):


dist = cv2.pointPolygonTest(cnt,(50,50),True)  

在这个函数中,点三个参数是measureDist。如果它是true,它会找到标记的距离。如果是false,它会查找该点是在内部还是在外部还是在轮廓上。(分别返回+1,-1,0).

Note:如果你不想查找距离,请确保第三个参数是false,因为这是一个耗时的过程。所以,设置为false可以加速2-3倍。


4.形状匹配

OpenCV带有一个函数cv2.matchShapes(),可以用来比较两个形状或两个轮廓,并返回一个显示相似度的度量。结果越低,匹配效果越好。它基于hu-moment值进行计算。文档中解释了不同的测量方法。

# -*- coding: utf-8 -*-
'''
形状匹配
'''
import cv2
import numpy as np

img1 = cv2.imread('car.png', 0)
img2 = cv2.imread('car1.png', 0)

ret, thresh = cv2.threshold(img1, 127, 255, 0)
ret, thresh2 = cv2.threshold(img2, 127, 255, 0)
binary, contours, hierarchy = cv2.findContours(thresh, 2, 1)
cnt1 = contours[0]
binary, contours, hierarchy = cv2.findContours(thresh2, 2, 1)
cnt2 = contours[0]

ret = cv2.matchShapes(cnt1, cnt2, 1, 0.0)
print(ret)

试着用上面的图片进行融合,输出结果:

得到的结果:

  • 形状A和它自身,返回值:0
  •  形状A和形状B,返回值:0.001946
  • 形状A和形状C,返回值:0.326911

可以看出,即使图像旋转对此比较的影响也不大。

联系:

1. 检查cv2.pointPolygonTest()的文档,你可以找到一个红色和蓝色的漂亮图像。它表示从所有像素到白色曲线的距离。 取决于距离,曲线内的所有像素都是蓝色的。 同样的外点是红色的。 轮廓边缘用白色标记。 所以问题很简单。 编写一个代码来创建这样的距离表示

2. 使用cv2.matchShapes()比较数字或字母的图像。(这将是向OCR迈出的简单一步)


第五小节:ContourHierarchy(轮廓的层次结构)

1.目标:我们了解轮廓的层次结构,即轮廓中的亲子关系。

2.原理

在最近几篇关于轮廓的文章中,我们已经使用了OpenCV提供的与轮廓相关的几个函数。但当我们使用cv2.findContours()函数在图像中找到轮廓时,我们已经传递了一个参数ContourRetrieval Mode,我们通常会传递cv2.RETR_LIST或者cv2.RETR_TREE,而且运行效果很好。但它们实际上意味着什么呢?

另外,在输出中,我们得到了三个数组,第一个是图像,第二个是轮廓,还有一个我们命名为层次结构的输出。但是我们从来没有在任何地方使用这个层次,那么这个层次结构是什么?它有什么用途?它与前面提到的函数参数有什么关系?

这些就是我们要谈论的点。


3.什么是层次结构

通常我们使用cv2.findContours()函数来检测图像中的对象。有时物体位于不同的位置,但在某些情况下,某些形状是其它形状。就像嵌套数字一样。在这种情况下,我们称外层为父层,内层为子层。这样,图像中的轮廓彼此之间有一些关系。我们可以指定一个轮廓是如何互相连接的,例如,它是否是其它轮廓的子节点,或者它是否是父节点等。此关系的表示形式称为层次结构。

考虑下面的示例图片:

在这幅图像里,这里有一些形状被标记为0-5,2和2a表示最外面盒子的外部和内部轮廓。

这里,轮廓0,1,2是外部或者最外面的。我们可以说,它们在层次结构0中,或者只是它们处于同一层次结构中。

接下来是轮廓2a,它可以被认为是轮廓2的子轮廓(或者相反,轮廓2是轮廓2a的父轮廓)。所以,让它在层次1,类似的,轮廓3是轮廓2的子轮廓,并且它在下一层出现。最后,轮廓4,5是轮廓3a的子轮廓,它们进入最后一个层级。从我对盒子的编号方式来看,我会说轮廓4是轮廓3a的第一个子轮廓(也就是轮廓5)。

我提到了这些名词术语。相同的层级(same hierarchy level),外部轮廓(external contour),子轮廓(child contour),父轮廓(parent contour),第一个子轮廓(first child)。好了现在进入OpenCV.


4.OpenCV中的层次结构的表示(HierarchyRepresentation in OpenCV)

所以每个轮廓都有自己的信息,包括它是什么层级、谁是它的孩子,谁是它的父母等。OpenCV将它表示为一个包含四个值的数组:[Next,Previous,First_child,Parent].


5.轮廓检索模式(ContourRetrieval Mode)

1)RETR_LIST

这是四种flag中最简单(从解释的角度看)。它只检索所有轮廓,但不创建任何父子关系。在这条规则下,父母和孩子是平等的,他们只是轮廓。即它们都属于同一层级。

所以在这里,层级数组中的第三和第四项总是-1。但显然,next和previous项将具有相应的值。只需自己检查并验证它。

下面是我得到的结果,每行都是相应轮廓的层次结构细节。例如,第一行对应于轮廓0,下一个轮廓是轮廓1。因此,next=1没有先前的轮廓,所以previous=0.其余两个,如前所述,它是-1.


>>> hierarchy  

array([[[ 1, -1, -1, -1],  

        [ 2,  0, -1, -1],  

        [ 3,  1, -1, -1],  

        [ 4,  2, -1, -1],  

        [ 5,  3, -1, -1],  

        [ 6,  4, -1, -1],  

        [ 7,  5, -1, -1],  

        [-1,  6, -1, -1]]])  

如果你不使用任何层次结构功能,则这是在代码中使用的理想选择。

2)RETR_EXTERNAL

如果你使用这个标志,它只会返回极端的外部标志。所有的孩子轮廓都被留下。我们可以说,根据这条规定,只有每个家庭中年长的人才会得到照顾。它不关系家庭的其它成员。

那么,在我们的图像中,那里还有多少个极端的外部轮廓呢?即在层级0级别的?只有3个,即轮廓0,1,2对吗?现在尝试用这个flag查找轮廓。在这里,给每个元素的值与上面相同。将它与以上结果进行比较以下是我得到:


hierarchy  

array([[[ 1, -1, -1, -1],  

        [ 2,  0, -1, -1],  

        [-1,  1, -1, -1]]])  

3)RETR_CCOMP

该flag检索所有轮廓并将它们排列为2级层次结构。即对象的外部轮廓(即其边界)被放置在层级-1中。对象内的孔的轮廓(如果有的话)放置在层次结构-2中。如果它内部有任何物体,其轮廓只能重新放置在层次1中。它在等级2中的孔洞等等。

只要考虑黑色背景上的“大白色的零”图像即可。零的外圈属于第一层次,零的内圈属于第二层次。

我们可以用一个简单的图像来解释它。在这里,我们已经用红色标出了轮廓的顺序以及它们所属的层次,颜色为绿色(1或2)。顺序与OpenCV检测轮廓的顺序相同。


所以考虑第一个轮廓,即轮廓0.它的层次结构是1.它有两个孔,轮廓1和2,它们属于层次2.因此对于轮廓0,同一层级中的下一个轮廓是轮廓3.其第一个孩子是在等级2中的轮廓-1。他没有父项,因此它在层次结构1中。所以它的层次数组是[3,-1,1,-1]。

现在轮廓-1。他在层次结构2中。下一个在同一层次中(在轮廓-1的父项之下)是轮廓2.没有前一个,也就是没有孩子。父母的轮廓是0,所以数组是[2,-1,-1,0].

同样的轮廓-2:它在层次-2.轮廓0下的同一层次中没有下一个轮廓。所以没有下一步。以前是轮廓-1.没有孩子啊,父母是轮廓-0.所以数组是[-1,1,-1,0].

轮廓-3:层次-1中的下一个轮廓是-5.以前是轮廓-0。孩子是轮廓4并且没有父母。所以数组是[5,0,4,-1].

轮廓4:在轮廓-3下的层次2中,他没有兄弟。所以没有下一个,没有以前,没有孩子,父母是轮廓3.所以数组是[-1,-1,-1,3].

这是我得到的最终答案:

 


>>> hierarchy  

array([[[ 3, -1,  1, -1],  

        [ 2, -1, -1,  0],  

        [-1,  1, -1,  0],  

        [ 5,  0,  4, -1],  

        [-1, -1, -1,  3],  

        [ 7,  3,  6, -1],  

        [-1, -1, -1,  5],  

        [ 8,  5, -1, -1],  

        [-1,  7, -1, -1]]])  

4)RETR_TREE

这是最后一个,它检索所有轮廓并创建完整的家庭层次列表。它甚至告诉我们,爷爷,父亲,儿子,孙子是谁,甚至还有….

例如,我拍摄了上面的图像,重写了cv2.RETR_TREE的代码,根据OpenCV给出的轮廓重新排序并对其进行分析。再次,红色字母给出等高线数字,绿色字母给出等级顺序。

取轮廓-0:它在层次结构-0中。同一层次中的下一个轮廓是林廓-7.没有以前的轮廓。孩子是轮廓-1.没有父母。所以数组是[7,-1,1,-1].

采取轮廓-2:它在层次1.没有同一级别的轮廓。没有前一个,孩子是轮廓2。父级轮廓为0。所以数组是[-1,-1,2,0].

以下是完整答案:


>>> hierarchy  

array([[[ 7, -1,  1, -1],  

        [-1, -1,  2,  0],  

        [-1, -1,  3,  1],  

        [-1, -1,  4,  2],  

        [-1, -1,  5,  3],  

        [ 6, -1, -1,  4],  

        [-1,  5, -1,  4],  

        [ 8,  0, -1, -1],  

        [-1,  7, -1, -1]]])  


最后一份代码示例:


'''
OpenCV中的轮廓:
1.绘制轮廓:cv2.drawCOntours(img,contours,index,color,thickness)
2.轮廓近似法:有时候并不需要存储所有边界,只需要存储这条线的两个端点。删除所有冗余的点并压缩轮廓,从而大大节省内存
3.轮廓特征:cv2.moments()计算所有时刻值的字典,如物体的质心、物体的面积、周长、重心等
'''
import cv2
import numpy as np
from matplotlib import pyplot as plt

plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

img = cv2.imread('temple.png', 0)

ret, thresh = cv2.threshold(img, 100, 255, 0)
binary, contours, hierarchy = cv2.findContours(thresh, 1, 2)
cv2.imshow('1', binary)

cnt = contours[0]
'''
M = cv2.moments(cnt)
# 轮廓周长
perimeter = cv2.arcLength(cnt, True)
# 轮廓区域
area = cv2.contourArea(cnt)  # area=M['m00']
# 轮廓近似
epsilon = 0.1 * cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
# 轮廓凸包
hull = cv2.convexHull(cnt)

print('cnt:', cnt)
print('轮廓近似坐标:', approx)
print('轮廓周长:', perimeter)
print('轮廓区域:', area)
print('图像时刻:', M)
'''

# 直线边界矩形:是一个直的矩形,不考虑对象的旋转,所以边界矩形的面积不会最小,这个矩形由cv2.boundingRect()发现
x, y, w, h = cv2.boundingRect(cnt)
img = cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)

# 旋转矩形:边界矩形以最小面积绘制的,所以它考虑旋转,使用的函数是cv2.minAreaRect(),
# 返回一个Box2D的结构(中心(x,y),(宽度,高度),旋转角度)
# 绘制这个矩形需要矩形的四个角,它是通过函数cv2.boxPoints()获得的
rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box)
img = cv2.drawContours(img, [box], 0, (0, 0, 255), 0)

# 最小封闭圈:使用函数cv2.minEnclosingCircle()找到对象的外接圆;它是一个以最小面积完全覆盖物体的圆圈
(x, y), radius = cv2.minEnclosingCircle(cnt)
center = (int(x), int(y))
radius = int(radius)
img = cv2.circle(img, center, radius, (0, 255, 0), 2)

# 拟合椭圆:接下来将一个椭圆拟合到一个对象,它返回椭圆被刻在其中的旋转矩形
ellipse = cv2.fitEllipse(cnt)
img = cv2.ellipse(img, ellipse, (0, 255, 0), 2)

# 拟合线条:用一条线来拟合一组点,下图包含一组白点,可以近似为一条线
rows, cols = img.shape[:2]
[vx, vy, x, y] = cv2.fitLine(cnt, cv2.DIST_L2, 0, 0.01, 0.01)
lefty = int((-x * vy / vx) + y)
righty = int(((cols - x) * vy / vx) + y)
img = cv2.line(img, (cols - 1, righty), (0, lefty), (0, 255, 0), 2)
cv2.imshow('6', img)

'''
titles = ['原图', '直线边界矩形', '旋转矩形', '最小封闭圈', '拟合椭圆', '拟合线条']
images = [img, rectContours, rotateRect, minCloseCircle, fitEllipse, fitLine]

for i in range(6):
    plt.subplot(2, 3, i + 1), plt.title(titles[i]), plt.imshow(images[i])
    plt.xticks([]), plt.yticks([])

plt.show()
'''

cv2.waitKey(0) & 0xFF

结果:



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

(三)OpenCV中的图像处理之轮廓 的相关文章

  • Python OpenCV:检测大体运动方向?

    我仍在编写一个书籍扫描脚本 现在 我所需要的只是能够自动检测翻页 这本书占据了 90 的屏幕 我使用一个粗糙的网络摄像头进行运动检测 所以当我翻页时 运动方向基本上是同一个方向 我修改了一个运动跟踪脚本 但导数却无济于事 usr bin e
  • 使用 OpenCV 进行车牌识别

    我有一个项目 需要使用 OpenCV 识别汽车的车牌 我想加载数字或字母的图像 让 OpenCV 识别它并将其打印到控制台 有一个函数可以做到这一点吗 如果没有 我该怎么办 Note 我正在研究灰度级 请帮忙 我必须在一周后完成 谢谢你的快
  • 在 iPad 上使用 OpenCV 避免碰撞

    我正在开展一个项目 需要使用 OpenCV 实现碰撞避免 这是在 iOS 上完成的 iOS 5 及以上版本即可 项目目标 这个想法是将 iPad 安装在汽车仪表板上并启动应用程序 应用程序应该从相机中抓取帧并进行处理 以检测汽车是否会与任何
  • 如何使用requirements.txt 在 Heroku python Web 应用程序中安装 Dlib?

    我构建了一个涉及机器学习的 Python Flask Web API 但在 Heroku 上部署它时遇到了很多挫折 问题是 我的应用程序依赖于 Dlib 一个库 我似乎找不到在我的 Heroku 服务器中安装的方法 我正在试图解决这个问题
  • 收据褪色部分可以恢复吗?

    我有一些包含一些扫描收据的文件 我需要使用 OCR 从中提取文本 由于收据上打印的文字在一段时间后会褪色 导致收据上的某些文字不清晰 影响OCR结果 褪色单词的一些示例 有什么方法可以恢复褪色的部分 以便提高 OCR 结果吗 我在OpenC
  • OpenCV findContours 破坏源图像

    我编写了一个在单通道空白图像中绘制圆形 直线和矩形的代码 之后 我只需找出图像中的轮廓 就可以正确获取所有轮廓 但找到轮廓后 我的源图像变得扭曲 为什么会出现这种情况 任何人都可以帮我解决这个问题 我的代码如下所示 using namesp
  • CvMat 和 Imread 与 IpImage 和 CvLoadImage

    使用 OpenCv 2 4 我有两个选项来加载图像 1 CvMat and Imread 2 IpImage and CvLoadImage 使用哪一个更好 我尝试将两者混合并最终出现段错误 imread返回一个Mat not CvMat
  • 使用 openCV 和 python 检测物体

    我正在尝试使用 OpenCV 和 Python 检测下图中的白点 我尝试使用函数 cv2 HoughCircles 但没有成功 我需要使用不同的方法吗 这是我的代码 import cv2 cv import numpy as np impo
  • 如何使用 OpenCV 检测图像帧中的对象?

    我正在使用 Raspberry Pi 开发一个漫游器 它将清扫房间并捡起掉落在地上的物体 为了检测物体 我使用了在流动站操作开始时拍摄的参考图像 以及每 10 秒单击一次的图像 新图像 为了确定图像帧是否发生变化 我在参考图像和新图像之间进
  • 在加载“cv2”二进制扩展期间检测到递归

    我有一个小程序 在 pyinstaller 编译后返回 opencv 错误 但无需编译即可工作 我在 Windows 10 上使用 Python 3 8 10 Program 导入 pyautogui将 numpy 导入为 np导入CV2
  • 我可以使用 openCV 比较两张不同图像上的两张脸吗?

    我对 openCV 很陌生 我看到它可以计算出脸部并返回一个矩形来指示脸部 我想知道 openCV 是否可以访问两张包含一张脸的图像 并且我希望 openCV 返回这两个人是否相同的可能性 Thanks OpenCV 不提供完整的人脸识别引
  • OpenCV 仅围绕大轮廓绘制矩形?

    第一次发帖 希望我以正确的方式放置代码 我正在尝试检测和计算视频中的车辆 因此 如果您查看下面的代码 我会在阈值处理和膨胀后找到图像的轮廓 然后我使用 drawContours 和矩形在检测到的轮廓周围绘制一个框 我试图在 drawCont
  • 多视图几何

    我从相距一定距离的两台相同品牌的相机捕获了两张图像 捕获了相同的场景 我想计算两个相机之间的现实世界旋转和平移 为了实现这一点 我首先提取了两张图像的 SIFT 特征并进行匹配 我现在有基本矩阵也单应性矩阵 然而无法进一步进行 有很多混乱
  • opencv形态扩张滤波器作为最大滤波器

    就像中值滤波器的定义一样 我可以将 最大滤波器 定义为局部窗口 例如dst x y max 3x3 局部窗口像素 但我在opencv中找不到这样的过滤器 最接近的是 dilate 函数 然后我使用 dilate 函数的默认配置 但结果不正确
  • 如何将输出视频保存到 OpenCV 中的文件中

    我想将输出视频保存到文件中而不是显示它并尝试使用 cvcaptureimage 但仍然无法获得结果 include
  • opencv 2.3.* 读取不工作

    我无法让 imread 工作 与这个人有同样的问题 OpenCV imwrite 2 2 在 Windows 7 上导致异常 并显示消息 OpenCV 错误 未指定错误 无法找到指定扩展名的编写器 https stackoverflow c
  • BASH 脚本编译多个 C++ 文件 - OpenCV

    请参见在C 和OpenCV中调用其他文件中的函数 https stackoverflow com questions 24442836 call functions in other files in c and opencv 对于最初的问
  • 使用 ffmpeg 或 OpenCV 处理原始图像

    看完之后维基百科页面 http en wikipedia org wiki Raw image format原始图像格式 是任何图像的数字负片 为了查看或打印 相机图像传感器的输出具有 进行处理 即转换为照片渲染 场景 然后以标准光栅图形格
  • minAreaRect OpenCV 返回的裁剪矩形 [Python]

    minAreaRectOpenCV 中返回一个旋转的矩形 如何裁剪矩形内图像的这部分 boxPoints返回旋转矩形的角点的坐标 以便可以通过循环框内的点来访问像素 但是在 Python 中是否有更快的裁剪方法 EDIT See code在
  • OpenCV 2.4.3 中的阴影去除

    我正在使用 OpenCV 2 4 3 最新版本 使用内置的视频流检测前景GMG http docs opencv org modules gpu doc video html highlight gmg gpu 3a 3aGMG GPU算法

随机推荐

  • PIXHAWK位置控制整体框架及期望推力向量转化成目标姿态旋转矩阵算法的深度解析

    万事开头难 xff0c 这是我的第一篇博客 谨以此将所学所悟记录下来 xff0c 以防遗失 xff0c 同时欢迎与大家进行技术交流 xff0c 共同学习 xff0c 共同进步 xff0c 玩的开心 xff01 这里的位置控制主要看PX4的m
  • 通过mavlink实现自主航线的过程笔记

    首先是mavlink的协议理解和移植过程 xff08 关于移植参考网址 xff1a https www cnblogs com lovechen p 6064802 html 和http www cnblogs com lovechen p
  • 李群,李代数的几何学心得总结

    大家好 xff0c 我是飞鸽 上了研究生后听到师兄讲到李群 李代数 xff0c 一脸的懵逼 xff0c 他说 我面试搞无人机的如果不知道这个肯定不要 于是乎 xff0c 我在图书馆查询了相关书籍 xff0c 可只有一些英文的书籍 xff0c
  • Hex HERE+ RTK GPS用于自创地面站的过程记录

    教研室本着便宜的原则买了一套hex HERE 43 RTK GPS 5000人民币 xff0c 水平定位精度5cm 垂直定位精度也还可以 xff0c 忘了当时测的是多少了 xff08 以前万级的RTK xff0c 定位精度厘米级 xff09
  • u-center配置GPS的使用说明

    承接上篇 HERE 43 RTK GPS用于自创地面站的过程记录 xff0c 本文主要讲解u center如何配置HERE 43 RTK GPS xff08 一般的gps同此方法一样 xff09 基本配置可先查看网址 xff1a http
  • 对云台、IMU、鲁棒性和硬件时间同步的理解

    作者 xff1a 朱金灿 来源 xff1a clever101的专栏 为什么大多数人学不会人工智能编程 xff1f gt gt gt slam是一门集硬件和软件的多科学技术 xff0c 涉及到很多技术术语 概念以及数学公式等等 下面我将结合
  • QT无人机地面站设计与制作随笔总结

    自己不是专职地面站设计 xff0c 这里简单将经验叙述下 xff08 另外把自己一直空缺着的QT分类补一篇文章 xff09 无论是处于地面站学习的过程还是做项目进行重新整体设计 xff0c 对于这种写代码的过程最好先在MindMaster中
  • 矩阵该左乘还是右乘

    首先说明一点 xff0c 矩阵具有几何意义 xff0c 对于这几何意义的具体理解可看B站的 线性代数的本质 的视频讲解 开始展开议题 为理解记忆这个问题 xff0c 可以从方向余弦矩阵开始着手 xff0c 方向余弦矩阵是由三次旋转矩阵顺序乘
  • 《绿皮书》影评

    以一种看经典的心态看这部电影 xff0c 确实这部电影没有让我失望 很经典 xff0c 很深刻 xff0c 可能每个人都有各自的认知 xff0c 各自的感受 而我感受到了主人公如同城市孤独人的孤独感 或许 xff0c 遭受肉体上的折磨和伤害
  • Cannot find module 'body-parser'

    bug Cannot find module 39 body parser 39 原因很明显 xff0c 这个模块是没有的 xff0c 查看node modules目录下 xff0c 确实没有 解决办法 xff1a 重新安装这个模块 xff
  • gitlab安装和使用--让我们的版本管理可视化

    一 安装环境 xff1a centos6 7实验2G xff0c 生产4G安装包下载 xff08 需要翻墙 xff0c 不然下载很慢 xff09 xff1a https packages gitlab com gitlab gitlab c
  • STM32CubeMX基本使用

    视频 https www bilibili com video BV11t41147wc from 61 search amp seid 61 9347368692610984203 前言 在配置好CubeMX之后 xff0c 就是新建工程
  • make 命令

    https www ibm com support knowledgecenter zh ssw aix 71 com ibm aix cmds3 make htm 用途 维护 更新和重新生成程序组 语法 make DVariable d
  • Opencv求轮廓的中心点坐标

    Opencv求轮廓的中心点坐标 思路 xff1a 1 通过findContours找出图片中的轮廓 xff1b 2 minAreaRect找到最小外接矩形 xff1b 3 得到最小外接矩形的中心点坐标作为轮廓的中心坐标 xff1b cv s
  • 路由器二次开发一步一步把工业路由器变成一个高端的可指定出网、节点和链路的路由器,包含详细过程及快捷脚本(四)

    路由器二次开发一步一步把工业路由器变成一个高端的可指定出网 节点和链路的路由器 包含详细过程及快捷脚本 四 如果没有 路由器 可以采用 废旧的电脑 详细环境部署参考第 一 篇文章 这里采用800米的工业路由器j1900进行二次定制开发 可以
  • 路由器二次开发一步一步把工业路由器变成一个高端的可指定出网、节点和链路的路由器,包含详细过程及快捷脚本(五)

    路由器二次开发一步一步把工业路由器变成一个高端的可指定出网 节点和链路的路由器 包含详细过程及快捷脚本 五 如果没有 路由器 可以采用 废旧的电脑 详细环境部署参考第 一 篇文章 这里采用800米的工业路由器j1900进行二次定制开发 可以
  • 应用windows批处理嵌套复制文件夹

    作者 xff1a 朱金灿 来源 xff1a clever101的专栏 为什么大多数人学不会人工智能编程 xff1f gt gt gt 应用windows批处理文件将一个文件夹下的多个子文件夹复制到另一个文件夹下 xff0c 代码如下 xff
  • FreeRTOS代码剖析之4:内存管理Heap_4.c

    FreeRTOS8 0 1内存管理的最后一个堆模型Heap 4 xff0c 貌似是在这一个版本才有的 所以找到的说明几乎没有 代码的开头注释也只是简单地说了一下实现了pvPortMalloc 和vPortFree 两个函数 xff0c 并且
  • 如何在 Ubuntu 上安装 Python 3.8

    Python是一种解释型 面向对象 动态数据类型的高级程序设计语言 Python是世界上使用最广泛的编程语言之一 xff0c 由于其简单易学的语法 xff0c Python是初学者和有经验的开发者的热门选择 xff0c Python是一种相
  • (三)OpenCV中的图像处理之轮廓

    注释 xff1a 本文翻译自OpenCV3 0 0 document gt OpenCV Python Tutorials xff0c 包括对原文档种错误代码的纠正 该章节分为以下四个小节 xff1a 一 Contours xff1a Ge