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);
}
// 獲取圖片尺寸但不加載全部內(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)代跨平臺方案)
首先安裝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) | 使用場景 |
|---|---|---|---|
| System.Drawing | 內(nèi)置庫,簡單 | 內(nèi)存占用高 | Windows小圖處理 |
| ImageSharp | 跨平臺,現(xiàn)代API | 學(xué)習(xí)曲線 | 需要跨平臺支持 |
| 內(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可能有壓縮問題
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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一文搞懂C#實(shí)現(xiàn)讀寫文本文件中的數(shù)據(jù)
這篇文章重點(diǎn)給大家介紹C#實(shí)現(xiàn)讀寫文本文件中的數(shù)據(jù)的一些知識,讀取.txt文件數(shù)據(jù)的實(shí)例代碼及寫入讀取過程完整代碼,感興趣的朋友跟隨小編一起看看吧2021-06-06
C#使用Linq to XML進(jìn)行XPath查詢的代碼實(shí)現(xiàn)
最近在用到HtmlAgliltyPack進(jìn)行結(jié)點(diǎn)查詢時(shí),發(fā)現(xiàn)這里選擇結(jié)點(diǎn)使用的是XPath,所以這里總結(jié)一下在C#中使用XPath查詢XML的方法,習(xí)慣了用Linq,這里也是用的Linq to xml的,需要的朋友可以參考下2024-08-08
詳解如何實(shí)現(xiàn)C#和Python間實(shí)時(shí)視頻數(shù)據(jù)交互
我們在做RTSP|RTMP播放的時(shí)候,遇到好多開發(fā)者,他們的視覺算法大多運(yùn)行在python下,需要高效率的實(shí)現(xiàn)C#和Python的視頻數(shù)據(jù)交互,本文給大家總結(jié)了一些常用的方法,感興趣的小伙伴跟著小編一起來看看吧2024-10-10

