如何矢量化 python 中的任务?

How to vectorize tasks in python?

我(将)有一个坐标列表;使用 python 的 pillow 模块,我想将一系列(裁剪后的)较小的图像保存到磁盘。目前,我正在使用 for 循环来一次确定一个坐标,然后 crop/save 图像,然后再继续下一个坐标。

有没有一种方法可以将此作业划分为多个图像可以同时 cropped/saved?我知道这会占用更多内存,但会减少执行时间。

我确定这是可能的,但我不确定这是否简单。我听说过 'vectorization' 和 'multi-threading' 之类的术语听起来不太适合这种情况。但是这些主题超出了我的经验范围。

我已附上代码以供参考。但是,我只是想征求推荐的策略。 (即我应该学习哪些技术来更好地调整我的方法,同时进行多种作物等?)


def parse_image(source, square_size, count, captures, offset=0, offset_type=0, print_coords=False):
    """
    Starts at top left corner of image. Iterates through image by square_size (width = height)
    across x values and after exhausting the row, begins next row lower by function of 
    square_size. Offset parameter is available such that, with multiple function calls, 
    overlapping images could be generated.
    """
    src = Image.open(source)
    dimensions = src.size
    max_down = int(src.height/square_size) * square_size + square_size
    max_right = int(src.width/square_size) * square_size + square_size

    if offset_type == 1:
        tl_x = 0 + offset
        tl_y = 0
        br_x = square_size + offset 
        br_y = square_size

        for y in range(square_size,max_down,square_size):
            for x in range(square_size + offset,max_right - offset,square_size):
                if (tl_x,tl_y) not in captures:
                    sample = src.crop((tl_x,tl_y,br_x,br_y))
                    sample.save(f"{source[:-4]}_sample_{count}_x{tl_x}_y{tl_y}.jpg")
                    captures.append((tl_x,tl_y))

                    if print_coords == True: 
                        print(f"image {count}: top-left (x,y): {(tl_x,tl_y)}, bottom-right (x,y): {(br_x,br_y)}")
                    tl_x = x
                    br_x = x + square_size
                    count +=1                
                else:
                    continue
            tl_x = 0 + offset
            br_x = square_size + offset
            tl_y = y
            br_y = y + square_size
    else:
        tl_x = 0
        tl_y = 0 + offset
        br_x = square_size 
        br_y = square_size + offset

        for y in range(square_size + offset,max_down - offset,square_size):
            for x in range(square_size,max_right,square_size):
                if (tl_x,tl_y) not in captures:
                    sample = src.crop((tl_x,tl_y,br_x,br_y))
                    sample.save(f"{source[:-4]}_sample_{count}_x{tl_x}_y{tl_y}.jpg")
                    captures.append((tl_x,tl_y))

                    if print_coords == True: 
                        print(f"image {count}: top-left (x,y): {(tl_x,tl_y)}, bottom-right (x,y): {(br_x,br_y)}")
                    tl_x = x
                    br_x = x + square_size
                    count +=1
                else:
                    continue
            tl_x = 0
            br_x = square_size 
            tl_y = y + offset
            br_y = y + square_size + offset
    return count

你想在这里实现的是具有更高的并行度,首先要做的是了解你在这里需要做的最小任务是什么,然后从中思考更好的方法分发它。

这里首先要注意的是有两种行为,第一种是 offset_type 0,另一种是 offset_type 1,将其拆分为两个不同的函数。

第二件事是:给定一张图像,您将以给定的偏移量 (x,y) 对整个图像进行给定尺寸的裁剪。例如,在给定图像偏移量 (x,y) 的情况下,您可以简化此函数以截取图像的一部分。然后,您可以为图像的所有 x 和 y 并行调用此函数。这几乎是大多数图像处理框架试图实现的目标,甚至更多的是 运行 GPU 内的代码,小块代码,在图像中本地运行。

所以假设您的图片宽度=100,高度=100,并且您正在尝试裁剪 w=10,h=10。考虑到我描述的简单函数,我将其命名为 crop(img, x, y, crop_size_x, crop_size_y) 您所要做的就是创建图像:

img = Image.open(source)
crop_size_x = 10
crop_size_y = 10
crops = [crop(img, x, y, crop_size_x, crop_size_y) for x, y in zip(range(img.width), range(img.height))]

稍后,您可以将列表理解替换为 multi_processing 库,该库实际上可以产生许多进程,进行真正的并行处理,甚至可以在 GPU kernel/shader 中编写此类代码,然后使用GPU并行性以实现高性能。