您现在的位置是:首页 >技术杂谈 >opencv_c++学习(二十四)网站首页技术杂谈

opencv_c++学习(二十四)

小小小~ 2024-06-26 14:23:55
简介opencv_c++学习(二十四)

一、积分图像

在这里插入图片描述
积分图像是对原图像进行积分操作的算法。如上图左所示,不同颜色代表不同区域。当我们想求取一个像素点的积分值时,我们需要求取该点左上方区域的数据之和,如P0的积分值是浅蓝色区域的数据之和。 P1的积分值为蓝色和橙色区域的数值之和。
在这里插入图片描述
上图展示的是倾斜求和积分方式。
积分图像计算函数:

integral(InputArray src, OutputArray sum, OutputArray sqsum, OutputArray tilted, int sdepth = -1, int sqdepth = -1)

src:输入图像,图像数据类型可以是CV_8U、CV_32F或者CV_64F。
sum:输出标准求和积分图像,图像的数据类型可以是CV_32S、CV_32F或者CV_64F。
sqsum:输出平方求和积分图像,图像的数据类型可以是CV_32F或者CV_64F。
tilted:输出倾斜45°的倾斜求和积分图像,其数据类型与sum相同。
sdepth:输出标准求和积分图像和倾斜求和积分图像的数据类型标志,可以选择的参数为CV_32S,CV_32F或者CV_64F,参数默认值为-1,表示满足数据存储的自适应类型。
sqdepth:输出平方求和积分图像的数据类型标志,可以选择的参数为CV_32F或者CV_64F,参数默认值为-1,表示满足数据存储的自适应类型。
本节使用案例如下:

int main() {
	//创建16*16的全1矩阵
	Mat img = Mat::ones(16, 16, CV_32FC1);

	//在图像中加入随机噪声
	RNG rng(10000);

	for (int y = 0; y < img.rows; y++)
	{
		for (int x = 0; x < img.cols; x++)
		{
			float d = rng.uniform(-0.5, 0.5);
			img.at<float>(y, x) = img.at<float>(y, x) + d;
		}
	}

	//计算标准求和积分
	Mat sum;
	integral(img, sum);

	//为了便于显示,转成CV_8U格式
	Mat sum8U = Mat_<uchar>(sum);
	namedWindow("q", WINDOW_NORMAL);
	imshow("q", sum8U);

	//计算平方求和积分
	Mat sqsum;
	integral(img, sum, sqsum);

	//为了便于显示,转成CV_8U格式
	Mat sqsum8U = Mat_<uchar>(sqsum);
	namedWindow("w", WINDOW_NORMAL);
	imshow("w", sqsum8U);

	//计算倾斜求和积分
	Mat tilted;
	integral(img, sum, sqsum, tilted);

	//为了便于显示,转成CV_8U格式
	Mat tilted8U = Mat_<uchar>(tilted);
	namedWindow("e", WINDOW_NORMAL);
	imshow("e", tilted8U);
		 
	waitKey(0);
	return 0;
}

二、图像分割——漫水填充

在这里插入图片描述

floodFill(InputOutputArray image, InputOutputArray mask, Point seedPoint, Scalar newVal, Rect * rect = 0, Scalar loDiff = Scalar(), upDiff =, Scalar scalar(), int flags = 4

image:输入输出图像,图像数据类型可以为CV_8U或者CV_32F的单通道或者三通道图像。
mask:掩码矩阵,尺寸比如输入图像的宽和高各大2的单通道图像,用于标记漫水填充的区域。
seedPoint:种子点。
newVal:归入种子点区域内像素点的新像素值。
rect:种子点漫水填充区域的最小矩形边界,默认值为0,表示不输出边界。loDiff:添加进种子点区域条件的下界差值。
upDiff:添加进种子点区域条件的上界差值。
flags:漫水填充方法的操作标志。
本节应用案例如下:

int main() {

	//读取图片
	Mat src = imread("1.png");
	if (src.empty())
	{
		printf("不能打开空图片");
		return -1;
	}

	//在生成随机像素的随机数
	RNG rng(10000);

	//连同邻域方式
	int connectivity = 4;

	//掩码图像数值
	int maskVal = 255;

	//漫水填充的标志位,不改变种子像素值
	int flags = connectivity | (maskVal << 8) | FLOODFILL_FIXED_RANGE;

	//设置与选中像素点的差值,上下都为20
	Scalar loDiff = Scalar(20, 20, 20);
	Scalar upDiff = Scalar(20, 20, 20);

	//声明掩膜矩阵变量
	Mat mask = Mat::zeros(src.rows + 2, src.cols + 2, CV_8UC1);

	while (true)
	{
		//随机产生图像中某一像素点
		int py = rng.uniform(0, src.rows - 1);
		int px = rng.uniform(0, src.cols - 1);
		Point point = Point(px, py);

		//对彩色图像填充像素值
		Scalar newVal = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));

		//进行漫水填充,返回填充像素数目
		int area = floodFill(src, mask, point, newVal, &Rect(), loDiff, upDiff, flags);

		imshow("q", mask);

		//按ESC键退出程序
		char key = (char)waitKey();
		if (key == 27 || key == 'q' || key == 'Q')
		{
			break;
		}

	}
	waitKey(0);
	return 0;

}

