如何减少魔杖内存使用量?

时间:2017-05-26 20:41:31

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

我正在使用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服务器。但是,因为即使是一个13.7 MB的pdf文件,image_pdf也会增长到大约4gb,所以它被oom杀手所阻止。我想尝试减少魔杖和ImageMagick使用的内存,而不是支付更高的内存。由于它已经异步,我不介意增加计算时间。我已经略过了这个:http://www.imagemagick.org/Usage/files/#massive,但我不确定它是否可以用魔杖实现。另一种可能的解决方法是一次在一个页面中打开一个pdf的方法,而不是一次将完整的图像放入RAM中。或者,我怎么能直接使用python与ImageMagick接口,以便我可以使用这些内存限制技术?

4 个答案:

答案 0 :(得分:2)

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

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

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

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

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

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

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

  6. 这是我如何处理这个问题的一个例子。注意,我将利用直接管理没有其他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)
    

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

答案 1 :(得分:0)

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

我需要导入ctype而不是cstyle

我也收到@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")

答案 2 :(得分:0)

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

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

{
  "ignore": ["js/framework/**/*.js","config.js"],
    "/": {
    "plugins": ["resolve-linked-dependencies"],
    ".babelrc": {
        "presets": ["liferay-standard"]
    },
    "post-plugins": ["namespace-packages", "inject-imports-dependencies"]
  },
  "*": {
    "plugins": ["replace-browser-modules"],
    ".babelrc": {
        "plugins":[ ["namespace-amd-define", {
                           "namespace": "Liferay.Loader"
                   }]
                   ],
        "presets": ["liferay-standard"]
    },
    "post-plugins": ["namespace-packages", "inject-peer-dependencies"]
 }

}

在这种情况下,正确使用内存和tmp文件。

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

  Liferay.Loader.define('sigerb-web$lodash-amd@4.17.15/_arrayShuffle',
    ['module', 'exports', 'require'], function (module, exports, require) 
       {
       var define = undefined;
         define(['./_copyArray', './_shuffleSelf'], function (copyArray, shuffleSelf) { ...

第二种情况是一个示例,其中我不能使用,因为我不得不通过序列来迭代页面,因此,我已经打开了文件并正在迭代您的页面。

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

答案 3 :(得分:0)

我遇到了类似的问题。

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

以及如何通过魔杖限制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确认它可以正常工作。