简体   繁体   English

如何在C#位图中替换相同颜色的色调变化?

[英]How do i replace tone variations of the same color in a C# Bitmap?

Scope : 适用范围

Hi everyone, i am trying to convert this captcha to a "black and white" (binarized) image, where the characters are white and the whole rest (background,lines,random pictures) are black. 大家好,我正在尝试将此验证码转换为“黑白”(二进制化)图像,其中字符为白色,其余所有字符(背景,线条,随机图片)为黑色。

The link to the captcha can be found here . 验证码的链接可以在这里找到。 Refreshing will give you another captcha. 刷新会给您另一个验证码。

Reason: 原因:

I know most of people think it is wrong to mess with captchas so here i am defending myself. 我知道大多数人都认为弄错验证码是错误的,所以我在这里为自己辩护。 This will be used for knowledge / self challenge only. 这将仅用于知识/自我挑战。 No other use is planned for these images. 这些图像没有其他用途。

Problem: 问题:

After studying these images for a while, i figured out that a good aproach would be to replace the colors : "White and Yellow" to "Color.Black", and every other color should be replaced to "Color.White." 在研究了这些图像一段时间之后,我发现一个很好的方法是将颜色从“白色和黄色”替换为“ Color.Black”,并将所有其他颜色替换为“ Color.White”。

After this, i would just "Invert" the colors, leading me to the output i want. 在此之后,我只需“反转”颜色,即可将其引导至所需的输出。

Code Sample: 代码示例:

In this code, i am trying to replace the color "Black" of every image for a SkyBlue Pixel. 在这段代码中,我试图将每张图片的颜色“黑色”替换为SkyBlue Pixel。

        WebRequests wr = new WebRequests(); // My class to handle WebRequests
        Bitmap bmp;
        string url = "http://www.fazenda.rj.gov.br/projetoCPS/codigoImagem";

        bmp = wr.GetBitmap (url);

        for (int i = 1; i < bmp.Height ; i++)
        {
            for (int j = 1 ; j < bmp.Width ; j++)
            {
                if (bmp.GetPixel(j,i).Equals(Color.Black))
                {
                    bmp.SetPixel(j,i, Color.SkyBlue);
                }
            }
        }

This code do not work at all, i'm not sure why, but no pixel is getting replaced in this example. 该代码根本不起作用,我不确定为什么,但是在此示例中没有像素被替换。

Question: 题:

How can i make this work ? 我该如何工作? What am i missing here ? 我在这里想念什么?

Also, the ideal scenario for me would be to "reduce" the color pallete of this image to "basic" colors, that would make my job here much easier. 另外,对我来说,理想的方案是将该图像的调色板“减少”为“基本”颜色,这将使我的工作更加轻松。

I already tried the AForge Framework , which i am using to reduce colors, but it is not working at all, the result is not the one i expected. 我已经尝试过AForge框架 ,我正在使用它来减少颜色,但是它根本不起作用,结果不是我所期望的。

What can i do here in order to binarize this image correctly ? 为了正确地对该图像进行二值化,我该怎么办?

Thanks in advance, 提前致谢,

Marcello Lins. 马塞洛·林斯(Marcello Lins)。

The reason why your algorithm does not work is that you are looking for an exact match. 您的算法不起作用的原因是您正在寻找完全匹配的内容。 But the black in the image isn't really black due to JPEG compression artifacts; 但是,由于JPEG压缩伪像,图像中的黑色并不是真正的黑色。 in three CAPTCHAS I've loaded there was not even a single black (0, 0, 0) pixel. 在我加载的三个CAPTCHAS中,甚至没有一个黑色(0、0、0)像素。 The closest value was (0, 0, 4) which looks black, but isn't. 最接近的值是(0,0,4),看起来是黑色,但不是。

An approximate approach would be: 一种近似的方法是:

  • Remove all grays (channels differing from each other less than 5%) having a channel average below 220 with gray. 移除通道平均值低于220的所有灰色(通道彼此之间的差异小于5%)。 This kills the thin lightgray lines as well as much of the ringing (JPEG) artifacts against a gray background. 这可以消除浅灰色的线条以及灰色背景上的许多振铃(JPEG)伪影。
  • Replace all pixels where the red, green, or blue channel is more than 25% above both the other two channels with gray. 将红色,绿色或蓝色通道比其他两个通道高25%以上的所有像素替换为灰色。 This takes care of reds, greens and blues as well as most of their ringing artifacts. 这可以照顾到红色,绿色和蓝色以及它们大部分的响声。
  • Run a simple despeckle to remove all nongray pixels surrounded by five grays and by no other pixel of the same colour within 20%. 运行一个简单的去斑点,以去除所有非灰色像素(被五个灰色包围,并且在20%内没有其他相同颜色的像素)。

At the end of this process, the background is all gray, all nongray pixels left are characters. 在此过程结束时,背景均为灰色,剩下的所有非灰色像素均为字符。

Some useful functions: 一些有用的功能:

// Very basic (and CIE-incorrect) check
public static int isGray(Color c)
{
    if (Math.Abs(c.R - c.G) > 5 * 2.55) return 0; // Not gray. R and G too different
    if (Math.Abs(c.R - c.B) > 5 * 2.55) return 0;
    if (Math.Abs(c.G - c.B) > 5 * 2.55) return 0;
    return 1;
}

// the blind man's test for shading :-)
public static int isShadeOfRed(Color c)
{
    if (4*c.R < 5*c.G) return 0; // Red not strong enough in respect to green
    if (4*c.R < 5*c.B) return 0; // Red not strong enough in respect to blue
    return 1; // Red is stronger enough than green and blue to be called "shade of red"
}

// (shades of green and blue left as an exercise)

// Very basic (and CIE-incorrect) check
public static int areSameColor(Color a, Color b)
{
    if (Math.Abs(a.R - b.R) > 5 * 2.55) return 0;
    if (Math.Abs(a.G - b.G) > 5 * 2.55) return 0; 
    if (Math.Abs(a.B - b.B) > 5 * 2.55) return 0;
    return 1; // "more or less" the same color
}

// This is more or less pseudo code...
public static int isNoise(int x, int y)
{
    if ((x < 1) || (y < 1)) return 0; // or maybe "return 1"
    if ((x+1 >= bmp.Width)||(y+1 >= bmp.Height)) return 0;
    pix = bmp.GetPixel(x,y);
    for (i = -1; i <= 1; i++)
        for (j = -1; j <= 1; j++)
        {
            if ((i == 0) && (j == 0)) continue;
            test = bmp.GetPixel(x+i, y+j);
            if (isGray(test)) grays++;
            if (isSameColor(pix, test)) same++;
        }
    // Pixel surrounded by grays, and has no neighbours of the same colour
    // (who knows, maybe we could skip gray calculation and check altogether?)
    // is noise.
    if ((grays == 5) && (same == 0))
       return 1;
    return 0;
}

// NOTE: do not immediately set to gray pixels found to be noise, for their neighbours
// will be calculated using the new gray pixel. Damage to image might result.
// Use a copy of bmp instead.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM