3

I am using this article to solve captchas. It works by removing the background from the image using AForge, and then applying Tesseract OCR to the resulting cleaned image.

The problem is, it currently relies on the letters being black, and since each captcha has a different text color, I need to either pass the color to the image cleaner, or change the color of the letters to black. To do either one, I need to know what the existing color of the letters is.

How might I go about identifying the color of the letters?

Image with letters in it

Image with letters in it

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
  • Not foolproof, but your letters look to all be the same color; they are *probably* either the top or next to top most frequent color in the picture. You could just turn the top color black, check the results, and if it fails try the next one or two. – BradleyDotNET Jan 19 '17 at 00:09
  • Well, AForge has functions for blob detection, so you can probably just use those to get the letter blobs and calculate the color from there. – Abion47 Jan 19 '17 at 00:11
  • **NOTE:** If you wish to download the MSDN project and try it out, make sure you unzip it to a folder without a `#` character in the folder name, as that `#` will cause the build to fail. – Robert Harvey Jan 19 '17 at 00:31
  • The only way you could detect the colour would be indirectly (since if you knew the colour of the letters, you would know the letters themselves). My first instinct would be to convert it to greyscale and then process it twice: Once with the original greyscale and once with the greyscaled inverted if the initial attempt fails. – Rob Jan 19 '17 at 00:36
  • IMHO, I can help you better if you try and create a [Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve) I am a strong believer in ***code clarity and simplicity*** – Kiquenet Mar 03 '17 at 22:07
  • @Kiquenet: This isn't a code troubleshooting question. – Robert Harvey Mar 03 '17 at 23:19

2 Answers2

3

Using the answer by @Robert Harvey♦ I went and developed the same code using LockBits and unsafe methods to improve it's speed. You must compile with the "Allow unsafe code" flag on. Note that the order of pixels returned from the image is in the bgr not rgb format and I am locking the bitmap using a format of Format24bppRgb to force it to use 3 bytes per colour.

public unsafe Color GetTextColour(Bitmap bitmap)
{
    BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
    try
    {
        const int bytesPerPixel = 3;
        const int red = 2;
        const int green = 1;

        int halfHeight = bitmap.Height / 2;

        byte* row = (byte*)_bitmapData.Scan0 + (halfHeight * _bitmapData.Stride);

        Color startingColour = Color.FromArgb(row[red], row[green], row[0]);
        for (int wi = bytesPerPixel, wc = _bitmapData.Width * bytesPerPixel; wi < wc; wi += bytesPerPixel)
        {
            Color thisColour = Color.FromArgb(row[wi + red], row[wi + green], row[wi]);
            if (thisColour != startingColour)
            {
                return thisColour;
            }
        }

        return Color.Empty; //Or some other default value
    }
    finally
    {
        bitmap.UnlockBits(bitmapData);
    }
}
Community
  • 1
  • 1
TheLethalCoder
  • 6,668
  • 6
  • 34
  • 69
2

The solution to this particular problem turned out to be relatively simple. All I had to do is get the color of the edge pixel halfway down the left side of the image, scan pixels to the right until the color changes, and that's the color of the first letter.

public Color GetTextColor(Bitmap bitmap)
{
    var y = bitmap.Height/2;
    var startingColor = bitmap.GetPixel(0, y);

    for (int x = 1; x < bitmap.Width; x++)
    {
        var thisColor = bitmap.GetPixel(x, y);
        if (thisColor != startingColor)
            return thisColor;
    }
    return null;
}
Robert Harvey
  • 178,213
  • 47
  • 333
  • 501