I am curious about an image processing algorithm used in this code pulled from this github repo. I have posted an explanation of the code below.

import numpy as np
import cv2
import sys

def calc_sloop_change(histo, mode, tolerance):
    sloop = 0
    for i in range(0, len(histo)):
       if histo[i] > max(1, tolerance):
          sloop = i
          return sloop
          sloop = i

def process(inpath, outpath, tolerance):
    original_image = cv2.imread(inpath)
    tolerance = int(tolerance) * 0.01

    #Get properties
    width, height, channels = original_image.shape

    color_image = original_image.copy()

    blue_hist = cv2.calcHist([color_image], [0], None, [256], [0, 256])
    green_hist = cv2.calcHist([color_image], [1], None, [256], [0, 256])
    red_hist = cv2.calcHist([color_image], [2], None, [256], [0, 256])

    blue_mode = blue_hist.max()
    blue_tolerance = np.where(blue_hist == blue_mode)[0][0] * tolerance
    green_mode = green_hist.max()
    green_tolerance = np.where(green_hist == green_mode)[0][0] * tolerance
    red_mode = red_hist.max()
    red_tolerance = np.where(red_hist == red_mode)[0][0] * tolerance

    sloop_blue = calc_sloop_change(blue_hist, blue_mode, blue_tolerance)
    sloop_green = calc_sloop_change(green_hist, green_mode, green_tolerance)
    sloop_red = calc_sloop_change(red_hist, red_mode, red_tolerance)

    gray_image = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY)
    gray_hist = cv2.calcHist([original_image], [0], None, [256], [0, 256])

    largest_gray = gray_hist.max()
    threshold_gray = np.where(gray_hist == largest_gray)[0][0]

    #Red cells
    gray_image = cv2.adaptiveThreshold(gray_image, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 85, 4)

    _, contours, hierarchy = cv2.findContours(gray_image, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

    c2 = [i for i in contours if cv2.boundingRect(i)[3] > 15]
    cv2.drawContours(color_image, c2, -1, (0, 0, 255), 1)

    cp = [cv2.approxPolyDP(i, 0.015 * cv2.arcLength(i, True), True) for i in c2]

    countRedCells = len(c2)

    for c in cp:
       xc, yc, wc, hc = cv2.boundingRect(c)
       cv2.rectangle(color_image, (xc, yc), (xc + wc, yc + hc), (0, 255, 0), 1)

    #Malaria cells
    gray_image = cv2.inRange(original_image, np.array([sloop_blue, sloop_green, sloop_red]), np.array([255, 255, 255]))

    _, contours, hierarchy = cv2.findContours(gray_image, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

    c2 = [i for i in contours if cv2.boundingRect(i)[3] > 8]
    cv2.drawContours(color_image, c2, -1, (0, 0, 0), 1)

    cp = [cv2.approxPolyDP(i, 0.15 * cv2.arcLength(i, True), True) for i in c2]

    countMalaria = len(c2)

    for c in cp:
       xc, yc, wc, hc = cv2.boundingRect(c)
       cv2.rectangle(color_image, (xc, yc), (xc + wc, yc + hc), (0, 0, 0), 1)

    #Write image
    cv2.imwrite(outpath, color_image)

    #Write statistics
    with open(outpath + '.stats', mode='w') as f:
       f.write(str(countRedCells) + '\n')
       f.write(str(countMalaria) + '\n')

The above code looks at images of cells(irregular shapes) and identifies if there are black spots /blobs inside them. Then, it draws contours around the cells and blobs. For example: enter image description here

First, the code is generating 3 histograms, one for each color signal. calcHist returns a histogram(array) of size 1x256. Each index represents pixel intensity levels of a 2D image and the value inside the index represents the number of pixels that have that intensity.

Next, in this array, the where method calculates which index has the greatest value, or in other words, which pixel intensity is most predominant. This value is multiplied by a tolerance percentage, this new value is called XXXX_tolerance

I'll explain what happens with calc_sloop_change with an example below.

Let me illustrate with an example: Let's say my tolerance passed into process() is 50. Let's say blue_hist returns an array [1, 2, 3, 4, 100, 0, ..., 0] and the largest value in this array is 100 at index 4. This indicates that there are a 100 pixels with an intensity of 4 in the gray scale version of the color image when just the blue signal is extracted. In this situation, the function where(blue_hist = blue_mode) will return 4. This value is multiplied by 0.01*tolerance giving us 2.

So, if the value 4 is pixel intensity value then multiplying it by a scalar only gives another pixel intensity value (in our case, (4 * (0.01*50)) = 2. This new pixel intensity is passed into calc_sloop_change(). In this function, compares histo[i] which returns the number of pixels at intensity i with tolerance(which is the pixel value we calculated earlier). So in our case, the first value greater than 2 happens when i=3. So 3 is returned.

This is where I'm confused. Why is this being done? It seems illogical to compare the number of pixels vs pixel intensity. They are not even the same entity. So, why are they using this algorithm? I must add that this code actually performs really well. So something must be right.

Lastly, the three values calculated by calc_sloop_change(), one for each color signal, acts as a lower cutoff to produce a binary image. Anything less than those values(which are actually pixel intensity values) becomes black and everything above those values becomes white.

  • 1
    $\begingroup$ Can you credit the source of this code? Can you explain the relevant part of the algorithm in a concise way that doesn't require understanding the Python language or OpenCV APIs? $\endgroup$ – D.W. Jul 19 '17 at 16:24
  • $\begingroup$ I have credited the source of this code. Also, I think I have explained the relevant part of the algorithm so that it doesn't require understanding of the Python language or OpenCV APIs. Is there anything more particular I should explain? Please let me know. $\endgroup$ – Jonathan Jul 19 '17 at 18:19
  • $\begingroup$ I don't understand the paragraph beginning "Next, the the new tolerance..." What do you mean by "pixel density"? What is the pixel density of an index? Why do you think this is comparing the number of pixels vs pixel intensity? I don't see any such comparison. What comparison is being done? What are the two quantities that are being compared? What are their units? Coding questions are off-topic here, so asking us to interpret/explain code is probably off-topic, but if you can identify the algorithm and describe it concisely without code, the question might be OK here. $\endgroup$ – D.W. Jul 19 '17 at 19:11
  • $\begingroup$ Is it more clear now? I've used an example to explain. $\endgroup$ – Jonathan Jul 19 '17 at 20:01
  • 1
    $\begingroup$ Yes, that's what I've stated up there as well. blue_mode represents which value is the greatest. But the where() command looks for which index(intensity) has that value. $\endgroup$ – Jonathan Jul 19 '17 at 21:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.