EmguCV学习笔记 VB.Net 6.2 轮廓处理

版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。

EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。

教程VB.net版本请访问: EmguCV学习笔记 VB.Net 目录-CSDN博客

教程C#版本请访问: EmguCV学习笔记 C# 目录-CSDN博客

笔者的博客网址:https://blog.csdn.net/uruseibest

教程配套文件及相关说明以及如何获得pdf教程和代码,请移步: EmguCV学习笔记

学习VB.Net知识,请移步: vb.net 教程 目录_vb中如何用datagridview-CSDN博客

学习C#知识,请移步: C# 教程 目录_c#教程目录-CSDN博客

6.2 轮廓处理

6.2.1 VectorOfPoint

VectorOfPoint是EmguCV中用于存储和操作点数可变的点集(Point)的类,它可以用来存储任意数量的点。在前面的章节中介绍过VectorOf~这样的类型。这里单独介绍一下VectorOfPoint。

使用VectorOfPoint类可以方便地进行点集的存储和操作,是在EmguCV中进行图像处理和分析的重要工具之一。在实际中,VectorOfPoint可以理解为很多点的集合,比如图像中的某个填充矩形,它就是由很多点组合在一起的,可以认为这个矩形就是一个VectorOfPoint。

VectorOfPoint类提供了一系列方法和属性,用于对点集进行增删改查、排序、遍历等操作,方便进行图像处理和分析。

1、创建VectorOfPoint对象

创建VectorOfPoint对象的方式如下:

Dim vop As New VectorOfPoint

2、向VectorOfPoint对象中增加点

通过Push方法向VectorOfPoint对象中增加点(这里需要传入点数组),例如:

vop.Push(New Point() {New Point(10, 20), New Point(30, 30)})

3、从VectorOfPoint对象中获取点

可以通过索引器或ToArray方法从VectorOfPoint对象中获取指定位置或所有位置的点,例如:

Dim p As Point = vop(0)

Dim arrP() As Point = vop.ToArray

4、修改VectorOfPoint对象中的点

非常遗憾的是,没有提供直接修改的方法。

5、获取VectorOfPoint中的点的数量

可以通过Length属性获取VectorOfPoint中点的数量。

6、清除VectorOfPoint对象中的点

可以通过Clear方法实现。

其它**VectorOf~**开头的类型,请参考VectorOfPoint。

6.2.2 VectorOfVectorOfPoint

VectorOfVectorOfPoint是EmguCV中用于存储和操作多个点集(VectorOfPoint)的类,它也是一个泛型类,可以用来存储任意数量的点集。该类提供了一系列方法和属性,用于对点集进行增删改查、排序、遍历等操作,方便进行图像处理和分析。

在实际中,VectorOfVectorOfPoint可以理解为很多VectorOfPoint的集合,比如图像中有矩形、圆形、多边形等,它们分别是由很多点组合在一起的,即这些图形自身是一个VectorOfPoint,而VectorOfVectorOfPoint就包含(可能是全部包含,也可能是部分包含)多个这样的VectorOfPoint。

VectorOfVectorOfPoint类的操作和VectorOfPoint类似,这里不再累述。

其它**VectorOfVectorOf~**开头的类型,请参考VectorOfVectorOfPoint。

6.2.3 轮廓查找FindContours

通过CvInvoke.FindContours方法,可以在二值图像中查找轮廓,该方法声明如下:

Public Shared Sub FindContours(image As Emgu.CV.IInputOutputArray, contours As Emgu.CV.IOutputArray, hierarchy As Emgu.CV.IOutputArray, mode As Emgu.CV.CvEnum.RetrType, method As Emgu.CV.CvEnum.ChainApproxMethod, Optional offset As System.Drawing.Point = Nothing)

参数说明:

  1. image:需要进行轮廓查找的二值图像。
  2. contours:存储轮廓的数组,这是一个VectorOfVectorOfPoint类型。
  3. hierarchy:存储轮廓的层级信息,它是一个包含四个整数的数组,其结构为:

