webp动图转gif

目录

前言

解决过程

遇到问题

获取duration


前言

上一次我们实现了webp转jpg格式:

https://blog.csdn.net/weixin_54143563/article/details/139758200

那么对于含动图的webp文件我们如何将其转为gif文件呢?

之所以会出现这个问题,是因为我很喜欢芙芙,逛b站的时候发现了一个芙芙的表情包:

https://i0.hdslb.com/bfs/new_dyn/e541e5ee95aaee7ce655f0074d50e5b8180207493.gif@1052w_!web-dynamic.avif

打开链接下载后发现是webp格式的,尽管在“另存为”的时候,将保存类型选为“所有文件”,然后文件名加.gif后缀可以下载为gif格式

但是打开这个文件却并不是动图。

由此我想要思考怎么才能将webp文件转为gif呢?(虽然在线格式转换的工具有很多,但是为什么不尝试自己动手解决呢?)

解决过程

选择imageio库来完成格式转化。

逻辑流程为:使用imageio.get_reader读取webp文件,然后读取每一帧图像,最后将这些图像写入gif文件中,代码如下:
 

import imageio

# 指定输入的webp文件路径
input_path = '芙宁娜.webp'
# 指定输出的gif文件路径
output_path = 'output.gif'

# 使用imageio读取webp文件
webp_reader = imageio.get_reader(input_path)

# 初始化一个空的gif列表,用于存储每一帧
gif_frames = []

# 读取每一帧图像
for i, frame in enumerate(webp_reader):
    gif_frames.append(frame)


# 关闭reader
webp_reader.close()

# 使用imageio写入gif文件,loop=0表示无限循环播放
imageio.mimsave(output_path, gif_frames, format='GIF',loop=0)

print(f"Converted {input_path} to {output_path}")

​

​

遇到问题

可以实现目标但是有一个问题:

播放的速度不一样。

在imageio.mimsave(output_path, gif_frames, format='GIF',loop=0)还有一个参数duration用来设置每帧持续时间,也就是播放速度。

获取duration

如果我们想要与原webp播放速度一致,那么就需要获取原图的duration信息。但是目前我还没有找到什么库能够解析出webp的duration。上网查了查给出的建议是用软件工具获取动图的各种信息。既然如此还不如直接下载格式转化的工具呢,毕竟我们没有必要为了这碟醋包饺子呀。

最后没有办法,只能手动解析了。

WebP 容器规范  |  Google for Developers

进入这个网站查看webp的文件结构。

webp的文件标题如下:

那么我们可以先读取这些信息:

        with open(file_path, 'rb') as f:
            # 读取文件头部 RIFF 标志
            riff = f.read(4)
            if riff != b'RIFF':
                raise ValueError("File is not a valid WebP file.")

            # 读取文件大小
            file_size = struct.unpack('<I', f.read(4))[0]


            # 读取文件类型 WEBP 标志
            webp = f.read(4)
            if webp != b'WEBP':
                raise ValueError("File is not a valid WebP file.")

            frame_durations = []

由于f.read()和f.seek()函数都能够更改文件指针的位置,所以经过上述操作指针f会指向webp文件标题的末尾。

接下来是一些块(chunk),与动画有关的块分别为“ANIM”和“ANMF”,其中我们需要的duration信息则存储在“ANMF”中。

“ANMF”区块的结果为:

在chunkheader中前四位存储的信息为名称ANMF,后四位存储的是ANMF的大小。

上面我们已经获取了整个webp文件的大小file_size,用f.tell()表示当前文件指针位置,在file_size范围内读取文件内容。

            while f.tell() < file_size:
                # 读取四字节的 chunk 标志
                chunk_id = f.read(4)
                if chunk_id == b'ANMF':
                    # 读取 ANMF chunk 大小
                    anmf_size = struct.unpack('<I', f.read(4))[0]

                    # 读取 ANMF chunk 数据
                    chunk_data = f.read(anmf_size)

                    # 解析 Frame Duration,偏移量为 12,读取3个字节
                    duration_bytes = chunk_data[12:15]
                    duration = (duration_bytes[2] << 16) + (duration_bytes[1] << 8) + duration_bytes[0]
                    frame_durations.append(duration)

                    

                else:
                    # 跳过未知的 chunk
                    chunk_size = struct.unpack('<I', f.read(4))[0]

                    f.seek(chunk_size, 1)

            return frame_durations

首先读取4个字节的标志,用条件语句判断是否为'ANMF'区块,进入'ANMF'区块后,由于已经读取了4个字节的标志,用anmf_size = struct.unpack('<I', f.read(4))[0]表示'ANMF'块的大小,这里是按照小字节存储的。

