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# 实例——绘制波浪线(附源码)

    c# 实例——绘制波浪线(附源码)

    这篇文章主要介绍了c#如何绘制波浪线,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-07-07
  • C# 类库打包dll文件的操作流程

    C# 类库打包dll文件的操作流程

    在C#中,有多种方式可以对代码进行加密,以保护源代码不被轻易查看或修改,这篇文章主要介绍将C# cs类文件加密为dll文件的方式进行保护,感兴趣的朋友一起看看吧
    2025-03-03
  • C# Socket文件上传至服务器的操作方法

    C# Socket文件上传至服务器的操作方法

    文件上传有FTP、WebApi、WebService等等,这次我们来实现一个基于socket通信的本地客户端上传文件到服务器的例子,这篇文章主要介绍了C# Socket文件上传至服务器的操作方法,需要的朋友可以参考下
    2024-05-05
  • C# WPF自制白板工具

    C# WPF自制白板工具

    着电子屏幕技术的发展,普通的黑板已不再适用现在的教学和演示环境,电子白板应运而生,本文将通过C# WPF自制一个白板工具,感兴趣的可以了解下
    2024-11-11
  • 一文搞懂C#实现读写文本文件中的数据

    一文搞懂C#实现读写文本文件中的数据

    这篇文章重点给大家介绍C#实现读写文本文件中的数据的一些知识,读取.txt文件数据的实例代码及写入读取过程完整代码,感兴趣的朋友跟随小编一起看看吧
    2021-06-06
  • C#闪退问题的排查全攻略

    C#闪退问题的排查全攻略

    作为 C# 开发者,最令人头疼的莫过于程序在没有任何报错提示的情况下瞬间闪退,本文将带你从开发环境到生产环境,由浅入深地掌握排查 C# 闪退问题的四大绝招,需要的朋友可以参考下
    2025-12-12
  • C#使用Linq to XML进行XPath查询的代码实现

    C#使用Linq to XML进行XPath查询的代码实现

    最近在用到HtmlAgliltyPack进行结点查询时,发现这里选择结点使用的是XPath,所以这里总结一下在C#中使用XPath查询XML的方法,习惯了用Linq,这里也是用的Linq to xml的,需要的朋友可以参考下
    2024-08-08
  • 使用C#进行音频处理的完整指南(从播放到编辑)

    使用C#进行音频处理的完整指南(从播放到编辑)

    在现代应用程序中,音频处理已经成为不可或缺的一部分,无论是开发一个简单的音频播放器,还是构建一个复杂的音频编辑工具,C#都提供了丰富的工具和库来实现这些功能,通过本文,我们将深入探索如何在C#中进行音频播放、录制、编辑、格式转换以及音频分析
    2025-04-04
  • 浅谈c# 浮点数计算

    浅谈c# 浮点数计算

    本文通过具体的示例给大家演示了下C#中浮点数运算所遇到的问题及解决方法,有需要的小伙伴可以参考下
    2017-09-09
  • 详解如何实现C#和Python间实时视频数据交互

    详解如何实现C#和Python间实时视频数据交互

    我们在做RTSP|RTMP播放的时候,遇到好多开发者,他们的视觉算法大多运行在python下,需要高效率的实现C#和Python的视频数据交互,本文给大家总结了一些常用的方法,感兴趣的小伙伴跟着小编一起来看看吧
    2024-10-10

最新评论