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

embedded/2024/10/18 8:27:46/

前言

最近领导在做库房工具管理这块的功能,希望能集成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;}}

其他实列图

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

结语

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


http://www.ppmy.cn/embedded/28368.html

相关文章

DFT(二)MBIST

DFT&#xff08;二&#xff09;MBIST MBIST&#xff08;Memory Built-In Self-Test&#xff0c;内置存储器自检测&#xff09;是一种用于检测半导体存储器&#xff08;如 RAM&#xff09;中制造缺陷和操作故障的技术。它允许存储器在没有外部测试设备的情况下进行自我测试&…

Java 基础重点知识-(泛型、反射、注解、IO)

文章目录 什么是泛型? 泛型有什么用?泛型原理是什么? Java 反射什么是反射? 反射作用是什么?动态代理有几种实现方式? 有什么特点? Java 注解什么是注解, 作用是什么? Java I/O什么是序列化?Java 是怎么实现系列化的?常见的序列化协议有哪些?BIO/NIO/AIO 有什么区别…

Spring IOC(二)

1. Bean的定义与获取 1.1 定义Bean 在Spring 中定义Bean的方式主要有三种&#xff1a; 1、基于XML配置文件的方式&#xff08;了解&#xff09;&#xff1a;通常会在配置文件中使用<bean>标签来定义Bean&#xff0c;并设置Bean的属性、依赖关系等信息。 2、基于注解的方…

操作系统(2)——进程线程

目录 小程一言专栏链接: [link](http://t.csdnimg.cn/8MJA9)基础概念线程详解进程详解进程间通信调度常用调度算法 重要问题哲学家进餐问题问题的描述策略 读者-写者问题问题的描述两种情况策略 总结进程线程一句话 小程一言 本操作系统专栏&#xff0c;是小程在学操作系统的过…

jenkins搭建

安装jdk yum install -y java-1.8.0-openjdk.x86_64 默认安装到usr/lib/jvm目录下 查看JDK信息,输入命令:java -version 检测JDK安装包,输入命令:rpm -qa | grep java 进入安装目录。 输入命令:cd /usr/lib/jvm 删除Java相关文件,输入命令:rm -rf /usr/lib/jvm 配置…

翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习三

合集 ChatGPT 通过图形化的方式来理解 Transformer 架构 翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习一翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深度学习二翻译: 什么是ChatGPT 通过图形化的方式来理解 Transformer 架构 深…

使用Python及R语言绘制简易数据分析报告

Pytohn实现 在python中有很多包可以实现绘制数据分析报告的功能&#xff0c;推荐两个较为方便的包&#xff1a;pandas-profiling 和 sweetviz 。 使用 pandas-profiling 包&#xff08;功能全面&#xff09; 这个包的个别依赖包与机器学习的 sklearn 包的依赖包存在版本冲突&a…

制作一个RISC-V的操作系统十六-系统调用

文章目录 用户态和内核态mstatus设置模式切换核心流程封装代码背景解释代码示例解析解释目的 用户态和内核态 mstatus设置 此时UIE设置为1和MPIE为1&#xff0c;MPP设置为0 代表当前权限允许UIE中断发生&#xff0c;并且在第一个mret后将权限恢复为用户态&#xff0c;同时MIE也…