SKBitmap图像的快速无损编码



我试图以无损压缩尽可能快地存储大型4096x3072SKBitmap图像。我试过使用SKImage.FromBitmap(bitmap).Encode(SKEncodedImageFormat.Png, 100)将它们存储为PNG,但这真的很慢。然后使用这个问题和这个示例代码中的信息,我制作了一个方法来将它们存储为Tiff图像,这要快得多,但对于我的目的来说仍然不够快。代码必须在Linux上工作也这是我当前的代码:

public static class SKBitmapExtensions
{
public static void SaveToPng(this SKBitmap bitmap, string filename)
{
using (Stream s = File.OpenWrite(filename))
{
SKData d = SKImage.FromBitmap(bitmap).Encode(SKEncodedImageFormat.Png, 100);
d.SaveTo(s);
}
}
public static void SaveToTiff(this SKBitmap img, string filename)
{
using (var tifImg = Tiff.Open(filename, "w"))
{
// Set the tiff information
tifImg.SetField(TiffTag.IMAGEWIDTH, img.Width);
tifImg.SetField(TiffTag.IMAGELENGTH, img.Height);
tifImg.SetField(TiffTag.COMPRESSION, Compression.LZW);
tifImg.SetField(TiffTag.PHOTOMETRIC, Photometric.RGB);
tifImg.SetField(TiffTag.ROWSPERSTRIP, img.Height);
tifImg.SetField(TiffTag.BITSPERSAMPLE, 8);
tifImg.SetField(TiffTag.SAMPLESPERPIXEL, 4);
tifImg.SetField(TiffTag.XRESOLUTION, 1);
tifImg.SetField(TiffTag.YRESOLUTION, 1);
tifImg.SetField(TiffTag.PLANARCONFIG, PlanarConfig.CONTIG);
tifImg.SetField(TiffTag.EXTRASAMPLES, 1, new short[] { (short)ExtraSample.UNASSALPHA });
// Copy the data
byte[] bytes = img.Bytes;
// Swap red and blue
convertSamples(bytes, img.Width, img.Height);
// Write the image into the memory buffer
for (int i = 0; i < img.Height; i++)
tifImg.WriteScanline(bytes, i * img.RowBytes, i, 0);
}
}
private static void convertSamples(byte[] data, int width, int height)
{
int stride = data.Length / height;
const int samplesPerPixel = 4;
for (int y = 0; y < height; y++)
{
int offset = stride * y;
int strideEnd = offset + width * samplesPerPixel;
for (int i = offset; i < strideEnd; i += samplesPerPixel)
{
byte temp = data[i + 2];
data[i + 2] = data[i];
data[i] = temp;
}
}
}
}

和测试代码:

SKBitmap bitmap = SKBitmap.Decode("test.jpg");
Stopwatch stopwatch = new();
stopwatch.Start();
int iterations = 20;
for (int i = 0; i < iterations; i++)
bitmap.SaveToTiff("encoded.tiff");
stopwatch.Stop();
Console.WriteLine($"Average Tiff encoding time for a {bitmap.Width}x{bitmap.Height} image = {stopwatch.ElapsedMilliseconds / iterations} ms");
stopwatch.Restart();
for (int i = 0; i < iterations; i++)
bitmap.SaveToPng("encoded.png");
stopwatch.Stop();
Console.WriteLine($"Average PNG encoding time for a {bitmap.Width}x{bitmap.Height} image = {stopwatch.ElapsedMilliseconds / iterations} ms");

结果是:

Average Tiff encoding time for a 4096x3072 image = 630 ms
Average PNG encoding time for a 4096x3072 image = 3092 ms

是否有更快的方法来存储这些图像?我可以想象,我可以避免在var bytes = img.Bytes复制数据,但我不确定如何。PNG的编码文件大小为10.3MBTiff26MB现在。

如果您对制作最优的png(从文件大小的角度来看)不那么感兴趣,那么您可以通过以下方式访问一些更快的编码选项:

SKBitmap bitmap = SKBitmap.Decode("test.jpg");
using(var pixmap= bitmap.PeekPixels())
{
var filters = SKPngEncoderFilterFlags.NoFilters;
int compress = 0;
var options = new SKPngEncoderOptions(filters, compress);
using (var data = pixmap.Encode(options))
{
byte[] bytes = data.ToArray();
// use data - write bytes to file etc
}
}

在上面的例子中:

  • compress = 0将不使用zlib压缩,因此png的大小将有效地与未压缩的TIFF相似。你可以尝试更高的压缩值(我认为9是最大的,但最慢)。
  • filters = SKPngEncoderFilterFlags。在所有场景下,NoFilters都是最快的。它可以使使用compress != 0生成的文件的文件大小变大。过滤器选项用于尝试使用SKPngEncoderFilterFlags来提高文件的可压缩性。

相关内容

  • 没有找到相关文章

最新更新