【Emgu CV教程】10.9、轮廓的纵横比、面积比、坚硬性、等效直径、方向、掩码及像素点、最值点和它的位置、平均颜色、极值点


一、轮廓的纵横比(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); // 显示最终结果

代码输出结果如下所示:
在这里插入图片描述


原创不易,请勿抄袭。共同进步,相互学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值