背景
我們有很多這樣的序列幀:
我這邊要把這些序列幀裁切最後合併成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");
gif合成方案1(SixLabors.ImageSharp)
這裏直接給出核心代碼
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
比較難解決了;
gif合成方案2(FFmpeg)--推薦
前面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#的話可能還是比較麻煩的,要去一個個圖片處理庫試了;