并行洪水填充算法

时间:2016-11-16 13:31:04

标签: python parallel-processing python-multiprocessing flood-fill

我有一个泛色填充算法,可以在画布中返回一组均匀着色的点。

我的实现工作正常,但需要几秒钟来计算大型画布(> = 1000x1000):

def uniform_area(canvas, x, y):
    color = canvas.point(x, y).color
    stack = {(x, y)}
    visited = set()
    results = set()

    while stack:
        x1, y1 = stack.pop()
        try:
            point = canvas.point(x1, y1)
            if point.color == color:
                results.add(point)

                nearby = [
                    (x1 - 1, y1),
                    (x1 + 1, y1),
                    (x1, y1 - 1),
                    (x1, y1 + 1)
                ]

                for p2 in nearby:
                    if p2 not in visited:
                        visited.add(p2)
                        stack.add(p2)

        except PointOutOfCanvas:
            pass

    return results

我正在研究它的并行化,并想知道最佳解决方案是什么。

我不是经验丰富的Python开发人员,但我知道GIL问题,所以我正在研究多处理模块。

首先我考虑使用一个进程池,但由于输入最初只由一个元素组成,并随着新点的发现而增长,因此Pool可能不太适合这个问题(如果我错了请纠正我这里)。

可能的解决方案是使用流程,队列和共享列表。 我实现了第一个并行化版本,但它有几个问题:

  • 它不会因大画布而终止(我猜它是因为每个进程都试图将一些元素放入一个完整的队列中导致死锁)。

  • 由于队列暂时为空,进程可能会过早停止工作。

  • 由于共享的访问列表及其周围的锁定,不确定这是否很快

  • 它看起来很复杂而且是单声道的

这是:

import multiprocessing as mp
from queue import Empty as EmptyQueue

class Manager:
    def __init__(self, manager, queue):
        self._visited_lock = mp.Lock()
        self._visited = manager.list()
        self._results_lock = mp.Lock()
        self._results = manager.list()
        self.queue = queue

    @property
    def results(self):
        return set(self._results)

    def visit(self, xy):
        with self._visited_lock:
            if xy not in self._visited:
                self._visited.append(xy)
                self.queue.put(xy)

    def found(self, point):
        with self._results_lock:
            self._results.append(point)


def uniform_area(canvas, x, y):
    NUM_PROCESSES = 4

    color = canvas.point(x, y).color

    manager = Manager(mp.Manager(), mp.Queue(1000))
    manager.queue.put((x, y))

    processes = [
        mp.Process(target=uniform_area_worker, args=(color, manager))
        for _ in range(NUM_PROCESSES)
    ]

    for process in processes:
        process.start()

    for process in processes:
        process.join()

    return manager.results

def uniform_area_worker(canvas, color, manager):
    while True:
        try:
            x1, y1 = manager.queue.get_nowait()

            point = canvas.point(x1, y1)
            if point.color == color:
                manager.found(point)

                nearby = [
                    (x1 - 1, y1),
                    (x1 + 1, y1),
                    (x1, y1 - 1),
                    (x1, y1 + 1)
                ]

                for p2 in nearby:
                    manager.visit(p2)

        except PointOutOfCanvas:
            pass

        except EmptyQueue:
            break

我不确定这是否可行。在我投入更多时间之前,我想得到一些反馈。

提前致谢!

0 个答案:

没有答案