在这篇文章中,你会了解到关于什么是自适应阈值,并且如何对图像使用自适应阈值。这篇文章中,除了介绍OpenCV中经典的cv2.adaptiveThreshold
函数,还有我根据halcon中的local_threshold
函数实现的自适应阈值方法。
在普通的二值化阈值算法中,是对图片中所有的像素都应用同样的一个阈值,大津算法(Otsu)也是如此,只不过这个算法自动计算了一个能让图片尽量分为两个部分的阈值,总之,都是全局阈值(global threshold)。但在实际需求中,由于光线明暗的变化,阴影等等,仅仅是全局阈值,可能并没有办法得到我们想要的结果。
这个时候,先不要急于使用深度学习分割方法(实例分割或者语义分割),你应该先试试自适应阈值的方法。
自适应阈值,顾名思义,自适应阈值就是通过每个像素相邻的像素,来计算出这个像素位置的阈值,每一个像素的阈值,都是私人定制!
现在我们通过一张图片来观察一下全局二值化和私人定制自适应阈值处理一张图片的效果。
为了方便进行阈值处理,我们直接把这张图片按照灰度图片来读取 :
img = cv2.imread('chart.JPG', 0)
这里第二个变量的位置写0,就代表按照黑白图片模式来读取一张图片。
可以看到这张表格有一些行的背景颜色是灰色的,而且还存在光照不均的问题,如果我们使用128作为全局阈值,进行二值化处理:
_,res3 = cv2.threshold(img,128,255,cv2.THRESH_BINARY)
cv2.imshow('binary threshold',res3)
cv2.waitKey()
我们的目的是提取出这张表格上的文字,这样的效果显然不能让我们满意。
otsu_thres,res4 = cv2.threshold(img,128,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
cv2.imshow('otsu threshold',res4)
cv2.waitKey()
大津算法为我们计算出来的全局阈值是95.0,居然还是一个浮点数我笑了。
再来看看效果:
比我们自己随便设的128好一些,不过也并不能满足我们。
再来看一下OpenCV中cv2.adaptiveThreshold
函数的效果:
res2 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 21, 15)
cv2.imshow('adaptive threshold',res2)
cv2.waitKey()
看起来很妙,不是吗?
在解读参数之前,来看看这个算子背后的数学。
T = m e a n ( I L ) − C T = mean(I_{L} )-C T