Het Manipuleren Van Pixels Door Het Gebruik Van Canvas

0
14

Moderne browsers ondersteunen het afspelen van video via het <video> element. De meeste browsers hebben ook toegang tot webcams via de MediaDevices.getUserMedia API. Maar zelfs met die twee dingen gecombineerd, kunnen we niet echt openen en te bewerken die pixels direct.

Gelukkig browsers hebben een Canvas API dat stelt ons in staat om tekeningen te maken met behulp van JavaScript. We kunnen eigenlijk tekenen van beelden naar de <canvas> – van de video zelf, dat geeft ons de mogelijkheid om te manipuleren en te spelen met de pixels.

Alles wat je hier te weten komen over hoe te manipuleren van de pixels geven u een stichting te werken met afbeeldingen en video ‘ s van welke aard of welke bron dan ook, niet alleen het doek.

Het toevoegen van een afbeelding op doek

Voordat wij beginnen met het spelen van video, laten we eens kijken naar het toevoegen van een afbeelding op canvas.

<img id=”SourceImage” src=”image.jpg”>
<div class=”video-container”>
<canvas id=”Canvas” class=”video”></canvas>
</div>

We creëerden een beeld element dat overeenkomt met het beeld dat zal worden opgesteld op het doek. Ook kunnen we gebruik maken van het Afbeelding object in JavaScript.

var canvas;
var context;

functie init() {
var afbeelding = document.getElementById(‘SourceImage’);
canvas = document.getElementById(‘Canvas’);
context = canvas.getContext(‘2d’);

drawImage(afbeelding);
// Of
// var afbeelding = new Image();
// afbeelding.onload = function () {
// drawImage(afbeelding);
de // }
// afbeelding.src = ‘image.jpg’;
}

functie drawImage(beeld) {
// Stel het doek met dezelfde breedte en hoogte van de afbeelding
canvas.breedte = image.breedte;
canvas.hoogte = image.hoogte;

de context.drawImage(image, 0, 0);
}

venster.addEventListener(‘load’, init);

De bovenstaande code trekt het hele beeld op het doek.

Zie de Pen Verf afbeelding op canvas door Welling Guzman (@wellingguzman) op CodePen.

Nu kunnen we beginnen te spelen met deze pixels!

Het bijwerken van de beeldgegevens

De beeldgegevens op de canvas stelt ons in staat om te manipuleren en veranderen de pixels.

De gegevens van onroerend goed is een Afbeeldingsgegevens object met drie eigenschappen — de breedte, de hoogte en de gegevens van die een weergave zijn van die dingen, gebaseerd op de originele afbeelding. Al deze eigenschappen zijn alleen-lezen. We zijn alleen geïnteresseerd in gegevens, n één-dimensionale array vertegenwoordigd door een Uint8ClampedArray object dat de gegevens bevat van elke pixel in een RGBA-formaat.

Hoewel de gegevens eigenschap is alleen-lezen, het betekent niet dat we niet kunnen veranderen van de waarde ervan. Het betekent dat we niet toewijzen aan een andere matrix voor dit huis.

// Haal het doek image data
var afbeeldingsgegevens = context.getImageData(0, 0, canvas.de breedte van het canvas.hoogte);

afbeelding.data = new Uint8ClampedArray(); // FOUT
afbeelding.data[1] = 0; // CORRECT

Welke waarden heeft de Uint8ClampedArray object vertegenwoordigen, kan je je afvragen. Hier is de beschrijving van MDN:

De Uint8ClampedArray getypt matrix vertegenwoordigt een array van 8-bit unsigned integers geklemd om 0-255; als u een waarde is buiten het bereik van [0,255], 0 of 255 zal worden ingesteld in de plaats; als u een niet-geheel getal, het dichtstbijzijnde gehele getal worden ingesteld. De inhoud worden geïnitialiseerd op 0. Eenmaal gevestigd, kunt u verwijzen naar elementen in de matrix met behulp van de object-methoden of met behulp van de standaard array-index syntaxis (dat is, met beugel notatie)

