Search code examples
pythonopencvcolorsadjustment

OpenCV RGB single channel color regulation


Input A image is a full RGB image, Output B image is a the same image but with "adjusted" R values

I need to rescale the RGB value to be between 128 and 255, so that minor values than 128 are scaled to an upper value.

RMAX = 127

img = cv2.imread(filename)         # load img
blue, green, red = cv2.split(img)  # get single color

red = red*RMAX/255+128             # scale the color as I need 

but this keep getting a wrong value:

if red value is 255 = 255*127/255+128 should output 255 but return 128

Why this happen?

EDIT:

The color values don't need to be recalculated every time, Would it be better to prepare an array at the start with the range of values, then replace the current value with the one from the array?

ValuesForRed = [0]*255

for i in range(0,255):
    ValuesForRed[i]=i*127 / 255 + 128

how to replace the values in the array is now the problem...

should replace the corresponding value with the corresponding index

i.e. red[45]= 0 
     ValuesForRed[0] = 128
     red[45]= 128

started new question at Python Opencv cv2.LUT() how to use


Solution

  • This happens because red is unsigned char, which is a number in 0 to 255 range. However, you expect red to behave like integer.

    So given that

    red = 255
    red = red*127/255 + 128
    

    When the program multiplies red*127 the result will overflow because its value will be greater than 255, and so the answer will be 0 (because 255*127 modulo 255 = 0). Hence you get red = red*127/255 + 128 = (255*127 modulo 255) / 255 + 128 = 0 /255 + 128 = 128

    To fix this, you can cast red to float when you do arithmetic operations on it, for example:

    red = (float)red * 127 / 255
    

    Edit As pointed out by William red is a cv::Mat of type CV_8U. You can convert the image to CV_32F type for calculations and then convert it back. For example (this is C++ code):

     Mat red_float;   
     red.convertTo(red_float,CV_32F);
     red_float = red_float*RMAX/255+128;
     red_float.convertTo(red,CV_8U);