Maison > interface Web > tutoriel CSS > le corps du texte

Comment manipuler les pixels à l'aide de Canvas

不言
Libérer: 2018-06-14 16:02:46
original
2122 Les gens l'ont consulté

Cet article présente principalement les informations pertinentes sur la façon d'utiliser Canvas pour faire fonctionner les pixels. Le contenu est assez bon, je vais le partager avec vous maintenant et le donner comme référence.

Les navigateurs modernes prennent en charge la lecture vidéo via l'élément <video>. La plupart des navigateurs peuvent également accéder à la caméra via l'API MediaDevices.getUserMedia(). Mais même si ces deux choses sont combinées, nous ne pouvons pas accéder directement à ces pixels et les manipuler.

Heureusement, les navigateurs disposent d'une API Canvas qui nous permet de dessiner des graphiques en utilisant JavaScript. Nous pouvons en fait dessiner une image <canvas> à partir de la vidéo elle-même, ce qui nous permet de manipuler et d'afficher ces pixels.

Ce que vous apprendrez ici sur la façon de manipuler les pixels vous fournira les bases pour travailler avec des images et des vidéos de tout type ou source, et pas seulement avec du canevas.

Ajouter une image au canevas

Avant de commencer la lecture de la vidéo, voyons comment ajouter une image au canevas.

<img src>
<p>
  <canvas id="Canvas" class="video"></canvas>
</p>
Copier après la connexion

Nous créons un élément d'image pour représenter l'image à dessiner sur la toile. Alternativement, nous pouvons utiliser l'objet Image en JavaScript.

var canvas;
var context;

