Python将PDF转成图片—PyMuPDF和pdf2image
前言:在最近的測試中遇到一個與PDF相關的測試需求,其中有一個過程是將PDF轉換成圖片,然后對圖片進行測試。
粗略的試了好幾種方式,其中語言嘗試了Python和Java,總體而言所找到的Python方式相對比Java更快一些,更簡單一些。
下面首先分享一下Python將PDF轉換成圖片,Java后續有時間在進行分享。
需求:我需要先將PDF轉換成為PNG圖片,并截取圖片的一部分存儲,然后作為測試目標進行測試。
操作:
1、PDF轉PNG圖片
2、對PNG圖片進行指定區域截圖,在另存到指定文件夾下
針對截圖此處所找到的方法如上一篇博客:
Python圖片裁剪的兩種方式——Pillow和OpenCV
1、PyMuPDF將PDF轉換成圖片
import sys, fitz import os import datetimedef 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=96zoom_x = 1.33333333 #(1.33333333-->1056x816) (2-->1584x1224)zoom_y = 1.33333333mat = 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 datetimedef 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=96zoom_x = 1.33333333 #(1.33333333-->1056x816) (2-->1584x1224)zoom_y = 1.33333333mat = 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 documentfor pg in range(pdfDoc.pageCount): # iterate through the pagespage = pdfDoc[pg]rotate = int(0)# 每個尺寸的縮放系數為1.3,這將為我們生成分辨率提高2.6的圖像# 此處若是不做設置,默認圖片大小為:792X612, dpi=96zoom_x = 1.33333333 #(1.33333333-->1056x816) (2-->1584x1224)zoom_y = 1.33333333mat = 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.3333clip = 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 PNGif __name__ == "__main__":pdfPath = '../path/demo.pdf'imagePath = '../path/image'#pyMuPDF_fitz(pdfPath, imagePath)#只是轉換圖片pyMuPDF2_fitz(pdfPath, imagePath)#指定想要的區域轉換成圖片當然上面這種是綜合下來最快的,另外PyMuPDF還可以對PDF進行追加刪除之類的功能。
下面再介紹一種方法pdf2image
2、pdf2image將PDF轉換成圖片
pdf2image也是個包裝器,真正的轉換工具是poppler
GitHub地址:https://github.com/Belval/pdf2image ,上面也有相關的配置說明。
1、安裝pdf2image: pip install pdf2image
2、Windows安裝配置poppler(這里只介紹Windows,Mac和Linux去上面Github地址里面參考官網)
Windows用戶必須為Windows安裝poppler (http://blog.alivate.com.au/poppler-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. 這種轉換是直接寫入到磁盤上的,因此不會占用太多內存。#下面的寫法直接寫入到內存,images = convert_from_path(pdfPath, dpi=96)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=96)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)以下是參數定義:
pdf_path --> 要轉換的PDF文檔路徑
dpi -->? DPI中的圖像質量(默認為200),Windows默認為96dpi
output_folder --> 將生成的圖像寫入文件夾(而不是直接寫入內存)若是path不做指定的話,path的默認地址是:C:\Users\pzhang7\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應該也可以對指定區域進行截取,暫時還沒詳細研究其方法,因為已經找到更快的方法解決問題了,對比如下所示:
3、比較PyMuPDF和pdf2image
以下是對一份75頁的PDF,輸出DPI=96的時間性能對比,pdf2image使用的是默認線程數,下面的對比并沒有設置多線程,使用多線程會快一點,當線程數設為5的時候,速度是9秒。
可以看出使用pyMuPDF_Fitz明顯快一倍多,最終選取了這種方式。
4、Wand將PDF轉換成圖片
和pdf2image一樣,wand都是包裝接口(bindings),而實際進行轉換的工具是ImageMagick.
Wind官網:
http://docs.wand-py.org/en/0.5.6/
ImageMagick官網:
https://imagemagick.org/script/download.php#windows
from?wand.image?import?Image filename="somefile.pdf" with(Image(filename=filename, resolution=120)) as source: images = source.sequencepages = len(images) for i in range(pages):n = i + 1newfilename = filename[:-4] + str(n) + '.jpeg'Image(images[i]).save(filename=newfilename)由于問題已經解決,而且性能也還不錯,就沒有具體去研究Wind這種方式了,感興趣的可以去看看。
萬水千山總是情,點個“在看” 行不行
?
總結
以上是生活随笔為你收集整理的Python将PDF转成图片—PyMuPDF和pdf2image的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python 利用PyQt5写一个简易的
- 下一篇: Pandas熊猫框架