同层下一个轮廓索引-同层上一个轮廓索引-第一个子轮廓索引-父轮廓索引

通常使用VectorOfRect来存储这个层级结构信息,以上结构信息对应Rect的:

X-Y-Width-Height

  1. mode:轮廓查找的模式。这是一个RetrType枚举类型,包含以下成员:
    1. List:提取所有轮廓,不建立层级关系。
    2. Ccomp:提取所有轮廓,并建立两层层级关系。
    3. External:只提取最外层的轮廓。
    4. Tree:提取所有的轮廓并建立完整的层级关系。
  2. method:轮廓近似的方法。这是一个ChainApproxMethod枚举类型,主要成员:
    1. ChainCode:表示使用链码近似算法,该算法使用像素点的坐标差异来表示轮廓的形状。计算复杂度较低。
    2. ChainApproxNone:表示不使用近似算法,直接使用所有像素点来表示轮廓的形状。计算复杂度较低。
    3. ChainApproxSimple:表示使用简单的近似算法,该算法使用线段连接像素点来近似轮廓的形状。计算复杂度较高。
    4. ChainApproxTc89L1:表示使用Teh-Chin链码近似算法之一,该算法可以更加准确地表示轮廓的形状。计算复杂度较高。
    5. ChainApproxTc89Kcos:表示使用Teh-Chin链码近似算法之一,该算法可以更加准确地表示轮廓的形状。计算复杂度较高。
  3. offset:输入参数,表示轮廓坐标的偏移量。

具体代码参看6.2.4 节【轮廓绘制DrawContours】

6.2.4 轮廓绘制DrawContours

在Emgu.CV中,CvInvoke.DrawContours函数用于绘制轮廓。它的用法如下:

Public Shared Sub DrawContours(image As Emgu.CV.IInputOutputArray, contours As Emgu.CV.IInputArrayOfArrays, contourIdx As Integer, color As Emgu.CV.Structure.MCvScalar, Optional thickness As Integer = 1, Optional lineType As Emgu.CV.CvEnum.LineType = 8, Optional hierarchy As Emgu.CV.IInputArray = Nothing, Optional maxLevel As Integer = 2147483647, Optional offset As System.Drawing.Point = Nothing)

参数说明:

  1. contours:要绘制的轮廓数组,这是一个VectorOfVectorOfPoint类型。对应使用FindContours方法获得的contours。
  2. contourIdx:要绘制的轮廓的索引。传入-1表示绘制所有的轮廓。
  3. color:绘制轮廓的颜色。可以使用MCvScalar结构指定颜色值。
  4. thickness:绘制轮廓的线条粗细。
  5. lineType:绘制轮廓的线条类型。
  6. hierarchy:轮廓的层级信息,如果不需要使用层级信息,可设置为Nothing。
  7. maxLevel:绘制轮廓的最大层级,默认值为Integer.MaxValue,表示绘制所有层级的轮廓。
  8. offset:绘制轮廓的坐标偏移量,默认值为Nothing。

【代码位置:frmChapter6】Button4_Click

'FindContours查找轮廓

Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click

'直接载入灰度图像

Dim m1 As New Mat("C:\learnEmgucv\shape1.jpg", CvEnum.ImreadModes.Grayscale)

ImageBox1.Image = m1

'二值化

Dim mid1 As New Mat

CvInvoke.Threshold(m1, mid1, 150, 255, ThresholdType.BinaryInv)

'最好使最外层为黑色。如果为白色,那么会将最外层算为一个轮廓。所以这里使用 ThresholdType.BinaryInv

ImageBox2.Image = mid1

Dim contours As New VectorOfVectorOfPoint

Dim hierarchy As New VectorOfRect

'同层下一个轮廓索引-同层上一个轮廓索引-第一个子轮廓索引-父轮廓索引

CvInvoke.FindContours(mid1, contours, hierarchy, RetrType.List, ChainApproxMethod.ChainApproxSimple)

