0

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

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

我已经阅读了这个非常好的教程,这似乎是一个反复出现的参考,但它只涵盖了“转换为灰度”部分,我不知道如何从位图中获取一个双精度数组,然后(在某个时刻)将其转换回 System.Drawing.Bitmap 以供查看。

4

2 回答 2

2

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

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

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

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

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

首先是一个小帮手把颜色变成它的灰度版本:

    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对象是您可以在各种操作中使用的实际图像的内存表示。颜色深度和图像格式的细节只是加载和保存实例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

如果您在双打上进行操作并且想要为小错误留出空间,那么真的在那里。

于 2013-09-12T13:57:28.567 回答
1

最终代码,主要基于从评论中获取的提示,特别是LockBits部分(此处为博客文章)和 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;
    }
于 2013-10-08T13:37:18.853 回答