传统图像处理算法
前言
不均匀光照的文本图片,看起来不清晰,有阴影,如下图所示
图像光照不均匀的具体表现
1) 图像整体灰度像素值偏低,由于拍摄时现场的光照条件限制或设备自身的原因,导致图像的整体灰度值是偏低的或者图像的对比度偏低,从而使图像的信息难以识别,如红外图像、灰暗条件下拍摄的图像;
2)图像的局部灰度像素值低,由于拍摄过程中周围环境的光照不均导致的图像一部分光照充足,一部分光照欠充足。光照充足的部分目标与背景对比度较高,易于辨认,而欠充足部分则灰度偏低且目标与背景差别较小,不易辨认 。
3)图像中含有具有反光特性的物体,这类采集到的图像中会有局部的反光现象(亮度很大),从而导致该部分像素信息的损失 。
一、针对光照不均匀图像的二值化方法主要可分为三大类
1)对图像进行分块处理
直方图谷底确定阈值法、迭代选择阈值法、最大类间方差法(OSTU)法等 。
OTSU的基本思想是:
首先将图像转换成灰度图像,按图像的灰度特性将图像分成背景(大于阈值)和目标(小于阈值)两部分。背景和目标之间的类间方差越大,说明构成图像的两部分的差别越大,当部分目标错分为背景或部分背景错分为目标都会导致两部分差别变小。
因此,使类间方差最大的分割意味着错分概率最小,OTSU通过最大化类间方差来实现最佳阈值的选取。
2) 对图像背景光照进行预处理,使图像灰度分布更均匀并增强图像对比度,灰度变换主要有以下几种方法 :
a) 利用直方图进行灰度变换
b) 基于感知理论( Retinex 理论)的增强方法
c) 基于图像梯度值的增强方
d) 背景估计法恢复光照不均匀图像
3)经典的局部阈值方法
局部阈值法是根据图像中的每一个像素点与其邻域像素点的灰度值变化来设定阈值,进而逐点进行二值化的方法。 经典的局部阈值算法有 Bernsen 算法、 Niblack 算法和 Sauvola算法、 Chow 和 Kaneko 算法等。但这些算法对图片逐点计算,计算量很大,运算时间随着图片的增大往往是几何倍数的增加。
Sauvola算法:输入是灰度图像,它以当前像素点为中心,根据当前像素点邻域内的灰度均值与标准方差来动态计算该像素点的阈值。
本文主要用OSTU算法与Sauvola算法做二值化处理,并进行效果对比。
二、OSTU算法处理
代码如下(示例):
def ostu_process(img):
#img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #图像灰度化处理
h, w = img.shape[:2]
threshold_t = 0
max_g = 0
for t in range(255):
front = img[img < t]
back = img[img >= t]
front_p = len(front) / (h * w)
back_p = len(back) / (h * w)
front_mean = np.mean(front) if len(front) > 0 else 0.
back_mean = np.mean(back) if len(back) > 0 else 0.
g = front_p * back_p * ((front_mean - back_mean) ** 2)
if max_g > g:
max_g = g
threshold_t = t
img[img >= threshold_t] = 255
cv2.imwrite("C:/Users/xh/Desktop/ma_test_result/pic1_b.jpg", img)
cv2.imshow('out_img', img)
print("end")
if cv2.waitKey(0) == 27:
cv2.destroyAllWindows()
return
OSTU处理效果如下:
三、Sauvola算法处理
代码如下(示例)
def sauvola(img, k=0.1, kernerl=(31, 31)):
# 计算积分图和积分平方和图
integral_sum, integral_sqrt_sum = integral(img)
# 创建图像
rows, cols = img.shape
diff = np.zeros((rows, cols), np.float32)
sqrt_diff = np.zeros((rows, cols), np.float32)
mean = np.zeros((rows, cols), np.float32)
threshold = np.zeros((rows, cols), np.float32)
std = np.zeros((rows, cols), np.float32)
whalf = kernerl[0] >> 1 # 计算领域类半径的一半
for row in range(rows):
print('第{}行处理中...'.format(row))
for col in range(cols):
xmin = max(0, row - whalf)
ymin = max(0, col - whalf)
xmax = min(rows - 1, row + whalf)
ymax = min(cols - 1, col + whalf)
area = (xmax - xmin + 1) * (ymax - ymin + 1)
if area <= 0:
sys.exit(1)
if xmin == 0 and ymin == 0:
diff[row, col] = integral_sum[xmax, ymax]
sqrt_diff[row, col] = integral_sqrt_sum[xmax, ymax]
elif xmin > 0 and ymin == 0:
diff[row, col] = integral_sum[xmax, ymax] - integral_sum[xmin - 1, ymax]
sqrt_diff[row, col] = integral_sqrt_sum[xmax, ymax] - integral_sqrt_sum[xmin - 1, ymax]
elif xmin == 0 and ymin > 0:
diff[row, col] = integral_sum[xmax, ymax] - integral_sum[xmax, ymax - 1]
sqrt_diff[row, col] = integral_sqrt_sum[xmax, ymax] - integral_sqrt_sum[xmax, ymax - 1]
else:
diagsum = integral_sum[xmax, ymax] + integral_sum[xmin - 1, ymin - 1]
idiagsum = integral_sum[xmax, ymin - 1] + integral_sum[xmin - 1, ymax]
diff[row, col] = diagsum - idiagsum
sqdiagsum = integral_sqrt_sum[xmax, ymax] + integral_sqrt_sum[xmin - 1, ymin - 1]
sqidiagsum = integral_sqrt_sum[xmax, ymin - 1] + integral_sqrt_sum[xmin - 1, ymax]
sqrt_diff[row, col] = sqdiagsum - sqidiagsum
mean[row, col] = diff[row, col] / area
std[row, col] = math.sqrt((sqrt_diff[row, col] - math.sqrt(diff[row, col]) / area) / (area - 1))
threshold[row, col] = mean[row, col] * (1 + k * ((std[row, col] / 128) - 1))
if img[row, col] < threshold[row, col]:
img[row, col] = 0
else:
img[row, col] = 255
def integral(img):
integral_sum = np.zeros((img.shape[0], img.shape[1]), dtype=np.int32)
integral_sqrt_sum = np.zeros((img.shape[0], img.shape[1]), dtype=np.int32)
rows, cols = img.shape[:2]
#print(rows, cols)
for r in range(rows):
sum = 0
sqrt_sum = 0
for c in range(cols):
sum += img[r][c]
sqrt_sum += math.sqrt(img[r][c])
if r == 0:
integral_sum[r][c] = sum
integral_sqrt_sum[r][c] = sqrt_sum
else:
integral_sum[r][c] = sum + integral_sum[r - 1][c]
integral_sqrt_sum[r][c] = sqrt_sum + integral_sqrt_sum[r - 1][c]
return integral_sum, integral_sqrt_sum
Sauvola处理效果如下:
总结
两种算法有各自的优势和劣势,从上面的二值化效果来看来看,Sauvola算法处理的效果要比OSTU算法要好,但是处理速度慢很多