使用无循环的索引范围从 np 数组中提取多个 ROI

Extract multiple roi's from np array using indices ranges without loops

提问人:bhomaidan90 提问时间:11/6/2023 最后编辑:bhomaidan90 更新时间:11/6/2023 访问量:72

问:

我有一个形状为 640 x 480: 的 numpy 数组:,并且我有一个最小值,最大行、列范围:np.ones((640, 480))

u_min=[497, 157, 493, 137, 567] 
v_min=[ 36,  46, 208, 412, 418] 
u_max=[502, 162, 498, 142, 572]
v_max=[41, 51 213, 417, 423]

我试过:

import numpy as np

arr = np.ones((640, 480))

u_min= np.array([497, 157, 493, 137, 567], dtype=np.int32)
u_max= np.array([502, 162, 498, 142, 572], dtype=np.int32)
v_min= np.array([36, 46, 208, 412, 418], dtype=np.int32)
v_max= np.array([41, 51, 213, 417, 423], dtype=np.int32)

rois = arr[u_min:u_max, v_min:v_max]

但我收到一个错误:

TypeError: only integer scalar arrays can be converted to a scalar index

我想在不循环的情况下以矢量化的方式对 numpy 数组进行切片。你能告诉我我该怎么做吗?谢谢

python 数组 numpy 切片

评论

0赞 hpaulj 11/6/2023
这需要一个最小的可重复示例。在之前的 SO 中已解决多个 1d 切片问题。除非您可以编写高级索引数组,否则需要某种循环。
0赞 grey_ranger 11/6/2023
请演示您希望看到的输出。对你的问题有两种不同的答案和两种解释。一个从 u,v 中给出的 5x5 子数组创建一个三维数组。另一个使用多个切片返回一个二维数组。
0赞 bhomaidan90 11/7/2023
@grey_ranger两种解决方案都实现了目标,唯一的问题是循环
1赞 hpaulj 11/7/2023
另一种可能性是构造一个 (5,5) ,并从中选择 5 个(或者是 25?)块。不能保证会更快,或者满足您的无循环标准。lib.stride_tricks.sliding_window_view
2赞 hpaulj 11/7/2023
我会将其标记为 stackoverflow.com/questions/55992055/ 的副本...,除了 SO 是 1d 情况,而不是 2d。同样的问题仍然适用。我探索了所有已知的替代方案。

答:

0赞 Homer512 11/6/2023 #1

我不认为花哨的索引涵盖了这一点。或者,如果是这样,那么不是通过简单的切片,而是通过明确的索引。因此,最有效的方法是

rois = np.stack([a[ul:uh, vl:vh] for ul, uh, vl, vh
                 in zip(u_min, u_max, v_min, v_max)])

请注意,这仅是因为您的 ROI 都是相同的形状。我认为这是故意的。

评论

0赞 grey_ranger 11/6/2023
请参阅我的答案,了解在选择的形状不同的情况下使用切片的方法。
0赞 Homer512 11/6/2023
@grey_ranger 在我看来,各个切片仍然需要具有相同的整体大小。例如,您仍然不能拥有 6 x 5 像素的 ROI。除非我错过了什么
0赞 grey_ranger 11/6/2023
我们的答案之间有一个重要的区别,这可能需要OP来澄清。我将 u_min/u_max 视为 ax1 上的多个切片,ax2 上的 v 也是如此,以 2-dim numpy 数组结尾。您正在选择每个 5x5 子阵列并将它们堆叠以形成一个 3 个暗淡的 numpy 数组。从这个问题中我不清楚哪个 OP 正在寻找。(25,25)(5,5,5)
1赞 Homer512 11/6/2023
@grey_ranger是的,在试图比较答案感到困惑之后,我正要写同样的东西。让我们 OP 弄清楚这一点
0赞 bhomaidan90 11/6/2023
@Homer512,嗨,感谢您的回答,我已经编辑了问题的标题以强调无循环,否则这个问题可以通过不同的方式解决。
0赞 grey_ranger 11/6/2023 #2

你的直觉是对的:你正在尝试使用多个切片来获得多个子数组的组合。但是,实际上被解释为对 Numpy 数组对象没有意义。取而代之的是,您需要将最大值和最小值作为对,因此它们定义了数组的一个切片rois = arr[u_min:u_max, v_min:v_max]rois = arr[<list>:<list>, <list>:<list>]

让我们首先将 和 值压缩在一起。这将创建一个对象。当我们索引 numpy 数组时,每个切片都确定一个开始和停止点(参见上面的链接)。u_minu_maxtupleslice

import numpy as np
arr = np.ones((640, 480))
u_min=[497, 157, 493, 137, 567] 
v_min=[ 36,  46, 208, 412, 418] 
u_max=[502, 162, 498, 142, 572]
v_max=[41, 51, 213, 417, 423]

u_slices = tuple(slice(u1, u2) for u1, u2 in zip(u_min, u_max))
v_slices = tuple(slice(v1, v2) for v1, v2 in zip(v_min, v_max))

需要注意的是,包括起始值,但不包括止损值如果要包含止损值,请更改上述行中的 -> 和 ->。我们的切片是:u2u2 + 1v2v2 + 1

> u_slices

(slice(497, 502, None),
 slice(157, 162, None),
 slice(493, 498, None),
 slice(137, 142, None),
 slice(567, 572, None))

接下来,我们需要一种方法在对 Numpy 数组的同一调用中使用多个切片。我们将使用此答案中的 R 技巧类。

> arr[np.r_.__getitem__(u_slices), :].shape

(25, 480)

这将选择 25 行(如果使用 ),则为 30 行。由于 Numpy 处理多索引的方式,我们需要在选择的末尾使用。这告诉 Numpy,我们只想要轴 0 和轴 1 的所有给定值。完成此选择后,我们可以使用 'v_slices.我们的最终结果:u2 + 1, :]u_slices

> result = arr[np.r_[u_slices], :][: , np.r_[v_slices]]
> result.shape

(25, 25)

评论

0赞 Homer512 11/6/2023
我不明白你为什么叫.根据我对算子的理解,它应该等价于和我尝试时,结果是一样的。所以如果你写__getitem__(u_slices)[u_slices]arr[np.r_[u_slices], np.r_[v_slices]]
0赞 grey_ranger 11/6/2023
好点子,我可能把它复杂得太复杂了。我需要用于更早的尝试,但现在不再使用。我会编辑以弥补。__getitem__
0赞 Homer512 11/6/2023
我认为您的修复程序破坏了代码。 结果和以前不一样。用随机数组检查并减去两个结果。它必须是我的评论中的一个arr[np.r_[u_slices], :][: , np.r_[v_slices]][]
0赞 bhomaidan90 11/6/2023
@grey_ranger,嗨,谢谢你的回答,我已经编辑了我的问题的标题,以强调没有循环,否则这个问题可以用不同的方式解决。