我需要在一起添加一些BMP图像。您可能知道,BMP图像有一个54字节的标题。我将自己的新标题写入一个新文件,然后我需要遍历一个BMP文件列表并将它们相互附加,但我需要跳过每个文件的前54个字节。
这适用于千兆像素图像拼接项目。我会使用已经存在的软件,除了图像中没有重叠(但我知道图块布局)。我需要能够将一些非常大的BMP文件(192,000x1080)作为行接收并将它们一个接一个地附加到另一个以创建最终图像,即20.7千兆像素。虽然我有32GB的内存,但最终的20.7千兆像素图像将是62.2GB(我将转换为PNG)。
鉴于这些巨大的内存要求,我如何阅读每个BMP并将它们附加到最终文件而不使用我的所有内存?我想尽可能使用字节数组;因为这些是无压缩的BMP,它们可以像这样读取并保存到另一个文件而不使用任何GDI对象。
答案 0 :(得分:4)
您可以写入文件中的任意位置。即:
using(var s = new FileStream("myBigImage.bmp", FileMode.Create, FileAccess.Write)) {
//assume that you write out the header here
foreach(var tileFile in tiles) {
byte[] imgData = /*read image data into a byte[]*/;
long y = /*obtain the correct y offset*/;
s.Position = y * pixelWidth * imageWidth + 58;
s.Write(imgData, 0, imgData.Length);
}
}
基本上,您可以跳转文件以填写您想要的任何位置。 Position
是一个长值,因此您不必担心会遇到32位整数的限制。
幸运的是,因为你的图像基本上只是一堆高的瓷砖,所以你不必担心处理每一行。在这种情况下,为了减少内存,你必须分别写出每个tile的行。
答案 1 :(得分:1)
您无需将所有生成的文件数据保存在内存中。只需打开FileStream并将数据写入文件即可。即使你写了数千兆字节的数据,FileStream也不会占用太多内存。
答案 2 :(得分:0)
如果有人跟我有兴趣,这就是我发现的工作:
for (int i = 0; i < reverseImageFiles.Length; i++)
{
string curBMP = reverseImageFiles[i];
using(Stream inStream = File.OpenRead(curBMP))
using (Stream writeStream = new FileStream(outputBMP,FileMode.Append,FileAccess.Write,FileShare.None))
{
BinaryReader reader = new BinaryReader(inStream);
BinaryWriter writer = new BinaryWriter(writeStream);
byte[] buffer = new Byte[134217728];
int bytesRead;
int totalBytes = 0;
while ((bytesRead = inStream.Read(buffer, 0, 134217728)) > 0)
{
totalBytes += bytesRead;
if (totalBytes <= 134217729) //if it's the first round of reading to the buffer, you need to get rid of 54-byte BMP header
{
writeStream.Write(buffer, 54, bytesRead - 54);
}
else
{
writeStream.Write(buffer, 0, bytesRead);
}
}
}
}
两件事:
特别是对于BMP,我发现你需要在追加时反转文件列表。例如,如果要追加的三个文件标记为001.bmp,002.bmp,003.bmp,其中001.bmp应位于顶部,则需要实际以003.bmp开始并向下运行。显然,BMP是向后编码的。
如您所见,我使用了128MB缓冲区。如果我想减少寻求硬盘驱动器,那么使用不同尺寸会更好吗?我的驱动器最近被碎片整理。谢谢你的帮助!