我们有很多这样的序列帧:
我这边要把这些序列帧裁切最后合并成gif,以下是我裁切后的png文件:
我一开始选用的是 SixLabors.ImageSharp
这是裁切代码:
using var image = Image.Load("input.jpg");
image.Clone(x => x.Crop(new Rectangle(10, 10, 250, 250)));
image.Save("output.jpg");
这里直接给出核心代码
public string FrameCombine(List<FrameConfig> frames, int fps)
{
Image firstFrame = null;
var delay = 100 / fps; //根据帧率技术延迟
GifDisposalMethod disposalMethod = GifDisposalMethod.RestoreToBackground; //背景处理方式
string outputPath = null;
for (int i = 0; i < frames.Count; i++)
{
Image tempImage = Image.Load(frames[i].Path);
if (i == 0) //第一帧做底图
{
outputPath = Path.Combine(Path.GetDirectoryName(frames[i].Path), "sticker.gif");
if (File.Exists(outputPath))
{
return null;
}
firstFrame = tempImage;
firstFrame.Frames.RootFrame.Metadata.GetGifMetadata().FrameDelay = delay;
firstFrame.Metadata.GetGifMetadata().RepeatCount = 0;
}
else
{
//把其他帧合到第一帧上
firstFrame.Frames.AddFrame(tempImage.Frames.RootFrame);
var meta = firstFrame.Frames[i].Metadata.GetGifMetadata();
meta.FrameDelay = delay;
meta.DisposalMethod = disposalMethod;
}
}
firstFrame.SaveAsGif(outputPath);
return outputPath;
}
最后合成效果(都多多少少有点问题)
大致显示正常(但锯齿和毛边严重)
还有这样的(带莫名的绿色噪点/绿底等):
这样的(莫名灰底):
试了很多方方法,想尽办法调各种属性都不行,看来用SixLabors.ImageSharp
比较难解决了;
前面SixLabors.ImageSharp
方案生成的gif太多问题了,最终是用FFmpeg重新合成才实现的。
步骤
首先,为所有图片生成一个统一的调色板:
ffmpeg -i %02d.png -vf "palettegen" palette.png
然后,使用这个调色板的颜色为基础来生成GIF:
ffmpeg -r 16 -i %02d.png -i palette.png -lavfi paletteuse sticker.gif
-r 16 :帧率
-i palette.png :是用于为GIF提供颜色调色板的图像。
-lavfi paletteuse:这是一个复杂的滤镜图描述,指示ffmpeg如何处理输入内容。paletteuse是一个特定的滤镜,它使用前面的name.png输入作为源来生成一个调色板,并使用这个调色板来处理其他输入(在本例中即img_%d.png匹配到的文件)。
将这两条命令合成一条
ffmpeg -r 16 -i %02d.png -filter_complex "palettegen=stats_mode=single[pal],[0:v][pal]paletteuse" sticker.gif
C#写法(用了这个执行控制台命令的nuget CliWrap)
var workDir = Path.GetDirectoryName(frames[0].Path);
var outputPath = Path.Combine(workDir, "sticker.gif");
var param = $" -r {fps} -i %02d.png -filter_complex \"palettegen=stats_mode=single[pal],[0:v][pal]paletteuse\" {outputPath} -y";
try
{
var result = await Cli.Wrap("ffmpeg").WithArguments(param).WithWorkingDirectory(workDir).ExecuteBufferedAsync();
if (result.ExitCode == 0)
{
return outputPath;
}
return outputPath;
}
catch (Exception ex)
{
logger.LogError(ex, "FrameCombine2 failed:{0}", frames[0]?.Path);
}
最后展示效果
有的时候其实是比较简单的问题,但如果思路限制在C#的话可能还是比较麻烦的,要去一个个图片处理库试了;