検索
ホームページウェブフロントエンドCSSチュートリアルCanvas を使用してピクセルを操作する方法

Canvas を使用してピクセルを操作する方法

Jun 14, 2018 pm 04:02 PM
canvasピクセル

この記事は主に Canvas を使用してピクセルを操作する方法に関する関連情報を紹介します。内容は非常に優れているので、参考として共有します。

最新のブラウザは、<video></video> 要素によるビデオ再生をサポートしています。ほとんどのブラウザは、MediaDevices.getUserMedia() API を通じてカメラにアクセスすることもできます。しかし、これら 2 つを組み合わせたとしても、これらのピクセルに直接アクセスして操作することはできません。 <video></video>元素播放视频。大多数浏览器也可以通过 MediaDevices.getUserMedia()API访问摄像头。但即使这两件事结合起来,我们也无法直接访问和操纵这些像素。

幸运的是,浏览器有一个Canvas API,允许我们使用JavaScript绘制图形。实际上,我们可以从视频本身将图像绘制到 <canvas></canvas>

幸いなことに、ブラウザには、JavaScript を使用してグラフィックを描画できる Canvas API があります。実際にビデオ自体から <canvas></canvas> に画像を描画することで、それらのピクセルを操作して表示することができます。

ピクセルの操作方法についてここで学んだことは、キャンバスだけでなく、あらゆる種類やソースの画像やビデオを操作するための基礎を提供します。

キャンバスに画像を追加する

ビデオを始める前に、キャンバスに画像を追加する方法を見てみましょう。

<img  src alt="Canvas を使用してピクセルを操作する方法" >
<p>
  <canvas id="Canvas" class="video"></canvas>
</p>

キャンバスに描画する画像を表す画像要素を作成します。あるいは、JavaScript で Image オブジェクトを使用することもできます。

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);

上記のコードは、画像全体をキャンバスに描画します。

CodePen の Welling Guzman (@wellingguzman) 経由でキャンバス画像にペイントを表示します。

画像データを更新します

キャンバス上の画像データを使用して、ピクセルを操作したり変更したりできます。

data プロパティは、width、height、data の 3 つのプロパティを持つ ImageData オブジェクトであり、これらはすべて元の画像に基づいて表され、Uint8ClampedArray で表される 1 次元配列になります。 RGBA 形式の各ピクセルのデータを含むオブジェクト。

data プロパティは読み取り専用ですが、その値を変更できないというわけではありません。このプロパティには別の配列が割り当てられているということです。

// 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

Uint8ClampedArray オブジェクトはどのような値を表すのかと疑問に思われるかもしれません。これは MDN からの説明です:

Uint8ClampedArray 型の配列は、0 にクランプされた 8 ビットの符号なし整数の配列を表します。 255; [0,255] の範囲外の値を指定した場合は 0 または 255 が設定され、整数以外を指定した場合は最も近い整数が設定されます。確立されたオブジェクトは参照に使用できます。配列内の要素、または標準の配列インデックス構文を使用します (つまり括弧表記を使用します)

つまり、この配列は各位置に 0 から 255 の範囲の値を格納します。これにより、RGBA 形式になります。各部分は次のように表されるため、完璧なソリューションになります。 0 から 255 までの値。

RGBA カラー

色は、赤、緑、青の組み合わせである RGBA 形式で表現できます。

配列内の各位置は、色の不透明度を表します。カラー (ピクセル) チャネル値

  • 最初の位置は赤色の値

  • 2 番目の位置は緑色の値

  • 青色の値

  • 4 番目の位置はアルファ値

  • 5 番目の位置は次のピクセルの赤の値です

  • 6 番目の位置は次のピクセルの緑の値です

  • 7 番目の位置は次のピクセルの青の値です

  • 8 番目の位置は次のピクセルですアルファ値

  • など...

2x2 画像がある場合、16 ビット配列 (2x2 ピクセル x 4 つの値がそれぞれ) になります

2x2 画像は縮小されます

。配列は次のようになります:

// RED                 GREEN                BLUE                 WHITE
[ 255, 0, 0, 255,      0, 255, 0, 255,      0, 0, 255, 255,      255, 255, 255, 255]

ピクセルデータの変更

最も簡単な方法の1つは、すべてのRGBA値を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);
}

データは参照として渡されます。つまり、データに変更を加えると、渡されたパラメータの値が変更されます。

色の反転

多くの計算を必要としない素晴らしい効果です。画像の色を反転します。

色の値は、XOR 演算子 (^) またはこの式 255 - 値 (値は 0 ~ 255 の間である必要があります) を使用して反転できます。

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);
}

以前のようにループを 1 ではなく 4 ずつインクリメントするので、配列内の 4 つの要素をピクセルからピクセルまで、各ピクセルに埋めることができます。

アルファ値は色の反転には影響しないため、スキップします。

明るさとコントラスト

次の式を使用して画像の明るさを調整します: newValue = currentValue + 255 * (明るさ / 100)。

