【发布时间】:2019-04-08 23:57:30
【问题描述】:
我正在编写一个对快速图像处理有关键要求的应用程序。会有大量的图像。我需要以可快速处理的格式保存它们。所以,我决定保存 colorful 具有 8 位颜色深度的 mipmap(使用调色板),然后将其保存为 byte 数组。所以,我需要一种在内存中快速转换格式的方法。此外,它必须尽可能跨平台(我在 Windows 上编写代码并将部署到 AWS)。
我已经尝试过ImageSharp 和.NET Core System.Drawing.Common 包。在 ImageSharp 的情况下,我得到了几乎可以接受的结果:调整大小的图像的质量是完美的,但它的工作速度真的很慢......我得到了 12 秒 4096x4096 24-bit full palette image。生成的调色板中有 256 种不同的颜色,这对我来说是完美的。 在 System.Drawing.Common 库的情况下,我得到了更快的转换速度:
- tiff 格式为 900 毫秒
- gif 格式为 2500 毫秒。
但是,结果调色板中不同颜色的数量:
- 对于 tiff:224(对我的解决方案不太好)
- 对于 gif:252(好) 此外,生成的 tiff 格式的图像质量非常很差,因此可能无法使用(也许我错了)。
System.Drawing.Common 实现:
var parameters = new EncoderParameters(1);
parameters.Param[0] = new EncoderParameter(Encoder.ColorDepth, 8L);
ImageCodecInfo bmpEncoder = ImageCodecInfo.GetImageEncoders()
.FirstOrDefault(x => x.FormatID == ImageFormat.Gif.Guid);
Stopwatch sw = Stopwatch.StartNew();
var allColors = new Bitmap(@"16777216colors.png");
Console.WriteLine("Loaded: " + sw.ElapsedMilliseconds); // 462
using (MemoryStream ms = new MemoryStream())
{
allColors.Save(ms, bmpEncoder, parameters);
Console.WriteLine("Saved: " + sw.ElapsedMilliseconds); // 2104
ms.Position = 0;
Bitmap bitmap = new Bitmap(ms);
Console.WriteLine("Loaded: " + sw.ElapsedMilliseconds); // 2383
// set to determine palette size
HashSet<int> set = new HashSet<int>();
// Temporary solution. It is needed to replace GetPixel to faster analog
for (int i = 0; i < bitmap.Width; i++)
{
for (int j = 0; j < bitmap.Height; j++)
{
var val = bitmap.GetPixel(i, j).ToArgb();
set.Add(val);
}
}
Console.WriteLine("Palette size: " + set.Count); // 252
Console.WriteLine("All done: " + sw.ElapsedMilliseconds); // 26345
}
/*
Output:
Loaded: 462
Saved: 2104
Loaded: 2383
Palette size: 252
All done: 26345
*/
ImageSharp 实现:
PngEncoder encoder = new PngEncoder()
{
ColorType = PngColorType.Palette,
BitDepth = PngBitDepth.Bit8,
CompressionLevel = 1
};
Image<Rgba32> img;
Stopwatch sw = Stopwatch.StartNew();
using (var file = File.OpenRead(@"16777216colors.png"))
{
img = Image.Load(file);
}
Console.WriteLine("Loaded: " + sw.ElapsedMilliseconds); // 1302
using (MemoryStream ms = new MemoryStream())
{
img.Save(ms, encoder);
Console.WriteLine("Saved: " + sw.ElapsedMilliseconds); // 12608
ms.Position = 0;
img = Image.Load(ms);
Console.WriteLine("Loaded: " + sw.ElapsedMilliseconds); // 12813
}
// set to determine palette size
HashSet<uint> set = new HashSet<uint>();
for (int i = 0; i < img.Width; i++)
{
for (int j = 0; j < img.Height; j++)
{
var val = img[i, j].PackedValue;
set.Add(val);
}
}
Console.WriteLine("Palette size: " + set.Count); // 256
Console.WriteLine("All done: " + sw.ElapsedMilliseconds); // 15807
/*
Output:
Loaded: 1302
Saved: 12608
Loaded: 12813
Palette size: 256
All done: 15807
*/
因此,最好的方法似乎是使用 System.Drawing.Common 实现,但可能会有与缓慢像素读取相关的开销。 那么,问题:
- 我希望 ImageSharp 具有高性能,所以我想我在做 出了点问题...知道如何提高性能会很酷!如果有人知道我做错了什么,请告诉我。
- 是否保证调色板颜色将是 任何图像都一样,所以,我将能够缓存这 256 种颜色之间的任意色差?
- 有没有人知道以其他方式解决我的问题的最佳方法?我很高兴看到建议。解决方案必须是跨平台且速度尽可能快的字节数组数据表示
【问题讨论】:
-
听起来这属于代码审查
-
为什么不使用 .bmp 文件格式?它处理您的颜色域,并且可能在 Windows 中内置了更快的转换器(不确定)。
-
superuser.com/questions/1101160/… - 还分享了一个可能的库来尝试。
-
诚实的问题:如果您需要所有这些速度和跨平台,您为什么使用 C#?再给你一个:social.msdn.microsoft.com/Forums/vstudio/en-US/…
-
如果您想使用 .Net GDI 类处理位图,那么您应该考虑使用 Bitmap.LockBits Method 及其示例代码来检索像素数据。
标签: c# image-processing .net-core