浅谈OpenCV 粗略计算工件轮廓面积和外接圆直径(Emgu.CV)

前言

最近领导在做库房工具管理这块的功能,希望能集成OpenCV 粗略的计算出工具的长度,以方便用户再归还工具的时候,提示用户该放在那种尺寸的盒子里面,这便是这篇文章的由来。

我们的系统是基于.net开发的,所以采用的是 Emgu.CV这个框架来开发相应的功能,首先我们来看看效果吧,如下图。在这里我们的高宽、面积、直径都是计算的像素值,实际情况中我们需要根据自己的相片尺寸和拍照背景板与摄像头的距离,得出比例尺,根据比例尺大概计算出物体的实际面积和长度。

在这里插入图片描述

实现思路

我们业务中不需要太高的精度,所以采用一些简单的调用OpenCV 函数就能实现,注意本文的背景采用的是A4纸白色背景,如果背景不同,二值化的过程中需要你自己调节对应的参数。

  • 对照片进行灰度和二值处理
  • 去除照片中的阴影
  • 描绘物体的轮廓、和外接圆,就可以得出面积和物体长度

代码(主要逻辑代码,关于页面的代码需要自己采用 winform设计)

 public partial class Form1 : Form
 {
     public Form1()
     {
         InitializeComponent();
     }

     string imagePath = string.Empty;
     string result_path = string.Empty;
     /// <summary>
     ///  上传图片
     /// </summary>
     /// <param name="sender"></param>
     /// <param name="e"></param>
     private void button_up_Click(object sender, EventArgs e)
     {
       

         OpenFileDialog openFileDialog = new OpenFileDialog();
         if (openFileDialog.ShowDialog() == DialogResult.OK)
         {
             imagePath = openFileDialog.FileName; //得到文件全路径名
             // 使用Image.FromFile方法加载图片  
             System.Drawing.Image image = System.Drawing.Image.FromFile(imagePath);
             // 设置PictureBox的图片  
             src.Image = image;
             // (可选)设置PictureBox的大小以适应图片  
             src.SizeMode = PictureBoxSizeMode.StretchImage;
            // AreaCalculate(imagePath);
             BinaryTreatment(imagePath);
         }
     }

     private void button_calculate_Click(object sender, EventArgs e)
     {
         AreaCalculate(result_path);
     }

     /// <summary>
     /// 去除图片阴影
     /// </summary>
     /// <param name="path"></param>
     private string RemoveShadow(string path)
     {
         // 加载图像
         Mat image = CvInvoke.Imread(path, ImreadModes.Color);

         // 将图像转换为灰度图像
         Mat grayImage = new Mat();
         CvInvoke.CvtColor(image, grayImage, ColorConversion.Bgr2Gray);

         // 增加对比度和亮度来减少阴影
         double alpha = 1.2; // 对比度增强因子
         int beta = 20; // 亮度增强因子
         grayImage.ConvertTo(grayImage, DepthType.Cv8U, alpha, beta);

         DateTime now = DateTime.Now; // 获取当前本地时间 
         long timestamp = (long)(now - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
         string shadow_path = "C:\\Users\\Administrator\\Desktop\\openCV\\" + timestamp.ToString() + "dilated.jpg";
         CvInvoke.Imwrite(shadow_path, grayImage);
         return shadow_path;
     }


     /// <summary>
     /// 灰度和二值处理
     /// </summary>
     /// <param name="file_path"></param>
     private void BinaryTreatment(string file_path)
     {
         string shadow_path = RemoveShadow(file_path);

         // 读取图片文件
         Mat src = CvInvoke.Imread(shadow_path);
         // 创建一个与源图像大小相同的目标图像
         Mat dst = new Mat(src.Size, DepthType.Cv8U, 3);
         // 将目标图像设置为黑色
         dst.SetTo(new MCvScalar(0, 0, 0));

        
         // 显示输入图像
         //CvInvoke.Imshow("input", src);

        // 创建一个灰度图像
         Mat grayImg = new Mat();
         // 将源图像转换为灰度图像
         CvInvoke.CvtColor(src, grayImg, ColorConversion.Bgr2Gray);

         // 对灰度图像进行阈值处理,得到二值化图像
         CvInvoke.Threshold(grayImg, grayImg, 127, 255, ThresholdType.BinaryInv);
         // 创建一个用于膨胀的核,通常使用3x3或5x5的矩形核  
         Mat kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), new Point(-1, -1));
         // 对灰度图像进行膨胀处理  
         CvInvoke.Dilate(grayImg, dst, kernel, new Point(-1, -1), 1, new BorderType(), new MCvScalar());
         DateTime now = DateTime.Now; // 获取当前本地时间 
         long timestamp = (long)(now - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
         result_path = "C:\\Users\\Administrator\\Desktop\\openCV\\" + timestamp.ToString() + "dilated.jpg";
         CvInvoke.Imwrite(result_path, dst);
         // 使用Image.FromFile方法加载图片  
         System.Drawing.Image image = System.Drawing.Image.FromFile(result_path);
         // 设置PictureBox的图片  
         result.Image = image;
         // (可选)设置PictureBox的大小以适应图片  
         result.SizeMode = PictureBoxSizeMode.StretchImage;
     }

     /// <summary>
     /// 计算面积和长度
     /// </summary>
     /// <param name="read_path"></param>
     private  void AreaCalculate( string  read_path)
     {
        
         // 读取图片  
         Mat src = CvInvoke.Imread(read_path);
         // 克隆源图像,用于绘制结果  
         Mat dst = src.Clone();
         // 创建一个新的Mat对象用于存储灰度图像 
         Mat grayImg = new Mat();     
         // 将源图像转换为灰度图像  
         CvInvoke.CvtColor(src, grayImg, ColorConversion.Bgr2Gray);
         CvInvoke.Imwrite(result_path, grayImg);
         // 初始化轮廓和层次结构向量 
         VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();

         VectorOfRect hierarchy = new VectorOfRect();

         // 查找轮廓,这里只查找最外层的轮廓  
         CvInvoke.FindContours(grayImg, contours, hierarchy, RetrType.External, ChainApproxMethod.ChainApproxNone);

         double maxArea = 0.0;
         int max_coutours = 0;
         
         for (int i = 0; i < contours.Size; i++)
         {
             //求取面积、周长、多边形逼近
             double length = CvInvoke.ArcLength(contours[i], true); //计算轮廓周长
             double area = CvInvoke.ContourArea(contours[i], false); //计算轮廓面积
             if (area > maxArea)
             {
                 maxArea = area;
                 max_coutours = i;
             }
             VectorOfPoint approxPoly = new VectorOfPoint();
             CvInvoke.ApproxPolyDP(contours[max_coutours], approxPoly, length * 0.001, true); //多变形轮廓拟合  0.001 值越小拟合程度高
             CvInvoke.Polylines(dst, approxPoly, true, new MCvScalar(255, 255, 0), 2); //绘制拟合多边形
         }

         if (!grayImg.IsEmpty)
         {
             Console.WriteLine("图像的宽度是:{0}", grayImg.Cols);
             textBox_width.Text = grayImg.Cols.ToString();

             Console.WriteLine("图像的高度是:{0}", grayImg.Rows);
             textBox_height.Text = grayImg.Rows.ToString();

             Console.WriteLine("图像的面积(总像素数)是:{0}", grayImg.Cols * grayImg.Rows);
             textBox_total.Text = (grayImg.Cols * grayImg.Rows).ToString();
         }
         Console.WriteLine("图像轮廓最大面积:" + maxArea);

         textBox_area.Text = maxArea.ToString();

         CircleF circleF1 = CvInvoke.MinEnclosingCircle(contours[max_coutours]);
         Console.WriteLine("最大半径: " + circleF1.Radius );
         textBox_length.Text = (circleF1.Radius*2 ).ToString();

         DateTime now = DateTime.Now; // 获取当前本地时间 
         long timestamp = (long)(now - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
         string c_path = "C:\\Users\\Administrator\\Desktop\\openCV\\" + timestamp.ToString() + "dilated.jpg";
         CvInvoke.Imwrite(c_path, dst);
         // 使用Image.FromFile方法加载图片  
         System.Drawing.Image image2 = System.Drawing.Image.FromFile(c_path);
         // 设置PictureBox的图片  
         pictureBox_contoures.Image = image2;
         // (可选)设置PictureBox的大小以适应图片  
         pictureBox_contoures.SizeMode = PictureBoxSizeMode.StretchImage;
          
     }
 }

其他实列图

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

结语

作为一个新手接触,记录一下学习成果,不喜勿喷。

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

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

相关文章

项目管理-项目采购管理1/2

项目管理&#xff1a;每天进步一点点~ 活到老&#xff0c;学到老 ヾ(◍∇◍)&#xff89;&#xff9e; 何时学习都不晚&#xff0c;加油 1.项目采购管理-主要内容 项目采购管理过程--重点&#xff1a; ①ITTO 输入&#xff0c;输出工具和技术。 ②问题和解决方案。 ③论文…

【白话机器学习系列】白话特征向量

白话特征向量 一个方阵 A A A 与列向量 v v v 的乘积会生成一个新的列向量。这个新向量通常与原向量有着不同的方向&#xff0c;矩阵在这里代表一个线性变换。然而&#xff0c;某些向量会保持其原始方向。我们称这种向量为矩阵 A A A 的特征向量&#xff08;eigenvector&…

python数据分析——业务指标分析

业务指标分析 前言一、业务指标分析的定义二、业务问题构建问题构建的要求 三、业务问题的识别在识别问题的阶段对于企业内部收益者的补充&#xff1a; 四、竞争者分析标题竞争者分析的内容&#xff1a;标题竞争者分析目的&#xff1a;案例&#xff1a; 黑莓公司为什么会消亡&a…

dynamic_cast 静态转换

dynamic_cast 静态转换 const_cast 常量转换 重新解释转换(reinterpret_cast) 最不安全

RocketMq详解:一、RocketMQ 介绍及基本概念

文章目录 前言1.RocketMQ简介2.RocketMQ 特点3.核心特性4.应用场景5.RocketMQ 优势6.RocketMQ 四大核心组件6.1 NameServer1.NameServer作用2.NameServer被设计为无状态的原因3.和NameServer和Zookeeper的区别4.NameServer的高可用保障 6.2 Broker1.Broker部署方式2.高可用与负…

ssm105基于JAVAEE技术校园车辆管理系统+jsp

校园车辆管理系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本校园车辆管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短…

泰克示波器电流探头如何抓浪涌电流波形?

泰克示波器是一种常见的电子测量仪器&#xff0c;广泛应用于电子工程、通信工程、医疗设备等领域。它的主要功能是实时显示电信号的波形&#xff0c;从而帮助工程师和技术人员分析和调试电路。而在一些特定的应用场景中&#xff0c;例如电源、电机、电器设备等&#xff0c;我们…

模型全参数训练和LoRA微调所需显存的分析

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

TypeScript 基础学习笔记:泛型 <T> vs 断言 as

TypeScript 基础学习笔记&#xff1a;泛型 <T> vs 断言 as &#x1f525; 引言 &#x1f44b; TypeScript (TS) 以其静态类型的魔力&#xff0c;让我们的代码更加健壮、易读且易于维护。今天&#xff0c;我们将深入探讨两个核心概念——泛型&#xff08;Generics&#x…

【华为】AC三层旁挂直接转发

【华为】AC三层旁挂直接转发 实验需求实验拓扑配置AC和AP二层通信ACLSW1LSW2AP2获取到的管理地址AP3获取到的管理地址 AP上线配置WLAN业务ACLSW1&#xff08;作DHCP地址池&#xff09;业务成功下发 访问公网&#xff08;NAT&#xff09;LSW1AR1 配置文档ACLSW1LSW2AR1ISP 实验需…

杭电acm1013 Digital Roots 数字根 Java解法 高精度

Problem - 1013 (hdu.edu.cn) 高精度算术模拟 开long没过想到开bI 开bl一次过 import java.math.BigInteger; import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);BigInteger i;while (!(i sc.nextB…

Docker新建容器 修改运行容器端口

目录 一、修改容器的映射端口 二、解决方案 三、方案 一、修改容器的映射端口 项目需求修改容器的映射端口 二、解决方案 停止需要修改的容器 修改hostconfig.json文件 重启docker 服务 启动修改容器 三、方案 目前正在运行的容器 宿主机的3000 端口 映射 容器…

【Python项目】基于时间序列的【大气污染预测系统】

技术简介&#xff1a;使用Python技术、B/S架构、MYSQL数据库等实现。 系统简介&#xff1a;本系统的主要使用角色为普通用户和管理员用户&#xff0c;两者的功能几乎是一致的&#xff0c;但管理员用户比普通用户多了用户管理的功能&#xff0c;可以对系统内的用户进行管理。普通…

Java IO编程必备:FilterInputStream类的原理与实现

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

如何构建进攻性的网络安全防护策略

进攻性安全&#xff08;Offensive security&#xff09;是指一系列主动安全策略&#xff0c;这些策略与恶意行为者在现实世界的攻击中使用的策略相同&#xff0c;区别在于其目的是加强而非损害网络安全。常见的进攻性安全方法包括红队、渗透测试和漏洞评估。 进攻性安全行动通…

C++入门 ——类和对象(二)

this指针 this指针的引出 我们先来定义一个日期类 Date class Date { public:void Init(int year, int month, int day){_year year;_month month;_day day;}void Print(){cout <<_year<< "-" <<_month << "-"<< _da…

基于springboot+vue+Mysql的自习室预订系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

Springboot工程创建

目录 一、步骤 二、遇到的问题及解决方案 一、步骤 打开idea,点击文件 ->新建 ->新模块 选择Spring Initializr&#xff0c;并设置相关信息。其中组为域名&#xff0c;如果没有公司&#xff0c;可以默认com.example。点击下一步 蓝色方框部分需要去掉&#xff0c;软件包…

牛客NC382 切割木头【中等 二分超找 Java/Go/C++】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/707d98cee255448c838c76918a702be0 核心 二分查找Java代码 import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可…

SpringBoot指标监控

一.SpringBoot指标监控_添加Actuator功能 Spring Boot Actuator可以帮助程序员监控和管理SpringBoot应用&#xff0c;比如健康检查、内存使用情况统计、线程使用情况统计等。我 们在SpringBoot项目中添加Actuator功能&#xff0c;即可使用Actuator监控 项目&#xff0c;用法如…