亲宝软件园·资讯

展开

OpenCV数字图像处理基于C++之图像形态学处理详解

qq_43784519 人气:0

1、图像腐蚀

原理:腐蚀用来收缩或细化二值图像中的前景,借此实现去噪声、元素分割等功能。和所有形态学滤波器一样,腐蚀和膨胀这两个滤波器的作用范围是由结构元素定义的像素集。在某个像素上应用结构元素时,结构元素的锚点与该像素对齐,所有与结构元素相交的像素就包含在当前集合中。腐蚀就是把当前像素替换成所定义像素集合中的最小像素值膨胀是腐蚀的反运算,它把当前像素替换成所定义像素集合中的最大像素值。由于输入的二值图像只包含黑色(值为0)和白色(值为255)像素,因此每个像素都会被替换成白色或黑色像素。

​ 要形象地理解这两种运算的作用,可考虑背景(黑色)和前景(白色)的物体。腐蚀时,如果结构元素放到某个像素位置时碰到了背景(即交集中有一个像素是黑色的),那么这个像素就变为背景;膨胀时,如果结构元素放到某个背景像素位置时碰到了前景物体,那么这个像素就被标为白色。正因如此,图像腐蚀后物体尺寸会缩小(形状被腐蚀),而图像膨胀后物体会扩大。在腐蚀图像中,有些面积较小的物体(可看作背景中的“噪声”像素)会彻底消失。与之类似,膨胀后的物体会变大.而物体中一些“空隙”会被填满。OpenCV默认使用3×3正方形结构元素。在调用函数时,就能得到默认的结构元素。你也可以通过提供一个矩阵来指定结构元素的大小(以及形状),矩阵中的非零元素将构成结构元素。

作用去除图像中的某些部分以及会缩小细化目标

1.1 CV腐蚀函数

CV_EXPORTS_W void erode( 
    InputArray src, 
    OutputArray dst, 
    InputArray kernel,
    Point anchor = Point(-1,-1),
    int iterations = 1,
    int borderType = BORDER_CONSTANT,
    const Scalar& borderValue = morphologyDefaultBorderValue()
);

src 输入图像;通道的数量可以是任意的,但是深度值应该是以下之一:
CV_8U, CV_16U, CV_16S, CV_32F or CV_64F.
dst 和源图像同样大小和类型的输出图像。
kernel 用于腐蚀的结构元素;如果element=Mat(),是一个3 x 3的矩形结构元素. Kernel 可以通过使用getStructuringElement来创建。
anchor 素中的锚点的位置,默认是值(-1,-1),也就是说锚点在元素的中心位置。
iterations 腐蚀的迭代次数。
borderType像素外推方法。参见#BorderTypes, BORDER_WRAP不支持。
borderValue 固定边缘的情况下的边缘值。
参考 dilate,morphologyEx,getStructuringElement
Mat cv::getStructuringElement(
int  shape,
Size  ksize,
Point  anchor = Point(-1,-1) 
 )
参数1:结构元的形状(0:矩形结构元;1:十字架结构元;2:椭圆结构元);
参数2:结构元大小(size(m, n));
参数3:结构元中心点所在位置(point(x, y))默认为中心点
Mat element = getStructuringElement(0, Size(3, 3)); //构造矩形结构元素
	Mat image_erosion;
	erode(image_bw, image_erosion, element, Point(-1, -1), 3); //执行腐蚀操作
	imshow("image_erosion", image_erosion);

原图

二值图

image-20221112111141942

CV腐蚀

image-20221112111119770

1.2 自定义腐蚀函数

void erode_my(Mat &src,Mat &dst,int size) {
	for (int i = 0; i < src.rows; ++i)
	{
		for (int j = 0; j < src.cols; ++j)
		{
			uchar minV = 255;
			//uchar maxV = 0;

			//遍历周围最小像素值
			for (int yi = i - size / 2; yi <= i + size / 2; yi++)
			{
				for (int xi = j - size / 2; xi <= j + size / 2; xi++)
				{
					if (xi < 0 || xi >= src.cols || yi < 0 || yi >= src.rows)
					{
						continue;
					}
					minV = (std::min<uchar>)(minV, src.at<uchar>(yi, xi));
					//maxV = (std::max<uchar>)(maxV, src.at<uchar>(yi, xi));			
				}
			}
			dst.at<uchar>(i, j) = minV;
		}
	}
}

自定义腐蚀

image-20221112111108277

1.3 对比

erode_my(image_bw, image_erosion_my,9);
当腐蚀结构扩大到9×9时效果基本与cv自带函数一致。

image-20221112111524985

2、图像膨胀

原理:腐蚀用来收缩或细化二值图像中的前景,借此实现去噪声、元素分割等功能。腐蚀过程:用结构元来逐个像素扫描被腐蚀图像,并根据结构元和被腐蚀图像的关系确定腐蚀结果。

