Python 如何使用Numpy快速构建图形?
为了使用PyStruct执行图像分割(通过推理[1]),我首先需要构建一个图,其节点对应于像素,边是这些像素之间的链接 因此,我编写了一个函数,可以这样做: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
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循环使用向量化)。我尝试使用矢量化在一次过程中构建整个输出,我使用helpfullnp.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广播教程!谢谢