2

我正在使用提供 COM 接口的电子软件 OMICRON MPD 和 MI。我正在通过 COM 接口提供的方法截取屏幕截图,并尝试将 byte[] 保存到图像文件中。

我的代码:

byte[] rawImg = ...
MemoryStream mstream = new MemoryStream(rawImg);
ImageConverter imageConverter = new System.Drawing.ImageConverter();
System.Drawing.Image image = imageConverter.ConvertFrom(rawImg) as System.Drawing.Image; //error line
image.Save(@"path\img.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);

我得到错误:

System.ArgumentException' occurred in System.Drawing.dll Parameter is not valid

我检查了字节数组的长度:

rawImg.Length
//897832

我可以使用以下方法将上述内存流保存到文件中:

using (FileStream file = new FileStream(@"path\img.txt", FileMode.Create, FileAccess.Write))
{
    mstream.WriteTo(file);
}

我不确定这意味着什么,但我该如何调试呢?错误在哪里?是我收到的数据是错误的还是将其保存为图像的 C# 代码。

根据 COM 接口文档,rawImg 是与设备无关的位图(格式与 .BMP 文件相同)。

失败的尝试 #1

ImageConverter imageConverter = new System.Drawing.ImageConverter();
            Image image = imageConverter.ConvertFrom(rawImg) as Image; //error line
            image.Save(@"path\img.bmp", System.Drawing.Imaging.ImageFormat.Bmp);

给出与上述无效参数相同的错误

最终解决方案

我观看了一个名为“Hex To BMP: Creating a Bitmap From Scratch”的视频,它帮助我根据获得的数据构建图像。

我收到的数据包含以字节为单位的图像数据、40 字节的 DIB 标头和一些初始的 27 字节数据(我无法真正弄清楚它是什么)。为了将其转换为图像,它需要在开头有一个 14 字节的文件头,我手动构造如下:

byte[] fileHeader = { 0x42, 0x4d, 0x0c, 0xef, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00 };

请注意,十六进制的文件大小(0x0c、0xef、0x82、0x00 等于 847760 字节文件大小)是硬编码的(字节可以很容易地变成动态的)。0x36 是实际图像数据开始的位置,索引 54 是十六进制的 36。

然后我只是通过偏移到 DIB Header 开始的位置从我的原始数组中附加该数据,在我的例子中是索引 27。

下面是我的原始数据的屏幕截图,其中包含初始 27 字节的未知数据和从索引 27 开始的 DIB 标头。

在此处输入图像描述

在此处输入图像描述

以上是我最终图像十六进制数据的屏幕截图。蓝色为 14 字节的文件头,红色为 40 字节的 DIB 头,其余以绿色开头的为图像数据。使用“.bmp”扩展名保存此数据,您将获得图像。

我的代码:

byte[] imgData, newImgData;
byte[] fileHeader = { 0x42, 0x4d, 0x0c, 0xef, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00 };
newImgData = new byte[847760];
BinaryFormatter bf = new BinaryFormatter();
string path2 = @"path\myImg.bmp";
using (MemoryStream ms = new MemoryStream())
{
  bf.Serialize(ms, value);
  mgData = ms.ToArray();

 }
 for(int i = 0; i < fileHeader.Count(); i++)
 {
       newImgData[i] = fileHeader[i];
 }

  int indx = 14;

  for (int i = 27; i < imgData.Count(); i++)
  {
       newImgData[indx] = imgData[i];
       indx++;
  }

  System.IO.File.WriteAllBytes(path2, newImgData.ToArray());
4

2 回答 2

2

查看 Notepad++ 转储,这似乎只是每像素 32 位或 4 字节 ARGB 图像的原始字节。您应该能够使用Bitmap(Int32, Int32, Int32, PixelFormat, IntPtr)构造函数并只传递原始字节。唯一的问题是找出图像的widthheightstridePixelFormat,但您应该能够通过一些实验来解决这个问题。

这是一个示例,其中我制作了一个与您展示的类似的字节数组:

byte[] bytes = new byte[] {
    0,64,128,128,
    0,64,128,128,
    0,64,128,128,
    0,64,128,128,
    0,64,128,128,
    0,64,128,128,
    0,64,128,128,
    0,64,128,128,
};

unsafe
{
    fixed (byte* pBytes = bytes)
    {
        int width = 4; // Your width
        int height = 2; // Your height
        int stride = width * 4; // Your stride
        Bitmap bitmap = new Bitmap(width, height, stride, PixelFormat.Format32bppArgb, new IntPtr(pBytes));
        bitmap.Save(@"c:\temp\bitmap.png"); // Could save to another format like .jpg
    }
}
于 2017-08-15T17:39:58.757 回答
1

尝试一下,而不是像这样的代码片段:

byte[] rawImg = ...

MemoryStream mstream = new MemoryStream(rawImg);
File.WriteAllBytes("screen.bmp", mstream.ToArray());

尝试#2

我的第二个猜测,这可能是 DIB 文件格式,如果是真的,您应该能够在大多数照片查看器/编辑器(如 GIMP 或其他)中打开“screen.dib”

byte[] rawImg = array;

File.WriteAllBytes("screen.dib", rawImg);
于 2017-08-15T16:47:24.920 回答