このページの本文へ

PROGRAMMING 古籏一浩のJavaScriptラボ ― 第43回

Web WorkersでPhotoshop風ヒストグラムを作ろう

2010年10月19日 11時00分更新

古籏一浩

  • この記事をはてなブックマークに追加
本文印刷
js-labo

 JavaScriptで並列処理を実現する「Web Workers」。前回はWeb Workersの基本的な使い方を学び、バイナリデータを解析するプログラムを作りました。今回は、Photoshopのヒストグラムのように、RGB(赤、緑、青)の輝度のピクセル数を調べる画像解析プログラムを作りましょう。画像処理は時間がかかることが多いので、Web Workersを利用するにはもってこいです。

【図16】fig16.png
Photoshopのヒストグラム。今回は同様のサンプルを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ピクセル数がいくつあるかを調べ、ページ内に表示します。

【図17】fig17.png
ボタンをクリックすると輝度が20のピクセルがいくつあるかを表示する

サンプル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.onload = function(){
                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
    });
}


書影

「まとめてじっくり読みたい!」という読者のみなさまの声にお応えし、この連載が本になりました。書籍化にあたって加筆修正し、記事公開後の最新情報やコラムも盛り込んでいます。

HTML5+JavaScript アイデア&実践サンプル

本体 2,800+税、B5変形判312ページ(オール4色刷)
ISBN:978-4-04-870448-9

Amazon.co.jpで買う 楽天ブックスで買う

この記事の編集者は以下の記事をオススメしています

ASCII.jp会員サービス 週刊Web Professional登録

HTMLリファレンス誘導バナー

CSSリファレンスサイト誘導バナー

Webディレクター江口明日香が行く

ランキング