4
votes

i am currently developing an application that has to process scanned forms. One of the tasks of my application is to determine which kind of form is scanned. There are 3 possible types of forms with a unique background color to identify each kind. The 3 colors that are possible are red/pink, green and blue. The problem i am having is, that my attempts fail to distinguish between the green and blue forms. Here are links to the green and blue sample files:

http://dl.dropbox.com/u/686228/Image0037.JPG

http://dl.dropbox.com/u/686228/Image0038.JPG

I am using C# .net Application and ImageMagick for some tasks i need to perform.

Currently i am getting color reduced histogram of my scanned form and try to determine which colors are in the form. But my app can't rely distinguish the green and blue ones.

Any advise or maybe a smarter approach would be gladly appreciated.

Thanks,

Erik

3
The spam alarm in my mind went off when I saw "Viagra".blizpasta
The green form really is quite green, and the blue form quite blue, even on my maladjusted monitor. :)Will A
How is anybody supposed to guess what's wrong with your code from an image file?Hans Passant
Include code man... you wont get far without it... Where in Munich are you?Anthony Graglia
any sample code with final solution ?Kiquenet

3 Answers

9
votes

I found this rather interesting and dug into it a little deeper.

The code to get the average color of a bitmap found at How to calculate the average rgb color values of a bitmap had problems like some invalid casts and red/blue channels swapped. Here is a fixed version:

private System.Drawing.Color CalculateAverageColor(Bitmap bm)
{
    int width = bm.Width;
    int height = bm.Height;
    int red = 0;
    int green = 0;
    int blue = 0;
    int minDiversion = 15; // drop pixels that do not differ by at least minDiversion between color values (white, gray or black)
    int dropped = 0; // keep track of dropped pixels
    long[] totals = new long[] { 0, 0, 0 };
    int bppModifier = bm.PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb ? 3 : 4; // cutting corners, will fail on anything else but 32 and 24 bit images

    BitmapData srcData = bm.LockBits(new System.Drawing.Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.ReadOnly, bm.PixelFormat);
    int stride = srcData.Stride;
    IntPtr Scan0 = srcData.Scan0;

    unsafe
    {
        byte* p = (byte*)(void*)Scan0;

        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                int idx = (y * stride) + x * bppModifier;
                red = p[idx + 2];
                green = p[idx + 1];
                blue = p[idx];
                if (Math.Abs(red - green) > minDiversion || Math.Abs(red - blue) > minDiversion || Math.Abs(green - blue) > minDiversion)
                {
                    totals[2] += red;
                    totals[1] += green;
                    totals[0] += blue;
                }
                else
                {
                    dropped++;
                }
            }
        }
    }

    int count = width * height - dropped;
    int avgR = (int)(totals[2] / count);
    int avgG = (int)(totals[1] / count);
    int avgB = (int)(totals[0] / count);

    return System.Drawing.Color.FromArgb(avgR, avgG, avgB);
}

Running this function on your input images, however, returned some indistinguishable grayish color for both of them, as already anticipated by Will A in the comments, which is why i'm dropping any colors from the calculation that do not have a difference of at least 15 between R, G and B.

The interesting thing is that the supposedly blue prescription scan averages equal values for G and B (R: 214, G: 237, B: 237). However the green prescription scan resulted in a big difference (18) between the values for G and B (R: 202, G: 232, B: 214) so that might be what you should be looking into. Ex:

if (color.G - color.B > 15) { form.Type = FormTypes.GreenForm }
3
votes

Workflow outline:

  • Convert Image into HSL/HSV colorspace
  • Build histogram of the H channel
  • The histogram should show a clear peak (for your samples at least) in the blue/green channel

If that's not distinctive enough, you can weight the histogram votes by something to reduce the effect of the white areas (e.g. in the HSV colorspace, weight by S).

0
votes

I haven't tried this out, but how about resizing the image to 1x1 pixel (which should "average" out all the pixels) and then check the hue of that pixel to see if it closest to red, blue or green.

EDIT

I don't have ImageMagik installed, so I hacked this with GetThumbnailImage:

private static bool ThumbnailCallback()
{
    return false;
}

static void Main(string[] args)
{
    var blueImage = Image.FromFile("blue.jpg").GetThumbnailImage(1, 1, new Image.GetThumbnailImageAbort(ThumbnailCallback), IntPtr.Zero);
    var blueBitmap = new Bitmap(blueImage);
    var blueHue = blueBitmap.GetPixel(0, 0).GetHue();

    var greenImage = Image.FromFile("green.jpg").GetThumbnailImage(1, 1, new Image.GetThumbnailImageAbort(ThumbnailCallback), IntPtr.Zero);
    var greenBitmap = new Bitmap(greenImage);
    var greenHue = greenBitmap.GetPixel(0, 0).GetHue();
}

Using your images I got a blueHue value of 169.0909 (Where true blue would be 180), and greenHue equal to 140 (pure green is 120, cyan is 150).

Red forms should be somewhere near 0 or 360.

I know you've already found an answer - just thought I'd give you an alternative.