保存为 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 种方法:
用自己的调色板覆盖屏幕的多帧
只需将图像分成多个叠加层,每个叠加层都有自己的调色板。这很慢(在解码热方面,因为您需要为每张图像处理更多帧,这可能会导致某些观众出现同步错误,并且您需要为每张图像多次处理所有与帧相关的块)。编码本身很快,因为您只需根据颜色将帧分开或 region/position 到多个帧。这里(基于region/position)示例:
示例图片取自此处:Wiki
GIF 支持透明度,因此子帧可以重叠... 这种方法在物理上将每个像素的颜色可能增加到 N*256
(或透明框架 N*255
),其中 N
是每张图像使用的框架或调色板的数量。
抖动
抖动是一种近似区域颜色以尽可能匹配颜色的技术,同时仅使用指定颜色(来自调色板)。这是快速且易于实现的,但结果有点嘈杂。有关更多信息,请参阅我的一些相关答案:
- c# image dithering routine that accepts an amount of dithering?
更好的颜色量化方法
便宜的编码器只是将颜色截断为预定义的调色板。通过基于直方图对使用的颜色进行聚类可以获得更好的结果。例如见:
- Effective gif/image color quantization?
结果通常比抖动好得多,但与抖动相比编码时间很长...
#1 和 #3 可以一起使用以进一步提高质量...
如果您无法访问编码代码或管道,您仍然可以在编码之前转换图像本身,而不是进行量化和调色板计算,并将结果直接加载到 GIF 编码器这应该是可能的(如果你使用的 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 种方法:
用自己的调色板覆盖屏幕的多帧
只需将图像分成多个叠加层,每个叠加层都有自己的调色板。这很慢(在解码热方面,因为您需要为每张图像处理更多帧,这可能会导致某些观众出现同步错误,并且您需要为每张图像多次处理所有与帧相关的块)。编码本身很快,因为您只需根据颜色将帧分开或 region/position 到多个帧。这里(基于region/position)示例:
示例图片取自此处:Wiki
GIF 支持透明度,因此子帧可以重叠... 这种方法在物理上将每个像素的颜色可能增加到
N*256
(或透明框架N*255
),其中N
是每张图像使用的框架或调色板的数量。抖动
抖动是一种近似区域颜色以尽可能匹配颜色的技术,同时仅使用指定颜色(来自调色板)。这是快速且易于实现的,但结果有点嘈杂。有关更多信息,请参阅我的一些相关答案:
- c# image dithering routine that accepts an amount of dithering?
更好的颜色量化方法
便宜的编码器只是将颜色截断为预定义的调色板。通过基于直方图对使用的颜色进行聚类可以获得更好的结果。例如见:
- Effective gif/image color quantization?
结果通常比抖动好得多,但与抖动相比编码时间很长...
#1 和 #3 可以一起使用以进一步提高质量...
如果您无法访问编码代码或管道,您仍然可以在编码之前转换图像本身,而不是进行量化和调色板计算,并将结果直接加载到 GIF 编码器这应该是可能的(如果你使用的 GIF 编码器至少有点复杂......)