繁体   English   中英

切片 numpy 数组的多个帧,具有多个 y1:y2, x1:x2

[英]Slice multiple frame of numpy array with multiple y1:y2, x1:x2

我有一个多帧(multiple_frames)的 numpy 数组,我想用不同的 y1、y2、x1、x2 对每个帧的高度和宽度进行切片,以在每个帧中绘制一个“1”的正方形。 (slice_yyxx) 是一个 numpy 数组,每帧包含一个 y1,y2,x1,x2 数组。

slice_yyxx = np.array(slice_yyxx).astype(int)
nbr_frame = slice_yyxx.shape[0]

multiple_frames = np.zeros(shape=(nbr_frame, target_shape[0], target_shape[1], target_shape[2]))
print(multiple_frames.shape)
# (5, 384, 640, 1)

print(slice_yyxx)
# Value ok

print(slice_yyxx.shape)
# (5, 4)
# Then 5 array of coord like [y1, y2, x1, x2] for slice each frames

print(slice_yyxx.dtype)
# np.int64

multiple_frames[:, slice_yyxx[:,0]:slice_yyxx[:,1], slice_yyxx[:,2]:slice_yyxx[:,3]] = 1
# ERROR: TypeError: only integer scalar arrays can be converted to a scalar index

这里真正的问题是如何将任意切片转换为可以跨多个维度使用而无需循环的东西。 我认为诀窍是巧妙地结合使用精美的arange 、范围和repeat

目标是创建对应于每个维度的行和列索引数组。 让我们举一个易于可视化的简单案例:一组 3x3 矩阵的 3 帧集合,我们希望将左上角和右下角的 2x2 子数组分配给前两帧,并将整个东西分配给最后一帧:

multi_array = np.zeros((3, 3, 3))
slice_rrcc = np.array([[0, 2, 0, 2], [1, 3, 1, 3], [0, 3, 0, 3]])

让我们想出与每个索引匹配的索引,以及大小和形状:

nframes = slice_rrcc.shape[0]                       # 3
nrows = np.diff(slice_rrcc[:, :2], axis=1).ravel()  # [2, 2, 3]
ncols = np.diff(slice_rrcc[:, 2:], axis=1).ravel()  # [2, 2, 3]
sizes = nrows * ncols                               # [4, 4, 9]

我们需要以下花哨的索引才能完成分配:

frame_index = np.array([0, 0, 0, 0,   1, 1, 1, 1,   2, 2, 2, 2, 2, 2, 2, 2, 2])
row_index   = np.array([0, 0, 1, 1,   1, 1, 2, 2,   0, 0, 0, 1, 1, 1, 2, 2, 2])
col_index   = np.array([0, 1, 0, 1,   1, 2, 1, 2,   0, 1, 2, 0, 1, 2, 0, 1, 2])

如果我们可以获得 arrays frame_indexrow_indexcol_index ,我们可以将每个段的数据设置如下:

multi_array[frame_index, row_index, col_index] = 1

frame_index索引很容易获得:

frame_index = np.repeat(np.arange(nframes), sizes)

row_index需要更多的工作。 您需要为每个单独的帧生成一组nrows索引,并重复它们ncols次。 您可以通过生成连续范围并使用减法在每一帧重新开始计数来做到这一点:

row_range = np.arange(nrows.sum())
row_offsets = np.zeros_like(row_range)
row_offsets[np.cumsum(nrows[:-1])] = nrows[:-1]
row_index = row_range - np.cumsum(row_offsets) + np.repeat(slice_rrcc[:, 0], nrows)
segments = np.repeat(ncols, nrows)
row_index = np.repeat(row_index, segments)

col_index将不再那么微不足道。 您需要为具有正确偏移量的每一行生成一个序列,并为每一行分块重复它,然后为每一帧重复它。 该方法类似于row_index ,带有一个额外的花哨索引来获得正确的顺序:

col_index_index = np.arange(sizes.sum())
col_index_resets = np.cumsum(segments[:-1])
col_index_offsets = np.zeros_like(col_index_index)
col_index_offsets[col_index_resets] = segments[:-1]
col_index_offsets[np.cumsum(sizes[:-1])] -= ncols[:-1]
col_index_index -= np.cumsum(col_index_offsets)

col_range = np.arange(ncols.sum())
col_offsets = np.zeros_like(col_range)
col_offsets[np.cumsum(ncols[:-1])] = ncols[:-1]
col_index = col_range - np.cumsum(col_offsets) + np.repeat(slice_rrcc[:, 2], ncols)
col_index = col_index[col_index_index]

使用这个公式,您甚至可以加强它并为每个帧指定不同的值。 如果您想在我的示例中将values = [1, 2, 3]分配给框架,只需执行

multi_array[frame_index, row_index, col_index] = np.repeat(values, sizes)

我们将看看是否有更有效的方法来做到这一点。 我问的一个部分是here

