C#将1GB大图裁剪为8张图片

发布于:2025-05-17 ⋅ 阅读:(15) ⋅ 点赞:(0)

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 功能强大 需要安装 专业图像处理

注意事项

  1. 内存管理:处理1GB图片需要至少2-3GB可用内存
  2. 文件格式:BMP/TIFF适合处理,JPEG可能有压缩问题
  3. 磁盘空间:确保有足够空间存放输出文件
  4. 异常处理:添加try-catch处理IO和内存不足情况
  5. 性能优化:
  • 使用64位应用程序
  • 增加GC内存限制:<gcAllowVeryLargeObjects enabled="true"/>
  • 分批处理减少内存压力

网站公告

今日签到

点亮在社区的每一天
去签到