Python |GIF 解析与构建(2):状态机解析

Python |GIF 解析与构建(2):状态机解析

目录

Python |GIF 解析与构建(2):状态机解析

引言

一、状态机概述

状态机的优势与改进方向

总结


引言

在《Python |GIF 解析与构建(1):初步解析》中,我们初步进行解析gif文件。

本文将深入探讨如何使用 ** 状态机(State Machine)** 实现 GIF 文件的解析,通过分阶段处理数据流,逐步提取关键信息(如版本、颜色表、图像数据等)。状态机的优势在于将复杂的解析逻辑拆解为多个可管理的状态,每个状态专注于处理特定的数据流片段,从而提高代码的可读性和可维护性。

一、状态机概述

GIF 文件由一系列区块(Block)组成,每个区块包含特定类型的数据(如文件头、颜色表、图像数据、扩展指令等)。状态机的核心思想是:根据当前解析进度,将程序划分为多个状态(State),每个状态负责解析某一类区块或数据片段,并根据输入数据决定下一步跳转的状态。

GIF 解析的主要状态包括

  1. 开头(Header):验证 GIF 文件签名(GIF87aGIF89a)。
  2. 逻辑屏幕标识符(Logical Screen Descriptor):解析画布尺寸、颜色表标志等基础信息。
  3. 全局颜色表(Global Color Table):提取全局调色板(若存在)。
  4. 区块检测(Block Detection):识别后续区块类型(如图像标识符、扩展块等)。
  5. 扩展块处理(Extension Blocks):解析图形控制扩展(如动画延迟)、应用程序扩展(如循环参数)。
  6. 图像标识符(Image Descriptor):解析图像位置、尺寸、局部颜色表(若存在)及压缩数据。
  7. 结束符检测(Trailer Detection):验证文件结束符(0x3B)。
状态机的优势与改进方向
  • 优势
    • 模块化:每个状态职责单一,便于调试和扩展。
    • 容错性:通过状态转移控制数据流,可优雅处理无效数据(如提前终止解析)。
  • 改进方向
    • 支持更多扩展块类型(如注释扩展、文本扩展)。
    • 优化 LZW 解码性能(如使用更高效的数据结构存储字典)。
    • 添加动画帧时序处理(结合图形控制扩展中的延迟时间)。
总结

本文通过状态机实现了 GIF 文件的逐步解析,将复杂的格式解析拆解为可管理的状态转移过程。状态机模型不仅适用于 GIF 解析,还可推广到其他二进制格式(如 PNG、BMP)的解析场景。下一篇文章将探讨如何基于状态机构建 GIF 文件,实现从像素数据到 GIF 动画的生成。

