## What is normalized RGB?

At times, you want to get rid of distortions caused by lights and shadows in an image. Normalizing the RGB values of an image can at times be a simple and effective way of achieving this.

When normalizing the RGB values of an image, you divide each pixel’s value by the sum of the pixel’s value over all channels. So if you have a pixel with intensitied R, G, and B in the respective channels… its normalized values will be R/S, G/S and B/S (where, S=R+G+B).

Here’s an example (I tried this out when working on the soccer bot I made):

The upper image is the original shot taken from a camera. The lower image is its normalized version. It might not look pretty, but take note of some key changes in the image:

- The shadows are the white edges have vanished
- The black and white circles have become indistinguishable
- The entire goal posts are not one solid colour.

Now this might not be the best picture to showoff normalized RGB. But it gets the point through. (If you find a better example, please let me know!!).

## Writing code for this

We’ll write a short function that will convert a given RGB image into a normalized RGB image. We begin by writing the function definition:

IplImage* NormalizeImage(IplImage* theimg) { |

Now we create 3 more images. Each to hold the 3 channels of theimg.

IplImage* redchannel = cvCreateImage(cvGetSize(theimg), 8, 1); IplImage* greenchannel = cvCreateImage(cvGetSize(theimg), 8, 1); IplImage* bluechannel = cvCreateImage(cvGetSize(theimg), 8, 1); |

And then, we create 4 images: one to hold the final normalized image and 3 to temporarily hold the channels of the normalized image:

IplImage* redavg = cvCreateImage(cvGetSize(theimg), 8, 1); IplImage* greenavg = cvCreateImage(cvGetSize(theimg), 8, 1); IplImage* blueavg= cvCreateImage(cvGetSize(theimg), 8, 1); IplImage* imgavg = cvCreateImage(cvGetSize(theimg), 8, 3); |

Next, we copy the 3 channels of the original image into redchannel, greenchannel and bluechannel.

cvSplit(theimg, bluechannel, greenchannel, redchannel, NULL); |

Next, we setup loops to iterate through each pixel of the image…

for(int x=0;x<theimg->width;x++) { for(int y=0;y<theimg->height;y++) { |

…and find out the R, G and B values at this pixel:

int redValue = cvGetReal2D(redchannel, y, x); int greenValue = cvGetReal2D(greenchannel, y, x); int blueValue = cvGetReal2D(bluechannel, y, x); |

Then, we calculate S = R+G+B:

double sum = redValue+greenValue+blueValue; |

And finally, we set the normalized values into the 3 channel images we created for the final normalized image:

cvSetReal2D(redavg, y, x, redValue/sum*255); cvSetReal2D(greenavg, y, x, greenValue/sum*255); cvSetReal2D(blueavg, y, x, blueValue/sum*255); } } |

After all this looping, we’ll have the final channels of the normalized image. We merge these into the final image:

cvMerge(blueavg, greenavg, redavg, NULL, imgavg); |

Once all of this is done, we erase the temporary images we had created, and return the result:

cvReleaseImage(&redchannel); cvReleaseImage(&greenchannel); cvReleaseImage(&bluechannel); cvReleaseImage(&redavg); cvReleaseImage(&greenavg); cvReleaseImage(&blueavg); return imgavg; } |

Using the function we just created is simple. You give it an image in the RGB colour space. And it returns a new image in the normalized RGB form.

## You’re losing information in this tranformation!

The normalized image can be represented using only 2 bytes per pixel (as opposed to 3 bytes per pixel in RGB). Here’s how:

R’ = R/(R+G+B)

G’ = G/(R+G+B)

B’ = B/(R+G+B)

But you can represent B’ like this as well:

B’ = 1-R’-G’

So ultimately, all you need to store is the value of R’ and G’. You can calculate the B’ component from the other two. Physically, its this loss of information that “removes” the lighting information from the image.

## Conclusion

Hopefully this article helped add a new tool to your arsenal of techniques and algorithms. If you have any doubt, or suggestions or even criticism, do leave me a message!

## 14 Comments

When you do this:

why do you multiple by 255?

The value of redValue/sum is a decimal number between 0-1. The 255 converts it into a number in the range 0-255.

I can’t even say how much i thank you! I knew i need somehow normalize RGB vectors but i didn’t know how. This is exactly what i was looking for. Even your example with robot soccer is precisely why i need it for. Tested and works just great!

Thanks and keep publishing

Glad you liked it! Are you working on some robosoccer bots?

Yes, I’m participating on a robbot soccer project where i am working on image analysis

Ah! Nice! Is this some competition?

Not yet, we are just building team

thank you very much for the code, it helped me a lot, i already had the algorithm but i looked at google to see if someone had already implemented it, and your implementation was quite elegant, i’ll be using a slightly modified version of yours.

Thanks again, and congratulations for the blog!

Hi, the image normalization you have looks brilliant. Can you do the equivalent in MATLAB? I need to do this and have tried a way here… (Scroll to the bottom)

http://www.mathworks.com/matlabcentral/newsreader/view_thread/171190#815364

it is ok, but doesn’t seem anyway near as effective as your way.

Hope you can give some advice

Thanks in advance

Pete

Are you sure that this part of the code

does the intended job (and not matrix multiply it)?

Hey, ur code sucks!!!! fit for nothing

mmm’kay

Those calculations will result in a Divide By Zero exception in case the pixel is (0,0,0). To avoid this you can add a value like 0.0001.

Ex,; redN = R/(sum+0.0001)*255

hyy,, i have trying ur code in openCV 2.3,, but i get error when try from ur source ( copy paste), then i have modified it then it run,, but i think the result is not diferent from source,, this is my code,, is it true ??

IplImage* NormalizeImage(IplImage* theimg) {

IplImage* redchannel = cvCreateImage(cvGetSize(theimg), 8, 1);

IplImage* greenchannel = cvCreateImage(cvGetSize(theimg), 8, 1);

IplImage* bluechannel = cvCreateImage(cvGetSize(theimg), 8, 1);

IplImage* redavg = cvCreateImage(cvGetSize(theimg), 8, 1);

IplImage* greenavg = cvCreateImage(cvGetSize(theimg), 8, 1);

IplImage* blueavg= cvCreateImage(cvGetSize(theimg), 8, 1);

IplImage* imgavg = cvCreateImage(cvGetSize(theimg), 8, 3);

cvSplit(theimg, bluechannel, greenchannel, redchannel, NULL);

for (int x = 0; x width; x++) {

for(int y = 0; y height; y++) {

int redValue = cvGetReal2D(redchannel, y, x);

int greenValue = cvGetReal2D(greenchannel, y, x);

int blueValue = cvGetReal2D(bluechannel, y, x);

double sum = redValue + greenValue + blueValue;

cvSetReal2D(redavg, y, x, redValue / sum * 255);

cvSetReal2D(greenavg, y, x, greenValue / sum * 255);

cvSetReal2D(blueavg, y, x, blueValue / sum * 255);

}

}

cvMerge(blueavg, greenavg, redavg, NULL, imgavg);

cvReleaseImage(&redchannel);

cvReleaseImage(&greenchannel);

cvReleaseImage(&bluechannel);

cvReleaseImage(&redavg);

cvReleaseImage(&greenavg);

cvReleaseImage(&blueavg);

return imgavg;

}