Manipulere Punkter Ved Hjelp Av Lerret

0
9

Moderne nettlesere støtter avspilling av video via <video> – element. De fleste nettlesere har også tilgang til webkameraer via MediaDevices.getUserMedia() API. Men selv med disse to tingene kombinert, vi kan egentlig ikke få tilgang til og manipulere de punkter direkte.

Heldigvis, nettlesere har et Lerret API som gjør det mulig for oss å tegne grafikk ved hjelp av JavaScript. Vi kan faktisk tegne bilder til <lerret> fra videoen, som gir oss muligheten til å manipulere og leke med de punkter.

Alt du lærer her om hvordan å manipulere punkter vil gi deg et grunnlag for å arbeide med bilder og videoer av noe slag, eller en hvilken som helst kilde, ikke bare lerret.

Legge et bilde til lerret

Før vi begynner å spille med video, la oss se på legge et bilde til lerret.

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

Vi har opprettet et element i bildet som representerer bildet som kommer til å bli trukket på lerretet. Alternativt kunne vi bruke Bildet objekt i JavaScript.

var lerret;
var kontekst;

function init() {
var bilde = – dokument.bürgerliches(‘SourceImage’);
lerret = – dokument.bürgerliches(‘Lerret’);
context = lerret.getContext(‘2d’);

drawImage(bilde);
// Eller
// var bilde = new Image();
// bilde.onload = function () {
// drawImage(bilde);
// }
// bilde.src = ‘image.jpg’;
}

funksjonen drawImage(bilde) {
// Sett lerret samme bredde og høyde på bildet
lerret.bredde = bilde.bredde;
lerret.høyde = bilde.høyde;

sammenheng.drawImage(bilde, 0, 0);
}

vinduet.addEventListener(‘load’, init);

Koden ovenfor trekker hele bildet på lerretet.

Se Penn, Maling bilde på lerret av Welling Guzman (@wellingguzman) på CodePen.

Nå kan vi begynne å leke med de punkter!

Oppdatering av bildedata

Bildedata på lerretet gir oss muligheten til å manipulere og endre punkter.

Data eiendom er en ImageData objekt med tre egenskaper — bredde, høyde og data/ alle som representerer de tingene som er basert på det opprinnelige bildet. Alle disse egenskapene er skrivebeskyttet. Det vi bryr oss om er data, n-dimensjonalt array representert ved en Uint8ClampedArray objekt, som inneholder data for hver piksel i et RGBA-format.

Selv om dataene eiendommen er skrivebeskyttet, det betyr ikke at vi ikke kan endre sin verdi. Det betyr at vi ikke kan tildele en annen matrise til denne eiendommen.

// Få lerretet image data
var imageData = kontekst.getImageData(0, 0, lerret.bredde, lerret.høyde);

bilde.data = new Uint8ClampedArray(); // FEIL
bilde.data[1] = 0; // KORREKT

Hvilke verdier har Uint8ClampedArray objekt representerer, kan du spørre. Her er beskrivelsen fra MDN:

Den Uint8ClampedArray skrevet matrisen representerer et utvalg av 8-bit unsigned heltall festet til 0-255; hvis du har angitt en verdi som er utenfor rekkevidden av [0,255], 0 eller 255 vil bli satt inn i stedet, hvis du angir en ikke-heltall, nærmeste heltall vil bli satt. Innholdet er initialisert til 0. Når etablert, kan du referere elementene i matrisen ved hjelp objektets metoder, eller bruker standard utvalg indeks syntaks (som er, ved hjelp av brakett notasjon)

Kort sagt, denne tabellen lagrer verdier går fra 0 til 255 i hver posisjon, noe som gjør dette til den perfekte løsningen for RGBA-format, så hver del er representert ved 0 til 255 verdier.

RGBA farger

Farger kan være representert ved RGBA-formatet, som er en kombinasjon av Rød, Grønn og Blå. Den representerer En alpha-verdi som er tettheten av farge.

Hver posisjon i tabellen representerer en farge (pixel) kanal verdi.

  • 1. posisjon er den Røde verdi
  • 2. posisjon er den Grønne verdi
  • 3. plass er de Blå verdi
  • 4. posisjon er Alfa verdi
  • 5. plass er neste pixel Røde verdi
  • 6. plass er neste pixel Grønn verdi
  • 7. plass er neste pixel Blå verdi
  • 8. plass er neste pixel Alpha verdi
  • Og så videre…

Hvis du har en 2×2 bildet, så har vi en 16 posisjon array (2×2 punkter x 4 hver verdi).

