opencv4arts: Gambarkan kotaku, Vincent

OpenCV adalah perpustakaan dengan sejarah perkembangan berkelanjutan dalam 20 tahun. Usia ketika Anda mulai menggali diri sendiri, mencari tujuan. Adakah proyek yang didasarkan padanya yang membuat hidup seseorang lebih baik, seseorang lebih bahagia? Bisakah kamu melakukannya sendiri? Dalam mencari jawaban dan keinginan untuk menemukan modul OpenCV yang sebelumnya tidak dikenal, saya ingin membangun aplikasi yang "melakukan dengan indah" - sehingga pada awalnya ada "wow" dan hanya kemudian Anda berkata "oh ya, itu visi komputer".


Hak artikel pertama adalah eksperimen dengan transfer gaya seniman dunia dalam fotografi. Dari artikel ini Anda akan belajar apa inti dari prosedur dan tentang OpenCV.js yang relatif baru - versi JavaScript dari perpustakaan OpenCV.



Transfer gaya


Lawan pembelajaran mesin akan memaafkan saya, tetapi komponen utama dalam artikel hari ini adalah jaringan konvolusional yang dalam. Karena berhasil. Tidak ada cara untuk melatih jaringan saraf di OpenCV, tetapi Anda dapat menjalankan model yang ada. Kami akan menggunakan jaringan CycleGAN yang sudah dilatih sebelumnya . Para penulis, yang sangat mereka syukuri, menawarkan jaringan unduhan gratis yang mengubah gambar apel menjadi jeruk, kuda menjadi zebra, gambar satelit menjadi peta, foto musim dingin ke foto musim panas, dan banyak lagi. Selain itu, prosedur pelatihan jaringan memungkinkan Anda untuk memiliki dua model generator yang bekerja di kedua arah sekaligus. Artinya, dengan mengajarkan transformasi musim dingin menjadi musim panas, Anda akan mendapatkan model untuk melukis lanskap musim dingin dalam foto-foto musim panas. Tawaran unik yang tidak mungkin ditolak.


Dalam contoh kami, kami mengambil model yang mengubah foto menjadi lukisan karya seniman. Yakni, Vincent Van Gogh, Claude Monet, Paul Cezanne atau dalam seluruh genre cetakan Jepang Ukiyo-e. Artinya, kami akan memiliki empat jaringan terpisah yang kami miliki. Perlu dicatat bahwa untuk pelatihan masing-masing, tidak ada satu gambar artis digunakan, tetapi banyak sekali, dengan demikian penulis mencoba untuk melatih jaringan saraf untuk tidak mengubah gaya satu karya, tetapi, seolah-olah, untuk mengadopsi gaya penulisan.


Opencv.js


OpenCV adalah pustaka yang dikembangkan dalam C ++, sedangkan untuk sebagian besar fungsinya ada kemungkinan membuat pembungkus otomatis yang memanggil metode asli. Secara resmi, pembungkus untuk bahasa Python dan Java didukung. Selain itu, ada solusi khusus untuk Go , PHP . Jika Anda memiliki pengalaman menggunakan bahasa lain, akan lebih baik untuk mengetahui di mana, dan terima kasih atas upaya siapa.


OpenCV.js adalah proyek yang telah memperoleh hak untuk hidup berkat program Google Summer of Code pada 2017. Ngomong-ngomong, begitu modul pembelajaran dalam OpenCV sendiri dibuat dan secara signifikan ditingkatkan dalam kerangka kerjanya. Tidak seperti bahasa lain, OpenCV.js saat ini bukan pembungkus metode asli dalam JavaScript, tetapi kompilasi penuh menggunakan Emscripten menggunakan LLVM dan Dentang. Ini memungkinkan Anda untuk membuat file dari aplikasi C dan C ++ atau pustaka .js yang dapat dijalankan, misalnya, di browser.


Sebagai contoh,


 #include <iostream> int main(int argc, char** argv) { std::cout << "Hello, world!" << std::endl; return 0; } 

Kompilasi di asm.js


 emcc main.cpp -s WASM=0 -o main.js 

Dan memuat:


 <!DOCTYPE html> <html> <head> <script src="main.js" type="text/javascript"></script> </head> </html> 

Anda dapat menghubungkan OpenCV.js ke proyek Anda sebagai berikut (build malam):


 <script src="https://docs.opencv.org/master/opencv.js" type="text/javascript"></script> 

