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)
4

2 回答 2

14

The multiprocessing package is pretty easy to use. Look at the Queues example for a guide. You'll be following the producer consumer model. You want one (or more) producer processes reading images, and one (or more) consumer processes doing the image processing.

Your example would look something like this:

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()

This is a bit simpler than your original idea. In this example the producer adds to the queue as fast as it can rather than just staying one ahead of the consumer. That might be a problem if the producer gets so far ahead that you don't have enough memory to hold the queue. If problems arise you can get deeper into the multiprocessing docs, but this should be enough to get you started.

于 2012-09-18T20:04:57.683 回答
14

菲利普的回答很好,但只会创建几个进程(一个读取,一个计算),这几乎不会最大化现代 > 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参数 to Pool,那么我得到

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

在我的四核超线程 i7 上。

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

于 2012-09-18T21:08:53.613 回答