使用增量邻域法以迭代方式为 voronoi 图逐个像素构建圆

Posted

技术标签:

【中文标题】使用增量邻域法以迭代方式为 voronoi 图逐个像素构建圆【英文标题】:Building a circle pixel by pixel in a iteration style for a voronoi diagram using the incremental neighborhood method 【发布时间】:2022-01-07 15:33:22 【问题描述】:

如上所述,我正在尝试使用增量邻域方法在图像中创建 Voronoi 图,该方法由给定的 n 个随机点(将是一个像素)组成,我为每个点绘制其邻居。然后这些新的邻域邻居,直到图像被填满。我目前面临的问题是这些地区完全搞砸了。我能猜到的是,如果我检查给定点的所有邻居,它最终会构建一个正方形,而不是一个圆形,因此任何点的距离都不会是欧几里得距离。我想知道如何检查和创建邻居,所以我以欧几里得距离正确绘制距离,因为我不想计算每个像素到随机点之间的距离,因为这会很慢。

我尝试使用一种方法,每次奇数迭代只检查一个像素的对角线,这让我更像圆形,但不太正确。

这就是当前代码正在做的事情。

以下是 50 次迭代和 75 次迭代的示例:

我使用的代码如下,只存在用于创建区域的部分,我稍后使用此地图正确生成图像

def createVoronoiIncremental(im, numPoints, param):
y, x, z = im.shape
points = []

count = 0
while count < numPoints:
    px = np.random.randint(0,x)
    py = np.random.randint(0,y)
    if not inPoints(np.array([px,py]), points):
        points.append(np.array([px,py]))
        count += 1

points = np.array(points)

mapPoint = 
mapDist = 

for i, col in enumerate(im):
        for j, row in enumerate(col):
            mapPoint[(j, i)] = -1 # white pixels
            mapDist[(j, i)] = y*x # white pixels


groups = 
groups[-1] = (0,0,0)
outer = 
count = 0
for point in points:
    i = point[1]
    j = point[0]
    mapPoint[(j, i)] = count # colored by group pixels
    mapDist[(j, i)] = 0
    outer[(j, i)] = [np.array([j, i])]
    groups[count] = (np.random.randint(0,255),np.random.randint(0,255),np.random.randint(0,255))
    count += 1

isNeighbour = True
count = 0
while isNeighbour:
    isNeighbour = False
    for point in points:
        outerPoints = outer[(point[0], point[1])].copy()
        newOuterPoints = []
        for p in outerPoints:
            n, mapPoint = neightbours(p, mapPoint, mapDist, (x,y), count)
            for neighbour in n:
                newOuterPoints.append(neighbour)
        outer[(point[0], point[1])] = newOuterPoints
        if len(newOuterPoints) != 0:
            isNeighbour = True
    count += 1
    if count > param:
        break


        

return mapPoint

这就是我定义社区的方式:

def neightbours(points, mapPoint, size, count):
neightbours = []

potentialNeighbours = []

if type(points) != 'numpy.ndarray':
    x = points[0]
    y = points[1]

    #vizinhos superiores
    if x-1 >= 0 and y+1 < size[1]:# and count%2 != 0:
        potentialNeighbours.append(np.array([x-1,y+1]))
    if y+1 < size[1]:
        potentialNeighbours.append(np.array([x  ,y+1]))
    if x+1 < size[0] and y+1 < size[1]:#  and count%2 != 0:
        potentialNeighbours.append(np.array([x+1,y+1]))

    #visinhos laterais
    if x-1 >= 0:
        potentialNeighbours.append(np.array([x-1,y]))
    if x+1 < size[0]:
        potentialNeighbours.append(np.array([x+1,y]))

    #vizinhos inferiores
    if x-1 >= 0 and y-1 >= 0:#  and count%2 != 0:
        potentialNeighbours.append(np.array([x-1,y-1]))
    if y-1 >= 0:
        potentialNeighbours.append(np.array([x  ,y-1]))
    if x+1 < size[0] and y-1 >= 0:#  and count%2 != 0:
        potentialNeighbours.append(np.array([x+1,y-1]))

    for potentialNeighbour in potentialNeighbours:
        if mapPoint[(potentialNeighbour[0], potentialNeighbour[1])] == -1: #white pixel
            mapPoint[(potentialNeighbour[0], potentialNeighbour[1])] = mapPoint[(x,y)]
            neightbours.append(potentialNeighbour)
