如果希望大幅提高图像保存速度,特别是在处理非常大的图像时,可以尝试以下更直接、更高效的方法:
1. 避免使用 Bitmap 类的 Save 方法
Bitmap.Save 方法的速度受限于 GDI+ 库的操作,尤其是对于非常大的图像,它可能会经历内存分配、像素格式转换等多重开销。我们可以通过直接操作图像数据流来绕过这些问题。
2. 直接将图像数据写入文件(原始像素数据)
对于大型图像,您可以直接将图像的像素数据转换为 BMP 格式的二进制流,并快速写入文件。BMP 格式是一个非常简单的无压缩格式,因此您可以手动构造 BMP 文件头和像素数据流。
下面是一个使用 原始图像数据流 写入RGB-BMP 文件的例子:
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
class Program
{
static void Main()
{
// 创建一个较大的 Bitmap 对象(例如 5000x5000)
Bitmap bitmap = new Bitmap(5000, 5000);
// 在这里填充图像,模拟数据
using (Graphics g = Graphics.FromImage(bitmap))
{
g.Clear(Color.Blue); // 蓝色背景
}
// 保存为 BMP 格式
SaveBitmapAsBmpFast(bitmap, "output.bmp");
}
static void SaveBitmapAsBmpFast(Bitmap bitmap, string filePath)
{
// 锁定位图数据
Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
BitmapData bitmapData = bitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat);
// 创建文件流
using (FileStream fs = new FileStream(filePath, FileMode.Create))
{
// 计算图像文件大小
int rowSize = bitmapData.Stride;
int dataSize = rowSize * bitmap.Height;
// BMP 文件头
byte[] fileHeader = new byte[14] {
0x42, 0x4D, // 'BM'标识符
0, 0, 0, 0, // 文件大小(稍后设置)
0, 0, // 保留字段
0, 0, // 保留字段
0x36, 0x00, 0x00, 0x00 // 位图数据的偏移量
};
fs.Write(fileHeader, 0, fileHeader.Length);
// 信息头
byte[] infoHeader = new byte[40] {
0x28, 0x00, 0x00, 0x00, // 信息头大小
(byte)(bitmap.Width & 0xFF), (byte)((bitmap.Width >> 8) & 0xFF), 0, 0, // 图像宽度
(byte)(bitmap.Height & 0xFF), (byte)((bitmap.Height >> 8) & 0xFF), 0, 0, // 图像高度
0x01, 0x00, // 颜色平面
0x18, 0x00, // 每像素位数(24位,RGB)
0x00, 0x00, 0x00, 0x00, // 压缩方法
0x00, 0x00, 0x00, 0x00, // 图像大小(0表示未压缩)
0x13, 0x0B, 0x00, 0x00, // 水平分辨率(假设为72 DPI)
0x13, 0x0B, 0x00, 0x00, // 垂直分辨率
0x00, 0x00, 0x00, 0x00, // 调色板数量(0表示不使用调色板)
0x00, 0x00, 0x00, 0x00 // 重要颜色数量
};
fs.Write(infoHeader, 0, infoHeader.Length);
// 写入像素数据
byte[] buffer = new byte[dataSize];
Marshal.Copy(bitmapData.Scan0, buffer, 0, buffer.Length);
fs.Write(buffer, 0, buffer.Length);
}
// 解锁位图数据
bitmap.UnlockBits(bitmapData);
Console.WriteLine("Image saved as BMP (fast).");
}
}
下面是一个使用 原始图像数据流 写入Gray-BMP 文件的例子:
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
class Program
{
static void Main()
{
// 创建一个灰度图像的 Bitmap 对象(例如 5000x5000)
Bitmap bitmap = new Bitmap(5000, 5000, PixelFormat.Format8bppIndexed);
// 创建一个调色板,包含256个灰度颜色
ColorPalette palette = bitmap.Palette;
for (int i = 0; i < 256; i++)
{
palette.Entries[i] = Color.FromArgb(i, i, i); // 设置灰度颜色
}
bitmap.Palette = palette;
// 填充灰度图像
using (Graphics g = Graphics.FromImage(bitmap))
{
for (int x = 0; x < bitmap.Width; x++)
{
for (int y = 0; y < bitmap.Height; y++)
{
// 创建一个灰度值(在此示例中,使用随机值)
int grayValue = (x + y) % 256; // 这里使用简单的算法产生灰度值,实际应用中可以按需要设置
bitmap.SetPixel(x, y, Color.FromArgb(grayValue, grayValue, grayValue));
}
}
}
// 保存为 BMP 格式
SaveBitmapAsBmpFast(bitmap, "output_gray_8bpp.bmp");
}
static void SaveBitmapAsBmpFast(Bitmap bitmap, string filePath)
{
// 锁定位图数据
Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
BitmapData bitmapData = bitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat);
// 创建文件流
using (FileStream fs = new FileStream(filePath, FileMode.Create))
{
// 计算图像文件大小
int rowSize = bitmapData.Stride;
int dataSize = rowSize * bitmap.Height;
// BMP 文件头
byte[] fileHeader = new byte[14] {
0x42, 0x4D, // 'BM'标识符
0, 0, 0, 0, // 文件大小(稍后设置)
0, 0, // 保留字段
0, 0, // 保留字段
0x36, 0x00, 0x00, 0x00 // 位图数据的偏移量
};
fs.Write(fileHeader, 0, fileHeader.Length);
// 信息头
byte[] infoHeader = new byte[40] {
0x28, 0x00, 0x00, 0x00, // 信息头大小
(byte)(bitmap.Width & 0xFF), (byte)((bitmap.Width >> 8) & 0xFF), 0, 0, // 图像宽度
(byte)(bitmap.Height & 0xFF), (byte)((bitmap.Height >> 8) & 0xFF), 0, 0, // 图像高度
0x01, 0x00, // 颜色平面
0x08, 0x00, // 每像素位数(8位灰度图)
0x00, 0x00, 0x00, 0x00, // 压缩方法
0x00, 0x00, 0x00, 0x00, // 图像大小(0表示未压缩)
0x13, 0x0B, 0x00, 0x00, // 水平分辨率(假设为72 DPI)
0x13, 0x0B, 0x00, 0x00, // 垂直分辨率
0x00, 0x00, 0x00, 0x00, // 调色板数量(0表示不使用调色板)
0x00, 0x00, 0x00, 0x00 // 重要颜色数量
};
fs.Write(infoHeader, 0, infoHeader.Length);
// 调色板(256个灰度颜色)
byte[] colorTable = new byte[1024]; // 256个条目,每个条目4字节(ARGB)
for (int i = 0; i < 256; i++)
{
colorTable[i * 4] = (byte)i; // R
colorTable[i * 4 + 1] = (byte)i; // G
colorTable[i * 4 + 2] = (byte)i; // B
colorTable[i * 4 + 3] = 0x00; // A (透明度)
}
fs.Write(colorTable, 0, colorTable.Length);
// 写入像素数据
byte[] buffer = new byte[dataSize];
Marshal.Copy(bitmapData.Scan0, buffer, 0, buffer.Length);
fs.Write(buffer, 0, buffer.Length);
}
// 解锁位图数据
bitmap.UnlockBits(bitmapData);
Console.WriteLine("Grayscale image (8bpp) saved as BMP (fast).");
}
}