Iris beberapa bingkai larik numpy dengan beberapa y1: y2, x1: x2
Saya memiliki array numpy dari beberapa frame (multiple_frames) dan saya ingin mengiris tinggi dan lebar setiap frame dengan y1, y2, x1, x2 yang berbeda untuk menggambar kuadrat "1" di setiap frame. (slice_yyxx) adalah larik numpy dan berisi satu larik y1, y2, x1, x2 untuk setiap frame.
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
Jawaban
Pertanyaan sebenarnya di sini adalah bagaimana mengubah irisan sembarang menjadi sesuatu yang dapat Anda gunakan di berbagai dimensi tanpa perulangan. Saya akan mengandaikan bahwa triknya adalah menggunakan kombinasi cerdas dari pengindeksan mewah arange,, dan repeat.
Tujuannya adalah untuk membuat larik indeks baris dan kolom yang sesuai dengan setiap dimensi. Mari kita ambil kasus sederhana yang mudah untuk divisualisasikan: set 3-frame matriks 3x3, di mana kita ingin menetapkan sub-array 2x2 kiri atas dan kanan bawah ke dua frame pertama, dan semuanya ke frame terakhir :
multi_array = np.zeros((3, 3, 3))
slice_rrcc = np.array([[0, 2, 0, 2], [1, 3, 1, 3], [0, 3, 0, 3]])
Mari kita buat indeks yang cocok dengan masing-masing, serta ukuran dan bentuknya:
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]
Kami membutuhkan indeks mewah berikut untuk dapat melakukan tugas:
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])
Jika kita dapat memperoleh array frame_index
, row_index
dan col_index
, kita dapat mengatur data untuk setiap segmen sebagai berikut:
multi_array[frame_index, row_index, col_index] = 1
frame_index
indeks mudah didapat:
frame_index = np.repeat(np.arange(nframes), sizes)
row_index
membutuhkan sedikit lebih banyak pekerjaan. Anda perlu membuat satu set nrows
indeks untuk setiap frame individu, dan mengulanginya ncols
berkali - kali. Anda dapat melakukan ini dengan membuat rentang kontinu dan memulai ulang penghitungan di setiap bingkai menggunakan pengurangan:
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
akan menjadi lebih sepele lagi. Anda perlu membuat urutan untuk setiap baris dengan offset yang tepat, dan mengulanginya dalam potongan untuk setiap baris, lalu untuk setiap frame. Pendekatannya mirip dengan untuk row_index
, dengan indeks mewah tambahan untuk mendapatkan pesanan yang benar:
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]
Dengan menggunakan formulasi ini, Anda bahkan dapat meningkatkannya dan menentukan nilai yang berbeda untuk setiap frame. Jika Anda ingin menetapkan values = [1, 2, 3]
bingkai dalam contoh saya, lakukan saja
multi_array[frame_index, row_index, col_index] = np.repeat(values, sizes)
Kami akan melihat apakah ada cara yang lebih efisien untuk melakukan ini. Satu bagian yang saya tanyakan ada di sini .
Tolok ukur
Perbandingan loop Anda vs solusi vektorisasi saya untuk nframes
dalam {10, 100, 1000} dan lebar dan tinggi multi_array
dalam {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)
Hasilnya sangat mendukung loop, dengan satu-satunya pengecualian untuk jumlah frame yang sangat besar dan ukuran frame yang kecil. Sebagian besar kasus "normal" adalah urutan besarnya lebih cepat dengan perulangan:
Pendauran
| 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 |
--------+---------+---------+---------+
Vektorisasi
| 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
Bisa dilakukan, tapi tidak disarankan:
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)
Ini adalah posting benchmarking menggunakan benchitpaket (beberapa alat benchmarking dikemas bersama; disclaimer: Saya penulisnya) untuk membandingkan solusi yang diusulkan.
Kami melakukan benchmarking set_slices
dari soln @Mad Physicist dengan arr[frame_index, row_index, col_index] = 1
dan set_slices_loop
tanpa perubahan apa pun untuk mendapatkan 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')
