如何从System.Drawing.Bitmap转换为灰度,然后转换为双精度数组(Double [,]),然后再转换为灰度位图?

时间:2013-09-12 13:43:08

标签: c# image image-processing

我需要在照片中执行一些数学运算,为此我需要图像的浮点灰度版本(可能来自具有各种颜色深度的JPG,PNG或BMP文件)。

我曾经使用PILscipy.ndimage在Python中执行此操作,使用PIL转换为灰度,然后转换为带{的浮点数数组非常简单{1}},但现在我需要在C#中做类似的事情,我很困惑如何这样做。

我已阅读this very nice tutorial,这似乎是一个反复出现的参考,但仅涵盖“转换为灰度”部分,我不知道如何从Bitmap获取双精度数组,然后(在某些时刻)将其转换回System.Drawing.Bitmap以供查看。

2 个答案:

答案 0 :(得分:1)

我确信有很多最佳方法可以做到这一点。

正如@Groo在评论部分中指出的那样,人们可以使用LockBits方法来编写和读取Bitmap实例的像素颜色。 更进一步,可以使用计算机的图形卡进行实际计算。

此外,将颜色变为其中的方法Color ToGrayscaleColor(Color color) 灰度版本不是光学正确的。实际上需要将一组比率应用于颜色分量强度。我只使用了1,1,1比率。这对我来说是可以接受的,对于艺术家或科学家来说可能是可怕的。

在评论部分,@ plinth非常高兴指出您应该注意的这个问题,如果您想进行解剖学上正确的转换:Converting RGB to grayscale/intensity

只想分享这个非常容易理解和实施的解决方案:

首先帮助将Color变成灰度版本:

    public static Color ToGrayscaleColor(Color color) {
        var level = (byte)((color.R + color.G + color.B) / 3);
        var result = Color.FromArgb(level, level, level);
        return result;
    }

然后将颜色位图转换为灰度位图:

    public static Bitmap ToGrayscale(Bitmap bitmap) {
        var result = new Bitmap(bitmap.Width, bitmap.Height);
        for (int x = 0; x < bitmap.Width; x++)
            for (int y = 0; y < bitmap.Height; y++) {
                var grayColor = ToGrayscaleColor(bitmap.GetPixel(x, y));
                result.SetPixel(x, y, grayColor);
            }
        return result;
    }

双打部分非常简单。 Bitmap对象是实际图像的内存表示,您可以在各种操作中使用它。 colordepth和image格式详细信息只是将Bitmap的实例加载和保存到流或文件上的问题。我们现在不必关心那些:

    public static double[,] FromGrayscaleToDoubles(Bitmap bitmap) {
        var result = new double[bitmap.Width, bitmap.Height];
        for (int x = 0; x < bitmap.Width; x++)
            for (int y = 0; y < bitmap.Height; y++)
                result[x, y] = (double)bitmap.GetPixel(x, y).R / 255;
        return result;
    }

将双阵列变回灰度图像:

    public static Bitmap FromDoublesToGrayscal(double[,] doubles) {
        var result = new Bitmap(doubles.GetLength(0), doubles.GetLength(1));
        for (int x = 0; x < result.Width; x++)
            for (int y = 0; y < result.Height; y++) {
                int level = (int)Math.Round(doubles[x, y] * 255);
                if (level > 255) level = 255; // just to be sure
                if (level < 0) level = 0; // just to be sure
                result.SetPixel(x, y, Color.FromArgb(level, level, level));
            }
        return result;
    }

以下几行:

    if (level > 255) level = 255; // just to be sure
    level < 0) level = 0; // just to be sure

真的存在,如果你操作双打,你想留出一些小错误的空间。

答案 1 :(得分:1)

最终代码,主要基于评论中的提示,特别是LockBits部分(blog post here)以及R,G和B值之间的感知平衡(这里不是最重要的,但是对于知道):

    private double[,] TransformaImagemEmArray(System.Drawing.Bitmap imagem) {
        // Transforma a imagem de entrada em um array de doubles
        // com os valores grayscale da imagem

        BitmapData bitmap_data = imagem.LockBits(new System.Drawing.Rectangle(0,0,_foto_franjas_original.Width,_foto_franjas_original.Height),
                                            ImageLockMode.ReadOnly, _foto_franjas_original.PixelFormat);

        int pixelsize = System.Drawing.Image.GetPixelFormatSize(bitmap_data.PixelFormat)/8;

        IntPtr pointer = bitmap_data.Scan0;
        int nbytes = bitmap_data.Height * bitmap_data.Stride;
        byte[] imagebytes = new byte[nbytes];
        System.Runtime.InteropServices.Marshal.Copy(pointer, imagebytes, 0, nbytes);

        double red;
        double green;
        double blue;
        double gray;

        var _grayscale_array = new Double[bitmap_data.Height, bitmap_data.Width];

        if (pixelsize >= 3 ) {
            for (int I = 0; I < bitmap_data.Height; I++) {
                for (int J = 0; J < bitmap_data.Width; J++ ) {
                    int position = (I * bitmap_data.Stride) + (J * pixelsize);
                    blue = imagebytes[position];
                    green = imagebytes[position + 1];
                    red = imagebytes[position + 2];
                    gray = 0.299 * red + 0.587 * green + 0.114 * blue;
                    _grayscale_array[I,J] = gray;
                }
            }
        }

        _foto_franjas_original.UnlockBits(bitmap_data);

        return _grayscale_array;
    }