基准

对于 {10, 100, 1000} 中的nframes{100, 1000, 10000}中的multi_array的宽度和高度,您的循环与我的矢量化解决方案的比较:

def set_slices_loop(arr, slice_rrcc):
    for a, s in zip(arr, slice_rrcc):
        a[s[0]:s[1], s[2]:s[3]] = 1

np.random.seed(0xABCDEF)
for nframes in [10, 100, 1000]:
    for dim in [10, 32, 100]:
        print(f'Size = {nframes}x{dim}x{dim}')
        arr = np.zeros((nframes, dim, dim), dtype=int)
        slice = np.zeros((nframes, 4), dtype=int)
        slice[:, ::2] = np.random.randint(0, dim - 1, size=(nframes, 2))
        slice[:, 1::2] = np.random.randint(slice[:, ::2] + 1, dim, size=(nframes, 2))
        %timeit set_slices_loop(arr, slice)
        arr[:] = 0
        %timeit set_slices(arr, slice)

结果压倒性地支持循环,唯一的例外是非常大量的帧和小帧大小。 大多数“正常”情况下循环的速度要快一个数量级:

循环播放

        |          Dimension          |
        |   100   |   1000  |  10000  |
--------+---------+---------+---------+
F    10 | 33.8 µs | 35.8 µs | 43.4 µs |
r  -----+---------+---------+---------+
a   100 |  310 µs |  331 µs |  401 µs |
m  -----+---------+---------+---------+
e  1000 | 3.09 ms | 3.31 ms | 4.27 ms |
--------+---------+---------+---------+

矢量化

        |          Dimension          |
        |   100   |   1000  |  10000  |
--------+---------+---------+---------+
F    10 |  225 µs |  266 µs |  545 µs |
r  -----+---------+---------+---------+
a   100 |  312 µs |  627 µs | 4.11 ms |
m  -----+---------+---------+---------+
e  1000 | 1.07 ms | 4.63 ms | 48.5 ms |
--------+---------+---------+---------+

TL;博士

可以做,但不推荐:

def set_slices(arr, slice_rrcc, value):
    nframes = slice_rrcc.shape[0]
    nrows = np.diff(slice_rrcc[:, :2], axis=1).ravel()
    ncols = np.diff(slice_rrcc[:, 2:], axis=1).ravel()
    sizes = nrows * ncols

    segments = np.repeat(ncols, nrows)

    frame_index = np.repeat(np.arange(nframes), sizes)

    row_range = np.arange(nrows.sum())
    row_offsets = np.zeros_like(row_range)
    row_offsets[np.cumsum(nrows[:-1])] = nrows[:-1]
    row_index = row_range - np.cumsum(row_offsets) + np.repeat(slice_rrcc[:, 0], nrows)
    row_index = np.repeat(row_index, segments)

    col_index_index = np.arange(sizes.sum())
    col_index_resets = np.cumsum(segments[:-1])
    col_index_offsets = np.zeros_like(col_index_index)
    col_index_offsets[col_index_resets] = segments[:-1]
    col_index_offsets[np.cumsum(sizes[:-1])] -= ncols[:-1]
    col_index_index -= np.cumsum(col_index_offsets)

    col_range = np.arange(ncols.sum())
    col_offsets = np.zeros_like(col_range)
    col_offsets[np.cumsum(ncols[:-1])] = ncols[:-1]
    col_index = col_range - np.cumsum(col_offsets) + np.repeat(slice_rrcc[:, 2], ncols)
    col_index = col_index[col_index_index]

    if values.size == 1:
        arr[frame_index, row_index, col_index] = value
    else:
        arr[frame_index, row_index, col_index] = np.repeat(values, sizes)

这是一篇使用benchit package(打包在一起的几个基准测试工具;免责声明:我是它的作者)对建议的解决方案进行基准测试的基准测试帖子。

我们正在使用arr[frame_index, row_index, col_index] = 1set_slices对来自 @Mad Physicist 的 soln 的 set_slices 进行基准测试, set_slices_loop无需任何更改即可获得 runtime (sec)

np.random.seed(0xABCDEF)
in_ = {}
for nframes in [10, 100, 1000]:
    for dim in [10, 32, 100]:
        arr = np.zeros((nframes, dim, dim), dtype=int)
        slice = np.zeros((nframes, 4), dtype=int)
        slice[:, ::2] = np.random.randint(0, dim - 1, size=(nframes, 2))
        slice[:, 1::2] = np.random.randint(slice[:, ::2] + 1, dim, size=(nframes, 2))
        in_[(nframes, dim)] = [arr, slice] 
    
import benchit
funcs = [set_slices, set_slices_loop]
t = benchit.timings(funcs, in_, input_name=['NumFrames', 'Dim'], multivar=True)
t.plot(sp_argID=1, logx=True, save='timings.png')

在此处输入图像描述

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM