Gamma correction is a point operation that transforms the image by raising it to an exponent (gamma). For a low brightness image, low values of gamma stretches the darker parts and suppresses the brighter parts, whereas for a high brightness image high values of gamma stretches the brighter parts and suppresses the darker parts.
Here I have taken a low brightness (Img1) and a high brightness (Img2) gray scale images for the experiment.
Img1: Gamma=1
Img1: Gamma=0.26
Img2: Gamma=1
Img2: Gamma=1.24
We can see from the pdf (histogram) of Img1 that most of the pixel has values <= 100. Say we want 100 to map to 200. Then we calculate the value of gamma as:
256((100/256)^gamma)=200 => gamma = 0.26
The histogram of Img2 shows most values are more than 120. To map 120 to 100, we use the following gamma value:
256((120/256)^gamma) = 100 => gamma = 1.24
Histogram of Img1
Histogram of Img1 after Gamma correction
Used the applyTransformation() function discussed in the next section
Histogram equalization is a technique for adjusting image intensities to enhance contrast. Specifically, we wish to find a transformation that converts the image such that it has a uniform histogram.
Histogram of Img2
CDF of Img2
Output Image
Histogram of output
Histogram of Img1
CDF of Img2
Output Image
Histogram of output
def getHist(image): #returns pdf array //this is a function to get the histogram (pdf) of an image
pdf = np.zeros([256])
for i in range(image.shape[0]):
for j in range(image.shape[1]):
pdf[image[i,j]] += 1
return pdf/image.size
def getHistEqTransformation(hist): //this function returns the cdf(cumulative distribution function) for the pdf of the image
cumsum = np.zeros(hist.shape)
for i in range(len(cumsum)):
cumsum[i] = hist[i] + (cumsum[i-1],0)[i==0]
transform = cumsum*(hist.size-1)
return [int(np.ceil(i)) for i in transform]
def applyTransformation(image, transformation): //Function to apply gamma transformation to the image
newimg = np.zeros(image.shape, dtype='uint8')
for i in range(image.shape[0]):
for j in range(image.shape[1]):
newimg[i][j] = transformation[image[i][j]]
return newimg
newImage = applyTransformation(image, getHistEqTransformation(getHist(image)))
In Histogram Matching a target histogram is specified. We calculate the CDF transformation T1 to go from input to uniform and CDF transformation T2 to go from target to uniform. Then T1 is applied followed by inverse of T2 to get the target shape. Here we apply histogram matching on Img1.
Calculating the inverse transformation may be tricky as there might not be a one to one correspondence. Interpolation might have to be used.
CDF of Target Histogram
Inverse of the target CDF
Histogram Matching Output
Histogram of output image post histogram matching
def getInverseMap(transformation): #Compute inverse function
inverse = [[] for i in range(len(transformation))]
for i in range(len(transformation)): #get the 'obvious' inverses
inverse[transformation[i]] += [i]
inverse = [np.median(i) if len(i)!=0 else 0 for i in inverse] #a single value might map to multiple values, so get median
for i in range(1,len(inverse)): #starting from 1. assume 0 maps to 0. interpolate and fill missing spots
if inverse[i]==0:
inverse[i] = inverse[i-1]
return np.ceil(inverse).astype('uint8')
T1 = getHisEqTransformation(getHist(image))
T2inv = getInverseMap(getHisEqTransformation(targetHist))
newImage = applyTransformation(applyTransformation(image, T1), T2inv)