JavaScriptで並列処理を実現する「Web Workers」。前回はWeb Workersの基本的な使い方を学び、バイナリデータを解析するプログラムを作りました。今回は、Photoshopのヒストグラムのように、RGB(赤、緑、青)の輝度のピクセル数を調べる画像解析プログラムを作りましょう。画像処理は時間がかかることが多いので、Web Workersを利用するにはもってこいです。
まず、調べたい輝度と画像データを以下のようにワーカーに渡します。
myWorker.postMessage({
width: canvasW, //キャンバスの横幅
height: canvasH, //キャンバスの縦幅
brightness: brightness, // 調べたい輝度
imageData: context.getImageData(0, 0, canvasW, canvasH).data // ピクセルデータ配列
});
ワーカー内では呼び出し元から指定されたRGBの輝度が、配列内にいくつあるかを調べます。配列にはCanvasに描画された画像のピクセルデータがRGBα形式で格納されています。R/G/B/αそれぞれの値が配列の1要素になるので、1ピクセルあたり4要素(4バイト)使うことになります。ピクセルデータは画面の左上から右下に向かって順番に格納されています。
ピクセルデータを1ピクセル分ずつ読み出して、指定された輝度と一致するかどうか、for()を使って縦横のサイズ分だけ繰り返し調べていきます。
for(var y=0; y<event.data.height; y++){
for(var x=0; x<event.data.width; x++){
var ptr = (y * event.data.width + x) * 4;
var R = pixels[ptr + 0];
var G = pixels[ptr + 1];
var B = pixels[ptr + 2];
if (R == c) { countR++; }
if (G == c) { countG++; }
if (B == c) { countB++; }
}
}
調べた結果は以下のようにして呼び出し元に送信します。redに赤の輝度のピクセル数、greenに緑の輝度のピクセル数、blueに青の輝度のピクセル数が入ります。
postMessage({
red : countR,
green : countG,
blue : countB
});
呼び出し元ではワーカーから返された結果を以下のようにしてページ上に表示します。
myWorker.addEventListener("message", function(event){
var txt = brightness+" = RGB["+event.data.red+","+event.data.green+","+event.data.blue+"]";
document.getElementById("result").innerHTML = txt+"<br>";
}, true);
サンプル6は指定された輝度のRGBピクセル数がいくつあるかを調べ、ページ内に表示します。
●サンプル6[HTML]
<!DOCTYPE html>
<head>
<meta charset="utf-8" />
<title>画像分析(1)</title>
</head>
<body>
<h1>画像分析(1)</h1>
<canvas id="myCanvas" style="width:300px;height:150px;border:1px solid black">
Canvasに対応したブラウザーでご覧下さい。
</canvas>
<form action="./image.cgi" method="get">
<input type="button" id="checkStart" value="分析開始" />
</form>
<div id="result"></div>
<script>
document.getElementById("checkStart").addEventListener("click", function(){
document.getElementById("result").innerHTML = "分析中...";
var brightness = 20; // チェックする輝度
// ワーカー設定
var myWorker = new Worker("analysis.js");
myWorker.addEventListener("message", function(event){
var txt = brightness+" = RGB["+event.data.red+","+event.data.green+","+event.data.blue+"]";
document.getElementById("result").innerHTML = txt+"<br>";
}, true);
myWorker.postMessage({
width: canvasW,
height: canvasH,
brightness: brightness,
imageData: context.getImageData(0, 0, canvasW, canvasH).data
});
}, true);
var canvasObj = document.getElementById("myCanvas");
var context = canvasObj.getContext("2d");
var canvasW = canvasObj.width;
var canvasH = canvasObj.height;
var imgObj = new Image();
imgObj.src = "sunflower.jpg";
imgObj.
context.drawImage(imgObj,0,0,300,150);
}
</script>
</body>
</html>
●サンプル6[analysis.js]
addEventListener("message", analysis, false);
function analysis(event){
var countR = countG = countB = 0;
var pixels = event.data.imageData;
var c = event.data.brightness; // 輝度
for(var y=0; y<event.data.height; y++){
for(var x=0; x<event.data.width; x++){
var ptr = (y * event.data.width + x) * 4;
var R = pixels[ptr + 0];
var G = pixels[ptr + 1];
var B = pixels[ptr + 2];
if (R == c) { countR++; }
if (G == c) { countG++; }
if (B == c) { countB++; }
}
}
postMessage({
red : countR,
green : countG,
blue : countB
});
}