【问题标题】:How to reduce wand memory usage?如何减少魔杖内存使用?
【发布时间】:2017-10-27 20:04:59
【问题描述】:

我正在使用 wand 和 pytesseract 将 pdf 的文本上传到 django 网站,如下所示:

image_pdf = Image(blob=read_pdf_file, resolution=300)
image_png = image_pdf.convert('png')

req_image = []
final_text = []

for img in image_png.sequence:
    img_page = Image(image=img)
    req_image.append(img_page.make_blob('png'))

for img in req_image:
    txt = pytesseract.image_to_string(PI.open(io.BytesIO(img)).convert('RGB'))
    final_text.append(txt)

return " ".join(final_text)

我让它在单独的 ec2 服务器中的 celery 中运行。但是,因为即使是 13.7 mb 的 pdf 文件, image_pdf 也会增长到大约 4gb,它会被 oom 杀手阻止。我不想支付更高的内存,而是想尝试减少 wand 和 ImageMagick 使用的内存。由于它已经是异步的,我不介意增加计算时间。我浏览了这个:http://www.imagemagick.org/Usage/files/#massive,但不确定它是否可以用 wand 实现。另一种可能的解决方法是一次在 wand 中打开 pdf 文件,而不是一次将完整图像放入 RAM。或者,我如何直接使用 python 与 ImageMagick 交互,以便我可以使用这些内存限制技术?

【问题讨论】:

    标签: python amazon-ec2 imagemagick wand python-tesseract


    【解决方案1】:

    请记住, 库与 MagickWand API 集成,反过来,将 PDF 编码/解码工作委托给 ghostscript。 MagickWand 和ghostscript 都分配了额外的内存资源,并且最好在每个任务结束时解除分配。但是,如果例程由 python 初始化,并由变量保存,则很可能会引入内存泄漏。

    以下是一些确保正确管理内存的提示。

    1. 对所有 Wand 分配使用 with 上下文管理。这将确保所有资源都通过__enter__ 和__exit__ 管理处理程序。

    2. 避免为传递数据创建blob。创建文件格式 blob 时,MagickWand 将分配额外的内存来复制和编码图像,python 将保存除原始 wand 实例之外的结果数据。在开发环境中通常很好,但在生产环境中可能会迅速失控。

    3. 避免使用Image.sequence。这是另一个复制繁重的例程,导致 python 持有一堆内存资源。请记住,ImageMagick 可以很好地管理图像堆栈,因此如果您不重新排序/操作单个帧,最好使用 MagickWand 方法而不涉及 python。

    4. 每个任务都应该是一个独立的进程,并且可以在完成时干净地关闭。对于使用 celery 作为队列工作者的您来说,这应该不是问题,但值得仔细检查线程/工作者配置 + 文档。

    5. 注意分辨率。 300 @ 16Q 的 pdf 分辨率将产生大量的光栅图像。使用许多 OCR(tesseract/opencv)技术,第一步是预处理入站数据以删除额外/不需要的颜色/通道/数据/&tc。

    这是我将如何处理此问题的示例。请注意,我将利用 直接管理图像堆栈而无需额外的 python 资源。

    import ctyles
    from wand.image import Image
    from wand.api import library
    
    # Tell wand about C-API method
    library.MagickNextImage.argtypes = [ctypes.c_void_p]
    library.MagickNextImage.restype = ctypes.c_int
    
    # ... Skip to calling method ...
    
    final_text = []
    with Image(blob=read_pdf_file, resolution=100) as context:
        context.depth = 8
        library.MagickResetIterator(context.wand)
        while(library.MagickNextImage(context.wand) != 0):
            data = context.make_blob("RGB")
            text = pytesseract.image_to_string(data)
            final_text.append(text)
    return " ".join(final_text)
    

    当然,您的里程可能会有所不同。如果您对 感到满意,您可以直接执行gs 和tesseract,并消除所有python 包装器。

    【讨论】:

    • 这里的上下文是什么?
    • 眼光敏锐! context 变量应该是由with 堆栈分配的Image 对象的实例。我将更新示例。
    • 感谢您的澄清,但随后 pytesseract 需要枕头 Image 对象,因此抛出 TypeError: Unsupported image object
    • 这个答案解决了 Wand 库的内存使用问题,而不是枕头/pytesseract API。我不能代表那些python模块,但我可以评论Tesseract使用Leptonica,因为它是光栅I/O,所以你可以消除pillow,PIL,pytesseract,Wand,& ImageMagick 完全。 documentation 告诉你怎么做,互联网上到处都是examples。
    • 你说避免Image.sequence,“while”是否循环遍历图像序列?
    【解决方案2】:

    @emcconville 中的代码有效,我的临时文件夹不再被 magick-* 文件填满

    我需要导入 ctypes 而不是 cstyles

    我也收到了@kerthik 提到的错误

    通过保存图像并再次加载来解决它,也可以将其保存到内存中

    from PIL import Image as PILImage
    
    ...
    context.save(filename="temp.jpg")
    text = pytesseract.image_to_string(PILImage.open("temp.jpg"))`
    

    编辑 我在How to convert wand.image.Image to PIL.Image?上找到了内存转换

    img_buffer = np.asarray(bytearray(context.make_blob(format='png')),dtype='uint8')
    bytesio = io.BytesIO(img_buffer)
    text = ytesseract.image_to_string(PILImage.open(bytesio),lang="dan")
    

    【讨论】:

      【解决方案3】:

      我还遇到了内存泄漏问题。经过一些研究和调整代码实现后,我的问题得到了解决。 我基本上使用 with 和 destroy() 功能正常工作。

      在某些情况下,我可以使用 with 打开和读取文件,如下例所示:

      with Image(filename = pdf_file, resolution = 300) as pdf:
      

      本例,使用with,内存和tmp文件得到正确管理。

      在另一种情况下,我必须使用 destroy() 函数,最好在 try / finally 块内,如下所示:

      try:
          for img in pdfImg.sequence:
          # your code
      finally:
          pdfImg.destroy()
      

      第二种情况是我不能使用 with 的例子,因为我必须通过 sequence 迭代页面,所以,我已经打开了文件并且正在迭代您的页面。

      这种解决方案组合解决了我的内存泄漏问题。

      【讨论】:

        【解决方案4】:

        我遇到了类似的问题。

        发现这个页面很有趣:http://www.imagemagick.org/script/architecture.php#tera-pixel

        以及如何通过 wand 限制 ImageMagick 使用的内存量: http://docs.wand-py.org/en/latest/wand/resource.html

        只需添加类似的内容:

        from wand.resource import limits
        
        # Use 100MB of ram before writing temp data to disk.
        limits['memory'] = 1024 * 1024 * 100
        

        这可能会增加计算时间(但和你一样,我不太介意)而且我实际上并没有注意到这么多差异。

        我使用 Python 的 memory-profiler 确认它按预期工作。

        【讨论】:

          猜你喜欢
          • 2017-12-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-04-08
          • 1970-01-01
          • 2013-05-21
          • 2018-07-12
          • 2021-12-12
          相关资源
          最近更新 更多