C# .net core中如何將多張png圖片合併成一個gif

背景

我們有很多這樣的序列幀:

image-20230727114152382

我這邊要把這些序列幀裁切最後合併成gif,以下是我裁切後的png文件:

image-20230727115247029

我一開始選用的是 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;
}

最後合成效果(都多多少少有點問題)

大致顯示正常(但鋸齒和毛邊嚴重)

sticker

還有這樣的(帶莫名的綠色噪點/綠底等):

sticker

sticker

這樣的(莫名灰底):

sticker

試了很多方方法,想盡辦法調各種屬性都不行,看來用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);
}

最後展示效果

sticker

sticker

sticker

總結

有的時候其實是比較簡單的問題,但如果思路限制在C#的話可能還是比較麻煩的,要去一個個圖片處理庫試了;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章