Det 2×2 bildet zoomet inn på nært hold

Matrisen vil være representert som vist nedenfor:

// RØD GRØNN BLÅ HVIT
[ 255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255, 255, 255, 255, 255]
Endre pixel data

En av de raskeste ting vi kan gjøre, er å angi alle punkter til hvit ved å endre alle RGBA verdier til 255.

// Bruker en knapp for å utløse “effekt”
var knapp = – dokument.bürgerliches(‘Knappen’);

knappen.addEventListener(‘click’, onClick);

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

funksjonen onClick() {
var imageData = kontekst.getImageData(0, 0, lerret.bredde, lerret.høyde);

changeToWhite(imageData.data);

// Oppdatere lerret med nye data
sammenheng.putImageData(imageData, 0, 0);
}

Dataene vil bli sendt som referanse, noe som betyr at eventuelle endringer vi gjør det, vil det endre verdien av argumentet som gikk.

Invertere farger

En fin effekt som ikke krever mye beregningen er å invertere fargene i et bilde.

Å snu en farge verdi kan gjøres ved hjelp av XOR operatøren (^) eller denne formelen 255 – verdi (verdien må være mellom 0-255).

funksjonen invertColors(data) {
for (var i = 0; i < data.lengde; i+= 4) {
data[i] = data[i] ^ 255; // Inverter Rød
data[i+1] = data[i+1] ^ 255; // Inverter Grønn
data[i+2] = data[i+2] ^ 255; // Inverter Blå
}
}

funksjonen onClick() {
var imageData = kontekst.getImageData(0, 0, lerret.bredde, lerret.høyde);

invertColors(imageData.data);

// Oppdatere lerret med nye data
sammenheng.putImageData(imageData, 0, 0);
}

Vi er stigende sløyfen ved 4 i stedet for 1 som vi gjorde før, så vi kan fra pixel for pixel at hver fylling 4 elementene i matrisen.

Alpha-verdien har ikke effekt på invertere farger, så vi hopper over det.

Lysstyrke og kontrast

Justere lysstyrken i et bilde kan gjøres ved hjelp av neste formel: newValue = currentValue + 255 * (lysstyrke / 100).

  • lysstyrke må være mellom -100 og 100
  • currentValue er dagens lys verdi på enten Rød, Grønn eller Blå.
  • newValue er resultatet av den aktuelle fargen lys pluss lysstyrke

Justere kontrasten i et bilde kan gjøres med denne formelen:

faktor = (259 * (kontrast + 255)) / (255 * (259 – kontrast))
color = GetPixelColor(x, y)
newRed = Avkorte(faktor * (Rød(farge) – 128) + 128)
newGreen = Avkorte(faktor * (Grønn(farge) – 128) + 128)
newBlue = Avkorte(faktor * (Blå(farge) – 128) + 128)

Den viktigste beregningen er å få kontrast faktor som brukes for hver farge verdi. Avkorte er en funksjon som gjør at verdien opphold mellom 0 og 255.

La oss skrive disse funksjonene inn i JavaScript:

funksjonen applyBrightness(data, lysstyrke) {
for (var i = 0; i < data.lengde; i+= 4) {
data[i] += 255 * (lysstyrke / 100);
data[i+1] += 255 * (lysstyrke / 100);
data[i+2] += 255 * (lysstyrke / 100);
}
}

funksjonen truncateColor(verdi) {
if (verdi < 0) {
verdi = 0;
} else if (verdi > 255) {
value = 255;
}

return verdi;
}

funksjonen applyContrast(data), contrast (kontrast) {
var faktor = (259.0 * (kontrast + 255.0)) / (255.0 * (259.0 – kontrast));

for (var i = 0; i < data.lengde; i+= 4) {
data[i] = truncateColor(faktor * (data[jeg] – 128.0) + 128.0);
data[i+1] = truncateColor(faktor * (data[jeg+1] – 128.0) + 128.0);
data[i+2] = truncateColor(faktor * (data[jeg+2] – 128.0) + 128.0);
}
}

I dette tilfellet trenger du ikke den truncateColor funksjon som Uint8ClampedArray vil avkorte disse verdiene, men for ordens skyld med å oversette algoritmen vi har lagt det i.

En ting å huske på er at hvis du bruker en lysstyrke eller kontrast, det er ingen vei tilbake til forrige tilstand som bildet data blir overskrevet. Det opprinnelige bildet data må lagres separat for referanse hvis vi vil tilbakestilles til den opprinnelige tilstanden. Holder bildet variabel som er tilgjengelig for andre funksjoner vil være nyttig som du kan bruke bildet i stedet for å tegne lerret med det opprinnelige bildet.

