Opencv C# 重叠 粘连 Overlap 轮廓分割 (不知道不知道)

先上效果图

一种基于凹陷检测重叠轮廓分割的方法

这两个星期压力大的一批,心脏都给干得乱跳了,现在高血压+心率不齐+贫血。兄弟们保重身体啊。

简单说下逻辑:

  1. 前处理:的噼里啪啦我就不说了,根据样品来(灰度,滤波,二值化,形态学...)
  2. 提取轮廓并计算凸包
  3. 填充凸包后的轮廓 减去 原始轮廓得到凹陷区域
  4. 计算凹陷区域的面积,利用最大的两个凹陷区域计算出两个凹陷区域的最近两点
  5. 两点一条线作为分割线

步骤说起来很简单,做起来准备上天。

要想做到好的效果就需要有过滤参数,我做了基本的四个参数

  1. 最小轮廓长度:用来过滤小颗粒
  2. 深度阈值:凸包凹陷的阈值 用来过滤凹陷浅的轮廓
  3. 迭代次数:我们上面只是拿了单个轮廓两个最大凹陷区域进行分割,只适合两个规则形状的重叠,那三个呢 四个呢 无数个呢(管他那么多就是干)
  4. 线宽:分割线的宽度 最好要为2,别问 问就是坑

下面看看不同参数的效果:

迭代次数:可以简单理解我要分为几个颗粒 

深度阈值:越小 小凹陷就越多

效果就这样了,这是基于形状的,但是都说基于距离变换+分水岭的好。先贴上代码吧白嫖兄弟们
!!!这是我封装的方法不一定适合你 参数自己传进来就行。最好别用,不一定好用。打开思路很重要..........

比如:根据长轴作为分界线 对应点是不是就可以不用那么多迭代???更好的?骨架作为分界线???迭代次数应该写在轮廓遍历里面???当然是的,但是我脑子不想思考了才来写个文章。

public ProcessingResult ProcessImage(Mat src, Dictionary<string, object> parameters, Mat? originalMat = null)
{if (!src.IsValidMat()) return new ProcessingResult();try{int depthThreshold = parameters.GetValueOrDefault("depthThreshold", 100)?.ToString()?.ToInt(100) ?? 100;int lineThickness = parameters.GetValueOrDefault("lineThickness", 2)?.ToString()?.ToInt(2) ?? 2;int miniArcLength = parameters.GetValueOrDefault("miniArcLength", 20)?.ToString()?.ToInt(20) ?? 20;int iterations = parameters.GetValueOrDefault("iterations", 1)?.ToString()?.ToInt(1) ?? 1;Mat binary = new Mat();if (src.Channels() > 1){var gray = src.CvtColor(ColorConversionCodes.BGR2GRAY);double otsuThresh = Cv2.Threshold(src, binary, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);}else{binary = src;}Mat resultImage = binary.CvtColor(ColorConversionCodes.GRAY2BGR); // 彩色图方便显示for (int iteration = 0; iteration < iterations; iteration++){var contours = Cv2.FindContoursAsArray(binary, RetrievalModes.External, ContourApproximationModes.ApproxSimple);foreach (var contour in contours){try{if (contour.Length < 5) continue;double arcLen = Cv2.ArcLength(contour, true);if (arcLen < miniArcLength) continue;int[] hullIndices = Cv2.ConvexHullIndices(contour, true);if (hullIndices.Length < 3) continue;var defects = Cv2.ConvexityDefects(contour, hullIndices);if (defects == null || defects.Length == 0) continue;if (!defects.Any(a => a.Item3 / 256f > depthThreshold)) continue;Point[] hull = new Point[hullIndices.Length];for (int i = 0; i < hullIndices.Length; i++){hull[i] = contour[hullIndices[i]];}var rect = Cv2.BoundingRect(contour);using Mat mask = new Mat(rect.Height, rect.Width, MatType.CV_8UC1, Scalar.Black);Point[] TranslateContour(Point[] pts, Point offset){return pts.Select(p => new Point(p.X - offset.X, p.Y - offset.Y)).ToArray();}var hullLocal = TranslateContour(hull, rect.TopLeft);var contourLocal = TranslateContour(contour, rect.TopLeft);Cv2.DrawContours(mask, new List<Point[]>() { hullLocal }, 0, Scalar.White, -1);Cv2.DrawContours(mask, new List<Point[]>() { contourLocal }, 0, Scalar.Black, -1);var maskContours = Cv2.FindContoursAsArray(mask, RetrievalModes.External, ContourApproximationModes.ApproxSimple);if (maskContours.Length < 2) continue;var maxAreaContours = GetTop2MaxAreaContours(maskContours).Take(2).ToArray();var minDistancePoints = GetMinDistancePoint(maxAreaContours[0], maxAreaContours[1]);var distance = Math.Sqrt((minDistancePoints[0].X - minDistancePoints[1].X) * (minDistancePoints[0].X - minDistancePoints[1].X) + (minDistancePoints[0].Y - minDistancePoints[1].Y) * (minDistancePoints[0].Y - minDistancePoints[1].Y));if (distance > rect.Width / 2) continue;// 注意minDistancePoints中是mask局部坐标,转回resultImage全局坐标:Cv2.Line(binary, minDistancePoints[0] + rect.TopLeft, minDistancePoints[1] + rect.TopLeft, Scalar.Black, lineThickness);Cv2.Line(resultImage, minDistancePoints[0] + rect.TopLeft, minDistancePoints[1] + rect.TopLeft, Scalar.OrangeRed, lineThickness);}catch (Exception){continue;}}}return new ProcessingResult(resultImage);}catch (Exception ex){Console.WriteLine(ex);return new ProcessingResult();}
}private Point[][] GetTop2MaxAreaContours(Point[][] contours)
{if (contours == null || contours.Length == 0)return new Point[0][];// 按轮廓面积降序排序,取前2个var top2 = contours.OrderByDescending(contour => Cv2.ContourArea(contour)).ToArray();return top2;
}
private Point[] GetMinDistancePoint(Point[] contour, Point[] contour1)
{if (contour == null || contour1 == null || contour.Length == 0 || contour1.Length == 0)return new Point[0];Point minP1 = new Point();Point minP2 = new Point();double minDist = double.MaxValue;foreach (var p1 in contour){foreach (var p2 in contour1){double dist = Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y));if (dist < minDist){minDist = dist;minP1 = p1;minP2 = p2;}}}return new Point[] { minP1, minP2 };
}

上面是基于凸包凹陷的,那都说分水岭+距离变换好!!!但是,好是有前提的 对一同样大小颗粒的分割效果是很好的。但是对大小不一效果不太行,但是我也做了优化 下面给出大体逻辑。

 一种基于距离变幻+分水岭 检测重叠轮廓分割的方法

  1. 前处理:的噼里啪啦我就不说了,根据样品来(灰度,滤波,二值化,形态学...)
  2. 进行距离变换 DistanceTransform+Normalize
  3. 获取前景标记  Cv2.Threshold(distTrans8u, distTrans8u, foregroundThreshold * 255, 255, ThresholdTypes.Binary);
  4. 创建标记图像  Mat markers = Mat.Zeros(binary.Size(), MatType.CV_32S);
  5. 标记背景    using var sureBg = new Mat();
    Cv2.Dilate(binary, sureBg, new Mat(), iterations: 3);
  6. 应用分水岭算法 Cv2.Watershed(originalMat, markers);
  7. 前面几步都是烂大街的 随便一搜都有的代码 不清楚的直接搜 距离变换+分水岭
  8. 得到轮廓并绘制在一张与原图大小相等的黑图上并把边界涂色
      result = Mat.Zeros(originalMat.Size(), originalMat.Type());var boundaries = new Mat();Cv2.Compare(markers, new Scalar(-1), boundaries, CmpType.EQ);result.SetTo(new Scalar(255, 255, 255), boundaries);result.Row(0).SetTo(new Scalar(0, 0, 0));result.Row(result.Rows - 1).SetTo(new Scalar(0, 0, 0));result.Col(0).SetTo(new Scalar(0, 0, 0));result.Col(result.Cols - 1).SetTo(new Scalar(0, 0, 0));boundaries.Dispose();
  9.  上一步得到了所谓分水岭的轮廓,但是很有可能把你的小轮廓给干掉了 或者说正常的轮廓,那么我们需要合并:
    这是分水岭前后的图片,会发现少了很多。其实也不多就那几个。
              
    1.原图减去分水岭得到的二值图 再做形态学处理得到 图4
           
    2.合并分水岭二值图与图四

  10. 至于效果我觉得一般 逻辑嘛也觉得一般 我就是菜鸡。

