文章目录
- 一、轮廓的纵横比(Aspect Ratio)
- 二、轮廓的面积比(Extent)
- 三、轮廓的坚硬性(Solidity)
- 四、轮廓的等效直径(Equivalent Diameter)
- 五、轮廓的方向(Orientation)
- 六、轮廓的掩码以及像素点(Mask and Pixel Points)
- 1.原始素材
- 2.代码
- 3.运行结果
- 七、轮廓的值点和它的位置
- 八、轮廓的平均颜色或平均强度
- 九、轮廓的极值点
一、轮廓的纵横比(Aspect Ratio)
轮廓的纵横比,就是轮廓的最小正外接矩形的 宽度/高度,首先利用
Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
计算出最小正外接矩形,再用
float aspectRatio = (float)rect.Width / (float)rect.Height;
计算出轮廓的纵横比。如果有多个轮廓呢,很简单,加上一个循环遍历:
for (int i = 0; i < contours.Size; i++)
{
Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
float aspectRatio = (float)rect.Width / (float)rect.Height;
Console.WriteLine("轮廓" + i + "的纵横比是: " + aspectRatio);
}
二、轮廓的面积比(Extent)
轮廓的面积比(Extent),就是轮廓的面积与其最小正外接矩形的比值。
for (int i = 0; i < contours.Size; i++)
{
double area = CvInvoke.ContourArea(contours[i]);
Rectangle rect = CvInvoke.BoundingRectangle(contours[key]);
float extent = (float)it.Value / ((float)rect.Width * (float)rect.Height);
Console.WriteLine("轮廓" + i + "的面积比是: " + extent);
}
三、轮廓的坚硬性(Solidity)
轮廓的坚硬性(Solidity),就是轮廓的面积与其凸包面积的比值。
VectorOfInt hull = new VectorOfInt(); // 存储凸包点索引
for (int i = 0; i < contours.Size; i++)
{
double area = CvInvoke.ContourArea(contours[i]);
Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
float extent = (float)it.Value / ((float)rect.Width * (float)rect.Height);
VectorOfPoint hullPoint = new VectorOfPoint();
System.Drawing.Point[] pt = new System.Drawing.Point[hull.Size];
MCvScalar randomColor = new MCvScalar(random.Next(0, 255), random.Next(0, 255), random.Next(0, 255));
for (int i = 0; i < hull.Size; i++)
{
pt[i] = contours[i][hull[i]];
if (i != hull.Size - 1)
{
CvInvoke.Line(dstMat, contours[i][hull[i]], contours[i][hull[i + 1]], randomColor, 2);
}
else
{
CvInvoke.Line(dstMat, contours[i][hull[i]], contours[i][hull[0]], randomColor, 2);
}
}
hullPoint.Push(pt);
double areaHull = CvInvoke.ContourArea(hullPoint);
float solidity = (float)it.Value / (float)areaHull;
Console.WriteLine("轮廓" + i + "的坚硬性是: " + solidity );
}
四、轮廓的等效直径(Equivalent Diameter)
轮廓的等效直径(Equivalent Diameter),是面积等于轮廓面积的圆的直径,不是最小外接圆的直径。
for (int i = 0; i < contours.Size; i++)
{
double area = CvInvoke.ContourArea(contours[i]);
double equivalentDiameter = Math.Sqrt(area / Math.PI) * 2;
Console.WriteLine("轮廓" + i + "的等效直径是: " + equivalentDiameter);
}
五、轮廓的方向(Orientation)
轮廓的方向(Orientation),就是物体指向的角度,通过FitEllipse()函数得到轮廓的最小外接椭圆,椭圆的旋转角度就是轮廓的方向,还能得出椭圆的中心点和轴长。
详细用法见
【Emgu CV教程】10.8、轮廓的最小外接圆和最小外接椭圆
六、轮廓的掩码以及像素点(Mask and Pixel Points)
轮廓的掩码是轮廓区域的掩码图像,背景为黑色,轮廓区域为白色填充,再利用FindNonZero()函数寻找非零像素点。
1.原始素材
原始素材srcMat如下图:
2.代码
代码如下:
Mat tempMat = srcMat.Clone();
Mat dstMat = srcMat.Clone();
Mat gray = new Mat();
int threshold = 40;
// 转成灰度图再二值化
CvInvoke.CvtColor(tempMat, gray, ColorConversion.Bgr2Gray);
CvInvoke.Threshold(gray, gray, threshold, 255, ThresholdType.Binary);
CvInvoke.Imshow("Gray and threshold", gray);
// 定义轮廓集合
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
VectorOfRect hierarchy = new VectorOfRect();
CvInvoke.FindContours(gray, contours, hierarchy, RetrType.External, ChainApproxMethod.ChainApproxNone);
// 在黑色图中画出所有轮廓和掩码图
Mat allContours1 = new Mat(new System.Drawing.Size(gray.Cols, gray.Rows), DepthType.Cv8U, 1);
Mat allContours2 = Mat.Zeros(gray.Rows, gray.Cols, DepthType.Cv8U, 1);
allContours1.SetTo(new MCvScalar(0, 0, 0));
CvInvoke.DrawContours(allContours1, contours, -1, new MCvScalar(255, 255, 255), 1);
CvInvoke.DrawContours(allContours2, contours, -1, new MCvScalar(255, 255, 255), -1);
// 查找轮廓的点
VectorOfPoint nonZeroCoordinates = new VectorOfPoint();
CvInvoke.FindNonZero(allContours2, nonZeroCoordinates);
List<string> pointsList = new List<string>();
for (int i = 0; i < nonZeroCoordinates.Size; i++)
{
pointsList.Insert(0, "Point" + i + "(" + nonZeroCoordinates[i].X + " , " + nonZeroCoordinates[i].Y + ")");
}
ListBoxPoints.ItemsSource = pointsList;
CvInvoke.PutText(dstMat, "Contours number:" + contours.Size, new System.Drawing.Point(20, 20), FontFace.HersheyComplex, 0.5, new Bgr(0, 255, 0).MCvScalar, 1, LineType.EightConnected, false);
CvInvoke.DrawContours(dstMat, contours, -1, new MCvScalar(255, 0, 0), 2, LineType.EightConnected, hierarchy);
CvInvoke.Imshow("All contours," + allContours1.Size.ToString(), allContours1);
CvInvoke.Imshow("Contours mask," + allContours2.Size.ToString(), allContours2);
CvInvoke.Imshow("Final result image, " + dstMat.Size.ToString(), dstMat); // 显示最终结果
3.运行结果
如下所示:
掩码图的非零点,就是pointsList。
七、轮廓的值点和它的位置
轮廓最值点和它的位置,要使用MinMaxLoc()函数进行查找。先求出二值化图像gray,然后执行下面代码:
double minValue = 0; // 轮廓的最大值
double maxValue = 0; // 轮廓的最小值
System.Drawing.Point minLocation = new System.Drawing.Point(); // 轮廓最大值的位置
System.Drawing.Point maxLocation = new System.Drawing.Point(); // 轮廓最小值的位置
CvInvoke.MinMaxLoc(gray, ref minValue, ref maxValue, ref minLocation, ref maxLocation, allContoursMask);
八、轮廓的平均颜色或平均强度
用Mean(()函数求轮廓的平均颜色,原始图像srcMat还是采用上面的米老鼠图片,求轮廓平均颜色的代码如下:
Mat tempMat = srcMat.Clone();
Mat dstMat = srcMat.Clone();
Mat gray = new Mat();
int threshold = Convert.ToInt16(TextBoxThreshold.Text.Trim().ToString());
// 转成灰度图再二值化
CvInvoke.CvtColor(tempMat, gray, ColorConversion.Bgr2Gray);
CvInvoke.Threshold(gray, gray, threshold, 255, ThresholdType.Binary);
CvInvoke.Imshow("Gray and threshold", gray);
// 定义轮廓集合
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
VectorOfRect hierarchy = new VectorOfRect();
CvInvoke.FindContours(gray, contours, hierarchy, RetrType.External, ChainApproxMethod.ChainApproxNone);
// 在一张黑色图中画出所有轮廓
Mat allContoursMask = new Mat(new System.Drawing.Size(gray.Cols, gray.Rows), DepthType.Cv8U, 1);
allContoursMask.SetTo(new MCvScalar(0, 0, 0));
CvInvoke.DrawContours(allContoursMask, contours, -1, new MCvScalar(255, 255, 255), -1);
// 各个通道的平均值
MCvScalar averageColor = CvInvoke.Mean(tempMat, allContoursMask);
CvInvoke.PutText(dstMat, "Contours number:" + contours.Size, new System.Drawing.Point(20, 20), FontFace.HersheyComplex, 0.5, new Bgr(0, 255, 0).MCvScalar, 1, LineType.EightConnected, false);
CvInvoke.PutText(dstMat, "Blue channel, contours average:" + averageColor.V0.ToString("0"), new System.Drawing.Point(20, 40), FontFace.HersheyComplex, 0.5, new Bgr(0, 255, 0).MCvScalar, 1, LineType.EightConnected, false);
CvInvoke.PutText(dstMat, "Green channel, contours average:" + averageColor.V1.ToString("0"), new System.Drawing.Point(20, 60), FontFace.HersheyComplex, 0.5, new Bgr(0, 255, 0).MCvScalar, 1, LineType.EightConnected, false);
CvInvoke.PutText(dstMat, "Red channel, contours average:" + averageColor.V2.ToString("0"), new System.Drawing.Point(20, 80), FontFace.HersheyComplex, 0.5, new Bgr(0, 255, 0).MCvScalar, 1, LineType.EightConnected, false);
CvInvoke.DrawContours(dstMat, contours, -1, new MCvScalar(255, 0, 0), 2, LineType.EightConnected, hierarchy);
CvInvoke.Imshow("All contours mask, " + allContoursMask.Size.ToString(), allContoursMask);
CvInvoke.Imshow("Final result image, " + dstMat.Size.ToString(), dstMat); // 显示最终结果
结果如下所示:
九、轮廓的极值点
轮廓极值点是指轮廓的上、下、左、右四个方向的极值,下面代码中,分别用红、绿、蓝、黄标注。
Mat tempMat = srcMat.Clone();
Mat dstMat = srcMat.Clone();
Mat gray = new Mat();
int threshold = 40;
// 转成灰度图再二值化
CvInvoke.CvtColor(tempMat, gray, ColorConversion.Bgr2Gray);
CvInvoke.Threshold(gray, gray, threshold, 255, ThresholdType.Binary);
CvInvoke.Imshow("Gray and threshold", gray);
// 定义轮廓集合
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
VectorOfRect hierarchy = new VectorOfRect();
CvInvoke.FindContours(gray, contours, hierarchy, RetrType.External, ChainApproxMethod.ChainApproxNone);
// 在一张黑色图中画出所有轮廓
Mat allContours = new Mat(new System.Drawing.Size(gray.Cols, gray.Rows), DepthType.Cv8U, 1);
allContours.SetTo(new MCvScalar(0, 0, 0));
CvInvoke.DrawContours(allContours, contours, -1, new MCvScalar(255, 255, 255), 1);
// 按照面积筛选,太小的轮廓不计算
Dictionary<int, double> dict = new Dictionary<int, double>();
if (contours.Size > 0)
{
for (int i = 0; i < contours.Size; i++)
{
double area = CvInvoke.ContourArea(contours[i]);
Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
if (rect.Width > 50 && rect.Height > 20 && area < 3000000)
{
dict.Add(i, area);
}
}
}
// 定义极值点
var item = dict.OrderByDescending(v => v.Value); // v.Value就代表面积,是降序排列
int index = 1;
System.Drawing.Point pointUp = new System.Drawing.Point
{
X = 0,
Y = gray.Rows
};
System.Drawing.Point pointDown = new System.Drawing.Point
{
X = 0,
Y = 0
};
System.Drawing.Point pointLeft = new System.Drawing.Point
{
X = gray.Cols,
Y = 0
};
System.Drawing.Point pointRight = new System.Drawing.Point
{
X = 0,
Y = 0
};
// 绘制极值点
CvInvoke.DrawContours(dstMat, contours, -1, new MCvScalar(0, 255, 0), 2, LineType.EightConnected, hierarchy);
foreach (var it in item)
{
int key = it.Key;
Rectangle rect = CvInvoke.BoundingRectangle(contours[key]);
// 遍历每个轮廓上的点
for (int i = 0; i < contours.Size; i++)
{
pointUp.X = 0;
pointUp.Y = gray.Rows;
pointDown.X = 0;
pointDown.Y = 0;
pointLeft.X = gray.Cols;
pointLeft.Y = 0;
pointRight.X = 0;
pointRight.Y = 0;
for (int j = 0; j < contours[i].Size; j++)
{
if (contours[i][j].Y < pointUp.Y)
{
pointUp.Y = contours[i][j].Y;
pointUp.X = contours[i][j].X;
}
if (contours[i][j].Y > pointDown.Y)
{
pointDown.Y = contours[i][j].Y;
pointDown.X = contours[i][j].X;
}
if (contours[i][j].X < pointLeft.X)
{
pointLeft.X = contours[i][j].X;
pointLeft.Y = contours[i][j].Y;
}
if (contours[i][j].X > pointRight.X)
{
pointRight.X = contours[i][j].X;
pointRight.Y = contours[i][j].Y;
}
}
CvInvoke.Circle(dstMat, pointUp, 8, new MCvScalar(0, 0, 255), 2);
CvInvoke.Circle(dstMat, pointDown, 8, new MCvScalar(0, 255, 0), 2);
CvInvoke.Circle(dstMat, pointLeft, 8, new MCvScalar(255, 0, 0), 2);
CvInvoke.Circle(dstMat, pointRight, 8, new MCvScalar(0, 255, 255), 2);
}
CvInvoke.PutText(dstMat, "Contour:" + index.ToString(), new System.Drawing.Point(rect.X, rect.Y - 10), FontFace.HersheyComplex, 0.5, new Bgr(0, 255, 0).MCvScalar, 1, LineType.EightConnected, false);
index++;
}
CvInvoke.PutText(dstMat, "Contours number:" + item.Count(), new System.Drawing.Point(20, 20), FontFace.HersheyComplex, 0.5, new Bgr(0, 255, 0).MCvScalar, 1, LineType.EightConnected, false);
CvInvoke.Imshow("All contours, " + allContours.Size.ToString(), allContours);
CvInvoke.Imshow("Final result image, " + dstMat.Size.ToString(), dstMat); // 显示最终结果
代码输出结果如下所示:
原创不易,请勿抄袭。共同进步,相互学习。