admin管理员组文章数量:1630184
2023-04-30更新 PyMuPDF=1.21.1
import datetime
# PyMuPDF=1.21.1
import fitz
def pdf2img(pdf_path, img_path):
pdfDoc = fitz.open(pdf_path)
for page in pdfDoc.pages():
# 将页面转换为图片
rotate = int(0)
# 每个尺寸的缩放系数为1.3,这将为我们生成分辨率提高2.6的图像。
# 此处若是不做设置,默认图片大小为:792X612, dpi=72
# pix = page.get_pixmap()
zoom_x = 6
zoom_y = 6
# (1.33333333-->1056x816) (2-->1584x1224) (3-->3572x2526)
# x和y的值越大越清晰,图片越大,但处理也越耗时间,这里取决于你想要图片的清晰度
# 默认为1.333333,一般日常使用3就够了,不能设置太大,太大容易使电脑死机
mat = fitz.Matrix(zoom_x, zoom_y)
pix = page.get_pixmap(matrix=mat, dpi=None, colorspace='rgb', alpha=False)
imageName = pdf_path.split("\\")[len(pdf_path.split("\\"))-1]
target_img_name = img_path + '\\%s.png' % imageName #构造图片名字
# 保存图片
pix.save(target_img_name)
if __name__ == '__main__':
startTime_pdf2img = datetime.datetime.now() # 开始时间
pdf_path = "E:\\PDF\\demo\\PDF\\test.PDF"
img_path = "E:\\PDF\\demo\\img"
pdf2img(pdf_path, img_path)
endTime_pdf2img = datetime.datetime.now() # 结束时间
print('===========END==========')
print('PDF转换图片累计耗时:【%s秒】' % (endTime_pdf2img - startTime_pdf2img).seconds)
前言:在最近的测试中遇到一个与PDF相关的测试需求,其中有一个过程是将PDF转换成图片,然后对图片进行测试。
粗略的试了好几种方式,其中语言尝试了Python和Java,总体而言所找到的Python方式相对比Java更快一些,更简单一些。
下面首先分享一下Python将PDF转换成图片,Java+PDFBox将PDF转成图片
需求:我需要先将PDF转换成为PNG图片,并截取图片的一部分存储,然后作为测试目标进行测试。
操作:
1、PDF转PNG图片
2、对PNG图片进行指定区域截图,在另存到指定文件夹下
针对截图此处所找到的方法如上一篇文章:Python图片裁剪的两种方式——Pillow和OpenCV
1、PyMuPDF将PDF转换成图片
pip install PyMuPDF
import sys, fitz
import os
import datetime
def pyMuPDF_fitz(pdfPath, imagePath):
startTime_pdf2img = datetime.datetime.now()#开始时间
print("imagePath="+imagePath)
pdfDoc = fitz.open(pdfPath)
for pg in range(pdfDoc.pageCount):
page = pdfDoc[pg]
rotate = int(0)
# 每个尺寸的缩放系数为1.3,这将为我们生成分辨率提高2.6的图像。
# 此处若是不做设置,默认图片大小为:792X612, dpi=72
zoom_x = 1.33333333 #(1.33333333-->1056x816) (2-->1584x1224)
zoom_y = 1.33333333
mat = fitz.Matrix(zoom_x, zoom_y).preRotate(rotate)
pix = page.getPixmap(matrix=mat, alpha=False)
if not os.path.exists(imagePath):#判断存放图片的文件夹是否存在
os.makedirs(imagePath) # 若图片文件夹不存在就创建
pix.writePNG(imagePath+'/'+'images_%s.png' % pg)#将图片写入指定的文件夹内
endTime_pdf2img = datetime.datetime.now()#结束时间
print('pdf2img时间=',(endTime_pdf2img - startTime_pdf2img).seconds)
if __name__ == "__main__":
pdfPath = '../path/demo.pdf'
imagePath = '../path/image'
pyMuPDF_fitz(pdfPath, imagePath)
PDF文档页数超过100页的话需要十几秒,因为先转换成一整张1056X816的图片,再对本地文件中的所有图片进行遍历截图,时间上比较慢,通过查看文档发现:
还可以在转换的同时指定图片的大小,对图片指定区域进行截取,这样快很多,一步到位,省去了二次截图的过程,前提使我们必须要知道想要截取哪一块区域并保存
官方示例代码如下:
#下面的这段代码就是想要从一页PDF的中心点为起点截取到右下角的区域,截取整张图的1/4.
>>> mat = fitz.Matrix(2, 2) # 在每个方向缩放因子2
>>> rect = page.rect # 页面的矩形
>>> mp = rect.tl + (rect.br - rect.tl) * 0.5 # 矩形的中心
>>> clip = fitz.Rect(mp, rect.br) # 我们想要的剪切区域
>>> pix = page.getPixmap(matrix = mat, clip = clip)
实际用到的例子是:
整张图片导出之后是1056*816,但是我想要的是这张图片最底部的部分1056*75,相当于PDF文档的页脚部分。
import sys, fitz
import os
import datetime
def pyMuPDF_fitz(pdfPath, imagePath):
startTime_pdf2img = datetime.datetime.now()#开始时间
pdfDoc = fitz.open(pdfPath)
for pg in range(pdfDoc.pageCount):
page = pdfDoc[pg]
rotate = int(0)
# 每个尺寸的缩放系数为1.3,这将为我们生成分辨率提高2.6的图像。
# 此处若是不做设置,默认图片大小为:792X612, dpi=72
zoom_x = 1.33333333 #(1.33333333-->1056x816) (2-->1584x1224)
zoom_y = 1.33333333
mat = fitz.Matrix(zoom_x, zoom_y).preRotate(rotate)
pix = page.getPixmap(matrix=mat, alpha=False)
if not os.path.exists(imagePath):#判断存放图片的文件夹是否存在
os.makedirs(imagePath) # 若图片文件夹不存在就创建
pix.writePNG(imagePath+'/'+'images_%s.png' % pg)#将图片写入指定的文件夹内
endTime_pdf2img = datetime.datetime.now()#结束时间
print('pdf2img时间=',(endTime_pdf2img - startTime_pdf2img).seconds)
def pyMuPDF2_fitz(pdfPath, imagePath):
pdfDoc = fitz.open(pdfPath) # open document
for pg in range(pdfDoc.pageCount): # iterate through the pages
page = pdfDoc[pg]
rotate = int(0)
# 每个尺寸的缩放系数为1.3,这将为我们生成分辨率提高2.6的图像
# 此处若是不做设置,默认图片大小为:792X612, dpi=72
zoom_x = 1.33333333 #(1.33333333-->1056x816) (2-->1584x1224)
zoom_y = 1.33333333
mat = fitz.Matrix(zoom_x, zoom_y).preRotate(rotate) # 缩放系数1.3在每个维度 .preRotate(rotate)是执行一个旋转
rect = page.rect # 页面大小
mp = rect.tl + (rect.bl - (0,75/zoom_x)) # 矩形区域 56=75/1.3333
clip = fitz.Rect(mp, rect.br) # 想要截取的区域
pix = page.getPixmap(matrix=mat, alpha=False, clip=clip) # 将页面转换为图像
if not os.path.exists(imagePath):
os.makedirs(imagePath)
pix.writePNG(imagePath+'/'+'psReport_%s.png' % pg)# store image as a PNG
if __name__ == "__main__":
pdfPath = '../path/demo.pdf'
imagePath = '../path/image'
#pyMuPDF_fitz(pdfPath, imagePath)#只是转换图片
pyMuPDF2_fitz(pdfPath, imagePath)#指定想要的区域转换成图片
当然上面这种是综合下来最快的,另外再介绍一种方法pdf2image
2、pdf2image 将PDF转换成图片
pdf2image也是个包装器,真正的转换工具是poppler
GitHub地址:GitHub - Belval/pdf2image: A python module that wraps the pdftoppm utility to convert PDF to PIL Image object ,上面也有相关的配置说明。
1、安装pdf2image: pip install pdf2image
2、Windows安装配置poppler(这里只介绍Windows,Mac和Linux去上面Github地址里面参考官网)
Windows用户必须为Windows安装poppler (Poppler for Windows),然后将bin/文件夹添加到PATH(开始>输入env>编辑系统环境变量>环境变量...>系统变量>Path)
注意这里配置之后需要重启一下电脑才会生效,不然会报如下错误:
ERROE:FileNotFoundError: [WinError 2] The system cannot find the file specified
During handling of the above exception, another exception occurred:
3、pip install pillow (如果你还没有安装过的话)
from pdf2image import convert_from_path,convert_from_bytes
import tempfile
from pdf2image.exceptions import (
PDFInfoNotInstalledError,
PDFPageCountError,
PDFSyntaxError
)
def pdf2image2(pdfPath, imagePath, pageNum):
#方法一:
#convert_from_path('a.pdf', dpi=500, "output",fmt="JPEG",output_file="ok",thread_count=4)
#这会将a.pdf转换成在output文件夹下形如ok_线程id-页码.jpg的一些文件。
#若不指定thread_count则默认为1,并且在文件名中显示id. 这种转换是直接写入到磁盘上的,因此不会占用太多内存。
#下面的写法直接写入到内存,默认是C:\Users\pppp\AppData\Local\Temp\生成的uuid4名字
images = convert_from_path(pdfPath, dpi=72)
for image in images:
if not os.path.exists(imagePath):
os.makedirs(imagePath)
image.save(imagePath+'/'+'psReport_%s.png' % images.index(image), 'PNG')
#方法二:
images = convert_from_bytes(open('/home/belval/example.pdf', 'rb').read())
for image in images:
if not os.path.exists(imagePath):
os.makedirs(imagePath)
image.save(imagePath+'/'+'psReport_%s.png' % images.index(image), 'PNG')
#方法三,也是最推荐的方法
with tempfile.TemporaryDirectory() as path:
images_from_path = convert_from_path(pdfPath, output_folder=path, dpi=72)
for image in images_from_path:
if not os.path.exists(imagePath):
os.makedirs(imagePath)
image.save(imagePath+'/'+'psReport_%s.png' % images_from_path.index(image), 'PNG')
print(images_from_path)
以下是参数定义:
convert_from_path(pdf_path, dpi=200, output_folder=None, first_page=None, last_page=None, fmt='ppm', thread_count=1, userpw=None, use_cropbox=False, strict=False, transparent=False, single_file=False, output_file=str(uuid.uuid4()), poppler_path=None)
convert_from_bytes(pdf_file, dpi=200, output_folder=None, first_page=None, last_page=None, fmt='ppm', thread_count=1, userpw=None, use_cropbox=False, strict=False, transparent=False, single_file=False, output_file=str(uuid.uuid4()), poppler_path=None)
pdf_path --> 要转换的PDF文档路径
dpi --> DPI中的图像质量(默认为200),Windows默认为96dpi
output_folder --> 将生成的图像写入文件夹(而不是直接写入内存)若是path不做指定的话,path的默认地址是:C:\Users\pppp\AppData\Local\Temp\生成的uuid4。
first_page --> 从哪一页开始转换,默认是PDF的第一页
last_page --> 转换到哪一页,默认是PDF的最后一页
fmt --> 输出图像格式默认格式是ppm,还可以设置为png和jpeg等
thread_count --> 允许生成多少个线程进行处理,一般不超过4个线程;
userpw --> PDF的密码(若有密码的话需要添加)
use_cropbox --> 使用cropbox而不是mediabox
strict --> 参数允许您使用自定义类型PDFSyntaxError捕获pdftoppm语法错误
transparent --> 参数允许生成没有背景的图像,而不是通常的白色图像(为此需要pdftocairo)
single_file --> 使用pdftoppm / pdftocairo中的-singlefile选项
output_file --> 输出文件名是什么
poppler_path --> 查找poppler二进制文件的路径,允许用户使用poppler_path指定poppler的安装路径;默认不指定的话需要将bin添加到系统PATH
pdf2image应该也可以对指定区域进行截取,暂时还没详细研究其方法,因为已经找到更快的方法解决问题了,对比如下所示:
比较PyMuPDF和pdf2image
以下是对一份75页的PDF,输出DPI=96的时间性能对比,pdf2image使用的是默认线程数,下面的对比并没有设置多线程,使用多线程会快一点,当线程数设为5的时候,速度是9秒。
PDF转换图片的方式 | pdf2image | pyMuPDF_Fitz | pyMuPDF2_Fitz (对图片进行指定区域裁剪) |
---|---|---|---|
时间(秒) | 11.88 | 4.5 | 0.9 |
依赖包 | pip install pdf2image poppler pip install pillow | pip install PyMuPDF |
可以看出使用pyMuPDF_Fitz明显快一倍多,最终选取了这种方式。
3、Wand将PDF转换成图片
和pdf2image一样,wand都是包装接口(bindings),而实际进行转换的工具是ImageMagick.
Wind官网:Wand — Wand 0.5.6
ImageMagick: ImageMagick – Download
from wand.image import Image
filename="somefile.pdf"
with(Image(filename=filename, resolution=120)) as source:
images = source.sequence
pages = len(images)
for i in range(pages):
n = i + 1
newfilename = filename[:-4] + str(n) + '.jpeg'
Image(images[i]).save(filename=newfilename)
由于问题已经解决,而且性能也还不错,就没有具体去研究Wind这种方式了,感兴趣的可以去看看。
2020/10/07 更新
感谢gingeers同学 ,很细心,查阅了API官方文档,发现文中的错误。
现在已经更正
默认的DPI是72,不是之前的96。
版权声明:本文标题:【PyMuPDF和pdf2image】Python将PDF转成图片 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dongtai/1729067942a1184744.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论