tags:

views:

60

answers:

2

I need to color balance an image that has an 18% gray card in it. The user loads this image into the application, then clicks on the gray card. From here is where I need help with an algorithm to color balance the image. I've found a few articles that mention doing a matrix transform, which I've tried, but without success (the image washes out or turns one color or another). The code I have now is:

        int sampleSize = 20;  // The square around the user's click on the gray card
        int rVal = 0, gVal = 0, bVal = 0;
        int count = 0;
        for (int x = 0; x < sampleSize - 1; x++)
        {
            for (int y = 0; y < sampleSize - 1; y++)
            {
                System.Drawing.Color c = grayCardArea.GetPixel(x, y);
                if (c.R > 0)
                {
                    rVal += c.R;
                    gVal += c.G;
                    bVal += c.B;
                    rs.Add(c.R);
                    count++;
                }
            }
        }
        grayCardGraphics.Dispose();

        int rAvg = 0, gAvg = 0, bAvg = 0;
        rAvg = (int)Math.Round((decimal)rVal / (count));
        gAvg = (int)Math.Round((decimal)gVal / (count));
        bAvg = (int)Math.Round((decimal)bVal / (count));

        // 117 is a value I found online for the neutral gray color of the gray card
        float rDiff = (117 / (float)rAvg);
        float gDiff = (117 / (float)gAvg);
        float bDiff = (117 / (float)bAvg);

        float[][] ptsArray = 
            { 
            new float[] {rDiff, 0, 0, 0, 0},
            new float[] {0, gDiff, 0, 0, 0},
            new float[] {0, 0, bDiff, 0, 0},
            new float[] {0, 0, 0, 1, 0},
            new float[] {0, 0, 0, .0f, 1}
            };
        // Create a ColorMatrix
        ColorMatrix clrMatrix = new ColorMatrix(ptsArray);

        // Create ImageAttributes
        ImageAttributes imgAttribs = new ImageAttributes();
        // Set color matrix
        imgAttribs.SetColorMatrix(clrMatrix, ColorMatrixFlag.Default, ColorAdjustType.Default);
        // Draw image with ImageAttributes
        outputImageGraphics.DrawImage(srcImage, new System.Drawing.Rectangle(0, 0, srcImage.Width, srcImage.Height),
            0, 0, srcImage.Width, srcImage.Height,
            GraphicsUnit.Pixel, imgAttribs);

Viewing a saved copy of the outputImage shows an odd transformation of the image.

Any help is greatly appreciated!

+1  A: 

My company, Atalasoft, has a free .NET Imaging SDK, with a class called LevelsCommand, that I think will do what you want.

http://atalasoft.com/photofree

Code is something like

AtalaImage img = new AtalaImage("filename");
LevelsCommand cmd = new LevelsCommand(/* ... */ ); //  need to pass in leveling colors
img = cmd.Apply(img).Image;
img.Save("filename-new", new PngEncoder(), null); // or could be new JpegEncoder() or something else

You should use proper extensions on filenames to indicate the format.

Lou Franco
+1  A: 

Your first assumption appears to be that the image was properly exposed in the first place and that making the gray card read 117, 117, 117 will solve the problem. My advice is to leave the exposure alone and adjust just the color cast. You might find a different color model useful -- e.g., HSL. The saturation of a gray card should always be zero.

Alternatively, I have an example gray target reading 71, 72, 60. This is a bit warm. It would stand to reason that a more correct reading would be 67,67,67 or (R+G+B)/3. Because the image is a bit underexposed, I left it that way, but achieved a true neutral without altering the density of the image.

I hope this provides some help along your path toward getting the color right.

Steve Ross