Python Numpy 数组索引和掩码

3
我需要帮助将一个三维数组(RGB/BGR图像)使用一个二维索引数组进行索引。所有值都是0、1或2,代表不同的颜色通道。结果应该是一个二维颜色值数组。如果有人能告诉我在Python中实现这个的语法,那就太好了!
为了了解我正在尝试做什么(也请阅读下面的TLDR):
我实际上正在尝试将以下代码从普通的for循环语法转换为更有效的Python / Numpy语法,因为它非常慢。
colorIndices = np.zeros((height,width));       # an array which has the index of the outstanding color
colorIndices -= 1;           # all -1's

for x in range(0,width):
    for y in range(0,height):

        pix = img[y,x];        # get the pixel, a 1D array of length 3
        colorID = np.argmax(pix);           #get which index has max value (candidate for outstanding color)

        if(pix[colorID]>np.average(pix)+np.std(pix)):      # if that value is more than one std dev away from the overall pixel's value, the pixel has an outstanding color
            colorIndices[y,x] = colorID;

我希望能够使用类似以下方式访问每个像素中的杰出颜色通道:

img[:,:,:]=0;
img[colorIndices] = 255;

简而言之,我想要设置一个像素为纯蓝、绿或红色,如果它是该颜色的一种阴影。我定义一个像素是否为红色的方法是,如果该像素的R值比 {R,G,B} 的整体分布平均值高一个标准差以上。

到目前为止,我有一些错误的代码:

  colorIDs = np.argmax(img, axis=2);

  averages = np.average(img, axis=2);
  stds = np.std(img, axis=2);
  cutoffs = averages + stds;

  print(img[colorIDs]);

Here is an example of the image I am using


你能贴一下 img 的示例吗? - najeem
你的代码为什么/如何出现了问题?img的形状是什么? - Mad Physicist
@MadPhysicist 我的代码还没有完成,我不知道如何完成它以满足我的目标。我应该说是未完成的。 - akarshkumar0101
@najeem 这个图片应该是通用的,适用于任何尺寸,但为了方便起见,我会上传我正在使用的那个。 - akarshkumar0101
2个回答

2

我认为你想将argmax的2d索引掩码应用于第二个轴:

In [38]: img=np.random.randint(0,10,(16,16,3))
In [39]: ids = np.argmax(img, axis=2)
In [40]: ids
Out[40]: 
array([[0, 1, 2, 1, 2, 0, 0, 0, 2, 2, 1, 0, 1, 2, 1, 0],
       [0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1],
       [1, 1, 0, 0, 0, 0, 1, 2, 0, 2, 2, 1, 2, 1, 1, 0],
       [2, 0, 1, 2, 0, 0, 1, 1, 0, 2, 2, 1, 1, 1, 1, 2],
       [2, 2, 1, 1, 0, 1, 0, 2, 1, 0, 0, 2, 2, 0, 1, 2],
       [1, 0, 2, 1, 0, 2, 0, 1, 0, 1, 1, 2, 1, 1, 0, 2],
       [1, 0, 0, 0, 1, 2, 1, 0, 1, 2, 1, 1, 1, 2, 0, 0],
       [1, 2, 2, 2, 0, 0, 1, 1, 0, 1, 0, 2, 2, 1, 1, 0],
       [0, 2, 2, 1, 0, 0, 1, 0, 2, 1, 1, 0, 2, 1, 1, 0],
       [1, 0, 2, 1, 2, 0, 1, 1, 0, 2, 2, 2, 1, 1, 0, 1],
       [1, 1, 1, 1, 1, 2, 1, 1, 0, 2, 1, 0, 0, 1, 0, 0],
       [1, 2, 1, 0, 2, 2, 2, 1, 0, 1, 2, 1, 2, 0, 2, 1],
       [2, 0, 2, 1, 2, 0, 1, 1, 2, 2, 2, 2, 1, 0, 2, 1],
       [0, 1, 0, 0, 2, 0, 1, 0, 0, 0, 0, 2, 0, 2, 0, 1],
       [0, 1, 2, 1, 1, 0, 1, 2, 0, 1, 0, 0, 2, 1, 0, 2],
       [0, 0, 2, 2, 2, 2, 2, 1, 0, 0, 0, 2, 0, 0, 1, 1]])
