读书笔记-opencv-形态学处理

读书笔记-opencv-形态学处理

常用的形态学处理包括:腐蚀,膨胀,开操作,闭操作,顶帽运算,底帽运算等,其中膨胀腐蚀为最基础的方法。

腐蚀

原理解析

图像的腐蚀是选取每个位置邻域中的最小值座位该位置的输出值。邻域可以是矩形结构,也可以是椭圆形结构,十字交叉结构,大多定义为结构元。

选取每个邻域的最小值,腐蚀后的图像亮度会降低,图像中较亮的区域面积会减小甚至消失。图像I,结构元S的腐蚀操作记为:
E=IS E = I⊝S E=I⊝S
对图像进行腐蚀操作缩小了亮度区域的面积,随意针对阈值分割后前景是白色的二值图,可以通过IEI-EI−E操作提取边界。

实现代码

对于图像腐蚀opencv提供函数:

erode(src, element[,dst[,iterations[,borderType[,borderValue]]]])
    //src			输入矩阵
    //element		结构元
    //anchor		结构元的锚点
    //iterations	迭代的次数
    //borderType	边界扩充类型
    //borderValue	边界扩充值

对于超出边界,需要扩充图像边界,镜像的方式最好。代表结构元参数element,opencv提供函数:

getStructuringElement(shape, ksize[,anchor])
    //shape			MORPH_RECT:产生矩形结构元
    //				MORPH_ELLIPSEM:产生椭圆结构元
    //				MORPH_CROSS:产生十字交叉结构元
    //ksize			结构元尺寸
    //anchor		结构元锚点

利用erode和getStrcturingElement函数进行腐蚀操作:

if __name__ =='__main__':
	#loading image
	image=cv2.imread("G:\\blog\\OpenCV_picture\\chapter6\\img7.jpg",cv2.IMREAD_GRAYSCALE)
	cv2.imshow("image",image)
	#Entropy threshold
	thresh,out=threshEntroy(image)

	#show thresh
	print(thresh)
	out=np.round(out)
	out.astype(np.uint8)

	#erode
	#create element
	element = cv2.getStructuringElement(cv2.MORPH_RECT,(3,3))
	#erode image, iterations set 1
	erodeImg = cv2.erode(out, element)
	#border extraction
	bordeExtractionImg = out - erodeImg

	cv2.imshow("out",out)
	cv2.imshow("erodeImage", erodeImg)
	cv2.imshow("border extraction image", bordeExtractionImg)
	cv2.waitKey(0)
	cv2.destroyAllWindows()

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7h40f80j-1571986151633)(C:\Users\huangxin\Desktop\仿射变换\opencv算法精讲-7\1.png)]

C++腐蚀操作:

	//创建结构元
	Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));
	
	//进行2次腐蚀操作
	Mat erodeImage1, erodeImage2;
	erode(OtsuMat, erodeImage1, element, Point(-1, -1), 2);
	erode(threshByEntroyMat, erodeImage2, element, Point(-1, -1), 2);
	
	//显示图片
	imshow("src", src);
	imshow("dst", thresh_out_dst);
	imshow("threshByEntroy", threshByEntroyMat);
	imshow("Otsu", OtsuMat);
	imshow("erode after Otsu", erodeImage1);
	imshow("erode after threshByEntroy", erodeImage2);

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y6V1KLkY-1571986151634)(C:\Users\huangxin\Desktop\仿射变换\opencv算法精讲-7\2.png)]

结构元尺寸增大,目标物体的白色区域越来越小;反复进行腐蚀操作,白色区域也会越来越小。

膨胀

与腐蚀相类似,但是取得是邻域的最大值,膨胀后图像的亮度会增加,较亮的区域会增加。图像I和结构元S膨胀操作记为:
D=IS D = I⊕S D=I⊕S

代码实现

open’c’v提供函数dilate()

