版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。
教程VB.net版本请访问:EmguCV学习笔记 VB.Net 目录-CSDN博客
教程C#版本请访问:EmguCV学习笔记 C# 目录-CSDN博客
笔者的博客网址:https://blog.csdn.net/uruseibest
教程配套文件及相关说明以及如何获得pdf教程和代码(博客上的教程内容会和pdf教程一致,教程中也会包含所有代码),请移步:EmguCV学习笔记
4.5 像素距离和连通区域
4.5.1 DistanceTransform 像素距离
在VB.NET中使用EmguCV,可以通过`CvInvoke.DistanceTransform`函数进行距离变换操作。
public static void DistanceTransform(
IInputArray src,
IOutputArray dst,
IOutputArray labels,
DistType distanceType,
int maskSize,
DistLabelType labelType = DistLabelType.CComp
)
参数说明:
- src:输入图像,类型为单通道CV8U的Mat。
- dst:输出图像,类型为CV8U或者CV32F的Mat。与输入图像具有相同的尺寸和类型,用于存储距离变换结果,它的每个像素的值表示该像素与0像素的最小距离。
- labels:输出图像,类型为CV32S的Mat。用于存储每个前景像素所属的连通域标签。
- distanceType:指定计算距离所使用的度量类型,类型为DistanceType,主要成员包括:
- L1:曼哈顿距离,也称为城市街区距离,表示两点之间在水平和垂直方向上的距离之和。
- L2:欧几里得距离,表示两点之间的直线距离,即两点之间的最短距离。
- C:Chebyshev距离,表示两点之间在水平、垂直和对角线方向上的最大距离。
- maskSize:指定掩膜大小,即要使用的邻域尺寸,常见的有3、5、7等,3表示3x3的掩膜,5表示5x5的掩膜,以此类推。
- labelType:指定计算连通域标签所使用的算法类型,可选值为DistLabelType.CComp或DistLabelType.Pixel。
【代码位置:frmChapter4】Button18_Click、printMatSingle
//DistanceTransform
private void Button18_Click(object sender, EventArgs e)
{
byte[,] bte = new byte[,]{
{ 0, 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 1, 1, 1, 1, 1, 1, 1, 0},
{ 0, 1, 1, 1, 1, 1, 1, 1, 0},
{ 0, 1, 1, 1, 1, 1, 1, 1, 0},
{ 0, 1, 1, 1, 1, 1, 1, 1, 0},
{ 0, 1, 1, 1, 1, 1, 1, 1, 0},
{ 0, 1, 1, 1, 1, 1, 1, 1, 0},
{ 0, 1, 1, 1, 1, 1, 1, 1, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
Matrix<byte> matr = new Matrix<byte>(bte);
Mat m = new Mat(matr.Size, DepthType.Cv8U, 1);
m = matr.Mat;
Mat mdistance = new Mat(m.Size, DepthType.Cv8U, 1);
Mat labels1 = new Mat();
CvInvoke.DistanceTransform(m, mdistance, labels1, DistType.L2, 3);
//如果参数labels使用nothing,那么 mdistance 中的元素类型是single
//CvInvoke.DistanceTransform(m, mdistance, Nothing, DistType.L2, 3);
printMatSingle(mdistance);
}
//输出single类型Mat的元素的值
private void printMatSingle(Mat m)
{
Matrix<Single> matr = new Matrix<Single>(m.Rows, m.Cols, m.NumberOfChannels);
m.CopyTo(matr);
for(int i = 0;i< matr.Rows;i++)
{
for(int j = 0;j< matr.Cols;j++)
{
Console.Write(matr[i, j]);
if (j != matr.Cols - 1)
Console.Write(",");
}
Console.WriteLine();
}
}
运行后如下图所示:
图4-18 像素间距离
4.5.2 connectedComponents 图像连通区域
ConnectedComponents方法是EmguCV中用于实现连通区域分析的函数。在图像处理中,经常需要对图像中的连通区域进行分析和处理,如区域标记、分割、形状识别等。ConnectedComponents函数可以将图像中的连通区域分析出来,并返回每个连通区域的标记和属性信息。声明如下:
public static int ConnectedComponents(
IInputArray image,
IOutputArray labels,
LineType connectivity = LineType.EightConnected,
DepthType labelType = DepthType.Cv32S,
ConnectedComponentsAlgorithmsTypes cclType = ConnectedComponentsAlgorithmsTypes.Default
)
参数说明:
- image:输入图像,类型为Mat。
- labels:输出一个单通道Mat。连通区域标记的结果将存储在该Mat中。背景一般存储为0,相同连通区域的值相同。
- connectivity:连通区域的连接方式,类型为LineType。指定连通区域的连接方式,常见的有4连通(LineType.FourConnected)和8连通(LineType. Eight Connected)。
- ltype:labels图像的深度类型,类型为DepthType。可以选择浮点型(DepthType.Cv32F)或整型(DepthType.Cv32S)。
- ccltype:连通区域标签类型,类型为ConnectedComponentsAlgorithmsTypes,这是一个枚举类型,它表示连通区域分析算法的类型。连通区域分析是一种将二值图像中的像素划分为不同连通区域的技术。在EmguCV中,提供了多种不同的算法来进行连通区域分析,每种算法都有其特点和适用范围。ConnectedComponentsAlgorithmsTypes枚举类型定义了可用的连通区域分析算法类型,主要包括:
- CCL_GRANA:Grana算法,一种基于扫描线的算法,适用于较小的图像。
- CCL_WU:Wu算法,一种基于扫描线的算法,适用于较大的图像。
- CCL_DEFAULT:默认算法,使用基于扫描线的算法,但会根据图像大小和特征动态选择算法。
返回值:
分析出的连通区域的数量。
【代码位置:frmChapter4】Button19_Click
//connectedComponents
private void Button19_Click(object sender, EventArgs e)
{
Mat m1 = new Mat("C:\\learnEmgucv\\shape.jpg", Emgu.CV.CvEnum.ImreadModes.Grayscale);
ImageBox1.Image = m1;
//二值化
Mat mid1 = new Mat();
CvInvoke.Threshold(m1, mid1, 150, 255, ThresholdType.BinaryInv);
//连通区域分析结果存储到result中
Mat result = new Mat();
int num;
//获得连通区域
num = CvInvoke.ConnectedComponents(mid1, result, LineType.FourConnected, DepthType.Cv16U);
Console.WriteLine("连通区域数量:" + num);
//在本代码中使用的图像,获得的连通区域为12(含背景),result中像素的值从0-11,
//所以这里看到的是全黑色的图
ImageBox2.Image = result;
Image<Gray, byte> img = new Image<Gray, byte>(m1.Size);
img = result.ToImage<Gray, byte>();
Image<Bgr, byte> img1 = new Image<Bgr, byte>(m1.Size);
int maxsize = 0;
//使用循环遍历分析出的连通区域,这里对非背景(0)的区域着相同颜色
for(int i = 0;i< img.Rows;i++)
{
for(int j = 0;j< img.Cols;j++)
{
//0的区域作为背景
if( img.Data[i, j, 0] == 0 )
{
img1.Data[i, j, 0] = 0;
img1.Data[i, j, 1] = 0;
img1.Data[i, j, 2] = 0;
}
else
{
Console.WriteLine(img.Data[i, j, 0]);
maxsize = (maxsize > img.Data[i, j, 0]) ? maxsize: img.Data[i, j, 0];
img1.Data[i, j, 0] = 0;
img1.Data[i, j, 1] = 255;
img1.Data[i, j, 2] = 255;
}
}
}
ImageBox3.Image = img1;
//为什么差1,因为包含了背景部分。
MessageBox.Show("num " + num + " maxsize " + maxsize);
}
运行后如下图所示:
图4-19 连通区域
4.5.3 connectedComponentsWithStats
ConnectedComponentsWithStats方法是用于进行连通区域分析的函数,它可以分析二值图像中的连通区域,并返回每个连通区域的标记值、面积、重心和边界框等信息。声明如下:
public static int ConnectedComponentsWithStats(
IInputArray image,
IOutputArray labels,
IOutputArray stats,
IOutputArray centroids,
LineType connectivity = LineType.EightConnected,
DepthType labelType = DepthType.Cv32S,
ConnectedComponentsAlgorithmsTypes cclType = ConnectedComponentsAlgorithmsTypes.Default
)
参数说明(其余参数请参看4.4.2节【connectedComponents 图像连通区域】):
- stats:统计信息,类型为Mat。用于存储每个连通区域的统计信息,如面积、位置、宽度、高度等。它是一个5列的矩阵, 每一行对应一个连通区域,分别为连通区域外接矩形的x、y、width、height和面积。
- centroids:质心坐标,类型为Mat。用于存储每个连通组件的质心坐标。
【代码位置:frmChapter4】Button20_Click
//连通区域的统计信息自定义结构,
//包含x、y、width、height和面积,
//由于Rectangle已经包含x、y、width、height,
//所以这里的结构是Rectangle和Integer
public struct sstats
{
public Rectangle rec;
public int area;
}
//连通区域分析
//注意:白色255代表有数据,黑色0代表没有数据,
//所以要根据图像实际情况,确保图像输入之前转换成黑底白图
private void Button20_Click(object sender, EventArgs e)
{
Mat m1 = new Mat("C:\\learnEmgucv\\shape.jpg", Emgu.CV.CvEnum.ImreadModes.Grayscale);
ImageBox1.Image = m1;
//二值化
Mat mid1 = new Mat();
//这里过滤了一部分连通区域
CvInvoke.Threshold(m1, mid1, 150, 255, ThresholdType.BinaryInv);
ImageBox2.Image = mid1;
Mat result = new Mat();
Mat mstats = new Mat();
Mat mcentroids = new Mat();
int num = CvInvoke.ConnectedComponentsWithStats(mid1, result, mstats, mcentroids, LineType.EightConnected, DepthType.Cv16U);
sstats[] stats = new sstats[num];
//拷贝到sstats数组
mstats.CopyTo(stats);
MCvPoint2D64f[] centroids = new MCvPoint2D64f[num];
//拷贝到点数组
mcentroids.CopyTo(centroids);
//载入彩色图像
Mat m2 = new Mat("C:\\learnEmgucv\\shape.jpg", Emgu.CV.CvEnum.ImreadModes.AnyColor);
//总的面积,算下来就是mid1或者result点的数量。
int allpoints = 0;
for(int i = 0;i< num;i++)
{
//画出每个连通区域的中心点
CvInvoke.Circle(m2, new Point((int)centroids[i].X, (int)centroids[i].Y), 10, new MCvScalar(0, 255, 255), -1);
//画出每个连通区域的外接矩形
CvInvoke.Rectangle(m2, stats[i].rec, new MCvScalar(255, 0, 0), 2);
//在本例中,使用的图像高度486,宽度537,即总的像素数量486*537=260982
//这里计算出的面积(总的点的数量)=260982
allpoints += stats[i].area;
}
ImageBox3.Image = m2;
}
运行后如下图所示:
图4-20 绘制连通区域的中心和外接矩形