OpenCV学习笔记10_形态学变换

一、什么是形态学
形态学一般指生物学中研究动物和植物结构的一个分支。用数学形态学(也称图像代数)表示以形态为基础对图像进行分析的数学工具。基本思想是用具有一定形态的结构元素去度量和提取图像中的对应形状以达到对图像分析和识别的目的。形态学图像处理的数学基础和所用语言是集合论。形态学图像处理的应用可以简化图像数据,保持它们基本的形状特性,并除去不相干的结构。
二、形态学变换
形态学操作是根据图像形状进行的简单操作,一般情况下对二值化图像进行的操作。需要输入两个参数,一个是原始图像,第二个被称为结构化元素或核,它是用来决定操作的性质的。两个基本的形态学操作是腐蚀和膨胀。他们的变体构成了开运算,闭运算,梯度等。
1、形态学的应用:
消除噪声、边界提取、区域填充、连通分量提取、凸壳、细化、粗化等;
分割出独立图像元素,或者图像相邻的元素;
求取图像明显的极大值区域和极小值区域;求取图像梯度等。

2、结构元素(StructuringElement)
结构元素跟卷积核类似,一个好的结构元素可以带来非常好的效果,一般来说结构元素是由元素为1或者0的矩阵组成,结构元素为1的区域定义了图像的领域,领域内的像素在进行膨胀和腐蚀等形态学操作时要进行考虑。

CV_EXPORTS_W Mat getStructuringElement
(
int shape, 
Size ksize, 
Point anchor = Point(-1,-1)
);

参数解释:
1、int shape:结构元素形状,可以参考MorphShapes。morph(变换)
2、Size ksize:结构元素大小。
3、Point anchor = Point(-1,-1):锚点,默认值Point(-1,-1)表示锚点位于结构元素中心。

3、膨胀:

膨胀的定义:

A ⊕ B = { z ∣ ( B ^ ) ∩ A ⊆ A } A \oplus B=\{z \mid(\hat{B}) \cap A \subseteq A\} AB={ z(B^)AA}
意思:图像A在结构元素B的映射下平移之后的交集为A的子集,那么A与B的并集为膨胀后的图像的元素点;
作用:结构元素的约束下将与目标区域相接触的背景合并到该目标物中,使目标边界向外部扩张,物体的面积增大了相应数量的点。

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() );

参数解释:
1、InputArray src:输入图像;
2、OutputArray dst:输出图像;
3、InputArray kernel:结构元素(与卷积核区别在于运算方法为交并补);
4、Point anchor = Point(-1,-1):锚点位置,默认为中心;
5、int iterations = 1:膨胀的次数;
6、int borderType = BORDER_CONSTANT:边缘处理
(BORDER_CONSTANT:使用常数填充边界);
7、const Scalar& borderValue = morphologyDefaultBorderValue():当边界为常数时边界值

4、腐蚀:

腐蚀的数学定义:
A ⊖ B = { z ∣ ( B ) z ⊆ A } A \ominus B=\left\{z \mid(B)_{z} \subseteq A\right\} AB={ z(B)zA}
意思:如果结构元素B在A平移且所有元素包含在A中则保存此锚点为腐蚀后像素点,如果不完全包含则不保存。

作用:腐蚀可以把小于结构元素的物体(毛刺、小凸起)去除,这样选取不同大小的结构元素,就可以在原图像中去掉不同大小的物体。如果两个物体之间有细小的连通,那么当结构元素足够大时,通过腐蚀运算可以将两个物体分开。

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() );

参数解释:
1、InputArray src:输入图像;
2、OutputArray dst:输出图像;
3、InputArray kernel:结构元素(与卷积核区别在于运算方法为交并补);
4、Point anchor = Point(-1,-1):锚点位置,默认为中心;
5、int iterations = 1:腐蚀的次数;
6、int borderType = BORDER_CONSTANT:边缘处理
(BORDER_CONSTANT:使用常数填充边界);
7、const Scalar& borderValue = morphologyDefaultBorderValue():当边界为常数时边界值

OpenCV中实现高级形态学运算的操作为:

CV_EXPORTS_W 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、InputArray src:输入图像;
2、OutputArray dst:输出图像;
3、int op:使用的运算方法;
4、InputArray kernel:结构元素(与卷积核区别在于运算方法为交并补);
5、Point anchor = Point(-1,-1):锚点位置,默认为中心;
6、int iterations = 1:腐蚀的次数;
7、int borderType = BORDER_CONSTANT:边缘处理
(BORDER_CONSTANT:使用常数填充边界);
8、const Scalar& borderValue = morphologyDefaultBorderValue():当边界为常数时边界值

6、开运算:
op = MORPH_OPEN
规则:先腐蚀再膨胀;
作用:开运算可以用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积。

7、闭运算:
op = MORPH_CLOSE
规则:先膨胀再腐蚀;
作用:闭运算能够排除小型噪声(黑色区域)。
8、顶帽:
op = MORPH_TOPHAT
规则:原图像与开运算的差;
作用:顶帽运算往往用来分离比邻近点亮一些的斑块。当一幅图像具有大幅的背景的时候,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。
9、黑帽:
op = MORPH_BLACKHAT
规则:原图像与闭运算的差;
作用:黑帽运算用来分离比邻近点暗一些的斑块;
10、形态学梯度:
op =MORPH_GRADIENT
规则:开运算与闭运算的差;
作用:保留边缘轮廓;
11、击中与不击中:
op = MORPH_HITMISS
规则:加上背景的腐蚀运算,只有结构元素与其覆盖的区域完全相同时输出像素点。
“当不需要背景时,击中或不击中退化为腐蚀操作”
作用:用于二值图像的配准。(输入输出图像需为单通道二值图);