注意,腐蚀操作等形态学操作,是逐个像素地来确定值的,每次判定的点都与结构元中心点所对应,如果结构元完全处于前景图像中,就将结构元中心点所对应的腐蚀结果图像中的像素点处理为前景色(如1)。如果结构元不完全处于前景图像中,就将结构元中心点所对应的腐蚀结果图像中的像素点处理为背景色(如0)。(结构元也被称为核

作用增大图像中的目标,或者填充、连接某些目标

image-20221112123246036

2.1 CV膨胀函数

CV_EXPORTS_W void dilate( 
    InputArray src, 
    OutputArray dst, 
    InputArray kernel,
    Point anchor = Point(-1,-1), 
    int iterations = 1,
    int borderType = BORDER_CONSTANT,
    const Scalar& borderValue = morphologyDefaultBorderValue() 
);

src 输入图像;通道的数量可以是任意的,但是深度值应该是以下之一:CV_8U, CV_16U, CV_16S, CV_32F or CV_64F.
dst 和源图像同样大小和类型的输出图像。
kernel 膨胀核元素,如果elemenat=Mat(), 是一个3 x 3的矩形核元素,核可以使用getStructuringElement来创建。
anchor 元素中的锚点的位置,默认是值(-1,-1),也就是说锚点在元素的中心位置。
iterations 膨胀的迭代次数。
borderType 像素外推方法。参见#BorderTypes, BORDER_WRAP不支持。
borderValue 固定边缘的情况下的边缘值。
参考:erode,morphologyEx,getStructuringElement
//膨胀
Mat element = getStructuringElement(0, Size(3, 3)); //构造矩形结构元素
Mat image_dilate;
dilate(image_bw, image_dilate, element, Point(-1, -1), 9); //执行膨胀操作
imshow("image_dilate", image_dilate);

二值图

image-20221112111141942

CV膨胀

image-20221112111035904

2.2 自定义膨胀函数

void dilate_my(Mat& src, Mat& dst, int size) {
	for (int i = 0; i < src.rows; ++i)
	{
		for (int j = 0; j < src.cols; ++j)
		{
			//uchar minV = 255;
			uchar maxV = 0;

			//遍历周围最大像素值
			for (int yi = i - size / 2; yi <= i + size / 2; yi++)
			{
				for (int xi = j - size / 2; xi <= j + size / 2; xi++)
				{
					if (xi < 0 || xi >= src.cols || yi < 0 || yi >= src.rows)
					{
						continue;
					}
					//minV = (std::min<uchar>)(minV, src.at<uchar>(yi, xi));
					maxV = (std::max<uchar>)(maxV, src.at<uchar>(yi, xi));
				}
			}
			dst.at<uchar>(i, j) = maxV;
		}
	}
	
}

自定义膨胀

image-20221112111059373

2.3 对比

dilate_my(image_bw, image_dilate_my, 9);
当膨胀结构扩大到9×9时效果基本与cv自带函数一致。

image-20221112111458723

腐蚀图像相当于对其反色图像膨胀后再取反色;
膨胀图像相当于对其反色图像腐蚀后再取反色。

3、开运算

原理先腐蚀,再膨胀

作用平滑物体轮廓、断开狭窄的狭颈、消除细长的突出和物体。可以去掉图像上一些小的对象。(假设图像前景色是白色,背景色是黑色)

原图

image-20221112132738519

3.1 方法一

//先腐蚀后膨胀
Mat open_image;
Mat element = getStructuringElement(0, Size(3, 3)); //构造矩形结构元素
erode(image_bw, image_erosion, element, Point(-1, -1), 3); //执行腐蚀操作
dilate(image_erosion, open_image, element, Point(-1, -1), 3); //执行膨胀操作
imshow("open", open_image);

image-20221112132820175

3.2 方法二

morphologyEx(image_bw, open_image, MORPH_OPEN, element);
	imshow("open2", open_image);

image-20221112133040153

4、闭运算

原理先膨胀,再腐蚀

作用闭运算可以填充小洞,填充小的噪点

4.1 方法一

Mat close_image;
Mat element = getStructuringElement(0, Size(3, 3)); //构造矩形结构元素
dilate(image_bw, image_dilate, element, Point(-1, -1), 3); //执行膨胀操作
erode(image_dilate, close_image, element, Point(-1, -1), 3); //执行腐蚀操作
imshow("close", close_image);

image-20221112133148201

4.2 方法二

morphologyEx(image_bw, close_image, MORPH_CLOSE, element);
imshow("close2", close_image);

image-20221112133209563

4.3 morphologyEx函数介绍

void morphologyEx( 
InputArray src, 
OutputArray dst,
int op, 
InputArray kernel,
Point anchor = Point(-1,-1), 
int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue() 
);
(1)参数1:InputArray src
输入图像,图像数据类型必须为CV_8U, CV_16U, CV_16S, CV_32F or CV_64F中的一种。
(2)参数2:OutputArray dst
输出图像,数据类型与大小和输入图像一样。
(3)参数3:int op 形态学处理的类型:
MORPH_ERODE = 0:腐蚀处理
MORPH_DILATE = 1:膨胀处理
MORPH_OPEN = 2:开运算处理
MORPH_CLOSE = 3:闭运算处理
MORPH_GRADIENT = 4:形态学梯度
MORPH_TOPHAT = 5:顶帽变换
MORPH_BLACKHAT = 6:黑帽变换
MORPH_HITMISS = 7 :击中-击不中变换
(4)参数4:InputArray kernel结构元矩阵
(5)参数5:Point anchor = Point(-1,-1)
结构元中心点, 默认值Point(-1,-1), 表示正中心
(6)参数6:int iterations = 1
腐蚀膨胀处理的次数,默认值为1;
如果是开运算闭运算,次数表示先腐蚀或者膨胀几次,再膨胀腐蚀几次,而不是开运算闭运算几次:
如开运算且次数为2:erode -> erode -> dilate -> dilate
(7)参数7:int borderType = BORDER_CONSTANT
图像边框插值类型,默认类型为固定值填充
BORDER_CONSTANT = 0:固定值 i 填充:iiiiii | abcdefgh | iiiiiii
BORDER_REPLICATE = 1:两端复制:aaaaaa | abcdefgh | hhhhhhh
BORDER_REFLECT = 2:镜像复制:fedcba | abcdefgh | hgfedcb
BORDER_WRAP = 3:去除一端的值然后复制: cdefgh | abcdefgh | abcdefg
BORDER_REFLECT_101 = 4:去除一端的值然后镜像复制: gfedcb|abcdefgh|gfedcba
BORDER_TRANSPARENT = 5:推导赋值 uvwxyz | abcdefgh | ijklmno
BORDER_REFLECT101 = BORDER_REFLECT_101: same as BORDER_REFLECT_101
BORDER_DEFAULT = BORDER_REFLECT_101: same as BORDER_REFLECT_101
BORDER_ISOLATED = 16:< do not look outside of ROI
(8)参数8:const Scalar& borderValue = morphologyDefaultBorderValue()
文档中说明:param borderValue Border value in case of a constant border. The default value has a special meaning.

5、顶帽运算

原理顶帽(或礼帽)运算是原始图像减去图像开运算的结果。

顶帽运算:原始图像 — 图像开运算

作用:得到图像的噪声。如下图所示:

image-20221112135918113

5.1 方法一

//构造结构元
Mat kernel = getStructuringElement(0, Size(5, 5));
Mat image_lm;
//顶帽变换(礼帽变换)
morphologyEx(image_bw, image_lm, 5, kernel);
cv::imshow("image_lm", image_lm);

5.2 方法二

Mat image_lm2;	//**顶帽运算:原始图像 — 图像开运算**
//dst=img1-img2;//这两个减法效果相同    若dst<0,则dst=0
subtract(image_bw,open_image2, image_lm2);//注意:要求被处理图片尺寸一致
cv::imshow("image_lm2",image_lm2);

image-20221112142411170

Mat image_lm2;
absdiff(image_bw, open_image2, image_lm2);//若dst<0,则dst=|dst|>=0    用于检测两幅相似图像的不同点,效果比上面的两种减法好
cv::imshow("image_lm2", image_lm2);

image-20221112142625878

6、黑帽运算

原理黑帽运算是图像闭运算操作减去原始图像的结果。

黑帽运算:闭运算 — 原始图像

作用:得到图像内部的小孔,或者前景色中的小黑点。如下图所示:

image-20221112140418742

6.1 方法一

//构造结构元
Mat kernel = getStructuringElement(0, Size(5, 5));
//底帽变换(黑帽变换)
Mat image_hm;
morphologyEx(image_bw, image_hm, 6, kernel);
cv::imshow("image_hm", image_hm);

6.2 方法二

Mat image_hm2;
	subtract(image_bw, close_image2, image_hm2);//注意:要求被处理图片尺寸一致
	cv::imshow("image_hm2", image_hm2);

image-20221112143051521

Mat image_hm2;
absdiff(image_bw, close_image2, image_hm2);//若dst<0,则dst=|dst|>=0    用于检测两幅相似图像的不同点,效果比上面的两种减法好
cv::imshow("image_hm2", image_hm2);

image-20221112142941890

7、形态学梯度

原理膨胀图与腐蚀图之差;

作用对二值图进行这一操作可以得到图像中白色区域的边界,因此可以用形态学梯度来保留物体的边界轮廓

7.1 方法一

//构造结构元
Mat kernel = getStructuringElement(0, Size(5, 5));
Mat image_lm;

//形态学梯度
morphologyEx(image_bw, image_td, 4, kernel);
cv::imshow("image_td", image_td);

image-20221112141231065

7.2 方法二

Mat image_tw2;
subtract(image_dilate, image_erosion, image_tw2);//注意:要求被处理图片尺寸一致
cv::imshow("image_tw2", image_tw2);

image-20221112143418223

Mat image_tw2;
absdiff(image_dilate, image_erosion, image_tw2);//若dst<0,则dst=|dst|>=0    用于检测两幅相似图像的不同点,效果比上面的两种减法好
cv::imshow("image_tw2", image_tw2);

image-20221112143455506

部分参考来自:数字图像处理(c++ opencv)–持续更新 - 知乎 (zhihu.com)

总结

加载全部内容

相关教程
猜你喜欢
用户评论