与多处理一起使用时,PyTesseract 调用工作非常缓慢

Posted

技术标签:

【中文标题】与多处理一起使用时,PyTesseract 调用工作非常缓慢【英文标题】:PyTesseract call working very slow when used along with multiprocessing 【发布时间】:2019-04-27 08:24:42 【问题描述】:

我有一个函数,它接收图像列表并在将 OCR 应用于图像后在列表中生成输出。我有另一个函数通过使用多处理来控制该函数的输入。因此,当我有一个列表(即没有多重处理)时,列表中的每个图像花费了大约 1 秒,但是当我将必须并行处理的列表增加到 4 个时,每个图像花费了惊人的 13 秒。

为了了解问题的真正所在,我尝试创建一个最小的问题工作示例。在这里,我有两个函数eat25eat100,它们打开图像name 并将其提供给使用API​​ pytesseract 的OCR。 eat25 执行 25 次,eat100 执行 100 次。

我的目标是在没有多处理的情况下运行 eat100,并在多处理的情况下运行 eat25(有 4 个进程)。从理论上讲,如果我有 4 个独立的处理器(我有 2 个内核,每个内核有 2 个线程,因此 CPU(s) = 4(如果我在这里错了,请纠正我),这应该比 eat100 少 4 倍的时间。

但是当我看到代码在打印“处理 0”4 次后甚至没有响应时,所有理论都被浪费了。单处理器函数eat100 工作正常。

我测试了一个简单的范围立方函数,它在多处理中运行良好,所以我的处理器肯定运行良好。这里唯一的罪魁祸首可能是:

pytesseract:见this 代码错误?我做的不对。

`

from pathos.multiprocessing import ProcessingPool
from time import time 
from PIL import Image
import pytesseract as pt
def eat25(name):
    for i in range(25):
        print('Processing :'+str(i))
        pt.image_to_string(Image.open(name),lang='hin+eng',config='--psm 6')
def eat100(name):
    for i in range(100):
        print('Processing :'+str(i))
        pt.image_to_string(Image.open(name),lang='hin+eng',config='--psm 6')
st = time()
eat100('normalBox.tiff')
en = time()
print('Direct :'+str(en-st))
#Using pathos
def caller():
    pool = ProcessingPool()
    pool.map(eat25,['normalBox.tiff','normalBox.tiff','normalBox.tiff','normalBox.tiff'])
if (__name__=='__main__'):
    caller()
en2 = time()

print('Pathos :'+str(en2-en))

那么,问题到底出在哪里?任何帮助表示赞赏!

编辑: 图片normalBox.tiff 可以找到here。如果人们重现代码并检查问题是否仍然存在,我会很高兴。

【问题讨论】:

我注意到您正在使用pathos.multiprocessing 模块。为什么不使用标准 concurrent.futures 包中的原生 ProcessPoolExecutor 【参考方案1】:

我是pathos 作者。如果您的代码需要1s 串行运行,那么在幼稚的并行进程中运行很可能需要更长的时间。使用幼稚的并行处理会产生开销:

    必须在每个处理器上启动一个新的 python 实例 您的函数和依赖项需要序列化并发送到每个处理器 您的数据需要序列化并发送到处理器 反序列化也是如此 您可能会遇到来自长寿命池或大量数据序列化的内存问题。

我建议检查一些简单的事情来检查您的问题可能出在哪里:

尝试pathos.pools.ThreadPool 使用线程并行而不是进程并行。这可以减少序列化和启动池的一些开销。 尝试pathos.pools._ProcessPool 更改pathos 管理池的方式。如果没有下划线,pathos 将池保持为单例,并且需要“终止”来明确终止池。使用下划线,当您删除池对象时,该池将终止。请注意,您的 caller 函数不会 closejoin(或 terminate)池。 您可能希望通过尝试dill.dumps 您尝试并行处理的元素之一来检查您的序列化程度。像大numpy 数组这样的东西可能需要一段时间才能序列化。如果传递的内容很大,您可以考虑使用共享内存数组(即multiprocess.Arraynumpy 数组的等效版本——另请参阅:numpy.ctypeslib)以最小化传递的内容每个进程之间。

后者的工作量更大,但如果您有很多要序列化的内容,可以节省大量资金。没有共享内存池,所以如果你需要走这条路,你必须对单个 multiprocess.Process 对象进行 for 循环。

【讨论】:

感谢您的回答!我只是想告诉你,这段代码在 Windows 中运行良好非常。这是真正的问题。 另外,关于您的建议,前两个的行为方式与原来的相同。 dill 中的tiff 图像存在问题,因为在酸洗和解酸之后,tiff 图像在馈送到pytesseract 时会引发错误。与 PNG 一起使用时效果很好。 我也是dill的作者。 dilltiff 图像与png 图像一无所知...所以很可能是某些对象(我假设)pytesseract 创建以处理tiff 的问题。首先,让我看看我是否理解正确......代码在 Windows 上的行为与预期一样,但在 linux/mac 上挂起?其次,您可以在尝试发送给不同并行工作人员的对象上尝试dill.copydill.check 吗?如果两者或其中任何一个都抛出错误,那么你有一个序列化问题。然后,您可以使用dill.detect.trace 诊断任何序列化问题。 是的,它在 linux 上挂起(准确地说,工作非常缓慢)。虽然没有在 mac 上测试过。 您正在将名称(string)映射到pytesseract.image_to_string 的返回值。所以序列化应该只需要处理返回的任何对象(看起来可能是一个字符串)。所以,我怀疑这是导致问题的原因。如果您只是将字符串传入和传出进程......那么最有可能的候选者是内存问题。你在交换你的记忆吗?

以上是关于与多处理一起使用时,PyTesseract 调用工作非常缓慢的主要内容,如果未能解决你的问题,请参考以下文章

将 django ORM 与多处理一起使用?

Boost.Python 与多处理兼容吗?

与多处理和共享变量一起运行两个函数

python 示例说明如何将实例方法与多处理模块一起使用

cProfiler 与多处理一起工作很奇怪

C - SIGINT处理程序不能与多线程一起工作,每个线程都有一个popen进程。