繁体   English   中英

在图像处理上使用多处理

[英]Using multiprocessing on Image processing

我正在尝试加快PIL.Image的处理速度,我将图像分成小部分,在数据库中搜索最相似的图像,然后用找到的图像替换图像的原始小部分。
这是描述的function:

def work_image(img, lenx, leny, neigh, split_dict, img_train_rot):
    constructed_img = Image.new(mode='L', size=img.size)
    for x in range(0,img.size[0],lenx):
        for y in range(0,img.size[1],leny):  
            box = (x,y,x+lenx,y+leny)
            split_img = img.crop(box)
            res = neigh.kneighbors(np.asarray(split_img).ravel().reshape((1,-1)))
            #look up the found image part in img_train_rot and define the position as new_box
            constructed_img.paste(img_train_rot[i].crop(new_box), (x,y))
    return constructed_img

现在我想并行化这个 function,因为每一行这样的图像部分都可以完全独立处理。
我使用multiprocessing.Pool提出了这种方法:

def work_image_parallel(leny, neigh, split_dict, img_train_rot, img_slice):
    constructed_img_slice = Image.new(mode='L', size=img_slice.size)
    for y in range(0, img_slice.size[1], leny):
        box = (0, y, img_slice.size[0], y+leny)
        img_part = img_slice.crop(box)
        res = neigh.kneighbors(np.asarray(img_part).ravel().reshape((1,-1)))
        #look up the found image part in img_train_rot and define the position as new_box
        constructed_img_slice.paste(img_train_rot[i].crop(new_box), (0,y))
    return constructed_img_slice

if __name__ == '__main__':
    lenx, leny = 16, 16
    #define my image database and so on
    neigh = setup_nearest_neighbour(train_imgs, n_neighbors=1)
    test_img = test_imgs[0]
    func = partial(work_image_parallel, leny, neigh, split_dict, img_train_rot)
    pool = multiprocessing.Pool()
    try:
        res = pool.map(func, map(lambda x: x, [test_img.crop((x, 0, x+lenx, test_img.size[1])) for x in range(0, test_img.size[0], lenx)])) 
    finally:
        pool.close()
        pool.join()
    test_result2 = Image.new(mode='L', size = test_img.size)
    for i in range(len(res)):
        test_result2.paste(res[i], box=(i*lenx, 0, i*lenx + lenx, test_result2.size[1]))

但是,这个并行版本并不比普通版本快,如果我减小图像分割的大小,并行版本会抛出一个 AssertionError (其他帖子说这可能是因为进程之间要发送的数据大小变成了太大)。

因此我的问题是,我可能做错了什么吗? 多处理在这里可能不是正确的方法吗? 或者为什么多处理不减少计算时间,因为每个图像切片的工作量应该足够大以抵消创建进程等所需的时间。任何帮助将不胜感激。

免责声明:我对 PIL 不太熟悉,因此您可能应该仔细查看 PIL 方法调用,这可能需要您进行一些“调整”,因为我无法实际测试它。

First, I observe that you will probably be making a lot of repeated invocations of your worker function work_image_parallel and that some of those arguments being passed to that function might be quite large (all of this depends, of course, on how large your images are )。 我宁愿将这些 arguments 一次复制到池中的每个进程并将它们实例化为全局变量,而不是重复传递如此大的 arguments。 这是通过池初始化程序 function 完成的。

其次,我尝试将您的work_image_parallel function 修改为与您的原始work_image function 一样接近,只是它现在只处理传递给它的单个 x、y 坐标对。 这样,您的子流程将完成更多工作。 我还尝试减少所需的粘贴操作次数(如果我正确理解发生了什么)。

第三,由于图像可能非常大,我使用生成器表达式来创建 arguments 与imap_unordered一起使用,而不是map 这是因为 x, y 对的数量在非常大的图像中可能非常大,并且map要求其可迭代参数使得可以计算其长度,以便可以计算有效的大小值(请参阅文档)。 使用imap_unordered ,如果我们期望迭代可能很大,我们应该指定一个显式的chunksize值以提高效率(如果未指定,则默认值为 1)。 如果您知道您正在处理相对较小的图像,以便x_y_args可迭代的大小在存储为列表时不会出现不合理的内存效率低下,那么您可以使用方法map和默认的chunksizeNone并具有池为您计算价值。 使用imap_unordered的优点是结果不必按顺序返回,因此处理速度会更快。

