场景需求
使用OpenCV,往往遇到这类场景:需要清除目标图像中比较小的噪声区,保留主要区域信息。
特此分享自己写的一个简单的清除小面积连通域函数,逻辑比较简单,给大家留出了足够的发展空间,根据自身场景需求进行调整。
原理可以简单归结为:搜索图像的连通区轮廓->遍历各个连通区->基于阈值删除面积较小的连通区
运行速度方面,我没单独测试过这个单元,大家如果试过之后太慢可以评论告诉我哦~
反正平常我工作跑那种2000*2000的图像,这个函数的耗时几乎忽略不计。。。
C++实现代码
- /**
- * @brief Clear_MicroConnected_Areas 清除微小面积连通区函数
- * @param src 输入图像矩阵
- * @param dst 输出结果
- * @return min_area 设定的最小面积清除阈值
- */
- void Clear_MicroConnected_Areas(cv::Mat src, cv::Mat &dst, double min_area)
- {
- // 备份复制
- dst = src.clone();
- std::vector<std::vector<cv::Point> > contours; // 创建轮廓容器
- std::vector<cv::Vec4i> hierarchy;
-
- // 寻找轮廓的函数
- // 第四个参数CV_RETR_EXTERNAL,表示寻找最外围轮廓
- // 第五个参数CV_CHAIN_APPROX_NONE,表示保存物体边界上所有连续的轮廓点到contours向量内
- cv::findContours(src, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE, cv::Point());
-
- if (!contours.empty() && !hierarchy.empty())
- {
- std::vector<std::vector<cv::Point> >::const_iterator itc = contours.begin();
- // 遍历所有轮廓
- while (itc != contours.end())
- {
- // 定位当前轮廓所在位置
- cv::Rect rect = cv::boundingRect(cv::Mat(*itc));
- // contourArea函数计算连通区面积
- double area = contourArea(*itc);
- // 若面积小于设置的阈值
- if (area < min_area)
- {
- // 遍历轮廓所在位置所有像素点
- for (int i = rect.y; i < rect.y + rect.height; i++)
- {
- uchar *output_data = dst.ptr<uchar>(i);
- for (int j = rect.x; j < rect.x + rect.width; j++)
- {
- // 将连通区的值置0
- if (output_data[j] == 255)
- {
- output_data[j] = 0;
- }
- }
- }
- }
- itc++;
- }
- }
- }
测试代码
- #include<iostream>
- #include<opencv2/opencv.hpp>
-
- using namespace std;
- using namespace cv;
-
- void Clear_MicroConnected_Areas(cv::Mat src, cv::Mat &dst, double min_area);
-
- int main(void)
- {
- Mat A = Mat::zeros(500, 500, CV_8UC1);
- circle(A, Point2i(100, 100), 50, 255, -1);
- circle(A, Point2i(300, 400), 15, 255, -1);
- Mat B;
- Clear_MicroConnected_Areas(A, B, 1000);
-
- imshow("before:A", A);
- imshow("after:B", B);
- waitKey(0);
-
- system("pause");
- return 0;
- }
-
- void Clear_MicroConnected_Areas(cv::Mat src, cv::Mat &dst, double min_area)
- {
- // 备份复制
- dst = src.clone();
- std::vector<std::vector<cv::Point> > contours; // 创建轮廓容器
- std::vector<cv::Vec4i> hierarchy;
-
- // 寻找轮廓的函数
- // 第四个参数CV_RETR_EXTERNAL,表示寻找最外围轮廓
- // 第五个参数CV_CHAIN_APPROX_NONE,表示保存物体边界上所有连续的轮廓点到contours向量内
- cv::findContours(src, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE, cv::Point());
-
- if (!contours.empty() && !hierarchy.empty())
- {
- std::vector<std::vector<cv::Point> >::const_iterator itc = contours.begin();
- // 遍历所有轮廓
- while (itc != contours.end())
- {
- // 定位当前轮廓所在位置
- cv::Rect rect = cv::boundingRect(cv::Mat(*itc));
- // contourArea函数计算连通区面积
- double area = contourArea(*itc);
- // 若面积小于设置的阈值
- if (area < min_area)
- {
- // 遍历轮廓所在位置所有像素点
- for (int i = rect.y; i < rect.y + rect.height; i++)
- {
- uchar *output_data = dst.ptr<uchar>(i);
- for (int j = rect.x; j < rect.x + rect.width; j++)
- {
- // 将连通区的值置0
- if (output_data[j] == 255)
- {
- output_data[j] = 0;
- }
- }
- }
- }
- itc++;
- }
- }
- }
测试效果
图1 处理前后图
到此这篇关于OpenCV-清除小面积连通域的文章就介绍到这了,更多相关OpenCV-清除小面积连通域内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!