from PIL import Image
import struct# 读取文件
gif_path = "1.gif"
for _ in range(2):try:with open(gif_path, 'rb') as f:data = f.read()breakexcept:# 创建全白帧 50x50white_frame = Image.new('RGB', (5, 5), color=(255, 255, 255))# 创建全黑帧 50x50black_frame = Image.new('RGB', (5, 5), color=(0, 0, 0))# 保存为无限循环的GIF动画white_frame.save(gif_path,save_all=True,append_images=[black_frame],duration=200,loop=0)# 解码
def lzw_decode(compressed):# 初始化字典dictionary = {i: bytes([i]) for i in range(256)}next_code = 258result = bytearray()# 处理压缩数据buffer = 0bits_in_buffer = 0code_size = 9prev_code = Nonefor byte in compressed:buffer |= byte << bits_in_bufferbits_in_buffer += 8while bits_in_buffer >= code_size:code = buffer & ((1 << code_size) - 1)buffer >>= code_sizebits_in_buffer -= code_sizeif code == 256:  # 清除码dictionary = {i: bytes([i]) for i in range(256)}next_code = 258code_size = 9prev_code = Nonecontinueelif code == 257:  # 结束码return bytes(result)if prev_code is None:result.extend(dictionary[code])prev_code = codecontinueif code in dictionary:entry = dictionary[code]result.extend(entry)dictionary[next_code] = dictionary[prev_code] + entry[:1]else:entry = dictionary[prev_code] + dictionary[prev_code][:1]result.extend(entry)dictionary[next_code] = entrynext_code += 1prev_code = codeif next_code >= (1 << code_size) and code_size < 12:code_size += 1return bytes(result)# 压缩
def lzw_decompress(compressed):dictionary = {i: bytes([i]) for i in range(256)}next_code = 258result = bytearray()buffer = 0bits = 0code_size = 9prev = Nonefor byte in compressed:buffer |= byte << bitsbits += 8while bits >= code_size:code = buffer & ((1 << code_size) - 1)buffer >>= code_sizebits -= code_sizeif code == 256:dictionary = {i: bytes([i]) for i in range(256)}next_code = 258code_size = 9prev = Nonecontinueelif code == 257:return bytes(result)if prev is None:result.extend(dictionary[code])prev = codecontinueif code in dictionary:entry = dictionary[code]result.extend(entry)dictionary[next_code] = dictionary[prev] + entry[:1]else:entry = dictionary[prev] + dictionary[prev][:1]result.extend(entry)dictionary[next_code] = entrynext_code += 1prev = codeif next_code > (1 << code_size) - 1 and code_size < 12:code_size += 1return bytes(result)# 按照分块处理
print(data)
print(data.hex(' '))# dict_ex = {"ff":"应用程序扩展","f9":'图形控制扩展'}# 状态机
class GIFDecode:def __init__(self):self.state = "开头"  # 状态self.state_child = "开始"  # 子状态self.global_color = []  # 全局颜色self.local_color_all = []  # 局部颜色(全部)self.local_color_one = []  # 局部颜色(单个)self.image_data = []self.version = ""  # 版本self.buffer = bytearray()  # 缓冲字节self.screen_width = 0  # 宽度self.screen_height = 0  # 长度self.has_global_color = 0  # 存在全局颜色表self.color_resolution = 3  # 颜色深度self.sort_flag = 0  # 分类标志self.global_color_table_size = 0  # 全局颜色数量self.bg_color_index = 0  # 背景颜色索引self.color_block_size = 0  # 颜色块长度self.pixel_aspect_ratio = 0  # 像素高宽比self.application_size = 0  # 应用程序扩展字节长度self.application_text = 0  # 应用程序扩展文本self.application_child_size = 0  # 应用程序扩展子块字节长度self.loop_type = 0  # 循环类型self.loop_parameter = 0  # 循环参数self.graphic_control_size = 0  # 图像控制字符长度self.graphic_control_sign_transparent = 0  # 图像控制标志位透明色self.graphic_control_sign_dispose = 0  # 图像控制标志位处置方法 # 0不使用  1把图形移去 2恢复到背景色 3恢复到先前状态 4-7自定义self.graphic_control_sign_input = 0  # 图像控制标志位用户输入self.graphic_control_sign_customize = 0  # 图像控制标志位自定义self.graphic_control_delay = 0  # 图像控制延迟时间self.graphic_control_sign_transparent_index = 0  # 图像控制透明色索引self.image_offset_x = 0  # 图像左偏移量 即X轴self.image_offset_y = 0  # 图像顶部偏移量 即Y轴self.image_width = 0  # 画布宽度self.image_length = 0  # 画布长度self.local_color_sign_number = 0  # 0-2颜色多少self.local_color_sign_retain = 0  # 3-4保留位self.image_local_color_sign_sort = 0  # 5排序self.local_color_sign_interwoven = 0  # 6交织self.local_color_sign_has_color_table = 0  # 7局部颜色表标志self.local_color_size = 0  # 局部颜色表字节self.image_compressed_bit = 0  # 压缩位self.image_block_size = 0  # 图像子块字节self.image_bytes_data = b"" # 图片压缩字节数据def feed(self, byte):print(self.buffer)if self.state == "开头":self.buffer.append(byte)if len(self.buffer) == 6:if self.buffer == b'GIF87a' or self.buffer == b'GIF89a':self.state = "逻辑屏幕标识符"self.version = self.buffer.decode('ascii')self.buffer.clear()else:raise ValueError(f"无效的GIF版本: {bytes(self.buffer)}")elif self.state == "逻辑屏幕标识符":self.buffer.append(byte)if len(self.buffer) == 7:# 解析逻辑屏幕描述符self.screen_width = int.from_bytes(self.buffer[0:2], 'little')self.screen_height = int.from_bytes(self.buffer[2:4], 'little')# 解析标志字节flags = self.buffer[4]"""假设:m = 1(存在全局颜色表);cr = 3(颜色深度为 3,二进制 011);s = 0(不使用分类标志);pixel = 4(全局颜色列表大小为 4,二进制 100)。计算各字段的位位置m 占位 7 → 需左移 7 位(m << 7);cr 占位 6-4 → 需左移 4 位(cr << 4);s 占位 3 → 需左移 3 位(s << 3);pixel 占位 2-0 → 无需位移(直接取 pixel)"""self.has_global_color = (flags & 0b10000000)self.color_resolution = (flags & 0b01110000)self.sort_flag = (flags & 0b00001000) != 0self.global_color_table_size = 2 ** ((flags & 0b00000111) + 1)self.bg_color_index = self.buffer[5]self.pixel_aspect_ratio = self.buffer[6]if self.has_global_color:self.state = "全局颜色表"self.color_block_size = self.global_color_table_size * 3self.buffer.clear()else:self.state = "区块检测"self.buffer.clear()elif self.state == "全局颜色表":self.buffer.append(byte)if len(self.buffer) == self.color_block_size:# 解析全局颜色表self.global_color = [(self.buffer[i], self.buffer[i + 1], self.buffer[i + 2]) for i inrange(0, len(self.buffer), 3)]self.state = "区块检测"self.buffer.clear()elif self.state == "结束符检测":self.state_child = "开始"  # 重置子状态if byte == 0x00:self.state = "区块检测"else:raise ValueError(f"无效的结束符: {bytes(byte)}")elif self.state == "区块检测":self.buffer.append(byte)if byte == 0x3B:  # 检测结束return Trueif self.state_child == "开始":if len(self.buffer) == 1:if self.buffer[0] == 0x21:self.state_child = "拓展块"self.buffer.clear()elif self.buffer[0] == 0x2C:self.state = "图像标识符"self.state_child = "开始"  # 重置子状态self.buffer.clear()else:raise ValueError(f"无效的区块: {bytes(self.buffer)}")elif self.state_child == "拓展块":if self.buffer[0] == 0xFF:self.state = "应用程序扩展"elif self.buffer[0] == 0xF9:self.state = "图形控制扩展"self.state_child = "开始"  # 重置子状态self.buffer.clear()else:raise ValueError(f"无效的区块: {bytes(self.buffer)}")elif self.state == "应用程序扩展":self.buffer.append(byte)if self.state_child == "开始":if len(self.buffer) == 1:self.application_size = struct.unpack('<B', self.buffer)[0]self.state_child = "解析"self.buffer.clear()elif self.state_child == "解析":if len(self.buffer) == self.application_size:self.application_text = self.buffer[0:self.application_size].decode('ascii', errors='replace')self.state_child = "子块长度"self.buffer.clear()elif self.state_child == "子块长度":if len(self.buffer) == 1:self.application_child_size = struct.unpack('<B', self.buffer)[0]self.state_child = "子块解析"self.buffer.clear()elif self.state_child == "子块解析":if len(self.buffer) == self.application_child_size:self.loop_type = self.buffer[0]self.loop_parameter = self.buffer[1:2]self.state = "结束符检测"self.buffer.clear()elif self.state == "图形控制扩展":self.buffer.append(byte)if self.state_child == "开始":if len(self.buffer) == 1:self.graphic_control_size = struct.unpack('<B', self.buffer)[0]self.state_child = "解析"self.buffer.clear()elif self.state_child == "解析":if len(self.buffer) == self.graphic_control_size:sign = self.buffer[0]# 解析标志位self.graphic_control_sign_transparent = (sign & 0b00000001)self.graphic_control_sign_dispose = (sign & 0b00000010)self.graphic_control_sign_input = (sign & 0b00011100)self.graphic_control_sign_customize = (sign & 0b11100000)self.graphic_control_delay = struct.unpack('<H', self.buffer[1:3])[0] * 0.01self.graphic_control_sign_transparent_index = self.buffer[3]self.state = "结束符检测"self.buffer.clear()elif self.state == "图像标识符":self.buffer.append(byte)if self.state_child == "开始":if len(self.buffer) == 9:self.image_offset_x = struct.unpack('<H', self.buffer[0:2])[0]self.image_offset_y = struct.unpack('<H', self.buffer[2:4])[0]self.image_width = struct.unpack('<H', self.buffer[4:6])[0]self.image_length = struct.unpack('<H', self.buffer[6:8])[0]sign = self.buffer[8]"""- 第 0-2 位:局部颜色表大小(0 = 无局部颜色表)- 第 3-4 位:保留位(0)- 第 5 位:排序标志(0 = 未排序)- 第 6 位:交织标志(0 = 非交织)- 第 7 位:局部颜色表标志(0 = 无局部颜色表)"""self.local_color_sign_number = (sign & 0b00000111)self.local_color_sign_retain = (sign & 0b00011000)self.image_local_color_sign_sort = (sign & 0b00100000)self.local_color_sign_interwoven = (sign & 0b01000000)self.local_color_sign_has_color_table = (sign & 0b10000000)# 检测是否存在局部颜色表格if self.local_color_sign_has_color_table == 128:self.local_color_size = 2 ** (self.local_color_sign_number + 1) * 3self.state_child = "获取局部颜色表"self.buffer.clear()else:self.state_child = "解析图像"self.buffer.clear()elif self.state_child == "获取局部颜色表":if len(self.buffer) == self.local_color_size:# 获取局部颜色表放入局部颜色中self.local_color_one = [(self.buffer[i], self.buffer[i + 1], self.buffer[i + 2]) for i inrange(0, len(self.buffer), 3)]self.local_color_all.append(self.local_color_one)self.state_child = "解析图像"self.buffer.clear()elif self.state_child == "解析图像":if len(self.buffer) == 2:self.image_compressed_bit = self.buffer[0]self.image_block_size = self.buffer[1]self.state_child = "获取数据"self.buffer.clear()elif self.state_child == "获取数据":if len(self.buffer) == self.image_block_size:self.image_bytes_data += bytes(self.buffer) # 转换字节而非对象self.state_child = "检测获取结束"self.buffer.clear()elif self.state_child == "检测获取结束":if self.buffer[0] == 0x00:image_data = self.lzw_decode(self.image_bytes_data)if self.local_color_one: # 如果局部存在 则按照局部生成 如果局部不存在则按照全局生成self.image_data.append([self.local_color_one[image_data[i]] for i in range(len(image_data))])else:self.image_data.append([self.global_color[image_data[i]] for i in range(len(image_data))])self.state = "区块检测"self.state_child = "开始"# 重置数据self.image_bytes_data = b""self.local_color_one.clear()self.buffer.clear()else:self.image_block_size = self.buffer[0]self.state_child = "获取数据"self.buffer.clear()# 解码def lzw_decode(self, compressed):# 初始化字典dictionary = {i: bytes([i]) for i in range(256)}next_code = 258result = bytearray()# 处理压缩数据buffer = 0bits_in_buffer = 0code_size = 9prev_code = Nonefor byte in compressed:buffer |= byte << bits_in_bufferbits_in_buffer += 8while bits_in_buffer >= code_size:code = buffer & ((1 << code_size) - 1)buffer >>= code_sizebits_in_buffer -= code_sizeif code == 256:  # 清除码dictionary = {i: bytes([i]) for i in range(256)}next_code = 258code_size = 9prev_code = Nonecontinueelif code == 257:  # 结束码return bytes(result)if prev_code is None:result.extend(dictionary[code])prev_code = codecontinueif code in dictionary:entry = dictionary[code]result.extend(entry)dictionary[next_code] = dictionary[prev_code] + entry[:1]else:entry = dictionary[prev_code] + dictionary[prev_code][:1]result.extend(entry)dictionary[next_code] = entrynext_code += 1prev_code = codeif next_code >= (1 << code_size) and code_size < 12:code_size += 1return bytes(result)passdecoder = GIFDecode()
for byte in data:decoder.feed(byte)print(decoder.version)
print(decoder.image_data)
print(decoder)

