如何减少魔杖内存使用?

How to reduce wand memory usage?

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

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 killer 阻止了。我不想为更高的 ram 付费,而是想尝试减少 wand 和 ImageMagick 使用的内存。因为它已经是异步的,所以我不介意增加计算时间。我浏览了这个:http://www.imagemagick.org/Usage/files/#massive,但我不确定它是否可以用魔杖实现。另一种可能的修复方法是一次在 wand 中打开一个 pdf,而不是一次将完整图像放入 RAM。或者,我如何使用 python 直接与 ImageMagick 交互,以便我可以使用这些内存限制技术?

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

这里有一些提示可确保正确管理内存。

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

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

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

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

  5. 注意分辨率。 300 @ 16Q 的 pdf 分辨率会产生大量的光栅图像。对于许多 OCR (tesseract/opencv) 技术,第一步是对入站数据进行预处理以 remove extra/unneeded colors / channels / data / &tc.

这是我将如何处理这个问题的示例。请注意,我将利用 直接管理图像堆栈 w/o 额外的 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 包装器。

@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"))`

编辑 我在

上找到了内存转换
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")

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

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

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 迭代页面,所以,我已经打开了文件并正在迭代你的页面。

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

我运行遇到了类似的问题。

发现此页面很有趣: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 确认它按预期工作。