OpenCv案例(九): 基于OpenCvSharp图像分割提取目标区域和定位

2023-11-06

以下原图中,物体连靠在一起,目的是将其分割开,再提取轮廓和定位

原图: 

 最终效果:

麻烦的地方是,分割开右下角部分,两个连在一起的目标物体,下图所示: 

基本方法:BoxFilter滤波、二值化、轮廓提取,凸包检测,图像的矩

代码如下:

        /// <summary>
        /// 获取分割点
        /// </summary>
        /// <param name="contours"></param>
        /// <param name="contourCount"></param>
        /// <param name="arcLength"></param>
        /// <param name="farDistance"></param>
        /// <returns></returns>
        public List<Point> GetSplitPoints(Point[][] contours, List<int> contourCount, int arcLength, int farDistance)
        {
            #region 凸包检测
            List<double> lArc = new List<double>();
            //Mat src = srcImage.Clone();
            List<Point[]> lpContours = new List<Point[]>();
            List<int> hulls = new List<int>();
            Point lastP = new Point();
            Point firstP = new Point();
            Point farLastP = new Point();
            List<Point> lps = new List<Point>();
            int dot = 1;
            List<int> depth = new List<int>();
            for (int i = 0; i < contourCount.Count; i++)
            {
                InputArray inputArray = InputArray.Create<Point>(contours[contourCount[i]]);
                OutputArray outputArray = OutputArray.Create(hulls);
                Cv2.ConvexHull(inputArray, outputArray, false, false);
                if (Cv2.ArcLength(inputArray, true) < arcLength)
                {
                    //lArc.Add(Cv2.ArcLength(inputArray, true));
                    continue;
                }
                //前三个值得含义分别为:凸缺陷的起始点,凸缺陷的终点,凸缺陷的最深点(即边缘点到凸包距离最大点)。
                var defects = Cv2.ConvexityDefects(contours[contourCount[i]], hulls);
                for (int j = 0; j < defects.Length; j++)
                {
                    OpenCvSharp.Point start = contours[contourCount[i]][defects[j].Item0];
                    OpenCvSharp.Point end = contours[contourCount[i]][defects[j].Item1];
                    OpenCvSharp.Point far = contours[contourCount[i]][defects[j].Item2];
                    //OpenCvSharp.Point fart = contours[contourCount[i]][defects[j].Item3];
                    if (defects[j].Item3 > farDistance) //(4500 < defects[j].Item3 && defects[j].Item3 < 300000)
                    {
                        lps.Add(contours[contourCount[i]][defects[j].Item2]);
                        depth.Add(defects[j].Item3);
                    }
                }
            }
            #endregion
            return lps;
        }

        /// <summary>
        /// 获取最小内接矩形
        /// </summary>
        /// <param name="contours"></param>
        /// <param name="contourCount"></param>
        /// <returns></returns>
        public List<RotatedRect> GetMinRects(Point[][] contours, List<int> contourCount)
        {
            //Cv2.ImShow(",mmmm", morphImage);

            //double rotateAngel = 0;
            Point2f[] vertices = new Point2f[4];
            //Point2f minRectcenterPoint = new Point2f();
            List<RotatedRect> minRects = new List<RotatedRect>();
            for (int i = 0; i < contourCount.Count; i++)
            {
                //获取轮廓点的矩形区域
                //绘制Rio区域最小矩形
                #region 绘制Rio区域最小矩形
                RotatedRect minRect = Cv2.MinAreaRect(contours[contourCount[i]]);
                minRects.Add(minRect);
                #endregion
            }
            return minRects;
        }
        /// <summary>
        /// 返回设置范围内的轮廓
        /// </summary>
        /// <param name="mat"></param>
        /// <param name="range1"></param>
        /// <param name="range2"></param>
        /// <param name="contourCount"></param>
        /// <returns></returns>
        public Point[][] GetImageContours(Mat mat, int length, out List<int> contourCount)
        {
            List<double> arclength = new List<double>();
            OpenCvSharp.Point[][] contours;
            HierarchyIndex[] hierarchies;
            //Cv2.ImShow(",mmmm", mat);
            Cv2.FindContours(mat, out contours, out hierarchies, RetrievalModes.External, ContourApproximationModes.ApproxSimple, new Point());
            Mat connImg = Mat.Zeros(mat.Size(), MatType.CV_8UC3);
            Point2f[] vertices = new Point2f[4];
            Mat drawOutline = Mat.Zeros(mat.Size(), mat.Type());
            int sum = 0;
            contourCount = new List<int>();
            for (int i = 0; i < contours.Length; i++)
            {
                Rect rect1 = Cv2.BoundingRect(contours[i]);
                if (Cv2.ArcLength(contours[i], true) > length)//(rect1.Width > range1 && rect1.Height < range2)
                {
                    Cv2.DrawContours(drawOutline, contours, i, new Scalar(255, 0, 255), 2, LineTypes.Link8, hierarchies);
                    contourCount.Add(i);
                    arclength.Add(Cv2.ArcLength(contours[i], true));
                    sum++;
                }
            }
            Cv2.ImShow("contours", drawOutline);
            return contours;
        }



        /// <summary>
        /// 图像灰度
        /// 盒子滤波 保留边缘信息
        /// 自适应阈值 效果不错 无需形态学降噪
        /// 取反操作 
        /// 过滤不需要轮廓信息(面积 边长)
        /// 轮廓提取 
        /// (以上每一步都很重要,否则,无法获取良好的轮廓)
        /// 凸包检测
        /// 根据轮廓信息,查找大凸包,获取分割点
        /// 重新操作图像
        /// 在二值化图像时,分割连接点位置
        /// 绘制轮廓
        /// 绘制最小内接矩形和质心点
        /// 识别目标位置完成
        /// 注意:不同大小的图像处理时,需要修改自适应阈值参数、轮廓过滤面积、凸包检测的分割点过滤
        /// </summary>
        /// <param name="srcImage"></param>
        /// <returns></returns>
        public Mat PreProcess(Mat srcImage)
        {
            Mat grayMat = new Mat();
            Cv2.CvtColor(srcImage, grayMat, ColorConversionCodes.BGRA2GRAY);
            //Cv2.ImShow("grayMat", grayMat);

            Mat blurImg = BoxFilter(grayMat);
            //Cv2.ImShow("blurImg", blurImg);

            // 注意:不同大小的图像处理时,需要修改参数
            Mat threshold = new Mat();
            Cv2.AdaptiveThreshold(blurImg, threshold, 255, AdaptiveThresholdTypes.MeanC, ThresholdTypes.Binary, 15, 2);
            //Cv2.Threshold(threshold, threshold, 0, 255, ThresholdTypes.BinaryInv);
            Cv2.ImShow("threshold", threshold);


            //Mat morphImg = MorphImage(threshold, MorphShapes.Ellipse, MorphTypes.Dilate, 1, new OpenCvSharp.Size(3, 3));
            //Cv2.ImShow("morphImg", morphImg);

            //Mat cannyImg = new Mat();
            //Cv2.Laplacian(morphImg2, cannyImg, MatType.CV_8UC3, 5, 1);//Cv2.Canny(morphImg, cannyImg, 30, 90);//3和4参数的 最佳比例在1/3和1/2之间
            //Cv2.ImShow("cannyImg", cannyImg);

            Mat bitwiseMat = new Mat();
            Cv2.BitwiseNot(threshold, bitwiseMat);
            Cv2.ImShow("bitwiseMat", bitwiseMat);

            List<int> contourCount;
            //轮廓提取
            Point[][] contours = GetImageContours(bitwiseMat, 600, out contourCount);
            //凸包检测
            List<Point> lps = GetSplitPoints(contours, contourCount, 800, 4500);


            // 注意:不同大小的图像处理时,需要修改参数
            //重新处理
            Cv2.AdaptiveThreshold(blurImg, threshold, 255.0, AdaptiveThresholdTypes.MeanC, ThresholdTypes.Binary, 13, 2);
            Cv2.ImShow("threshold1", threshold);

            //MorphImage(threshold, MorphShapes.Ellipse, MorphTypes.Close, 1, new OpenCvSharp.Size(3, 3));
            //Cv2.ImShow("morphImg1", morphImg);

            Cv2.BitwiseNot(threshold, bitwiseMat);
            Cv2.ImShow("bitwiseMat1", bitwiseMat);
            //提取凸显点坐标

            if (lps.Count > 1)
            {
                Cv2.Line(bitwiseMat, lps[0], lps[1], Scalar.Black, 2, LineTypes.Link8);
            }
            Cv2.ImShow("bitwiseMat2", bitwiseMat);
            //轮廓提取   
            contourCount.Clear();    // 注意:不同大小的图像处理时,需要修改length参数
            Point[][] newContours = GetImageContours(bitwiseMat, 550, out contourCount);
            List<RotatedRect> rotatedRects = GetMinRects(newContours, contourCount);

            for (int i = 0; i < rotatedRects.Count; i++)
            {
                #region 绘制Rio区域最小矩形
                Point2f[] vertices = rotatedRects[i].Points();
                #endregion
                //绘制最小矩形
                #region 绘制最小矩形
                Cv2.Line(srcImage, Convert.ToInt32(vertices[0].X), Convert.ToInt32(vertices[0].Y), Convert.ToInt32(vertices[1].X), Convert.ToInt32(vertices[1].Y), new Scalar(0, 0, 255), 2);
                Cv2.Line(srcImage, Convert.ToInt32(vertices[0].X), Convert.ToInt32(vertices[0].Y), Convert.ToInt32(vertices[3].X), Convert.ToInt32(vertices[3].Y), new Scalar(0, 0, 255), 2);
                Cv2.Line(srcImage, Convert.ToInt32(vertices[1].X), Convert.ToInt32(vertices[1].Y), Convert.ToInt32(vertices[2].X), Convert.ToInt32(vertices[2].Y), new Scalar(0, 0, 255), 2);
                Cv2.Line(srcImage, Convert.ToInt32(vertices[2].X), Convert.ToInt32(vertices[2].Y), Convert.ToInt32(vertices[3].X), Convert.ToInt32(vertices[3].Y), new Scalar(0, 0, 255), 2);
                //获取重心点
                Moments M;
                M = Cv2.Moments(vertices);
                double cX = M.M10 / M.M00;
                double cY = M.M01 / M.M00;
                //显示目标中心并提取坐标点
                Cv2.Circle(srcImage, (int)cX, (int)cY, 2, Scalar.Yellow, 2);
                //Console.WriteLine("AngleRect_angle: {0}", minRect.Angle);
                #endregion
            }
            Cv2.ImShow("srcImage", srcImage);
            return null;
        }

