Canvas(HTML5)の画像処理 : JavaScript

Pocket

Canvasの画像はImageDataオブジェクトのdataプロパティはRed, Green, Blue, Alpha(不透明度)の情報をそれぞれ0〜255の値を1次元の配列として持つ。

Canvasの1pixelに対してRGBAの4要素をもつのでcanvasが横width, 縦: heightの画像のdata配列の長さはw × h * 4になる。

3 × 3の画像を考える。

----------------------------
| (0, 0) | (0, 1) | (0, 2) |
----------------------------
| (1, 0) | (1, 1) | (1, 2) |
----------------------------
| (2, 1) | (2, 2) | (2, 2) |
----------------------------

ImageData.dataはCanvasの各(i, j)ピクセルにたいしてRGBAの4つの要素を持つの。

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
R, G, B, A, R, G, B, A, R, G,  B,  A,  R,  G,  B,  A,  R,  G,  B,  A,  R,  G,  B,  A,  R,  G,  B,  A,  R,  G,  B,  A,  R,  G,  B,  A,

添字の計算は下記のようになる。

redIndex   = (j * 4) + (i * ImageData.width * 4);
greenIndex = redIndex + 1;
blueIndex  = redIndex + 2;
alphaIndex = redIndex + 3;

各添字に対応する値を取り出す。

red   = ImageData.data[redIndex];
green = ImageData.data[blueIndex];
blue  = ImageData.data[blueIndex];

取り出したデータに処理を加えてCanvasへ書き出す。

今までの処理を使ってゲレースケールへ画像を変換する。下記コードはredIndexをk, greenIndexをk + 1, blueIndexをk + 2とする[1]


/**
* grayscale
*/
function grayscale(canvas) {
var i, j, k, avg, cxt, canvasImageData;

cxt = canvas.getContext(‘2d’);

// get canvas ImageData
canvasImageData = cxt.getImageData(0, 0, canvas.width, canvas.height);

// transform grayscale
for (i = 0; i < canvasImageData.height; i++) { for (j = 0; j < canvasImageData.width; j++) { k = j * 4 + i * canvasImageData.width * 4; avg = parseInt((canvasImageData.data[k] + canvasImageData.data[k + 1] + canvasImageData.data[k + 2]) / 3, 10); canvasImageData.data[k] = canvasImageData.data[k + 1] = canvasImageData.data[k + 2] = avg; } } // put new ImageData cxt.putImageData(canvasImageData, 0, 0, canvas.height, canvas.width); }); [/javascript]

空間フィルタリング(spatial filtering

画像処理をフィルタリングするとき元の画像の対応する点だけでなくその近傍をもとに画像加工を施す。

フィルタと近傍の積和をもとに新たなデータを作成するフィルタリングを線形フィルタという。

線形フィルタの例 平滑化。


/**
* average
*
* smoothing liner filter
*/
$(‘#average’).click(function () {
var i ,j ,k ,l, m, n, sumRed, sumGreen, sumBlue;

checkCanvas();

// average smoothing
inputImageData = cxt.getImageData(0, 0, canvas.width, canvas.height);
outputImageData = cxt.getImageData(0, 0, canvas.width, canvas.height);
for (i = 1; i < inputImageData.height - 1; i++) { for (j = 1 ; j < inputImageData.width - 1; j++) { sumRed = sumGreen = sumBlue = 0; k = (i * inputImageData.width + j) * 4; for (l = -1; l <= 1; l++) { for (m = -1; m <= 1; m++) { n = k + (l * inputImageData.width + m) * 4; sumRed += inputImageData.data[n]; sumGreen += inputImageData.data[n + 1]; sumBlue += inputImageData.data[n + 2]; } } outputImageData.data[k] = Math.floor(sumRed / 9); // R outputImageData.data[k + 1] = Math.floor(sumGreen / 9); // G outputImageData.data[k + 2] = Math.floor(sumBlue / 9); // B } } cxt.putImageData(outputImageData, 0, 0, canvas.width, canvas.height); }); /** * sharpen * * sharpening filter */ $('#sharpen').click(function () { var i ,j ,k ,l, m, n, filter = []; // sharpening filter filter = [-1, -1, -1, -1, 9, -1, -1, -1, -1]; checkCanvas(); inputImageData = cxt.getImageData(0, 0, canvas.width, canvas.height); outputImageData = cxt.getImageData(0, 0, canvas.width, canvas.height); for (i = 1; i < inputImageData.height - 1; i++) { for (j = 1 ; j < inputImageData.width - 1; j++) { sumRed = sumGreen = sumBlue = 0; k = (i * inputImageData.width + j) * 4; for (l = -1; l <= 1; l++) { for (m = -1; m <= 1; m++) { n = k + (l * inputImageData.width + m) * 4; sumRed += inputImageData.data[n] * filter[(l + 1) * 3 + m + 1]; sumGreen += inputImageData.data[n + 1] * filter[(l + 1) * 3 + m + 1]; sumBlue += inputImageData.data[n + 2] * filter[(l + 1) * 3 + m + 1]; } } outputImageData.data[k] = sumRed; outputImageData.data[k + 1] = sumGreen; outputImageData.data[k + 2] = sumBlue; } } cxt.putImageData(outputImageData, 0, 0, canvas.width, canvas.height); }); [/javascript]
[1] アルファは使わない(初期値1)。

コメント

No comments yet.

コメントの投稿

改行と段落タグは自動で挿入されます。
メールアドレスは表示されません。