使用 ColorMatrix 将图像转换为单色

Convert image to monochrome using ColorMatrix

我正在尝试构建一个将彩色图像转换为单色图像的函数。关于术语有很多混淆,但我说的是真正的黑白,我们只有两种颜色:黑色和白色 - 没有灰色。

这是我正在努力实现的示例:

-->

我还想要一种方法来控制转换的“阈值”(即像素必须有多“亮”才能产生白色像素)。

这是我现在拥有的。

private Bitmap bitmap2Monochrome(Bitmap srcImg, int brightness)
{
    Bitmap monoImg = new Bitmap(srcImg.Width, srcImg.Height);

    // Loop through all the pixels in the image
    for (int y = 0; y < srcImg.Height; y++)
    {
        for (int x = 0; x < srcImg.Width; x++)
        {
            // Get pixel color
            Color existingColor = srcImg.GetPixel(x, y);
            
            // Average R, G, and B to determine "brightness" of this pixel
            int gray = (int)((existingColor.R + existingColor.G + existingColor.G) / 3);

            // If the "brightness" is greater than the threshold, this is a white pixel
            int color = 0;
            if (gray > brightness)
            {
                color = 255;
            }

            // Update the output image
            Color newColor = Color.FromArgb(color, color, color);
            monoImg.SetPixel(x, y, newColor);
        }
    }

    return monoImg;
}

此方法完全符合我的要求,但它(可以理解)非常慢。这是因为 GetPixel()SetPixel() 函数非常慢。

我很想使用 ColorMatrix,因为它们速度非常快,但我不太擅长矩阵,而且我似乎无法想出一个可以实现此结果的矩阵。

下面是逻辑工作原理的示例:

  1. 源颜色=(200, 150, 250)
  2. 所以,灰度等效 = (200, 200, 200)
  3. 所以,灰度值=200
  4. 所以,200 > threshold
  5. 所以,单色值=255
  6. 所以,单色=(255, 255, 255)

有什么方法可以使用 ColorMatrix 实现这种功能?

我也乐于接受其他想法!

考虑在

的第五行使用身份 ColorMatrix
{ -threshold, -threshold, -threshold, 0, 1 }

这将允许您将低于阈值的所有颜色移动到 0

然后您可以使用第二个 ColorMatrix 来乘以所有 RGB,使它们大于 1.0d。这会产生你想要的效果(假设 ColorMatrix 的实现防止负值和大于 1.0d 的值)

只是扩展 DekuDesu 的回答,这里有一个编码解决方案。

private Bitmap bitmap2Monochrome(Bitmap img, int threshold)
{
    // Convert the picture to grayscale (so we have "lightness values")
    Bitmap grayImg = bitmap2Grayscale(img, 0);

    // Adjust threshold to be in range 0.0 - 1.0
    float thresh = -1 * ((float)threshold / 255);

    // Subtract threshold value from all pixels to change dark pixels to black
    // Note: Values are automatically bound in range 0 - 255
    float[][] colorTransMatrix =
    {
        new float[] {1.000F, 0.000F, 0.000F, 0.000F, 0.000F},
        new float[] {0.000F, 1.000F, 0.000F, 0.000F, 0.000F},
        new float[] {0.000F, 0.000F, 1.000F, 0.000F, 0.000F},
        new float[] {0.000F, 0.000F, 0.000F, 1.000F, 0.000F},
        new float[] {thresh, thresh, thresh, 0.000F, 1.000F}
    };
    Bitmap temp = translateBitmap(img, colorTransMatrix);

    // Multiply remaining pixels by large value to change light pixels to white
    // Note: Values are automatically bound in range 0 - 255
    float[][] colorTransMatrix2 =
    {
        new float[] {100.000F, 100.000F, 100.000F, 0.000F, 0.000F},
        new float[] {100.000F, 100.000F, 100.000F, 0.000F, 0.000F},
        new float[] {100.000F, 100.000F, 100.000F, 0.000F, 0.000F},
        new float[] {0.000F, 0.000F, 0.000F, 1.000F, 0.000F},
        new float[] {0.000F, 0.000F, 0.000F, 0.000F, 1.000F}
    };
    Bitmap monoImg = translateBitmap(temp, colorTransMatrix2);

    // Return the monochrome image
    return monoImg;
}


private Bitmap bitmap2Grayscale(Bitmap img, int brightness)
{
    // Adjust brightness to be in range 0.0 - 1.0
    float bright = -1 * ((float)brightness / 255);

    // Average R, G, B values of all pixels
    float[][] colorTransMatrix =
    {
        new float[] {0.333F, 0.333F, 0.333F, 0.000F, 0.000F},
        new float[] {0.333F, 0.333F, 0.333F, 0.000F, 0.000F},
        new float[] {0.333F, 0.333F, 0.333F, 0.000F, 0.000F},
        new float[] {0.000F, 0.000F, 0.000F, 1.000F, 0.000F},
        new float[] {bright, bright, bright, 0.000F, 1.000F},
    };
    Bitmap grayImg = translateBitmap(img, colorTransMatrix);

    // Return the grayscale image
    return grayImg;
}


private Bitmap translateBitmap(Bitmap img, float[][] colorTranslationMatrix)
{
    // Setup color translation
    ColorMatrix colorMatrix = new ColorMatrix(colorTranslationMatrix);
    ImageAttributes imgAttr = new ImageAttributes();
    imgAttr.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

    // Draw the image with translated colors
    Bitmap trImg = new Bitmap(img.Width, img.Height);
    Graphics g = Graphics.FromImage(trImg);
    g.DrawImage(img, new Rectangle(0, 0, trImg.Width, trImg.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, imgAttr);

    // Return the translated image
    return trImg;
}