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

文章目录

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

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


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/495104.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring实战:采用Spring配置文件管理Bean

文章目录 一、Spring框架概述二、实战&#xff1a;采用Spring配置文件管理Bean&#xff08;一&#xff09;创建Jakarta EE项目&#xff08;二&#xff09;添加Spring依赖&#xff08;三&#xff09;创建杀龙任务类&#xff08;四&#xff09;创建勇敢骑士类&#xff08;五&…

基于springboot实现校园周边美食探索及分享平台系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现园周边美食探索及分享平台系统演示 摘要 美食一直是与人们日常生活息息相关的产业。传统的电话订餐或者到店消费已经不能适应市场发展的需求。随着网络的迅速崛起&#xff0c;互联网日益成为提供信息的最佳俱渠道和逐步走向传统的流通领域&#xff0c;传统的…

uni-app(使用阿里图标)

1.注册阿里矢量图标库 注册阿里图标库账号并登录&#xff0c;https://www.iconfont.cn/ 2.加入购物车 搜索适合自己的图标&#xff0c;加入购物车&#xff0c;如下图&#xff1a; 3.加入项目 我的->资源管理->我的项目->创建项目&#xff0c;然后返回购物车&#…

SpringCloud学习笔记二:服务间调用

微服务中&#xff0c;很多服务系统都在独立的进程中运行&#xff0c;通过各个服务系统之间的协作来实现一个大项目的所有业务功能。服务系统间 使用多种跨进程的方式进行通信协作&#xff0c;而RESTful风格的网络请求是最为常见的交互方式之一。 spring cloud提供的方式&#…

工厂数字化看板是什么?部署工厂数字化看板有什么作用?

随着工业4.0时代的来临&#xff0c;数字化转型已成为制造业发展的必然趋势。在这个背景下&#xff0c;工厂数字化看板作为一种高效的信息展示与管理工具&#xff0c;正逐渐受到越来越多企业的青睐。那么&#xff0c;什么是工厂数字化看板&#xff1f;部署工厂数字化看板又有哪些…

真没想到,SQL注入漏洞的这么大,竟然导致1400万名俄罗斯大学毕业生信息泄露

不知道各位面试时&#xff0c;有没有相关的面试官有没有问到这样的问题&#xff0c;什么是sql注入&#xff0c;sql注入的危害是什么&#xff0c;mybatis的#与$的区别是什么等等&#xff0c;我想很多人都知道使用mybatis的#去规避sql注入&#xff0c;但是很多人不知道其原理&…

备份SQLserver数据库到本地位置

怎么选择合适的数据库备份方案&#xff1f; 有人可能会说SSMS&#xff0c;确实&#xff0c;SSMS作为一个微软官方提供的SQLserver数据库管理工具&#xff0c;是可以帮助我们完成对数据库的备份还原任务的&#xff0c;但是它也有一些局限性&#xff0c;比如不能进行批量化的备份…

LLM应用:Prompt flow vs LangChain

背景 Prompt flow和LangChain都是LLM时代&#xff0c;为高效地构建LLM应用而生。 Prompt flow是Microsoft开源的&#xff0c;其诞生时&#xff0c;LangChain已经很有名气了。 所以作为后生的Prompt flow会为我们带来哪些新的东西呢&#xff1f; ​​​​​​​ Prompt flo…

JTW——01,简述、对比

简述、对比 一、jwt跟token的区别二、什么是jwt三、jwt能做什么四、传统的session认证五、Jwt认证 一、jwt跟token的区别 https://blog.csdn.net/wangxinxinsj/article/details/132746876 二、什么是jwt 三、jwt能做什么 四、传统的session认证 五、Jwt认证

Docker搭建LNMP环境实战(06):Docker及Docker-compose常用命令

Docker搭建LNMP环境实战&#xff08;06&#xff09;&#xff1a;Docker及Docker-compose常用命令 此处列举了docker及docker-compose的常用命令&#xff0c;一方面可以做个了解&#xff0c;另一方面可以在需要的时候进行查阅。不一定要强行记忆&#xff0c;用多了就熟悉了。 1、…

ETL工具-nifi干货系列 第五讲 处理器GenerateFlowFile

