如何减少魔杖内存使用?
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 交互,以便我可以使用这些内存限制技术?
请记住,wand 库与 MagickWand
API 集成,然后将 PDF encoding/decoding 工作委托给 ghostscript
。 MagickWand
和 ghostscript
都分配了额外的内存资源,并且最好在每个任务结束时释放。但是,如果例程由 python 初始化并由变量保存,则很可能会引入内存泄漏。
这里有一些提示可确保正确管理内存。
对所有 Wand 分配使用 with
上下文管理。这将确保所有资源都通过 __enter__
& __exit__
管理处理程序。
避免blob
创建传递数据。创建文件格式的 blob 时,MagickWand 将分配额外的内存来复制和编码图像,并且 python 除了原始魔杖实例之外还将保存结果数据。通常在开发环境中很好,但在生产环境中可能会迅速失控。
避免Image.sequence
。这是另一个复制繁重的例程,导致 python 持有一堆内存资源。请记住 ImageMagick 很好地管理图像堆栈,因此如果您不重新排序/操作单个帧,最好使用 MagickWand 方法并且不涉及 python.
每个任务都应该是一个独立的进程,并且可以在完成时干净地关闭。对于 celery
作为队列工作人员的您来说,这应该不是问题,但值得仔细检查 thread/worker 配置 + 文档。
注意分辨率。 300 @ 16Q 的 pdf 分辨率会产生大量的光栅图像。对于许多 OCR (tesseract/opencv) 技术,第一步是对入站数据进行预处理以 remove extra/unneeded colors / channels / data / &tc.
这是我将如何处理这个问题的示例。请注意,我将利用 ctypes 直接管理图像堆栈 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)
当然,您的里程数可能会有所不同。如果您对 subprocess 感到满意,您可以直接执行 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")
我也遇到了内存泄漏问题。经过一些研究和调整代码实现后,我的问题得到了解决。
我基本上使用 with 和 destroy() 函数正常工作。
在某些情况下,我可以使用 和 打开和读取文件,如下例所示:
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 确认它按预期工作。
我正在使用 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 交互,以便我可以使用这些内存限制技术?
请记住,wand 库与 MagickWand
API 集成,然后将 PDF encoding/decoding 工作委托给 ghostscript
。 MagickWand
和 ghostscript
都分配了额外的内存资源,并且最好在每个任务结束时释放。但是,如果例程由 python 初始化并由变量保存,则很可能会引入内存泄漏。
这里有一些提示可确保正确管理内存。
对所有 Wand 分配使用
with
上下文管理。这将确保所有资源都通过__enter__
&__exit__
管理处理程序。避免
blob
创建传递数据。创建文件格式的 blob 时,MagickWand 将分配额外的内存来复制和编码图像,并且 python 除了原始魔杖实例之外还将保存结果数据。通常在开发环境中很好,但在生产环境中可能会迅速失控。避免
Image.sequence
。这是另一个复制繁重的例程,导致 python 持有一堆内存资源。请记住 ImageMagick 很好地管理图像堆栈,因此如果您不重新排序/操作单个帧,最好使用 MagickWand 方法并且不涉及 python.每个任务都应该是一个独立的进程,并且可以在完成时干净地关闭。对于
celery
作为队列工作人员的您来说,这应该不是问题,但值得仔细检查 thread/worker 配置 + 文档。注意分辨率。 300 @ 16Q 的 pdf 分辨率会产生大量的光栅图像。对于许多 OCR (tesseract/opencv) 技术,第一步是对入站数据进行预处理以 remove extra/unneeded colors / channels / data / &tc.
这是我将如何处理这个问题的示例。请注意,我将利用 ctypes 直接管理图像堆栈 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)
当然,您的里程数可能会有所不同。如果您对 subprocess 感到满意,您可以直接执行 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")
我也遇到了内存泄漏问题。经过一些研究和调整代码实现后,我的问题得到了解决。 我基本上使用 with 和 destroy() 函数正常工作。
在某些情况下,我可以使用 和 打开和读取文件,如下例所示:
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 确认它按预期工作。