Dim m2 As New Mat

m2 = m1.Clone()

m2.SetTo(New MCvScalar(0))

'绘制轮廓

For i As Integer = 0 To contours.Size - 1

CvInvoke.DrawContours(m2, contours, i, New MCvScalar(255), 2)

Next

ImageBox3.Image = m2

'输出轮廓层级信息

For i As Integer = 0 To hierarchy.Size - 1

Console.WriteLine(i & "-" & hierarchy(i).X & "-" & hierarchy(i).Y & "-" & hierarchy(i).Width & "-" & hierarchy(i).Height)

Next

End Sub

运行后如下图所示:

图6-4 绘制出的轮廓

图6-5 输出轮廓间关系

以下代码只绘制出最外层轮廓,由于不需要层级关系,所以hierarchy设置为nothing,mode设置为External。

【代码位置:frmChapter6】Button5_Click

'FindContours查找轮廓

Private Sub Button5_Click(sender As Object, e As EventArgs) Handles Button5.Click

Dim m1 As New Mat("C:\learnEmgucv\shape1.jpg", CvEnum.ImreadModes.Grayscale)

ImageBox1.Image = m1

Dim mid1 As New Mat

CvInvoke.Threshold(m1, mid1, 150, 255, ThresholdType.BinaryInv)

ImageBox2.Image = mid1

Dim contours As New VectorOfVectorOfPoint

Dim hierarchy As New VectorOfRect

'只提取最外层轮廓

CvInvoke.FindContours(mid1, contours, Nothing, RetrType.External, ChainApproxMethod.ChainApproxSimple)

'在全黑的图像上绘制出轮廓

Dim m2 As New Mat

m2 = m1.Clone()

m2.SetTo(New MCvScalar(0))

For i As Integer = 0 To contours.Size - 1

CvInvoke.DrawContours(m2, contours, i, New MCvScalar(255), 2)

Next

ImageBox3.Image = m2

End Sub

运行后如下图所示:

图6-6 绘制出的轮廓

6.2.5 轮廓面积 ContourArea

CvInvoke.ContourArea方法用于计算轮廓的面积。它的声明如下:

Public Shared Function ContourArea(contour As Emgu.CV.IInputArray, Optional oriented As Boolean = False) As Double

参数说明:

  1. contour:要计算面积的轮廓。这是一个VectorOfPoint类型。对应使用FindContours方法获得的contours所包含的成员。
  2. calculateArea:指定轮廓是否有方向,即面积是否为有向面积。如果为True,则计算的是有向面积,否则计算的是无向面积。有向面积是指轮廓线的外侧为正面,内侧为负面,因此有向面积可以为正数或负数;无向面积则是指轮廓线所包含的平面区域的面积,只能为正数。默认情况下,该参数为 false,即不考虑方向,总是返回面积的绝对值。

【代码位置:frmChapter6】Button6_Click

'按照面积进行筛选输出

Private Sub Button6_Click(sender As Object, e As EventArgs) Handles Button6.Click

Dim m1 As New Mat("C:\learnEmgucv\shape1.jpg", CvEnum.ImreadModes.Grayscale)

ImageBox1.Image = m1

'二值化

Dim mid1 As New Mat

CvInvoke.Threshold(m1, mid1, 150, 255, ThresholdType.BinaryInv)

ImageBox2.Image = mid1

Dim contours As New VectorOfVectorOfPoint

Dim hierarchy As New VectorOfRect

'查找轮廓

CvInvoke.FindContours(mid1, contours, hierarchy, RetrType.List, ChainApproxMethod.ChainApproxSimple)

Dim m2 As New Mat

m2 = m1.Clone()

m2.SetTo(New MCvScalar(0))

For i As Integer = 0 To contours.Size - 1

Dim carea As VectorOfPoint = contours(i)

'获得轮廓面积

Dim area As Double = CvInvoke.ContourArea(carea, False)

'符合条件时,绘制轮廓