from PIL import Image
import struct# 读取文件
gif_path = "1.gif"
for _ in range(2):try:with open(gif_path, 'rb') as f:data = f.read()breakexcept:# 创建全白帧 50x50white_frame = Image.new('RGB', (5, 5), color=(255, 255, 255))# 创建全黑帧 50x50black_frame = Image.new('RGB', (5, 5), color=(0, 0, 0))# 保存为无限循环的GIF动画white_frame.save(gif_path,save_all=True,append_images=[black_frame],duration=200,loop=0)# 解码
def lzw_decode(compressed):# 初始化字典dictionary = {i: bytes([i]) for i in range(256)}next_code = 258result = bytearray()# 处理压缩数据buffer = 0bits_in_buffer = 0code_size = 9prev_code = Nonefor byte in compressed:buffer |= byte << bits_in_bufferbits_in_buffer += 8while bits_in_buffer >= code_size:code = buffer & ((1 << code_size) - 1)buffer >>= code_sizebits_in_buffer -= code_sizeif code == 256:  # 清除码dictionary = {i: bytes([i]) for i in range(256)}next_code = 258code_size = 9prev_code = Nonecontinueelif code == 257:  # 结束码return bytes(result)if prev_code is None:result.extend(dictionary[code])prev_code = codecontinueif code in dictionary:entry = dictionary[code]result.extend(entry)dictionary[next_code] = dictionary[prev_code] + entry[:1]else:entry = dictionary[prev_code] + dictionary[prev_code][:1]result.extend(entry)dictionary[next_code] = entrynext_code += 1prev_code = codeif next_code >= (1 << code_size) and code_size < 12:code_size += 1return bytes(result)# 压缩
def lzw_decompress(compressed):dictionary = {i: bytes([i]) for i in range(256)}next_code = 258result = bytearray()buffer = 0bits = 0code_size = 9prev = Nonefor byte in compressed:buffer |= byte << bitsbits += 8while bits >= code_size:code = buffer & ((1 << code_size) - 1)buffer >>= code_sizebits -= code_sizeif code == 256:dictionary = {i: bytes([i]) for i in range(256)}next_code = 258code_size = 9prev = Nonecontinueelif code == 257:return bytes(result)if prev is None:result.extend(dictionary[code])prev = codecontinueif code in dictionary:entry = dictionary[code]result.extend(entry)dictionary[next_code] = dictionary[prev] + entry[:1]else:entry = dictionary[prev] + dictionary[prev][:1]result.extend(entry)dictionary[next_code] = entrynext_code += 1prev = codeif next_code > (1 << code_size) - 1 and code_size < 12:code_size += 1return bytes(result)# 按照分块处理
print(data)
print(data.hex(' '))# dict_ex = {"ff":"应用程序扩展","f9":'图形控制扩展'}# 状态机
class GIFDecode:def __init__(self):self.state = "开头"  # 状态self.state_child = "开始"  # 子状态self.global_color = []  # 全局颜色self.local_color_all = []  # 局部颜色(全部)self.local_color_one = []  # 局部颜色(单个)self.image_data = []self.version = ""  # 版本self.buffer = bytearray()  # 缓冲字节self.screen_width = 0  # 宽度self.screen_height = 0  # 长度self.has_global_color = 0  # 存在全局颜色表self.color_resolution = 3  # 颜色深度self.sort_flag = 0  # 分类标志self.global_color_table_size = 0  # 全局颜色数量self.bg_color_index = 0  # 背景颜色索引self.color_block_size = 0  # 颜色块长度self.pixel_aspect_ratio = 0  # 像素高宽比self.application_size = 0  # 应用程序扩展字节长度self.application_text = 0  # 应用程序扩展文本self.application_child_size = 0  # 应用程序扩展子块字节长度self.loop_type = 0  # 循环类型self.loop_parameter = 0  # 循环参数self.graphic_control_size = 0  # 图像控制字符长度self.graphic_control_sign_transparent = 0  # 图像控制标志位透明色self.graphic_control_sign_dispose = 0  # 图像控制标志位处置方法 # 0不使用  1把图形移去 2恢复到背景色 3恢复到先前状态 4-7自定义self.graphic_control_sign_input = 0  # 图像控制标志位用户输入self.graphic_control_sign_customize = 0  # 图像控制标志位自定义self.graphic_control_delay = 0  # 图像控制延迟时间self.graphic_control_sign_transparent_index = 0  # 图像控制透明色索引self.image_offset_x = 0  # 图像左偏移量 即X轴self.image_offset_y = 0  # 图像顶部偏移量 即Y轴self.image_width = 0  # 画布宽度self.image_length = 0  # 画布长度self.local_color_sign_number = 0  # 0-2颜色多少self.local_color_sign_retain = 0  # 3-4保留位self.image_local_color_sign_sort = 0  # 5排序self.local_color_sign_interwoven = 0  # 6交织self.local_color_sign_has_color_table = 0  # 7局部颜色表标志self.local_color_size = 0  # 局部颜色表字节self.image_compressed_bit = 0  # 压缩位self.image_block_size = 0  # 图像子块字节self.image_bytes_data = b"" # 图片压缩字节数据def feed(self, byte):print(self.buffer)if self.state == "开头":self.buffer.append(byte)if len(self.buffer) == 6:if self.buffer == b'GIF87a' or self.buffer == b'GIF89a':self.state = "逻辑屏幕标识符"self.version = self.buffer.decode('ascii')self.buffer.clear()else:raise ValueError(f"无效的GIF版本: {bytes(self.buffer)}")elif self.state == "逻辑屏幕标识符":self.buffer.append(byte)if len(self.buffer) == 7:# 解析逻辑屏幕描述符self.screen_width = int.from_bytes(self.buffer[0:2], 'little')self.screen_height = int.from_bytes(self.buffer[2:4], 'little')# 解析标志字节flags = self.buffer[4]"""假设:m = 1(存在全局颜色表);cr = 3(颜色深度为 3,二进制 011);s = 0(不使用分类标志);pixel = 4(全局颜色列表大小为 4,二进制 100)。计算各字段的位位置m 占位 7 → 需左移 7 位(m << 7);cr 占位 6-4 → 需左移 4 位(cr << 4);s 占位 3 → 需左移 3 位(s << 3);pixel 占位 2-0 → 无需位移(直接取 pixel)"""self.has_global_color = (flags & 0b10000000)self.color_resolution = (flags & 0b01110000)self.sort_flag = (flags & 0b00001000) != 0self.global_color_table_size = 2 ** ((flags & 0b00000111) + 1)self.bg_color_index = self.buffer[5]self.pixel_aspect_ratio = self.buffer[6]if self.has_global_color:self.state = "全局颜色表"self.color_block_size = self.global_color_table_size * 3self.buffer.clear()else:self.state = "区块检测"self.buffer.clear()elif self.state == "全局颜色表":self.buffer.append(byte)if len(self.buffer) == self.color_block_size:# 解析全局颜色表self.global_color = [(self.buffer[i], self.buffer[i + 1], self.buffer[i + 2]) for i inrange(0, len(self.buffer), 3)]self.state = "区块检测"self.buffer.clear()elif self.state == "结束符检测":self.state_child = "开始"  # 重置子状态if byte == 0x00:self.state = "区块检测"else:raise ValueError(f"无效的结束符: {bytes(byte)}")elif self.state == "区块检测":self.buffer.append(byte)if byte == 0x3B:  # 检测结束return Trueif self.state_child == "开始":if len(self.buffer) == 1:if self.buffer[0] == 0x21:self.state_child = "拓展块"self.buffer.clear()elif self.buffer[0] == 0x2C:self.state = "图像标识符"self.state_child = "开始"  # 重置子状态self.buffer.clear()else:raise ValueError(f"无效的区块: {bytes(self.buffer)}")elif self.state_child == "拓展块":if self.buffer[0] == 0xFF:self.state = "应用程序扩展"elif self.buffer[0] == 0xF9:self.state = "图形控制扩展"self.state_child = "开始"  # 重置子状态self.buffer.clear()else:raise ValueError(f"无效的区块: {bytes(self.buffer)}")elif self.state == "应用程序扩展":self.buffer.append(byte)if self.state_child == "开始":if len(self.buffer) == 1:self.application_size = struct.unpack('<B', self.buffer)[0]self.state_child = "解析"self.buffer.clear()elif self.state_child == "解析":if len(self.buffer) == self.application_size:self.application_text = self.buffer[0:self.application_size].decode('ascii', errors='replace')self.state_child = "子块长度"self.buffer.clear()elif self.state_child == "子块长度":if len(self.buffer) == 1:self.application_child_size = struct.unpack('<B', self.buffer)[0]self.state_child = "子块解析"self.buffer.clear()elif self.state_child == "子块解析":if len(self.buffer) == self.application_child_size:self.loop_type = self.buffer[0]self.loop_parameter = self.buffer[1:2]self.state = "结束符检测"self.buffer.clear()elif self.state == "图形控制扩展":self.buffer.append(byte)if self.state_child == "开始":if len(self.buffer) == 1:self.graphic_control_size = struct.unpack('<B', self.buffer)[0]self.state_child = "解析"self.buffer.clear()elif self.state_child == "解析":if len(self.buffer) == self.graphic_control_size:sign = self.buffer[0]# 解析标志位self.graphic_control_sign_transparent = (sign & 0b00000001)self.graphic_control_sign_dispose = (sign & 0b00000010)self.graphic_control_sign_input = (sign & 0b00011100)self.graphic_control_sign_customize = (sign & 0b11100000)self.graphic_control_delay = struct.unpack('<H', self.buffer[1:3])[0] * 0.01self.graphic_control_sign_transparent_index = self.buffer[3]self.state = "结束符检测"self.buffer.clear()elif self.state == "图像标识符":self.buffer.append(byte)if self.state_child == "开始":if len(self.buffer) == 9:self.image_offset_x = struct.unpack('<H', self.buffer[0:2])[0]self.image_offset_y = struct.unpack('<H', self.buffer[2:4])[0]self.image_width = struct.unpack('<H', self.buffer[4:6])[0]self.image_length = struct.unpack('<H', self.buffer[6:8])[0]sign = self.buffer[8]"""- 第 0-2 位:局部颜色表大小(0 = 无局部颜色表)- 第 3-4 位:保留位(0)- 第 5 位:排序标志(0 = 未排序)- 第 6 位:交织标志(0 = 非交织)- 第 7 位:局部颜色表标志(0 = 无局部颜色表)"""self.local_color_sign_number = (sign & 0b00000111)self.local_color_sign_retain = (sign & 0b00011000)self.image_local_color_sign_sort = (sign & 0b00100000)self.local_color_sign_interwoven = (sign & 0b01000000)self.local_color_sign_has_color_table = (sign & 0b10000000)# 检测是否存在局部颜色表格if self.local_color_sign_has_color_table == 128:self.local_color_size = 2 ** (self.local_color_sign_number + 1) * 3self.state_child = "获取局部颜色表"self.buffer.clear()else:self.state_child = "解析图像"self.buffer.clear()elif self.state_child == "获取局部颜色表":if len(self.buffer) == self.local_color_size:# 获取局部颜色表放入局部颜色中self.local_color_one = [(self.buffer[i], self.buffer[i + 1], self.buffer[i + 2]) for i inrange(0, len(self.buffer), 3)]self.local_color_all.append(self.local_color_one)self.state_child = "解析图像"self.buffer.clear()elif self.state_child == "解析图像":if len(self.buffer) == 2:self.image_compressed_bit = self.buffer[0]self.image_block_size = self.buffer[1]self.state_child = "获取数据"self.buffer.clear()elif self.state_child == "获取数据":if len(self.buffer) == self.image_block_size:self.image_bytes_data += bytes(self.buffer) # 转换字节而非对象self.state_child = "检测获取结束"self.buffer.clear()elif self.state_child == "检测获取结束":if self.buffer[0] == 0x00:image_data = self.lzw_decode(self.image_bytes_data)if self.local_color_one: # 如果局部存在 则按照局部生成 如果局部不存在则按照全局生成self.image_data.append([self.local_color_one[image_data[i]] for i in range(len(image_data))])else:self.image_data.append([self.global_color[image_data[i]] for i in range(len(image_data))])self.state = "区块检测"self.state_child = "开始"# 重置数据self.image_bytes_data = b""self.local_color_one.clear()self.buffer.clear()else:self.image_block_size = self.buffer[0]self.state_child = "获取数据"self.buffer.clear()# 解码def lzw_decode(self, compressed):# 初始化字典dictionary = {i: bytes([i]) for i in range(256)}next_code = 258result = bytearray()# 处理压缩数据buffer = 0bits_in_buffer = 0code_size = 9prev_code = Nonefor byte in compressed:buffer |= byte << bits_in_bufferbits_in_buffer += 8while bits_in_buffer >= code_size:code = buffer & ((1 << code_size) - 1)buffer >>= code_sizebits_in_buffer -= code_sizeif code == 256:  # 清除码dictionary = {i: bytes([i]) for i in range(256)}next_code = 258code_size = 9prev_code = Nonecontinueelif code == 257:  # 结束码return bytes(result)if prev_code is None:result.extend(dictionary[code])prev_code = codecontinueif code in dictionary:entry = dictionary[code]result.extend(entry)dictionary[next_code] = dictionary[prev_code] + entry[:1]else:entry = dictionary[prev_code] + dictionary[prev_code][:1]result.extend(entry)dictionary[next_code] = entrynext_code += 1prev_code = codeif next_code >= (1 << code_size) and code_size < 12:code_size += 1return bytes(result)passdecoder = GIFDecode()
for byte in data:decoder.feed(byte)print(decoder.version)
print(decoder.image_data)
print(decoder)

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

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