打个总结,牛马不如骡子。 

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

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

相关文章

CentOS7 安装 rust 1.82.0

CentOS7 安装 rust 1.82.0 我在CentOS7.9中安装rust遇到报错版本低&#xff0c;再升级版本的过程中遇到诸多问题&#xff0c;简单记录。 遇到的问题 提示版本低 centos7 安装 ERROR: Rust 1.75.0 or newer required.Rust version 1.72.1 was found.原因是 CentOS7 的默认的软件…

Compose 适配 - 键鼠模式

一、概念不止触摸交互&#xff0c;在 ChromeOS 或外接键鼠的设备上&#xff0c;需要考虑焦点、悬停、右键等操作逻辑。二、使用2.1 焦点使用 Tab 键来导航&#xff0c;改变边框以提供清晰的焦点指示器。Composable fun Demo() {val interactionSource remember { MutableInter…

征服 Linux 网络:核心服务与实战解析

在当今的IT基础设施中&#xff0c;Linux作为服务器操作系统的基石&#xff0c;其强大的网络功能是其不可或缺的优势。对于任何志在成为高级系统管理员或运维工程师的人来说&#xff0c;精通Linux网络配置与服务管理是核心竞争力。 与日常应用不同&#xff0c;Linux网络管理往往…

Spark 之 DataFrame

# foreach useFeatureDF.rdd.foreachPartition {iter => iter.foreach {row =>val userId = row.getAs[Int]

射频信号(大宽高比)时频图目标检测anchors配置(下)

书接上文&#xff1a; 射频信号&#xff08;大宽高比&#xff09;时频图目标检测anchors配置&#xff08;上&#xff09; 三、4090加成检测效果深度优化 在4090 24G专用显存加持下继续探究大宽高比目标检测的奥秘&#xff1a; Conda环境迁移至租的云服 在云服上第一次测试…

跨境支付入门~国际支付结算(区块链篇)

摘要Web3区块链技术架构解析&#xff1a;从底层共识到应用生态本文系统梳理了Web3作为稳定币基础设施的技术架构&#xff0c;采用"数字共和国"的比喻框架&#xff0c;将区块链技术分解为六大核心模块&#xff1a;宪法根基&#xff08;区块链层&#xff09;&#xff1…

Docker 私服

什么是 Docker 私服&#xff1f; Docker 官方的 Docker Hub 是一个用于管理公共镜像的仓库&#xff0c;我们可以从上面拉取镜像 到本地&#xff0c;也可以把我们自己的镜像推送上去。 但是&#xff0c;有时候我们的服务器无法访问互联网&#xff0c;或者你不希望将自己的镜像…

DeepSeek vs ChatGPT:谁更胜一筹?

新兴 AI 聊天机器人的崛起与挑战&#xff1a;对话模型发展观察近年来&#xff0c;生成式人工智能领域持续取得突破&#xff0c;聊天机器人作为其中的代表&#xff0c;广泛应用于写作、编程、问答和信息处理等任务。2025 年初&#xff0c;国内某 AI 团队宣布了多项模型技术进展&…

飞算科技:以原创技术为翼,赋能产业数字化转型

在数字经济浪潮席卷全球的当下&#xff0c;一批专注于技术创新的中国企业正加速崛起&#xff0c;飞算数智科技&#xff08;深圳&#xff09;有限公司&#xff08;简称 “飞算科技”&#xff09;便是其中的佼佼者。作为一家国家级高新技术企业&#xff0c;飞算科技以自主创新为核…

电商接口什么意思?

“电商接口”这四个字&#xff0c;在中文互联网上出现的频次越来越高&#xff1a;商家后台、小程序、ERP、数据大屏……几乎任何与线上零售沾边的场景都会提到它。然而&#xff0c;对大多数刚进入电商行业的新人&#xff0c;甚至一些已经开了很多年网店的老板来说&#xff0c;这…

前端面试专栏-前沿技术:30.跨端开发技术(React Native、Flutter)

