วิธีแก้ปัญหา MemoryError โดยใช้ไลบรารี Python 3.7 pdf2image

ฉันกำลังเรียกใช้การแปลง PDF เป็นรูปภาพอย่างง่ายโดยใช้ไลบรารี Python PDF2Image ฉันเข้าใจได้อย่างแน่นอนว่าไลบรารีนี้ข้ามขีดจำกัดหน่วยความจำสูงสุดเพื่อให้ได้ข้อผิดพลาดนี้ แต่ 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

นอกจากนี้ อะไรคือวิธีแก้ปัญหาที่เป็นไปได้สำหรับเรื่องนี้?

อัปเดต: เมื่อฉันลดพารามิเตอร์ 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)[Pages] - person Pablo; 21.07.2020

ฉันมาช้าไปหน่อย แต่ปัญหาเกี่ยวข้องกับ 136 หน้าที่เข้าสู่หน่วยความจำ คุณสามารถทำสามสิ่ง

  1. ระบุรูปแบบสำหรับภาพที่แปลงแล้ว

ตามค่าเริ่มต้น pdf2image จะใช้ PPM เป็นรูปแบบรูปภาพ ซึ่งเร็วกว่า แต่ยังใช้หน่วยความจำมากกว่ามาก (มากกว่า 30MB ต่อภาพ!) สิ่งที่คุณสามารถทำได้เพื่อแก้ไขปัญหานี้คือการใช้รูปแบบที่เหมาะกับหน่วยความจำมากขึ้น เช่น 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 ไฟล์ในคราวเดียว และการใช้ RAM จะน้อยที่สุดเสมอ

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 หลายไฟล์ด้วย 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' )

อ้างอิง:รวมหลายภาพในแนวนอนด้วย 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