明るさは-100から100の間でなければなりません
  1. currentValueは、赤、緑、または青の現在の照明値です。
  2. newValue は、現在のカラー ライトに明るさを加えた結果です
  3. 画像のコントラストの調整は、次の式で行うことができます:
  4. factor = (259 * (contrast + 255)) / (255 * (259 - contrast))
    color = GetPixelColor(x, y)
    newRed   = Truncate(factor * (Red(color)   - 128) + 128)
    newGreen = Truncate(factor * (Green(color) - 128) + 128)
    newBlue  = Truncate(factor * (Blue(color)  - 128) + 128)

主な計算は、次のコントラストを取得することです。各カラー値要素に適用されます。切り捨ては、値が 0 ~ 255 の間にあることを保証する関数です。

これらの関数を JavaScript に記述してみましょう:

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);
  }
}

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

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

var image = document.getElementById(&#39;SourceImage&#39;);

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

使用视频

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

HTML

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

 <img  src alt="Canvas を使用してピクセルを操作する方法" >

...with this:

<video src></video>

JavaScript

替换这一行:

var image = document.getElementById(&#39;SourceImage&#39;);

...添加这行:

var video = document.getElementById(&#39;SourceVideo&#39;);

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

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);
});

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

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

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

function drawFrame(video) {
  context.drawImage(video, 0, 0);

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

在执行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
  }
}

并将其添加到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);
}

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

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);
}

使用 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)
  );
}</code>

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

我们想要使用这些公式找到的是每个像素颜色的亮度等级。该值的范围从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
  }
}

简而言之,我们从颜色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},
];

缩放颜色过渡的表示

上面有一个从#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;
}

Live Demo

结论

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

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

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

以上がこの記事の全内容です。その他の関連コンテンツについては、PHP 中国語 Web サイトをご覧ください。

関連する推奨事項:

キャンバスの線のプロパティについて

キャンバスを使用して画像モザイクを実現する方法

以上がCanvas を使用してピクセルを操作する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
Google Fontsをタグ付けし、Goofonts.comを作成する方法Google Fontsをタグ付けし、Goofonts.comを作成する方法Apr 12, 2025 pm 12:02 PM

Goofontsは、開発者妻とデザイナーの夫によって署名されたサイドプロジェクトであり、どちらもタイポグラフィの大ファンです。 Googleにタグを付けています

時代を超越したWeb開発記事時代を超越したWeb開発記事Apr 12, 2025 am 11:44 AM

Pavithra Kodmadは、彼らが変化したWeb開発に関する最も時代を超越した記事のいくつかであると考えていることについて、人々に推奨事項を求めました

セクション要素との取引セクション要素との取引Apr 12, 2025 am 11:39 AM

2つの記事がまったく同じ日に公開されました。

graphQlの練習JavaScript APIでクエリをクエリしますgraphQlの練習JavaScript APIでクエリをクエリしますApr 12, 2025 am 11:33 AM

GraphQL APIの構築方法を学ぶことは非常に挑戦的です。ただし、10分でGraphQL APIを使用する方法を学ぶことができます!そして、それは私が完璧になったことがあります

コンポーネントレベルのCMSコンポーネントレベルのCMSApr 12, 2025 am 11:09 AM

コンポーネントがデータが近くに住んでいる環境に住んでいる場合、視覚コンポーネントと

円にタイプを設定します...オフセットパス付き円にタイプを設定します...オフセットパス付きApr 12, 2025 am 11:00 AM

ここでは、Yuanchuanからの合法的なCSSの策略があります。このCSSプロパティオフセットパスがあります。むかしむかし、それはモーションパスと呼ばれ、その後改名されました。私

CSSで「戻る」ことは何をしますか?CSSで「戻る」ことは何をしますか?Apr 12, 2025 am 10:59 AM

Miriam Suzanneは、このテーマに関するMozilla開発者のビデオで説明しています。

現代の恋人現代の恋人Apr 12, 2025 am 10:58 AM

私はこのようなものが大好きです。

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

mPDF

mPDF

mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。

SecLists

SecLists

SecLists は、セキュリティ テスターの究極の相棒です。これは、セキュリティ評価中に頻繁に使用されるさまざまな種類のリストを 1 か所にまとめたものです。 SecLists は、セキュリティ テスターが必要とする可能性のあるすべてのリストを便利に提供することで、セキュリティ テストをより効率的かつ生産的にするのに役立ちます。リストの種類には、ユーザー名、パスワード、URL、ファジング ペイロード、機密データ パターン、Web シェルなどが含まれます。テスターはこのリポジトリを新しいテスト マシンにプルするだけで、必要なあらゆる種類のリストにアクセスできるようになります。

EditPlus 中国語クラック版

EditPlus 中国語クラック版

サイズが小さく、構文の強調表示、コード プロンプト機能はサポートされていません

SublimeText3 Linux 新バージョン

SublimeText3 Linux 新バージョン

SublimeText3 Linux 最新バージョン

Dreamweaver Mac版

Dreamweaver Mac版

ビジュアル Web 開発ツール