dilate(src, element[,dst[,iterations[,borderType[,borderValue]]]])
//src			输入矩阵
//element		结构元
//anchor		结构元的锚点
//iterations	迭代的次数
//borderType	边界扩充类型
//borderValue	边界扩充值
if __name__=="__main__":
	image=cv2.imread("G:\\blog\\OpenCV_picture\\chapter6\\img7.jpg",cv2.IMREAD_GRAYSCALE)
	cv2.imshow("init iamge", image)

	#结构元半径,迭代次数
	radius, iterationsNum = 1, 1
	MAX_R, MAX_Iter = 20, 20
	
	#显示膨胀窗口
	cv2.namedWindow("dilate", 1)

	def nothing(*arg):
		pass

	#调节结构圆半径
	cv2.createTrackbar("radius", "dilate", radius, MAX_R, nothing)
	cv2.createTrackbar("iterations", "dilate", iterationsNum, MAX_Iter, nothing)
	while True:

		#获得当前的半径值
		radius = cv2.getTrackbarPos("radius", 'dilate')
		iterationsNum = cv2.getTrackbarPos("iterations", "dilate")
		#创建结构元
		element = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2*radius+1, 2*radius+1))

		#膨胀图像
		dilateImg = cv2.dilate(image, element, iterations=iterationsNum)

		#显示膨胀效果
		cv2.imshow("dilate", dilateImg)
		ch = cv2.waitKey(5)
		
		#按下Esc键退出
		if ch ==  27:
			break
	cv2.destroyAllWindows()

注意在这段代码“s = cv2.getStructuringElement(cv2.MORPH_OPEN,(2r+1,2r+1))”里面把cv2.cv2.MORPH_ELLIPSE在另一个程序里面会报错会报错,显示“error: (-215:Assertion failed) anchor.inside(Rect(0, 0, ksize.width, ksize.height)) in function ‘cv::normalizeAnchor’

找到这个问题出在哪里,因为我的cv2.destroyAllWindows()写在while循环里面了,造成后面的无法获取半径值和迭代值。

element最小的尺寸为3*3,必须要比这个尺寸大,不然也会报错

int radius = 1;
int MAX_R = 20;
Mat image, outImg;


//回调函数,调节半径
void callBack(int, void *)
{
	//创建只有垂直方向上的矩形结构元
	Mat element = getStructuringElement(MORPH_RECT, Size(1, 2 * radius + 1));

	dilate(image, outImg, element);

	imshow("dilate", outImg);
	
}

int main()
{

	//输入图像
	std::string imagePath = "G:\\blog\\OpenCV_picture\\chapter6\\img7.jpg";
	Mat src = imread(imagePath, 0);
	
	if (!src.data)
	{
		std::cout << "load image error!" << std::endl;
		return -1;
	}

	Mat thresh_out_dst;
	int ret = 0;

	ret = threshTwoPeaks(src, thresh_out_dst);
	if (!ret)
	{
		return -1;
	}
	cout << ret << endl;


	Mat threshByEntroyMat;
	ret = threshByEntroy(src, threshByEntroyMat);
	if (!ret)
	{
		return -1;
	}
	cout << ret << endl;

	
	Mat OtsuMat;
	ret = Otsu(src, OtsuMat);
	if (!ret)
	{
		return -1;
	}
	cout << ret << endl;

	imshow("Otsu", OtsuMat);
	waitKey(0);
	OtsuMat.copyTo(image);
	//image=OtsuMat.clone();
	imshow("img", image);
	namedWindow("dilate", WINDOW_AUTOSIZE);
	createTrackbar("radius", "dilate", &radius, MAX_R, callBack);
	callBack(0, 0);

	waitKey(0);


	return 0;
}

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jilr78rT-1571986151636)(C:\Users\huangxin\Desktop\仿射变换\opencv算法精讲-7\4.png)]

其中的OstuMat是前面的Ostu阈值分割得到图片。

开运算和闭运算

开运算先进行膨胀在进行腐蚀。

消除亮度较高的细小区域,在纤细点处分离物体,对于较大物体,可以不明显改变其面积的情况下平滑边界等作用。

闭运算先进行腐蚀在进行膨胀。

它可以填充白色物体内细小的黑色空洞区域,连接邻近物体,同一结构元,多次处理,也可以不明显改变其面积的情况下平滑边界等作用。

opencv提供函数norphologyEx()函数

norphologyEx(src, op, element[,dst[, anchor[iterations[,borderType[,borderValue]]]]])
//src			输入矩阵
//op			形态学处理的各种运算
//				NORPH_OPEN		开运算
//				NORPH_CLOSE		闭运算
//				NORPH_GRADIENT	形态梯度
//				NORPH_TOPHAT	顶帽运算
//				NORPH_BLACKHAT	底帽运算
//element		结构元
//iterations	迭代次数
//anchor		结构元锚点
if __name__ == "__main__":
	image = cv2.imread("G:\\blog\\OpenCV_picture\\chapter7\\img1.jpg", 0)
	cv2.imshow("InitImage", image)

	#结构元半径
	radius, iter = 1, 1
	MAX_R, MAX_Iter = 20, 20

	#
	cv2.namedWindow("morphology", 1)
	def nothing(*arg):
		pass

	cv2.createTrackbar("radius", "morphology", radius, MAX_R, nothing)
	cv2.createTrackbar("iterations", "morphology", iter, MAX_Iter, nothing)

	while True:
		radius = cv2.getTrackbarPos("radius", "morphology")
		iter = cv2.getTrackbarPos("iterations", "morphology")

		element = cv2.getStructuringElement(cv2.MORPH_RECT,(2*radius+1, 2*radius +1))

		photoMorphology = cv2.morphologyEx(image, cv2.MORPH_OPEN,element,iterations=iter)

		cv2.imshow("morphology", photoMorphology)
		
		ch = cv2.waitKey(5)
		if ch  == 27:
			break

	cv2.destroyAllWindows()

运行结果[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vBQSaH8w-1571986151637)(C:\Users\huangxin\Desktop\仿射变换\opencv算法精讲-7\5.png)]

开操作消除暗背景下的的较亮区域,在明显不改变面积的情况下,消除白色亮的区域。闭操作是消除较亮区域内的较暗区域

顶帽变换

顶帽变换的定义图像减去开运算的结果

开运算可以消除暗背景下的较亮区域。原图减去开运算得到途中灰度较亮的区域,作用之一较正不均匀光照。又称白顶帽变换

底帽变换

底帽运算的定义图像减去闭运算的结果

开运算可以消除较亮背景下的较暗区域。原图减去开运算得到途中灰度较暗的区域,又称黑顶帽变换

形态学梯度

形态学梯度的定义为膨胀减去腐蚀的结果,膨胀为邻域的最大值,腐蚀为淋雨的最小值,高亮度减去低亮度得到的是物体的边界。

在opencv提供的函数morphologyEx修改op的值就可以实现顶帽,底帽运算,形态学梯度

//输入图像
Mat I;
//输出图像
Mat d;
//结构元
Mat element;
string window = "形态学处理";
//结构元半径
int r = 1;
int MAX_R = 20;
//迭代次数
int i = 1;
int MAX_I = 20;
//回调函数,调节r和i
void callBack(int, void*)
{
	//创建结构元
	element = getStructuringElement(MORPH_RECT, Size(2 * r + 1, 2 * r + 1));
	//形态学处理
	morphologyEx(I, d, cv::MORPH_TOPHAT, element, Point(-1, -1), i);
	//显示形态处理的效果
	imshow(window, d);
}
int main(int argc, char*argv[])
{
	//输入图像
	I = imread("G:\\blog\\OpenCV_picture\\chapter7\\open.jpg", 0);
	if (!I.data)
		return 0;
	//显示原图
	imshow("原图", I);
	//创建显示形态学结果显示窗口
	namedWindow(window, 1);
	//创建调节r的进度条
	createTrackbar("半径", window, &r, MAX_R, callBack);
	//创建调节i的进度条
	createTrackbar("迭代次数", window, &i, MAX_I, callBack);
	callBack(0, 0);
	waitKey(0);
	return 0;
}

运行图片:

{
//输入图像
I = imread(“G:\blog\OpenCV_picture\chapter7\open.jpg”, 0);
if (!I.data)
return 0;
//显示原图
imshow(“原图”, I);
//创建显示形态学结果显示窗口
namedWindow(window, 1);
//创建调节r的进度条
createTrackbar(“半径”, window, &r, MAX_R, callBack);
//创建调节i的进度条
createTrackbar(“迭代次数”, window, &i, MAX_I, callBack);
callBack(0, 0);
waitKey(0);
return 0;
}


运行图片:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qG5oqqdk-1571986151638)(C:\Users\huangxin\Desktop\仿射变换\opencv算法精讲-7\6.png)]
上一篇:Python+OpenCV实现图像特定区域宽度测量


下一篇:如何使用python的matplotlib重绘图像?