4

I have split an image into 3 separate color channels - one blue, one green, and one red. I would like to normalize each of these channels by the image's intensity, where intensity = (red + blue + green)/3. To be clear, I am trying to make an image that is composed of one of the three color channels, divided by the image's intensity, where the intensity is described by the equation above. I am new to OpenCV and I do not think I am doing this correctly; when the images are displayed, all the pixels appear to be black. I am new to OpenCV (I have worked through the tutorials that come with the documentation, but that is it) - any advice about how to go about this normalization would be extremely helpful.

Thanks!

Here is my attempt:

int main(int argc, char** argv){
Mat sourceImage, I;
const char* redWindow = "Red Color Channel";
const char* greenWindow = "Green Color Channel";
const char* blueWindow = "Blue Color Channel";


if(argc != 2)
{
  cout << "Incorrect number of arguments" << endl;
}
/* Load the image */
sourceImage = imread(argv[1], 1);
if(!sourceImage.data)
{
  cout << "Image failed to load" << endl;
}

/* First, we have to allocate the new channels */
Mat r(sourceImage.rows, sourceImage.cols, CV_8UC1);
Mat b(sourceImage.rows, sourceImage.cols, CV_8UC1);
Mat g(sourceImage.rows, sourceImage.cols, CV_8UC1);

/* Now we put these into a matrix */
Mat out[] = {b, g, r};

/* Split the image into the three color channels */
split(sourceImage, out);

/* I = (r + b + g)/3  */
add(b, g, I);
add(I, r, I);
I = I/3;

Mat red = r/I;
Mat blue = b/I;
Mat green = g/I;

/* Create the windows */
namedWindow(blueWindow, 0);
namedWindow(greenWindow, 0);
namedWindow(redWindow, 0);

/* Show the images */
imshow(blueWindow, blue);
imshow(greenWindow, green);
imshow(redWindow, red);

waitKey(0);
return 0;
}
Nathan
  • 61
  • 1
  • 1
  • 8
  • 1
    1. you don't need to pre-alloc the channels used in split() (they will get realloced/overwritten anyway) 2. something like r/I suffers from integer division 3. don#t do that in rgb space, convert to hsv, split,manipulate h only, merge, convert back to rgb – berak Jun 25 '14 at 19:40
  • Thanks! Ok, I got rid of the pre-allocation, and I used the divide function instead of '/' (is this any better?). Also, can you explain a little bit more what you mean by manipulate h only? – Nathan Jun 25 '14 at 20:00
  • divide is the same as /. and sorry for my sloppyness, - i should have asked before, what youre trying to achieve by that normalization ? – berak Jun 25 '14 at 20:08
  • No worries - I am trying to implement a basic salient object detection algorithm. As such, a salient region would contain the least probable value in either of the r, g, or b color channels – Nathan Jun 25 '14 at 20:18
  • So normalizing each of the channels means that a certain area would appear brightest on one of the normalized channels. (theoretically, at least as far as I understand it) – Nathan Jun 25 '14 at 20:19
  • ahh, see, source of misunderstanding, - i was more thinking in terms of equalization. but again, if you want to divide, you probably need to convert the channels to floating point first. else you run into problems like: 7/12 (that's 0 with integers/uchar) – berak Jun 25 '14 at 20:24
  • Right, thanks, another good point! – Nathan Jun 25 '14 at 20:37
  • Is there any way you could explain a little more what you mean by manipulate h only? – Nathan Jun 25 '14 at 20:37
  • again, that was more refering to an equalization problem, and i was even wrong there, if you convert it to hsv (or lab), you can normalize *v* only to get an equalized luminosity/intensity. again, probably not exactly, what you're after. – berak Jun 25 '14 at 20:42
  • that's what i confused your problem with: http://stackoverflow.com/questions/24341114/simple-illumination-correction-in-images-opencv-c/24341809#24341809 – berak Jun 25 '14 at 20:49

1 Answers1

5

Once you divide by the intensity the pixel values will be in the range [0, 1], except since they are integers they will be 0 or 1. For a display image white is 255 and 0 is black, so this is why everything appears black to you. You need to use floating point to get an accurate result, and you need to scale the result by 255 to see it. Doing that results in this (which I an not sure is particularly useful)

enter image description here (Image source: BSDS500)

And here is the code that generated it:

#include <opencv2/core/core.hpp>
#include <vector>   
int main(int argc, char** argv)
{
    // READ RGB color image and convert it to Lab
    cv::Mat bgr_image = cv::imread("208001.jpg"); // BSDS500 mushroom
    cv::imshow("original image", bgr_image);
    cv::Mat bgr_image_f;
    bgr_image.convertTo(bgr_image_f, CV_32FC3);

    // Extract the color planes and calculate I = (r + g + b) / 3
    std::vector<cv::Mat> planes(3);
    cv::split(bgr_image_f, planes); 

    cv::Mat intensity_f((planes[0] + planes[1] + planes[2]) / 3.0f);
    cv::Mat intensity;
    intensity_f.convertTo(intensity, CV_8UC1);
    cv::imshow("intensity", intensity);

    //void divide(InputArray src1, InputArray src2, OutputArray dst, double scale=1, int dtype=-1)
    cv::Mat b_normalized_f;
    cv::divide(planes[0], intensity_f, b_normalized_f);
    cv::Mat b_normalized;
    b_normalized_f.convertTo(b_normalized, CV_8UC1, 255.0);
    cv::imshow("b_normalized", b_normalized);

    cv::Mat g_normalized_f;
    cv::divide(planes[1], intensity_f, g_normalized_f);
    cv::Mat g_normalized;
    g_normalized_f.convertTo(g_normalized, CV_8UC1, 255.0);
    cv::imshow("g_normalized", g_normalized);

    cv::Mat r_normalized_f;
    cv::divide(planes[2], intensity_f, r_normalized_f);
    cv::Mat r_normalized;
    r_normalized_f.convertTo(r_normalized, CV_8UC1, 255.0);
    cv::imshow("r_normalized", r_normalized);
    cv::waitKey();
}
Bull
  • 11,771
  • 9
  • 42
  • 53
  • I realize this is an old answer, but I want to add this in case others encounter it. There's something a little squirrely about your normalization. Because you divide the intensity by 3, a pixel with initial value (255,0,0) would have a b_normalized_f value of (3,0,0). You probably want to leave the intensity as the sum rather than then normalized value, so that the normalized channels actually run from 0 to 1. Incidentally, that appears to be why your channels tend to clip in the CV_8UC1 displays. – jranalli Mar 08 '18 at 16:51
  • 1
    @jranalli Thanks for pointing this out. What you say makes sense, but the answer just followed what the OP specified. – Bull Mar 09 '18 at 13:34