Browser modern mendukung pemutaran video melalui elemen <video>
. Sebagian besar browser juga dapat mengakses kamera melalui MediaDevices.getUserMedia() API. Namun meskipun kedua hal ini digabungkan, kita tidak dapat mengakses dan memanipulasi piksel tersebut secara langsung.
Untungnya, browser memiliki Canvas API yang memungkinkan kita menggambar grafik menggunakan JavaScript. Kita sebenarnya dapat menggambar gambar ke <canvas>
dari video itu sendiri, yang memungkinkan kita memanipulasi dan menampilkan piksel tersebut.
Apa yang Anda pelajari di sini tentang cara memanipulasi piksel akan memberi Anda dasar untuk bekerja dengan gambar dan video apa pun jenis atau sumbernya, bukan hanya kanvas.
Tambahkan gambar ke kanvasSebelum kita memulai videonya, mari kita lihat cara menambahkan gambar ke kanvas.
<img src><div> <kanvas id=Kanvas kelas=video></canvas></div>
Kami membuat elemen gambar untuk mewakili gambar yang akan digambar di kanvas. Alternatifnya, kita bisa menggunakan objek Gambar di JavaScript.
var kanvas;var konteks;fungsi init() { var gambar = document.getElementById('SourceImage'); kanvas = document.getElementById('Canvas'); // Atau // var gambar = Gambar baru(); // gambar.onload = fungsi () { // drawImage(gambar); // gambar.src = 'image.jpg';}function drawImage(image) {// Atur lebar dan tinggi kanvas sama dengan gambar canvas.width = image.width; canvas.height = image.height; 0);}window.addEventListener('memuat', init);
Kode di atas menarik seluruh gambar ke kanvas.
Lihat gambar Cat di atas kanvas oleh Welling Guzman (@wellingguzman) di CodePen.
Sekarang kita bisa mulai bermain-main dengan piksel itu!
Perbarui data gambarData gambar di kanvas memungkinkan kita memanipulasi dan mengubah piksel.
Atribut data adalah objek ImageData yang memiliki tiga properti - lebar, tinggi dan data/semuanya mewakili sesuatu berdasarkan gambar aslinya. Semua properti ini bersifat read-only. Yang kami pedulikan adalah datanya, array satu dimensi yang diwakili oleh objek Uint8ClampedArray yang berisi data untuk setiap piksel dalam format RGBA.
Meskipun properti data bersifat read-only, bukan berarti kita tidak bisa mengubah nilainya. Ini berarti kita tidak dapat menetapkan array lain ke properti ini.
// Dapatkan data gambar kanvas imageData = konteks.getImageData(0, 0, kanvas.lebar, kanvas.tinggi);gambar.data = new Uint8ClampedArray(); // WRONGimage.data[1] = 0;
Anda mungkin bertanya, nilai apa yang diwakili oleh objek Uint8ClampedArray? Berikut penjelasan dari MDN:
Array tipe Uint8ClampedArray mewakili array bilangan bulat 8-bit yang tidak ditandatangani yang dijepit ke 0-255; jika Anda menentukan nilai di luar rentang [0,255], 0 atau 255 akan ditetapkan; jika Anda menentukan nilai non-integer, yang terdekat Integer akan ditetapkan. Isinya diinisialisasi ke 0. Setelah ditetapkan, elemen dalam array dapat direferensikan menggunakan metode objek, atau menggunakan sintaks pengindeksan array standar (yaitu menggunakan notasi braket)
Singkatnya, array ini menyimpan nilai mulai dari 0 hingga 255 di setiap lokasi, yang menjadikannya solusi sempurna untuk format RGBA karena setiap bagian diwakili oleh nilai dari 0 hingga 255.
warna RGBAWarna dapat direpresentasikan dalam format RGBA yang merupakan kombinasi warna merah, hijau, dan biru. A mewakili nilai alfa opacity warna.
Setiap posisi dalam array mewakili nilai saluran warna (piksel).
Jika Anda memiliki gambar 2x2, maka kami memiliki array 16 bit (masing-masing 2x2 piksel x 4 nilai).
Gambar 2x2 diperkecil
Arraynya akan terlihat seperti ini:
// MERAH HIJAU BIRU PUTIH[255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255, 255, 255, 255, 255]Ubah data piksel
Salah satu hal tercepat yang dapat kita lakukan adalah mengatur semua piksel menjadi putih dengan mengubah semua nilai RGBA menjadi 255.
// Gunakan tombol untuk memicu effectvar button = document.getElementById('Button');button.addEventListener('click', onClick);function changeToWhite(data) { for (var i = 0; i < data.length; i++) { data[i] = 255; }}fungsi onClick() { var imageData = konteks.getImageData(0, 0, kanvas.lebar, canvas.height); changeToWhite(imageData.data); // Perbarui kanvas dengan konteks data baru.putImageData(imageData, 0, 0);}
Data akan diteruskan sebagai referensi, artinya setiap modifikasi yang kita lakukan akan mengubah nilai parameter yang diteruskan.
Balikkan warnaEfek bagus yang tidak memerlukan terlalu banyak perhitungan adalah dengan membalikkan warna gambar.
Nilai warna dapat dibalik menggunakan operator XOR (^) atau rumus ini 255 - nilai (nilai harus antara 0-255).
function invertColors(data) { for (var i = 0; i < data.length; i+= 4) { data[i] = data[i] ^ 255; // Balikkan Merah data[i+1] = data[i +1] ^ 255; // Balikkan data Hijau[i+2] = data[i+2] ^ 255; // Balikkan Biru }}fungsi onClick() { var imageData = konteks.getImageData(0, 0, kanvas.lebar, kanvas.tinggi); invertColors(imageData.data); // Perbarui kanvas dengan data baru konteks.putImageData(imageData, 0, 0);}
Kami menambah loop sebesar 4, bukan 1 seperti yang kami lakukan sebelumnya, sehingga kami dapat mengisi 4 elemen dalam array dari piksel ke piksel, setiap piksel.
Nilai alpha tidak berpengaruh pada pembalikan warna, jadi kita lewati saja.
Kecerahan dan kontrasKecerahan suatu gambar dapat diatur menggunakan rumus berikut: newValue = currentValue + 255 * (brightness / 100).
faktor = (259 * (kontras + 255)) / (255 * (259 - kontras))warna = GetPixelColor(x, y)newRed = Truncate(faktor * (Merah(warna) - 128) + 128)newGreen = Truncate( faktor * (Hijau(warna) - 128) + 128)baruBiru = Potong(faktor * (Biru(warna) - 128) + 128)
Perhitungan utamanya adalah mendapatkan faktor kontras yang akan diterapkan pada setiap nilai warna. Pemotongan adalah fungsi yang memastikan nilainya tetap antara 0 dan 255.
Mari tulis fungsi-fungsi ini ke dalam JavaScript:
function applyBrightness(data, kecerahan) { for (var i = 0; i < data.length; i+= 4) { data[i] += 255 * (kecerahan / 100); (kecerahan / 100); data[i+2] += 255 * (kecerahan / 100); (nilai < 0) { nilai = 0; } else if (nilai > 255) { nilai = 255; } mengembalikan nilai;}fungsi applyContrast(data, kontras) { faktor var = (259,0 * (kontras + 255,0)) / ( 255.0 * (259.0 - kontras)); for (var i = 0; i < data.length; i+= 4) { data[i] = truncateColor(faktor * (data[i] - 128.0) + 128.0); data[i+1] = truncateColor(faktor * (data[i+1] - 128.0) + 128.0); faktor * (data[i+2] - 128,0) + 128,0 }}
Dalam hal ini Anda tidak memerlukan fungsi truncateColor karena Uint8ClampedArray memotong nilainya, tetapi untuk menerjemahkan algoritma yang kami tambahkan di dalamnya.
Satu hal yang perlu diingat adalah jika Anda menerapkan kecerahan atau kontras, data gambar akan ditimpa dan tidak dapat dikembalikan ke keadaan sebelumnya. Jika kita ingin mereset ke keadaan semula, data gambar asli harus disimpan terpisah untuk referensi. Menjaga agar variabel gambar dapat diakses oleh fungsi lain akan sangat membantu karena Anda dapat menggunakan gambar untuk menggambar ulang kanvas dan gambar asli.
var gambar = document.getElementById('SourceImage'); fungsi redrawImage() { konteks.drawImage(gambar, 0, 0);}Gunakan video
Agar berfungsi untuk video, kami akan mengambil skrip gambar awal dan kode HTML dan membuat beberapa modifikasi kecil.
HTMLUbah elemen Gambar pada elemen video dengan mengganti baris berikut:
<img src>
...dengan ini:
<video src></video>
JavaScript
Ganti baris ini:
var gambar = document.getElementById('SourceImage');
...tambahkan baris ini:
var video = document.getElementById('SourceVideo');
Untuk mulai memproses video, kita harus menunggu hingga video siap diputar.
video.addEventListener('canplay', function () {// Atur lebar dan tinggi kanvas sama dengan video canvas.width = video.videoWidth; canvas.height = video.videoHeight; // Putar video video.play( ); // mulai menggambar frame drawFrame(video);});
Pemutaran acara diputar setidaknya untuk beberapa bingkai bila terdapat cukup data untuk memutar media.
Kami tidak dapat melihat satu pun video yang ditampilkan di kanvas karena kami hanya menampilkan frame pertama. Kita harus menjalankan drawFrame setiap n milidetik untuk mengimbangi kecepatan frame video.
Di dalam drawFrame, kami memanggil drawFrame lagi setiap 10 mdtk.
fungsi drawFrame(video) { konteks.drawImage(video, 0, 0); setTimeout(fungsi () { drawFrame(video); }, 10);}
Setelah menjalankan drawFrame, kami membuat loop yang mengeksekusi drawFrame setiap 10 md - waktu yang cukup agar video tetap sinkron di dalam kanvas.
Tambahkan efek ke videoKita dapat menggunakan fungsi yang sama yang kita buat sebelumnya untuk membalikkan warna:
function invertColors(data) { for (var i = 0; i < data.length; i+= 4) { data[i] = data[i] ^ 255; // Balikkan Merah data[i+1] = data[i +1] ^ 255; // Balikkan data Hijau[i+2] = data[i+2] ^ 255; // Balikkan Biru }}
dan tambahkan ini ke fungsi drawFrame:
fungsi drawFrame(video) { konteks.drawImage(video, 0, 0); var imageData = konteks.getImageData(0, 0, canvas.width, canvas.height); 0, 0); setTimeout(fungsi () { drawFrame(video); }, 10);}
Kita dapat menambahkan tombol dan mengaktifkan efeknya:
fungsi drawFrame(video) { konteks.drawImage(video, 0, 0); if (applyEffect) { var imageData = konteks.getImageData(0, 0, kanvas.lebar, kanvas.tinggi); .putImageData(imageData, 0, 0); } setTimeout(fungsi () { drawFrame(video); }, 10);}menggunakan kamera
Kami akan menyimpan kode yang sama yang kami gunakan untuk video, satu-satunya perbedaan adalah kami akan menggunakan MediaDevices.getUserMedia untuk mengubah aliran video dari file ke aliran kamera.
MediaDevices.getUserMedia adalah API baru yang menghentikan API MediaDevices.getUserMedia() sebelumnya. Browser masih mendukung versi lama, dan beberapa browser tidak mendukung versi yang lebih baru, dan kami harus menggunakan polyfill untuk memastikan browser mendukung salah satunya.
Pertama, hapus atribut src dari elemen video:
<video><code></pre><pre><code>// Setel sumber video ke kamera streamfunction initCamera(stream) { video.src = window.URL.createObjectURL(stream);}if (navigator .mediaDevices.getUserMedia) { navigator.mediaDevices.getUserMedia({video: benar, audio: false}) .then(initCamera) .catch(konsol.kesalahan) );}
Demo Langsung
MemengaruhiSemua yang telah kita bahas sejauh ini adalah fondasi yang kita perlukan untuk menciptakan efek berbeda pada video atau gambar. Kita dapat menggunakan banyak efek berbeda dengan mengonversi setiap warna secara terpisah.
Skala abu-abuMengubah warna menjadi skala abu-abu dapat dilakukan dengan berbagai cara menggunakan rumus/teknik yang berbeda, untuk menghindari masalah yang terlalu mendalam, saya akan menunjukkan kepada Anda lima rumus berdasarkan alat desaturasi GIMP dan Luma:
Abu-abu = 0,21R + 0,72G + 0,07B // LuminositasAbu-abu = (R + G + B) 3 // Kecerahan Rata-rataAbu-abu = 0,299R + 0,587G + 0,114B // rec601 standardGray = 0,2126R + 0,7152G + 0,0722B / /ITU-RBT.709 standarAbu-abu = 0,2627R + 0,6780G + 0,0593B // standar ITU-R BT.2100
Yang ingin kita temukan dengan menggunakan rumus ini adalah tingkat kecerahan setiap warna piksel. Nilainya berkisar dari 0 (hitam) hingga 255 (putih). Nilai-nilai ini akan menciptakan efek skala abu-abu (hitam putih).
Artinya warna paling terang akan mendekati 255 dan warna paling gelap akan mendekati 0.
Demo Langsung
dua nadaPerbedaan antara efek duotone dan efek skala abu-abu adalah penggunaan dua warna. Dalam skala abu-abu Anda memiliki gradien dari hitam ke putih, sedangkan dalam duotone Anda memiliki gradien dari warna apa pun ke warna lain (dari biru ke merah muda).
Dengan menggunakan nilai intensitas skala abu-abu, kita dapat menggantinya dengan nilai gradien.
Kita perlu membuat gradien dari ColorA ke ColorB.
function createGradient(colorA, colorB) {//Nilai gradien dari colorA ke colorB var gradien = []; // nilai warna maksimum adalah 255 var maxValue = 255; objek var dari = getRGBColor(colorA); var to = getRGBColor(colorB); // Membuat 256 warna dari Warna A ke Warna B untuk (var i = 0; i <= maxValue; i++) { // IntensitasB akan naik dari 0 ke 255 // IntensitasA akan naik dari 255 ke 0 // IntensitasA akan menurunkan intensitas sementara intensitasB akan meningkat // Artinya ColorA akan mulai solid dan perlahan berubah menjadi ColorB // Jika dilihat dengan cara lain transparansi warna A akan meningkat dan transparansi warna B akan berkurang var intensitasB = i; var intensitasA = maxValue - intensitasB; rumus di bawah menggabungkan dua warna berdasarkan intensitasnya // (IntensityA * ColorA + IntensityB * ColorB) / maxValue gradien[i] = { r: (intensityA*from.r + intensitasB*to.r) / maxValue, g: ( intensitasA*dari.g + intensitasB*ke.g) / maxValue, b: (intensitasA*dari.b + intensitasB*ke.b) / maxValue }; gradien;}// Fungsi pembantu untuk mengubah nilai hex 6 digit menjadi objek warna RGBfungsi getRGBColor(hex){ var colorValue if (hex[0] === '#') { hex = hex.substr(1); } colorValue = parseInt(hex, 16); return { r: colorValue >> 16, g: (colorValue >> 8) & 255, b: Nilai warna & 255 }}
Singkatnya, kita membuat sekumpulan nilai warna mulai dari warna A, mengurangi intensitas, lalu menuju warna B dan meningkatkan intensitas.
Dari #0096ff ke #ff00f0
gradien var = [ {r: 32, g: 144, b: 254}, {r: 41, g: 125, b: 253}, {r: 65, g: 112, b: 251}, {r: 91 , g: 96, b: 250}, {r: 118, g: 81, b: 248}, {r: 145, g: 65, b: 246}, {r: 172, g: 49, b: 245}, {r: 197, g: 34, b: 244}, {r: 220, g: 21 , b: 242}, {r: 241, g: 22, b: 242},];
Representasi transisi warna skala
Di atas adalah contoh gradien 10 nilai warna dari #0096ff hingga #ff00f0.
Representasi transisi warna dalam skala abu-abu
Sekarang kita memiliki representasi gambar dalam skala abu-abu, kita dapat menggunakannya untuk memetakannya ke nilai gradien duotone.
Gradien duotone memiliki 256 warna sedangkan skala abu-abu juga memiliki 256 warna mulai dari hitam (0) hingga putih (255).
var gradienWarna = createGradient('#0096ff', '#ff00f0');var imageData = konteks.getImageData(0, 0, canvas.width, canvas.height);applyGradient(imageData.data);for (var i = 0; i < data.length; i += 4) { // Dapatkan nilai warna setiap saluran var redValue = data[i]; data[i+1]; var blueValue = data[i+2]; // Memetakan nilai warna ke indeks gradien // Mengganti nilai warna skala abu-abu dengan warna untuk gradien duotone data[i] = gradienWarna[ Nilai merah] .r; data[i+1] = gradienWarna[Nilai hijau].g; data[i+2] = warna gradien[Nilai biru].b;
Demo Langsung
sebagai kesimpulanTopik ini bisa lebih mendalam atau menjelaskan lebih banyak implikasi. Pekerjaan rumah Anda adalah menemukan algoritma berbeda yang dapat diterapkan pada contoh kerangka ini.
Memahami struktur piksel pada kanvas akan memungkinkan Anda membuat efek dalam jumlah tak terbatas seperti sepia, pencampuran warna, efek layar hijau, kilatan/gangguan gambar, dll.
Anda bahkan dapat membuat efek dengan cepat tanpa menggunakan gambar atau video
Di atas adalah keseluruhan isi artikel ini, saya harap dapat bermanfaat untuk pembelajaran semua orang. Saya juga berharap semua orang mendukung VeVb Wulin Network.