6

I am trying to to loop through an nparray which contains pixel data. I want to perform an equalization to each of the pixel values and display them as a histogram.

I already achieved my goal by doing following:

def stratch_contrast(img): 

    hist,bins = np.histogram(img.flatten(),256,[0,256])
    cdf = hist.cumsum()
    cdf_normalized = cdf * hist.max()/ cdf.max()

    cdf_m = np.ma.masked_equal(cdf,0)
    cdf_m = (cdf_m - cdf_m.min())*255/(cdf_m.max()-cdf_m.min())
    cdf = np.ma.filled(cdf_m,0).astype('uint8')
    img = cdf[img]

    plt.hist(img.flatten(),256,[0,256], color = 'black')
    plt.xlim([0,256])
    plt.legend(('cdf','histogram'), loc = 'upper left')
    plt.show()

    img = cv2.imread(name,0)
    equ = cv2.equalizeHist(img)
    res = np.hstack((img,equ)) #stacking images side-by-side
    cv2.imwrite('res.png',res)

    return

But I really would like to do this with out using predefined functions for learning purposes.

So I tried following:

 def stratch_contrast(img, darkestValue, whitestValue):

     newImgPixelList = []

     h = img.shape[0] #number of pixels in the hight
     w = img.shape[1] #number of piexels in the weight

     darkestValueStratch = 256 #opposite so it can get darker while loop
     whitestValueStratch = 0 #opposite so it can get lighter while loop

     for y in range(0, w):
         for x in range(0, h):
              newImg[x][y] = (img[x][y]-darkestValue)*256/(whitestValue-darkestValue)
              pxStratch = newImg[x][y]
              newImgPixelList.append(pxStratch)
              if darkestValueStratch > pxStratch:
                  darkestValueStratch = pxStratch
              if whitestValueStratch < pxStratch:
                  whitestValueStratch = pxStratch   

      return newImgPixelList, darkestValueStratch, whitestValueStratch

But when I am then calling my plotting function, like so:

plot(newImgPixelList, int(darkestValueStratch), int(whitestValueStratch))

The plotted histogram is not equalized at all. It looks nearly exactly the same, like my not equalized histogram, so something must be wrong. I would be very gratefull if someone could help me with that!

My complete code:

import matplotlib.pyplot as plt
import numpy as np
import cv2
np.seterr(over='ignore')

name = 'puppy.jpg'

img = cv2.imread(name, cv2.IMREAD_GRAYSCALE) #import image
newImg = np.zeros((img.shape))

def get_histo_scope(img):

    imgPixelList = [] #array which later can save the pixel values of the image

    h = img.shape[0] #number of pixels in the hight
    w = img.shape[1] #number of piexels in the weight

    darkestValue = 256 #opposite so it can get darker while loop
    whitestValue = 0 #opposite so it can get lighter while loop

    for y in range(0, w):
        for x in range(0, h):       
            px = img[x][y] #reads the pixel which is a npndarray [][][]
            imgPixelList.append(px) #saves the pixel data of every pixel we loop so we can use it later to plot the histogram
            if darkestValue > px: #identifies the darkest pixel value
                darkestValue = px
            if whitestValue < px: #identifies the whitest pixel value
                whitestValue = px 

    return darkestValue, whitestValue, imgPixelList

def plot(imgPixelList, darkestValue, whitestValue):
    values = range(darkestValue, whitestValue, 1) #creates and array with all data from whitesValue to darkestValue
    bin_edges = values

    plt.hist(imgPixelList, bins=bin_edges, color='black')
    plt.xlabel('Color Values')
    plt.ylabel('Number of Poxels')
    plt.show()  

    return     

def stratch_contrast(img, darkestValue, whitestValue): 

    #hist,bins = np.histogram(img.flatten(),256,[0,256])
    #cdf = hist.cumsum()
    #cdf_normalized = cdf * hist.max()/ cdf.max()

    #Comment out to remove Equalization 
    #cdf_m = np.ma.masked_equal(cdf,0)
    #cdf_m = (cdf_m - cdf_m.min())*255/(cdf_m.max()-cdf_m.min())
    #cdf = np.ma.filled(cdf_m,0).astype('uint8')
    #img = cdf[img]

    #plt.hist(img.flatten(),256,[0,256], color = 'black')
    #plt.xlim([0,256])
    #plt.legend(('cdf','histogram'), loc = 'upper left')
    #plt.show()

    #img = cv2.imread(name,0)
    #equ = cv2.equalizeHist(img)
    #res = np.hstack((img,equ)) #stacking images side-by-side
    #cv2.imwrite('res.png',res)

    newImgPixelList = []

    h = img.shape[0] #number of pixels in the hight
    w = img.shape[1] #number of piexels in the weight

    darkestValueStratch = 256 #oposite so it can get darker while loop
    whitestValueStratch = 0 #oposite so it can get lighter while loop

    for y in range(0, w):
       for x in range(0, h):
            newImg[x][y] = (img[x][y]-darkestValue)*256/(whitestValue-darkestValue)
            pxStratch = newImg[x][y]
            newImgPixelList.append(pxStratch)
            if darkestValueStratch > pxStratch: #identifies the darkest pixel value
                darkestValueStratch = pxStratch
            if whitestValueStratch < pxStratch: #identifies the whitest pixel value
                whitestValueStratch = pxStratch   

    return newImgPixelList, darkestValueStratch, whitestValueStratch

