保存为 GIF 后图像质量下降

Images lose quality after saving as GIF

我正在开发一个 iOS 应用程序,允许用户拍摄一系列照片 - 然后将照片放入动画中并导出为 MP4 和 GIF。

虽然 MP4 呈现源质量,但 GIF 颜色等级可见。

这里是视觉比较:

动图:

MP4

我用于导出为 GIF 的代码:

var dictFile = new NSMutableDictionary();
        var gifDictionaryFile = new NSMutableDictionary();
        gifDictionaryFile.Add(ImageIO.CGImageProperties.GIFLoopCount, NSNumber.FromFloat(0));

        dictFile.Add(ImageIO.CGImageProperties.GIFDictionary, gifDictionaryFile);

        var dictFrame = new NSMutableDictionary();
        var gifDictionaryFrame = new NSMutableDictionary();
        gifDictionaryFrame.Add(ImageIO.CGImageProperties.GIFDelayTime, NSNumber.FromFloat(0f));

        dictFrame.Add(ImageIO.CGImageProperties.GIFDictionary, gifDictionaryFrame);


        InvokeOnMainThread(() =>
    {
        var imageDestination = CGImageDestination.Create(fileURL, MobileCoreServices.UTType.GIF, _images.Length);

        imageDestination.SetProperties(dictFile);

        for (int i = 0; i < this._images.Length; i++)
        {
            imageDestination.AddImage(this._images[i].CGImage, dictFrame);

        }
        imageDestination.Close();
    });

我用于导出为 MP4 的代码:

var videoSettings = new NSMutableDictionary();
            videoSettings.Add(AVVideo.CodecKey, AVVideo.CodecH264);
            videoSettings.Add(AVVideo.WidthKey, NSNumber.FromNFloat(images[0].Size.Width));
            videoSettings.Add(AVVideo.HeightKey, NSNumber.FromNFloat(images[0].Size.Height));

            var videoWriter = new AVAssetWriter(fileURL, AVFileType.Mpeg4, out nsError);
            var writerInput = new AVAssetWriterInput(AVMediaType.Video, new AVVideoSettingsCompressed(videoSettings));

            var sourcePixelBufferAttributes = new NSMutableDictionary();

            sourcePixelBufferAttributes.Add(CVPixelBuffer.PixelFormatTypeKey, NSNumber.FromInt32((int)CVPixelFormatType.CV32ARGB));

            var pixelBufferAdaptor = new AVAssetWriterInputPixelBufferAdaptor(writerInput, sourcePixelBufferAttributes);

            videoWriter.AddInput(writerInput);

            if (videoWriter.StartWriting())
            {
                videoWriter.StartSessionAtSourceTime(CMTime.Zero);

                for (int i = 0; i < images.Length; i++)
                {
                    while (true)
                    {
                        if (writerInput.ReadyForMoreMediaData)
                        {
                            var frameTime = new CMTime(1, 10);
                            var lastTime = new CMTime(1 * i, 10);

                            var presentTime = CMTime.Add(lastTime, frameTime);
                            var pixelBufferImage = PixelBufferFromCGImage(images[i].CGImage, pixelBufferAdaptor);

                            Console.WriteLine(pixelBufferAdaptor.AppendPixelBufferWithPresentationTime(pixelBufferImage, presentTime));
                            break;
                        }
                    }
                }

                writerInput.MarkAsFinished();
                await videoWriter.FinishWritingAsync();

非常感谢你的帮助!

亲切的问候,

安德烈

这只是我评论的总结...

我不在你的平台上编码,所以我只提供一般性答案(以及我自己的 GIF encoder/decoder 编码经验的见解)。

GIF 图像格式支持每个像素最多 8 位,使用原始编码后每个像素最多 256 种颜色。廉价的编码器只是将输入图像截断为 256 种或更少的颜色,通常会导致难看的像素化结果。要提高 GIF 的着色质量,我知道有 3 种方法:

  1. 用自己的调色板覆盖屏幕的多帧

    只需将图像分成多个叠加层,每个叠加层都有自己的调色板。这很慢(在解码热方面,因为您需要为每张图像处理更多帧,这可能会导致某些观众出现同步错误,并且您需要为每张图像多次处理所有与帧相关的块)。编码本身很快,因为您只需根据颜色将帧分开或 region/position 到多个帧。这里(基于region/position)示例:

    示例图片取自此处:Wiki

    GIF 支持透明度,因此子帧可以重叠... 这种方法在物理上将每个像素的颜色可能增加到 N*256(或透明框架 N*255),其中 N 是每张图像使用的框架或调色板的数量。

  2. 抖动

    抖动是一种近似区域颜色以尽可能匹配颜色的技术,同时仅使用指定颜色(来自调色板)。这是快速且易于实现的,但结果有点嘈杂。有关更多信息,请参阅我的一些相关答案:

    • c# image dithering routine that accepts an amount of dithering?
  3. 更好的颜色量化方法

    便宜的编码器只是将颜色截断为预定义的调色板。通过基于直方图对使用的颜色进行聚类可以获得更好的结果。例如见:

    • Effective gif/image color quantization?

    结果通常比抖动好得多,但与抖动相比编码时间很长...

#1#3 可以一起使用以进一步提高质量...

如果您无法访问编码代码或管道,您仍然可以在编码之前转换图像本身,而不是进行量化和调色板计算,并将结果直接加载到 GIF 编码器这应该是可能的(如果你使用的 GIF 编码器至少有点复杂......)