【问题标题】:How to solve MemoryError using Python 3.7 pdf2image library?如何使用 Python 3.7 pdf2image 库解决 MemoryError?
【发布时间】:2019-06-06 06:08:22
【问题描述】:

我正在使用 Python PDF2Image 库运行一个简单的 PDF 到图像的转换。我当然可以理解,这个库正在跨越最大内存阈值来达到这个错误。但是,the PDF 是 6.6 MB(大约),那么为什么会占用 GBs 的内存来引发内存错误呢?

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 参数时,它就像一个魅力。但是制作的图片质量很差(原因很明显)。有没有办法解决这个问题?就像每次批量创建图像和清除内存一样。如果有办法,该怎么做?

【问题讨论】:

  • 一定要用Python,还是可以用imagemagick?
  • 我想通过编码来实现,Python是一种非常方便的编程语言。

标签: python python-3.x out-of-memory data-conversion


【解决方案1】:

每次以 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))

【讨论】:

  • 非常简短、清晰且出色的解决方案。谢谢!
  • 我知道了,'pdf2image' 没有属性 '_page_count'。知道这是什么意思吗?
  • pdf2image._page_count 是模块的未记录功能。也许它已被删除或重命名。
  • 尝试从 pdf2image.pdf2image 导入 pdfinfo_from_path 然后 pdfinfo_from_path(pdf_file, userpw=None, poppler_path=None)["Pages"]
【解决方案2】:

我对此有点晚了,但问题确实与进入内存的 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 允许您定义要处理的第一页和最后一页。这意味着在您的情况下,使用 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)

【讨论】:

  • 关于分块处理PDF:在最新版本的convert_from_path中没有firstlast,而是first_pagelast_page
  • @EugeneChabanov 它一直是 first_page 和 last_page,当我第一次写答案时,我只是错过了它。我会更新的。
【解决方案3】:

接受的答案有一个小问题。

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 个,并且内存使用量总是最小的。

【讨论】:

    【解决方案4】:

    相对较大的 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)
    

    split a multi-page pdf file into multiple pdf files with 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' )
    

    参考:Combine several images horizontally with Python

    【讨论】:

      【解决方案5】:

      最终,结合这些技术,我最终编码如下,目标是将 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()
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-09
        • 2019-10-27
        • 2019-07-18
        • 2021-09-17
        相关资源
        最近更新 更多