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
繁體地址,請注明出處:http://hk.pswp.cn/news/906611.shtml
英文地址,請注明出處:http://en.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 文檔的…