In het kort, deze array slaat waarden variërend van 0 tot 255 in elke positie, waardoor dit de perfecte oplossing voor de RGBA formaat, elk onderdeel wordt vertegenwoordigd door 0 tot 255 waarden.

RGBA kleuren

De kleuren kunnen worden vertegenwoordigd door RGBA-formaat, dat een combinatie van Rood, Groen en Blauw. De Een geeft de alpha waarde, is de dekking van de kleur.

Elke positie in de matrix vertegenwoordigt een kleur (pixel) kanaal.

  • 1e positie is de waarde voor Rood
  • 2de positie is de Groene waarde
  • Een 3e plaats is de waarde voor Blauw
  • 4e positie is de Alpha-waarde
  • De 5e plaats is de volgende pixel Rood waarde
  • 6e positie is de volgende pixel Groene waarde
  • 7e positie is de volgende pixel Blue waarde
  • 8e positie is de volgende pixel Alpha waarde
  • En zo verder…

Als u een 2×2 beeld, dan hebben we een 16 positie array (2×2 pixels x 4 waarde).

De 2×2 afbeelding ingezoomde close-up

De matrix zal worden vertegenwoordigd, zoals hieronder weergegeven:

// ROOD GROEN BLAUW WIT
[ 255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255, 255, 255, 255, 255]
Het veranderen van de pixel data

Een van de snelste dingen die we kunnen doen is alle pixels op wit door het veranderen van alle RGBA waarden en 255.

// Gebruik de knop voor het activeren van het “effect”
var-toets = – document.getElementById(‘Button’);

knop.addEventListener(‘klik’, onClick);

functie changeToWhite(data) {
for (var i = 0; i < data.lengte; i++) {
data[i] = 255;
}
}

functie onClick() {
var afbeeldingsgegevens = context.getImageData(0, 0, canvas.de breedte van het canvas.hoogte);

changeToWhite(afbeeldingsgegevens.data);

// Update de doek met de nieuwe gegevens
de context.putImageData(afbeeldingsgegevens, 0, 0);
}

De gegevens zullen worden doorgegeven als referentie, wat betekent dat elke wijziging die we aanbrengen, verandert de waarde van het argument.

Het omkeren van kleuren

Een mooi effect dat hoeft niet veel berekening is het omkeren van de kleuren van een afbeelding.

Het omkeren van een rgb-waarde kan worden gedaan met behulp van een XOR-operator (^) of deze formule 255 – waarde (een waarde op tussen 0 en 255).

functie invertColors(data) {
for (var i = 0; i < data.lengte; i+= 4) {
data[i] = data[i] ^ 255; // Omkeren Rood
gegevens[i+1] = data[i+1] ^ 255; // Omkeren Groen
gegevens[i+2] = data[i+2] ^ 255; // Omkeren Blauw
}
}

functie onClick() {
var afbeeldingsgegevens = context.getImageData(0, 0, canvas.de breedte van het canvas.hoogte);

invertColors(afbeeldingsgegevens.data);

// Update de doek met de nieuwe gegevens
de context.putImageData(afbeeldingsgegevens, 0, 0);
}

We zijn het verhogen van de lus met 4 in plaats van 1 zoals we eerst deden, dus we kunnen van pixel naar pixel die elk vul de 4 elementen in de array.

De alpha-waarde heeft geen effect op het omkeren van kleuren, dus we kunnen overslaan.

Helderheid en contrast

Het aanpassen van de helderheid van een foto kan worden gedaan met behulp van de volgende formule: newValue = currentValue + 255 * (helderheid / 100).

  • de helderheid moet worden tussen -100 en 100
  • currentValue is de huidige licht-waarde van Rood, Groen of Blauw.
  • newValue is het resultaat van de huidige kleur licht plus helderheid

Het aanpassen van het contrast van een foto kan worden gedaan met deze formule:

factor = (259 * (contrast + 255)) / (255 * (259 – contrast))
kleur = GetPixelColor(x, y)
newRed = Afkappen(factor * (Rood(kleur) – 128) + 128)
newGreen = Afkappen(factor * (Groen(kleur) – 128) + 128)
newBlue = Afkappen(factor * (Blue(kleur) – 128) + 128)