1、今天我们一起来学习处理器GenerateFlowFile。这个处理器创建带有随机数据或自定义内容的 FlowFiles。GenerateFlowFile 对于负载测试、配置和模拟非常有用。从工具栏拖动处理器到画布&#xff0c;然后选择GenerateFlowFile即可。 2、点击add按钮或者双击 GenerateFlowFile可…

大型矿业集团安全知识竞赛主持词

男&#xff1a;尊敬的各位领导&#xff0c;员工同志们&#xff1a; 合&#xff1a;大家好&#xff01; 男&#xff1b;首先让我们以热烈的掌声对公司领导亲临比赛现场指导观看表示欢迎&#xff01; 男&#xff1b;继成功开展了荣辱观专题讲座、好矿嫂女红艺术展、安全谜语竞猜…

CCF-CSP认证考试 202212-3 JPEG 解码 100分题解

更多 CSP 认证考试题目题解可以前往&#xff1a;CSP-CCF 认证考试真题题解 原题链接&#xff1a; 202212-3 JPEG 解码 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 512.0MB 问题背景 四年一度的世界杯即将画上尾声。在本次的世界杯比赛中&#xff0c;视频助理裁判&…

MySQL为什么会选错索引

在平时不知道一有没有遇到过这种情况&#xff0c;我明明创建了索引&#xff0c;但是MySQL为何不用索引呢&#xff1f;为何要进行全索引扫描呢&#xff1f; 一、对索引进行函数操作 假设现在维护了一个交易系统&#xff0c;其中交易记录表 tradelog 包含交易流水号(tradeid)、交…

计算机组成原理 — 指令系统

指令系统 指令系统指令的概述指令的格式指令的字长取决于 操作数类型和操作种类操作数的类型数据在存储器中的存放方式操作类型 寻址方式指令寻址数据寻址立即寻址直接寻址隐含寻址间接寻址寄存器寻址寄存器间接寻址基址寻址变址寻址堆栈寻址 RISC 和 CISC 技术RISC 即精简指令…

通过组策略统一开启终端系统的远程桌面,并修改远程桌面的端口号

通过组策略可以统一开启终端系统的远程桌面服务&#xff0c;但是修改远程桌面端口号则需要通过注册表或者其他方式实现&#xff0c;因为组策略本身不提供直接修改远程桌面端口的功能。以下是如何操作&#xff1a; 开启终端系统的远程桌面&#xff1a; 打开“组策略管理编辑器…

『Apisix系列』破局传统架构:探索新一代微服务体系下的API管理新范式与最佳实践

文章目录 『Apisix基石篇』『Apisix入门篇』『Apisix进阶篇』『Apisix安全篇』 『Apisix基石篇』 &#x1f680; 手把手教你从零部署APISIX高性能API网关 利用Docker-compose快速部署Apache APISIX及其依赖组件&#xff0c;实现高效的API网关搭建通过编写RPM安装脚本来自动化安…

cesium加载.tif格式文件

最近项目中有需要直接加载三方给的后缀名tif格式的文件 <script src"https://cdn.jsdelivr.net/npm/geotiff"></script> 或者 yarn add geotiff npm install geotiff 新建tifs.js import GeoTIFF, { fromBlob, fromUrl, fromArrayBuffer } from geotif…

反勒索组件的核心功能是什么

反勒索组件是一种重要的网络安全工具&#xff0c;旨在防止和应对勒索软件的攻击。勒索软件&#xff0c;通常被称为“勒索病毒”&#xff0c;是一种恶意软件&#xff0c;它会加密用户的文件并要求支付赎金以获取解密密钥。反勒索组件通过一系列的技术和策略&#xff0c;帮助用户…

操作教程|在MeterSphere中通过SSH登录服务器的两种方法

MeterSphere开源持续测试平台拥有非常强大的插件集成机制&#xff0c;用户可以通过插件实现平台能力的拓展&#xff0c;借助插件或脚本实现多种功能。在测试过程中&#xff0c;测试人员有时需要通过SSH协议登录至服务器&#xff0c;以获取某些配置文件和日志文件&#xff0c;或…