Bagaimana cara mengatasi MemoryError menggunakan perpustakaan gambar Python 3.7 pdf2?

Saya menjalankan konversi PDF ke gambar sederhana menggunakan perpustakaan Python PDF2Image. Saya pasti dapat memahami bahwa ambang batas memori maksimal sedang dilewati oleh perpustakaan ini untuk sampai pada kesalahan ini. Namun, PDF berukuran 6,6 MB (kira-kira), lalu mengapa dibutuhkan memori GB untuk menimbulkan kesalahan memori?

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

Selain itu, apa solusi yang mungkin untuk mengatasi hal ini?

Pembaruan: Saat saya mengurangi parameter dpi dari fungsi convert_from_path, fungsinya sangat bagus. Namun gambar yang dihasilkan berkualitas rendah (karena alasan yang jelas). Apakah ada cara untuk memperbaikinya? Seperti pembuatan gambar batch demi batch dan membersihkan memori setiap saat. Jika ada jalan, bagaimana cara melakukannya?


person Aakash Basu    schedule 06.06.2019    source sumber
comment
Apakah harus menggunakan Python, atau bisa juga menggunakan imagemagick?   -  person Jan Christoph Terasa    schedule 06.06.2019
comment
Saya ingin melakukannya melalui coding dan Python adalah bahasa pemrograman yang sangat berguna.   -  person Aakash Basu    schedule 06.06.2019


Jawaban (5)


Konversikan PDF dalam blok 10 halaman setiap kali (1-10,11-20 dan seterusnya ...)

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
Solusi yang sangat singkat, tajam, dan brilian. Terima kasih! - person Aakash Basu; 06.06.2019
comment
Saya mengerti, 'pdf2image' tidak memiliki atribut '_page_count'. Tahu tentang apa ini? - person itsyahani; 12.09.2019
comment
pdf2image._page_count adalah fungsi modul yang tidak terdokumentasi. mungkin itu telah dihapus atau diganti namanya. - person napuzba; 12.09.2019
comment
Coba dari pdf2image.pdf2image impor pdfinfo_from_path lalu pdfinfo_from_path(pdf_file, userpw=None, poppler_path=None)[Pages] - person Pablo; 21.07.2020

Saya agak terlambat dalam hal ini, tetapi masalahnya memang terkait dengan 136 halaman yang masuk ke memori. Anda dapat melakukan tiga hal.

  1. Tentukan format untuk gambar yang dikonversi.

Secara default, pdf2image menggunakan PPM sebagai format gambarnya, lebih cepat, namun juga memakan lebih banyak memori (lebih dari 30MB per gambar!). Yang dapat Anda lakukan untuk memperbaikinya adalah menggunakan format yang lebih ramah memori seperti jpeg atau png.

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

Itu mungkin akan menyelesaikan masalah, tetapi sebagian besar hanya karena kompresi, dan pada titik tertentu (katakanlah untuk +500 halaman PDF) masalah akan muncul kembali.

  1. Gunakan direktori keluaran

Ini yang saya rekomendasikan karena memungkinkan Anda memproses PDF apa pun. Contoh di halaman README menjelaskannya dengan baik:

import tempfile

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

Ini akan menulis gambar ke penyimpanan komputer Anda untuk sementara sehingga Anda tidak perlu menghapusnya secara manual. Pastikan untuk melakukan pemrosesan apa pun yang perlu Anda lakukan sebelum keluar dari konteks with!

  1. Proses file PDF dalam beberapa bagian

pdf2image memungkinkan Anda menentukan halaman pertama dan terakhir yang ingin Anda proses. Artinya, dalam kasus Anda, dengan PDF sebanyak 136 halaman, Anda dapat melakukan:

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
Mengenai pemrosesan PDF dalam beberapa bagian: di versi terbaru convert_from_path tidak ada first dan last, melainkan first_page dan last_page - person Eugene Chabanov; 09.09.2020
comment
@EugeneChabanov selalu menjadi halaman_pertama dan halaman_terakhir, saya hanya melewatkannya saat pertama kali menulis jawabannya. Saya akan memperbaruinya. - person Belval; 10.09.2020

Jawaban yang diterima memiliki masalah kecil.

maxPages = pdf2image._page_count(pdf_file)

tidak dapat digunakan lagi, karena _page_count sudah tidak digunakan lagi. Saya menemukan solusi yang berfungsi untuk hal yang sama.

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)

Dengan cara ini, seberapa besar pun filenya, akan diproses 100 sekaligus dan penggunaan ram selalu minimal.

person Bot_Start    schedule 16.09.2019

PDF yang relatif besar akan menghabiskan seluruh memori Anda dan menyebabkan proses terhenti (kecuali jika Anda menggunakan folder keluaran) https://github.com/Belval/pdf2image saya kira akan membantu Anda untuk memahaminya.

Solusi: Pecahkan pdf menjadi bagian-bagian kecil dan ubah menjadi gambar. Gambarnya bisa digabungkan...

 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)

bagi file pdf multi-halaman menjadi beberapa file pdf dengan python?

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

lihat:Gabungkan beberapa gambar secara horizontal dengan Python

person raunak rathi    schedule 06.06.2019

akhirnya, dengan menggabungkan teknik-teknik ini, saya akhirnya membuat kode seperti berikut, dengan tujuan untuk mengubah pdf menjadi pptx dengan menghindari kelebihan memori dan mempertimbangkan kecepatan yang baik:

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