Contrast Stretching

关于对比度

在图像中,对比度指明暗程度,对比度高说明物体相对于其他更容易分辨。如下图,左边低对比度图像看起来雾蒙蒙的,像是在阴天拍的,很难区分其中的细节,而右边高对比度图像就好像在夏天拍的,光线充足,看得很清楚。

来源:OpenCV

再通过像素强度图来看两张图的像素分布,低对比度图像强度分布比较集中,而高对比图强度分散在整个区域,如下分别是两张图片的histogram图:

来源:OpenCV

造成图像低对比度的原因有拍照时周围环境较暗,廉价的图像传感器,或者是系统错误的设置。

如何提高对比度?

根据上面的histogram图,可以看到,要提高对比度,我们需要把分布集中的像素分散开,是指分布到整个区域(0-255)。

来源:OpenCV

根据转换函数的不同,增强对比度的方式分为线性非线性。线性方式有Contrast-Stretching,基于分段线性函数,非线性方式包括Histogram Equilisation和Gaussian Stretch等,都是基于非线性函数来转换histogram图。

本文将详细介绍基于线性函数方式的Contrast-Stretching方法。Contrast-Stretching是基于分段的线性函数,每段函数都是递增函数,如下图:

来源:OpenCV

通过改变(r1, s1)和(r2, s2),转换函数也随之改变,r和s代表像素强度值。

⁃ 如果r1=r2, s1=s2, 则函数变成一个线性函数
⁃ 如果r1=r2, s1=0, s2=L-1, 则函数变成一个thresholding函数
⁃ 如果(r1,s1)=(r_min, 0), (r2, s2)=(r_max, L-1), 则函数变成Min-Max Stretching函数
⁃ 如果(r1,s1)=(r_min + c, 0), (r2, s2)=(r_max - c, L-1), 则函数变成Percentile Stretching函数

接下来着重讲解Min-Max Stretching和Percentile Stretching函数。

在Min-Max 拉伸中,输入图像的最小值与最大值将被分别映射到数值区域的最大和最小(0和255),比如最小像素值为50,转换后对应为0,最大值200经过转换后为255,位于最大值与最小值区间的值则被映射到0-255之间。Min-Max计算公式如下:

Xnew=XinputXminXmaxXmin255(1)X_{new} = \frac{X_{input} - X_{min}}{X_{max} - X_{min}} * 255 \tag{1}

经过Min-Max操作后,对比如下:

两者对比之后会看到,下面的图清晰了一点,histogram图可以发现像素值向两边分散了一点,但是不是很明显,区域两端的像素值分区仍然很少。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import cv2
import numpy as np

# Read the image
img1 = cv2.imread('D:/downloads/contrast.PNG',0)

# Create zeros array to store the stretched image
minmax_img = np.zeros((img1.shape[0],img1.shape[1]),dtype = 'uint8')

min_v = np.min(img1)
max_v = np.max(img1)

# Loop over the image and apply Min-Max formulae
for i in range(img1.shape[0]):
for j in range(img1.shape[1]):
minmax_img[i,j] = 255*(img1[i,j]-min_v)/(max_v-min_v)

# Displat the stretched image
cv2.imshow('Minmax',minmax_img)
cv2.waitKey(0)

参考:

  1. https://theailearner.com/2019/01/30/what-is-contrast-in-image-processing/
  2. https://docs.opencv.org/3.1.0/d5/daf/tutorial_py_histogram_equalization.html