画像処理におけるキャンバスの活用
先日、前任者が面白いプロジェクトを薦めてくれました。TensorFlow.jsとcanvasの画像処理を使って、動画中の人の消失を実現する「Real-Time-Person-Removal」です。この機会に、canvasでの画像処理の基本をおさらいしておこうと思います。
基本API
canvasの画像処理機能は、ImageDataオブジェクトを通してピクセルデータを扱います。主なAPIは以下の通りです。
- createImageData():空のImageDataオブジェクトを作成します。
- getImageData():キャンバスのピクセルデータを取得します。
- putImageData() : ピクセルデータをキャンバスに書き込む
imageData = {
width: Number,
height: Number,
data: Uint8ClampedArray
}
width はキャンバスの幅、つまり x 軸のピクセル数、height はキャンバスの高さ、つまり y 軸のピクセル数、data はキャンバスのピクセルデータの配列で、全長 w * h * 4、4 つの値(rgba)はそれぞれ 1 ピクセルに対応します。
画像の処理
canvasの基本的な画像処理機能を、いくつかの例で見てみましょう。
オリジナル画像効果。
const cvs = document.getElementById("canvas");
const ctx = cvs.getContext("2d");
const img = new Image();
img.src = "Image URL";
img.onload = function () {
ctx.drawImage(img, 0, 0, w, h);
}
ネガティブ/マイナス効果
アルゴリズム:255とピクセルポイントのrgbの差を現在値として取る。
function negative(x) {
let y = 255 - x;
return y;
}
レンダリング
const imageData = ctx.getImageData(0, 0, w, h);
const { data } = imageData;
let l = data.length;
for(let i = 0; i < l; i+=4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
data[i] = negative(r);
data[i + 1] = negative(g);
data[i + 2] = negative(b);
}
ctx.putImageData(imageData, 0, 0);
モノクローム効果
モノクローム効果とは、現在のピクセルのrgbの3つの値のうち1つを保持し、他の色の値を削除するものです。
for(let i = 0; i < l; i+=4) { // remove the values of r, g
data[i] = 0;
data[i + 1] = 0;
}
レンダリング
グレースケール画像
グレースケール。1画素に1つの色値しかない画像。0から255の色値で、黒から白まである。
for(let i = 0; i < l; i+=4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
const gray = grayFn(r, g, b);
data[i] = gray;
data[i + 1] = gray;
data[i + 2] = gray;
}
アルゴリズム1 - 平均化。
const gray = (r + g + b) / 3;
レンダリング
アルゴリズム2 - 人間の目の知覚:人間の目が赤、緑、青をどの程度知覚しているかに基づいて、緑 > 赤 > 青に、重み付けをした分割を行う。
const gray = r * 0.3 + g * 0.59 + b * 0.11
レンダリング
これ以外にも、あります。
最大値または最小値をとります。
const grayMax = Math.max(r, g, b); // large value, brighter
const grayMin = Math.min(r, g, b); // small value, darker
1つのチャンネル、すなわちrgbの3つの値のうちの1つを取る。
バイナリグラフ
アルゴリズム 色値を決定し、現在のrgb値と比較し、これより大きい値には黒を、そうでなければ白を表示します。
for(let i = 0; i < l; i+=4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
const gray = gray1(r, g, b);
const binary = gray > 126 ? 255 : 0;
data[i] = binary;
data[i + 1] = binary;
data[i + 2] = binary;
}
レンダリング
ガウシアンブラー
ガウスぼかしは、ぼかしアルゴリズムの1つで、各画素値は、周囲の隣接する画素値の加重平均となります。元のピクセル値は最大のガウス分布を持ち(最大の重みを持ち)、隣接するピクセルは元のピクセルから離れるにつれて小さな重みを持つようになります。
一次方程式です。
(一次式の方がアルゴリズムが単純なので一次式が使われる)
const radius = 5; // fuzzy radius
const weightMatrix = generateWeightMatrix(radius); // weight matrix
for(let y = 0; y < h; y++) {
for(let x = 0; x < w; x++) {
let [r, g, b] = [0, 0, 0];
let sum = 0;
let k = (y * w + x) * 4;
for(let i = -radius; i <= radius; i++) {
let x1 = x + i;
if(x1 >= 0 && x1 < w) {
let j = (y * w + x1) * 4;
r += data[j] * weightMatrix[i + radius];
g += data[j + 1] * weightMatrix[i + radius];
b += data[j + 2] * weightMatrix[i + radius];
sum += weightMatrix[i + radius];
}
}
data[k] = r / sum;
data[k + 1] = g / sum;
data[k + 2] = b / sum;
}
}
for(let x = 0; x < w; x++) {
for(let y = 0; y < h; y++) {
let [r, g, b] = [0, 0, 0];
let sum = 0;
let k = (y * w + x) * 4;
for(let i = -radius; i <= radius; i++) {
let y1 = y + i;
if(y1 >= 0 && y1 < h) {
let j = (y1 * w + x) * 4;
r += data[j] * weightMatrix[i + radius];
g += data[j + 1] * weightMatrix[i + radius];
b += data[j + 2] * weightMatrix[i + radius];
sum += weightMatrix[i + radius];
}
}
data[k] = r / sum;
data[k + 1] = g / sum;
data[k + 2] = b / sum;
}
}
function generateWeightMatrix(radius = 1, sigma) { // sigma standard deviation of normal distribution
const a = 1 / (Math.sqrt(2 * Math.PI) * sigma);
const b = - 1 / (2 * Math.pow(sigma, 2));
let weight, weightSum = 0, weightMatrix = [];
for (let i = -radius; i <= radius; i++){
weight = a * Math.exp(b * Math.pow(i, 2));
weightMatrix.push(weight);
weightSum += weight;
}
return weightMatrix.map(item => item / weightSum); // normalize
}
レンダリング
その他の効果
ここでは、その他の画像効果処理について簡単に説明します。例は単純で繰り返しが多いため、コードと効果画像は改めて示さない。
- 明るさ調整:rgbの値を取り、それぞれに所定の値を加算する。
- 透明度の処理:rgba 値の a 値を変更します。
- コントラスト強調:rgb値をそれぞれ2倍し、所定の値を差し引く。
概要
さて、以上が基本的な画像処理アルゴリズムです。
参考文献
キャンバス画像処理の活用についての記事は以上です。キャンバス画像処理の詳細については、スクリプトハウスの過去記事を検索していただくか、引き続き以下の記事をご覧ください。
関連
-
HTML5 ドラッグ&ドロップによるファイルアップロードのサンプルコード
-
HTML5ページの要素と属性の分析
-
顔決済機能の実装をベースにしたHTML5+tracking.js
-
html5 色彩公差キーイング with canvas
-
H5では、ダイナミックなグラフィックス機能を実現するために、キャンバスの最も強力なインタフェース
-
Html5ベースの音声検索機能
-
ダブルキャッシュを使用したCanvas clearRectによるスプラッシュスクリーンの問題を解決しました。
-
左右の高さの差がありすぎる問題を解決するための小型プログラムウォーターフォール
-
キャンバスでの複数描画の順番の説明
-
Canvasユーティリティライブラリ Fabric.jsマニュアル
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン