从位图中删除一种颜色的所有色调

Remove all tones of a color from a bitmap

你好,我正在尝试消除保存在位图中的图像的所有橙色色调,我需要使用 tesseract 在图像中进行 OCR,而扫描文档的橙色似乎阻碍了产生错误的过程文本,我尝试用 photoshop 去除橙色,使 OCR 完美运行,主要问题是像素不是所有相同的颜色,它们是橙色但深浅不同

Bitmap modificar = new Bitmap("imagenamodificar.png");
        for (int ycount2 = 0; ycount2 < modificar.Height; ycount2++)
        {
            for (int xcount2 = 0; xcount2 < modificar.Width; xcount2++)
            {
                if (modificar.GetPixel(xcount2, ycount2) == Color.Orange)
                {
                    modificar.SetPixel(xcount2, ycount2, Color.White);
                }
            }
        }

此代码完全没有任何作用,图像保持不变。

然后我想到与像素 (0,0) 进行比较,因为它始终是我要消除的颜色。

Bitmap modificar = new Bitmap("imagenamodificar.png");
        for (int ycount2 = 0; ycount2 < modificar.Height; ycount2++)
        {
            for (int xcount2 = 1; xcount2 < modificar.Width; xcount2++)
            {
                if (modificar.GetPixel(xcount2, ycount2) == modificar.GetPixel(0,0))
                {
                    modificar.SetPixel(xcount2, ycount2, Color.White);
                }
            }
        }

但问题是它只去除了一小部分,橙色像素仍然存在,因为正如我之前提到的,并不是所有的橙色调都是一样的,有人能想到点什么吗?

这里有一些关键点可以帮助您一路走来

  1. 不要使用 GetPixel SetPixel,它非常慢
  2. 为了提高速度,最好使用 unsafe 指针访问并调用 lockbits 以获得 Pinned Array
  3. 您可能想使用 阈值 来确定特定像素颜色是否接近您要删除的颜色

一个简单的颜色阈值可以通过以下计算(你也可以在Hue上计算)

给定

  • threshold有点int
  • 一种源颜色
  • 像素颜色

阈值

var thresh = threshold * threshold;

// decode the RBG from the image Pointer
var r = ((*p >> 16) & 255) - sR;
var g = ((*p >> 8) & 255) - sG;
var b = ((*p >> 0) & 255) - sB;

// compare it against the threshold
if (r * r + g * g + b * b > thresh)
   continue;

Note : The link given in the comments by TaW is extremely helpful at figuring out color distance.

使用lockbits访问扫描线固定我们的记忆

Bitmap.LockBits Method (Rectangle, ImageLockMode, PixelFormat)

Locks a Bitmap into system memory.

代码

private static unsafe void ConvertImage(string fromPath, string toPath, Color source, Color targetColor, double threshold)
{
   var thresh = threshold * threshold;
   var target = targetColor.ToArgb();

   using (var bmp = new Bitmap(fromPath))
   {   
      // lock the array for direct access
      var data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppPArgb);
      // Convert the source to rgb
      int sR = source.R, sG = source.G, sB = source.B;
      // store the max length so we don't have to recalculate it
      var length = (int*)data.Scan0 + bmp.Height * bmp.Width;

      for (var p = (int*)data.Scan0; p < length; p++)           
      {

         // get the rgb Distance
         var r = ((*p >> 16) & 255) - sR;
         var g = ((*p >> 8) & 255) - sG;
         var b = ((*p >> 0) & 255) - sB;

         // compare it against the threshold
         if (r * r + g * g + b * b > thresh)
            continue;
         // poke the target color in
         *p = target;
      }

      // unlock the bitmap
      bmp.UnlockBits(data);
      bmp.Save(toPath);
   }
}

用法

ConvertImage(@"d:\test.jpg", @"D:\result.bmp", Color.FromArgb(247, 107, 1), Color.Black, 25);

Note : i'm using a jpg color wheel so its not as clean as it could be


原图

阈值 25

阈值 75

阈值 150

橙色测试阈值 75


unsafe (C# Reference)

The unsafe keyword denotes an unsafe context, which is required for any operation involving pointers

Unsafe Code and Pointers (C# Programming Guide)

In the common language runtime (CLR), unsafe code is referred to as unverifiable code. Unsafe code in C# is not necessarily dangerous; it is just code whose safety cannot be verified by the CLR. The CLR will therefore only execute unsafe code if it is in a fully trusted assembly. If you use unsafe code, it is your responsibility to ensure that your code does not introduce security risks or pointer errors.