前言:去年曾写过一个维信机器人,目的是为了方便管理我个人建的一个微信群,当时想在维信消息里面做个带UI的小游戏,但是会受到微信消息框的约束,思来想去,就干脆通过gif来呈现吧,大致就是用户在微信群里发命令,机器人就会根据命令做出相应动作,并通过gif的形式呈现出来。

本例代码可以从如下链接中获得

https://github.com/shanlihou/pythonFunc/tree/master/gif

python test.py,可以通过一个小游戏飞行棋,生成对应的gif

python gifHelper.py可以解析当前文件夹下box.gif的文件,为了测试方便

正题:

0x01:GIF头

先上uedit上的二进制截图,被红框框出来的就是gif的头部

下面是如上头部解析后的内容,可以看到有:版本号,宽高

m1:占1位全局颜色列表标志,置位则后面紧跟全局颜色列表

cr3:占3位颜色深度,为取值+1为某一原色位数,则111为7,7+1,所以每个像素颜色深度占3*8位

s1:占1位,排序标志,置位情况下颜色列表按照频率排序

pixel3:占3位,2^(pixel + 1)全局颜色列表大小,010则为2^(2 + 1)=8

bgColor:背景色,在全局列表中索引的位置

W:H:宽高比

接下来13-37字节为全局颜色列表,这个大小为3 * 8,8就是前面的全局颜色列表大小,每三个字节一个rgb值。

下面是python解析gif头并打印的代码

    def parseGif(self, fileName):fileRead = open(fileName, 'rb')tmp = fileRead.read(6)print '_' * 60   self.formatPrint(0, 6, tmp, 'ver')tmp = fileRead.read(7)self.formatPrint(6, 8, ord(tmp[0]) + ord(tmp[1]) * 256, 'width')self.formatPrint(8, 10, ord(tmp[2]) + ord(tmp[3]) * 256, 'height')comment = ['m1:cr3:s1:pixel3', 'bgColor', 'W:H']for i in xrange(3):self.formatPrint(i + 10, i + 11, self.get2(ord(tmp[i + 4])), comment[i])pixel = ord(tmp[4]) & 0x7pixel_size = int(math.pow(2, pixel + 1))m = ord(tmp[4]) & 0x80#print 'pixel', pixel_sizeif m == 0x80:tell = fileRead.tell()tmp = fileRead.read(3 * pixel_size)self.formatPrint(tell, tell + 3 * pixel_size, 'rgb * %d' % pixel_size, 'pixel_table')print '_' * 60self.parseFlag(fileRead)

0x02:解析图形控制扩展

接着解析如下红色框出来的块

如下为程序解析后的结果

____________________________________________________________37-      38|                  21|                flag|38-      39|                  f9|                flag|39-      40|                   4|          block size|40-      41|            00000101|  res3:method3:i1:t1|41-      43|                  50|          delay time|43-      44|                   7|         transparent|44-      45|                   0|          terminator|
____________________________________________________________

首先是第一个标志:21这个标识本块为扩展块,具体类型需要下一字节确认

f9为图形控制扩展块,本例的图形控制扩展块主要用来说明接下来的一帧图片的延迟时间

block size:说明本块的大小,包括当前字节,不包括终结器

res3:method3:i1:t1:

res3:为3位的保留字段

method3:3位的处置方法,当前方法为1,移去当前图片

i1:1位的用户输入标志

t1:1位的透明色标志,置位的话,接下来的透明色索引有意义

delay time:延迟50 * (1/100)秒

transparent:透明色的索引

terminator:终结器,标志块终结,默认为0

下面为当前块的解析代码:

    def parseF9(self, fRead):tell = fRead.tell()block_size = ord(fRead.read(1))self.formatPrint(tell, tell + 1, block_size, 'block size')tell += 1tmp = fRead.read(block_size)self.formatPrint(tell, tell + 1, self.get2(ord(tmp[0])), 'res3:method3:i1:t1')tell += 1self.formatPrint(tell, tell + 2, ord(tmp[1]) + ord(tmp[2]) * 256, 'delay time') tell += 2self.formatPrint(tell, tell + 1, ord(tmp[3]), 'transparent')tell += 1tmp = fRead.read(1)self.formatPrint(tell, tell + 1, ord(tmp), 'terminator')print '_' * 60return fRead.read(1)

0x03:解析图像帧

如上是图像帧的帧头,解析后如下所示

____________________________________________________________45-      46|                  2c|               image|46-      48|                   0|            x offset|48-      50|                   0|            y offset|50-      52|                 256|               width|52-      54|                 256|              height|54-      55|            00000000|    m1:i1:s1:r2:pix3|55-      56|                   4|      bits per pixel|

2c为图像帧的标志,检测到2c即为一个图像帧的开始

x offset:为x方向的偏移量

y offset:为y方向的偏移量

width:为图像的宽

height:为图像的高

m1:i1:s1:r2:pix3解释如下

