여러 y1 : y2, x1 : x2로 여러 프레임의 numpy 배열 슬라이스
여러 프레임 (multiple_frames)의 배열이 많고 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])
배열 frame_index
, row_index
및을 얻을 수 있으면 col_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
덜 사소한 것입니다. 오른쪽 오프셋을 사용하여 각 행에 대해 시퀀스를 생성하고 각 행에 대해 청크로 반복 한 다음 각 프레임에 대해 반복해야합니다. 접근 방식은 for과 유사 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)
더 효율적인 방법이 있는지 살펴 보겠습니다. 내가 물어 본 한 부분은 여기에 있습니다 .
기준
nframes
{10, 100, 1000} 및 multi_array
in의 너비 및 높이에 대한 루프와 벡터화 된 솔루션 비교 {100, 1000, 10000}
:
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; DR
수행 할 수 있지만 권장하지 않습니다.
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제안 된 솔루션을 벤치마킹하기 위해 패키지 (함께 패키징 된 몇 가지 벤치마킹 도구, 면책 조항 : 저자입니다)를 사용하는 벤치마킹 게시물 입니다.
set_slices
@Mad Physicist의 솔루션에서 런타임을 얻기 위해 변경 사항을 포함 arr[frame_index, row_index, col_index] = 1
하거나 포함 set_slices_loop
하지 않고 벤치마킹 하고 (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')
