## Opencv Python adaptive contrast enhancement (ACE)

SongpingWang 2020-11-13 11:05:18
opencv python adaptive contrast enhancement

### One 、 Algorithm principle

In the method of image processing , The adaptive method is related to the information of the image itself , A series of methods for image processing based on image to image features , These methods tend to have better robustness 、 Universality . And this kind of ACE Method by NarendraPM Narendra P MNarendraPM Et al. 《Real-Time Adaptive Contrast Enhancement》 mention . Different from the global image enhancement method , Local contrast enhancement using local standard deviation is better than global image enhancement . It takes full account of the image area contrast . But it's a lot more computation .

In the classical image algorithm , Algorithms involving local regions , Most of the effects are better than the global ones , But the amount of computation is almost exponential , for example NLM Noise reduction algorithm .

For every point in the image , The local mean and local standard deviation are calculated respectively ;
M ( i , j ) = 1 ( 2 n + 1 ) ( 2 m + 1 ) ∑ s = i − n i + n ∑ k = j − m j + m f ( s , k ) M(i,j) = \frac{1}{(2n+1)(2m+1)}\sum_{s=i-n}^{i+n}\sum_{k=j-m}^{j+m}f(s,k)

σ ( i , j ) = 1 ( 2 n + 1 ) ( 2 m + 1 ) ∑ s = i − n i + n ∑ k = j − m j + m ( f ( s , k ) − M ( i , j ) ) \sigma (i,j) = \frac{1}{(2n+1)(2m+1)}\sum_{s=i-n}^{i+n}\sum_{k=j-m}^{j+m}(f(s,k)-M(i,j))

among ：
f ( s , k ) f(s,k) For coordinates ( s , k ) (s,k) Point pixel value of .
M ( i , j ) M(i,j) For the point ( i , j ) (i,j) Centered , Window size is ( 2 n + 1 ) , ( 2 m + 1 ) (2n+1),(2m+1) The local mean of the region of
σ 2 ( i , j ) σ^2 (i,j) Is the local variance , namely σ ( i , j ) σ(i,j) Is the standard deviation of the local image .

After obtaining the local mean and standard deviation , You can enhance the image , The formula is as follows ;
I ( i . j ) = M ( i . j ) + G ( f ( i . j ) − M ( i . j ) ) I(i.j) = M(i.j) +G ( f(i.j) -M(i.j) )

G = α M σ ( i . j )        0 < α < 1 G = \alpha \frac{M}{\sigma (i.j)} ~~~~~~ 0<\alpha <1

In the above formula :
I ( i , j ) I(i,j) Is the enhanced pixel value ,
M M Is the global mean （ You can also set it to a reasonable value ）,
α α It's a coefficient parameter , Generally less than 1 Greater than 0 Decimals of .

If we take the local mean of each point M ( i , j ) M(i,j) Make up a picture ,《 The physical meaning and simple application of digital image Fourier transform 》
mention , Mean filtering is a low-pass filter , What you get is the low frequency part of the image , That's the background part , f ( i , j ) − M ( i , j ) f(i,j)−M(i,j) It can be used to quantify whether a point in the image is high frequency or low frequency . And in general ,G Are greater than 1 Of , So pass G ( f ( i , j ) − M ( i , j ) ) G(f(i,j)−M(i,j)) It can enlarge the high frequency part of the image , And then enhance the image .

It can be seen from the above process that , If G It's a fixed parameter , By means of the local mean of the equation , We have been able to achieve a certain degree of adaptive enhancement of the image . So why do we have to use the parameter G How about introducing the standard deviation in ？

Let's recall the original intention of contrast enhancement , The purpose of contrast enhancement is to make the contrast of the image with low contrast become obvious , And for images with strong contrast , There is no need to enhance . So in the same image , In particular, we need to enhance the low contrast part . The variance represents the uniformity of the pixel value of the image , We can think of a local region with a larger variance , The more uneven the pixel value is , The more contrast ; conversely , The smaller the variance, the smaller the local area , The more uniform the pixel value is , The weaker the contrast . therefore , In the parameter GG Divided by the local standard deviation , It can make the enhancement effect of the weak contrast part of the image more obvious .

