Birden fazla y1: y2, x1: x2 ile numpy dizisinin birden çok karesini dilimleyin
Çok sayıda çerçeveden oluşan uyuşmuş bir dizim var (çoklu çerçeve) ve her karede "1" 'lik bir kare çizmek için her çerçevenin yüksekliğini ve genişliğini farklı y1, y2, x1, x2 ile dilimlemek istiyorum. (dilim_yyxx) uyuşmuş bir dizidir ve her çerçeve için bir y1, y2, x1, x2 dizisi içerir.
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
Yanıtlar
Buradaki asıl soru, rastgele dilimleri döngü yapmadan birden çok boyutta kullanabileceğiniz bir şeye nasıl dönüştürebileceğinizdir. İşin püf noktası, süslü indekslemenin akıllıca bir kombinasyonunu kullanmaktır arangeve repeat.
Amaç, her bir boyuta karşılık gelen bir satır ve sütun dizini dizisi oluşturmaktır. Görselleştirmesi kolay basit bir durumu ele alalım: Sol üst ve sağ alt 2x2 alt dizileri ilk iki kareye ve her şeyi son kareye atamak istediğimiz 3 çerçeveli bir 3x3 matris seti. :
multi_array = np.zeros((3, 3, 3))
slice_rrcc = np.array([[0, 2, 0, 2], [1, 3, 1, 3], [0, 3, 0, 3]])
Boyut ve şekillerin yanı sıra her birine uyan endeksleri bulalım:
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]
Görevi yapabilmek için aşağıdaki süslü endekslere ihtiyacımız var:
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])
Dizilerle elde Eğer frame_index
, row_index
ve col_index
aşağıdaki gibi, her bir segment için veri ayarlayabilirsiniz:
multi_array[frame_index, row_index, col_index] = 1
frame_index
indeksi elde etmek kolaydır:
frame_index = np.repeat(np.arange(nframes), sizes)
row_index
biraz daha çalışma gerektirir. nrows
Her bir çerçeve için bir dizi endeks oluşturmanız ve bunları ncols
defalarca tekrarlamanız gerekir . Bunu, sürekli bir aralık oluşturarak ve çıkarmayı kullanarak her karede sayımı yeniden başlatarak yapabilirsiniz:
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
yine de daha az önemsiz olacak. Doğru ofseti olan her satır için bir dizi oluşturmanız ve bunu her satır için ve ardından her kare için parçalar halinde tekrarlamanız gerekir. Yaklaşım, row_index
siparişi doğru bir şekilde elde etmek için ek bir fantezi endeksi ile buna benzer :
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]
Bu formülasyonu kullanarak, onu artırabilir ve her kare için farklı bir değer belirleyebilirsiniz. Örneğimdeki values = [1, 2, 3]
çerçevelere atamak isterseniz ,
multi_array[frame_index, row_index, col_index] = np.repeat(values, sizes)
Bunu yapmanın daha verimli bir yolu olup olmadığını göreceğiz. Sorduğum bir bölüm burada .
Kıyaslama
Benim vectorized çözeltisi vs loopunuzda karşılaştırılması nframes
{10, 100, 1000} ve genişliği ve yüksekliği multi_array
de {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)
Sonuçlar, çok sayıda çerçeve ve küçük çerçeve boyutları dışında büyük ölçüde döngü lehinedir. Çoğu "normal" durum, döngüde daha hızlıdır:
Döngü
| 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 |
--------+---------+---------+---------+
Vektörize
| 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
Yapılabilir, ancak tavsiye edilmez:
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)
Bu, benchitönerilen çözümleri karşılaştırmak için paketi (birlikte paketlenmiş birkaç kıyaslama aracı; sorumluluk reddi: Ben onun yazarıyım) kullanan bir kıyaslama gönderisidir .
set_slices
@Mad Physicist'in çözümünden çalışma zamanı elde etmek için herhangi bir değişiklik olsun arr[frame_index, row_index, col_index] = 1
veya set_slices_loop
olmasın kıyaslama yapıyoruz (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')