var bilde = – dokument.bürgerliches(‘SourceImage’);

funksjonen redrawImage() {
sammenheng.drawImage(bilde, 0, 0);
}

Ved hjelp av videoer

For å gjøre det arbeidet med videoer, vi kommer til å ta det første bildet skript og HTML-kode og gjøre noen små endringer.

HTML

Endre Bilde-element med en video element ved å erstatte denne linjen:

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

…med dette:

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

Erstatte denne linjen:

var bilde = – dokument.bürgerliches(‘SourceImage’);

…med dette:

var video = – dokument.bürgerliches(‘SourceVideo’);

Å begynne å jobbe med video, vi må vente til video som kan spilles.

videoen.addEventListener(‘canplay’, function () {
// Sett lerret samme bredde og høyde av video
lerret.bredde = video.videoWidth;
lerret.høyde = video.videoHeight;

// Spille video
videoen.spill av();

// begynne å tegne bilder
drawFrame(video);
});

Arrangementet canplay er aktivert når du får nok data er tilgjengelig på at media kan spilles av, i hvert fall for et par av rammer.

Vi kan ikke se noen av videoen vises ikke på lerretet fordi vi bare vise det første bildet. Vi må utføre drawFrame hver n millisekunder for å holde tritt med video rammer pris.

Inne drawFrame vi kaller drawFrame igjen hver 10ms.

