如何在C#中从byte []创建bmp文件

时间:2022-09-02 15:17:02

I have a byte[] array received in TCP Client.The array contains a 24-bit RGB Bitmap file.How to create that bitmap file with given Width ,Height and data?

我在TCP Client中收到一个byte []数组。该数组包含一个24位RGB位图文件。如何创建具有给定宽度,高度和数据的位图文件?

In C++ I use this

在C ++中我使用它

int WriteBitmapFile(const char *filename, int width, int height, unsigned char *imageData)
{
FILE             *filePtr;        // file pointer
BITMAPFILEHEADER bitmapFileHeader;    // bitmap file header
BITMAPINFOHEADER bitmapInfoHeader;    // bitmap info header
DWORD                 imageIdx;    // used for swapping RGB->BGR
unsigned char     tempRGB;            // used for swapping

// open file for writing binary mode
filePtr = fopen(filename, "wb");
if (!filePtr)
    return 0;

// define the bitmap file header
bitmapFileHeader.bfSize = sizeof(BITMAPFILEHEADER);
bitmapFileHeader.bfType = 0x4D42;
bitmapFileHeader.bfReserved1 = 0;
bitmapFileHeader.bfReserved2 = 0;
bitmapFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

// define the bitmap information header
bitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfoHeader.biPlanes = 1;
bitmapInfoHeader.biBitCount = 32;                        // 24-bit
bitmapInfoHeader.biCompression = BI_RGB;                // no compression
bitmapInfoHeader.biSizeImage = width * abs(height) * 4;    // width * height * (RGB bytes)
bitmapInfoHeader.biXPelsPerMeter = 0;
bitmapInfoHeader.biYPelsPerMeter = 0;
bitmapInfoHeader.biClrUsed = 0;
bitmapInfoHeader.biClrImportant = 0;
bitmapInfoHeader.biWidth = width;                        // bitmap width
bitmapInfoHeader.biHeight = height;                    // bitmap height

// switch the image data from RGB to BGR
for(imageIdx = 0; imageIdx < bitmapInfoHeader.biSizeImage; imageIdx+=4)
{
    tempRGB = imageData[imageIdx];
    imageData[imageIdx] = imageData[imageIdx + 2];
    imageData[imageIdx + 2] = tempRGB;
}

// write the bitmap file header
fwrite(&bitmapFileHeader, 1, sizeof(BITMAPFILEHEADER), filePtr);

// write the bitmap info header
fwrite(&bitmapInfoHeader, 1, sizeof(BITMAPINFOHEADER), filePtr);

// write the image data
fwrite(imageData, 1, bitmapInfoHeader.biSizeImage, filePtr);

// close our file
fclose(filePtr);

// Success
return 1;
}

How could I do that in C#?

我怎么能用C#做到这一点?

4 个解决方案

#1


If the array actually contains a bitmap file, then you can just save the bytes as a file:

如果数组实际上包含位图文件,那么您只需将字节保存为文件:

File.WriteAllBytes(fileName, imageData);

If the array contains only raw pixel data, you can create a Bitmap object using the data:

如果数组仅包含原始像素数据,则可以使用以下数据创建Bitmap对象:

unsafe {
   fixed (byte* ptr = imageData) {
      using (Bitmap image = new Bitmap(width, height, stride, PixelFormat.Format24bppRgb, new IntPtr(ptr))) {
         image.Save(fileName);
      }
   }
}

The stride value is the number of bytes between the scan lines. If there is no padding between the scan lines, it's width * 3 for a 24bpp format.

步幅值是扫描线之间的字节数。如果扫描线之间没有填充,则对于24bpp格式,宽度为* 3。

This method uses the data in the array without creating another copy of the entire image in memory (which is why it needs the stride value).

此方法使用数组中的数据,而不在内存中创建整个图像的另一个副本(这就是它需要步幅值的原因)。

If the bitmap data is stored upside down in the array, the stride value should be negative, and the pointer should be the start of the last scan line in memory (ptr + stride * (height - 1)).

如果位图数据颠倒存储在数组中,则步幅值应为负值,指针应为内存中最后一条扫描线的起点(ptr + stride *(height - 1))。

#2


I can't test it using the stream you will be receiving, but this should work.

我无法使用您将收到的流进行测试,但这应该可行。

int WriteBitmapFile(string filename, int width, int height, byte[] imageData)
{
  using (var stream = new MemoryStream(imageData))
  using (var bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb))
  {
    BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0,
                                                    bmp.Width,
                                                    bmp.Height),
                                      ImageLockMode.WriteOnly,
                                      bmp.PixelFormat);

    Marshal.Copy(imageData, 0, bmpData.Scan0, imageData.Length);

    bmp.UnlockBits(bmpData);

    bmp.Save(filename);
  }

  return 1;
}

