[C#]使用OpencvSharp去除面积较小的连通域
【C++介绍】
关于opencv实现有比较好的算法,可以参考这个博客OpenCV去除面积较小的连通域_c#opencv 筛选小面积区域-CSDN博客
但是没有对应opencvsharp实现同类算法,为了照顾懂C#编程同学们,因此将 去除面积较小的连通域算法转成C#代码。
方法一流程:
//=======函数实现=====================================================================
void RemoveSmallRegion(Mat &Src, Mat &Dst, int AreaLimit, int CheckMode, int NeihborMode)
{int RemoveCount = 0;//新建一幅标签图像初始化为0像素点,为了记录每个像素点检验状态的标签,0代表未检查,1代表正在检查,2代表检查不合格(需要反转颜色),3代表检查合格或不需检查 //初始化的图像全部为0,未检查 Mat PointLabel = Mat::zeros(Src.size(), CV_8UC1);if (CheckMode == 1)//去除小连通区域的白色点 {//cout << "去除小连通域.";for (int i = 0; i < Src.rows; i++){for (int j = 0; j < Src.cols; j++){if (Src.at<uchar>(i, j) < 10){PointLabel.at<uchar>(i, j) = 3;//将背景黑色点标记为合格,像素为3 }}}}else//去除孔洞,黑色点像素 {//cout << "去除孔洞";for (int i = 0; i < Src.rows; i++){for (int j = 0; j < Src.cols; j++){if (Src.at<uchar>(i, j) > 10){PointLabel.at<uchar>(i, j) = 3;//如果原图是白色区域,标记为合格,像素为3 }}}}vector<Point2i>NeihborPos;//将邻域压进容器 NeihborPos.push_back(Point2i(-1, 0));NeihborPos.push_back(Point2i(1, 0));NeihborPos.push_back(Point2i(0, -1));NeihborPos.push_back(Point2i(0, 1));if (NeihborMode == 1){//cout << "Neighbor mode: 8邻域." << endl;NeihborPos.push_back(Point2i(-1, -1));NeihborPos.push_back(Point2i(-1, 1));NeihborPos.push_back(Point2i(1, -1));NeihborPos.push_back(Point2i(1, 1));}else int a = 0;//cout << "Neighbor mode: 4邻域." << endl;int NeihborCount = 4 + 4 * NeihborMode;int CurrX = 0, CurrY = 0;//开始检测 for (int i = 0; i < Src.rows; i++){for (int j = 0; j < Src.cols; j++){if (PointLabel.at<uchar>(i, j) == 0)//标签图像像素点为0,表示还未检查的不合格点 { //开始检查 vector<Point2i>GrowBuffer;//记录检查像素点的个数 GrowBuffer.push_back(Point2i(j, i));PointLabel.at<uchar>(i, j) = 1;//标记为正在检查 int CheckResult = 0;for (int z = 0; z < GrowBuffer.size(); z++){for (int q = 0; q < NeihborCount; q++){CurrX = GrowBuffer.at(z).x + NeihborPos.at(q).x;CurrY = GrowBuffer.at(z).y + NeihborPos.at(q).y;if (CurrX >= 0 && CurrX<Src.cols&&CurrY >= 0 && CurrY<Src.rows) //防止越界 {if (PointLabel.at<uchar>(CurrY, CurrX) == 0){GrowBuffer.push_back(Point2i(CurrX, CurrY)); //邻域点加入buffer PointLabel.at<uchar>(CurrY, CurrX) = 1; //更新邻域点的检查标签,避免重复检查 }}}}if (GrowBuffer.size()>AreaLimit) //判断结果(是否超出限定的大小),1为未超出,2为超出 CheckResult = 2;else{CheckResult = 1;RemoveCount++;//记录有多少区域被去除 }for (int z = 0; z < GrowBuffer.size(); z++){CurrX = GrowBuffer.at(z).x;CurrY = GrowBuffer.at(z).y;PointLabel.at<uchar>(CurrY, CurrX) += CheckResult;//标记不合格的像素点,像素值为2 }//********结束该点处的检查********** }}}CheckMode = 255 * (1 - CheckMode);//开始反转面积过小的区域 for (int i = 0; i < Src.rows; ++i){for (int j = 0; j < Src.cols; ++j){if (PointLabel.at<uchar>(i, j) == 2){Dst.at<uchar>(i, j) = CheckMode;}else if (PointLabel.at<uchar>(i, j) == 3){Dst.at<uchar>(i, j) = Src.at<uchar>(i, j);}}}//cout << RemoveCount << " objects removed." << endl;
}
//=======函数实现=====================================================================//=======调用函数=====================================================================Mat img;img = imread("D:\\1_1.jpg", 0);//读取图片threshold(img, img, 128, 255, CV_THRESH_BINARY_INV);imshow("去除前", img);Mat img1;RemoveSmallRegion(img, img, 200, 0, 1);imshow("去除后", img);waitKey(0);
//=======调用函数=====================================================================
此段代码包含一个名为RemoveSmallRegion的函数,其功能是从给定的二值图像中移除符合条件的小连通区域。函数接受五个参数:
Mat &Src: 输入的原始二值图像(单通道,通常为黑白图像)。Mat &Dst: 输出的目标图像,存储经过处理后的结果。int AreaLimit: 面积阈值,低于该阈值的连通区域会被移除。int CheckMode: 检查模式,决定要移除的是图像中的小连通白区还是小连通黑区。CheckMode == 1: 移除小连通白区(白色像素点构成的区域)。CheckMode == 0: 移除小连通黑区(黑色像素点构成的区域)。
int NeihborMode: 邻域模式,决定采用4邻域还是8邻域算法进行连通区域扩展。NeihborMode == 1: 使用8邻域算法(包括上下左右和四个对角方向相邻的像素)。NeihborMode == 0: 使用4邻域算法(仅考虑上下左右相邻的像素)。
函数的具体实现步骤如下:
-
初始化
RemoveCount变量记录移除的连通区域数量,创建与输入图像相同大小的PointLabel矩阵作为标签图像,用于记录每个像素点的检验状态(0:未检查;1:正在检查;2:检查不合格;3:检查合格或无需检查)。 -
根据
CheckMode确定移除目标,分别针对小连通白区和小连通黑区对PointLabel进行初始化。对于不需要移除的像素点(即背景或前景),将其标签设为3,表示已检查且合格。 -
定义
NeihborPos容器存储邻域位置,并根据NeihborMode选择使用4邻域或8邻域。 -
使用两层嵌套循环遍历输入图像的所有像素点。对于未检查的像素点(标签为0),执行以下操作:
- 初始化
GrowBuffer容器,用于记录当前连通区域内的像素点。 - 将当前像素点标记为正在检查(标签设为1),并启动基于邻域扩展的生长过程。
- 使用广度优先搜索(BFS)策略,依次访问
GrowBuffer中的像素点及其邻域像素,将未检查的邻域像素加入GrowBuffer并标记为正在检查。 - 当遍历完所有邻域像素后,根据
GrowBuffer的大小与AreaLimit比较,判断该连通区域是否应被移除。 - 根据判断结果更新
GrowBuffer内所有像素点在PointLabel上的标签为2(检查不合格)或保持为1(检查合格)。
- 初始化
-
得到最终的
PointLabel后,根据CheckMode对255取反(即255 * (1 - CheckMode)),用于后续翻转图像像素值。遍历Src和PointLabel,将标签为2的像素点在Dst中翻转颜色(即将白变黑或黑变白),标签为3的像素点保持原色不变。
最后,代码提供了对RemoveSmallRegion函数的调用示例:
- 读取图像"D:\1_1.jpg",并对其进行二值化处理(阈值为128,反相)。
- 显示二值化处理后的原始图像。
- 调用
RemoveSmallRegion函数,移除面积小于200的黑区(CheckMode = 0),使用8邻域算法(NeihborMode = 1)。 - 显示经过处理后的图像。
- 等待用户按键后关闭窗口。
方法二流程:
//测试
void CCutImageVS2013Dlg::OnBnClickedTestButton1()
{vector<vector<Point> > contours; //轮廓数组vector<Point2d> centers; //轮廓质心坐标 vector<vector<Point> >::iterator itr; //轮廓迭代器vector<Point2d>::iterator itrc; //质心坐标迭代器vector<vector<Point> > con; //当前轮廓double area;double minarea = 1000;double maxarea = 0;Moments mom; // 轮廓矩Mat image, gray, edge, dst;image = imread("D:\\66.png");cvtColor(image, gray, COLOR_BGR2GRAY);Mat rgbImg(gray.size(), CV_8UC3); //创建三通道图blur(gray, edge, Size(3, 3)); //模糊去噪threshold(edge, edge, 200, 255, THRESH_BINARY_INV); //二值化处理,黑底白字//--------去除较小轮廓,并寻找最大轮廓--------------------------findContours(edge, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); //寻找轮廓itr = contours.begin(); //使用迭代器去除噪声轮廓while (itr != contours.end()){area = contourArea(*itr); //获得轮廓面积if (area<minarea) //删除较小面积的轮廓 {itr = contours.erase(itr); //itr一旦erase,需要重新赋值}else{itr++;}if (area>maxarea) //寻找最大轮廓{maxarea = area;}}dst = Mat::zeros(image.rows, image.cols, CV_8UC3);/*绘制连通区域轮廓,计算质心坐标*/Point2d center;itr = contours.begin();while (itr != contours.end()){area = contourArea(*itr); con.push_back(*itr); //获取当前轮廓if (area == maxarea){vector<Rect> boundRect(1); //定义外接矩形集合boundRect[0] = boundingRect(Mat(*itr));cvtColor(gray, rgbImg, COLOR_GRAY2BGR);Rect select;select.x = boundRect[0].x;select.y = boundRect[0].y;select.width = boundRect[0].width;select.height = boundRect[0].height;rectangle(rgbImg, select, Scalar(0, 255, 0), 3, 2); //用矩形画矩形窗drawContours(dst, con, -1, Scalar(0, 0, 255), 2); //最大面积红色绘制}elsedrawContours(dst, con, -1, Scalar(255, 0, 0), 2); //其它面积蓝色绘制con.pop_back();//计算质心mom = moments(*itr);center.x = (int)(mom.m10 / mom.m00);center.y = (int)(mom.m01 / mom.m00);centers.push_back(center);itr++;}imshow("rgbImg", rgbImg);//imshow("gray", gray);//imshow("edge", edge);imshow("origin", image);imshow("connected_region", dst);waitKey(0);return;}
提供的代码为一个使用OpenCV库对输入图像"D:\66.png"进行处理的C++实现,执行以下任务:
-
图像预处理:
- 读取图像并将其从BGR色彩空间转换为灰度图像(
cvtColor)。 - 应用高斯模糊,使用大小为
3x3的核来减少噪声(blur)。 - 对模糊后的图像执行二值阈值处理,阈值设为
200,将高于该值的像素设置为白色,其余为黑色(threshold)。
- 读取图像并将其从BGR色彩空间转换为灰度图像(
-
轮廓检测与筛选:
- 使用
findContours函数在二值化图像上查找外部轮廓,存储在contours容器中。 - 遍历所有轮廓,通过
contourArea函数计算每个轮廓的面积。- 删除面积小于最小阈值
minarea(初始设定为1000)的噪声轮廓,使用迭代器itr进行动态删除。 - 同时记录下当前遍历到的最大轮廓面积
maxarea。
- 删除面积小于最小阈值
- 最后保留下来的轮廓为满足面积条件的有效轮廓。
- 使用
-
绘制轮廓与计算质心:
- 创建一个新的
Mat对象dst,用于绘制处理结果。 - 初始化一个空的
centers向量,用于存储各个轮廓的质心坐标。 - 再次遍历有效轮廓:
- 将当前轮廓添加到临时向量
con中。 - 计算当前轮廓面积。
- 如果面积等于最大面积
maxarea,则执行以下操作:- 计算当前轮廓的外接矩形,并用绿色边框在RGB图像
rgbImg上绘制。 - 在最终输出图像
dst上以红色绘制当前轮廓。
- 计算当前轮廓的外接矩形,并用绿色边框在RGB图像
- 否则,在
dst上以蓝色绘制当前轮廓。 - 使用
moments函数计算当前轮廓的矩,进而得到质心坐标,并将其添加到centers向量。 - 清除临时向量
con中的当前轮廓。
- 将当前轮廓添加到临时向量
- 显示各阶段处理结果:
- RGB图像
rgbImg(仅包含最大轮廓的绿色外接矩形)。 - 原始灰度图像
gray(注释掉未显示)。 - 二值边缘图像
edge(注释掉未显示)。
- RGB图像
- 创建一个新的
【C#版本效果展示】
方法一使用opencvsharp效果:

方法二opencvsharp效果:

可见已经用opencvsharp复刻C++版本算法。
【测试环境】
vs2019
netframework4.7.2
opencvsharp4.8.0
【源码下载地址】
https://download.csdn.net/download/FL1623863129/89074335
相关文章:
[C#]使用OpencvSharp去除面积较小的连通域
【C介绍】 关于opencv实现有比较好的算法,可以参考这个博客OpenCV去除面积较小的连通域_c#opencv 筛选小面积区域-CSDN博客 但是没有对应opencvsharp实现同类算法,为了照顾懂C#编程同学们,因此将 去除面积较小的连通域算法转成C#代码。 方…...
联邦学习目前面临的挑战以及解决方案
学习目标: 联邦学习目前面临的挑战以及解决方案 学习内容: 联邦学习是一种新兴的人工智能基础技术,它在保障大数据交换时的信息安全、保护终端数据和个人数据隐私、保证合法合规的前提下,在多参与方或多计算结点之间开展高效率的…...
Day60:WEB攻防-XMLXXE安全无回显方案OOB盲注DTD外部实体黑白盒挖掘
目录 XML&XXE-传输-原理&探针&利用&玩法 XXE 黑盒发现 XXE 白盒发现 XXE修复防御方案 有回显 无回显 XML&XXE-黑盒-JSON&黑盒测试&类型修改 XML&XXE-白盒-CMS&PHPSHE&无回显 知识点: 1、XXE&XML-原理-用途&…...
解锁网络安全新境界:雷池WAF社区版让网站防护变得轻而易举!
网站运营者的救星:雷池WAF社区版 ️ 嘿朋友们!今天我超级激动要跟你们分享一个神器——雷池WAF社区版。这个宝贝对我们这帮网站运营者来说,简直就是保护伞! 智能语义分析技术:超级侦探上线 先说说为啥我这么稀饭它。雷…...
RabbitMQ安装详细教程
(一)在Windows系统上安装Erlang的步骤如下: 打开Erlang的官方下载页面,选择适合你的Windows系统的版本进行下载。 下载完成后,双击运行下载的.exe文件,进入Erlang的安装向导。 在安装向导中,按…...
如何快速写出一个完整的测试用例
测试用例是为了验证软件功能或需求而设计的一组测试输入、执行条件和预期结果。编写测试用例的目的是确保测试过程全面高效、有据可查。 一般来说,编写测试用例的流程包括以下几个步骤: 分析需求:阅读需求文档,理解软件的功能和业…...
Docker容器与虚拟化技术:OpenEuler 部署 ES 与 Kibana
目录 一、实验 1.环境 2.OpenEuler 部署 ES (EalasticSearch) 3.OpenEuler 部署 Kibana 4.部署 Elasticvue插件 5.使用cpolar内网穿透 6.使用Elasticvue 一、实验 1.环境 (1)主机 表1 主机 系统架构版本IP备注LinuxopenEuler22.03 LTS SP2 1…...
数学中的各种符号虚数概念
max i∈SA i ≥ ∑ i∈SB i. 这个不等式表达的意思是对于集合 S 中的任意非空子集,子集中的最大的 A_i(A 的元素)的值都大于等于子集中所有 B_i(B 的元素)的值的总和。换句话说,集合 S 中的最大…...
什么是中间件
中间件是指在应用程序与操作系统之间提供服务的软件,它可以隐藏底层操作系统的复杂性,为应用程序提供各种实用的服务,以便应用程序更好地实现业务逻辑。中间件通常提供如下几种服务: 数据库连接:中间件可以为应用程序提…...
RabbitMQ面经 手敲浓缩版
保证可靠性 生产者 本地事务完成和消息发送同时完成 通过事务消息完成 重写confirm在里面做逻辑处理 确保发送成功(不成功就放入到重试队列) MQ 打开持久化确保消息不会丢失 消费者 改成手动回应 不重复消费 生产者 保证不重复发送消息 消费者…...
解锁金融数据中心场景,实现国产化AD替代,宁盾身份域管为信创电脑、应用提供统一管理
随着信创国产化改造持续推进,越来越多的金融机构不断采购信创服务器、PC、办公软件等,其 IT 基础设施逐渐迁移至国产化 IT 架构下。为支撑国产化 IT 基础设施的正常使用和集中管理运维,某金融机构数据中心的微软Active Directory(…...
Django的js文件没有响应(DOMContentLoaded)
问题出现的原因是因为当浏览器解析到“script”标签并执行其中的JavaScript代码时,页面上的DOM元素尚未完全加载和渲染。这意味着,当尝试通过document.getElementById(‘create-theme-button’)获取元素时,该元素还不存在,导致add…...
滑动窗口代码模板
代码模板: //滑动窗口伪代码 class Solution { public:int minWindow(string s) {// 同方向移动,起始的时候,都位于 0,表示我们定义搜索区间为 [left, right) ,此时区间为空区间int left 0;int right 0;while(right…...
SpringBoot实现邮箱验证
目录 1、开启邮箱IMAP/SMTP服务,获取授权码 2、相关代码 1、使用配置Redis(用于存储验证码,具有时效性) 2、邮箱依赖和hutool(用于随机生成验证码) 3、配置Redis和邮箱信息 4、开启Redis服务 5、编写发送…...
Mac安装Docker提示Another application changed your Desktop configuration解决方案
1. 问题描述 Mac安装Docker后,提示Another application changed your Desktop configuration,Re-apply configurations无效 2. 解决方案 在终端执行下述命令即可解决: sudo ln -sf /Applications/Docker.app/Contents/Resources/bin/docke…...
5分钟安装docker和docker compose环境
5分钟安装docker和docker compose环境 5分钟安装docker和docker compose环境环境介绍卸载docker环境安装docker安装docker compose 5分钟安装docker和docker compose环境 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑…...
leetcode热题100.跳跃游戏2
Problem: 45. 跳跃游戏 II 文章目录 题目思路复杂度Code 题目 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i j] 处: …...
【前端】CSS(引入方式+选择器+常用元素属性+盒模型+弹性布局)
文章目录 CSS一、什么是CSS二、语法规范三、引入方式1.内部样式表2.行内样式表3.外部样式 四、选择器1.选择器的种类1.基础选择器:单个选择器构成的1.标签选择器2.类选择器3.id 选择器4.通配符选择器 2.复合选择器1.后代选择器2.子选择器3.并集选择器4.伪类选择器 五…...
迷茫下是自我提升
长夜漫漫,无心睡眠。心中所想,心中所感,忧愁当前,就执笔而下,写下这篇文章。 回忆过往 回想当初为啥学前端,走前端这条路,学校要求嘛,兴趣爱好嘛,还是为了钱。 时间带着…...
用vscode仿制小米官网
html内容: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><link rel&quo…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...
云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...
从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...
Linux 下 DMA 内存映射浅析
序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存,但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程,可以参考这篇文章,我觉得写的非常…...
二维FDTD算法仿真
二维FDTD算法仿真,并带完全匹配层,输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...
flow_controllers
关键点: 流控制器类型: 同步(Sync):发布操作会阻塞,直到数据被确认发送。异步(Async):发布操作非阻塞,数据发送由后台线程处理。纯同步(PureSync…...