secondly , If all the points in the whole image are enhanced in equal proportion , The high frequency part of the image itself is over enhanced , The image looks very strange .

Color image of ACE
I saw someone on the Internet saying , Color image enhancement can be done on RGB Three channels are enhanced and merged . This view is wrong , Because each channel is enhanced separately , Will cause the color change of the image , The image will not change its original color .

So you need to turn the image to HSI Color space , Or is it YCrCb Color space . The former only needs to be about I The brightness channel is enhanced , and H、S There is no need to change the hue and saturation channels . The latter uses Y The channel represents brightness , Only need to Y The channel can be enhanced . After enhancement, merge channels , Convert back to RGB The space completes the enhancement of color image .

### Two 、 The code is compared with each method

Use PIL Image processing package ：（ Overall ） Image contrast enhancement .【 For more information, please click 】

from PIL import Image
from PIL import ImageEnhance
img = Image.open('./001.png')
# Contrast enhancement
enh_con = ImageEnhance.Contrast(img)
contrast = 1.5
img_contrasted = enh_con.enhance(contrast)
img_contrasted.save("./001_1.png")


Local adaptive image enhancement

# -*- coding: utf-8 -*-
import numpy as np
import cv2
def getVarianceMean(scr, winSize):
if scr is None or winSize is None:
print("The input parameters of getVarianceMean Function error")
return -1
if winSize % 2 == 0:
print("The window size should be singular")
return -1
copyBorder_map=cv2.copyMakeBorder(scr,winSize//2,winSize//2,winSize//2,winSize//2,cv2.BORDER_REPLICATE)
shape=np.shape(scr)
local_mean=np.zeros_like(scr)
local_std=np.zeros_like(scr)
for i in range(shape[0]):
for j in range(shape[1]):
temp=copyBorder_map[i:i+winSize,j:j+winSize]
local_mean[i,j],local_std[i,j]=cv2.meanStdDev(temp)
if local_std[i,j]<=0:
local_std[i,j]=1e-8
return local_mean,local_std
def adaptContrastEnhancement(scr, winSize, maxCg):
if scr is None or winSize is None or maxCg is None:
print("The input parameters of ACE Function error")
return -1
YUV_img=cv2.cvtColor(scr,cv2.COLOR_BGR2YUV) ## Switching channels
Y_Channel = YUV_img[:,:,0]
shape=np.shape(Y_Channel)
meansGlobal=cv2.mean(Y_Channel)[0]
## It is available here boxfilter Methods for calculating local homogeneity and variance
# localMean_map=cv2.boxFilter(Y_Channel,-1,(winSize,winSize),normalize=True)
# localVar_map=cv2.boxFilter(np.multiply(Y_Channel,Y_Channel),-1,(winSize,winSize),normalize=True)-np.multiply(localMean_map,localMean_map)
# greater_Zero=localVar_map>0
# localVar_map=localVar_map*greater_Zero+1e-8
# localStd_map = np.sqrt(localVar_map)
localMean_map, localStd_map=getVarianceMean(Y_Channel,winSize)
for i in range(shape[0]):
for j in range(shape[1]):
cg = 0.2*meansGlobal/ localStd_map[i,j];
if cg >maxCg:
cg=maxCg
elif cg<1:
cg=1
temp = Y_Channel[i,j].astype(float)
temp=max(0,min(localMean_map[i,j]+cg*(temp-localMean_map[i,j]),255))
# Y_Channel[i,j]=max(0,min(localMean_map[i,j]+cg*(Y_Channel[i,j]-localMean_map[i,j]),255))
Y_Channel[i,j]=temp
YUV_img[:,:,0]=Y_Channel
dst=cv2.cvtColor(YUV_img,cv2.COLOR_YUV2BGR)
return dst
def main():
img=cv2.imread(input_fn)
if img is None:
print("The file name error,please check it")
return -1
print(np.shape(img))
dstimg=adaptContrastEnhancement(img,15,10)
cv2.imwrite('output.jpg',dstimg)
cv2.waitKey(0)
return 0
input_fn='temp1.jpg'
if __name__ == '__main__':
main()