Histogram Equalization Python (No Numpy and No Plotting)

Question:

I am trying to work on a code for increasing the contrast on grayscale images to make them clearer. I can’t seem to get this code to work. I am trying to get the distribution frequency of each value (without using any modules aside from cv2) in the pixel and get the cumulative distribution frequency so I can then change the value using the equation below. Any idea what is wrong with my code?

import cv2
img=cv2.imread(raw_input())
shape=img.shape
row=shape[0]
col=shape[1]

def df(img): #to make a histogram (count distribution frequency)
    values=[]
    occurances=[]
    for i in range (len(img)):
        for j in img[i]:
            values.append(j)
            if j in values:
                count +=3
                occurances.append(count)
    return occurances

def cdf (img): #cumulative distribution frequency
    values2=[]
    for i in values:
        j=0
        i=i+j
        j+1
        values2.append(i)
    return values2

def h(img): #equation for the new value of each pixel
    h=((cdf(img)-1)/((row*col)-1))*255
    return h

newimage=cv2.imwrite('a.png')

This is an example of what I’m trying to do.
enter image description here

Thank you in advance.

Asked By: Fat Cat

||

Answers:

In case you’re not aware, opencv provides a built in function for historgram equalization, documented here.

Also concerning your code:

The distribution frequency (or histogram) isn’t calculated properly, since you only count the frequency of colors that do appear in the image. You should count the appearances of all color values, even if they don’t appear.
Also everytime your color reappears you add a new element of that color to your list, which doesn’t make a lot of sense. I’m not quite sure where the +=3 comes from either.

What I would do is something like this:

def df(img): #to make a histogram (count distribution frequency)
    values = [0] * 256
    for i in range(len(img)):
        for j in img[i]:
           values[j] += 1
Answered By: Mefaso

Here is a solution with some modifications. It gives the following output

Original: original

Equalized: histequalized

Major Modifications:

  1. The df() and cdf() functions have been made simple. Do print their output on execution to check if it matches with what you would expect it to give
  2. The equalize_image() function equalizes the image by interpolating from the normal pixel range (which is range(0,256)) to your cumulative distribution function

Here’s the code:

import cv2
img = cv2.imread(raw_input('Please enter the name of your image:'),0) #The ',0' makes it read the image as a grayscale image
row, col = img.shape[:2]


def df(img):  # to make a histogram (count distribution frequency)
    values = [0]*256
    for i in range(img.shape[0]):
        for j in range(img.shape[1]):
            values[img[i,j]]+=1
    return values


def cdf(hist):  # cumulative distribution frequency
    cdf = [0] * len(hist)   #len(hist) is 256
    cdf[0] = hist[0]
    for i in range(1, len(hist)):
        cdf[i]= cdf[i-1]+hist[i]
    # Now we normalize the histogram
    cdf = [ele*255/cdf[-1] for ele in cdf]      # What your function h was doing before
    return cdf

def equalize_image(image):
    my_cdf = cdf(df(img))
    # use linear interpolation of cdf to find new pixel values. Scipy alternative exists
    import numpy as np
    image_equalized = np.interp(image, range(0,256), my_cdf)
    return image_equalized

eq = equalize_image(img)
cv2.imwrite('equalized.png', eq)