Moderne Browser unterstützen die Videowiedergabe über das <video> -Element. Die meisten Browser können auch über die API MediaDevices.getUserMedia() auf die Kamera zugreifen. Aber selbst wenn diese beiden Dinge kombiniert werden, können wir nicht direkt auf diese Pixel zugreifen und sie manipulieren.
Glücklicherweise verfügen Browser über eine Canvas-API, die es uns ermöglicht, Grafiken mit JavaScript zu zeichnen. Wir können tatsächlich Bilder aus dem Video selbst auf <canvas> zeichnen, wodurch wir diese Pixel bearbeiten und anzeigen können.
Was Sie hier über die Manipulation von Pixeln lernen, bietet Ihnen die Grundlage für die Arbeit mit Bildern und Videos jeglicher Art und Quelle, nicht nur mit Leinwand.
Bild zur Leinwand hinzufügenBevor wir mit dem Video beginnen, sehen wir uns an, wie man ein Bild zur Leinwand hinzufügt.
<img src><div> <canvas id=Canvas class=video></canvas></div>
Wir erstellen ein Bildelement, um das Bild darzustellen, das auf der Leinwand gezeichnet werden soll. Alternativ können wir das Image-Objekt in JavaScript verwenden.
var canvas;var context;function init() { var image = document.getElementById('SourceImage'); context = canvas.getContext('2d'); // Oder // var image = new Image(); // image.onload = function () { // drawImage(image); // image.src = 'image.jpg';}function drawImage(image) { // Stellen Sie die Leinwand auf die gleiche Breite und Höhe wie das Bild ein. canvas.width = image.width; context.drawImage(image, 0, 0);}window.addEventListener('load', init);Der obige Code zeichnet das gesamte Bild auf die Leinwand.
Schauen Sie sich das Bild „Bild auf Leinwand malen“ von Welling Guzman (@wellingguzman) auf CodePen an.
Jetzt können wir anfangen, mit diesen Pixeln zu spielen!
Bilddaten aktualisierenBilddaten auf der Leinwand ermöglichen es uns, Pixel zu manipulieren und zu ändern.
Das Datenattribut ist ein ImageData-Objekt mit drei Eigenschaften – Breite, Höhe und Daten – die alle etwas darstellen, das auf dem Originalbild basiert. Alle diese Eigenschaften sind schreibgeschützt. Was uns wichtig ist, sind die Daten, ein eindimensionales Array, das durch ein Uint8ClampedArray-Objekt dargestellt wird und Daten für jedes Pixel im RGBA-Format enthält.
Auch wenn eine Dateneigenschaft schreibgeschützt ist, bedeutet das nicht, dass wir ihren Wert nicht ändern können. Das bedeutet, dass wir dieser Eigenschaft kein weiteres Array zuweisen können.
// Die Canvas-Bilddaten abrufenvar imageData = context.getImageData(0, 0, canvas.width, canvas.height);image.data = new Uint8ClampedArray(); // WRONGimage.data[1] = 0; // CORRECT
Sie fragen sich vielleicht, welchen Wert ein Uint8ClampedArray-Objekt darstellt? Hier ist die Beschreibung von MDN:
Das Array vom Typ Uint8ClampedArray stellt ein Array von 8-Bit-Ganzzahlen ohne Vorzeichen dar, die auf 0-255 begrenzt sind. Wenn Sie einen Wert außerhalb des Bereichs [0,255] angeben, wird 0 oder 255 festgelegt Es wird eine Ganzzahl gesetzt. Der Inhalt wird auf 0 initialisiert. Sobald Elemente im Array erstellt sind, können sie mithilfe der Methoden des Objekts oder mithilfe der standardmäßigen Array-Indizierungssyntax (d. h. mithilfe der Klammernotation) referenziert werden.
Kurz gesagt speichert dieses Array an jeder Stelle Werte im Bereich von 0 bis 255, was es zu einer perfekten Lösung für das RGBA-Format macht, da jeder Teil durch einen Wert von 0 bis 255 dargestellt wird.
RGBA-FarbeFarben können im RGBA-Format dargestellt werden, das eine Kombination aus Rot, Grün und Blau ist. A stellt den Alphawert der Deckkraft der Farbe dar.
Jede Position im Array stellt einen Farbkanalwert (Pixel) dar.
Wenn Sie ein 2x2-Bild haben, dann haben wir ein 16-Bit-Array (2x2 Pixel x jeweils 4 Werte).
2x2 Bild verkleinert
Das Array sieht folgendermaßen aus:
// ROT GRÜN BLAU WEISS[255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255, 255, 255, 255, 255]Pixeldaten ändern
Eines der schnellsten Dinge, die wir tun können, besteht darin, alle Pixel auf Weiß zu setzen, indem wir alle RGBA-Werte auf 255 ändern.
// Eine Schaltfläche verwenden, um den Effekt auszulösenvar button = document.getElementById('Button');button.addEventListener('click', onClick);function changeToWhite(data) { for (var i = 0; i < data.length; i++) { data[i] = 255; }}function onClick() { var imageData = context.getImageData(0, 0, canvas.width, canvas.height); changeToWhite(imageData.data); // Die Leinwand mit den neuen Daten aktualisieren context.putImageData(imageData, 0, 0);}Die Daten werden als Referenz übergeben, was bedeutet, dass jede Änderung, die wir daran vornehmen, den Wert des übergebenen Parameters ändert.
Farben umkehrenEin schöner Effekt, der nicht allzu viel Berechnung erfordert, ist das Invertieren der Farben eines Bildes.
Farbwerte können mit dem XOR-Operator (^) oder dieser Formel 255 – Wert (Wert muss zwischen 0 und 255 liegen) invertiert werden.
function invertColors(data) { for (var i = 0; i < data.length; i+= 4) { data[i] = data[i] ^ 255; // Invert Red data[i+1] = data[i +1] ^ 255; // Grün invertieren data[i+2] = data[i+2] ^ 255; // Blau invertieren }}function onClick() { var imageData = context.getImageData(0, 0, canvas.width, canvas.height); invertColors(imageData.data); // Die Leinwand mit den neuen Daten aktualisieren context.putImageData(imageData, 0, 0);}Wir erhöhen die Schleife um 4 statt um 1 wie zuvor, sodass wir von Pixel zu Pixel und jedem Pixel 4 Elemente im Array füllen können.
Der Alpha-Wert hat keinen Einfluss auf die Invertierung der Farben, daher überspringen wir ihn.
Helligkeit und KontrastDie Helligkeit eines Bildes kann mit der folgenden Formel angepasst werden: neuerWert = aktuellerWert + 255 * (Helligkeit / 100).
Faktor = (259 * (Kontrast + 255)) / (255 * (259 - Kontrast))color = GetPixelColor(x, y)newRed = Truncate(factor * (Red(color) - 128) + 128)newGreen = Truncate( Faktor * (Grün(Farbe) - 128) + 128)newBlue = Truncate(Faktor * (Blau(Farbe) - 128) + 128)
Die Hauptberechnung besteht darin, den Kontrastfaktor zu ermitteln, der auf jeden Farbwert angewendet wird. Truncation ist eine Funktion, die sicherstellt, dass der Wert zwischen 0 und 255 bleibt.
Schreiben wir diese Funktionen in JavaScript:
Funktion applyBrightness(Daten, Helligkeit) { for (var i = 0; i < data.length; i+= 4) { data[i] += 255 * (brightness / 100); data[i+1] += 255 * (Helligkeit / 100); data[i+2] += 255 * (Helligkeit / 100); }function truncateColor(value) { if (Wert < 0) { Wert = 0; } else if (Wert > 255) { Wert = 255; } Rückgabewert;}Funktion applyContrast(Daten, Kontrast) { var Faktor = (259,0 * (Kontrast + 255,0)) / ( 255,0 * (259,0 - Kontrast)); for (var i = 0; i < data.length; i+= 4) { data[i] = truncateColor(factor * (data[i] - 128.0) + 128.0); data[i+1] = truncateColor(factor * (data[i+1] - 128.0) + 128.0); data[i+2] = truncateColor( Faktor * (Daten[i+2] - 128,0) + 128,0);In diesem Fall benötigen Sie die Funktion truncateColor nicht, da Uint8ClampedArray die Werte abschneidet, sondern um den darin hinzugefügten Algorithmus zu übersetzen.
Beachten Sie Folgendes: Wenn Sie Helligkeit oder Kontrast anwenden, werden die Bilddaten überschrieben und können nicht in den vorherigen Zustand zurückversetzt werden. Wenn wir den Originalzustand wiederherstellen möchten, müssen die Originalbilddaten als Referenz separat gespeichert werden. Es ist hilfreich, die Bildvariable für andere Funktionen zugänglich zu halten, da Sie das Bild zum Neuzeichnen der Leinwand und des Originalbilds verwenden können.
var image = document.getElementById('SourceImage'); function redrawImage() { context.drawImage(image, 0, 0);} Verwenden Sie VideosDamit es für Videos funktioniert, nehmen wir unser ursprüngliches Bildskript und HTML-Code und nehmen einige kleinere Änderungen vor.
HTMLÄndern Sie das Bildelement des Videoelements, indem Sie die folgende Zeile ersetzen:
<img src>
...mit diesem:
<video src></video>
JavaScript
Ersetzen Sie diese Zeile:
var image = document.getElementById('SourceImage');...diese Zeile hinzufügen:
var video = document.getElementById('SourceVideo');Um mit der Verarbeitung des Videos zu beginnen, müssen wir warten, bis das Video abspielbereit ist.
video.addEventListener('canplay', function () { // Stellen Sie die Leinwand auf die gleiche Breite und Höhe wie das Video ein. canvas.width = video.videoWidth; canvas.height = video.videoHeight; // Spielen Sie das Video ab. video.play( ); // Beginnen Sie mit dem Zeichnen der Frames drawFrame(video);});Die Ereigniswiedergabe wird mindestens einige Frames lang abgespielt, wenn genügend Daten zum Abspielen der Medien vorhanden sind.
Wir können keines der auf der Leinwand angezeigten Videos sehen, da wir nur das erste Bild anzeigen. Wir müssen drawFrame alle n Millisekunden ausführen, um mit der Videobildrate Schritt zu halten.
Innerhalb von drawFrame rufen wir drawFrame alle 10 ms erneut auf.
function drawFrame(video) { context.drawImage(video, 0, 0); setTimeout(function () { drawFrame(video); }, 10);}Nachdem wir „drawFrame“ ausgeführt haben, erstellen wir eine Schleife, die „drawFrame“ alle 10 ms ausführt – genug Zeit, damit das Video innerhalb der Leinwand synchron bleibt.
Fügen Sie dem Video Effekte hinzuWir können dieselbe Funktion verwenden, die wir zuvor erstellt haben, um die Farben zu invertieren:
function invertColors(data) { for (var i = 0; i < data.length; i+= 4) { data[i] = data[i] ^ 255; // Invert Red data[i+1] = data[i +1] ^ 255; // Grün invertieren data[i+2] = data[i+2] ^ 255; // Invertieren Blau }}und fügen Sie dies zur Funktion drawFrame hinzu:
function drawFrame(video) { context.drawImage(video, 0, 0); var imageData = context.getImageData(0, 0, canvas.width, canvas.height); 0, 0); setTimeout(function () { drawFrame(video); }, 10);}Wir können eine Schaltfläche hinzufügen und den Effekt umschalten:
function drawFrame(video) { context.drawImage(video, 0, 0); if (applyEffect) { var imageData = context.getImageData(0, 0, canvas.width, canvas.height); invertColors(imageData.data); .putImageData(imageData, 0, 0); } setTimeout(function () { drawFrame(video); }, 10);} Kamera verwendenWir behalten den gleichen Code bei, den wir für das Video verwendet haben. Der einzige Unterschied besteht darin, dass wir MediaDevices.getUserMedia verwenden, um den Videostream von einer Datei in einen Kamerastream umzuwandeln.
MediaDevices.getUserMedia ist eine neue API, die die vorherige API MediaDevices.getUserMedia() veraltet. Browser unterstützen immer noch ältere Versionen, und einige Browser unterstützen neuere Versionen nicht, und wir müssen auf Polyfills zurückgreifen, um sicherzustellen, dass der Browser eine davon unterstützt.
Entfernen Sie zunächst das src-Attribut aus dem Videoelement:
<video><code></pre><pre><code>// Setze die Quelle des Videos auf die Kamera streamfunction initCamera(stream) { video.src = window.URL.createObjectURL(stream);}if (navigator .mediaDevices.getUserMedia) { navigator.mediaDevices.getUserMedia({video: true, audio: false}) .then(initCamera) .catch(console.error) );}Live-Demo
WirkungAlles, was wir bisher besprochen haben, ist die Grundlage, die wir brauchen, um unterschiedliche Effekte für Videos oder Bilder zu erzeugen. Wir können viele verschiedene Effekte nutzen, indem wir jede Farbe einzeln konvertieren.
GraustufenDie Konvertierung von Farben in Graustufen kann auf unterschiedliche Weise und mit unterschiedlichen Formeln/Techniken erfolgen. Um nicht zu tief in das Problem einzusteigen, zeige ich Ihnen fünf Formeln, die auf dem GIMP-Entsättigungstool und Luma basieren:
Grau = 0,21R + 0,72G + 0,07B // LuminosityGray = (R + G + B) ÷ 3 // Durchschnittliche HelligkeitGray = 0,299R + 0,587G + 0,114B // rec601 standardGray = 0,2126R + 0,7152G + 0,0722B / /ITU-RBT.709 StandardGray = 0,2627R + 0,6780G + 0,0593B // ITU-R BT.2100-Standard
Was wir mithilfe dieser Formeln ermitteln möchten, ist der Helligkeitsgrad jeder Pixelfarbe. Der Wert reicht von 0 (schwarz) bis 255 (weiß). Diese Werte erzeugen einen Graustufeneffekt (Schwarzweiß).
Das bedeutet, dass die hellste Farbe am nächsten bei 255 und die dunkelste Farbe am nächsten bei 0 liegt.
Live-Demo
zweifarbigDer Unterschied zwischen einem Duotone-Effekt und einem Graustufeneffekt besteht darin, dass zwei Farben verwendet werden. Bei Graustufen haben Sie einen Farbverlauf von Schwarz zu Weiß, während Sie bei Duoton einen Farbverlauf von jeder Farbe zu jeder anderen Farbe haben (von Blau zu Rosa).
Mithilfe des Intensitätswerts der Graustufen können wir ihn durch den Gradientenwert ersetzen.
Wir müssen einen Farbverlauf von FarbeA zu FarbeB erstellen.
function createGradient(colorA, colorB) { // Werte des Farbverlaufs von colorA zu colorB var gradient = [] // der maximale Farbwert ist 255 var maxValue = 255; // Konvertieren Sie die Hex-Farbwerte in RGB object var from = getRGBColor(colorA); var to = getRGBColor(colorB); // Erstellt 256 Farben von Farbe A bis Farbe B für (var i = 0; i <= maxValue; i++) { // IntensityB geht von 0 auf 255 // IntensityA geht von 255 auf 0 // IntensityA verringert die Intensität, während IntensityB zunimmt // Das bedeutet, dass ColorA zunächst fest wird und sich langsam in verwandelt ColorB // Wenn man es anders betrachtet, nimmt die Transparenz von Farbe A zu und die Transparenz von Farbe B ab Die folgende Formel kombiniert die beiden Farben basierend auf ihrer Intensität // (IntensityA * ColorA + IntensityB * ColorB) / maxValue gradient[i] = { r: (intensityA*from.r +intensitätB*to.r) / maxValue, g: ( IntensitätA*von.g + IntensitätB*bis.g) / maxValue, b: (IntensitätA*von.b + IntensitätB*bis.b) / maxValue }; gradient;}// Hilfsfunktion zum Konvertieren von 6-stelligen Hex-Werten in ein RGB-Farbobjektfunction getRGBColor(hex){ var colorValue; if (hex[0] === '#') { hex = hex.substr(1); } colorValue = parseInt(hex, 16); return { r: colorValue >> 16, g: (colorValue >> 8) & 255, b: Farbwert & 255 }}Kurz gesagt, wir erstellen eine Reihe von Farbwerten, beginnend mit Farbe A, wobei die Intensität verringert wird, während wir zu Farbe B übergehen und die Intensität erhöhen.
Von #0096ff bis #ff00f0
var Gradienten = [ {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},];Darstellung skalierender Farbübergänge
Oben ist ein Beispiel für einen Farbverlauf von 10 Farbwerten von #0096ff bis #ff00f0.
Graustufendarstellung von Farbübergängen
Da wir nun über eine Graustufendarstellung des Bildes verfügen, können wir diese verwenden, um es einem Duoton-Gradientenwert zuzuordnen.
Der Duotone-Farbverlauf hat 256 Farben, während die Graustufe ebenfalls 256 Farben von Schwarz (0) bis Weiß (255) hat. Das bedeutet, dass ein Graustufen-Farbwert einem Farbverlaufselementindex zugeordnet wird.
var gradientColors = createGradient('#0096ff', '#ff00f0');var imageData = context.getImageData(0, 0, canvas.width, canvas.height);applyGradient(imageData.data);for (var i = 0; i < data.length; i += 4) { // Den Farbwert jedes Kanals abrufen var redValue = data[i]; data[i+1]; var blueValue = data[i+2]; // Zuordnen der Farbwerte zum Farbverlaufsindex // Ersetzen des Graustufenfarbwerts durch eine Farbe für den Duotone-Farbverlauf data[i] = gradientColors[ redValue] .r; data[i+1] = gradientColors[greenValue].g; data[i+2] = gradientColors[blueValue].b;}Live-Demo
abschließendDieses Thema könnte ausführlicher sein oder mehr Implikationen erläutern. Ihre Hausaufgabe besteht darin, verschiedene Algorithmen zu finden, die auf diese Skelettbeispiele angewendet werden können.
Wenn Sie die Struktur der Pixel auf der Leinwand verstehen, können Sie eine unbegrenzte Anzahl von Effekten wie Sepia, Farbmischung, Greenscreen-Effekte, Bildblitze/Störungen usw. erstellen.
Sie können sogar spontan Effekte erstellen, ohne Bilder oder Videos zu verwenden
Das Obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, dass er für das Studium aller hilfreich ist. Ich hoffe auch, dass jeder das VeVb Wulin Network unterstützt.