De belangrijkste berekening is om het contrast factor die zal worden toegepast op elke kleur waarde. Afkappen is een functie die ervoor zorgen dat de waarde tussen 0 en 255.

Laten we het schrijven van deze functies in JavaScript:

functie applyBrightness(data, helderheid) {
for (var i = 0; i < data.lengte; i+= 4) {
gegevens[i] += 255 * (helderheid / 100);
gegevens[i+1] += 255 * (helderheid / 100);
gegevens[i+2] += 255 * (helderheid / 100);
}
}

functie truncateColor(waarde) {
if (waarde < 0) {
waarde = 0;
} else if (value > 255) {
waarde = 255;
}

return waarde;
}

functie applyContrast(data, contrast) {
var factor = (259.0 * (contrast + 255.0)) / (255.0 * (259.0 – contrast));

for (var i = 0; i < data.lengte; i+= 4) {
data[i] = truncateColor(factor * (data[i] – 128.0) + 128.0);
gegevens[i+1] = truncateColor(factor * (data[i+1] – 128.0) + 128.0);
gegevens[i+2] = truncateColor(factor * (data[i+2] – 128.0) + 128.0);
}
}

In dit geval hoeft u niet het truncateColor functie als Uint8ClampedArray zullen afkappen van deze waarden, maar voor het belang van het vertalen van het algoritme hebben we toegevoegd dat in.

Een ding om in gedachten te houden is dat, als je een helderheid of het contrast, er is geen weg terug naar de vorige staat van de image data wordt overschreven. De originele beeldgegevens moet worden afzonderlijk opgeslagen voor verwijzing als wij wilt herstellen naar de originele staat. Het houden van de afbeelding variabele toegankelijk is voor andere functies is handig als u kunt gebruik maken van die afbeelding in plaats van om te tekenen de doek met de originele afbeelding.

var afbeelding = document.getElementById(‘SourceImage’);

functie redrawImage() {
de context.drawImage(image, 0, 0);
}

Met behulp van video ‘ s

Om het werken met video ‘ s, gaan we onze eerste afbeelding script en HTML-code en een aantal kleine veranderingen.

HTML

De Afbeelding wijzigen element met een video element te vervangen door deze regel:

<img id=”SourceImage” src=”image.jpg”>

…met deze:

<video id=”SourceVideo” src=”video.mp4″></video>
JavaScript

Vervang deze regel:

var afbeelding = document.getElementById(‘SourceImage’);

…met deze:

var video = document.getElementById(‘SourceVideo’);

Om te beginnen met het werken met de video die we hebben om te wachten tot de video kan worden afgespeeld.

de video.addEventListener(‘rustigkunnen spelen’, function () {
// Stel het doek met dezelfde breedte en hoogte van de video
canvas.breedte = video.videoWidth;
canvas.height = video.videoHeight;

// Het afspelen van video
de video.play();

// beginnen met het tekenen van de frames
drawFrame(video);
});

Het evenement rustigkunnen spelen, wordt geactiveerd wanneer voldoende gegevens beschikbaar zijn die de media gespeeld kan worden, ten minste voor een paar van frames.

We kunnen niet zien welke van de video weergegeven op het doek, omdat we alleen het weergeven van het eerste frame. Wij moeten uitvoeren drawFrame elke n milliseconden te houden met de video frames tarief.

Binnen drawFrame noemen we drawFrame weer elke 10ms.

functie drawFrame(video) {
de context.drawImage(video, 0, 0);

setTimeout(function () {
drawFrame(video);
}, 10);
}

Nadat we uitvoeren drawFrame, we maken een lus uitvoeren van drawFrame elke 10ms — genoeg tijd om de video in sync in het doek.

Het toevoegen van het effect naar de video

We kunnen gebruik maken van dezelfde functie die we gemaakt voor het omkeren van de kleuren:

