OpenCV二值化后 按面积选区域 填充孔洞 排序

本文介绍了如何使用OpenCV进行图像的二值化处理,通过删除小面积区域来减少噪声,以及如何填充图像中的空洞。此外,还详细阐述了轮廓排序的方法,包括按x、y坐标及面积进行排序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

按面积选择区域 select_shape

二值化为了减少噪声的干扰,删除面积小的区域,代码中将连通区域面积(像素个数)不足100的区域认为是噪声点,并将其删除(即置为背景黑色)。

 

 

#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;
}

 

 

填充空洞 fill_up

有的时候,我们需要对图像进行孔洞填充,一般想到的方法都是,使用findcontours函数,查找最外层轮廓,然后画出找到的轮廓。

这确实是一种方法,但似乎不够高效。下面贴出一个函数,可以更好的实现这个功能。

void fillHole(const cv::Mat srcimage, cv::Mat &dstimage)
{
	Size m_Size = srcimage.size();
	Mat tempImage = Mat::zeros(m_Size.height + 2, m_Size.width + 2, srcimage.type());//延展图像  
	srcimage.copyTo(temimage(Range(1, m_Size.height + 1), Range(1, m_Size.width + 1)));
	floodFill(temimage, Point(0, 0), Scalar(255));
	Mat cutImg;//裁剪延展的图像  
	tempImage(Range(1, m_Size.height + 1), Range(1, m_Size.width + 1)).copyTo(cutImg);
	dstimage = srcimage | (~cutImg);
}

 


 

 

排序 sort_contour

按照x、y、面积排序

/**
* @brief 排序
* @param inputContours    输入轮廓
* @param outputContours   输出轮廓
* @param sortType   排序类型:0-按x排序,1-按y排序,2-按面积排序
* @param sortOrder   排序方式:true-升序,false-降序
*
* @return 返回说明
*     -<em>false</em> fail
*     -<em>true</em> succeed
*/
void  CTestCvDlg::SortContourPoint(vector<vector<Point>> inputContours, vector<vector<Point>> &outputContours, int sortType, bool sortOrder)
{
	vector<Point> tempContoursPoint;

	//计算轮廓矩 	
	vector<Moments> mu(inputContours.size());
	//计算轮廓的质心 	
	vector<Point2f> mc(inputContours.size());
	//计算轮廓的面积
	double area, area1;
	//2.2 定义Rect类型的vector容器roRect存放最小外接矩形,初始化大小为contours.size()即轮廓个数  
	vector<RotatedRect> roRect(inputContours.size());
	//计算轮廓最小外接矩形中心
	Point center, center1;

	for (int i = 0; i < inputContours.size(); i++)
	{
		tempContoursPoint.clear();    //每次循环注意清空

		mu[i] = moments(inputContours[i], false); //轮廓矩

		mc[i] = Point2d(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00); //轮廓质心

		area = inputContours[i].size(); //轮廓点数
		area = contourArea(inputContours[i], false); //轮廓的面积

		//旋转矩形主要成员有center、size、 angle、points()
		roRect[i] = minAreaRect(Mat(inputContours[i]));
		center = roRect[i].center; //轮廓最小外接矩形中心


		//outputContours[0] = inputContours[1];
	}


	//冒泡法
	for (int i = 0; i < inputContours.size() - 1; i++)          //n个数要进行n-1趟比较
	{
		for (int j = 0; j < (inputContours.size() - 1) - i; j++)       //每趟比较n-i次  
		{
			mu[j] = moments(inputContours[j], false); //轮廓矩
			mu[j+1] = moments(inputContours[j+1], false); //轮廓矩

			mc[j] = Point2d(mu[j].m10 / mu[j].m00, mu[j].m01 / mu[j].m00); //轮廓质心
			mc[j+1] = Point2d(mu[j+1].m10 / mu[j+1].m00, mu[j+1].m01 / mu[j+1].m00); //轮廓质心

			area = inputContours[j].size(); //轮廓点数
			area = contourArea(inputContours[j], false); //轮廓的面积
			area1 = inputContours[j+1].size(); //轮廓点数
			area1 = contourArea(inputContours[j+1], false); //轮廓的面积

			//旋转矩形主要成员有center、size、 angle、points()
			roRect[j] = minAreaRect(Mat(inputContours[j]));
			center = roRect[j].center; //轮廓最小外接矩形中心
			roRect[j+1] = minAreaRect(Mat(inputContours[j+1]));
			center1 = roRect[j+1].center; //轮廓最小外接矩形中心

			if (mc[j].x > mc[j + 1].x)      //依次比较两个相邻的数,将小数放在前面,大数放在后面:升序
			{
				tempContoursPoint = inputContours[j];   //temp是局部变量
				inputContours[j] = inputContours[j + 1];
				inputContours[j + 1] = tempContoursPoint;
			}
		}
	}
	
	outputContours = inputContours;


	//for (int i = 0; i < inputContours.size(); i++)
	//{
	//	tempContoursPoint.clear();    //每次循环注意清空

	//	mu[i] = moments(inputContours[i], false); //轮廓矩

	//	mc[i] = Point2d(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00); //轮廓质心

	//	area = inputContours[i].size(); //轮廓点数
	//	area = contourArea(inputContours[i], false); //轮廓的面积

	//	//旋转矩形主要成员有center、size、 angle、points()
	//	roRect[i] = minAreaRect(Mat(inputContours[i]));
	//	center = roRect[i].center; //轮廓最小外接矩形中心


	//	//outputContours[0] = inputContours[1];
	//}

}

排序前:

排序后:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值