&#x1f525; 欢迎来到前端面试通关指南专栏&#xff01;从js精讲到框架到实战&#xff0c;渐进系统化学习&#xff0c;坚持解锁新技能&#xff0c;祝你轻松拿下心仪offer。 前端面试通关指南专栏主页 前端面试专栏规划详情 跨端开发技术&#xff08;React Native、Flutter&am…

复盘—MySQL触发器实现监听数据表值的变化,对其他数据表做更新

文章目录 MySQL交换数据库表中两列的值(额外的知识) 为防止后面有疑问,提前解释为什么需要 `$$` ? 第一版需求 第二版需求 第三版需求 注意事项: 存在的严重问题 最终版 关键修复说明: 完整测试场景: 额外建议(如果需要显式处理NULL): COALESCE函数 业务中出现的问题…

SpringCloud【Sentinel】

1&#xff0c;工作原理 2&#xff0c;常见规则 流量控制&#xff08;FlowRule&#xff09; 1&#xff0c;阈值类型设置如下 单机均摊&#xff1a;每个机器均摊&#xff0c;比如阈值填5&#xff0c;三个机器&#xff0c;就个机器都可以有5个 总体阈值&#xff1a;所有机器总阈…

解构未来金融:深入剖析DeFi与去中心化交易所(DEX)的技术架构

今天&#xff0c;我们来聊一个颠覆传统金融界的热门话题——DeFi&#xff08;去中心化金融&#xff09;。大家可能听说过它如何承诺将银行、交易所、保险等金融服务构建在一个开放、无需许可的区块链网络上。而这一切魔法的核心&#xff0c;正是其独特的“技术架构”。 在这篇文…

中国西北典型绿洲区土壤水分特征(2018-2019年)

数据集摘要该数据包含张掖绿洲黑河沿岸湿地、过渡带杨树林土壤水分、温度数据。数据采集时间为2018年至2019年&#xff0c;采集地点为张掖绿洲&#xff0c;数据为日数据。该数据集是按照课题制定的试验方案和中国生态系统研究网络编著的陆地生态系统水土气生观测规范进行数据的…

MySQL高可用部署

目录 一、MHA&#xff08;一主多从模式&#xff09; 1.环境准备&#xff08;所有节点&#xff09; 2. 部署 MySQL 主从复制&#xff08;MasterSlave&#xff09; 3.部署 MHA Manager&#xff08;管理节点&#xff09; &#xff08;1&#xff09;安装 MHA Manager &#xf…

从 XSS 到 Bot 攻击:常见网络攻击防不胜防?雷池 WAF 用全场景防护为网站筑牢安全墙

1. 网络攻击类型当前常见的网络攻击类型包括&#xff1a;重放攻击&#xff08;HTTP Request Replay Attack&#xff09;&#xff1a;攻击者截获合法用户的 HTTP 请求并重新发送&#xff0c;以欺骗服务器执行相同操作。危害包括消耗服务器资源、大量抓取数据或绕过认证操作敏感接…

【王树森推荐系统】推荐系统涨指标的方法05:特殊用户人群

为什么要特殊对待特殊人群&#xff1f; 新用户&#xff0c;低活用户的行为很少&#xff0c;个性化推荐不准确。个性化的召回和排序都需要基于用户的历史行为&#xff0c;如果历史行为少&#xff0c;个性化就做不好&#xff0c;尤其是新用户&#xff0c;这就需要策略把个性化做的…

Java 大视界 -- Java 大数据在智能家居能源管理与节能优化中的深度应用(361)

Java 大视界 -- Java 大数据在智能家居能源管理与节能优化中的深度应用&#xff08;361&#xff09;引言&#xff1a;正文&#xff1a;一、Java 构建的智能家居能源数据架构1.1 多源能耗数据实时采集1.2 家庭能源画像与异常检测二、Java 驱动的节能策略与智能控制2.1 多场景节能…

从零开始的云计算生活——番外5,使用ELK实现对应用日志的监控

目录 一.环境准备 试验机安装 修改文件配置 二.收集测试机&#xff08;test&#xff09;日志 配置pipline文件 配置filebeat配置文件 三.收集测试机nginx日志 下载安装nginx 修改filebeat文件 修改pipline文件 四.收集网络服务模块日志 1.DHCP 下载dhcp 修改配置…