funksjonen drawFrame(video) {
sammenheng.drawImage(video, 0, 0);

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

Etter at vi utføre drawFrame, kan vi lage en løkke utfører drawFrame hver 10ms — nok tid til å holde video i sync på lerretet.

Legge til effekt til video

Vi kan bruke samme funksjon vi opprettet før for å invertere farger:

funksjonen invertColors(data) {
for (var i = 0; i < data.lengde; i+= 4) {
data[i] = data[i] ^ 255; // Inverter Rød
data[i+1] = data[i+1] ^ 255; // Inverter Grønn
data[i+2] = data[i+2] ^ 255; // Inverter Blå
}
}

Og legge det inn i drawFrame funksjon:

funksjonen drawFrame(video) {
sammenheng.drawImage(video, 0, 0);

var imageData = kontekst.getImageData(0, 0, lerret.bredde, lerret.høyde);
invertColors(imageData.data);
sammenheng.putImageData(imageData, 0, 0);

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

Vi kan legge til en knapp og veksle effekter:

funksjonen drawFrame(video) {
sammenheng.drawImage(video, 0, 0);

hvis (applyEffect) {
var imageData = kontekst.getImageData(0, 0, lerret.bredde, lerret.høyde);
invertColors(imageData.data);
sammenheng.putImageData(imageData, 0, 0);
}

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

Ved hjelp av kamera

Vi kommer til å holde den samme koden vi bruker til video med bare annerledes er at vi kommer til å endre video stream fra en fil til kameraet strøm ved hjelp av MediaDevices.getUserMedia

MediaDevices.getUserMedia er den nye API deprecating forrige API MediaDevices.getUserMedia(). Det er fortsatt nettleser støtte for den gamle versjonen, og noen nettleser ikke støtter den nye versjonen, og vi må ty til polyfill å sørge for at nettleseren støtter en av dem

Først må du fjerne src attributtet fra video element:

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

<pre rel=”JavaScript”><code class=”språk-javascript”>// Sett kilde av video til kamera stream
funksjonen initCamera(stream) {
videoen.src = vinduet.URL-en.createObjectURL(stream);
}

hvis (navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia({video: true, lyd: false})
.deretter(initCamera)
.catch (- konsollen.- feil)
);
}

Live-Demo

Virkninger

Alt vi har dekket så langt er grunnlaget vi trenger for å lage forskjellige effekter på en video eller bilde. Det finnes en rekke forskjellige effekter som vi kan bruke ved å transformere hver farge uavhengig av hverandre.

Gråtoner

Konvertere farger til gråtoner kan gjøres på ulike måter med ulike formler/teknikker, for å unngå å komme for dypt inn i emnet, vil jeg vise deg fem av formler basert på GIMP avmett verktøy og Luma:

Grå = 0.21 R + 0.72 G + 0.07 B // Glød
Grå = (R + G + B) ÷ 3 // Gjennomsnittlig Lysstyrke
Grå = 0.299 R + 0.587 G + 0.114 B // rec601 standard
Grå = 0.2126 R + 0.7152 G + 0.0722 B // ITU-R-BT.709 standard
Grå = 0.2627 R + 0.6780 G + 0.0593 B // ITU-R-BT.2100 standard

Hva ønsker vi å finne ved hjelp av disse formlene er lysstyrken intensitet nivå av hver pixel farge. Verdien vil variere fra 0 (svart) til 255 (kvit). Disse verdiene vil opprette en gråtone (svart og hvit) effekt.

Dette betyr at den lyseste fargen vil være nærmest til 255, og den mørkeste fargen som er nærmest til 0.

Live-Demo

Duotones

Forskjellen mellom tofargede effekt og gråtoner effekt er to farger som brukes. På gråtoner du har en gradient fra svart til hvitt, mens på tofargede du kan ha en gradient fra en farge til en annen farge, blått til rosa som et eksempel.

Ved hjelp av intensiteten verdien av gråtoner, kan vi erstatte denne fra gradient verdier.

Vi trenger å skape en gradient fra ColorA å ColorB.

funksjonen createGradient(colorA, colorB) {
// Verdier av gradient fra colorA å colorB
var gradient = [];
// maksimal farge verdien 255
var maxValue = 255;
// Konverter hex fargeverdier til RGB-objekt
var fra = getRGBColor(colorA);
var = getRGBColor(colorB);

// Lager 256 farger fra En Farge til Farge B
for (var i = 0; i <= maxValue; i++) {
// IntensityB vil gå fra 0 til 255
// IntensityA vil gå fra 255 0
// IntensityA vil redusere intensiteten mens instensityB vil øke
// Hva dette betyr er at ColorA vil starte solid og langsomt forvandles til ColorB
// Hvis du ser på det på annen måte åpenhet i fargen vil øke, og gjennomsiktigheten av farge B vil redusere
var intensityB = i;
var intensityA = maxValue – intensityB;

// Formelen nedenfor kombinerer to farger basert på deres intensitet
// (IntensityA * ColorA + IntensityB * ColorB) / maxValue
gradient[jeg] = {
r: (intensityA*fra.r + intensityB*hvis du vil.r) / maxValue,
g: (intensityA*fra.g + intensityB*hvis du vil.g) / maxValue,
b: (intensityA*fra.b + intensityB*hvis du vil.b) / maxValue
};
}

tilbake gradient;
}

// Helper funksjonen til å konvertere 6digit heksadesimale verdier til et RGB-farge-objekt
funksjonen getRGBColor(hex)
{
var colorValue;

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

colorValue = parseInt(hex, 16);

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

Kort sagt, vi skaper en rekke farger verdier fra En Farge redusere intensiteten mens du går til Farge B og øke intensiteten.

Fra #0096ff #ff00f0
Zoomet bilde av farge overgang

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

Ovenfor er et eksempel på et fall på 10 farger verdier fra #0096ff #ff00f0.

Gråtoner representasjon av farge overgang

Nå som vi har gråtoner representasjon av bildet, kan vi bruke det til å tilordne det til tofargede gradient verdier.

Det tofargede gradient har 256 farger mens gråtoner har også 256 farger, alt fra svart (0) til hvitt (255). Det betyr at en gråtone farge verdi vil kartet til en gradient element indeks.

var gradientColors = createGradient(‘#0096ff’, ‘#ff00f0’);
var imageData = kontekst.getImageData(0, 0, lerret.bredde, lerret.høyde);
applyGradient(imageData.data);

for (var i = 0; i < data.lengde; i += 4) {
// Få hver kanal fargeverdi
var redValue = data[i];
var greenValue = data[i+1];
var blueValue = data[i+2];

// Kartlegging fargeverdiene til gradient index
// Skifte gråtoner farge verdi med en farge for tofargede gradient
data[i] = gradientColors[redValue].r;
data[i+1] = gradientColors[greenValue].g;
data[i+2] = gradientColors[blueValue].b;
data[i+3] = 255;
}

Live-Demo

Konklusjon

Dette emnet kan gå mer i dybden eller forklare mer virkninger. Lekser for deg er å finne forskjellige algoritmer som du kan bruke på disse skjelett eksempler.

Å vite hvordan de punkter som er strukturert på et lerret vil tillate deg å opprette et ubegrenset antall av effekter som sepia, farge blanding, en grønn skjerm effekt, bildet flimrer/glitching, etc.

Du kan også lage effekter på fly uten å bruke et bilde eller en video:

Jetpack WordPress plugin går på dette nettstedet, slår ikke bare relaterte innlegg nedenfor, men sikkerhet og sikkerhetskopiering, Markdown-støtte, søk nettstedet, kommentar form, sosiale nettverk-tilkoblinger, og mer!