在C#中将int数组转换为BMP [英] Turning int array into BMP in C#

查看:110
本文介绍了在C#中将int数组转换为BMP的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在将整数的整数数组(int32 [,])转换为C#的BMP格式时遇到问题. 我尝试遍历整个数组以设置BMP中的像素颜色,它确实可以工作,但最终速度很慢,几乎无法使用. 我做了很多谷歌搜索,但是找不到我的问题的答案. 我需要将该图像实时放入PictureBox中,因此该方法需要快速进行.

I'm having problems converting a grayscale array of ints (int32[,]) into BMP format in C#. I tried cycling through the array to set pixel color in the BMP, it does work but it ends up being really slow and practically unusable. I did a lot of googling but I cannot find the answer to my question. I need to put that image in a PictureBox in real time so the method needs to be fast.

相关讨论这里

该数组为8位深度,但存储为int32

the array is 8bit depth but stored as int32

Edit2:刚刚找到了这段代码

Just found this code

    private unsafe Task<Bitmap> BitmapFromArray(Int32[,] pixels, int width, int height)
    {
        return Task.Run(() =>
         {
             Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
             BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
             for (int y = 0; y < height; y++)
             {
                 byte* row = (byte*)bitmapData.Scan0 + bitmapData.Stride * y;
                 for (int x = 0; x < width; x++)
                 {
                     byte grayShade8bit = (byte)(pixels[x, y] >> 4);
                     row[x * 3 + 0] = grayShade8bit;
                     row[x * 3 + 1] = grayShade8bit;
                     row[x * 3 + 2] = grayShade8bit;
                 }
             }
             bitmap.UnlockBits(bitmapData);
             return bitmap;
         });
    }

似乎可以足够快地工作,但是图像几乎是黑色的.如果我卸下相机顶部,则图像应该是完全白色的,但只会显示出非常深的灰色.我猜这是在将像素值解释为32位,而不是8位.然后尝试投射(ushort)pixels [x,y]但不起作用

Seems to work fast enough but the image is almost black. If I remove the top of the camera the Image should be completely white but it just displays a really dark grey. I guess it's interpreting the pixel value as 32bit, not 8bit. Then tried to cast (ushort)pixels[x, y] but doesn't work

推荐答案

我实际上在此处编写了通用的BuildImage函数从字节数组构建图像,但是,当然,您不是从字节数组开始的,而是从二维Int32数组开始的.解决该问题的简单方法就是预先对其进行转换.

I actually wrote a universally usable BuildImagefunction here on SO to build an image out of a byte array, but of course, you're not starting from a byte array, you're starting from a two-dimensional Int32 array. The easy way to get around it is simply to transform it in advance.

您的整数字节数组是一件很奇怪的事情.如果是从灰度图像读取的,我宁愿假设这是32位ARGB数据,而您只是使用每个值的最低部分(这将是蓝色的),但是如果将这些值下移4位产生统一的暗值,我倾向于相信你的话;否则,下一个颜色成分(绿色)的位将渗出,从而产生明亮的颜色.

Your array of bytes-as-integers is a rather odd thing. If this is read from a grayscale image I'd rather assume this is 32-bit ARGB data, and you're just using the lowest component of each value (which would be the blue one), but if downshifting the values by 4 bits produced uniformally dark values I'm inclined to take your word for that; otherwise the bits of the next colour component (green) would bleed in, giving bright colours as well.

不管怎么说,除了沉思和猜测之外,这是我的实际答案.

Anyway, musing and second-guessing aside, here's my actual answer.

您可能会认为,将每个值注入到8位图像中时,仅仅是亮度,但是这实际上是错误的. System.Drawing像素格式中没有特定类型来指示8位灰度,并且8位图像已被调色板表示,这意味着图像上的每个值均指调色板上的一种颜色. .因此,要实际制作一个8位灰度图像(其中字节值表示像素的亮度),您需要明确定义一个调色板,其中调色板上0到255的索引包含从(0,0, 0)至(255,255,255).当然,这很容易生成.

You may think each of your values, when poured into an 8-bit image, is simply the brightness, but this is actually false. There is no specific type in the System.Drawing pixel formats to indicate 8-bit grayscale, and 8-bit images are paletted, which means that each value on the image refers to a colour on the colour palette. So, to actually make an 8-bit grayscale image where your byte values indicate the pixel's brightness, you'll need to explicitly define a colour palette where the indices of 0 to 255 on the palette contain gray colours going from (0,0,0) to (255,255,255). Of course, this is pretty easy to generate.

