可能重复:
How to crop huge image
有一个问题是询问如何裁剪图像here
我已阅读并使用了答案。但他们似乎都需要将图片加载到Bitmap
或Image
。
这对我造成了内存问题。因为我试图将5幅图像(8000 x 8000)裁剪成瓷砖。一次一个。
如果我错了,请纠正我,但那是8000x8000x4字节= 244 MB。每张图片。
我随机出现内存问题。
如何从另一张图像中获取1000x1000图像,同时减少内存消耗。
答案 0 :(得分:5)
所以,这是一件非常重要的事情 - 基本上,你必须为给定的图像格式重新实现图像解码器。这不简单。
对于“简单”的Windows BMP格式,有这些野兽需要应对:
那就是说,我不得不尝试一下午休时间...这是我能够想出的一个很好的LINQPad就绪脚本。
(注意:仅限Windows BMP!)
void Main()
{
// Carve out a 100x100 chunk
var top = 100;
var left = 100;
var bottom = 300;
var right = 300;
// For BMP only - open input
var fs = File.OpenRead(@"c:\temp\testbmp.bmp");
// Open output
if(File.Exists(@"c:\temp\testbmp.cropped.bmp")) File.Delete(@"c:\temp\testbmp.cropped.bmp");
var output = File.Open(@"c:\temp\testbmp.cropped.bmp", FileMode.CreateNew);
var bw = new BinaryWriter(output);
// Read out the BMP header fields
var br = new BinaryReader(fs);
var headerField = br.ReadInt16();
var bmpSize = br.ReadInt32();
var reserved1 = br.ReadInt16();
var reserved2 = br.ReadInt16();
var startOfData = br.ReadInt32();
// Read out the BMP DIB header
var header = new BITMAPV5Header();
var headerBlob = br.ReadBytes(Marshal.SizeOf(header));
var tempMemory = Marshal.AllocHGlobal(Marshal.SizeOf(header));
Marshal.Copy(headerBlob, 0, tempMemory, headerBlob.Length);
header = (BITMAPV5Header)Marshal.PtrToStructure(tempMemory, typeof(BITMAPV5Header));
Marshal.FreeHGlobal(tempMemory);
// This file is a 24bpp rgb bmp,
var format = PixelFormats.Bgr24;
var bytesPerPixel = (int)(format.BitsPerPixel / 8);
Console.WriteLine("Bytes/pixel:{0}", bytesPerPixel);
// And now I know its dimensions
var imageWidth = header.ImageWidth;
var imageHeight = header.ImageHeight;
Console.WriteLine("Input image is:{0}x{1}", imageWidth, imageHeight);
var fromX = left;
var toX = right;
var fromY = imageHeight - top;
var toY = imageHeight - bottom;
// How "long" a horizontal line is
var strideInBytes = imageWidth * bytesPerPixel;
Console.WriteLine("Stride size is:0x{0:x}", strideInBytes);
// new size
var newWidth = Math.Abs(toX - fromX);
var newHeight = Math.Abs(toY - fromY);
Console.WriteLine("New slice dimensions:{0}x{1}", newWidth, newHeight);
// Write out headers to output file
{
// header = "BM" = "Windows Bitmap"
bw.Write(Encoding.ASCII.GetBytes("BM"));
var newSize = 14 + Marshal.SizeOf(header) + (newWidth * newHeight * bytesPerPixel);
Console.WriteLine("New File size: 0x{0:x} bytes", newSize);
bw.Write((uint)newSize);
// 2 reserved shorts
bw.Write((ushort)0);
bw.Write((ushort)0);
// offset to "data"
bw.Write(header.HeaderSize + 14);
// Tweak size in header to cropped size
header.ImageWidth = newWidth;
header.ImageHeight = newHeight;
// Write updated DIB header to output
tempMemory = Marshal.AllocHGlobal(Marshal.SizeOf(header));
Marshal.StructureToPtr(header, tempMemory, true);
byte[] asBytes = new byte[Marshal.SizeOf(header)];
Marshal.Copy(tempMemory, asBytes, 0, asBytes.Length);
Marshal.FreeHGlobal(tempMemory);
bw.Write(asBytes);
asBytes.Dump();
}
// Jump to where the pixel data is located (on input side)
Console.WriteLine("seeking to position: 0x{0:x}", startOfData);
fs.Seek(startOfData, SeekOrigin.Begin);
var sY = Math.Min(fromY, toY);
var eY = Math.Max(fromY, toY);
for(int currY = sY; currY < eY; currY++)
{
long offset = startOfData + ((currY * strideInBytes) + (fromX * bytesPerPixel));
fs.Seek(offset, SeekOrigin.Begin);
// Blast in each horizontal line of our chunk
var lineBuffer = new byte[newWidth * bytesPerPixel];
int bytesRead = fs.Read(lineBuffer, 0, lineBuffer.Length);
output.Write(lineBuffer, 0, lineBuffer.Length);
}
fs.Close();
output.Close();
}
[StructLayout(LayoutKind.Sequential, Pack=0)]
public struct BITMAPV5Header
{
public uint HeaderSize;
public int ImageWidth;
public int ImageHeight;
public ushort Planes;
public ushort BitCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=36)]
public byte[] DontCare;
}