背景

我们有很多这样的序列帧:

我这边要把这些序列帧裁切最后合并成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#的话可能还是比较麻烦的,要去一个个图片处理库试了;

C# .net core中如何将多张png图片合并成一个gif的更多相关文章

  1. ros中同时订阅两个topic(2张图像)合并成一个topic(1张图像)

    2019-12-06 15:42:39 先暂时做个资料保存 要同时用两个红外相机,但是没有做硬件上的 时间戳同步,就是笔记本上同时插着两个相机. 两个topic发布各自相机的图像,然后要有个节点同时订 ...

  2. mysql中的多行查询结果合并成一个

    SELECT GROUP_CONCAT(md.data1) FROM DATA md,contacts cc WHERE md.conskey=cc.id AND md.mimetype_id= 5 ...

  3. gulp-css-spriter 将css代码中的切片图片合并成雪碧图

    NPM地址:https://www.npmjs.com/package/gulp-css-spriter/ 配置gulpfile.js: var gulp = require('gulp'),     ...

  4. MATLAB读取一张RGB图片转成YUV格式

    1.读入照片 控制输出的标志定义 clc;close all;clear YES = 1; NO = 0; %YES表示输出该文件,请用户配置 yuv444_out_txt = 1; yuv444_o ...

  5. php将两张身份证图片合并到一张图

    /** * @desc 合并身份证的正反面到同一张图片 * @author Jimmy * @date 2016-12-33 * @param $imageSrc0 身份证正面 * @param $i ...

  6. python中如何将两个list合并成一个list,不用for语句

    1, add 2, 用list的extend方法,L1.extend(L2),该方法将参数L2的全部元素添加到L1的尾部,例如: 3, 用切片(slice)操作,L1[len(L1):len(L1)] ...

  7. .NET Core 中的日志与分布式链路追踪

    目录 .NET Core 中的日志与分布式链路追踪 .NET Core 中的日志 控制台输出 非侵入式日志 Microsoft.Extensions.Logging ILoggerFactory IL ...

  8. .NET Core中的数据保护组件

    原文地址: PREVENTING INSECURE OBJECT REFERENCES IN ASP.NET CORE 2.0 作者: Tahir Naushad 背景介绍 在 OWASP(开放式 W ...

  9. .net core中的对象池

    asp.net core中通过扩展库的方式提供给了一个标准的对象池ObjectPool,定义在Microsoft.Extensions.ObjectPool.dll 程序集中.它本身是个纯虚的抽象类, ...

  10. 在asp.net core中使用托管服务实现后台任务

    在业务场景中经常需要后台服务不停的或定时处理一些任务,这些任务是不需要及时响应请求的. 在 asp.net中会使用windows服务来处理. 在 asp.net core中,可以使用托管服务来实现,托 ...

随机推荐

  1. VUE+element页面按钮调用dialog

    VUE+element通过按钮调用普通弹框(弹框页面独立出一个dialog页面,非在同一个页面文件里) 代码如下 <el-dialog> <el-button type=" ...

  2. 2024-05-04:用go语言,给定一个起始索引为0的字符串s和一个整数k。 要进行分割操作,直到字符串s为空: 选择s的最长前缀,该前缀最多包含k个不同字符; 删除该前缀,递增分割计数。如果有剩余

    2024-05-04:用go语言,给定一个起始索引为0的字符串s和一个整数k. 要进行分割操作,直到字符串s为空: 选择s的最长前缀,该前缀最多包含k个不同字符: 删除该前缀,递增分割计数.如果有剩余 ...

  3. 【爬虫案例】用Python爬取百度热搜榜数据!

    目录 一.爬取目标 二.编写爬虫代码 三.同步视频讲解 四.完整源码 一.爬取目标 您好,我是@马哥python说,一名10年程序猿. 本次爬取的目标是:百度热搜榜 分别爬取每条热搜的: 热搜标题.热 ...

  4. GitHub总是打不开

    终极解决方案 http://tool.chinaz.com/dns?type=1&host=github.com&ip= github.com vim /etc/hosts 添加下面内 ...

  5. 在线自动加字幕!超快完成Pr的字幕制作!

    在线自动加字幕!超快完成Pr的字幕制作! https://www.bilibili.com/video/BV1T441167eX/?spm_id_from=333.788.videocard.3 ht ...

  6. 【源码研读】MLIR Dialect 分层设计

    以「疑问 - 求解」的形式来组织调研,此处记录整个过程. 1. MLIR 中的 Dialect 是「分层」设计的么? 先问是不是,再谈为什么.从 LLVM 社区 可以看出,至少在做 Codegen 时 ...

  7. ARM64: ARDP

    1 指令语法 ardp <Xd>, <lable> 2 指令语义 1 获取程序计数器PC寄存器的值: 2 将PC寄存器值的低12位全部取0; 3 将lable的值乘以4096, ...

  8. Oracle数据库WHERE子查询按时间段查询用法(to_date函数)

    oracle中的to_date参数含义 1.日期格式参数 含义说明 D 一周中的星期几  DAY 天的名字,使用空格填充到9个字符  DD 月中的第几天  DDD 年中的第几天  DY 天的简写名   ...

  9. apisix~jwt-auth插件

    在网关开启jwt-auth插件之后,你的网关就具有了jwt解析和校验的功能,主要是校验jwt token的有效性,包含过期时间和签名等. https://apisix.apache.org/docs/ ...

  10. springboot项目启动会报4个加载不到的debug提示,可改可不改

    1. 因为启动的时候会报提示: Unable to locate LocaleResolver with name 'localeResolver': using default [org.sprin ...