Khaled
Khaled

Reputation: 573

Normalize an image returns --->OverflowError: cannot convert float infinity to integer

Below is a python code I've written in order to normalise an image and make it illuminance-invariance. For each pixel of the image, the new pixel value should be (R/sum,G/sum,B/sum), where sum=R+G+B.

import numpy as np
import cv2 


img=cv2.imread(r'C:/Users/kjbaili/.spyder-py3/color_supression_RV/rub00.jpg')


print(img[200,200])
print(img[200,200,0])

def normalized(down):

    norm_img = np.zeros(down.shape, down.dtype)
    

    width,height,channels=down.shape
    
    for y in range(0,height):
        for x in range(0,width):
              sum=down[x,y,0]+down[x,y,1]+down[x,y,2]
             
              b=(down[x,y,0]/ sum)*255
              g=(down[x,y,1]/ sum)*255
              r=(down[x,y,2]/ sum)*255
            
            
              norm_img[x,y,0]= b
              norm_img[x,y,1]= g
              norm_img[x,y,2]= r
              
    return norm_img


image=normalized(img)


cv2.imshow('normalized',image)


cv2.waitKey(0)
cv2.destroyAllWindows()

However, I'm getting the following error:

OverflowError: cannot convert float infinity to integer

Although, I've found similar answered questions related to this here, i couldn't project this to my problem as i don't know which value comes results an infinity.

Would be glad for some help

Thanks in Advance

Upvotes: 0

Views: 1045

Answers (1)

rayryeng
rayryeng

Reputation: 104555

Your access indices are switched. You should be doing down[y,x,0] etc. not down[x,y,0]. However, I suspect that you're not encountering any errors in accessing here because the image is square. In addition, when you add three numbers together with limited precision in comparison to full floating-point precision, your values will overflow. For example, adding 200 + 100 + 50 in unsigned 8-bit integer will result in 350 % 256 = 94. What is probably happening in your infinity result is that either you have completely black pixels so the normalisation results in a divide by 0 error, or the sum of three values overflows to give you a value of 0 to again give you this result.

What you can do is perform a sanity check to make sure that if the sum of the three channels is not equal to 0, then perform the normalisation. In addition, you will want to change the precision so that it can handle higher values after summing.

In other words:

def normalized(down):

    norm_img = np.zeros(down.shape, down.dtype)
    

    width,height,channels=down.shape
    
    for y in range(0,height):
        for x in range(0,width):
              sum=float(down[y,x,0])+float(down[y,x,1])+float(down[y,x,2])  # Change
              
              if sum > 0:  # Change
                  b=(down[y,x,0]/ sum)*255.0   # Change
                  g=(down[y,x,1]/ sum)*255.0
                  r=(down[y,x,2]/ sum)*255.0
            
                  norm_img[y,x,0]= b  # Should cast downwards automatically
                  norm_img[y,x,1]= g
                  norm_img[y,x,2]= r
              
    return norm_img

This of course is very inefficient as you are looping over individual pixels and not taking advantage of the vectorisation that embodies NumPy arrays. Simply put, use numpy.sum and sum along the third dimension, then divide every channel by the corresponding amount:

def normalized(down):
    sum_img = np.sum(down.astype(np.float), axis=2)
    sum_img[sum_img == 0] = 1
    return (255 * (down.astype(np.float) / sum_img[...,None])).astype(down.dtype)

The first line calculates a 2D array where each location sums along the channel dimension to give you the sum of the RGB values per spatial location. I've also promoted the type to floating-point to maintain precision when normalising. Next, the intermediate check on the second line of code ensures that there's no divide by zero error so any pixels that are 0, we set to a sentinel value of 1 so that the division results in a 0 value. After, we take the input image and divide each corresponding RGB pixel by the sum at the corresponding spatial location. Note that I've employed broadcasting so that I've made the 2D sum array into a 3D array with a singleton third channel to allow the broadcasting to work properly. Finally, I multiply by 255 as you've done in your previous version. I also make sure that I cast the final result to the incoming type in the function.

To be a bit cleaner, you can simplify this more by using the keepdims argument of numpy.sum to maintain the singleton dimension after you sum in the third dimension. This way it avoids the manual singleton dimension insertion:

def normalized(down):
    sum_img = np.sum(down.astype(np.float), axis=2, keepdims=True)
    sum_img[sum_img == 0] = 1
    return (255 * (down.astype(np.float) / sum_img)).astype(down.dtype)

Upvotes: 3

Related Questions