【问题标题】:Bitmap to int[,] conversion and vice versa位图到 int[,] 的转换,反之亦然
【发布时间】:2016-12-05 09:52:06
【问题描述】:

考虑以下两个例程。

    //Tested
    ///Working fine. 
    public static Bitmap ToBitmap(int [,] image)
    {
        int Width = image.GetLength(0);
        int Height = image.GetLength(1);
        int i, j;
        Bitmap bitmap = new Bitmap(Width, Height);
        BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, Width, Height),
                                 ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
        unsafe
        {
            byte* address = (byte*)bitmapData.Scan0;

            for (i = 0; i < bitmapData.Height; i++)
            {
                for (j = 0; j < bitmapData.Width; j++)
                {
                    // write the logic implementation here
                    address[0] = (byte)image[j, i];
                    address[1] = (byte)image[j, i];
                    address[2] = (byte)image[j, i];
                    address[3] = (byte)255;
                    //4 bytes per pixel
                    address += 4;
                }//end for j

                //4 bytes per pixel
                address += (bitmapData.Stride - (bitmapData.Width * 4));
            }//end for i
        }//end unsafe
        bitmap.UnlockBits(bitmapData);
        return bitmap;// col;
    }

    //Tested
    ///Working fine. 
    public static int[,] ToInteger(Bitmap bitmap)
    {
        int[,] array2D = new int[bitmap.Width, bitmap.Height];

        BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                                                 ImageLockMode.ReadWrite,
                                                 PixelFormat.Format32bppRgb);

        unsafe
        {
            byte* address = (byte*)bitmapData.Scan0;

            int paddingOffset = bitmapData.Stride - (bitmap.Width * 4);//4 bytes per pixel

            for (int i = 0; i < bitmap.Width; i++)
            {
                for (int j = 0; j < bitmap.Height; j++)
                {
                    byte[] temp = new byte[4];
                    temp[0] = address[0];
                    temp[1] = address[1];
                    temp[2] = address[2];
                    temp[3] = address[3];

                    array2D[j, i] = BitConverter.ToInt32(temp, 0);

                    //4-bytes per pixel
                    address += 4;//4-channels
                }
                address += paddingOffset;
            }
        }
        bitmap.UnlockBits(bitmapData);

        return array2D;
    }

这两个例程适用于 32bpp 图像。这些例程仅在像素格式设置为PixelFormat.Format32bpp 时有效。如果我使用PixelFormat.Format8bppIndexed,它会生成一个异常。

为了避免该异常(另外,由于地址计算问题,我无法实现byteint之间的无缝转换),我需要每次将那个32位Bitmap转换为灰度int[,] 被转换回 Bitmap。我想摆脱这个问题。

        Bitmap grayscale = Grayscale.ToGrayscale(InputImage);

        //Here, the Bitmap is treated as a 32bit image
        //to avoid the exception eventhough it is already
        //an 8bpp grayscale image.
        int[,] i1 = ImageDataConverter.ToInteger(grayscale);

        Complex[,] comp = ImageDataConverter.ToComplex(i1);

        int[,] i2 = ImageDataConverter.ToInteger(comp);

        Bitmap b2 = ImageDataConverter.ToBitmap(i2);

        //It is already a Grayscale image.
        //But, the problem is, b2.PixelFormat is set to 
        //PixelFormat.Formap32bpp because of those routines.
        //Hence the unnecessay conversion.
        b2 = Grayscale.ToGrayscale(b2);

我需要修改它们以仅对 8bpp 索引(灰度)图像进行操作。

我怎样才能做到这一点?

【问题讨论】:

  • 你要什么操作?您的意思是您希望能够传递byte[,] 而不是int[,]?如果不是,那么如何解释int[,]?每个 32 位值是否只存储 0 到 255 之间的值?他们真的在你写的时候被索引了吗?如果是这样,应该使用什么调色板?还是它们真的是灰度的(正如您也写的那样)?每个 32 位值中是否包含四个像素?无论哪种方式,如何解释 8 位值?你想输出一个 32bpp 的图像,还是应该输出也是 8bpp?您几乎忽略了规范的所有重要部分。

标签: c# image image-processing bitmap integer


【解决方案1】:

如果要处理索引位图,则需要读取图像的每个字节,并从调色板中查找颜色。保存图像时,您需要执行相反的逻辑:

public static Bitmap ToBitmap(int[,] image)
{
    int width = image.GetLength(0);
    int height = image.GetLength(1);
    Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
    BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height),
                                ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
    int stride = bitmapData.Stride;

    // A dictionary of colors to their index values
    Dictionary<int, int> palette = new Dictionary<int, int>();
    // A flat list of colors
    List<Color> paletteList = new List<Color>();

    unsafe
    {
        byte* address = (byte*)bitmapData.Scan0;

        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                // Get the color from the Bitmap
                int color = image[x, y];
                if (!palette.ContainsKey(color))
                {
                    // This color isn't in the palette, go ahead and add it
                    palette.Add(color, palette.Count);
                    paletteList.Add(Color.FromArgb(color));
                    if (palette.Count >= 256)
                    {
                        // The palette is too big.  Ideally this function would
                        // dither some pixels so it could handle this condition
                        // but that would make this example overly complicated
                        throw new InvalidOperationException("Too many colors in image");
                    }
                }

                // And lookup the index of the color in the palette and
                // add it to the BitmapData's memory
                address[stride * y + x] = (byte)palette[color];
            }
        }
    }

    bitmap.UnlockBits(bitmapData);

    // Each time you call Bitmap.Palette it actually returns
    // a Clone of the object, so we need to ask for a cloned
    // copy here.
    var newPalette = bitmap.Palette;

    // For each one of our colors, add it to the palette object
    for (int i = 0; i < paletteList.Count; i++)
    {
        newPalette.Entries[i] = paletteList[i];
    }

    // And since this is a clone, assign it back to the bitmap
    // so it'll take effect.
    bitmap.Palette = newPalette;

    return bitmap;
}

public static int[,] ToInteger(Bitmap bitmap)
{
    if (bitmap.Palette.Entries.Length == 0)
    {
        // This doesn't appear to have a palette, so this operation doesn't
        // make sense
        throw new InvalidOperationException("bitmap is not an indexed bitmap");
    }

    int width = bitmap.Width;
    int height = bitmap.Height;
    int[,] array2D = new int[width, height];

    BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height),
                                                ImageLockMode.ReadOnly,
                                                PixelFormat.Format8bppIndexed);

    unsafe
    {
        // Pull out the stride to prevent asking for it many times
        int stride = bitmapData.Stride;
        byte* address = (byte*)bitmapData.Scan0;

        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                // Lookup the color based off the pixel, and set it's value
                // to the return array
                array2D[x, y] = bitmap.Palette.Entries[address[stride * y + x]].ToArgb();
            }
        }
    }

    bitmap.UnlockBits(bitmapData);

    return array2D;
}

【讨论】:

  • 很慢!需要很长时间才能执行。
  • 是的。我试图展示您的问题的流程和解决方案,而不是尽可能提供最高效的解决方案。
【解决方案2】:
public static int[,] ToInteger(Bitmap bitmap)
{
    int[,] array2D = new int[bitmap.Width, bitmap.Height];

    BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                                            ImageLockMode.ReadWrite,
                                            PixelFormat.Format8bppIndexed);
    int bytesPerPixel = sizeof(byte);

    unsafe
    {
        byte* address = (byte*)bitmapData.Scan0;

        int paddingOffset = bitmapData.Stride - (bitmap.Width * bytesPerPixel);

        for (int i = 0; i < bitmap.Width; i++)
        {
            for (int j = 0; j < bitmap.Height; j++)
            {
                byte[] temp = new byte[bytesPerPixel];

                for (int k = 0; k < bytesPerPixel; k++)
                {
                    temp[k] = address[k];
                }

                int iii = 0;

                if (bytesPerPixel >= sizeof(int))
                {
                    iii = BitConverter.ToInt32(temp, 0);
                }
                else
                {
                    iii = (int)temp[0];
                }

                array2D[j, i] = iii;

                address += bytesPerPixel;
            }
            address += paddingOffset;
        }
    }
    bitmap.UnlockBits(bitmapData);

    return array2D;
}

public static Bitmap ToBitmap(int[,] image)
{
    int Width = image.GetLength(0);
    int Height = image.GetLength(1);
    int i, j;

    Bitmap bitmap = new Bitmap(Width, Height, PixelFormat.Format8bppIndexed);

    BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, Width, Height),
                             ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);

    int bytesPerPixel = sizeof(byte);

    unsafe
    {
        byte* address = (byte*)bitmapData.Scan0;

        for (i = 0; i < bitmapData.Height; i++)
        {
            for (j = 0; j < bitmapData.Width; j++)
            {
                byte[] bytes = BitConverter.GetBytes(image[j, i]);

                for (int k = 0; k < bytesPerPixel; k++)
                {
                    address[k] = bytes[k];
                }

                address += bytesPerPixel;
            }

            address += (bitmapData.Stride - (bitmapData.Width * bytesPerPixel));
        }
    }
    bitmap.UnlockBits(bitmapData);

    Grayscale.SetGrayscalePalette(bitmap);

    return bitmap;
}

【讨论】:

  • 稍微解释一下就好了
猜你喜欢
  • 2015-01-23
  • 1970-01-01
  • 2020-10-24
  • 2020-03-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多