If area < 10000 Then

CvInvoke.DrawContours(m2, contours, i, New MCvScalar(255), -1)

End If

Next

ImageBox3.Image = m2

End Sub

运行后如下图所示:

图6-7 输出面积小于10000的轮廓

6.2.6 轮廓周长 ArcLength

在Emgu.CV中,CvInvoke.ArcLength函数用于计算轮廓的弧长(周长)。它的用法如下:

Public Shared Function ArcLength(curve As Emgu.CV.IInputArray, isClosed As Boolean) As Double

参数说明:

  1. contour:要计算周长的轮廓。这是一个VectorOfPoint类型。对应使用FindContours方法获得的contours所包含的成员。
  2. closed:表示轮廓是否是闭合的。如果为True,返回轮廓的曲线长度;如果为False,返回轮廓的周长。

【代码位置:frmChapter6】Button7_Click

'轮廓的面积和周长

Private Sub Button7_Click(sender As Object, e As EventArgs) Handles Button7.Click

Dim m1 As New Mat("C:\learnEmgucv\shape1.jpg", CvEnum.ImreadModes.Grayscale)

ImageBox1.Image = m1

'二值化并取反

Dim mid1 As New Mat

CvInvoke.Threshold(m1, mid1, 150, 255, ThresholdType.BinaryInv)

ImageBox2.Image = mid1

Dim contours As New VectorOfVectorOfPoint

Dim hierarchy As New VectorOfRect

'查找轮廓

CvInvoke.FindContours(mid1, contours, hierarchy, RetrType.List, ChainApproxMethod.ChainApproxSimple)

Dim m2 As New Mat

m2 = m1.Clone()

m2.SetTo(New MCvScalar(0))

For i As Integer = 0 To contours.Size - 1

'绘制轮廓

CvInvoke.DrawContours(m2, contours, i, New MCvScalar(255), 2)

'获得面积

Dim area As Double = CvInvoke.ContourArea(contours(i), False)

'获得周长

Dim length As Double = CvInvoke.ArcLength(contours(i), True)

'输出面积和周长

Console.WriteLine(i & "-" & area & "-" & length)

Next

ImageBox3.Image = m2

End Sub

运行后如下图所示:

图6-8 输出轮廓的面积和周长

相关推荐
Das18 分钟前
【计算机视觉】05_不变性
人工智能·计算机视觉
OAK中国_官方16 分钟前
将Roboflow工作流引入OAK4:简化实时计算机视觉
人工智能·计算机视觉
步步为营DotNet26 分钟前
深度解析.NET中属性(Property)的幕后机制:优化数据访问与封装
java·算法·.net
智驱力人工智能40 分钟前
森林防火无人机火焰监测系统 构建“天空地”一体化智能防火体系 无人机火焰检测,支持红色火焰检测 城市高层建筑无人机火焰识别
人工智能·深度学习·opencv·算法·目标检测·无人机·边缘计算
Crazy Struggle2 小时前
一款轻量级 WinForm 开源控件库,让老界面秒变高颜值
.net·winform·ui控件库
棒棒的皮皮2 小时前
【OpenCV】Python图像处理形态学之腐蚀
图像处理·python·opencv·计算机视觉
智驱力人工智能2 小时前
无人机河道漂浮物检测 从项目构建到价值闭环的系统工程 无人机河道垃圾识别 农村河道漂浮物智能清理方案 无人机辅助河道清洁预警
opencv·算法·安全·yolo·目标检测·无人机·边缘计算
CoovallyAIHub2 小时前
从电影特效到体育科学,运动追踪只能靠“人眼”吗?
深度学习·算法·计算机视觉
CoookeCola3 小时前
无需抠图!Qwen-Image-Layered 一键分解图像图层,支持图层级精准编辑
论文阅读·深度学习·计算机视觉·ai作画·开源·视觉检测·aigc
十铭忘3 小时前
SAM2跟踪的理解12——mask decoder
人工智能·计算机视觉