Saya baru mengenal array multidimensi numpy, dan terjebak pada apa yang tampaknya merupakan konsep "mudah".

Dalam kode di bawah ini, fakepng mewakili gambar RGBA di dalam array numpy. Saya ingin menyetel saluran alfa ke 0 untuk setiap piksel hitam murni [0,0,0] dari gambar itu agar transparan. Saya bisa melakukan ini dengan loop for bersarang:

fakepng = np.array([[[0,0,0,255],[0,255,255,255]],[[255,255,0,255],[255,255,255,255]]])
rows, cols, channels = fakepng.shape
for x in range(0, rows):
    for y in range(0, cols):
        if (fakepng[x, y, 0] == 0 and fakepng[x, y, 1] == 0 and fakepng[x, y, 2] == 0):
            fakepng[x, y] = [0, 0, 0, 0]

Namun, saya tersesat mencoba menemukan fungsi dan/atau sintaks numpy "benar" untuk menjadikan ini 1 baris dan operasi yang efisien. Saya tidak yakin apakah ini masalah pengindeksan atau penyamaran, apakah saya memerlukan fungsi vektor, atau apa konsep terbaiknya.

0
Kevin Hakanson 25 Mei 2020, 02:16

1 menjawab

Jawaban Terbaik

Untuk menghindari loop for yang sangat lambat, Anda dapat melakukan hal berikut:

# 1. Create an auxiliary 2D array
aux_fakepng = fakepng.reshape(-1, 4)

# 2. Find all the rows in which all the elements but the last are 0
idx_rows = np.all(aux_fakepng[:, :-1] == 0, axis=1)

# 3. Set the alpha channel of those rows to 0
aux_fakepng[idx_rows, -1] = 0

Perhatikan bahwa Anda sedang memodifikasi aux_fakepng. Karena Anda memiliki dua referensi yang menunjuk ke larik yang sama, setiap modifikasi yang Anda buat pada aux_fakepng juga akan memengaruhi fakepng.

EDIT

Karena beberapa orang mungkin baru mengenal numpy dan array multidemnsional, saya akan mencoba menjelaskan sebaik mungkin apa yang terjadi di sini.

Pertama, kita mulai dengan membuat referensi baru yang akan menunjuk ke fakepng. Selama proses, kami juga membentuk ulang array, jadi lebih mudah untuk bekerja dengannya:

aux_fakepng = fakepng.reshape(-1, 4)

Saat kita membentuk ulang array kita, kita tentukan bahwa bentuk baru akan menjadi (-1, 4). Ini berarti bahwa referensi baru kita akan memiliki 4 kolom dan akan mencoba menyesuaikan jumlah baris sebaik mungkin dengan mempertimbangkan dimensi lain dari larik asli. Perhatikan bahwa operasi ini mungkin gagal jika tidak dapat memuat larik yang sempurna, yaitu larik yang semua barisnya memiliki 4 kolom.

Jika saluran memiliki nilai lebih atau kurang daripada 4, kami dapat melakukan hal berikut:

aux_fakepng = fakepng.reshape(-1, fakepng.shape[-1])

Dengan melakukan ini, kami menggunakan nilai terakhir dari nilai fakepng (yang mengacu pada jumlah saluran).

Tugas ini, seperti yang dikatakan sebelumnya, membuat referensi baru ke array asli. Ini berarti bahwa setiap modifikasi yang dilakukan pada aux_fakepng juga akan mempengaruhi fakepng, karena mereka adalah objek yang sama. Tugas ini bukan salinan dalam (ada fungsi seperti np.copy() yang memungkinkan untuk membuat salinan larik yang dalam).

Setelah kita memiliki array 2D, kita perlu menemukan baris di mana semua nilai RGB sama dengan 0. Untuk menemukan baris ini, kita dapat melakukan hal berikut:

idx_rows = np.all(aux_fakepng[:, :-1] == 0, axis=1)

Tapi, apa yang terjadi di sini? Apa yang seharusnya dilakukan aux_fakepng[:, :-1]? Mari kita mundur selangkah dulu.

Saat ini, ini adalah aux_fakepng:

array([[  0,   0,   0, 255],
       [  0, 255, 255, 255],
       [255, 255,   0, 255],
       [255, 255, 255, 255]])

Di sini, kami memilih SEMUA BARIS (: bagian) dan di setiap baris kami memilih SEMUA KOLOM KECUALI SATU TERAKHIR (:-1 bagian). Jadi, apa yang kita akses adalah array berikut:

array([[  0,   0,   0],
       [  0, 255, 255],
       [255, 255,   0],
       [255, 255, 255]])

Sekarang, kita bandingkan apakah setiap elemen sama dengan 0 atau tidak dengan melakukan aux_fakepng[:, :-1] == 0. Ini mengembalikan array berikut:

array([[ True,  True,  True],
       [ True, False, False],
       [False, False,  True],
       [False, False, False]])

Namun, kita ingin mencari baris yang semua elemennya 0, bukan di mana arraynya sama dengan 0. Untuk melakukannya, kita menggunakan fungsi np.all() dan menerapkannya sepanjang axis=1. Ini berarti bahwa fungsi akan memeriksa bahwa semua elemen di baris adalah 0. Silakan merujuk ke halaman ini jika Anda ingin pemahaman yang lebih baik tentang sumbu numpy. Fungsi menghasilkan output berikut dan menetapkannya ke idx_rows:

array([ True, False, False, False])

Terakhir, kami mengubah nilai saluran alfa dari baris tempat kami menemukan piksel hitam di (nilai [0, 0, 0]) menjadi 0:

aux_fakepng[idx_rows, -1] = 0

Dengan melakukan ini, kita mengakses baris yang indeksnya sesuai dengan posisi idx_rows di mana nilai True ditemukan. Di dalam baris-baris itu kita mengakses elemen terakhir (indeks -1), yang sesuai dengan saluran alfa, dan kita menetapkan nilainya ke 0.

3
Volokin 26 Mei 2020, 18:38