【问题标题】:Using multiprocessing with the PIL在 PIL 中使用多处理
【发布时间】:2012-09-16 06:10:52
【问题描述】:

我有一些代码对一堆 Python Imaging Library (PIL) Image 对象执行(独立)操作。我想尝试使用并行处理来加快速度,所以我阅读了下面的多处理模块:

http://docs.python.org/library/multiprocessing.html

但我仍然不太清楚如何使用多处理来解决这个问题。

从概念上讲,我似乎可以使用图像对象的 multiprocessing.Queue 并使用工作人员池。但 Image 对象似乎“不可挑选”:

UnpickleableError: Cannot pickle <type 'ImagingCore'> objects

有没有更好的方法来并行处理 PIL 图像?

【问题讨论】:

    标签: python multiprocessing python-imaging-library


    【解决方案1】:

    如果您从文件中获取图像对象,您只需将文件名发送给工作人员,让他们自己打开图像。

    否则,您可以发送图像数据(使用Image.getdata())以及尺寸和像素格式,并让工作人员使用Image.new()Image.putdata() 重建图像。

    【讨论】:

    • 谢谢。我在正确的轨道上使用队列和工人池吗?
    • 听起来像。 multiprocessing 是如果你想用 Python 做这种 CPU-bound/memory-bound 并行处理的模块。
    • @M-V:如果您使用队列将图像数据提供给工作人员,则在父进程中加载​​所有图像可能会成为瓶颈。并且你会在数据的酸洗/解酸过程中产生额外的开销。如果您只是将文件名提供给工作人员,他们也可以并行加载图像。
    • 感谢 nneonneo 和 Roland 提出使用文件名的想法。
    【解决方案2】:

    只需将文件名放在一个列表中,然后让工作进程处理它们。下面的示例在subprocess 中使用ImageMagick 从晦涩的格式进行一些图像转换。但是同样的原理也可以用于 PIL。只需替换processfile() 函数的内容即可。这是我经常使用的一个程序,用于将 DICOM 文件(一种用于医学成像的格式,在本例中来自 X 射线机)转换为 PNG 格式。

    """Convert DICOM files to PNG format, remove blank areas. The blank erea
       removal is based on the image size of a Philips flat detector. The image
       goes from 2048x2048 pixels to 1574x2048 pixels."""
    
    import os
    import sys
    import subprocess
    from multiprocessing import Pool, Lock
    
    globallock = Lock()
    
    def checkfor(args):
        """Make sure that a program necessary for using this script is
        available.
    
        Arguments:
        args -- string or list of strings containing a command to test
        """
        if isinstance(args, str):
            args = args.split()
        try:
            f = open('/dev/null')
            subprocess.call(args, stderr=subprocess.STDOUT, stdout=f)
            f.close()
        except:
            print "Required program '{}' not found! exiting.".format(args[0])
            sys.exit(1)
    
    def processfile(fname):
        """Use the convert(1) program from the ImageMagick suite to convert the
           image and crop it.
    
        Arguments:
        fname -- string containing the name of the file to process
        """
        size = '1574x2048'
        args = ['convert', fname, '-units', 'PixelsPerInch', '-density', '300',
                '-crop', size+'+232+0', '-page', size+'+0+0', fname+'.png']
        rv = subprocess.call(args)
        globallock.acquire()
        if rv != 0:
            print "Error '{}' when processing file '{}'.".format(rv, fname)
        else:
            print "File '{}' processed.".format(fname)
        globallock.release()
    
    def main(argv):
        """Main program.
    
        Arguments:
        argv -- command line arguments
        """
        if len(argv) == 1:
            # If no filenames are given, print a usage message.
            path, binary = os.path.split(argv[0])
            print "Usage: {} [file ...]".format(binary)
            sys.exit(0)
        # Verify that the convert program that we need is available.
        checkfor('convert')
        # Apply the processfile() function to all files in parallel.
        p = Pool()
        p.map(processfile, argv[1:])
        p.close()
    
    if __name__ == '__main__':
        main(sys.argv)
    

    【讨论】:

    • multiprocessing 如果你使用subprocess 是不必要的
    • 确实如此。但是使用multiprocessing.Pool 比自己管理一堆subprocess.Popen 对象方便得多。 :-)
    • 这个解决方案看起来很完整。作为一个新手,只是问它最后是否需要'p.join()'?谢谢
    • 由于程序在p.close之后立即退出,我认为这里不需要join
    【解决方案3】:

    另一种选择是将convert PIL 图像转换为可腌制的 numpy 数组。

    from __future__ import print_function
    import numpy
    import pickle
    
    my_array = numpy.array([1,2,3])
    pickled_array = pickle.dumps(my_array)
    print('The pickled version of %s is:\n\n%s.' % (my_array, pickled_array))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-01-31
      • 1970-01-01
      • 2010-10-29
      • 1970-01-01
      • 1970-01-01
      • 2020-02-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多