m1:占1位,局部颜色列表标志位

i1:占1位,交织标志,决定了图像排列顺序

s1:占1位,决定紧跟着的颜色列表是否分类排列

r2:两位的保留字

pix3:决定接下里的局部颜色列表大小

bits per pixel:这个字节非常重要,他表示接下来要解析的图像的首个要读入数据的位数

0x04:lzw算法

终于激动人心的算法时刻了,lzw是一种压缩算法,就是gif中所运用的编码算法。

要想知道怎么用lzw解码,一定要首先介绍lzw的编码算法

编码:

先上代码

    def encode(self):#encode tabledictCode = {}#clear codeclearCode = 1 << self.bit#initial running bitsrunBits = self.bit + 1#end codeendCode = clearCode + 1runCode = endCode + 1for i in range(clearCode):dictCode[chr(i)] = ipre = ''self.putBit(runBits, clearCode)for i in self.data:if pre == '':pre = ielse:strWhole = pre + iif dictCode.has_key(strWhole):pre = strWholeelse:#print runCodedictCode[strWhole] = runCodeself.putBit(runBits, dictCode[pre])pre = irunCode += 1if runCode > 1 << runBits:runBits += 1self.putBit(runBits, dictCode[pre])                     runCode += 1if runCode > 1 << runBits:runBits += 1      self.putBit(runBits, endCode)self.putBit(-1) 

dictCode:就是编码表

clearcode:是清除码表标志位,每次遇到clearCode,dictCode就要清空,dictCode={},大家可以看到我代码中并没有做这个操作,原因是因为我“懒”,通常情况下当码表达到4096长度时候才需要将clearCode输入缓冲区,并清空码表,但是一般不会达到这种情况,所以我这里就没有判断

runBits:这个是当前向数据流中输出的位数,由于lzw是一种动态位数的编码方式,每一个数据流中的数据的位是在编译过程中不断增加的,初始位根据全局或者局部颜色列表大小决定,比如如果全局表中有7种颜色,先向上去整为8,就是三位,之后再+1,则为4位,因为clearCode要使用2^3,且endcode=clearCode+1=2^3+1 =9,这就占了两位,所以要多加一位

endCode:这个为编码结束位,当检测到endCode,意味着图像数据流结束,为clearCode+1

runCode:当前编码索引,由于编码表从endCode之后开始,所以runCode=endCode+1

正式开始讲解编码过程:

bits per pixel:这个字节非常重要,他表示接下来要解析的图像的首个要读入数据的位数

先向输出流中输出clearCode,再把pre初始化为空,作为前缀,之后,读入第一个像素i,先将pre初始化为i,不做任何操作,继续读入下一个像素i,组成当前字符串pre+i,然后在编码表中比对,看是否有这个key,然后分成两个流程:

如果有dictCode有pre+i这个key,则pre=pre+i并继续读入下一个数据

如果dictCode中没有pre+i这个可以,则更新编码表dictCode[pre+i]=runCode,并将dictCode中pre这个key对应的runCode输出到输出流中,然后让pre赋值为i,并且增加编码索引runCode。

解码:

先上代码

        cur = self.fFetch(fRead, RunningBits)while cur != EOFCode:if cur == ClearCode:pre = 0RunningCode = EOFCode + 1RunningBits = BitsPerPixel + 1cur = self.fFetch(fRead, RunningBits)continueif RunningCode == EOFCode + 1:passelif cur == RunningCode - 1:dictCode[RunningCode - 1] = pre + pre[0]else:dictCode[RunningCode - 1] = pre + dictCode[cur][0]pre = dictCode[cur]output += preRunningCode += 1cur = self.fFetch(fRead, RunningBits)if RunningCode == (1 << RunningBits):RunningBits += 1

fFetch:这个函数是从fRead文件流中取出RunningBits个比特,之前也说,lzw算法中,编码的bit长度是逐渐递增的,所以这个runningBits是随着编码表的扩大而增大

cur:这个为当前取出的第一个索引,并将pre赋值为cur,然后取出下一个cur并继续后面循环,然后分两种情况来处理:

第一种情况:当编码表中有这个cur的时候,这时候更新编码表,将pre+索引表中对应cur的这段编码的首位,也就是pre+dictCode[cur][0]放到编码表最后一个的位置,并将pre赋值为cur对应的编码,然后将pre加入到输出流中。

第二种情况:当编码表中没有这个cur的时候,这时候只有一种情况就是cur刚好对应于编码表即将添加的位置,这个将要添加的编码就为pre+pre[0]。要解释为什么,就需要大家回忆一下编码的过程,在我们进行编码时候,遇到了一个pre+i未知,将pre+i加入到编码表中,将pre输出,并用i作为下一个的首位来编码,下一次编码到i+mid+j时候出现未知编码,则将i+mid对应的索引输出,如果i+mid的索引刚好为新添加到编码表中的编码时候,则pre+i=i+mid,所以pre的首和mid的为相等,都为i。这也就是为什么如果我们在解码过程中遇到一个cur的索引刚好为即将加入编码表的位置时候,要添加pre+pre[0]这个编码。

0x05:结束

如上就是gif编解码的python实现。完整版代码在最上方链接中。

鸣谢:在写python实现的时候也是看了这位朋友的博客,讲的还是很详细的

https://blog.csdn.net/wzy198852/article/details/17266507

gif编解码python实战相关推荐

  1. python 图片base64 编解码,转换成Opencv,PIL.Image图片格式

    Python PIL.Image和OpenCV图像格式相互转换 二进制打开图片文件,base64编解码转成Opencv格式: # coding: utf-8 import base64 import ...

  2. Python 下JSON的两种编解码方式实例解析

    概念 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,易于人阅读和编写.在日常的工作中,应用范围极其广泛.这里就介绍python下它的两种编解码方法: 使 ...

  3. Netty实战 IM即时通讯系统(八)服务端和客户端通信协议编解码

    Netty实战 IM即时通讯系统(八)服务端和客户端通信协议编解码 零. 目录 IM系统简介 Netty 简介 Netty 环境配置 服务端启动流程 客户端启动流程 实战: 客户端和服务端双向通信 数 ...

  4. python编码格式有哪些_Python JSON编解码的方式有哪些

    Python JSON编解码的方式有哪些 发布时间:2020-11-04 17:52:46 来源:亿速云 阅读:92 今天就跟大家聊聊有关Python JSON编解码的方式有哪些,可能很多人都不太了解 ...

  5. Python学习教程:Python3内置模块之base64编解码方法小结

    Python学习教程:Python3内置模块之base64编解码方法小结 概述 Base64 是网络上最常见的用于传输 8Bit 字节码的编码方式之一,Base64 就是一种基于 64 个可打印字符来 ...

  6. python 编码解码原理_Python JSON编解码方式原理详解

    这篇文章主要介绍了Python JSON编解码方式原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 概念 JSON(JavaScript Ob ...

  7. Python基于二维码实现的在线编解码系统

    目 录 摘 要 I Abstract II 第一章 绪论 1 1.1 课题背景 1 1.2 研究现状 1 1.3 工作环境和背景 2 1.3.1 操作系统 3 1.3.2 编程语言 3 1.3.3编码 ...

  8. Python学习笔记(八)爬虫基础(正则和编解码)

    知识点 正则 正则匹配url,引用re库,将需要匹配的字段用(.*?)来匹配,可以匹配任何字符串.如果有换行,可以用如下方式解决: 1. ([\s\S]*?) 2. re.findall(reg,ht ...

  9. 【听如子说】-python模块系列-AIS编解码Pyais

    Pyais Module Introduce pyais一个简单实用的ais编解码模块 工作中需要和ais打交道,在摸鱼的过程中发现了一个牛逼的模块,对ais编解码感兴趣的可以拿项目学习一下,或者运用 ...

最新文章

  1. 什么?搞不定Kafka重复消费?
  2. NSNotificationCenter
  3. 安卓自动化测试(1)安卓自动化测试原理概念
  4. 测验1: Python基本语法元素 (第1周)
  5. 【python数据挖掘课程】十五.Matplotlib调用imshow()函数绘制热图
  6. CoreCLR源码探索(一) Object是什么
  7. noip模拟赛 写代码
  8. vscode删除文件夹,VSCode:删除文件中的所有注释
  9. python装饰器class_PYTHON里的装饰器能装饰类吗
  10. 安卓mqtt调试工具_MOTT工具调试阿里云物联网平台
  11. 【Oracle】ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired
  12. html怎么编辑文字位置,html – 修正文本的位置背景剪辑
  13. Unity中设置对象匀速移动
  14. 百度云网盘批量复制文件,在线复制到每个文件夹中PC版
  15. 图的常见衡量指标及算法调研
  16. centOS最全下载地址
  17. 通过XMind Update制作思维导图
  18. K3老单开发-销售订单计算比例(实际价格反推)
  19. 全平台小程序开发框架Uni-app重点概览
  20. A.O.史密斯净水热饮机 -- 直接获得多温度净水,热饮不再等待

热门文章

  1. 使用JsonFormat映射protobuf和javabean
  2. dotnet 一些代码审查套路
  3. 【iOS-iap防护】验证用户付费收据!拒绝iap Cracker!拒绝iap Free!让iphone越狱用户无从下手!
  4. 微信公众平台开发简要说明 —— 基本原理
  5. 从赌场逻辑,分析平台币的投资价值 2020-03-03
  6. windows server上novnc的部署和使用
  7. Windows 7 fails to install; Status: 0xc0000225
  8. Linux系统时间的设置
  9. 怎样设置计算机u盘启动程序,怎么进入BIOS设置U盘启动_如何设置U盘为第一启动项?-192路由网...
  10. linux pushd 不起作用,Linux中的pushd和popd