保存为 GIF 后图像质量下降

And*_*re 4 image-processing gif ios swift

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

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

这是视觉比较:

动图:动图

MP4MP4

我用于导出为 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();
    });
Run Code Online (Sandbox Code Playgroud)

我用于导出为 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();
Run Code Online (Sandbox Code Playgroud)

我将不胜感激您的帮助!

亲切的问候,

安德烈

Spe*_*tre 6

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

我不在你们的平台上编码,所以我只提供通用答案(以及我自己的GIF编码器/解码器编码经验的见解)。

GIF图像格式支持每像素高达 8 位,从而通过简单编码实现每像素最多 256 种颜色。廉价的编码器只是将输入图像截断为 256 种或更少的颜色,通常会导致丑陋的像素化结果。为了提高GIF的着色质量,我知道有 3 种方法:

  1. 多个框架覆盖屏幕并带有自己的调色板

    只需将图像划分为多个叠加层,每个叠加层都有自己的调色板。这是很慢的(就解码而言,因为您需要为每个图像处理更多帧,这可能会导致某些查看器出现同步错误,并且您需要为每个图像多次处理所有与帧相关的块)。编码本身很快,因为您只需根据颜色或区域/位置将帧分离为多个帧。这里(基于区域/位置)示例:

    多个调色板

    示例图像取自此处:Wiki

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

  2. 抖动

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

  3. 更好的色彩量化方法

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

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

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

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