三、图像分割方法——分水岭法

在这里插入图片描述

watershed(InputArray image, InputOutputArray markers)

image:输入图像,数据类型为CV_8U的三通道图像。
markers:输入(种子区域)/输出CV_32S的单通道图像的标记结果,与原图像具有相同的尺寸。
本节样例如下:

int main() {

	//读取要标记的图片
	Mat src = imread("1.png");
	//读取原始图片
	Mat src1 = imread("3.png");
	if (src.empty() || src1.empty())
	{
		printf("不能打开空图片");
		return -1;
	}

	//watershed参数设置
	Mat maskWaterShed;

	//转化为灰度图
	Mat gray;
	cvtColor(src, gray, COLOR_BGR2GRAY);

	Mat imgMask;
	//二值化并开运算
	threshold(gray, imgMask, 254, 255, THRESH_BINARY);
	Mat k = getStructuringElement(0, Size(3, 3));
	morphologyEx(imgMask, imgMask, MORPH_OPEN, k);

	//轮廓提取
	vector<vector<Point>>contours;
	vector<Vec4i> hierarchy;
	findContours(imgMask, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);

	//在maskWaterShed上绘制轮廓,用于输入分水岭算法
	maskWaterShed = Mat::zeros(imgMask.size(), CV_32S);
	for (int index = 0; index < contours.size(); index++)
	{
		drawContours(maskWaterShed, contours, index, Scalar::all(index + 1), -1, 8, hierarchy, INT_MAX);
	}

	//进行分水岭算法
	watershed(src1, maskWaterShed);

	//随机生成颜色,用于显示分割
	vector<Vec3b> colors;
	for (int i = 0; i < contours.size(); i++)
	{
		int b = theRNG().uniform(0, 255);
		int g = theRNG().uniform(0, 255);
		int r = theRNG().uniform(0, 255);
		colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
	}

	//显示图像
	Mat resultImg = Mat(src1.size(), CV_8UC3);
	for (int i = 0; i < imgMask.rows; i++)
	{
		for (int j = 0; j < imgMask.cols; j++)
		{
			//绘制不同区域
			int index = maskWaterShed.at<int>(i, j);

			//如果区域边界
			if (index == -1)
			{
				resultImg.at<Vec3b>(i, j) = Vec3b(255, 255, 255);
			}

			//没有被标记的区域置0
			else if (index<=0 || index > contours.size())
			{
				resultImg.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
			}

			//其他区域保持像素不变
			else
			{
				resultImg.at<Vec3b>(i, j) = colors[index - 1];
			}
		}
	}

	imshow("resultImg", resultImg);
	resultImg = resultImg * 0.8 + src1 * 0.2;
	imshow("resultImg1", resultImg);

	//绘制每个区域的图案
	for (int n = 0; n < contours.size(); n++)
	{
		Mat resImage1 = Mat(src.size(), CV_8UC3);
		for (int i = 0; i < imgMask.rows; i++)
		{
			for (int j = 0; j < imgMask.cols; j++)
			{
				//绘制不同区域
				int index = maskWaterShed.at<int>(i, j);

				//如果区域边界
				if (index == n)
				{
					resultImg.at<Vec3b>(i, j) = src1.at<Vec3b>(i, j);
				}

				else
					resultImg.at<Vec3b>(i, j) = Vec3b(0, 0, 0);

			}
		}
		imshow(to_string(n), resImage1);
	}
	waitKey(0);
	return 0;
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。