Reputation: 573
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
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