C#處理超大圖片(1GB)需要特別注意內存管理和性能優化。以下是幾種高效裁剪方案:
方法1:使用System.Drawing分塊處理(內存優化版)
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;class Program
{static void Main(){string sourceImagePath = "1GB_Image.bmp";string outputFolder = "CroppedImages";if (!Directory.Exists(outputFolder)){Directory.CreateDirectory(outputFolder);}// 獲取圖片尺寸但不加載全部內容using (var image = Image.FromFile(sourceImagePath)){int totalWidth = image.Width;int totalHeight = image.Height;// 計算每塊尺寸 (2x4網格)int chunkWidth = totalWidth / 2;int chunkHeight = totalHeight / 4;// 分塊裁剪for (int row = 0; row < 4; row++){for (int col = 0; col < 2; col++){int x = col * chunkWidth;int y = row * chunkHeight;// 確保最后一塊包含剩余部分int width = (col == 1) ? totalWidth - x : chunkWidth;int height = (row == 3) ? totalHeight - y : chunkHeight;CropImage(sourceImagePath,Path.Combine(outputFolder, $"part_{row}_{col}.jpg"),x, y, width, height);}}}}static void CropImage(string sourcePath, string destPath, int x, int y, int width, int height){// 使用流式處理避免全圖加載using (var source = new Bitmap(sourcePath))using (var dest = new Bitmap(width, height))using (var graphics = Graphics.FromImage(dest)){graphics.DrawImage(source,new Rectangle(0, 0, width, height),new Rectangle(x, y, width, height),GraphicsUnit.Pixel);// 保存為JPEG減少體積dest.Save(destPath, ImageFormat.Jpeg);Console.WriteLine($"已保存: {destPath} ({width}x{height})");}}
}
方法2:使用ImageSharp(現代跨平臺方案)
首先安裝NuGet包:
Install-Package SixLabors.ImageSharp
實現代碼:
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Formats.Jpeg;class Program
{static async Task Main(){string sourcePath = "1GB_Image.jpg";string outputDir = "CroppedImages";Directory.CreateDirectory(outputDir);// 配置內存選項處理大圖var configuration = Configuration.Default.Clone();configuration.MemoryAllocator = new SixLabors.ImageSharp.Memory.ArrayPoolMemoryAllocator();// 分塊加載和處理using (var image = await Image.LoadAsync(configuration, sourcePath)){int totalWidth = image.Width;int totalHeight = image.Height;int chunkWidth = totalWidth / 2;int chunkHeight = totalHeight / 4;for (int row = 0; row < 4; row++){for (int col = 0; col < 2; col++){int x = col * chunkWidth;int y = row * chunkHeight;int width = (col == 1) ? totalWidth - x : chunkWidth;int height = (row == 3) ? totalHeight - y : chunkHeight;// 克隆并裁剪區域using (var cropped = image.Clone(ctx => ctx.Crop(new Rectangle(x, y, width, height)))){string outputPath = Path.Combine(outputDir, $"part_{row}_{col}.jpg");await cropped.SaveAsync(outputPath, new JpegEncoder {Quality = 80 // 適當壓縮});Console.WriteLine($"已保存: {outputPath}");}}}}}
}
方法3:使用內存映射文件處理超大圖
using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Drawing;
using System.Drawing.Imaging;class Program
{static void Main(){string sourcePath = "1GB_Image.bmp";string outputDir = "CroppedImages";Directory.CreateDirectory(outputDir);// 獲取BMP文件頭信息var bmpInfo = GetBmpInfo(sourcePath);int width = bmpInfo.Width;int height = bmpInfo.Height;int bytesPerPixel = bmpInfo.BitsPerPixel / 8;int stride = width * bytesPerPixel;// 計算分塊int chunkWidth = width / 2;int chunkHeight = height / 4;// 使用內存映射文件處理using (var mmf = MemoryMappedFile.CreateFromFile(sourcePath, FileMode.Open)){for (int row = 0; row < 4; row++){for (int col = 0; col < 2; col++){int x = col * chunkWidth;int y = row * chunkHeight;int cropWidth = (col == 1) ? width - x : chunkWidth;int cropHeight = (row == 3) ? height - y : chunkHeight;// 創建目標位圖using (var dest = new Bitmap(cropWidth, cropHeight, PixelFormat.Format24bppRgb)){var destData = dest.LockBits(new Rectangle(0, 0, cropWidth, cropHeight),ImageLockMode.WriteOnly,dest.PixelFormat);try{// 計算源文件偏移量(BMP文件頭54字節 + 數據偏移)long offset = 54 + (height - y - 1) * stride + x * bytesPerPixel;// 逐行復制for (int line = 0; line < cropHeight; line++){using (var accessor = mmf.CreateViewAccessor(offset - line * stride, cropWidth * bytesPerPixel)){byte[] lineData = new byte[cropWidth * bytesPerPixel];accessor.ReadArray(0, lineData, 0, lineData.Length);IntPtr destPtr = destData.Scan0 + (line * destData.Stride);System.Runtime.InteropServices.Marshal.Copy(lineData, 0, destPtr, lineData.Length);}}}finally{dest.UnlockBits(destData);}string outputPath = Path.Combine(outputDir, $"part_{row}_{col}.jpg");dest.Save(outputPath, ImageFormat.Jpeg);Console.WriteLine($"已保存: {outputPath}");}}}}}static (int Width, int Height, int BitsPerPixel) GetBmpInfo(string filePath){using (var fs = new FileStream(filePath, FileMode.Open))using (var reader = new BinaryReader(fs)){// 讀取BMP頭if (reader.ReadChar() != 'B' || reader.ReadChar() != 'M')throw new Exception("不是有效的BMP文件");fs.Seek(18, SeekOrigin.Begin); // 跳轉到寬度信息int width = reader.ReadInt32();int height = reader.ReadInt32();fs.Seek(28, SeekOrigin.Begin); // 跳轉到位深信息int bitsPerPixel = reader.ReadInt16();return (width, height, bitsPerPixel);}}
}
方法4:使用Magick.NET(專業圖像處理)
首先安裝NuGet包:
Install-Package Magick.NET-Q16-x64
實現代碼:
using ImageMagick;
using System;
using System.IO;class Program
{static void Main(){string sourcePath = "1GB_Image.tif";string outputDir = "CroppedImages";Directory.CreateDirectory(outputDir);// 設置資源限制MagickNET.SetResourceLimit(ResourceType.Memory, 1024 * 1024 * 1024); // 1GB// 使用像素流處理大圖using (var image = new MagickImage(sourcePath)){int width = image.Width;int height = image.Height;int chunkWidth = width / 2;int chunkHeight = height / 4;for (int row = 0; row < 4; row++){for (int col = 0; col < 2; col++){int x = col * chunkWidth;int y = row * chunkHeight;int cropWidth = (col == 1) ? width - x : chunkWidth;int cropHeight = (row == 3) ? height - y : chunkHeight;using (var cropped = image.Clone(new MagickGeometry{X = x,Y = y,Width = cropWidth,Height = cropHeight})){string outputPath = Path.Combine(outputDir, $"part_{row}_{col}.jpg");cropped.Quality = 85;cropped.Write(outputPath);Console.WriteLine($"已保存: {outputPath}");}}}}}
}
裁剪方案選擇建議
方法???????? | 優點 | 缺點 | 使用場景 |
System.Drawing | 內置庫,簡單 | 內存占用高 | Windows小圖處理 |
ImageSharp | 跨平臺,現代API?? ? | 學習曲線 | 需要跨平臺支持 |
內存映射 | 內存效率高 | 復雜,僅限BMP | 超大圖處理 |
Magick.NET | 功能強大 | 需要安裝 | 專業圖像處理 |
注意事項
- 內存管理:處理1GB圖片需要至少2-3GB可用內存
- 文件格式:BMP/TIFF適合處理,JPEG可能有壓縮問題
- 磁盤空間:確保有足夠空間存放輸出文件
- 異常處理:添加try-catch處理IO和內存不足情況
- 性能優化:
- 使用64位應用程序
- 增加GC內存限制:<gcAllowVeryLargeObjects enabled="true"/>
- 分批處理減少內存壓力