灰度图像后图像二值化:

图像取反

 

 绘制轮廓

 凸包检测,查找分割点,下图黄色点标记处即找到的分割点位置

 将找到的分割点在二值化图像中,连接一条线后,重新轮廓识别即可分割

最小轮廓矩形提取和绘制,以及绘制质心位置

 到此,已将连接处分隔开

注意:使用以上方法是需要根据图像大小设置部分参数,例如二值化处理参数、过滤轮廓形状大小,凸包检测点的获取等位置,需要根据实际情况设置参数;

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenCv案例(九): 基于OpenCvSharp图像分割提取目标区域和定位 的相关文章

随机推荐

  • SpringBoot--基础--04--拦截器

    SpringBoot 基础 04 拦截器 拦截器 实现HandlerInterceptor接口 注册拦截器 一 代码结构 MyMvcConfig 使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能 Conf
  • 同步方法及同步代码块

    synchronized方法和synchronized块 synchronized方法必须获得对象的锁才能执行 否则线程阻塞 方法一旦执行 就独占此锁 直到方法返回才释放锁 后面被阻塞的线程才能获得这个锁 继续执行 synchronized
  • 谷歌取消了2000多人的offer,特殊时期如何应对裁员?

    就在刚刚 CNBC报道 科技大厂谷歌有上千人饭碗保不住了 图片来自NBC 版权属于原作者 谷歌缩减招聘 据悉这部分人主要是谷歌的合同工和临时工 纽约时报 报道 谷歌取消了2000多人的工作录取 上个月 谷歌首席执行官劈柴君向员工承认 由于新
  • 字符串 字符串中的第一个唯一字符

    LC 字符串中的第一个唯一字符 给定一个字符串 s 找到 它的第一个不重复的字符 并返回它的索引 如果不存在 则返回 1 Swift 实现 func firstUniqChar s String gt Int 创建字符数组 var arra
  • [HTTP] 01一张思维导图带你领略HTTP发展史

    HTTP是什么 HTTP作为一个较常见的应用协议 在面试过程中经常会对其相关内容进行考察 其发展史亦是重点 HTTP是什么 HTTP为Hyper Text Transfer Protocol的简写 中译超文本传输协议 是在计算机网络里面点对
  • 网络原理:ISO/OSI参考模型

    1 ISO OSI参考模型 1 1 各层功能 1 物理层功能 物理层是OSI参考模型的最低层 它利用传输介质为数据链路层提供物理连接 保证二进制位流的透明传送 2 数据链路层 数据链路层是为网络层提供服务的 解决两个相邻结点之间的通信问题
  • C# 学习笔记--个人学习使用 <1>

    C 学习笔记 Chapter 1 C 比较软的基础部分 Section 1 类与命名空间 Part 1 命名空间 NameSpace Part 2 类 Class Section 2 基本元素 Section 3 数据类型 Part 1 什
  • 使用opencv rotate函数实现图片旋转

    使用rotate函数 旋转图片 rotate src dst ROTATE 90 CLOCKWISE 使用rotate 旋转图片 src是源图片的mat dts是目标图片的mat 第3个参数是旋转的角度 有以下4个选项 ROTATE 90
  • android app缓存机制会自动清除,深入理解Android缓存机制(一)缓存简介

    概述 说起缓存 大家可能很容易想到Http的缓存机制 LruCache 其实缓存最初是针对于网络而言的 也是狭义上的缓存 广义的缓存是指对数据的复用 我这里提到的也是广义的缓存 比较常见的是内存缓存以及磁盘缓存 不过要想进一步理解缓存体系
  • Python内置函数详解,进阶必备

    内置函数就是Python给你提供的 拿来直接用的函数 比如print input等 截止到python版本3 6 2 python一共提供了68个内置函数 具体如下 本文将这68个内置函数综合整理为12大类 正在学习Python基础的读者一
  • 关于SDC时钟约束的事

    1 quartus的SDC约束就跟xilinx的ucf约束文件一样 2 主要用途一般是 一从输入端口到寄存器 二寄存器到寄存器 通过设定时钟频率方式进行约束 三寄存器到输出 四创建时钟约束命令 五时钟延迟约束 六时钟抖动约束 七输入和输出延
  • Android实现简单的相册

    用手机浏览靓照也是件非常惬意的事了 然而如何实现这一功能呢 其实也并不难 下面是一个简单的例子 功能为 主屏幕上显示用户选择的靓照 屏幕下面滚动显示靓照集 点击即可浏览 如下图所示 实现代码为 这里图片没法提供了 可以设置自己喜欢的图片 R
  • MYSQL查询优化器

    MYSQL 逻辑结构 MySQL 使用典型的客户端 服务器 Client Server 结构 体系结构大体可以分为三层 客户端 服务器层以及存储引擎层 其中 服务器层又包括了连接管理 查询缓存 SQL 接口 解析器 优化器 缓冲与缓存以及各
  • Navicat连接Mysql遇到的错误

    在navicat链接mysql以后的版本时 会出现2059的错误 这个错误出现的原因是在mysql8之前的版本中加密规则为mysql native password 而在mysql8以后的加密规则为caching sha2 password
  • mysql内测试连通性命令_怎么使用ping命令进行连通性测试

    关于ping命令的作用 ping 命令有助于验证网络层的连通性 一般进行网络故障排除时 可以使用ping 命令向目标计算机或IP地址发送ICMP回显请求 目标计算机会返回回显应答 如果目标计算机不能返回回显应答 说明在源计算机和目标计算机之
  • python提取txt文件中的数据

    从存储在txt文件中的30000多条数据当中提取自己需要的信息 如下图示例的数据格式 需要提取出now后的数值并存储在另外一个txt文件当中 结果示例 code path r r new data1 txt with open path r
  • C++ 项目

    C 方向 10个企业级别项目 138个工作日 1 学生信息管理系统 C 基础 2 俄罗斯方块 控制台 C 基础
  • Arcgis server在solaris和linux上,用直连的方式连接sde的时的配置

    Arcgis server在solaris和linux上 用直连的方式连接sde的时候需要做一点配置才可以连上 1 在
  • 微信小程序从入坑到放弃二十:生成海报前使用wx.downloadFile或wx.getImageInfo时潜在的坑

    摘要 上周六下午16点左右 公司研发群中扔来一张bug截图 当天21点 群里又扔来bug截图 研发团队二话不说开始查找bug如何复现 22点20分左右 我终于复现了bug 随后就是看源码 搜索 跳坑 爬坑 周日同其他研发小伙伴一样 又是奉献
  • OpenCv案例(九): 基于OpenCvSharp图像分割提取目标区域和定位

    以下原图中 物体连靠在一起 目的是将其分割开 再提取轮廓和定位 原图 最终效果 麻烦的地方是 分割开右下角部分 两个连在一起的目标物体 下图所示 基本方法 BoxFilter滤波 二值化 轮廓提取 凸包检测 图像的矩 代码如下