【问题标题】:Asynchronously read and process an image in python在python中异步读取和处理图像
【发布时间】:2012-09-10 13:12:16
【问题描述】:

上下文

我经常发现自己处于以下情况:

  • 我有一个需要处理的图像文件名列表
  • 我使用例如 scipy.misc.imread 顺序读取每个图像
  • 然后我对每张图像进行某种处理并返回一个结果
  • 我将结果沿图像文件名保存到一个书架中

问题在于,简单地读取图像会花费不可忽略的时间,有时与图像处理相比甚至更长。

问题

所以我认为理想情况下我可以在处理图像 n 时读取图像 n + 1。或者以自动确定的最佳方式一次更好地处理和读取多张图像?

我读过有关多处理、线程、扭曲、gevent 等的信息,但我不知道该使用哪一个以及如何实现这个想法。有没有人可以解决这类问题?

小例子

# generate a list of images
scipy.misc.imsave("lena.png", scipy.misc.lena())
files = ['lena.png'] * 100

# a simple image processing task
def process_image(im, threshold=128):
    label, n = scipy.ndimage.label(im > threshold)
    return n

# my current main loop
for f in files:
    im = scipy.misc.imread(f)
    print process_image(im)

【问题讨论】:

  • 从使用Queue 的线程工作池开始。然后,一旦你完成了这项工作并理解了它,如果需要,就可以使用其他方法。
  • 多线程通常会产生令人失望的 Python 多核性能扩展;多处理是要走的路(见菲利普的回答)。

标签: python image-processing asynchronous numpy scipy


【解决方案1】:

多处理包非常易于使用。查看Queues example 获取指南。您将遵循生产者消费者模型。您需要一个(或多个)生产者进程读取图像,而一个(或多个)消费者进程进行图像处理。

您的示例将如下所示:

from multiprocessing import Process, Queue
import scipy

def process_images(q):
    while not q.empty():
        im = q.get()
        # Do stuff with item from queue

def read_images(q, files):
    for f in files:
        q.put(scipy.misc.imread(f))

if __name__ == '__main__':
    q = Queue()

    producer = Process(target=read_images, args=(q, files))
    producer.start()
    consumer = Process(target=process_images, args=(q, ))
    consumer.start()

这比您最初的想法要简单一些。在这个例子中,生产者尽可能快地添加到队列中,而不是仅仅领先于消费者。如果生产者领先得太远以至于您没有足够的内存来保存队列,那可能是一个问题。如果出现问题,您可以深入了解多处理文档,但这应该足以让您入门。

【讨论】:

  • q 后面加一个逗号,把括号变成一个元组:consumer = Process(target=process_images, args=(q,))
【解决方案2】:

Philip's answer 很好,但只会创建几个进程(一个读取,一个计算),这几乎不会最大化现代 >2 核心系统。这是使用multiprocessing.Pool(特别是它的 map 方法)的替代方法,它创建了执行读取和计算方面的进程,但应该更好地利用所有可用的内核(假设文件多于内核)。

#!/usr/bin/env python

import multiprocessing
import scipy
import scipy.misc
import scipy.ndimage

class Processor:
    def __init__(self,threshold):
        self._threshold=threshold

    def __call__(self,filename):
        im = scipy.misc.imread(filename)
        label,n = scipy.ndimage.label(im > self._threshold)
        return n

def main():
    scipy.misc.imsave("lena.png", scipy.misc.lena())
    files = ['lena.png'] * 100

    proc=Processor(128)
    pool=multiprocessing.Pool()
    results=pool.map(proc,files)

    print results

if __name__ == "__main__":
    main()

如果我将图像数量增加到 500,并使用 processes=N 参数到 Pool,那么我得到

Processes   Runtime
   1         6.2s
   2         3.2s
   4         1.8s
   8         1.5s

在我的四核超线程 i7 上。

如果您遇到更实际的用例(即实际不同的图像),您的进程可能会花费更多时间等待图像数据从存储中加载(在我的测试中,它们几乎是从缓存磁盘立即加载)然后明确地创建比核心更多的进程以获得更多的计算和负载重叠可能是值得的。不过,只有您自己在实际负载和硬件上进行的可扩展性测试才能告诉您什么才是最适合您的。

【讨论】:

    猜你喜欢
    • 2022-07-07
    • 1970-01-01
    • 1970-01-01
    • 2022-11-25
    • 1970-01-01
    • 2015-07-26
    • 2019-02-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多