简单来说例如123,1位于百位,那么我们用数组arr这样存储arr[0]=3,arr[1]=2,arr[2]=1.因此在后面按位读取是这样的:

duration = (duration_bytes[2] << 16) + (duration_bytes[1] << 8) + duration_bytes[0]

我们继续刚才的过程,获取anmf_size的过程也有f.read(4),所以之后就进入了:

剩下的:

所以帧时长的偏移量从3+3+3+3=12开始,读取3个字节。

duration_bytes = chunk_data[12:15]

然后读取duration = (duration_bytes[2] << 16) + (duration_bytes[1] << 8) + duration_bytes[0]

其实,我们只用读取第一帧的duration就可以了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/730591.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AI赋能前端:你的Chrome 控制台需要AI(爱)

像会永生那样去学习,像明天就要死亡那样去生活。——圣雄甘地 大家好,我是柒八九。一个专注于前端开发技术/Rust及AI应用知识分享的Coder 此篇文章所涉及到的技术有 AI(Gemini)ChromeDevTool🪜魔法接码平台因为,行文字数所限,有些概念可能会一带而过亦或者提供对应的学习…

可一件转化的视频生成模型:快手官方大模型“可灵”重磅来袭!

可一件转化的视频生成模型“可灵”重磅来袭&#xff01; 前言 戴墨镜的蒙娜丽莎 达芬奇的画作《蒙娜丽莎的微笑》相信大家是在熟悉不过了&#xff0c;可《戴墨镜的蒙娜丽莎》大家是不是第一次见&#xff1f;而且这还不是以照片的形式&#xff0c;而是以视频的形式展示给大家。 …

gstreamer+qt5实现简易视频播放器

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、安装环境1.QT52.gstreamer 二、代码1.Windows实现 三、测试效果总结 前言 最近在研究mpp&#xff0c;通过gstreamer实现了硬解码&#xff0c;但是我在想我…

CVPR 2024第三弹:李飞飞教授惊喜亮相,CVPR之家乐队火爆演奏惊艳全场

CVPR 2024第三弹&#xff1a;小编与李飞飞教授惊喜同框&#xff0c;"CVPR之家"乐队火爆演奏惊艳全场&#xff01; 会议之眼 快讯 2024 年 CVPR &#xff08;Computer Vision and Pattern Recogntion Conference) 即国际计算机视觉与模式识别会议&#xff0c;于6月1…

CAC 2.0融合智谱AI大模型,邮件安全新升级

在数字化时代&#xff0c;电子邮件的安全问题日益成为关注的焦点。Coremail CACTER邮件安全人工智能实验室&#xff08;以下简称“CACTER AI实验室”&#xff09;凭借其在邮件安全领域的深入研究与创新实践&#xff0c;不断推动技术进步。 此前&#xff0c;CACTER AI实验室已获…

任务4.8.3 利用SparkSQL统计每日新增用户

实战概述&#xff1a;利用SparkSQL统计每日新增用户 任务背景 在大数据时代&#xff0c;快速准确地统计每日新增用户是数据分析和业务决策的重要部分。本任务旨在使用Apache SparkSQL处理用户访问历史数据&#xff0c;以统计每日新增用户数量。 任务目标 处理用户访问历史数…

Apifox 中如何处理加密或编码过的响应数据?

接口返回的响应数据有时是经过编码或加密处理的&#xff0c;要转换成可读的明文&#xff0c;可以使用 Apifox 内置的 JS 类库、或者通过调用外部编程语言 &#xff08;如 Python、JavaScript 等&#xff09; 来进行处理。 例如&#xff0c;一个经过 Base64 编码的数据可以通过…

vue2与vue3数据响应式对比之检测变化

vue2 由于javascript限制&#xff0c;vue不能检测数组和对象的变化 什么意思呢&#xff0c;举例子来说吧 深入响应式原理 对象 比如说我们在data里面定义了一个info的对象 <template><div id"app"><div>姓名: {{ info.name }}</div><…

本地部署Ollama+qwen本地大语言模型Web交互界面

什么是 Ollama WebUI&#xff1f; Ollama WebUI 已经更名为 Open WebUI. Open WebUI 是一个可扩展、功能丰富且用户友好的自托管 WebUI&#xff0c;旨在完全离线操作。它支持各种 LLM 运行程序&#xff0c;包括 Ollama 和 OpenAI 兼容的 API。 Ollama WebUI 是一个革命性的 L…

