You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python标准库实现PNG/GIF与二进制文件转换及图像操作?

解决思路与代码实现

你的核心错误是直接修改了PNG文件的整体二进制比特,这会破坏PNG的格式结构(文件头、压缩块、校验信息等),导致无法还原为正常图像。正确的逻辑是解析出图像的原始像素数据,操作像素后重新编码为PNG,以下是基于Python标准库的完整实现:

关键步骤说明

  1. 解析PNG文件:提取图像的宽、高、颜色格式等元信息,解压出原始像素数据
  2. 操作像素:针对像素数据执行黑白翻转等图像操作
  3. 保存/读取二进制文件:将原始/修改后的像素数据存入.bin/.dat文件
  4. 重新生成PNG:将修改后的像素数据按照PNG格式规范重新编码

完整代码实现

import struct
import zlib

def parse_png(file_path):
    with open(file_path, 'rb') as f:
        # 验证PNG文件头
        header = f.read(8)
        assert header == b'\x89PNG\r\n\x1a\n', "输入文件不是PNG格式"
        
        ihdr_info = None
        idat_raw = b''
        
        while True:
            # 读取块长度(大端4字节)
            len_bytes = f.read(4)
            if not len_bytes:
                break
            chunk_len = struct.unpack('!I', len_bytes)[0]
            # 读取块类型
            chunk_type = f.read(4)
            # 读取块数据
            chunk_data = f.read(chunk_len)
            # 跳过CRC校验(练习阶段简化处理)
            f.read(4)
            
            if chunk_type == b'IHDR':
                # 解析图像元信息:宽、高、位深度、颜色类型等
                width, height, bit_depth, color_type, _, _, _ = struct.unpack('!IIBBBBB', chunk_data)
                ihdr_info = (width, height, bit_depth, color_type)
            elif chunk_type == b'IDAT':
                # 合并所有IDAT块的压缩数据
                idat_raw += chunk_data
            elif chunk_type == b'IEND':
                break
        
        assert ihdr_info is not None, "未找到PNG图像头块"
        width, height, bit_depth, color_type = ihdr_info
        
        # 解压像素数据(包含每行的过滤字节)
        decompressed_data = zlib.decompress(idat_raw)
        
        # 去除过滤字节,提取纯像素数据
        pixel_data = b''
        bytes_per_pixel = (bit_depth // 8) * (3 if color_type == 2 else 1)  # 支持灰度/简单RGB
        line_stride = bytes_per_pixel * width
        for i in range(height):
            # 跳过每行开头的过滤类型字节
            pixel_data += decompressed_data[i*(line_stride+1)+1 : (i+1)*(line_stride+1)]
        
        return width, height, bit_depth, color_type, pixel_data

def flip_gray_pixels(pixel_data):
    # 8位灰度图黑白翻转:255 - 每个像素的灰度值
    return bytearray([255 - b for b in pixel_data])

def flip_rgb_pixels(pixel_data):
    # RGB图黑白翻转:每个颜色通道都取反
    flipped = bytearray()
    for i in range(0, len(pixel_data), 3):
        flipped.append(255 - pixel_data[i])
        flipped.append(255 - pixel_data[i+1])
        flipped.append(255 - pixel_data[i+2])
    return flipped

def build_png(width, height, bit_depth, color_type, pixel_data):
    # 重新添加过滤字节(使用0型无过滤)
    bytes_per_pixel = (bit_depth // 8) * (3 if color_type == 2 else 1)
    line_stride = bytes_per_pixel * width
    filtered_data = b''
    for i in range(height):
        filtered_data += b'\x00'  # 过滤类型0
        filtered_data += pixel_data[i*line_stride : (i+1)*line_stride]
    
    # 压缩处理后的像素数据
    compressed_data = zlib.compress(filtered_data, level=6)
    
    # 组装PNG文件
    png_content = b'\x89PNG\r\n\x1a\n'
    
    # 添加IHDR块
    ihdr_struct = struct.pack('!IIBBBBB', width, height, bit_depth, color_type, 0, 0, 0)
    ihdr_len = struct.pack('!I', len(ihdr_struct))
    ihdr_crc = struct.pack('!I', zlib.crc32(b'IHDR' + ihdr_struct))
    png_content += ihdr_len + b'IHDR' + ihdr_struct + ihdr_crc
    
    # 添加IDAT块(简化为单个块)
    idat_len = struct.pack('!I', len(compressed_data))
    idat_crc = struct.pack('!I', zlib.crc32(b'IDAT' + compressed_data))
    png_content += idat_len + b'IDAT' + compressed_data + idat_crc
    
    # 添加IEND块
    iend_len = struct.pack('!I', 0)
    iend_crc = struct.pack('!I', zlib.crc32(b'IEND'))
    png_content += iend_len + b'IEND' + b'' + iend_crc
    
    return png_content

# 主执行流程
if __name__ == '__main__':
    # 1. 解析原始PNG
    width, height, bit_depth, color_type, raw_pixels = parse_png('984269812_1.png')
    
    # 2. 保存原始像素到.bin文件
    with open('original_pixels.bin', 'wb') as f:
        f.write(raw_pixels)
    
    # 3. 执行黑白翻转(根据图像类型选择对应函数)
    if color_type == 0 and bit_depth == 8:
        flipped_pixels = flip_gray_pixels(raw_pixels)
    elif color_type == 2 and bit_depth == 24:
        flipped_pixels = flip_rgb_pixels(raw_pixels)
    else:
        raise ValueError("当前仅支持8位灰度图或24位RGB图")
    
    # 4. 保存修改后的像素到.dat文件
    with open('flipped_pixels.dat', 'wb') as f:
        f.write(flipped_pixels)
    
    # 5. 从.dat读取像素并生成翻转后的PNG
    with open('flipped_pixels.dat', 'rb') as f:
        flipped_pixels = f.read()
    
    new_png = build_png(width, height, bit_depth, color_type, flipped_pixels)
    with open('flipped_image.png', 'wb') as f:
        f.write(new_png)

代码说明

  • 仅依赖Python标准库:struct用于解析二进制结构,zlib处理PNG的压缩/解压
  • 支持8位灰度图和24位RGB图的黑白翻转,如需处理二值图(1位),可修改翻转逻辑为bytearray([0xff ^ b for b in pixel_data])
  • .bin/.dat文件存储的是原始像素数据(非PNG压缩数据),符合你理解图像与二进制关系的练习目的

内容的提问来源于stack exchange,提问作者The Pointer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 19:54:59