functie invertColors(data) {
for (var i = 0; i < data.lengte; i+= 4) {
data[i] = data[i] ^ 255; // Omkeren Rood
gegevens[i+1] = data[i+1] ^ 255; // Omkeren Groen
gegevens[i+2] = data[i+2] ^ 255; // Omkeren Blauw
}
}

En voeg het in de drawFrame functie:

functie drawFrame(video) {
de context.drawImage(video, 0, 0);

var afbeeldingsgegevens = context.getImageData(0, 0, canvas.de breedte van het canvas.hoogte);
invertColors(afbeeldingsgegevens.data);
de context.putImageData(afbeeldingsgegevens, 0, 0);

setTimeout(function () {
drawFrame(video);
}, 10);
}

We kunnen het toevoegen van een knop en stelt de effecten:

functie drawFrame(video) {
de context.drawImage(video, 0, 0);

als (applyEffect) {
var afbeeldingsgegevens = context.getImageData(0, 0, canvas.de breedte van het canvas.hoogte);
invertColors(afbeeldingsgegevens.data);
de context.putImageData(afbeeldingsgegevens, 0, 0);
}

setTimeout(function () {
drawFrame(video);
}, 10);
}

Met behulp van camera

We zijn steeds dezelfde code gebruiken we voor de video met het enige verschil is dat we het gaan om het wijzigen van de video-stream van een bestand naar de camera van stroom met behulp van MediaDevices.getUserMedia

MediaDevices.getUserMedia is de nieuwe API-deprecating de vorige API MediaDevices.getUserMedia(). Er is nog steeds browser ondersteuning voor de oude versie en sommige browser geen ondersteuning voor de nieuwe versie en wij genoodzaakt zijn om polyfill te zorgen dat de browser ondersteuning van één van hen

Verwijder eerst het src-attribuut van het video-element:

<video id=”SourceVideo”><code></pre>

<pre rel=”JavaScript”><code class=”language-javascript”>// Stel de bron van de video om de camera van stroom
functie initCamera(stream) {
de video.src = venster.URL.createObjectURL(stream);
}

als (navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia({video: true, audio: false})
.vervolgens(initCamera)
.catch(console.fout)
);
}

Live Demo

Effecten

Alles wat we tot dusver hebben besproken is de basis die we nodig hebben om om verschillende effecten te creëren op een video of afbeelding. Er zijn veel verschillende effecten die we kunnen gebruiken door het transformeren van elke kleur afzonderlijk.

Grijstinten

Converteren van kleur naar grijstinten kan op verschillende manieren gedaan met behulp van verschillende formules/technieken, om te voorkomen dat je te diep in het onderwerp zal ik je laten zien vijf van de formules gebaseerd op de GIMP onverzadigd gereedschap en Luma:

Grijs = 0.21 R + 0,72 G + 0.07 B // Helderheid
Grijs = (R + G + B) ÷ 3 // Gemiddelde Helderheid
Grijs = 0.299 R + 0.587 G + 0.114 B // rec601 standaard
Grijs = 0.2126 R + 0.7152 G + 0.0722 B // ITU-R BT.709 standaard
Grijs = 0.2627 R + 0.6780 G + 0.0593 B // ITU-R BT.2100 standaard

Wat we willen bereiken met behulp van deze formules is de helderheid intensiteit van elke pixel van kleur. De waarde varieert van 0 (zwart) tot 255 (wit). Deze waarden maken een grijswaarden (zwart-wit) effect.

Dit betekent dat de helderste kleur die het dichtst bij de 255 en de donkerste kleur die het dichtst bij 0.

Live Demo

Duotones

Het verschil tussen duotone effect en grijstinten effect zijn de twee kleuren worden gebruikt. Op grijstinten u een kleurverloop van zwart naar wit, terwijl op duotoon u kunt een verloop van een kleur naar een andere kleur, blauw en roze als een voorbeeld.

Met behulp van de intensiteit van de waarde van de grijswaarden, kunnen we vervangen door deze uit het verloop waarden.

We maken een verloop van ColorA te ColorB.

