Python 如何使用Numpy快速构建图形?

Python 如何使用Numpy快速构建图形?,python,numpy,Python,Numpy,为了使用PyStruct执行图像分割(通过推理[1]),我首先需要构建一个图,其节点对应于像素,边是这些像素之间的链接 因此,我编写了一个函数,可以这样做: def create_graph_for_pystruct(mrf, betas, nb_labels): M, N = mrf.shape b1, b2, b3, b4 = betas edges = [] pairwise = np.zeros((nb_labe

为了使用PyStruct执行图像分割(通过推理[1]),我首先需要构建一个图,其节点对应于像素,边是这些像素之间的链接

因此,我编写了一个函数,可以这样做:

    def create_graph_for_pystruct(mrf, betas, nb_labels):

        M, N = mrf.shape
        b1, b2, b3, b4 = betas

        edges = []
        pairwise = np.zeros((nb_labels, nb_labels))

        # loop over rows
        for i in range(M):

            # loop over columns
            for j in range(N):

                # get rid of pixels belonging to image's borders
                if i!=0 and i!=M-1 and j!=0 and j!=N-1:

                    # get the current linear index
                    current_linear_ind = i * N + j

                    # retrieve its neighborhood (yield a list of tuple (row, col))
                    neigh = np.array(getNeighborhood(i, j, M, N))

                    # convert neighbors indices to linear ones
                    neigh_linear_ind = neigh[:, 0] * N + neigh[:, 1]

                    # add edges
                    [edges.append((current_linear_ind, n)) for n in neigh_linear_ind]


                    mat1 = b1 * np.eye(nb_labels)
                    mat2 = b2 * np.eye(nb_labels)
                    mat3 = b3 * np.eye(nb_labels)
                    mat4 = b4 * np.eye(nb_labels)

                    pairwise = np.ma.dstack((pairwise, mat1, mat1, mat2, mat2, mat3, mat3, mat4, mat4))               



        return np.array(edges), pairwise[:, :, 1:]
然而,它是缓慢的,我想知道我在哪里可以改进我的功能,以加快它。
[1]

这里有一个代码建议,它应该运行得更快(在numpy中,应该关注对for循环使用向量化)。我尝试使用矢量化在一次过程中构建整个输出,我使用helpfull
np.ogrid
生成xy坐标

def new(mrf, betas, nb_labels):
    M, N = mrf.shape
    b1, b2, b3, b4 = betas

    mat1,mat2,mat3,mat4 =  np.array([b1,b2,b3,b4])[:,None,None]*np.eye(nb_labels)[None,:,:]
    pairwise = np.array([mat1, mat1, mat2, mat2, mat3, mat3, mat4, mat4]*((M-2)*(N-2))).transpose()

    m,n=np.ogrid[0:M,0:N]

    a,b,c= m[0:-2]*N+n[:,0:-2],m[1:-1]*N+n[:,0:-2],m[2: ]*N+n[:,0:-2]
    d,e,f= m[0:-2]*N+n[:,1:-1],m[1:-1]*N+n[:,1:-1],m[2: ]*N+n[:,1:-1]
    g,h,i= m[0:-2]*N+n[:,2:  ],m[1:-1]*N+n[:,2:  ],m[2: ]*N+n[:,2:  ]

    center_index = e
    edges_index = np.stack([a,b,c,d,f,g,h,i])

    edges=np.empty(list(edges_index.shape)+[2])
    edges[:,:,:,0]= center_index[None,:,:]
    edges[:,:,:,1]= edges_index

    edges=edges.reshape(-1,2)

    return edges,pairwise
定时和比较测试:

import timeit

args=(np.empty((40,50)), [1,2,3,4], 10)

f1=lambda : new(*args)
f2=lambda : create_graph_for_pystruct(*args)


edges1, pairwise1 = f1()
edges2, pairwise2 = f2()

#outputs are not exactly indentical: the order isn't the the same
#I sort both to compare the results
edges1 = edges1[np.lexsort(np.fliplr(edges1).T)]
edges2 = edges2[np.lexsort(np.fliplr(edges2).T)]


print("edges identical ?",(edges1 == edges2).all())
print("pairwise identical ?",(pairwise1 == pairwise2).all())

print("new : ",timeit.timeit(f1,number=1))
print("old : ",timeit.timeit(f2,number=1))
输出:

edges identical ? True
pairwise identical ? True
new :  0.015270026000507642
old :  4.611805051001284

注意:我不得不猜测
getNeighborhood
函数中的内容,这里有一个代码建议,它应该运行得更快(在numpy中,应该关注对for循环使用向量化)。我尝试使用矢量化在一次过程中构建整个输出,我使用helpfull
np.ogrid
生成xy坐标

def new(mrf, betas, nb_labels):
    M, N = mrf.shape
    b1, b2, b3, b4 = betas

    mat1,mat2,mat3,mat4 =  np.array([b1,b2,b3,b4])[:,None,None]*np.eye(nb_labels)[None,:,:]
    pairwise = np.array([mat1, mat1, mat2, mat2, mat3, mat3, mat4, mat4]*((M-2)*(N-2))).transpose()

    m,n=np.ogrid[0:M,0:N]

    a,b,c= m[0:-2]*N+n[:,0:-2],m[1:-1]*N+n[:,0:-2],m[2: ]*N+n[:,0:-2]
    d,e,f= m[0:-2]*N+n[:,1:-1],m[1:-1]*N+n[:,1:-1],m[2: ]*N+n[:,1:-1]
    g,h,i= m[0:-2]*N+n[:,2:  ],m[1:-1]*N+n[:,2:  ],m[2: ]*N+n[:,2:  ]

    center_index = e
    edges_index = np.stack([a,b,c,d,f,g,h,i])

    edges=np.empty(list(edges_index.shape)+[2])
    edges[:,:,:,0]= center_index[None,:,:]
    edges[:,:,:,1]= edges_index

    edges=edges.reshape(-1,2)

    return edges,pairwise
定时和比较测试:

import timeit

args=(np.empty((40,50)), [1,2,3,4], 10)

f1=lambda : new(*args)
f2=lambda : create_graph_for_pystruct(*args)


edges1, pairwise1 = f1()
edges2, pairwise2 = f2()

#outputs are not exactly indentical: the order isn't the the same
#I sort both to compare the results
edges1 = edges1[np.lexsort(np.fliplr(edges1).T)]
edges2 = edges2[np.lexsort(np.fliplr(edges2).T)]


print("edges identical ?",(edges1 == edges2).all())
print("pairwise identical ?",(pairwise1 == pairwise2).all())

print("new : ",timeit.timeit(f1,number=1))
print("old : ",timeit.timeit(f2,number=1))
输出:

edges identical ? True
pairwise identical ? True
new :  0.015270026000507642
old :  4.611805051001284


注意:我必须猜测
getNeighborhood
函数中有什么内容

一个小改动应该会有所帮助:移动mat1、mat2等。。。循环之外的定义,它们不会改变,所以每次重新定义它们都是巨大的时间损失。事实上,我已经做到了。但是,它并没有加快任何速度。下一件大事是删除那些for循环,但我并不真正了解你的代码在做什么,所以我甚至不确定它是否可以矢量化…这个想法是在图像的所有像素上循环,对于每个像素,检索它的8个邻居。另一个修复方法是移动
pairwise=np.ma.dstack((两两,
从循环中移出,一次构建整个过程。所有堆叠的数组都必须复制到新数组中,这样随着两两数组的增长,每个副本将花费更多的时间。一个小的改变应该会有所帮助:将mat1、mat2等定义移出循环,它们不会改变,因此每次都需要重新定义它们这是一个巨大的时间损失。的确,我已经做了。但是,它并没有加快任何速度。下一件大事是删除那些for循环,但我不知道你的代码在做什么,所以我甚至不确定它是否可以矢量化…这个想法是循环图像的所有像素,并为每个像素检索它的8个邻居。另一个修复方法是将
pairwise=np.ma.dstack((pairwise,
)移出循环,一次完成整个构建。所有堆叠的数组都必须复制到一个新数组中,以便随着pairwise数组的增长,每次复制都会花费多一点时间。如果我们不使用图像边框,则getNeighborary()函数将返回一个包含8个元组的列表(X,Y)对应于给定像素邻域的2D坐标。b1适用于两个垂直邻域,b2适用于两个水平邻域,b3适用于属于第一条对角线的两个邻域,b4适用于属于第二条对角线的两个邻域。太好了,这就是我用来进行比较的。你能详细说明这样一条线:m[0:-2]*N+N[:,0:-2]有效?虽然我经常使用numpy,但我在理解这一点时遇到了一些困难,因为维度似乎不兼容。m[0:-2]和N[:,0:-2]都是二维数组,它们的维度都适合广播(m是m,1,N是1,N).所以numpy自动将第一项维度从M-2,1延伸到M-2,N-2,第二项维度从1,N-2延伸到M-2,N-2。事实上,我刚刚阅读了numpy广播教程!谢谢如果我们不使用图像边框,getNeighborator()函数将返回一个包含8个元组(X,Y)的列表对应于给定像素邻域的2D坐标。b1适用于两个垂直邻域,b2适用于两个水平邻域,b3适用于属于第一条对角线的两个邻域,b4适用于属于第二条对角线的两个邻域。太好了,这就是我用来进行比较的。你能详细说明这样一条线:m[0:-2]*N+N[:,0:-2]有效?虽然我经常使用numpy,但我在理解这一点时遇到了一些困难,因为维度似乎不兼容。m[0:-2]和N[:,0:-2]都是二维数组,它们的维度都适合广播(m是m,1,N是1,N).所以numpy自动将第一个术语维度从M-2,1延伸到M-2,N-2,第二个术语维度从1,N-2延伸到M-2,N-2。事实上,我刚刚阅读了numpy广播教程!谢谢