Как решить MemoryError с помощью библиотеки Python 3.7 pdf2image?

Я использую простой PDF-файл для преобразования изображений с помощью библиотеки Python PDF2Image. Я, конечно, могу понять, что эта библиотека пересекает максимальный порог памяти, чтобы прийти к этой ошибке. Но PDF составляет 6,6 МБ. (приблизительно), тогда зачем нужны ГБ памяти, чтобы выдать ошибку памяти?

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

Кроме того, как это можно решить?

Обновление: когда я уменьшил параметр dpi из функции convert_from_path, он работает как шарм. Но получаемые картинки некачественные (по понятным причинам). Есть ли способ исправить это? Подобно пакетному созданию изображений и очистке памяти каждый раз. Если есть способ, как это сделать?


person Aakash Basu    schedule 06.06.2019    source источник
comment
Вам нужно использовать Python или вы также можете использовать imagemagick?   -  person Jan Christoph Terasa    schedule 06.06.2019
comment
Я хочу делать это с помощью кодирования, а Python - очень удобный язык программирования.   -  person Aakash Basu    schedule 06.06.2019


Ответы (5)


Конвертируйте PDF-файл блоками по 10 страниц каждый раз (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))
person napuzba    schedule 06.06.2019
comment
Очень короткое, четкое и блестящее решение. Спасибо! - person Aakash Basu; 06.06.2019
comment
Я понял, что pdf2image не имеет атрибута _page_count. Есть идеи, о чем это? - person itsyahani; 12.09.2019
comment
pdf2image._page_count - недокументированная функция модуля. возможно его удалили или переименовали. - person napuzba; 12.09.2019
comment
Попробуйте из pdf2image.pdf2image импортировать pdfinfo_from_path, затем pdfinfo_from_path (pdf_file, userpw = None, poppler_path = None) [Страницы] - person Pablo; 21.07.2020

Я немного опоздал с этим, но проблема действительно связана с тем, что 136 страниц попадают в память. Вы можете сделать три вещи.

  1. Укажите формат преобразованных изображений.

По умолчанию pdf2image использует PPM в качестве формата изображения, он быстрее, но также требует гораздо больше памяти (более 30 МБ на изображение!). Что вы можете сделать, чтобы исправить это, так это использовать более удобный для памяти формат, такой как jpeg или png.

convert_from_path('C:\path\to\your\pdf', fmt='jpeg')

Это, вероятно, решит проблему, но в основном из-за сжатия, и в какой-то момент (скажем, для + 500 страниц PDF) проблема появится снова.

  1. Использовать выходной каталог

Это тот, который я бы порекомендовал, потому что он позволяет обрабатывать любой PDF-файл. Пример на странице README хорошо это объясняет:

import tempfile

with tempfile.TemporaryDirectory() as path:
    images_from_path = convert_from_path('C:\path\to\your\pdf', output_folder=path)

Это временно запишет изображение в хранилище вашего компьютера, поэтому вам не придется удалять его вручную. Однако не забудьте выполнить любую необходимую обработку перед выходом из with контекста!

  1. Обработка PDF-файла по частям

pdf2image позволяет вам определить первую и последнюю страницу, которую вы хотите обработать. Это означает, что в вашем случае с PDF-файлом из 136 страниц вы можете:

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)
person Belval    schedule 06.06.2019
comment
Что касается обработки PDF по частям: в последней версии convert_from_path нет first и last, вместо этого first_page и last_page - person Eugene Chabanov; 09.09.2020
comment
@EugeneChabanov это всегда была first_page и last_page, я просто пропустил это, когда впервые написал ответ. Я обновлю его. - person Belval; 10.09.2020

В принятом ответе есть небольшая проблема.

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 одновременно, а использование оперативной памяти всегда будет минимальным.

person Bot_Start    schedule 16.09.2019

Относительно большой 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)

разделите многостраничный файл PDF на несколько файлов pdf с питоном?

 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' )

см .: Объедините несколько изображений по горизонтали с помощью Python

person raunak rathi    schedule 06.06.2019

В конце концов, объединив эти методы, я закончил кодирование следующим образом, учитывая цель преобразовать 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()
person Raffael Meier    schedule 05.11.2020