我编写了这个python代码来获取标签的邻居(一组像素共享一些常见属性)。标签的邻居被定义为位于边界另一侧的其他标签(相邻标签共享边界)。所以,我写的代码有效,但速度极慢:
# segments: It is a 2-dimensional numpy array (an image really)
# where segments[x, y] = label_index. So each entry defines the
# label associated with a pixel.
# i: The label whose neighbours we want.
def get_boundaries(segments, i):
neighbors = []
for y in range(1, segments.shape[1]):
for x in range(1, segments.shape[0]):
# Check if current index has the label we want
if segments[x-1, y] == i:
# Check if neighbour in the x direction has
# a different label
if segments[x-1, y] != segments[x, y]:
neighbors.append(segments[x,y])
# Check if neighbour in the y direction has
# a different label
if segments[x, y-1] == i:
if segments[x, y-1] != segments[x, y]:
neighbors.append(segments[x, y])
return np.unique(np.asarray(neighbors))
你可以想象,我可能在这里完全滥用了python。我想知道是否有办法优化此代码以使其更加pythonic。
答案 0 :(得分:4)
你走了:
def get_boundaries2(segments, i):
x, y = np.where(segments == i) # where i is
right = x + 1
rightMask = right < segments.shape[0] # keep in bounds
down = y + 1
downMask = down < segments.shape[1]
rightNeighbors = segments[right[rightMask], y[rightMask]]
downNeighbors = segments[x[downMask], down[downMask]]
neighbors = np.union1d(rightNeighbors, downNeighbors)
return neighbors
如您所见,根本没有Python循环;我还试图最小化副本(第一次尝试制作了带有NAN边界的segments
副本,但后来我设计了“保持边界”检查。)
请注意,我没有从这里的“邻居”中过滤掉i
本身;如果你愿意,你可以在最后轻松添加。一些时间:
输入2000x3000:原始需要13秒,我需要370毫秒(35倍加速)。
输入1000x300:原始需要643毫秒,我的需要17.5毫秒(36倍加速)。
答案 1 :(得分:0)
您需要使用numpy的隐式循环替换for循环。
我不太了解您的代码以直接转换它,但我可以举个例子。
假设你有一个包含100000个随机整数的数组,你需要得到每个元素的数组除以它的邻居。
import random, numpy as np
a = np.fromiter((random.randint(1, 100) for i in range(100000)), int)
这样做的一种方法是:
[a[i] / a[i+1] for i in range(len(a)-1)]
或者这个,速度要快得多:
a / np.roll(a, -1)
Timeit:
initcode = 'import random, numpy as np; a = np.fromiter((random.randint(1, 100) for i in range(100000)), int)'
timeit.timeit('[a[i] / a[i+1] for i in range(len(a)-1)]', initcode, number=100)
5.822079309000401
timeit.timeit('(a / np.roll(a, -1))', initcode, number=100)
0.1392055350006558