浅谈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;}}

其他实列图

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

结语

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分布式系统事务一致性解决方案(基于事务消息)

参考&#xff1a;https://rocketmq.apache.org/zh/docs/featureBehavior/04transactionmessage/ 文章目录 概要错误的方案方案一&#xff1a;业务方自己实现方案二&#xff1a;RocketMQ 事务消息什么是事务消息事务消息处理流程事务消息生命周期使用限制使用示例使用建议 概要 …

MATLAB语音信号分析与合成——MATLAB语音信号分析学习资料汇总(图书、代码和视频)

教科书&#xff1a;MATLAB语音信号分析与合成&#xff08;第2版&#xff09; 链接&#xff08;含配套源代码&#xff09;&#xff1a;https://pan.baidu.com/s/1pXMPD_9TRpJmubPGaRKANw?pwd32rf 提取码&#xff1a;32rf 基础入门视频&#xff1a; 视频链接&#xff1a; 清…

急急急!微信朋友圈删除了怎么恢复?

微信朋友圈是我们与朋友分享生活点滴的重要平台&#xff0c;但有时候微信出现异常&#xff0c;导致我们编辑好的朋友圈被删除了&#xff0c;这时候该怎么办呢&#xff1f; 幸运的是&#xff0c;微信提供了一种简单的方式来恢复已删除的朋友圈内容。微信朋友圈删除了怎么恢复&a…

利用二叉检索树将文章中的单词建立索引(正则表达式)

知识储备 链接: 【二叉检索树的实现——增删改查、读取命令文件、将结果写入新文件】 1、正则表达式的处理 &#xff08;1&#xff09;r’前缀的作用 r’前缀的用于定义原始字符串&#xff0c;特点是不会处理反斜杠\作为转义字符 &#xff08;2&#xff09;正则表达式中元…

场外个股期权开户新规及操作方法

场外个股期权开户新规 场外个股期权开户新规主要涉及对投资者资产实力、专业知识、风险承受能力和诚信记录的要求。以下是根据最新规定总结的关键要点&#xff1a; 来源/&#xff1a;股指研究院 资产门槛&#xff1a;投资者需具备一定的资产实力&#xff0c;确保在申请开户前…

【Linux】文件打包解压_tar_zip

文章目录 &#x1f4d1;引言&#xff1a;一、文件打包压缩1.1 什么是文件打包压缩&#xff1f;1.2 为什么需要文件打包压缩&#xff1f; 二、打包解压2.1 zip2.2 unzip2.3 tar指令 &#x1f324;️全篇小结&#xff1a; &#x1f4d1;引言&#xff1a; 在Linux操作系统中&#…

OpenCV-Python: 强大的计算机视觉库

文章目录 OpenCV-Python: 强大的计算机视觉库背景OpenCV-Python是什么&#xff1f;安装简单的库函数使用方法场景示例人脸检测和识别图像分割目标跟踪 常见问题和解决方案总结 OpenCV-Python: 强大的计算机视觉库 背景 OpenCV (Open Source Computer Vision Library) 是一个开…

如何修改php版本

我使用的Hostease的Windows虚拟主机产品,由于网站程序需要支持高版本的PHP,程序已经上传到主机&#xff0c;但是没有找到切换PHP以及查看PHP有哪些版本的位置&#xff0c;因此咨询了Hostease的技术支持&#xff0c;寻求帮助了解到可以实现在Plesk面板上找到此切换PHP版本的按钮…

基于Springboot+Vue的Java项目-火车票订票系统开发实战(附演示视频+源码+LW)

大家好&#xff01;我是程序员一帆&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &am…

GDPU 算法分析与设计 天码行空5

一、【实验目的】 &#xff08;1&#xff09;熟悉动态规划算法的基本思想. &#xff08;2&#xff09;理解动态规划算法中子问题的划分和递推方程设计的基本方法. &#xff08;3&#xff09;熟悉矩阵链乘法的基本思想并编程实现。 二、【实验内容】 输入:矩阵链Ai…j的输入为…

Power BI:如何将文件夹批量Excel(多sheet页)文件导入?

故事背景&#xff1a; 业务同事想用Power BI分析近两年市场费用。 数据源全部是Excel文件&#xff0c;并且以每月一个Excel文件的方式&#xff0c;统一存放到同一文件夹下面。 重点&#xff0c;每张Excel文件会有多张sheet页&#xff0c;用区分每家分公司的费用信息。 目前…

Linux之进程间通信(二)

system V system V共享内存是内核中专门设计的通信的方式, 粗粒度划分操作系统分为进程管理, 内存管理, 文件系统, 驱动管理.., 粒度更细地分还有 进程间通信模块. 对于操作系统, 通信的场景有很多, 有以传送数据, 快速传送数据, 传送特定数据块, 进程间协同与控制以目的, 它…

数字信号的产生与检测——DSP学习笔记六

本专栏的博客的图片大部分来源于老师的PPT&#xff0c;本博客只是博主对于上课内容的知识结构的分析和梳理。 几种数字信号的产生 正弦波信号 多项式逼近(除了泰勒展开&#xff0c;还有一种方法是切比雪夫逼近法&#xff0c;感兴趣可以自己去了解一下&#xff09; 查找表 核心思…

<计算机网络自顶向下> Internet Protocol

互联网中的网络层 IP数据报格式 ver: 四个比特的版本号&#xff08;IPV4 0100, IPV6 0110&#xff09; headlen&#xff1a;head的长度&#xff08;头部长度字段&#xff08;IHL&#xff09;指定了头部的长度&#xff0c;以32位字&#xff08;4字节&#xff09;为单位计算。这…

Java数组深度剖析:掌握数据结构的基石

引言 在编程世界中&#xff0c;数仅仅是一种数据类型&#xff0c;它是理解内存分配、多维数据处理以及性能优组像是构建复杂数据结构的基本积木。它们简洁、高效&#xff0c;是管理元素集的首选方式。在Java中&#xff0c;数组不化的关键。 这篇文章致力于深入探讨Java数组的各…

git出错、文件无法删除、文件无法访问、文件或目录损坏且无法读取 等相关问题处理

一、错误历程与解决方案 1. 在用idea时&#xff0c;突然出现 部分git的命令无法使用&#xff0c;提示错误 2. 尝试删除项目文件夹&#xff0c;重新从git拉取代码 3.发现无法删除文件夹&#xff0c;删除操作没有任何反应&#xff0c;但是可以对文件夹重命名。 4.重新clone g…

李沐70_bert微调——自学笔记

微调BERT 1.BERT滴哦每一个词元返回抽取了上下文信息的特征向量 2.不同的任务使用不同的特性 句子分类 将cls对应的向量输入到全连接层分类 命名实体识别 1.识别应该词元是不是命名实体&#xff0c;例如人名、机构、位置 2.将非特殊词元放进全连接层分类 问题回答 1.给…

QT c++ 代码布局原则 简单例子

本文描述QT c widget代码布局遵循的原则&#xff1a;实中套虚&#xff0c;虚中套实。 本文最后列出了代码下载链接。 在QT6.2.4 msvc2019编译通过。 所谓实是实体组件&#xff1a;比如界面框、文本标签、组合框、文本框、按钮、表格、图片框等。 所谓虚是Layout组件&#x…

Redis哈希槽和一致性哈希

前言 单点的Redis有一定的局限&#xff1a; 单点发生故障&#xff0c;数据丢失&#xff0c;影响整体服务应用自身资源有限&#xff0c;无法承载更多资源分配并发访问&#xff0c;给服务器主机带来压力&#xff0c;性能瓶颈 我们想提升系统的容量、性能和可靠性&#xff0c;就…

sentinel-1.8.7与nacos-2.3.0实现动态规则配置、双向同步

&#x1f60a; 作者&#xff1a; 一恍过去 &#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390 &#x1f38a; 社区&#xff1a; Java技术栈交流 &#x1f389; 主题&#xff1a; sentinel-1.8.7与nacos-2.3.0实现动态规则配置、双向同步 ⏱️ 创作时…