C#實(shí)現(xiàn)將超大圖片(1GB)裁剪為8張小圖片
C#處理超大圖片(1GB)需要特別注意內(nèi)存管理和性能優(yōu)化。以下是幾種高效裁剪方案:
方法1:使用System.Drawing分塊處理(內(nèi)存優(yōu)化版)
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); } // 獲取圖片尺寸但不加載全部?jī)?nèi)容 using (var image = Image.FromFile(sourceImagePath)) { int totalWidth = image.Width; int totalHeight = image.Height; // 計(jì)算每塊尺寸 (2x4網(wǎng)格) 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(現(xiàn)代跨平臺(tái)方案)
首先安裝NuGet包:
Install-Package SixLabors.ImageSharp
實(shí)現(xiàn)代碼:
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); // 配置內(nèi)存選項(xiàng)處理大圖 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; // 克隆并裁剪區(qū)域 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 // 適當(dāng)壓縮 }); Console.WriteLine($"已保存: {outputPath}"); } } } } } }
方法3:使用內(nèi)存映射文件處理超大圖
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; // 計(jì)算分塊 int chunkWidth = width / 2; int chunkHeight = height / 4; // 使用內(nèi)存映射文件處理 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; // 創(chuàng)建目標(biāo)位圖 using (var dest = new Bitmap(cropWidth, cropHeight, PixelFormat.Format24bppRgb)) { var destData = dest.LockBits( new Rectangle(0, 0, cropWidth, cropHeight), ImageLockMode.WriteOnly, dest.PixelFormat); try { // 計(jì)算源文件偏移量(BMP文件頭54字節(jié) + 數(shù)據(jù)偏移) long offset = 54 + (height - y - 1) * stride + x * bytesPerPixel; // 逐行復(fù)制 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); // 跳轉(zhuǎn)到寬度信息 int width = reader.ReadInt32(); int height = reader.ReadInt32(); fs.Seek(28, SeekOrigin.Begin); // 跳轉(zhuǎn)到位深信息 int bitsPerPixel = reader.ReadInt16(); return (width, height, bitsPerPixel); } } }
方法4:使用Magick.NET(專業(yè)圖像處理)
首先安裝NuGet包:
Install-Package Magick.NET-Q16-x64
實(shí)現(xiàn)代碼:
using ImageMagick; using System; using System.IO; class Program { static void Main() { string sourcePath = "1GB_Image.tif"; string outputDir = "CroppedImages"; Directory.CreateDirectory(outputDir); // 設(shè)置資源限制 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}"); } } } } } }
裁剪方案選擇建議
方法 | 優(yōu)點(diǎn) | 缺點(diǎn) | 使用場(chǎng)景 |
---|---|---|---|
System.Drawing | 內(nèi)置庫(kù),簡(jiǎn)單 | 內(nèi)存占用高 | Windows小圖處理 |
ImageSharp | 跨平臺(tái),現(xiàn)代API | 學(xué)習(xí)曲線 | 需要跨平臺(tái)支持 |
內(nèi)存映射 | 內(nèi)存效率高 | 復(fù)雜,僅限BMP | 超大圖處理 |
Magick.NET | 功能強(qiáng)大 | 需要安裝 | 專業(yè)圖像處理 |
注意事項(xiàng)
1.內(nèi)存管理:處理1GB圖片需要至少2-3GB可用內(nèi)存
2.文件格式:BMP/TIFF適合處理,JPEG可能有壓縮問(wèn)題
3.磁盤空間:確保有足夠空間存放輸出文件
4.異常處理:添加try-catch處理IO和內(nèi)存不足情況
5.性能優(yōu)化:
- 使用64位應(yīng)用程序
- 增加GC內(nèi)存限制:<gcAllowVeryLargeObjects enabled="true"/>
- 分批處理減少內(nèi)存壓力
到此這篇關(guān)于C#實(shí)現(xiàn)將超大圖片(1GB)裁剪為8張小圖片的文章就介紹到這了,更多相關(guān)C#圖片裁剪內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#/VB.NET 給Excel添加、刪除數(shù)字簽名的方法
這篇文章主要介紹了C#/VB.NET 給Excel添加、刪除數(shù)字簽名的方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11Unity Shader實(shí)現(xiàn)3D翻頁(yè)效果
這篇文章主要為大家詳細(xì)介紹了Unity Shader實(shí)現(xiàn)3D翻頁(yè)效果,Plane實(shí)現(xiàn)翻頁(yè)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07基于WPF平臺(tái)使用純C#實(shí)現(xiàn)動(dòng)態(tài)處理json字符串
在當(dāng)今的軟件開發(fā)領(lǐng)域,數(shù)據(jù)的交換與存儲(chǔ)變得愈發(fā)頻繁,JSON作為一種輕量級(jí)的數(shù)據(jù)交換格式,在 WPF平臺(tái)開發(fā)的桌面應(yīng)用里,我們常常需要與各種數(shù)據(jù)源交互,動(dòng)態(tài)處理JSON字符串就成為了一項(xiàng)必備技能,本文將深入探討如何在 WPF 平臺(tái)上,僅使用純C#代碼實(shí)現(xiàn)對(duì)JSON字符串的動(dòng)態(tài)處理2025-01-01C#中的DataSet、string、DataTable、對(duì)象轉(zhuǎn)換成Json的實(shí)現(xiàn)代碼
這篇文章主要介紹了C#中的DataSet、string、DataTable、對(duì)象轉(zhuǎn)換成Json的實(shí)現(xiàn)代碼,需要的朋友可以參考下2014-09-09