else:
    for point in points:
        x = point[0]
        y = point[1]

        #vizinhos superiores
        if x-1 >= 0 and y+1 < size[1]:# and count%2 != 0:
            potentialNeighbours.append(np.array([x-1,y+1]))
        if y+1 < size[1]:
            potentialNeighbours.append(np.array([x  ,y+1]))
        if x+1 < size[0] and y+1 < size[1]:#  and count%2 != 0:
            potentialNeighbours.append(np.array([x+1,y+1]))

        #visinhos laterais
        if x-1 >= 0:
            potentialNeighbours.append(np.array([x-1,y]))
        if x+1 < size[0]:
            potentialNeighbours.append(np.array([x+1,y]))

        #vizinhos inferiores
        if x-1 >= 0 and y-1 >= 0:#  and count%2 != 0:
            potentialNeighbours.append(np.array([x-1,y-1]))
        if y-1 >= 0:
            potentialNeighbours.append(np.array([x  ,y-1]))
        if x+1 < size[0] and y-1 >= 0:#  and count%2 != 0:
            potentialNeighbours.append(np.array([x+1,y-1]))

        for potentialNeighbour in potentialNeighbours:
            if mapPoint[(potentialNeighbour[0], potentialNeighbour[1])] == -1: #white pixel
                mapPoint[(potentialNeighbour[0], potentialNeighbour[1])] = mapPoint[(x,y)]
                neightbours.append(potentialNeighbour)
                

return neightbours, mapPoint

解决方案:

使用 Bresenham 的画圆算法和其他问题中给出的答案:Given a image, a pixel point and a radious in pixels. How do I find the pixel coordenate of the circle border it creates

增加圆弧度并检查是否绘制了点,您可以创建 voronoi 图表效果:

【问题讨论】:

我的建议是首先通过计算每个像素到随机点之间的距离来检查正确性和速度来实现它 - 然后,如果它实际上太慢,请使用 hacky 方法来估计这个距离...或者更好的是,实现一个适当的高效算法(海滩线算法可能是一个不错的选择) 我已经完成了检查每个像素到每个点的距离的简单方法,它确实非常慢。财富的算法(或海滩线)有点复杂。我目前正在实施洪水跳跃,但我真的很想让这个也能工作。这是我正在工作的一个项目,我将比较实现之间的运行时间。不过还是谢谢你的建议! 什么是“增量邻域法”?你能提供任何论文的链接,或者至少是描述它的其他网站吗? 如果每个像素都有它的邻居连接到它,这就像一个 bfs。 youtube.com/…这个关于跳跃洪水的快速视频在2:55提到了这种方法。但正如我所提到的,这种方法创建了一些带有随机点的奇怪情况,如上图所示。因为它认为对角线与高度一样接近。当我一次运行代码迭代时,您可以看到这种情况发生 【参考方案1】:

我会用一种完全不同的方式,使用优先级队列:

    pq是元组(dist, center, x, y)的优先级队列,其中(x,y)是可能在中心center附近绘制的一个点,dist是该点到中心的平方距离,即 (x-center.x)2 + (y-center.y)2。优先级队列必须按照dist的升序排序。 对于每个中心c,将(0, c, c.x, c.y) 添加到pq 从队列中取出具有最小dist 的元组(dist, center, x, y)。如果(x,y) 尚未着色,则为center 着色,并将其未着色的邻居添加到具有正确dist 的队列中。 重复步骤 (3),直到 pq 为空。

这样您就不必担心如何绘制圆圈或如何找到给定距离处的像素,或其他任何事情。

如果您想查看部分结果,半径r 内的所有像素都已填充,那么您只需在dist &gt; r*r 时停止。

【讨论】:

以上是关于使用增量邻域法以迭代方式为 voronoi 图逐个像素构建圆的主要内容,如果未能解决你的问题,请参考以下文章

如何查询 Voronoi 图?

OpenCV生成点集的Delaunay剖分和Voronoi图

来自 Emgu CV(或 OpenCV)中多边形集的 Voronoi 图

最简单的Voronoi图算法实现? [关闭]

《图像处理实例》 之 Voronoi 图

Voronoi图及matlab实现