#3


I'd recommend making a Bitmap in C#, and letting it save itself.

我建议在C#中创建一个Bitmap,并让它自己保存。

For an example, see this post. (Particularly, the last response is correct.)

例如,请看这篇文章。 (特别是,最后的回答是正确的。)

#4


this is one way of doing it, here i have created a custom Event args that contains the size at which the image was stored as a byte array. You may not need to bother with this, this was code i created to retreive images from a byte array that a gige camera was storing to so for me this made sence.

这是一种方法,在这里我创建了一个自定义事件args,其中包含将图像存储为字节数组的大小。您可能不需要为此烦恼,这是我创建的代码,用于从gige相机存储的字节数组中检索图像,因此对我来说这是有意义的。

public Bitmap ShowImage(byte[] sender, EventImageParams e)
    {
        Bitmap bitmap = new Bitmap(e.width, e.height, PixelFormat.Format24bppRgb);
        BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                                            ImageLockMode.ReadWrite, bitmap.PixelFormat);
        IntPtr pNative = bmData.Scan0;

        Marshal.Copy(sender, 0, pNative, (e.width  * e.height * 3));
     //    
        bitmap.UnlockBits(bmData);

        return bitmap;
    }

#1


If the array actually contains a bitmap file, then you can just save the bytes as a file:

如果数组实际上包含位图文件,那么您只需将字节保存为文件:

File.WriteAllBytes(fileName, imageData);

If the array contains only raw pixel data, you can create a Bitmap object using the data:

如果数组仅包含原始像素数据,则可以使用以下数据创建Bitmap对象:

unsafe {
   fixed (byte* ptr = imageData) {
      using (Bitmap image = new Bitmap(width, height, stride, PixelFormat.Format24bppRgb, new IntPtr(ptr))) {
         image.Save(fileName);
      }
   }
}

The stride value is the number of bytes between the scan lines. If there is no padding between the scan lines, it's width * 3 for a 24bpp format.

步幅值是扫描线之间的字节数。如果扫描线之间没有填充,则对于24bpp格式,宽度为* 3。

This method uses the data in the array without creating another copy of the entire image in memory (which is why it needs the stride value).

此方法使用数组中的数据,而不在内存中创建整个图像的另一个副本(这就是它需要步幅值的原因)。

If the bitmap data is stored upside down in the array, the stride value should be negative, and the pointer should be the start of the last scan line in memory (ptr + stride * (height - 1)).

如果位图数据颠倒存储在数组中,则步幅值应为负值,指针应为内存中最后一条扫描线的起点(ptr + stride *(height - 1))。

#2


I can't test it using the stream you will be receiving, but this should work.

我无法使用您将收到的流进行测试,但这应该可行。

int WriteBitmapFile(string filename, int width, int height, byte[] imageData)
{
  using (var stream = new MemoryStream(imageData))
  using (var bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb))
  {
    BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0,
                                                    bmp.Width,
                                                    bmp.Height),
                                      ImageLockMode.WriteOnly,
                                      bmp.PixelFormat);

    Marshal.Copy(imageData, 0, bmpData.Scan0, imageData.Length);

    bmp.UnlockBits(bmpData);

    bmp.Save(filename);
  }

  return 1;
}

#3


I'd recommend making a Bitmap in C#, and letting it save itself.

我建议在C#中创建一个Bitmap,并让它自己保存。

For an example, see this post. (Particularly, the last response is correct.)

例如,请看这篇文章。 (特别是,最后的回答是正确的。)

#4


this is one way of doing it, here i have created a custom Event args that contains the size at which the image was stored as a byte array. You may not need to bother with this, this was code i created to retreive images from a byte array that a gige camera was storing to so for me this made sence.

这是一种方法,在这里我创建了一个自定义事件args,其中包含将图像存储为字节数组的大小。您可能不需要为此烦恼,这是我创建的代码,用于从gige相机存储的字节数组中检索图像,因此对我来说这是有意义的。

public Bitmap ShowImage(byte[] sender, EventImageParams e)
    {
        Bitmap bitmap = new Bitmap(e.width, e.height, PixelFormat.Format24bppRgb);
        BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                                            ImageLockMode.ReadWrite, bitmap.PixelFormat);
        IntPtr pNative = bmData.Scan0;

        Marshal.Copy(sender, 0, pNative, (e.width  * e.height * 3));
     //    
        bitmap.UnlockBits(bmData);

        return bitmap;
    }