functie createGradient(colorA, colorB) {
// De waarden van het verloop van colorA te colorB
var verloop = [];
// de maximale kleur waarde is 255
var maxValue = 255;
// Converteer de hex kleuren naar RGB object
var van = getRGBColor(colorA);
var = getRGBColor(colorB);

// Hiermee 256 kleuren in Een Kleur naar Kleur B
for (var i = 0; i <= maxValue; i++) {
// IntensityB gaat van 0 tot 255
// IntensityA zal gaan van 255 naar 0
// IntensityA zal afnemen in intensiteit, terwijl instensityB zal toenemen
// Wat dit betekent is dat ColorA start solide en langzaam veranderen in ColorB
// Als je kijkt naar het op andere wijze de transparantie van Een kleur zal toenemen en de transparantie van de kleur B zal afnemen
var intensityB = i;
var intensityA = maxValue – intensityB;

// De onderstaande formule combineert de twee kleuren op basis van hun intensiteit
// (IntensityA * ColorA + IntensityB * ColorB) / maxValue
verloop[i] = {
r: (intensityA*uit.r + intensityB*.r) / maxValue,
g: (intensityA*uit.g + intensityB*.g) / maxValue,
b: (intensityA*uit.b + intensityB*.b) / maxValue
};
}

terug verloop;
}

// Helper functie om te zetten 6digit hex-waarden van een RGB color-object
functie getRGBColor(hex)
{
var colorValue;

als (hex[0] === ‘#’) {
hex = hex.substr(1);
}

colorValue = parseInt(hex, 16);

terug {
r: colorValue >> 16,
g: (colorValue >> 8) & 255,
b: colorValue & 255
}
}

Kortom, we zijn het creëren van een verschillende kleur-waarden van de Kleur van Een verlaging van de intensiteit, terwijl het gaat om de Kleur van B en het verhogen van de intensiteit.

Van #0096ff #ff00f0
Ingezoomde weergave van de kleur overgang

var verlopen = [
{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},
];

Boven is er een voorbeeld van een verloop van 10 kleuren waarden van #0096ff #ff00f0.

Grijstinten de vertegenwoordiging van de kleur overgang

Nu hebben we de grijswaarden representatie van het beeld, we kunnen het gebruiken om de kaart te zetten naar het verloop duotoon waarden.

Het verloop duotoon heeft 256 kleuren, terwijl de grijstinten is ook 256 kleuren, variërend van zwart (0) wit (255). Dat betekent dat in grijstinten kleur waarde wordt toegewezen aan een gradiënt element index.

var gradientColors = createGradient(‘#0096ff’, ‘#ff00f0’);
var afbeeldingsgegevens = context.getImageData(0, 0, canvas.de breedte van het canvas.hoogte);
applyGradient(afbeeldingsgegevens.data);

for (var i = 0; i < data.lengte; i += 4) {
// Voor elk kanaal kleur waarde
var redValue = data[i];
var greenValue = data[i+1];
var blueValue = data[i+2];

// In kaart brengen van de kleurwaarden het verloop index
// Het vervangen van de grijstinten kleur waarde met een kleur voor het verloop duotoon
data[i] = gradientColors[redValue].r;
gegevens[i+1] = gradientColors[greenValue].g;
gegevens[i+2] = gradientColors[blueValue].b;
gegevens[i+3] = 255;
}

Live Demo

Conclusie

Dit onderwerp kan gaan meer in de diepte uit te leggen of meer effecten. Het huiswerk voor u om te vinden van verschillende algoritmen die je kunt toepassen op deze skelet voorbeelden.

Weten hoe de pixels zijn opgebouwd op een canvas zal u toelaten om een onbeperkt aantal effecten, zoals sepia, kleur mengen, een groen scherm effect, beeldflikkering/glitching, enz.

U kunt zelfs effecten op de vliegen zonder gebruik van een afbeelding of een video:

De Jetpack WordPress plugin draait op deze site, het voeden niet alleen de gerelateerde berichten hieronder, maar de beveiliging en de back-ups, Markdown ondersteuning, site search, reactieformulier, sociale netwerk-verbindingen, en meer!