此代码会将您的数组转换为8位图像.它使用前面提到的BuildImage函数.请注意,该函数使用 no 不安全代码. Marshal.Copy的使用意味着永远不会直接处理原始指针,从而使代码得到完全管理.

This code will transform your array into an 8-bit image. It uses the aforementioned BuildImage function. Note that that function uses no unsafe code. The use of Marshal.Copy means raw pointers are never handled directly, making the code completely managed.

public static Bitmap FromTwoDimIntArrayGray(Int32[,] data)
{
    // Transform 2-dimensional Int32 array to 1-byte-per-pixel byte array
    Int32 width = data.GetLength(0);
    Int32 height = data.GetLength(1);
    Int32 byteIndex = 0;
    Byte[] dataBytes = new Byte[height * width];
    for (Int32 y = 0; y < height; y++)
    {
        for (Int32 x = 0; x < width; x++)
        {
            // logical AND to be 100% sure the int32 value fits inside
            // the byte even if it contains more data (like, full ARGB).
            dataBytes[byteIndex] = (Byte)(((UInt32)data[x, y]) & 0xFF);
            // More efficient than multiplying
            byteIndex++;
        }
    }
    // generate palette
    Color[] palette = new Color[256];
    for (Int32 b = 0; i < 256; b++)
        palette[b] = Color.FromArgb(b, b, b);
    // Build image
    return BuildImage(dataBytes, width, height, width, PixelFormat.Format8bppIndexed, palette, null);
}

注意,即使整数完整的ARGB值,上面的代码仍将完全相同.如果您只使用整数中四个字节中的最低字节,如我所说,那将只是整个ARGB整数中的蓝色分量.如果图像是灰度图像,则所有三个颜色分量都应该相同,因此您仍将获得相同的结果.

Note, even if the integers were full ARGB values, the above code would still work exactly the same; if you only use the lowest of the four bytes inside the integer, as I said, that'll simply be the blue component of the full ARGB integer. If the image is grayscale, all three colour components should be identical, so you'll still get the same result.

假设您发现自己拥有相同的字节数组,其中整数 do 实际上包含完整的32bpp ARGB数据,那么您将必须移出所有四个字节值,并且不会生成的灰色调色板,但除此之外,代码将非常相似.只是,每个X迭代处理4个字节.

Assuming you ever find yourself with the same kind of byte array where the integers actually do contain full 32bpp ARGB data, you'd have to shift out all four byte values, and there would be no generated gray palette, but besides that, the code would be pretty similar. Just, handling 4 bytes per X iteration.

    public static Bitmap fromTwoDimIntArrayGray(Int32[,] data)
    {
        Int32 width = data.GetLength(0);
        Int32 height = data.GetLength(1);
        Int32 stride = width * 4;
        Int32 byteIndex = 0;
        Byte[] dataBytes = new Byte[height * stride];
        for (Int32 y = 0; y < height; y++)
        {
            for (Int32 x = 0; x < width; x++)
            {
                // UInt32 0xAARRGGBB = Byte[] { BB, GG, RR, AA }
                UInt32 val = (UInt32)data[x, y];
                // This code clears out everything but a specific part of the value
                // and then shifts the remaining piece down to the lowest byte
                dataBytes[byteIndex + 0] = (Byte)(val & 0x000000FF); // B
                dataBytes[byteIndex + 1] = (Byte)((val & 0x0000FF00) >> 08); // G
                dataBytes[byteIndex + 2] = (Byte)((val & 0x00FF0000) >> 16); // R
                dataBytes[byteIndex + 3] = (Byte)((val & 0xFF000000) >> 24); // A
                // More efficient than multiplying
                byteIndex+=4;
            }
        }
        return BuildImage(dataBytes, width, height, stride, PixelFormat.Format32bppArgb, null, null);
    }

当然,如果您希望此操作不透明,则可以像以前一样使用三个字节,或者在最终调用中将PixelFormat.Format32bppArgb更改为PixelFormat.Format32bppRgb,这会将第四个字节的含义从alpha更改为只是填充.

Of course, if you want this without transparency, you can either go with three bytes as you did, or simply change PixelFormat.Format32bppArgb in the final call to PixelFormat.Format32bppRgb, which changes the meaning of the fourth byte from alpha to mere padding.

这篇关于在C#中将int数组转换为BMP的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