In [41]: I,J = np.ix_(np.arange(16), np.arange(16))
In [42]: img[I,J,ids]
Out[42]: 
array([[5, 9, 9, 8, 8, 8, 5, 7, 1, 9, 9, 5, 5, 9, 6, 8],
       [6, 7, 5, 8, 5, 6, 9, 6, 7, 7, 7, 8, 3, 7, 9, 5],
       [7, 6, 8, 7, 6, 9, 6, 8, 9, 5, 8, 8, 9, 7, 9, 6],
       [8, 9, 3, 4, 7, 5, 8, 4, 4, 9, 1, 4, 9, 9, 9, 7],
       [9, 8, 9, 7, 9, 8, 7, 5, 8, 9, 9, 6, 9, 5, 8, 8],
       [7, 9, 8, 8, 9, 3, 6, 9, 8, 6, 8, 7, 7, 7, 7, 7],
       [8, 8, 5, 8, 9, 8, 8, 2, 8, 7, 8, 9, 5, 5, 6, 7],
       [9, 6, 6, 9, 5, 3, 6, 4, 7, 6, 8, 8, 6, 3, 9, 9],
       [7, 8, 9, 7, 5, 7, 5, 9, 6, 4, 7, 7, 8, 5, 7, 8],
       [9, 7, 6, 4, 8, 9, 3, 8, 9, 2, 6, 9, 6, 7, 9, 7],
       [9, 8, 6, 6, 5, 9, 3, 9, 2, 4, 9, 5, 9, 9, 6, 9],
       [8, 7, 8, 3, 8, 8, 9, 7, 9, 5, 9, 8, 6, 9, 7, 8],
       [8, 2, 7, 7, 4, 5, 9, 8, 8, 8, 6, 5, 3, 9, 9, 6],
       [6, 8, 8, 5, 8, 8, 8, 9, 3, 7, 7, 8, 5, 4, 2, 9],
       [3, 7, 9, 9, 8, 5, 9, 8, 9, 7, 3, 3, 9, 5, 5, 9],
       [8, 4, 3, 6, 4, 9, 9, 9, 9, 9, 9, 7, 9, 7, 5, 8]])

最近的numpy版本有一个函数可以帮我们完成这个操作。
np.take_along_axis(img, ids[:,:,None], 2)[:,:,0]

使用 np.put_along_axis 来设置值。


这会创建一个由0和255组成的掩码吗? - Mad Physicist
@MadPhysicist,take along axis 方法正是我一直在寻找的(使用2D数组进行索引),但是之后我无法将其应用于我的操作中。你的帖子解决了这个问题。 - akarshkumar0101
@hpaulj 感谢您提醒我 take_along_axis 函数。我之前完全不知道这个函数,感到非常惊喜。不过,很遗憾的是,在 OP 的情况下,我认为 put_along_axis 并没有像 take 那样有优势。 - Mad Physicist

1
您可以将给定轴上的数字索引转换为值,您可以使用 np.take_along_axis花式索引。在使用花式索引时,您需要沿着所有轴用形状广播到最终结果大小的数组进行索引。np.ogrid 可以帮助实现这一点。对于一个 MxNx3 数组 img (M, N, _ = img.shape),如果您有 ix = np.argmax(img, axis=2),则索引将如下所示:
r, c = np.ogrid[:M, :N]
maxes = img[r, c, ix]

使用take_along_axis可以节省一步操作和一些临时数组:
maxes = np.take_along_axis(img, ix, 2)

现在创建您的口罩:
significant = np.abs(maxes - img.mean(axis=2) > img.std(axis=2))

此时,您拥有一个二维布尔掩码和第三个维度上的整数索引。最简单的方法可能是将所有内容转换为线性索引:
r, c = np.where(significant)

现在您可以构建输出:
color_index = np.zeros_like(img)
color_index[r, c, ix[significant]] = 255

虽然很诱人,但np.put_along_axis不能直接在这里使用。问题在于用significant屏蔽ix将使其形状不同。但是,您可以创建一个中间的二维数组,在由significant标记的位置处包含255,并将其与put_along_axis一起使用:

values = np.zeros(significant.shape, dtype=img.dtype)
values[significant] = 255
color_index = np.zeros_like(img)
np.put_along_axis(color_index, ix, values, 2)

所有组合:

ix = np.argmax(img, axis=2)
significant = np.abs(np.take_along_axis(img, ix, 2) - img.mean(axis=2)) > img.std(axis=2)
color_index = np.zeros_like(img)
color_index[(*np.where(significant), ix[significant])] = 255

M和N变量是什么? - akarshkumar0101
而且,最后一段代码中的那些星号对我来说无法运行,并且显示语法错误。 - akarshkumar0101
看了你的代码,似乎color_index将是一个二维灰度图像。这是真的吗?如果可以的话,我更喜欢它是一张彩色图像,如果是突出像素,则保持纯色,并在其他情况下保持精确像素颜色。 - akarshkumar0101
1
你说得对。我可能已经修复了这两个问题,但由于我在移动平台上,无法真正测试。我将在几个小时后在桌面上尝试并更新您。 - Mad Physicist
1
@akarshkumar0101。我已经通过实际测试修复了所有的代码片段。 - Mad Physicist
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接