このページの本文へ

Web WorkersでPhotoshop風ヒストグラムを作ろう (2/4)

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

文●古籏一浩

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

ヒストグラムを表示する

 サンプル6は指定された輝度のピクセル数を返すだけなので、Photoshopのようなヒストグラムの表示にはなりません。ヒストグラムを表示するには0~255までの合計256段階の輝度を調べる必要があります。サンプル6程度の処理であればワーカーを利用するメリットはほとんどありませんが、すべての輝度を調べてヒストグラムを表示する処理となるとワーカーを使うメリットが出てきます。

 ヒストグラムは、あらかじめ赤、緑、青のGIF画像(1×1ピクセル)をそれぞれ用意しておき、img要素のheight属性にワーカーから得られた結果(輝度のピクセル数)を指定して表示します。ただし、得られた値をそのまま指定すると非常に縦長のヒストグラムになってしまう場合があるので、以下のように適度に除算し調整します。


red += '<img src="r.gif" width="1" height="'+(event.data.red/8)+'">';
green += '<img src="g.gif" width="1" height="'+(event.data.green/8)+'">';
blue += '<img src="b.gif" width="1" height="'+(event.data.blue/8)+'">';


 実際のプログラムはサンプル7です。

【図18】fig18.png

キャンバスに表示されている画像のヒストグラムが表示される。Opera 10.6で実行した画面。ワーカー処理はOperaでは高速かつ安定して動作する

サンプル7(ワーカー内のスクリプトはサンプル6と同じ)


<!DOCTYPE html>
    <head>
        <meta charset="utf-8" />
        <title>画像分析(2)</title>
    </head>
    <body>
        <h1>画像分析(2)</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 = "分析中...<br>";
                var brightness = 0;   // チェックする輝度
                var red = "";
                var green = "";
                var blue = "";
                // ワーカー設定
                var myWorker = new Worker("analysis.js");
                myWorker.addEventListener("message", function(event){
                    red += '<img src="r.gif" width="1" height="'+(event.data.red/8)+'">';
                    green += '<img src="g.gif" width="1" height="'+(event.data.green/8)+'">';
                    blue += '<img src="b.gif" width="1" height="'+(event.data.blue/8)+'">';
                    document.getElementById("result").innerHTML = red+green+blue;
                    brightness++;
                    if (brightness < 256) {
                        startWorker();
                    }
                }, true);
                var img = context.getImageData(0, 0, canvasW, canvasH).data;
                function startWorker(){
                    myWorker.postMessage({
                        width: canvasW,   
                        height: canvasH,
                        brightness: brightness,
                        imageData: img
                    });
                }
                startWorker();
            }, 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>


 サンプル7はSafari 5で実行すると途中で処理が止まってしまい、Safari本体が強制終了します(Safariのメモリリークではないかと思われます)。以下の行で、canvas内のピクセルデータ配列を変数に入れてワーカーに渡しており、この処理を何度も繰り返すとクラッシュします。


var img = context.getImageData(0, 0, canvasW, canvasH).data;


 筆者の環境であるMac OS X (10.6) + Safari 5では最高でも156回ほど繰り返した時点でクラッシュしてしまいました。ワーカーにピクセルデータ配列として渡さず、ワーカー内でピクセルデータ配列を読み出すとクラッシュしにくくなります。


var img = context.getImageData(0, 0, canvasW, canvasH);


 ただし、この方法を使うと今度はFirefox 3.6でワーカー内でピクセルデータが取得できず、正しく動作しなくなります。なお、Firefox 4.0以降では不具合は解消されています(ポインタ渡し/参照渡しができる)。

この連載の記事

一覧へ

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