相关文章

PCB设计实践(二十六)贴片电容与插件电容的全面解析:差异、演进与应用场景

一、核心差异&#xff1a;结构与性能对比 物理结构与封装形式 贴片电容&#xff08;Surface Mount Device, SMD&#xff09;采用扁平化设计&#xff0c;外形多为长方体或圆柱体&#xff0c;直接通过焊盘固定在电路板表面。其封装材料通常为陶瓷、聚合物或铝电解层&#xff0c;外…

XC7A200T-2FFG1156I FPGA AMD Xilinx Artix-7

XC7A200T-2FFG1156I 是 AMD Xilinx Artix-7 系列的一款高性能低功耗 FPGA&#xff0c;采用 28 nm 高性能低功耗&#xff08;HPL&#xff09;工艺制造&#xff0c;核心电压在 0.95 V–1.05 V 之间&#xff0c;可在 –40 C 至 100 C 工业温度范围内稳定工作 。 逻辑资源&#xff…

LVS + Keepalived + Nginx 高可用负载均衡系统实验

1. 项目背景 在现代 Web 应用中&#xff0c;高可用性和负载均衡是至关重要的需求。本项目旨在通过 LVS&#xff08;Linux Virtual Server&#xff09;实现流量分发&#xff0c;通过 Keepalived 实现高可用性&#xff0c;通过 Nginx 提供后端服务。该架构能够确保在单点故障的情…

