C#实现将超大图片(1GB)裁剪为8张小图片

 更新时间:2025年05月18日 10:06:32   作者:MartinYangHJ  
C#处理超大图片(1GB)需要特别注意内存管理和性能优化,这篇文章为大家整理了4种常见的处理方法,并进行了对比,大家可以根据自己的需要进行选择

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"/>
  • 分批处理减少内存压力

到此这篇关于C#实现将超大图片(1GB)裁剪为8张小图片的文章就介绍到这了,更多相关C#图片裁剪内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • c#读取excel内容内容示例分享

    c#读取excel内容内容示例分享

    这篇文章主要介绍了c#读取excel内容内容示例,要求Excel需是.xls格式,需要的朋友可以参考下
    2014-03-03
  • C#/VB.NET 给Excel添加、删除数字签名的方法

    C#/VB.NET 给Excel添加、删除数字签名的方法

    这篇文章主要介绍了C#/VB.NET 给Excel添加、删除数字签名的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11
  • C#多线程的ResetAbort()方法

    C#多线程的ResetAbort()方法

    这篇文章介绍了C#多线程的ResetAbort()方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • C#中用管理员身份运行程序代码实例

    C#中用管理员身份运行程序代码实例

    这篇文章主要介绍了C#中用管理员身份运行程序代码实例,本文直接给出实现代码,需要的朋友可以参考下
    2015-02-02
  • 详解C#泛型的类型参数约束

    详解C#泛型的类型参数约束

    这篇文章主要介绍了C#泛型的类型参数约束的相关资料,文中讲解非常细致,帮助大家更好的理解和学习c#,感兴趣的朋友可以了解下
    2020-07-07
  • Unity Shader实现3D翻页效果

    Unity Shader实现3D翻页效果

    这篇文章主要为大家详细介绍了Unity Shader实现3D翻页效果,Plane实现翻页效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • 基于WPF平台使用纯C#实现动态处理json字符串

    基于WPF平台使用纯C#实现动态处理json字符串

    在当今的软件开发领域,数据的交换与存储变得愈发频繁,JSON作为一种轻量级的数据交换格式,在 WPF平台开发的桌面应用里,我们常常需要与各种数据源交互,动态处理JSON字符串就成为了一项必备技能,本文将深入探讨如何在 WPF 平台上,仅使用纯C#代码实现对JSON字符串的动态处理
    2025-01-01
  • c# EnumHelper枚举常用操作类

    c# EnumHelper枚举常用操作类

    在项目中需要把枚举填充到下拉框中,所以使用统一的方法实现,测试代码如下,需要的朋友可以参考下
    2016-11-11
  • 一文带你搞懂什么是WPF中的依赖属性

    一文带你搞懂什么是WPF中的依赖属性

    依赖属性是 WPF 的一个核心概念,它为传统的 .NET 属性提供了增强功能,支持绑定、样式、动画和默认值等功能,下面我们就来看看WPF依赖属性的具体应用吧
    2024-11-11
  • C#中的DataSet、string、DataTable、对象转换成Json的实现代码

    C#中的DataSet、string、DataTable、对象转换成Json的实现代码

    这篇文章主要介绍了C#中的DataSet、string、DataTable、对象转换成Json的实现代码,需要的朋友可以参考下
    2014-09-09

最新评论