function init() {
  var image = document.getElementById(&#39;SourceImage&#39;);
  canvas = document.getElementById(&#39;Canvas&#39;);
  context = canvas.getContext(&#39;2d&#39;);

  drawImage(image);
  // Or
  // var image = new Image();
  // image.onload = function () {
  //    drawImage(image);
  // }
  // image.src = &#39;image.jpg&#39;;
}

function drawImage(image) {
  // Set the canvas the same width and height of the image
  canvas.width = image.width;
  canvas.height = image.height;

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

window.addEventListener(&#39;load&#39;, init);
Copier après la connexion

Le code ci-dessus dessine l'image entière sur le canevas.

Affichez l'image Paint sur toile via Welling Guzman (@wellingguzman) sur CodePen

Maintenant, nous pouvons commencer à jouer avec ces pixels

Mettre à jour les données de l'image <🎜. >

Les données d'image sur le canevas nous permettent de manipuler et de modifier les pixels. La propriété

data est un objet ImageData qui a trois propriétés - width, height et data/. basé sur l'image originale. Toutes ces propriétés sont en lecture seule. Ce qui nous intéresse, ce sont les données, un tableau unidimensionnel représenté par un objet Uint8ClampedArray, contenant les données de chaque pixel au format RGBA > Bien que la propriété data soit lue. -seulement, cela ne signifie pas que nous ne pouvons pas changer sa valeur. Cela signifie que nous ne pouvons pas attribuer un autre tableau à cette propriété 🎜>

Vous vous demandez peut-être quelle valeur représente l'objet Uint8ClampedArray ?

Le tableau de type Uint8ClampedArray représente un tableau d'entiers non signés de 8 bits limités à 0-255 ; Si vous spécifiez une valeur en dehors de la plage [0,255], 0 ou 255 sera défini ; un nombre non entier, l'entier le plus proche sera défini ; le contenu sera initialisé à 0. Une fois établi, le tableau peut être référencé en utilisant les méthodes de l'objet, ou en utilisant la syntaxe d'indexation de tableau standard (c'est-à-dire en utilisant la notation entre crochets) <🎜. >

En bref, ce tableau stocke une valeur allant de 0 à 255 à chaque position, ce qui en fait le format RGBA Solution parfaite puisque chaque partie est représentée par 0 à 255 valeurs

// Get the canvas image data
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);

image.data = new Uint8ClampedArray(); // WRONG
image.data[1] = 0; // CORRECT
Copier après la connexion
RGBA Couleur

La couleur peut être représentée au format RGBA qui est rouge, vert et une combinaison de couleurs. Chaque position dans le tableau

représente une valeur de canal de couleur (pixel). est la valeur rouge

La deuxième position est la valeur verte

La troisième position est la valeur bleue

    La quatrième position est la valeur Alpha
  • La 5ème position est la valeur rouge du pixel suivant
  • La 6ème position est le vert valeur du pixel suivant
  • La 7ème position est la valeur bleue du pixel suivant
  • La 8ème position est la prochaine valeur du Pixel Alpha ​​
  • et ainsi de suite...
  • Si vous avez une image 2x2, alors nous avons un tableau de 16 bits (2x2 pixels chacun x 4 valeurs).
  • Image 2x2 réduite

  • Le tableau ressemblera à ceci :
  • Modifier les données de pixels

L'une des choses les plus rapides que nous puissions faire est de définir tous les pixels en blanc en modifiant toutes les valeurs RGBA à 255.

// RED                 GREEN                BLUE                 WHITE
[ 255, 0, 0, 255,      0, 255, 0, 255,      0, 0, 255, 255,      255, 255, 255, 255]
Copier après la connexion
Les données seront transmises comme référence, ce qui signifie que toute modification que nous y apporterons changera la valeur du paramètre transmis.

Inverser les couleurs

Un excellent effet qui ne nécessite pas trop de calculs consiste à inverser les couleurs d'une image.

Les valeurs de couleur peuvent être inversées à l'aide de l'opérateur XOR (^) ou de cette formule 255 - valeur (la valeur doit être comprise entre 0 et 255).

// Use a button to trigger the "effect"
var button = document.getElementById(&#39;Button&#39;);

button.addEventListener(&#39;click&#39;, 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);

  // Update the canvas with the new data
  context.putImageData(imageData, 0, 0);
}
Copier après la connexion

Nous incrémentons la boucle de 4 comme avant au lieu de 1, nous pouvons donc passer de pixel en pixel, en remplissant le tableau avec 4 pour chacun éléments de pixels.

La valeur alpha n'a aucun effet sur l'inversion de la couleur, nous la sautons donc.

Luminosité et contraste

Utilisez la formule suivante pour régler la luminosité d'une image : newValue = currentValue + 255 * (luminosité / 100).

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; // Invert Green
    data[i+2] = data[i+2] ^ 255; // Invert Blue
  }
}

function onClick() {
  var imageData = context.getImageData(0, 0, canvas.width, canvas.height);

  invertColors(imageData.data);

  // Update the canvas with the new data
  context.putImageData(imageData, 0, 0);
}
Copier après la connexion

La luminosité doit être comprise entre -100 et 100

currentValue est la valeur d'éclairage actuelle pour le rouge, le vert ou le bleu.

newValue est le résultat de l'ajout de luminosité à la lumière de couleur actuelle

    Le réglage du contraste de l'image peut être effectué à l'aide de cette formule :
  1. Le calcul principal consiste à obtenir le facteur de contraste qui sera appliqué à chaque valeur de couleur. La troncature est une fonction qui garantit que la valeur reste comprise entre 0 et 255.

    Écrivons ces fonctions en JavaScript :
  2. function applyBrightness(data, brightness) {
      for (var i = 0; i < data.length; i+= 4) {
        data[i] += 255 * (brightness / 100);
        data[i+1] += 255 * (brightness / 100);
        data[i+2] += 255 * (brightness / 100);
      }
    }
    
    function truncateColor(value) {
      if (value < 0) {
        value = 0;
      } else if (value > 255) {
        value = 255;
      }
    
      return value;
    }
    
    function applyContrast(data, contrast) {
      var factor = (259.0 * (contrast + 255.0)) / (255.0 * (259.0 - contrast));
    
      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(factor * (data[i+2] - 128.0) + 128.0);
      }
    }
    Copier après la connexion

    在这种情况下,您不需要truncateColor函数,因为Uint8ClampedArray会截断这些值,但为了翻译我们在其中添加的算法。

    需要记住的一点是,如果应用亮度或对比度,则图像数据被覆盖后无法回到之前的状态。如果我们想要重置为原始状态,则原始图像数据必须单独存储以供参考。保持图像变量对其他函数可访问将会很有帮助,因为您可以使用该图像来重绘画布和原始图像。

    var image = document.getElementById(&#39;SourceImage&#39;);
    
    function redrawImage() {
      context.drawImage(image, 0, 0);
    }
    Copier après la connexion

    使用视频

    为了使它适用于视频,我们将采用我们的初始图像脚本和HTML代码并做一些小的修改。

    HTML

    通过替换以下行来更改视频元素的Image元素:

     <img src>
    Copier après la connexion

    ...with this:

    <video src></video>
    Copier après la connexion

    JavaScript

    替换这一行:

    var image = document.getElementById(&#39;SourceImage&#39;);
    Copier après la connexion

    ...添加这行:

    var video = document.getElementById(&#39;SourceVideo&#39;);
    Copier après la connexion

    要开始处理视频,我们必须等到视频可以播放。

    video.addEventListener(&#39;canplay&#39;, function () {
        // Set the canvas the same width and height of the video
        canvas.width = video.videoWidth;
        canvas.height = video.videoHeight;    
    
        // Play the video
        video.play();
    
        // start drawing the frames  
        drawFrame(video);
    });
    Copier après la connexion

    当有足够的数据可以播放媒体时,至少在几帧内播放事件播放。

    我们无法看到画布上显示的任何视频,因为我们只显示第一帧。我们必须每n毫秒执行一次drawFrame以跟上视频帧速率。

    在drawFrame内部,我们每10ms再次调用drawFrame。

    function drawFrame(video) {
      context.drawImage(video, 0, 0);
    
      setTimeout(function () {
        drawFrame(video);
      }, 10);
    }
    Copier après la connexion

    在执行drawFrame之后,我们创建一个循环,每10ms执行一次drawFrame - 足够的时间让视频在画布中保持同步。

    将效果添加到视频

    我们可以使用我们之前创建的相同函数来反转颜色:

    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; // Invert Green
        data[i+2] = data[i+2] ^ 255; // Invert Blue
      }
    }
    Copier après la connexion

    并将其添加到drawFrame函数中:

    function drawFrame(video) {
      context.drawImage(video, 0, 0);
    
      var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
      invertColors(imageData.data);
      context.putImageData(imageData, 0, 0);
    
      setTimeout(function () {
        drawFrame(video);
      }, 10);
    }
    Copier après la connexion

    我们可以添加一个按钮并切换效果:

    function drawFrame(video) {
      context.drawImage(video, 0, 0);
    
      if (applyEffect) {
        var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
        invertColors(imageData.data);
        context.putImageData(imageData, 0, 0);
      }
    
      setTimeout(function () {
        drawFrame(video);
      }, 10);
    }
    Copier après la connexion

    使用 camera

    我们将保留我们用于视频的相同代码,唯一不同的是我们将使用MediaDevices.getUserMedia将视频流从文件更改为相机流。

    MediaDevices.getUserMedia是弃用先前API MediaDevices.getUserMedia()的新API。浏览器仍旧支持旧版本,并且某些浏览器不支持新版本,我们必须求助于polyfill以确保浏览器支持其中一种。

    首先,从视频元素中删除src属性:

    <video><code>
    <code>// Set the source of the video to the camera stream
    function initCamera(stream) {
        video.src = window.URL.createObjectURL(stream);
    }
    
    if (navigator.mediaDevices.getUserMedia) {
      navigator.mediaDevices.getUserMedia({video: true, audio: false})
        .then(initCamera)
        .catch(console.error)
      );
    }
    Copier après la connexion

    Live Demo

    效果

    到目前为止,我们所介绍的所有内容都是我们需要的基础,以便为视频或图像创建不同的效果。我们可以通过独立转换每种颜色来使用很多不同的效果。

    灰阶

    将颜色转换为灰度可以使用不同的公式/技巧以不同的方式完成,以避免陷入太深的问题我将向您展示基于GIMP desaturate tool去饱和工具和Luma的五个公式:

    Gray = 0.21R + 0.72G + 0.07B // Luminosity
    Gray = (R + G + B) ÷ 3 // Average Brightness
    Gray = 0.299R + 0.587G + 0.114B // rec601 standard
    Gray = 0.2126R + 0.7152G + 0.0722B // ITU-R BT.709 standard
    Gray = 0.2627R + 0.6780G + 0.0593B // ITU-R BT.2100 standard
    Copier après la connexion

    我们想要使用这些公式找到的是每个像素颜色的亮度等级。该值的范围从0(黑色)到255(白色)。这些值将创建灰度(黑白)效果。

    这意味着最亮的颜色将最接近255,最暗的颜色最接近0。

    Live Demo

    双色调

    双色调效果和灰度效果的区别在于使用了两种颜色。在灰度上,您有一个从黑色到白色的渐变色,而在双色调中,您可以从任何颜色到任何其他颜色(从蓝色到粉红色)都有一个渐变。

    使用灰度的强度值,我们可以将其替换为梯度值。

    我们需要创建一个从ColorA到ColorB的渐变。

    function createGradient(colorA, colorB) {   
      // Values of the gradient from colorA to colorB
      var gradient = [];
      // the maximum color value is 255
      var maxValue = 255;
      // Convert the hex color values to RGB object
      var from = getRGBColor(colorA);
      var to = getRGBColor(colorB);
    
      // Creates 256 colors from Color A to Color B
      for (var i = 0; i <= maxValue; i++) {
        // IntensityB will go from 0 to 255
        // IntensityA will go from 255 to 0
        // IntensityA will decrease intensity while instensityB will increase
        // What this means is that ColorA will start solid and slowly transform into ColorB
        // If you look at it in other way the transparency of color A will increase and the transparency of color B will decrease
        var intensityB = i;
        var intensityA = maxValue - intensityB;
    
        // The formula below combines the two color based on their intensity
        // (IntensityA * ColorA + IntensityB * ColorB) / maxValue
        gradient[i] = {
          r: (intensityA*from.r + intensityB*to.r) / maxValue,
          g: (intensityA*from.g + intensityB*to.g) / maxValue,
          b: (intensityA*from.b + intensityB*to.b) / maxValue
        };
      }
    
      return gradient;
    }
    
    // Helper function to convert 6digit hex values to a RGB color object
    function getRGBColor(hex)
    {
      var colorValue;
    
      if (hex[0] === &#39;#&#39;) {
        hex = hex.substr(1);
      }
    
      colorValue = parseInt(hex, 16);
    
      return {
        r: colorValue >> 16,
        g: (colorValue >> 8) & 255,
        b: colorValue & 255
      }
    }
    Copier après la connexion

    简而言之,我们从颜色A创建一组颜色值,降低强度,同时转到颜色B并增加强度。

    从 #0096ff 到 #ff00f0

    var gradients = [
      {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},
    ];
    Copier après la connexion

    缩放颜色过渡的表示

    上面有一个从#0096ff到#ff00f0的10个颜色值的渐变示例。

    颜色过渡的灰度表示

    现在我们已经有了图像的灰度表示,我们可以使用它将其映射到双色调渐变值。

    The duotone gradient has 256 colors while the grayscale has also 256 colors ranging from black (0) to white (255). That means a grayscale color value will map to a gradient element index.

    var gradientColors = createGradient(&#39;#0096ff&#39;, &#39;#ff00f0&#39;);
    var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
    applyGradient(imageData.data);
    
    for (var i = 0; i < data.length; i += 4) {
      // Get the each channel color value
      var redValue = data[i];
      var greenValue = data[i+1];
      var blueValue = data[i+2];
    
      // Mapping the color values to the gradient index
      // Replacing the grayscale color value with a color for the duotone gradient
      data[i] = gradientColors[redValue].r;
      data[i+1] = gradientColors[greenValue].g;
      data[i+2] = gradientColors[blueValue].b;
      data[i+3] = 255;
    }
    Copier après la connexion

    Live Demo

    结论

    这个主题可以更深入或解释更多的影响。为你做的功课是找到可以应用于这些骨架示例的不同算法。

    了解像素在画布上的结构将允许您创建无限数量的效果,如棕褐色,混色,绿色屏幕效果,图像闪烁/毛刺等。

    您甚至可以在不使用图像或视频的情况下即时创建效果

    Ce qui précède représente l'intégralité du contenu de cet article. J'espère qu'il sera utile à l'étude de chacun. Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois !

    Recommandations associées :

    À propos des propriétés des lignes de canevas

    Comment utiliser le canevas pour implémenter une mosaïque d'images

    Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal