[英]How to solve MemoryError using Python 3.7 pdf2image library?
我正在使用 Python PDF2Image 庫運行一個簡單的 PDF 到圖像轉換。 我當然可以理解這個庫正在跨越最大內存閾值以達到這個錯誤。 但是, PDF是 6.6 MB(大約),那么為什么它會占用 GB 的內存來引發內存錯誤呢?
Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:06:47) [MSC v.1914 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from pdf2image import convert_from_path
>>> pages = convert_from_path(r'C:\Users\aakashba598\Documents\pwc-annual-report-2017-2018.pdf', 200)
Exception in thread Thread-3:
Traceback (most recent call last):
File "C:\Users\aakashba598\AppData\Local\Programs\Python\Python37-32\lib\threading.py", line 917, in _bootstrap_inner
self.run()
File "C:\Users\aakashba598\AppData\Local\Programs\Python\Python37-32\lib\threading.py", line 865, in run
self._target(*self._args, **self._kwargs)
File "C:\Users\aakashba598\AppData\Local\Programs\Python\Python37-32\lib\subprocess.py", line 1215, in _readerthread
buffer.append(fh.read())
MemoryError
另外,對此的可能解決方案是什么?
更新:當我從convert_from_path函數減少 dpi 參數時,它就像一個魅力。 但是生成的圖片質量很低(原因很明顯)。 有沒有辦法來解決這個問題? 就像批量創建圖像和每次清除內存一樣。 如果有辦法,應該怎么做?
每次以 10 頁為單位轉換 PDF(1-10,11-20 等...)
from pdf2image import pdfinfo_from_path,convert_from_path
info = pdfinfo_from_path(pdf_file, userpw=None, poppler_path=None)
maxPages = info["Pages"]
for page in range(1, maxPages+1, 10) :
convert_from_path(pdf_file, dpi=200, first_page=page, last_page = min(page+10-1,maxPages))
我對此有點晚了,但問題確實與進入內存的 136 頁有關。 你可以做三件事。
默認情況下,pdf2image 使用 PPM 作為其圖像格式,速度更快,但也占用更多內存(每個圖像超過 30MB!)。 您可以做些什么來解決這個問題,那就是使用更內存友好的格式,如 jpeg 或 png。
convert_from_path('C:\path\to\your\pdf', fmt='jpeg')
這可能會解決問題,但這主要是因為壓縮,並且在某些時候(例如 +500 頁 PDF)問題會再次出現。
這是我推薦的,因為它允許您處理任何 PDF。 README 頁面上的示例很好地解釋了它:
import tempfile
with tempfile.TemporaryDirectory() as path:
images_from_path = convert_from_path('C:\path\to\your\pdf', output_folder=path)
這會將圖像臨時寫入您的計算機存儲,因此您不必手動刪除它。 但是,請確保在退出with
上下文之前執行您需要執行的任何處理!
pdf2image
允許您定義要處理的第一頁和最后一頁。 這意味着在您的情況下,使用 136 頁的 PDF,您可以執行以下操作:
for i in range(0, 136 // 10 + 1):
convert_from_path('C:\path\to\your\pdf', first_page=i*10, last_page=(i+1)*10)
接受的答案有一個小問題。
maxPages = pdf2image._page_count(pdf_file)
不能再使用,因為_page_count
已棄用。 我找到了相同的工作解決方案。
from PyPDF2 import PdfFileWriter, PdfFileReader
inputpdf = PdfFileReader(open(pdf, "rb"))
maxPages = inputpdf.numPages
for page in range(1, maxPages, 100):
pil_images = pdf2image.convert_from_path(pdf, dpi=200, first_page=page,
last_page=min(page + 100 - 1, maxPages), fmt= 'jpg',
thread_count=1, userpw=None,
use_cropbox=False, strict=False)
這樣,無論文件有多大,它都會一次處理 100 個,並且 ram 使用量總是最小的。
一個相對較大的 PDF 會耗盡你所有的內存並導致進程被終止(除非你使用輸出文件夾) https://github.com/Belval/pdf2image我想這會幫助你理解。
解決方案:將pdf分成小部分並將其轉換為圖像。 圖片可以合並...
from PyPDF2 import PdfFileWriter, PdfFileReader
inputpdf = PdfFileReader(open("document.pdf", "rb"))
for i in range(inputpdf.numPages):
output = PdfFileWriter()
output.addPage(inputpdf.getPage(i))
with open("document-page%s.pdf" % i, "wb") as outputStream:
output.write(outputStream)
import numpy as np
import PIL
list_im = ['Test1.jpg', 'Test2.jpg', 'Test3.jpg']
imgs = [ PIL.Image.open(i) for i in list_im ]
# pick the image which is the smallest, and resize the others to match it (can be arbitrary image shape here)
min_shape = sorted( [(np.sum(i.size), i.size ) for i in imgs])[0][1]
imgs_comb = np.hstack( (np.asarray( i.resize(min_shape) ) for i in imgs ) )
# save that beautiful picture
imgs_comb = PIL.Image.fromarray( imgs_comb)
imgs_comb.save( 'Trifecta.jpg' )
# for a vertical stacking it is simple: use vstack
imgs_comb = np.vstack( (np.asarray( i.resize(min_shape) ) for i in imgs ) )
imgs_comb = PIL.Image.fromarray( imgs_comb)
imgs_comb.save( 'Trifecta_vertical.jpg' )
最終,結合這些技術,我最終編寫了如下代碼,目標是將 pdf 轉換為 pptx,同時避免內存溢出和良好的速度:
import os, sys, tempfile, pprint
from PIL import Image
from pdf2image import pdfinfo_from_path,convert_from_path
from pptx import Presentation
from pptx.util import Inches
from io import BytesIO
pdf_file = sys.argv[1]
print("Converting file: " + pdf_file)
# Prep presentation
prs = Presentation()
blank_slide_layout = prs.slide_layouts[6]
# Create working folder
base_name = pdf_file.split(".pdf")[0]
# Convert PDF to list of images
print("Starting conversion...")
print()
path: str = "C:/ppttemp" #temp dir (use cron to delete files older than 1h hourly)
slideimgs = []
info = pdfinfo_from_path(pdf_file, userpw=None, poppler_path='C:/Program Files/poppler-0.90.1/bin/')
maxPages = info["Pages"]
for page in range(1, maxPages+1, 5) :
slideimgs.extend( convert_from_path(pdf_file, dpi=250, output_folder=path, first_page=page, last_page = min(page+5-1,maxPages), fmt='jpeg', thread_count=4, poppler_path='C:/Program Files/poppler-0.90.1/bin/', use_pdftocairo=True) )
print("...complete.")
print()
# Loop over slides
for i, slideimg in enumerate(slideimgs):
if i % 5 == 0:
print("Saving slide: " + str(i))
imagefile = BytesIO()
slideimg.save(imagefile, format='jpeg')
imagedata = imagefile.getvalue()
imagefile.seek(0)
width, height = slideimg.size
# Set slide dimensions
prs.slide_height = height * 9525
prs.slide_width = width * 9525
# Add slide
slide = prs.slides.add_slide(blank_slide_layout)
pic = slide.shapes.add_picture(imagefile, 0, 0, width=width * 9525, height=height * 9525)
# Save Powerpoint
print("Saving file: " + base_name + ".pptx")
prs.save(base_name + '.pptx')
print("Conversion complete. :)")
print()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.