OpenCV RGB single channel color regulation
Asked Answered
D

2

5

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

Dachy answered 13/1, 2014 at 14:45 Comment(1)
You can use the suggestion from my second answer. It shows step-by-step method on how to solve the problem in C++. The approach in Python will be similar. The main idea is to use inRange() function (available for Python OpenCV) to obtain the mask for the image that specifies the pixels, which value is between 129 and 255 (inclusive). See the second answer for more details.Supersaturated
S
5

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);
Supersaturated answered 13/1, 2014 at 14:58 Comment(5)
Agree. Also an auxiliary cv::Mat with type CV_32FC3 could be used for calculations and then converted to uchar with convertTo(,CV_8UC3).Antimalarial
I'm using Python 2.7, I've tried with int(red * 127 / 255 + 128) but got: TypeError: only length-1 arrays can be converted to Python scalars, so I've tried with abs(red * 127 / 255 + 128) but it doesn't do the conversion still got the 128.Dachy
@Dachy red is a matrix. So you'll need to convert the matrix to CV_32F type as described in the second part of the answer (for C++). Since you are using Python, this answer might be of help.Supersaturated
reds = np.int16(red) red= reds* 127 / 255 + 128 make the calculation correctly but when I do a merge of the new colors, I get:cv2.error: /build/opencv-Ai8DTy/opencv-2.4.6.1+dfsg/modules/core/src/convert.cpp:286: error: (-215) mv[i].size == mv[0].size && mv[i].depth() == depth in function mergeDachy
@Dachy Your question was "why this happen?". Which I answered. Please tell me in more detail what you are trying to achieve, or edit your question so I can help you better. You can also post it as a new question.Supersaturated
S
2

The other question that OP has is "how best to solve this problem?". This is how I would approach it. This is C++ code but you should be able to translate it to Python easily. This approach is fast and there is no need to convert matrices to CV_32F type.

  • Split input image into channels

    Mat input_image; //input image
    vector<Mat> split_image(3);
    split(input_image, split_image);
    Mat red = split_image[2];
    
  • Obtain mask_red, such that a location in mask_red is set to 255 if the corresponding location in red is between 129 and 255 (inclusive bounds), otherwise it is set to 0. This can be achieved with inRange() function.

    Mat mask_red;
    inRange(red, Scalar(129), Scalar(255), mask_red);
    
  • Now apply setTo() function to red to set all masked pixels to 255.

    red.setTo(Scalar(255), mask_red);
    
  • Merge the channels to form the final image.

    Mat output_image;   // output image
    merge(split_image, output_image);
    
Supersaturated answered 14/1, 2014 at 14:54 Comment(1)
actually I need to perform a proportional scaling, inRange seems to return 255 if it's in the range, something like a true if verified, not a proportional scaling, if 0:128, if 3:129 etc...Dachy

© 2022 - 2024 — McMap. All rights reserved.