OpenCV删除面积小的区域 实现图像二值化分割 标记连通区域

 OpenCV删除面积小的区域 实现图像二值化分割 标记连通区域

   【尊重原创,转载请注明出处】http://blog.csdn.net/guyuealian/article/details/78142749
   之前本博客在Matlab实现了《Matlab形态学图像处理:二值图像分割 标记连通区域和重心位置 删除连通区域》http://blog.csdn.net/guyuealian/article/details/71440949,现在本人使用OpenCV实现这一功能:对图像进行二值化分割,并用“红色矩形”标记连通区域的面积,为了减少噪声的干扰,删除面积小的区域,代码中将连通区域面积(像素个数)不足100的区域认为是噪声点,并将其删除(即置为背景黑色)。本人制作了一个GIF动画图,以便大家观看效果图:

OpenCV参考代码如下:

#include "stdafx.h"
#include <iostream>  
#include<vector>
#include<algorithm>
#include <opencv2\opencv.hpp>  
#include <opencv2\highgui\highgui.hpp>  
using namespace std;
using namespace cv;


//轮廓按照面积大小升序排序
bool ascendSort(vector<Point> a, vector<Point> b) {
	return a.size() < b.size();

}

//轮廓按照面积大小降序排序
bool descendSort(vector<Point> a, vector<Point> b) {
	return a.size() > b.size();
}
int main() {
	Mat srcImage = imread("D:\\OpencvTest\\123.jpg");
	Mat thresholdImage;
	Mat grayImage;
	cvtColor(srcImage, grayImage, CV_BGR2GRAY);
	threshold(grayImage, thresholdImage, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
	//Mat resultImage;
	//thresholdImage.copyTo(resultImage);
	vector< vector< Point> > contours;  //用于保存所有轮廓信息
	vector< vector< Point> > contours2; //用于保存面积不足100的轮廓
	vector<Point> tempV;				//暂存的轮廓

	findContours(thresholdImage, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
	//cv::Mat labels;
	//int N = connectedComponents(resultImage, labels, 8, CV_16U);
	//findContours(labels, contours2, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

	//轮廓按照面积大小进行升序排序
	sort(contours.begin(), contours.end(), ascendSort);//升序排序
	vector<vector<Point> >::iterator itc = contours.begin();
	int i = 0;
	while (itc != contours.end())
	{
		//获得轮廓的矩形边界
		Rect rect = boundingRect(*itc);
		int x = rect.x;
		int y = rect.y;
		int w = rect.width;
		int h = rect.height;
		//绘制轮廓的矩形边界
		cv::rectangle(srcImage, rect, { 0, 0, 255 }, 1);
		//保存图片
		char str[10];
		sprintf(str, "%d.jpg", i++);
		cv::imshow("srcImage", srcImage);
		imwrite(str, srcImage);
		waitKey(1000);
	
		if (itc->size() < 100)
		{
			//把轮廓面积不足100的区域,放到容器contours2中,
			tempV.push_back(Point(x, y));
			tempV.push_back(Point(x, y+h));
			tempV.push_back(Point(x+w, y+h));
			tempV.push_back(Point(x+w, y));
			contours2.push_back(tempV);
			/*也可以直接用:contours2.push_back(*itc);代替上面的5条语句*/
			//contours2.push_back(*itc);

			//删除轮廓面积不足100的区域,即用黑色填充轮廓面积不足100的区域:
			cv::drawContours(srcImage, contours2, -1, Scalar(0,0,0), CV_FILLED);
		}
		//保存图片
		sprintf(str, "%d.jpg", i++);
		cv::imshow("srcImage", srcImage);
		imwrite(str, srcImage);
		cv::waitKey(100);
		tempV.clear();
		++itc;
	}
	return 0;
}

【2】findContours的用法:

using namespace std;
using namespace cv;
using namespace cv::xphoto;
#include "stdafx.h"
#include <iostream>  
#include<vector>
#include<algorithm>
#include <opencv2\opencv.hpp>  
#include <opencv2\highgui\highgui.hpp>  
using namespace std;
using namespace cv;

//轮廓按照面积大小升序排序
bool ascendSort(vector<Point> a, vector<Point> b) {
	return a.size() < b.size();

}

//轮廓按照面积大小降序排序
bool descendSort(vector<Point> a, vector<Point> b) {
	return a.size() > b.size();
}

//自己实现的将灰度图像转为三通道的BGR图像
cv::Mat gray2BGR(cv::Mat grayImg) {
	if (grayImg.channels() == 3)
		return grayImg;
	cv::Mat bgrImg = cv::Mat::zeros(grayImg.size(), CV_8UC3);
	std::vector<cv::Mat> bgr_channels;
	cv::split(bgrImg, bgr_channels);
	bgr_channels.at(0) = grayImg;
	bgr_channels.at(1) = grayImg;
	bgr_channels.at(2) = grayImg;
	cv::merge(bgr_channels, bgrImg);
	return bgrImg;
}

//自定义的drawImage函数的功能类似于OpenCV的drawContours函数
cv::Mat drawImage(cv::Mat image, vector< vector< Point> > pointV) {
	cv::Mat destImage=image.clone();
	if (destImage.channels()==1)
	{
		destImage = gray2BGR(destImage);
	}
	for (size_t i=0;i<pointV.size();i++)
	{
		for (size_t j = 0; j<pointV.at(i).size(); j++)
		{
			cv::Point point = pointV.at(i).at(j);
			destImage.at<Vec3b>(point) = cv::Vec3b(0, 0, saturate_cast<uchar>(255-i*5));
		}

	}
	return destImage;
}
int main() {
	Mat srcImage = imread("D:\\OpencvTest\\mask5.jpg");
	cv::imshow("srcImage", srcImage);
	Mat thresholdImage;
	Mat grayImage;
	cvtColor(srcImage, grayImage, CV_BGR2GRAY);
	threshold(grayImage, thresholdImage, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
	cv::Mat mask = thresholdImage.clone();
	//(1)CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE
	vector< vector< Point> > contours1;
	findContours(mask, contours1, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
	cv::Mat destImage = drawImage(mask, contours1);
	imshow("destImage", destImage);

	//(2)CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE
	vector< vector< Point> > contours2; 
	findContours(mask, contours2, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
	cv::Mat destImage2 = drawImage(mask, contours2);
	imshow("destImage2", destImage2);

	//(3)CV_RETR_LIST, CV_CHAIN_APPROX_NONE
	vector< vector< Point> > contours3; 
	findContours(mask, contours3, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
	cv::Mat destImage3 = drawImage(mask, contours3);
	imshow("destImage3", destImage3);


	//(4)CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE
	vector< vector< Point> > contours4;
	findContours(mask, contours4, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
	cv::Mat destImage4 = drawImage(mask, contours4);
	imshow("destImage4", destImage4);


	//cv::Mat imge2 = gray2BGR(mask);
	//drawContours(imge2, contours4, -1, cv::Scalar(0, 0, 255), 1);
	//cv::fillConvexPoly(mask, contours1.at(0), cv::Scalar(255, 255, 255));
	cv::waitKey(0);
	return 0;
}


如果你觉得该帖子帮到你,还望贵人多多支持,鄙人会再接再厉,继续努力的~



  • 16
    点赞
  • 106
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
好的,我来回答你的问题。首先,连通区域标记图像处理中常用的一种算法,可以用来对图像中的连通区域进行标记,常用于图像分割、目标检测等领域。 二次扫描算法是一种比较常用的连通区域标记算法,下面我将用C++和OpenCV实现这个算法。 首先,我们需要加载一张图像并对其进行二值化处理,这里我使用OpenCV中的cv::threshold函数来实现: ```cpp cv::Mat src = cv::imread("test.jpg", cv::IMREAD_GRAYSCALE); cv::Mat dst; cv::threshold(src, dst, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU); ``` 接下来,我们需要定义一个结构体来表示标记信息: ```cpp struct LabelInfo { int label; int area; int xmin; int ymin; int xmax; int ymax; }; ``` 其中,label表示标记的编号,area表示标记面积,xmin、ymin、xmax、ymax表示标记的最小外接矩形。 然后,我们需要定义一个函数来实现二次扫描算法: ```cpp void scan(cv::Mat& img, std::vector<LabelInfo>& labels) { int rows = img.rows; int cols = img.cols; int label = 0; std::vector<int> parent(rows * cols); // 第一次扫描 for (int i = 0; i < rows; i++) { int* data = img.ptr<int>(i); for (int j = 0; j < cols; j++) { if (data[j] != 0) { int left = (j == 0) ? 0 : parent[i * cols + j - 1]; int up = (i == 0) ? 0 : parent[(i - 1) * cols + j]; if (left == 0 && up == 0) { label++; parent[i * cols + j] = label; labels.push_back({ label, 1, j, i, j, i }); } else if (left != 0 && up == 0) { parent[i * cols + j] = left; labels[left - 1].area++; labels[left - 1].xmax = std::max(labels[left - 1].xmax, j); labels[left - 1].ymax = i; } else if (left == 0 && up != 0) { parent[i * cols + j] = up; labels[up - 1].area++; labels[up - 1].xmax = j; labels[up - 1].ymax = std::max(labels[up - 1].ymax, i); } else { if (left != up) { int minlabel = std::min(left, up); int maxlabel = std::max(left, up); parent[i * cols + j] = minlabel; labels[minlabel - 1].area += labels[maxlabel - 1].area + 1; labels[minlabel - 1].xmax = std::max(labels[maxlabel - 1].xmax, j); labels[minlabel - 1].ymax = std::max(labels[maxlabel - 1].ymax, i); for (int k = 0; k < rows * cols; k++) { if (parent[k] == maxlabel) { parent[k] = minlabel; } } labels[maxlabel - 1].area = 0; labels[maxlabel - 1].xmin = 0; labels[maxlabel - 1].ymin = 0; labels[maxlabel - 1].xmax = 0; labels[maxlabel - 1].ymax = 0; } else { parent[i * cols + j] = left; labels[left - 1].area++; labels[left - 1].xmax = std::max(labels[left - 1].xmax, j); labels[left - 1].ymax = std::max(labels[left - 1].ymax, i); } } } } } // 第二次扫描 for (int i = 0; i < rows; i++) { int* data = parent.data() + i * cols; for (int j = 0; j < cols; j++) { if (data[j] != 0) { data[j] = labels[data[j] - 1].label; } } } } ``` 最后,我们可以调用这个函数来进行连通区域标记: ```cpp std::vector<LabelInfo> labels; scan(dst, labels); ``` 这样就完成了用C++和OpenCV实现连通区域标记的二次扫描算法的过程。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI吃大瓜

尊重原创,感谢支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值