def init_pool(the_img, the_img_train_rot, the_neigh, the_split_dict):
    global img, img_train_rot, neigh, split_dict
    img = the_img
    img_train_rot = the_img_train_rot
    neigh = the_neigh
    split_dict = the_split_dict

def work_image_parallel(lenx, leny, t):
    x, y = t
    box = (x,y,x+lenx,y+leny)
    split_img = img.crop(box)
    res = neigh.kneighbors(np.asarray(split_img).ravel().reshape((1,-1)))
    #look up the found image part in img_train_rot and define the position as new_box
    # return original x, y values used:
    return x, y, img_train_rot[i].crop(new_box)

def compute_chunksize(iterable_size, pool_size):
    chunksize, remainder = divmod(iterable_size, 4 * pool_size)
    if remainder:
        chunksize += 1
    return chunksize

if __name__ == '__main__':
    lenx, leny = 16, 16
    #define my image database and so on
    neigh = setup_nearest_neighbour(train_imgs, n_neighbors=1)
    test_img = test_imgs[0]
    func = partial(work_image_parallel, lenx, leny)
    # in case this is a very large image, use a generator expression
    x_y_args = ((x, y) for x in range(0, test_img.size[0], lenx) for y in range(0, test_img.size[1], leny))
    # approximate size of x_y_args:
    iterable_size = (test_img.size[0] // lenx) * (test_img.size[1] // leny)
    pool_size = multiprocessing.cpu_count()
    chunksize = compute_chunksize(iterable_size, pool_size)
    pool = multiprocessing.Pool(pool_size, initiializer=init_pool, initargs=(test_img, img_train_rot, neigh, split_dict))
    test_result2 = Image.new(mode='L', size = test_img.size)
    try:
        # use imap or imap_unordered when the iterable is a generator to avoid conversion of iterable to a list
        # but specify a suitable chunksize for efficiency in case the iterable is very large:
        for x, y, res in pool.imap_unordered(func, x_y_args, chunksize=chunksize):
            test_result2.paste(res, (x, y))
    finally:
        pool.close()
        pool.join()

更新(将图像分成更大的切片)

def init_pool(the_img, the_img_train_rot, the_neigh, the_split_dict):
    global img, img_train_rot, neigh, split_dict
    img = the_img
    img_train_rot = the_img_train_rot
    neigh = the_neigh
    split_dict = the_split_dict

def work_image_parallel(lenx, leny, x):
    img_slice = img.crop((x, 0, x+lenx, img.size[1]))
    constructed_img_slice = Image.new(mode='L', size=img_slice.size)
    for y in range(0, img_slice.size[1], leny):
        box = (0, y, img_slice.size[0], y+leny)
        img_part = img_slice.crop(box)
        res = neigh.kneighbors(np.asarray(img_part).ravel().reshape((1,-1)))
        #look up the found image part in img_train_rot and define the position as new_box
        constructed_img_slice.paste(img_train_rot[i].crop(new_box), (0,y))
    return constructed_img_slice

if __name__ == '__main__':
    lenx, leny = 16, 16
    #define my image database and so on
    neigh = setup_nearest_neighbour(train_imgs, n_neighbors=1)
    test_img = test_imgs[0]
    pool = multiprocessing.Pool(pool_size, initiializer=init_pool, initargs=(test_img, img_train_rot, neigh, split_dict))
    func = partial(work_image_parallel, lenx, leny)
    try:
        test_result2 = Image.new(mode='L', size = test_img.size)
        x = 0
        for res in pool.map(func, [x for x in range(0, test_img.size[0], lenx)]):
            test_result2.paste(res, box=(x, 0, x + lenx, test_result2.size[1]))
            x += lenx
    finally:
        pool.close()
        pool.join()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM