OpenCV4学习笔记(20)——二值分割

本次要整理记录的内容是有关于图像二值化的知识,图像二值化是一种非常重要的预处理手段,这步操作所得到的二值图像对后续的图像处理过程会有非常大的影响。如果能够获得质量很高的二值图像,那么后续的处理操作也会简便得多。

- 全局阈值分割

要对图像进行二值化操作,首先需要将图像转换成灰度图像,然后设置一个用来进行二值分割的阈值,再遍历灰度图像的每个像素点,如果该像素点的灰度值大于阈值,就将该像素点设为255的灰度值,如果该像素点的灰度值小于阈值,就将该像素点设为0的灰度值。这样就实现了最简单的图像二值化,下面给出代码实现:

Mat image_gray;
	cvtColor(image, image_gray, COLOR_BGR2GRAY);
	Scalar m = mean(image_gray);			//mean(Mat())函数的返回值是一个Scalar对象
	int threshold = m[0];			//以灰度图的像素值均值作为二值分割的阈值
	int height = image.rows;
	int width = image.cols;
	Mat binary = Mat::zeros(image.size(), CV_8UC1);
	for (int row = 0; row < height; row++)
	{
		for (int col = 0; col < width; col++)
		{
			uchar  gray = image_gray.at<uchar>(row, col);
			if (gray > threshold)
			{
				binary.at<uchar>(row, col) = 255;
			}
		}
	}
	imshow("binary", binary);

在上述代码中,我通过mean(image_gray)函数来获取一幅灰度图像的像素均值,要注意mean(Mat())函数的返回值是一个Scalar对象,然后将该均值作为二值分割的阈值,再进行像素点遍历求取二值图像。效果如下:在这里插入图片描述
实际上,在OpenCV中提供了一些相关的API可以使用,不需要我们这么麻烦地去遍历像素点来进行图像二值化。相关代码如下:


	Mat image_gray, binary_image, binary_color_image, binary_image_OTSU, binary_image_TRIANGLE;
	cvtColor(image, image_gray, COLOR_BGR2GRAY);
	threshold(image_gray, binary_image, 127, 255, THRESH_BINARY);		//将大于阈值的设为255,小于阈值的设为0
	//threshold(image_gray, binary_image, 127, 255, THRESH_BINARY_INV);		//将大于阈值的设为0,小于阈值的设为255
	//threshold(image_gray, binary_image, 127, 255, THRESH_TOZERO);		//将小于阈值的设为0,大于阈值的保留
	//threshold(image_gray, binary_image, 127, 255, THRESH_TOZERO_INV);	//将大于阈值的设为0,小于阈值的保留
	//threshold(image_gray, binary_image, 127, 255, THRESH_TRUNC);		//将大于阈值的设置为阈值,小于阈值的保留


	double thresh = threshold(image_gray, binary_image_OTSU, 0, 255, THRESH_BINARY | THRESH_OTSU);			//自动计算阈值,参数thresh无效;只适用于8位单通道图
	//double thresh = threshold(image_gray, binary_image_TRIANGLE, 0, 255, THRESH_BINARY_INV | THRESH_TRIANGLE);			//自动计算阈值,参数thresh无效;只适用于8位单通道图

	imshow("binary_image", binary_image);
	imshow("binary_image_OTSU", binary_image_OTSU);
	//imshow("binary_image_OTSU", binary_image_TRIANGLE);
	//imshow("binary_color_image", binary_color_image);

其中threshold()函数是主要API,下面对其参数进行介绍:
第一个参数src:需要进行图像二值化的输入图像,可以是单通道的灰度图也可以是三通道的彩色图,但一般使用灰度图像来进行二值分割;
第二个参数dst:输出的二值图像;
第三个参数thresh:输入的分割阈值,可以自动计算;
第四个参数maxvalue:输入图像的最大像素值,一般为255;
第五个参数type:有以下7种常见方法选择,前五种需要自行设置参数thresh,
1、THRESH_BINARY:将大于阈值的设为255,小于阈值的设为0
2、THRESH_BINARY_INV:将大于阈值的设为0,小于阈值的设为255
3、THRESH_TOZERO:将小于阈值的设为0,大于阈值的保留
4、THRESH_TOZERO_INV:将大于阈值的设为0,小于阈值的保留
5、THRESH_TRUNC:将大于阈值的设置为阈值,小于阈值的保留
6、THRESH_OTSU:自动计算阈值,参数thresh无效;只适用于8位单通道图;适用于具有双峰直方图的图像进行二值分割
7、THRESH_TRIANGLE:自动计算阈值,参数thresh无效;只适用于8位单通道图;适用于具有单峰直方图的图像进行二值分割
其中,第六、七种THRESH_OTSU和THRESH_TRIANGLE可以根据图像直方图实现自动全局阈值寻找,并进行二值分割,但是只适用于灰度图像。

两个自动全局阈值寻找算法的大致原理是:
OTSU算法:将直方图的不同区间进行分类,并计算最大的类间方差即为阈值。对直方图有两个峰,中间有明显波谷的直方图的对应图像的二值化效果比较好,而对于只有一个单峰的直方图对应的图像分割效果比较差;
TRIANGLE算法:将直方图的单峰近似为三角形,寻找顶点做垂线到底部的点,并将该点向三角形的边做垂线,垂线和三角形的边的交点所对应的像素值即为阈值。对直方图只有单峰的对应图像的二值化效果比较好。

如果使用了自动全局阈值寻找算法,可以通过threshold()函数的返回值来获取自动计算的阈值,当然也可以不设置获取该返回值。

threshold(image_gray, binary_image_OTSU, 0, 255, THRESH_BINARY | THRESH_OTSU)

而且,在使用自动全局阈值寻找算法时,一般会使用THRESH_BINARY | THRESH_OTSU这种写法,这样我们能够人为指定阈值分割方式,便于我们分割出较为理想的结果。

- 局部自适应阈值分割
上面所使用的threshold()方式,都是基于全局阈值进行分割的,一旦一幅图像中具有像素分布不均、光照分布不均匀等情况,就难以达到比较好的效果。尤其是在进行OCR识别的时候,拍摄的文本图像很经常会存在光照不均的情况,这时候如果使用全局阈值分割,效果会比较差。所以OpenCV又提供了一个自适应阈值分割的API来解决类似的问题。
局部自适应阈值分割,其实是根据每个像素的邻域块的像素值分布来确定该像素位置上的二值化阈值,因此会形成很多的局部阈值,在图像中的不同区域根据不同的局部阈值来进行二值分割。
在OpenCV中自适应阈值的计算方法是,先对图像求均值(分为高斯模糊均值和盒子模糊均值两种方法),再用原图像减去均值图像,得到的差值再减去一个规定值,再判断最后结果是否大于0,并进行相应的阈值分割。
其中的高斯模糊均值,是对输入图像进行高斯模糊后求其均值;而盒子模糊均值,是对输入图像进行均值模糊后求其均值。下面是代码演示:

	Mat gray_image, adaptive_binary_image_gauss, adaptive_binary_image_mean;
	cvtColor(image, gray_image, COLOR_BGR2GRAY);
	adaptiveThreshold(gray_image, adaptive_binary_image_gauss, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 25, 10);	
	adaptiveThreshold(gray_image, adaptive_binary_image_mean, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 25, 10);
	
	imshow("adaptive_binary_image_gauss", adaptive_binary_image_gauss);
	imshow("adaptive_binary_image_mean", adaptive_binary_image_mean);

其中,主要API就是adaptiveThreshold(),下面介绍其参数:
第一个参数src:需要进行二值分割的输入图像;
第二个参数:输出的二值图像;
第三个参数maxValue:最大灰度值,一般为255;
第四个参数adaptiveMethod:选择是高斯模糊均值还是盒子模糊均值,分别是ADAPTIVE_THRESH_GAUSSIAN_CADAPTIVE_THRESH_MEAN_C
第五个参数thresholdType:使用的阈值分割方式,只能输入THRESH_BINARY或THRESH_BINARY_INV,当使用其他阈值分割方式时会报错;二值分割后,需要是黑色背景、白色前景,根据目标是处于背景或前景来选择该参数;
THRESH_BINARY:二值图像 = 原图 – 均值图像 - C > 0? 255 : 0
THRESH_BINARY_INV: 二值图像 = 原图 – 均值图像 - C > 0? 0 : 255
第六个参数blockSize:进行模糊时的窗口大小,必须是奇数
参数C:与原图和均值图像的差值相减的规定值,可以为负值;经验值为10~25。
自适应阈值分割效果如下:
在这里插入图片描述
可以看出相比全局阈值,局部自适应阈值的二值化效果相对来说会比较好一些。

二值分割是很多图像处理操作的基础,例如二值分析用于缺陷检测:将输入图像二值化,通过形态学操作除去干扰,进行轮廓发现及轮廓分析,再将轮廓进行排序,对细微缺陷可以进行扩大,并与模板进行比对(如相减,相减后不为零的区域即为缺陷),最后输出检测结果。这只是其中一种具体应用,可见二值分割在图像处理中的重要地位。

扫描二维码关注公众号,回复: 10016538 查看本文章

其实图像二值化操作,有好几种主要方式,下面做一个总结:
(1)全局阈值二值化:通过threshold()这个API对全局实现阈值化,可以自动设置阈值,也可以根据图像是单峰直方图或双峰直方图来自动计算阈值;
(2)基于形态学梯度二值化:通过morphologyEx()的MORPH_GRADIENT对图像求基本梯度,再转换成灰度图进行全局阈值分割;
(3)inRange二值化:通过不同色彩的H(色调)和S(饱和度)的不同范围来选取不同的颜色,通过图像中某区域是否被选取来进行图像二值化;
(4)基于Canny边缘二值化:将图像直接进行canny算子边缘检测操作,能够消除部分噪声(内部进行了高斯模糊),并对边缘进行加强,同时输出边缘的二值图像,适用于需要对边缘操作的图像,例如轮廓发现等;
(5)自适应二值化:通过adaptiveThreshold()这个API实现对图像的局部自适应二值分割,适用于光照不均匀的图像,尤其是需要进行OCR识别的文本图像,比普通的全局阈值分割有比较好的分割效果。

本次主要记录了全局阈值二值化以及局部自适应二值化,后续有机会再逐一整理其他二值化操作,那就到此结束啦,谢谢阅读~

PS:本人的注释比较杂,既有自己的心得体会也有网上查阅资料时摘抄下的知识内容,所以如有雷同,纯属我向前辈学习的致敬,如果有前辈觉得我的笔记内容侵犯了您的知识产权,请和我联系,我会将涉及到的博文内容删除,谢谢!

发布了36 篇原创文章 · 获赞 43 · 访问量 1806

猜你喜欢

转载自blog.csdn.net/weixin_45224869/article/details/104692021