我正在编写对快速图像处理有严格要求的应用程序。将会有大量的图像。我需要将它们保存为可快速处理的格式。因此,我决定保存具有8位色深的彩色贴图(使用调色板),然后将其保存为字节数组。因此,我需要一种非常快速且在内存中转换格式的方法。另外,它必须尽可能地跨平台(我在Windows上编写代码并将部署到AWS)。
我尝试了ImageSharp和.NET Core System.Drawing.Common软件包。在使用ImageSharp的情况下,我得到的结果几乎可以接受:调整大小的图像的质量是完美的,但是效果确实很慢...对于4096x4096 24-bit full palette image,我得到了12秒。生成的调色板中有256种不同的颜色,这对我来说很完美。 对于System.Drawing.Common库,我的转换速度要快得多:
但是,结果调色板中不同颜色的数量:
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实现,但是可能会有与像素读取慢有关的开销。 因此,问题:
答案 0 :(得分:0)
尝试使用Accord
。很难说是否很快。您需要在您的场景中尝试一下。
您要有8bpp图像吗?所以你想成为GreyScale吗?然后,从using Accord.Imaging.Filters;
使用此功能。
private Bitmap Create8bppGreyscaleImage(Bitmap bitmap)
=> Grayscale.CommonAlgorithms.BT709.Apply(bitmap);
此This jpg图像上的转换采用500ms
。产生的图像如下所示:
如您所见,Grayscale.CommonAlghoritms.BT709
以Bitmap
作为参数。如果要字节数组,只需使用Marshall.Copy
:
BitmapData bitmapData = this.Bitmap.LockBits(
new Rectangle(0, 0, this.Bitmap.Width, this.Bitmap.Height),
ImageLockMode.ReadWrite,
this.Bitmap.PixelFormat);
int pixels = bitmapData.Stride * this.Bitmap.Height;
byte[] bytes = new byte[pixels];
Marshal.Copy(bitmapData.Scan0, bytes, 0, pixels);
this.Bitmap.UnlockBits(bitmapData);
this.Bytes = bytes;
通过此方法将图像加载为8bpp字节数组(上图)为18ms
。
它将字节数组保存到内存中。然后,您可以将位解锁到位图并将其保存为例如.png
:
BitmapData bitmapData = this.Bitmap.LockBits(
new Rectangle(0, 0, this.Bitmap.Width, this.Bitmap.Height),
ImageLockMode.ReadWrite,
this.Bitmap.PixelFormat);
int stride = bitmapData.Stride * this.Height;
Marshal.Copy(bytes, 0, bitmapData.Scan0, stride);
this.Bitmap.UnlockBits(bitmapData);
this.Bitmap.Save("test.png");
如果我的回答对您有帮助,请告诉我。 .NET Core中没有开箱即用的引用System.Drawing.dll
,您需要自己添加