darkestValue, whitestValue, imgPixelList = get_histo_scope(img) #get scope and pixel values from the img data

plot(imgPixelList, darkestValue, whitestValue) #plot the collected pixel values

newImgPixelList, darkestValueStratch, whitestValueStratch = stratch_contrast(img, darkestValue, whitestValue)

plot(newImgPixelList, int(darkestValueStratch), int(whitestValueStratch))
Leonard Michalas
  • 1,130
  • 1
  • 11
  • 26
  • There's a problem in `stratch_contrast`. Where do the `darkestValue` and `whitestValue` used in the formula come from ? Are they the same as `darkestValueStratch` and `whitestValueStratch` ? – Sunreef May 15 '18 at 06:02
  • No, they are not the same, darkestValue and whitestValue are coming from get_histo_scope(img). There I am looping through the pixels and keep track of min and max. Btw, I am doing this for greyscale pictures only atm. – Leonard Michalas May 15 '18 at 06:25
  • The first function applies `cv.equalizeHist`, which applies histogram equalization. The second function applies a linear mapping, and clips values outside of the range. If you think these two operations are anything but tangentially related, you need to go back to your text books. – Cris Luengo May 18 '18 at 16:50
  • This question has the answer you are looking for: https://stackoverflow.com/q/28518684/7328782 (I can't vote to close as duplicate because of the bounty.) – Cris Luengo May 18 '18 at 16:50
  • This other question has an answer explaining the algorithm a bit: https://stackoverflow.com/q/11341440/7328782 – Cris Luengo May 18 '18 at 16:51
  • thanks for you answer, Chris. But unfortunatly I dont want to use the predefined functions as I said in my question. I want to use my own function (s. stratch_contrast) and I want to understand why this doesnt work. So no, my question isnt a duplicate. – Leonard Michalas May 18 '18 at 17:25
  • That answer shows how to implement histogram equalization using `histogram`, `cumsum` and `interp`. Those are pretty basic functions, and pretty easy to implement yourself. I would suggest you start there. Marking as duplicate is meant to be helpful, not dismissive. I'm giving you an answer, after all. -- BTW: Please type `@CrisLuengo` (or whatever username) when replying to a comment, to make sure your reply is read. – Cris Luengo May 18 '18 at 17:51
  • @CrisLuengo Thanks, but when you look at my code you see that I already have this implemented (but commented it out with #), and I said in my question that this solution already works, but that I want to do this completly on my own. So the post your linked early does nothing have to do with my problem.. My problem is that the code I wrote my self, the code with the two for loops is not working... and it should working, because logically it shoudl be correct :/ – Leonard Michalas May 18 '18 at 17:55
  • @CrisLuengo In my code I am looping through each individual pixel of the array and apply a calculation function on the value of this pixel. After that I am plotting the new generated array with the modified values in it. Normally this should do the job, but unfortunatly nothing changes. The new array is identical to the old one before the calculation function... And my problem is that I dont understand why.. and not that I dont know how to use functions someone else wrote. – Leonard Michalas May 18 '18 at 17:58
  • If `darkestValue==0` and `whitestValue==255` then your loop doesn't change anything. If these values are different, your loop will only stretch the intensity axis, which you would see as a change of the x-axis of the histogram, but not a change of its shape. As I said, histogram equalization is something different. It is a non-linear mapping. You use the cumulative histogram as a lookup table. Look at the links I gave you, you will learn how equalization works. – Cris Luengo May 18 '18 at 20:22

1 Answers1

3

I think you misunderstood the contrast stretching algorithm.

The goal of the algorithm is to linearly scale the values of the pixels so that your image uses the full dynamic range available, i.e min(I) = 0 and max(I) = 255.

For that, you have to find the current min(I) and max(I) before looping through the pixels and scaling them. Just loop through the whole image while keeping track of the maximum and minimum value for each channel (3 channels for an RGB image). Then use those values to scale your pixels using the formula newValue = 255 * (oldValue - minimum) / (maximum - minimum). Treat each of the R, G and B channels independently.

Sunreef
  • 4,452
  • 21
  • 33
  • That is what I am doing in get_histo_scope(). I am just keeping track of darkestValueStratch and whitestValueStratch again. But I am using darkestValue and whitestValue, which I got in get_histo_scope to perform the calculation (img[x][y]-darkestValue)*256/(whitestValue-darkestValue)) – Leonard Michalas May 15 '18 at 06:22
  • But you're not passing the values to the stratch_contrast function. And you're not handling channels independently. – Sunreef May 15 '18 at 06:27
  • Even when I pass the value the error still exist. I just edited the code. Since I am only handling greyscale pictures for now, I dont have to handle different channels right? – Leonard Michalas May 15 '18 at 06:29
  • any more ideas what I can try? – Leonard Michalas May 15 '18 at 16:23