在python中异步读取和处理图像

Nic*_*bey 12 python asynchronous numpy image-processing scipy

上下文

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

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

问题在于简单地读取图像需要不可忽略的时间,有时甚至比图像处理更长或更长.

所以我认为理想情况下我可以在处理图像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)
Run Code Online (Sandbox Code Playgroud)

tim*_*day 12

菲利普的答案很好,但只会创建几个过程(一个阅读,一个计算),这几乎不会超过现代的> 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()
Run Code Online (Sandbox Code Playgroud)

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

Processes   Runtime
   1         6.2s
   2         3.2s
   4         1.8s
   8         1.5s
Run Code Online (Sandbox Code Playgroud)

在我的四核超线程i7上.

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


Phi*_*lip 11

多处理包非常易于使用.查看Queues示例以获取指南.您将关注生产者消费者模型.您希望一个(或多个)生产者进程读取图像,以及一个(或多个)消费者进程进行图像处理.

你的例子看起来像这样:

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()
Run Code Online (Sandbox Code Playgroud)

这比你原来的想法简单一点.在这个例子中,生产者尽可能快地添加到队列中,而不是仅仅停留在消费者之前.如果生产者远远领先于你没有足够的内存来保存队列,这可能是一个问题.如果出现问题,您可以深入了解多处理文档,但这应该足以让您入门.

  • 在`q`之后添加一个逗号将括号转换为元组:`consumer = Process(target = process_images,args =(q,))`. (4认同)