复盘最近的面试

这个礼拜一直在面试&#xff0c;想着看看能否拿到不错的offer前去实习&#xff0c;从周一到周四&#xff0c;面了将近10家&#xff0c;特整理此份面经&#xff0c;希望对秋招的各位有所帮助 A公司 一面 面试官人很好&#xff0c;我回答的时候不会他会笑笑然后提醒我 自我介绍~…

2024软考系规考前复习20问!看看你能答上来多少

今天给大家整理了——2024系统规划与管理师考前20问&#xff0c;这是一份很重要的软考备考必看干货&#xff0c;包含很多核心知识点。有PDF版&#xff0c;可打印下来&#xff0c;过完一遍教材后&#xff0c;来刷一刷、背一背&#xff0c;说不定可以帮你拿下不少分。 第1问- 信息…

怎么用二维码在线下载视频?视频用二维码下载的制作方法

怎么把视频转换成二维码之后还可以下载视频呢&#xff1f;现在使用二维码的方式来分享视频内容在很多行业和场景中都有应用&#xff0c;这种方式能够更加简单快捷的完成视频的传播分享&#xff0c;那么怎么让扫码者可以自由选择下载视频呢&#xff1f;下面来给大家分享扫码下载…

STM32小项目———感应垃圾桶

文章目录 前言一、超声波测距1.超声波简介2.超声波测距原理2.超声波测距步骤 二、舵机的控制三、硬件搭建及功能展示总结 前言 一个学习STM32的小白~ 有问题请评论区或私信指出 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、超声波测距 1.超声波…

项目训练营第三天

项目训练营第三天 注册登录测试 前面我们编写了用户注册、登录的逻辑代码&#xff0c;每编写完一个功能模块之后&#xff0c;我们都要对该模块进行单元测试&#xff0c;来确保该功能模块的正确性。一般情况下使用快捷键Ctrl Shift Insert&#xff0c;鼠标左击类名可以自动生…

代码随想录算法训练营第30天| 122.买卖股票的最佳时机II、55. 跳跃游戏、 45.跳跃游戏II、 1005.K次取反后最大化的数组和

122.买卖股票的最佳时机II 题目链接&#xff1a;122.买卖股票的最佳时机II 文档讲解&#xff1a;代码随想录 状态&#xff1a;so easy 思路&#xff1a;不用考虑操作的次数&#xff0c;其实只要有赚就拿下就行了。 题解&#xff1a; class Solution {public int maxProfit(in…

数据通信与网络(三)

物理层概述&#xff1a; 物理层是网络体系结构中的最低层 它既不是指连接计算机的具体物理设备&#xff0c;也不是指负责信号传输的具体物理介质&#xff0c; 而是指在连接开放系统的物理媒体上为上一层(指数据链路层)提供传送比特流的一个物理连接。 物理层的主要功能——为…

期末考后怎样发成绩?

老师们&#xff0c;下周可就是期末考啦&#xff0c;又到了头疼发成绩的时候了。每当这个时候&#xff0c;家长们总是急切地咨询孩子的考试表现&#xff0c;向老师们询问成绩。这种场景几乎成了每学期结束时的常态。 别担心&#xff0c;我来安利一个超棒的工具——“易查分小程序…

前端菜鸡流水账日记 -- git管理工具(多版本)

哈喽哇&#xff0c;我又又又来了&#xff0c;其实之前就挺想进行一篇关于git管理工具的分享的&#xff0c;但是一直都没有来的及&#xff0c;直到今天&#xff0c;在学习的时候&#xff0c;&#xff0c;一个朋友新发现了一个vscode中的小插件&#xff0c;所以我就决定一起来分享…

市值3万亿英伟达的崛起:技术、坚持与市场的力量,厚积薄发的经典案例

在科技领域&#xff0c;英伟达&#xff08;NVIDIA&#xff09;的故事无疑是一个厚积薄发的经典案例。作为一家专注于图形处理单元&#xff08;GPU&#xff09;的公司&#xff0c;英伟达用31年的时间证明了技术的价值、计算的价值和坚持的价值。本文将详细探讨英伟达如何从一家市…

Leaflet【四】轨迹回放效果控制台控制轨迹运动效果

安装依赖&准备静态资源 轨迹回放使用leaflet-trackplayer插件 npm i leaflet-trackplayer --force初始化地图&#xff0c;在这里初始化地图可以参考&#xff0c;然后导入了轨迹回放插件和一个图片&#xff08;用于标记当前轨迹运动点&#xff09;图片资源 import L from…