如何用pikepdf从PDF的Image XObject创建PIL.Image对象
解决PDF Image XObject转PIL.Image的问题
问题根源
PDF中的Image XObject不是标准图像格式(如PNG/JPG),它仅包含原始像素数据,并附带PDF特有的编码滤镜、颜色空间定义,直接用Image.open()读取会因为缺少文件头、不识别PDF颜色空间而报错。
解决方案步骤
- 解码PDF图像流:先处理FlateDecode等压缩滤镜,得到原始像素数据
- 解析图像参数:提取宽度、高度、位深、颜色空间等信息
- 适配颜色空间:将PDF的Indexed/ICCBased等颜色空间转换为PIL支持的RGB/灰度模式
- 构建PIL图像:用
Image.frombytes()从原始像素数据创建图像
完整代码实现
import pikepdf from PIL import Image with pikepdf.open("./doc.pdf") as pdf: for page in pdf.pages: for image_key, image_data in page.images.items(): # 解码PDF图像流,自动处理FlateDecode等滤镜 decoded_pixel_data = image_data.decode() img_metadata = image_data.as_dict() # 提取核心图像参数 width = img_metadata["/Width"] height = img_metadata["/Height"] bits_per_component = img_metadata["/BitsPerComponent"] # 处理Indexed颜色空间(你的示例中是这种情况) color_space = img_metadata["/ColorSpace"] if color_space[0] == "/Indexed": # 获取索引查找表的解码数据(每个索引对应3字节RGB) lookup_stream = color_space[3] lookup_table = lookup_stream.decode() # 4位深度:每个字节包含两个像素索引,转换为RGB像素 rgb_pixels = [] for byte in decoded_pixel_data: # 拆分高4位和低4位的索引 idx_high = (byte >> 4) & 0x0F idx_low = byte & 0x0F # 从查找表中取出对应RGB值 rgb_pixels.extend(lookup_table[idx_high*3 : (idx_high+1)*3]) rgb_pixels.extend(lookup_table[idx_low*3 : (idx_low+1)*3]) # 创建RGB模式的PIL图像 img = Image.frombytes("RGB", (width, height), bytes(rgb_pixels)) else: # 适配其他常见颜色空间(如DeviceRGB/DeviceGray) pil_mode = "RGB" if bits_per_component == 8 else "L" img = Image.frombytes(pil_mode, (width, height), decoded_pixel_data) # 无损PNG压缩保存(compress_level=0为无损) img.save(f"{image_key}_extracted.png", "PNG", optimize=True, compress_level=0)
关键说明
- 解码流:必须用
image_data.decode()替代get_raw_stream_buffer(),后者返回的是未解压的原始PDF流,无法被PIL识别。 - 颜色空间处理:PDF的Indexed颜色空间需要手动将索引映射为RGB值,PIL不原生支持这种PDF特有的颜色模式。
- 图像创建:用
Image.frombytes()而非Image.open(),因为前者直接处理原始像素数据,不需要标准图像文件头。
内容的提问来源于stack exchange,提问作者eigenVector5
相关产品推荐
相关产品推荐