Perpustakaan tambahan untuk membaca gambar, bekerja dengan kamera dan hal-hal lain, yang ditulis secara manual dalam JavaScript, mungkin juga berguna:


 <script src="https://docs.opencv.org/master/utils.js" type="text/javascript"></script> 

Unggah Gambar


Gambar di OpenCV.js dapat dibaca dari elemen seperti canvas atau img . Ini berarti bahwa mengunduh file gambar secara langsung kepada mereka tetap menjadi tugas pengguna. Untuk kenyamanan, fungsi tambahan addFileInputHandler akan secara otomatis memuat gambar ke elemen canvas diinginkan ketika gambar dipilih dari disk dengan mengklik tombol.


 var utils = new Utils(''); utils.addFileInputHandler('fileInput', 'canvasInput'); var img = cv.imread('canvasInput'); 

dimana


 <input type="file" id="fileInput" name="file" accept="image/*" /> <canvas id="canvasInput" ></canvas> 

Poin penting adalah bahwa img akan menjadi gambar 4-channel RGBA, yang berbeda dari cv::imread biasa, yang menciptakan gambar BGR. Ini harus dipertimbangkan, misalnya, ketika porting algoritma dari bahasa lain.


Dengan rendering, semuanya sederhana - panggil imshow dengan id canvas diinginkan (mengharapkan RGB atau RGBA).


 cv.imshow("canvasOutput", img); 

Algoritma


Algoritma pemrosesan gambar keseluruhan adalah peluncuran jaringan saraf. Misalkan apa yang terjadi di dalam tetap ajaib, kita hanya perlu menyiapkan input yang benar dan menafsirkan prediksi dengan benar (output jaringan).


Jaringan yang dipertimbangkan dalam contoh ini menerima tensor empat dimensi dengan nilai float dalam interval [-1, 1] . Masing-masing dimensi, dalam urutan kecepatan perubahan, adalah indeks gambar, saluran, tinggi dan lebar. Gaya ini disebut NCHW, dan tensor itu sendiri disebut gumpalan, objek besar biner. Tugas pra-pemrosesan adalah mengonversi gambar OpenCV, yang intensitasnya disisipkan, memiliki interval nilai [0, 255] tipe unsigned char di gumpalan NCHW dengan kisaran nilai [-1, 1] .



sepotong Nizhny Novgorod Kremlin (seperti yang dilihat seseorang)



tampilan yang disisipkan (bagaimana OpenCV menyimpan)



tampilan planar (apa yang dibutuhkan jaringan)


Sebagai pasca-pemrosesan, akan diperlukan untuk melakukan transformasi terbalik: jaringan mengembalikan gumpalan NCHW dengan nilai-nilai dalam interval [-1, 1] , yang harus dikemas ulang ke dalam gambar, dinormalisasi ke [0, 255] dan dikonversi ke unsigned char .


Dengan demikian, dengan mempertimbangkan semua fitur membaca dan menulis gambar OpenCV.js, langkah-langkah berikut mulai terbentuk:


 imread -> RGBA -> BGR [0, 255] -> NCHW [-1, 1] -> [] [] -> NCHW [-1, 1] -> RGB [0, 255] -> imshow 

Melihat pipa yang dihasilkan, timbul pertanyaan, mengapa jaringan tidak dapat langsung bekerja pada RGBA interleaved dan mengembalikan RGB interleaved? Mengapa transformasi tambahan diperlukan untuk permutasi piksel dan normalisasi? Jawabannya adalah bahwa jaringan saraf adalah objek matematika yang melakukan transformasi pada input data dari distribusi tertentu. Dalam kasus kami, ia dilatih untuk menerima data dalam formulir ini, oleh karena itu, untuk mendapatkan hasil yang diinginkan, perlu mereproduksi preprocessing yang digunakan penulis dalam pelatihan.


Implementasi


Jaringan saraf yang akan kita jalankan disimpan dalam bentuk file biner, yang pertama-tama harus dimuat ke dalam sistem file lokal.


 var net; var url = 'style_vangogh.t7'; utils.createFileFromUrl('style_vangogh.t7', url, () => { net = cv.readNet('style_vangogh.t7'); }); 

Omong-omong, url adalah tautan lengkap ke file. Dalam hal ini, kami hanya mengunggah file di sebelah halaman HTML saat ini, tetapi Anda dapat menggantinya dengan sumber asli (dalam hal ini, waktu pengunduhan mungkin lebih lama).


