webp动图转gif

news2024/11/24 2:00:49

目录

前言

解决过程

遇到问题

获取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就可以了。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 5G基站光纤连接问题(200分) - 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f…

容器之工具栏构件演示

代码; #include <gtk-2.0/gtk/gtk.h> #include <glib-2.0/glib.h> #include <gtk-2.0/gdk/gdkkeysyms.h> #include <stdio.h>int main(int argc, char *argv[]) {gtk_init(&argc, &argv);GtkWidget *window;window gtk_window_new(GTK_WINDO…

远程桌面总是连接不上,mstsc远程总是连接不上解决方法

远程桌面连接在日常生活和工作中扮演着至关重要的角色&#xff0c;它允许用户在不同地点和设备之间无缝协作。然而&#xff0c;有时用户可能会遇到MSTSC&#xff08;Microsoft远程桌面连接&#xff09;总是连接不上的问题&#xff0c;这可能是由于多种原因造成的。本文将针对这…

若依 ruoyi 排序 顺序 倒序 的实现

1. table标签新增排序相关属性 // :default-sort"defaultSort" 指定默认排序 // sort-change"handleSortChange" 指定排序点击事件 :default-sort"defaultSort" sort-change"handleSortChange" 2. 列上新增排序相关配置 自定义查询语…

解决File协议导致的CORS限制,用Node.js搭建本地服务器

文章目录 一、前言二、分析报错原因三、如何解决四、具体步骤 你是否曾遇到这样的困境&#xff1a;在本地使用file://协议直接打开HTML文件时&#xff0c;由于现代浏览器的安全限制&#xff0c;无法跨源请求&#xff08;CORS&#xff09;本地资源&#xff1f;尤其是当你试图通过…

非关系型数据库NoSQL数据层解决方案 之 redis springboot整合与读写操作 2024详解以及window版redis5.0.14下载百度网盘

redis下载安装以及基本使用 下载地址 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;0410 一个名对应一个数值 内存级 在内存里进行操作 准备启动 我们现在就有一个redis客户端的服务器了 我们再启动一个cmd 操作redis数据库 redis里面的基本数据类型有五种 …

小程序 如何支付后获取 Unionid

接口说明 接口英文名 getPaidUnionid 功能描述 该接口用于在用户支付完成后&#xff0c;获调用本接口前需要用户完成支付&#xff0c;用户支付完成后&#xff0c;取该用户的 UnionId&#xff0c;无需用户授权。本接口支付后的五分钟内有效。 注意事项 调用前需要用户完成…

重构大学数学基础_week05_雅各比矩阵与雅各比行列式

这周来讲一下雅各比矩阵和雅各比行列式。 多元函数的局部线性属性 首先我们来回顾一下向量函数&#xff0c;就是我们输入一个向量&#xff0c;输出也是一个向量&#xff0c;我们假设现在有一个向量函数 这个函数意思就是在说&#xff0c;我们在原来的平面上有一个向量(x,y),经…

收藏||电商数据采集流程||电商数据采集API接口

商务数据分析的流程 第一步&#xff1a;明确分析目的。首先要明确分析目的&#xff0c;并把分析目的分解成若干个不同的分析要点&#xff0c;然后梳理分析思路&#xff0c;最后搭建分析框架。 第二步&#xff1a;数据采集。主流电商API接口数据采集&#xff0c;一般可以通过数…

【芯片知识】QSOP24封装-NRK3502语音识别芯片方案

一、NRK3502语音识别芯片的简介 NRK3502系列芯片是一款蓝牙双模智能语音l0T芯片。芯片集成32位CPU处理器&#xff0c;包含 UART、GPIO、SPI、SD卡、12C、ADC、TouchSensor 等外围接口;内置 NPU、浮点运算单元。 依托于在语音识别技术上的积累和算法的不断优化和创新&#xff0c…

数据库复习——模式分解

模式分解这边主要包括无损分解和保持函数依赖的分解两种形式&#xff0c;简单整理一下。 无损分解 把一个 R R R 分成 ρ { R 1 , R 2 , ⋯ , R k } \rho \{R_1,R_2,\cdots,R_k\} ρ{R1​,R2​,⋯,Rk​}&#xff0c;然后通过自然连接 R 1 ⋈ R 2 ⋈ ⋯ ⋈ R k R_1\bowtie R…

C#和python端通信之使用共享内存

一、前言 本篇主要实验通过使用共享内存实现C#端代码和python端代码之间的通信&#xff0c;主要目的是相较于直接传输较大的数据&#xff08;例如图像数据&#xff09;&#xff0c;该方式更节省时间。 二、代码 C#端&#xff1a; 创建了一个大小为1的共享内存&#xff0c;名为…

Docker容器基础知识,即linux日常运维命令

Docker 是一个流行的用 Go 语言开发的开源项目&#xff0c;基于Linux内核的cgroup、namespace及 AUFS 等技术&#xff0c;对进程进行封装隔离&#xff0c;由 Dotcloud 公司开发。Docker已成为容器行业的事实标准。 小陈发现一个有趣的事情&#xff0c;容器的英文是Container&am…

数据通信与网络(三)

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

MDK-ARM 编译后 MAP 文件分析

本文配合 STM32 堆栈空间分布 食用更佳&#xff01; 一图胜千言。。。

U盘文件夹损坏0字节:现象解析、恢复方法与预防措施

在日常工作和生活中&#xff0c;U盘因其便携性和大容量成为我们存储和传输数据的重要工具。然而&#xff0c;当U盘中的文件夹突然损坏并显示为0字节时&#xff0c;我们可能会感到困惑和焦虑。本文将对U盘文件夹损坏0字节的现象进行详细描述&#xff0c;分析其可能的原因&#x…

[Qt]Qt框架解析:从入门到精通,探索平台开发的无限可能

一、Qt的概述 Qt是一个跨平台的C图形用户界面应用程序框架&#xff08;GUI&#xff09;。它为应用程序开发者提供建立艺术级图形界面所需的所有功能。它是完全面向对象的&#xff0c;很容易扩展&#xff0c;并且允许真正的组件编程。开发环境为Qt creator5.8.0&#xff0c;下载…

【MATLAB源码-第229期】基于matlab的有源功率因数校正仿真,采用PI调节改善功率因数;改善后达到99%.

操作环境&#xff1a; MATLAB 2022a 1、算法描述 一、整体结构概述 有源功率因数校正&#xff08;APFC&#xff09;是一种用于提高电力系统功率因数的技术&#xff0c;通过控制电流的波形&#xff0c;使其与电压的波形保持一致&#xff0c;从而减少无功功率的产生&#xff…

原来这个东西叫 Docker

大家好&#xff0c;我是徒手敲代码。 今天来聊聊 Docker 三个时代的演变 首先要先了解 Docker 出现的背景。在计算机技术的发展历程中&#xff0c;我们经历了从物理机时代到虚拟机时代&#xff0c;再到容器化时代的演变。 在物理机时代&#xff0c;应用程序直接运行在物理服务…

精准测试与传统的手工测试

大部分测试从业人员都经历了手工测试到自动化测试递进&#xff0c;测试技术及思路都发生了日新月异的变化&#xff0c;有些中厂及大厂都有一套强大且复杂的自动化测试用例时刻保障产品的稳定性及正确性。 所谓精准测试&#xff0c;就是借助一定的技术手段、通过算法的辅助对传…