Birden fazla y1: y2, x1: x2 ile numpy dizisinin birden çok karesini dilimleyin

Aug 19 2020

Ç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

1 MadPhysicist Aug 20 2020 at 17:52

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_indexve col_indexaş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_indexbiraz daha çalışma gerektirir. nrowsHer bir çerçeve için bir dizi endeks oluşturmanız ve bunları ncolsdefalarca 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_indexyine 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_indexsipariş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_arrayde {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)
1 Divakar Aug 20 2020 at 22:22

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] = 1veya set_slices_loopolması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')