如何用Python快速将PDF条带转为1位BMP?(PyVIPS/PIL可选)
问题
项目需求为将PDF文档转换为尺寸约65000×4800px的1位BMP图像条带,核心要求是转换速度,画质要求较低。目前已通过PyVIPS快速生成PNG格式的图像条带,但使用ImageMagick将PNG转换为1位BMP时速度极慢(单条带耗时超10秒),现寻求Python环境下的快速转换方案,不局限于PyVIPS。
当前使用工具与代码
ImageMagick转换命令
magick Billy_04.jpg -resize 65000x -dither FloydSteinberg -remap pattern:gray50 -colors 2 -monochrome BMP3:Billy_04_converted.bmp
PyVIPS生成PNG条带脚本
#!/usr/bin/python3 import sys import pyvips import math destinationwidth = 60000 destinationheight = 4700 image = pyvips.Image.new_from_file(sys.argv[1]) for i in range(1): # 示例仅处理第一页 # for i in range(image.get('n-pages')): image = pyvips.Image.new_from_file(sys.argv[1], page=i, scale=10) mywidth = image.width myheight = image.height scale_in_width = destinationwidth/mywidth stripheight = math.floor(destinationheight/scale_in_width) stripcount = myheight/stripheight stripcountInt = math.ceil(stripcount) print("Width = ", mywidth, " height = ", myheight) print("Strip height = ", stripheight, " amount of strips = ", stripcount , " (rounded to) ", stripcountInt) theheight = stripheight verticalpos = 0 for s in range(stripcountInt): print(f"Strip number {s} at {s*stripheight} : ") thestrip = image.crop(0, s*stripheight, mywidth, theheight) verticalpos += theheight # 避免超出图像范围 if (verticalpos + stripheight) > myheight: theheight = myheight - verticalpos - 1 thestrip.write_to_file(f"Pstrip_{s}.png")
解决方案
以下几种方案均能大幅提升转换速度,推荐优先选择和现有PyVIPS流程整合的方案:
方案1:PyVIPS直接生成1位BMP条带
利用PyVIPS的图像处理能力,在生成条带的环节直接完成二值化和BMP格式保存,省去PNG中间文件的读写和ImageMagick的二次处理,这是效率最高的方案。
修改后的脚本示例
#!/usr/bin/python3 import sys import pyvips import math destinationwidth = 60000 destinationheight = 4700 image = pyvips.Image.new_from_file(sys.argv[1]) for i in range(1): # 示例仅处理第一页 # for i in range(image.get('n-pages')): image = pyvips.Image.new_from_file(sys.argv[1], page=i, scale=10) mywidth = image.width myheight = image.height scale_in_width = destinationwidth/mywidth stripheight = math.floor(destinationheight/scale_in_width) stripcount = myheight/stripheight stripcountInt = math.ceil(stripcount) print("Width = ", mywidth, " height = ", myheight) print("Strip height = ", stripheight, " amount of strips = ", stripcount , " (rounded to) ", stripcountInt) theheight = stripheight verticalpos = 0 for s in range(stripcountInt): print(f"Strip number {s} at {s*stripheight} : ") thestrip = image.crop(0, s*stripheight, mywidth, theheight) verticalpos += theheight # 避免超出图像范围 if (verticalpos + stripheight) > myheight: theheight = myheight - verticalpos - 1 # 直接处理为1位图像并保存为BMP # 画质要求低,用简单的阈值二值化(比抖动更快) gray_strip = thestrip.colourspace("b-w") binary_strip = gray_strip > 128 # 阈值可根据需求调整 # 保存为1位深度的BMP binary_strip.write_to_file(f"Pstrip_{s}.bmp", depth=1)
优势
- 完全整合现有流程,无需额外工具
- libvips流式处理对大图像友好,内存占用低,速度远超ImageMagick
- 省去中间PNG文件的IO开销
方案2:使用Pillow快速转换PNG为1位BMP
如果需要保留现有PNG生成流程,可使用Pillow库快速完成二值化和BMP保存,速度远快于ImageMagick。
代码示例
from PIL import Image def png_to_1bit_bmp(png_path, bmp_path): with Image.open(png_path) as img: # 快速二值化(画质要求低,无需抖动) gray_img = img.convert("L") binary_img = gray_img.point(lambda x: 0 if x < 128 else 255, "1") # 保存为1位BMP binary_img.save(bmp_path, format="BMP") # 调用示例 png_to_1bit_bmp("Pstrip_0.png", "Pstrip_0.bmp")
优势
- 代码简洁,上手快
- 单条带处理耗时可控制在1秒内,远优于ImageMagick
方案3:优化ImageMagick命令
如果必须使用ImageMagick,可通过简化参数大幅提升速度(画质要求低的前提下):
magick Billy_04.jpg -resize 65000x\! -filter point -threshold 50% BMP3:Billy_04_converted.bmp
参数优化说明
-resize 65000x\!:强制指定宽度,避免按比例缩放(匹配需求)-filter point:使用最近邻插值,速度远快于默认的Lanczos插值-threshold 50%:简单阈值二值化,替代复杂的抖动和颜色映射,速度提升明显- 移除冗余的
-dither、-remap、-colors 2、-monochrome参数,-threshold已完成二值化
优势
- 无需修改现有PyVIPS流程
- 优化后速度比原命令提升数倍
内容的提问来源于stack exchange,提问作者MyICQ
相关产品推荐
相关产品推荐