Membaca gambar dari canvas dan mengkonversi dari RGBA ke BGR:


 var imgRGBA = cv.imread('canvasInput'); var imgBGR = new cv.Mat(imgRGBA.rows, imgRGBA.cols, cv.CV_8UC3); cv.cvtColor(imgRGBA, imgBGR, cv.COLOR_RGBA2BGR); 

Membuat gumpalan 4D di mana fungsi blobFromImage dikonversi ke float data float menggunakan konstanta normalisasi. Lalu - luncurkan jaringan.


 var blob = cv.blobFromImage(imgBGR, 1.0 / 127.5, //  {width: imgBGR.cols, height: imgBGR.rows}, //  [127.5, 127.5, 127.5, 0]); //   net.setInput(blob); var out = net.forward(); 

Hasilnya dikonversi kembali ke gambar dari tipe yang diinginkan dan interval nilai [0, 255]


 //     [-1, 1]  [0, 255] var outNorm = new cv.Mat(); out.convertTo(outNorm, cv.CV_8U, 127.5, 127.5); //  interleaved   planar  var outHeight = out.matSize[2]; var outWidth = out.matSize[3]; var planeSize = outHeight * outWidth; var data = outNorm.data; var b = cv.matFromArray(outHeight, outWidth, cv.CV_8UC1, data.slice(0, planeSize)); var g = cv.matFromArray(outHeight, outWidth, cv.CV_8UC1, data.slice(planeSize, 2 * planeSize)); var r = cv.matFromArray(outHeight, outWidth, cv.CV_8UC1, data.slice(2 * planeSize, 3 * planeSize)); var vec = new cv.MatVector(); vec.push_back(r); vec.push_back(g); vec.push_back(b); var rgb = new cv.Mat(); cv.merge(vec, rgb); //   cv.imshow("canvasOutput", rgb); 

Saat ini, OpenCV.js sedang dibangun dalam mode semi-otomatis. Dalam arti bahwa tidak semua modul dan metode darinya menerima tanda tangan yang sesuai dalam JavaScript. Misalnya, untuk modul dnn, daftar fungsi yang valid didefinisikan sebagai berikut:


 dnn = {'dnn_Net': ['setInput', 'forward'], '': ['readNetFromCaffe', 'readNetFromTensorflow', 'readNetFromTorch', 'readNetFromDarknet', 'readNetFromONNX', 'readNet', 'blobFromImage']} 

Konversi terakhir, membagi gumpalan menjadi tiga saluran dan kemudian mencampurkannya ke dalam gambar, pada kenyataannya, dapat dilakukan dengan imagesFromBlob metode imagesFromBlob , yang sama sekali belum ditambahkan ke daftar di atas. Mungkin ini akan menjadi kontribusi pertama Anda untuk pengembangan OpenCV? ;)


Kesimpulan


Sebagai demonstrasi, saya menyiapkan halaman di GitHub di mana Anda dapat menguji kode yang dihasilkan: https://dkurtaev.imtqy.com/opencv4arts (Perhatian! Mengunduh jaringan sekitar 22MB, menghemat lalu lintas Anda. Juga disarankan untuk memuat ulang halaman untuk setiap gambar baru, jika tidak kualitasnya. pemrosesan selanjutnya entah bagaimana sangat terdistorsi). Bersiaplah untuk proses pemrosesan yang panjang atau coba ubah ukuran gambar, yang akan menjadi hasilnya, sebuah slider.


Sambil mengerjakan artikel dan memilih gambar yang akan menjadi wajahnya, saya tidak sengaja menemukan foto teman saya, yang menggambarkan Kremlin kota kami dan semuanya menyatu - muncul dengan nama artikel dan baru kemudian merasa bahwa itu harus seperti itu. Saya sarankan Anda mencoba aplikasi pada foto-foto tempat favorit Anda dan, mungkin, menceritakan sesuatu yang menarik tentang itu di komentar atau dalam surat pribadi.


Dari saya - fakta yang menyenangkan. Sebagian besar penduduk Nizhny Novgorod dan wilayah Nizhny Novgorod menggunakan kata "keluar" dalam arti kata "cocok" (temukan tempat gratis). Misalnya, pertanyaan "Apakah kami akan membersihkan mobil Anda?" berarti "Apakah kami memiliki cukup ruang di mobil Anda?", tetapi tidak "Bisakah kami membersihkan mobil Anda?". Ketika siswa dari daerah lain datang kepada kami untuk magang musim panas, kami senang mengatakan fakta ini - banyak yang sangat terkejut.


Tautan yang bermanfaat


Source: https://habr.com/ru/post/id437600/


All Articles