问题发现
被拍摄的物体由于不均匀光照可能对二值化结果产生较大影响,例如我希望通过二值化方法提取滤芯中间的图形:
因为光照的不均匀,导致图片左下角明显比右上角更亮,加上不合理的二值化阈值,导致中间图形未封闭。进而影响轮廓检测。
解决方案
分块二值化
思路:将整个图形切分为多个部分,对每个部分选取不同的阈值进行二值化,最后组合在一起。
提升对比度
有三种方式:提高高亮度区域、降低低亮度区域、两着同时进行
这里仅以提高高亮度区域举例:
思路:对每个像素点的灰度值使用某种非线性变换,该变换应具有以下特征:
- 低亮度区域值基本不变
- 高亮度区域值趋于统一
经测试,以下方程比较符合要求(详见 Photoshop中曝光度的三个参数原理):
\(Y = -((-X + 1)^{\frac {1}{2.2}} – 0.1)^{\frac {1}{2.2}} + 1\)
其中 X 为原始灰度,Y 为调整后的值。图形如下:

# 提高高光区域亮度
pic = (-np.clip((-pic_crop/255 + 1) ** 2.2 - 0.1, 0, 1) ** (1/2.2) + 1)*255
处理后的结果如下第一张图:
可以看到,仅外边高亮轮廓灰度被统一,内部图形阴影几乎没变。经过处理后的轮廓提取更准确了。
伽马变换其实也是这个原理,只不过换了另外一个函数,使得暗的地方更暗,或亮的地方更亮。
直方图均衡化
这也是一种常见的增强对比度方法,图像的直方图反应的是图像中各个灰度值分布数据。例如在某个图中,灰度值为1的像素有多少个,灰度值为2的像素又有多少个等等。
直方图均衡化就好比劫富济贫,把某些数量很多的灰度值向其两边扩散。例如,若一个黑底的图片上有一个白色的圆。经过直方图均衡化之后就会得到一个从中心向四周灰度径向变换的圆。中心的亮度比原来更白,而周边的亮度比原来更暗,这就增强了对比度。
举例:
这是正常的图片以及其直方图
这是经过直方图均衡化之后的
由于对比度的提高,使得暗处的更暗,进而可以完整地提取出整个中间的图形
equalize_hist_pic = cv.equalizeHist(pic)
使用梯度计算边缘
有时候光线照射角度实在刁钻:

目视可以很轻易地区分出内部的小圆,但该图却不能通过二值化提取,否则会出现如下情况

出现该情况的原因是:大圆左边的灰度其实和小圆右边的灰度值是相同的,但由于它们处于不同的参考系下,人眼很难辨认它们的真实灰度。这种情况下,即使按照上面的办法做也是不行的。此时就能用梯度方法匹配
具体做法是:
生成四个方向的差分卷积核,对图片进行四次卷积后,将结果取绝对值然后相加
# 求出图像梯度,pic_crop是原图
k_l = np.array([[1,1,1,-1,-1,-1],[1,1,1,-1,-1,-1],[1,1,1,-1,-1,-1]])
pic_crop_kl = np.abs(cv.filter2D(pic_crop, -1, k_l))
k_t = np.array([[1,1,1],[1,1,1],[1,1,1],[-1,-1,-1],[-1,-1,-1],[-1,-1,-1]])
pic_crop_kt = np.abs(cv.filter2D(pic_crop, -1, k_t))
k_r = np.array([[-1,-1,-1, 1,1,1],[-1,-1,-1, 1,1,1],[-1,-1,-1, 1,1,1]])
pic_crop_kr = np.abs(cv.filter2D(pic_crop, -1, k_r))
k_d = np.array([[-1,-1,-1],[-1,-1,-1],[-1,-1,-1],[1,1,1],[1,1,1],[1,1,1]])
pic_crop_kd = np.abs(cv.filter2D(pic_crop, -1, k_d))
pic_crop_k = pic_crop_kl + pic_crop_kt + pic_crop_kr + pic_crop_kd
pic_crop_k = np.clip(pic_crop_k, 0, 255)
再进行轮廓的提取就简单的多
后记
方法还有很多,我没一一列举,之前也有看到一篇论文说用什么插值法,但是没实验。