window 显示驱动开发-视频内存供应和回收(一)

Windows 显示驱动程序模型 (WDDM) 1.2 及更高版本用户模式显示驱动程序必须使用内存套餐和回收功能&#xff08;从Windows 8开始提供&#xff09;&#xff0c;以减少本地内存和系统内存中临时表面所需的内存开销。 最低 WDDM 版本&#xff1a;1.2 最低 Windows 版本&#xff…

什么是VR场景?VR与3D漫游到底有什么区别

在数字化时代&#xff0c;虚拟现实&#xff08;Virtual Reality, 简称VR&#xff09;场景与3D漫游作为两种前沿技术&#xff0c;改变着人们的生活方式和体验模式。通过计算机模拟真实或假想的场景&#xff0c;让用户仿佛身临其境&#xff0c;并能与虚拟环境进行互动。尽管VR场景…

JAVA查漏补缺(2)

AJAX 什么是Ajax Ajax&#xff08;Asynchronous Javascript And XML&#xff09;&#xff0c;即是异步的JavaScript和XML&#xff0c;Ajax其实就是浏览器与服务器之间的一种异步通信方式 异步的JavaScript 它可以异步地向服务器发送请求&#xff0c;在等待响应的过程中&…

客服中心大模型应用演进路线:从传统服务到超级智能助手的转型

客服中心作为企业与客户沟通的重要桥梁&#xff0c;近年来经历了从人工服务到人工智能驱动的深刻变革。本文基于"客服中心大模型应用演进路线图"&#xff0c;详细解析客服中心从传统模式向AI驱动智能服务的转型历程&#xff0c;剖析每个发展阶段的特点、应用场景及关…

使用 OpenCV 实现“随机镜面墙”——多镜片密铺的哈哈镜效果

1. 引言 “哈哈镜”是一种典型的图像变形效果&#xff0c;通过局部镜面反射产生扭曲的视觉趣味。在计算机视觉和图像处理领域&#xff0c;这类效果不仅有趣&#xff0c;还能用于艺术创作、交互装置、视觉特效等场景。 传统的“哈哈镜”往往是针对整张图像做某种镜像或扭曲变换…

Python训练营打卡——DAY33(2025.5.22)

目录 简单的神经网络 一、PyTorch的安装 二、准备工作 三、数据的准备 四、模型架构定义 五、模型训练&#xff08;CPU版本&#xff09; 1. 定义损失函数和优化器 2. 开始循环训练 3. 可视化结果 六、通俗解释 1. 环境安装&#xff08;相当于买锅碗瓢盆&#xff09;…

目标检测 Lite-DETR(2023)详细解读

文章目录 迭代高级特征跨尺度融合高效的低层次特征跨尺度融合KDA&#xff1a;Key-aware Deformable Attention 论文翻译&#xff1a; CVPR 2023 | Lite DETR&#xff1a;计算量减少60%&#xff01;高效交错多尺度编码器-CSDN博客 DINO团队的 &#xff08;Lightweight Transfo…

ES(Elasticsearch) 基本概念(一)

Elasticsearch作为当前最流行的开源搜索和分析引擎&#xff0c;广泛应用于日志分析、全文搜索、业务智能等领域。Elasticsearch是一个基于 Apache Lucene 构建的分布式搜索和分析引擎、可扩展数据存储和矢量数据库。它针对生产级工作负载的速度和相关性进行了优化。使用 Elasti…

当物联网“芯”闯入纳米世界:ESP32-S3驱动的原子力显微镜能走多远?

上次咱们把OV2640摄像头“盘”得明明白白&#xff0c;是不是感觉ESP32-S3这小东西潜力无限&#xff1f;今天&#xff0c;咱们玩个更刺激的&#xff0c;一个听起来就让人肾上腺素飙升的挑战——尝试用ESP32-S3这颗“智慧芯”&#xff0c;去捅一捅科学界的“马蜂窝”&#xff0c;…

Excel合并单元格后,如何自动批量生成序号列

1.选择整列 2.组合键&#xff1a;CtrlG 3.定位条件&#xff0c;选择“空值” 4.在第一个框中输入“MAX(”&#xff0c;鼠标选中A1框&#xff0c;后加“&#xff1a;”&#xff0c;鼠标选中前方“A1”&#xff0c;按“F4”绝对引用&#xff0c;补全右括号&#xff0c;后输入“1…

TDengine 运维—容量规划

概述 若计划使用 TDengine 搭建一个时序数据平台&#xff0c;须提前对计算资源、存储资源和网络资源进行详细规划&#xff0c;以确保满足业务场景的需求。通常 TDengine 会运行多个进程&#xff0c;包括 taosd、taosadapter、taoskeeper、taos-explorer 和 taosx。 在这些进程…

Axure设计数字乡村可视化大屏:从布局到交互的实战经验分享

乡村治理正从传统模式向“数据驱动”转型。数字乡村可视化大屏作为数据展示的核心载体&#xff0c;不仅能直观呈现乡村发展全貌&#xff0c;还能为决策提供科学依据。本文以Axure为工具&#xff0c;结合实际案例&#xff0c;分享如何从零设计一个功能完备、交互流畅的数字乡村大…

从零基础到最佳实践:Vue.js 系列(1/10):《环境搭建与基础概念》

Vue.js 环境搭建与基础概念 关键点 Vue.js 是一个简单易用的前端框架&#xff0c;适合初学者快速上手。搭建开发环境需要安装 Node.js 和 npm/Yarn&#xff0c;推荐使用最新 LTS 版本。Vue CLI 是官方工具&#xff0c;可快速创建项目并提供开发服务器。Vue.js 基于 MVVM 模式&…

使用docker compose部署dify(大模型开发使用平台)

Dify是一款生成式 AI 应用中间件&#xff0c;面向有私有部署与数据合规需求的企业用户&#xff0c;推动企业向 AI 时代转型。平台支持无代码构建&#xff0c;业务人员即可快速搭建与调试 AI 应用&#xff0c;显著降低开发门槛。 参考 https://docs.dify.ai/zh-hans/getting-s…

npm 安装时 SSL 证书过期问题笔记

问题描述: npm error code CERT_HAS_EXPIRED npm error errno CERT_HAS_EXPIRED npm error request to https://registry.npm.taobao.org/axios failed, reason: certificate has expired 这表明当前配置的 npm 镜像源&#xff08;淘宝镜像 https://registry.npm.taobao.org&…

力扣HOT100之二叉树: 236. 二叉树的最近公共祖先

果然&#xff0c;这道题二刷还是不会做&#xff0c;回去看卡尔视频了。结合灵神的题解&#xff0c;我对这道题有了一些新的理解。 首先这道题还是用递归来做&#xff0c;由于我们需要计算两个节点的最近公共祖先&#xff0c;一定是从下往上来遍历&#xff0c;只有先判断左右子树…

Word 转 HTML API 接口

Word 转 HTML API 接口 图像/转换 Word 文档转换为 HTML 文件转换 / 超高精度与还原度 文件转换 / Word。 1. 产品功能 超高精度与还原度的 HTML 文件转换&#xff1b;支持将 Word 文档转换为 HTML 格式&#xff1b;支持 .doc 和 .docx 格式&#xff1b;保持原始 Word 文档的…