代码:

#include <opencv.hpp>

void main()
{
    
    
	cv::Mat src = cv::imread("test_Morphological.png");
	cv::Mat src_BGR = cv::imread("test.jpg");

	cv::Mat Threshold_src,dst_dilate,dst_erode,dst_open,dst_close,
	dst_top_hat,dst_black_hat, dst_gradient,
	dst_hitmiss,dst_dilate_BGR, dst_erode_BGR, gradient, gradient_BGR;
	cv::Mat Threshold_dst;

	cv::threshold(src, Threshold_src,236,255, cv::THRESH_BINARY_INV);

	cv::cvtColor(Threshold_src, Threshold_dst, cv::COLOR_BGR2GRAY);


	//形态学运算一般用于二值图像
	//膨胀操作
	cv::Mat kernel_dilate = cv::getStructuringElement(cv::MORPH_RECT,cv::Size(7,7));
	cv::dilate(Threshold_src, dst_dilate, kernel_dilate);

	cv::dilate(src_BGR, dst_dilate_BGR, kernel_dilate);


	//腐蚀操作
	cv::Mat kernel_erode = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(7, 7));
	cv::erode(Threshold_src, dst_erode, kernel_erode);
	
	cv::erode(src_BGR, dst_erode_BGR, kernel_erode);
	//开运算
	cv::Mat kernel_open = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(7, 7));
	cv::morphologyEx(Threshold_src, dst_open, cv::MORPH_OPEN, kernel_open);

	//闭运算
	cv::Mat kernel_close = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(7, 7));
	cv::morphologyEx(Threshold_src, dst_close, cv::MORPH_CLOSE, kernel_close);

	//顶帽运算
	cv::Mat kernel_top = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(7,7));
	cv::morphologyEx(Threshold_src, dst_top_hat, cv::MORPH_TOPHAT, kernel_top);

	//黑帽运算
	cv::Mat kernel_black = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(7,7));
	cv::morphologyEx(Threshold_src, dst_black_hat, cv::MORPH_BLACKHAT, kernel_black);


	//梯度运算 膨胀减去腐蚀
	gradient = dst_dilate - dst_erode;
	cv::subtract(dst_dilate_BGR, dst_erode_BGR, gradient_BGR);
	cv::Mat kernel_gradient = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(7, 7));
	cv::morphologyEx(Threshold_src, dst_gradient, cv::MORPH_GRADIENT, kernel_gradient);

	//击中与不击中
	cv::Mat Hitms = (cv::Mat_<uchar>(8, 8) << 0, 0, 0, 0, 0, 0, 0, 0,
		0, 255, 255, 255, 0, 0, 0, 255,
		0, 255, 255, 255, 0, 0, 0, 0,
		0, 255, 255, 255, 0, 255, 0, 0,
		0, 0, 255, 0, 0, 0, 0, 0,
		0, 0, 255, 0, 0, 255, 255, 0,
		0, 255, 0, 255, 0, 0, 255, 0,
		0, 255, 255, 255, 0, 0, 0, 0
		);

	cv::Mat hit = (cv::Mat_<uchar>(3, 3) << 0, 1, 0,
		1, 1, 1,
		0, 1, 0);
	cv::Mat miss = (cv::Mat_<uchar>(3, 3) << 0, 0, 0,
		0, 1, 0,
		0, 0, 0);
	cv::Mat hitmiss = (cv::Mat_<char>(3, 3) << 0, 1, 0,
		1, -1, 1,
		0, 1, 0);
// 	std::printf("%d", Threshold_src.channels());
// 	std::printf("%d",Threshold_dst.channels());
// 	std::printf("%d", dst_hitmiss.channels());

	//此处注意通道匹配问题,输入输出图像为单通道
	cv::morphologyEx(Hitms, dst_hitmiss, cv::MORPH_HITMISS, hitmiss);
	//inputarray Threshold_dst 
	std::printf("%d", dst_hitmiss.channels());




	cv::imshow("dst_hitmiss", dst_hitmiss);
	cv::imshow("dst_gradient", dst_gradient);
	cv::imshow("gradient_BGR", gradient_BGR);
	cv::imshow("gradient", gradient);
	cv::imshow("dst_erode_BGR", dst_erode_BGR);
	cv::imshow("dst_dilate_BGR", dst_dilate_BGR);
	cv::imshow("dst_erode", dst_erode);
	cv::imshow("dst_dilate", dst_dilate);
	cv::imshow("dst_open", dst_open);
	cv::imshow("dst_close", dst_close);
	cv::imshow("dst_top", dst_top_hat);
	cv::imshow("dst_black", dst_black_hat);
	cv::imshow("Threshold_src", Threshold_src);
	cv::imshow("src", src);
	cv::waitKey(0);
}

猜你喜欢

转载自blog.csdn.net/qq_40595787/article/details/121159550