javascriptで画像処理をする【HTML5】

公開日: : 最終更新日:2014/08/14 JavaScript/jQuery, 画像処理

スポンサーリンク

HTML5でjavascriptを使って画像処理をしてみました。このページでは手始めにグレースケール化(白色・灰色・黒色にする)とソーベルフィルタの処理について取り組みます。

この記事では一部HTML5のcanvasを利用しておりこれはIEの8までは動かないのですが、ExploreCanvasというライブラリを使用することで他のブラウザと同じような挙動を見せることができます。

画像の読み込み

画像処理する対象の画像についてはじめからURLを指定するかHTML5のFile APIを利用してページを開いた方のローカルファイルから選ぶかで読み込ませることができます。

外部ファイルから読み込む

前述の通りHTML5のFileAPIを使います。

HTML側

<input type="file" id="selectfile" accept='image/*'>

JS側

//ファイルオープンの際のイベント
var ofd = document.getElementById("selectfile");
ofd.addEventListener("change", function(evt) {
    //ここに画像データを入力
    var img = null;

    var file = evt.target.files;
    var reader = new FileReader();

    //dataURL形式でファイルを読み込む
    reader.readAsDataURL(file[0]);

    //ファイルの読込が終了した時の処理
    reader.onload = function(){
        img = new Image();
        img.onload = function(){
                /*読み込み終了後ここで画像を加工して表示する*/
        }
        //読み込んだ画像ソースを入れる
        img.src = reader.result;
    }
}, false);

HTML側ではinputタグのtypeをfileにすることでファイルオープンのダイアログを出すボタンが作成されます。ここのaccept属性を「image/*」にするとデフォルトで画像ファイルの選択になります。

次に、JS側ではFileReaderクラスのreadAsDataURLメソッドを用いて取得したファイルパスからファイルを読み取ります。そして、今回は画像ファイルを読み込んでいるので取得したものをImageクラス(のsrcプロパティ)に代入してCanvasで使えるように準備をしておきます。

なおこのスクリプトは順序を逆にする場合は全てのデータを読み込ませてからスクリプトを動かした方がよいので次のイベント内に先ほどのスクリプトを書くといいです。(次からは省略します)

window.addEventListener("DOMContentLoaded", function(){
    /*ここにスクリプトを書く*/
}, false);

あらかじめ指定する

画像ファイルを外部読み込み式にするのでなく、指定のURLの画像にしたい場合は単純にImageのsrcプロパティに直接代入すればよいです。

img = new Image();
img.onload = function(){
    /*読み込み終了後ここで画像を加工して表示する*/
}
//読み込んだ画像ソースを入れる
img.src = "sample.jpg";

画像の操作・加工

画像処理というとピクセル単位で画像にアクセスして画素値を取得したり変更したりといった操作が要だと思います。この処理をするにあたってcanvasを使います。

canvasタグはあらかじめ設置しておくことになりますが、必ずしも設置せずともdocument.createElement(‘canvas’)で対応することができます。ただし、この場合画像の表示は別に考える必要が出てくるので注意してください。

以下、先ほどのImageクラスのonloadイベント内の処理です。

//キャンバスに画像をセット
var canvas = document.getElementById('canvas');
//var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var width = img.width;
var height = img.height;
canvas.width = width;
canvas.height = height;
context.drawImage(img, 0, 0);

canvasからcontextを生成してそのdrawImageメソッドでcanvasに絵を描きます(画像が設置される)。この際canvasのサイズも画像と同じサイズに変更しておかないと途中ではみ出る形になってしまうので気を付けてください。

画素値の取得と変更

画像に対してピクセル単位でアクセスするようにするにはimagedata形式にするとできるようになります。

imagedata形式

imagedata.width…画像の横幅(px)
imagedata.height…画像の縦幅(px)
imagedata.data…RGBA の順番のデータを含んだ1次元配列。それぞれの値は 0 ~ 255 の範囲。

画素値を変更するにはこのimagedata.dataの配列の値をいじくればいいということになります。このimagedataですが、contextから取得するにはcontextのgetImageDataメソッドおよびcreateImageDataメソッドで可能で、それぞれ保持している画像をimagedataとして返すというものと、空のimagedataを返すものとなっています。

imagedata = context.createImageData(sw, sh)
imagedata = context.createImageData(imagedata)
imagedata = context.getImageData(sx, sy, sw, sh)
(sx…開始X座標、sy…開始Y座標、sw…横幅、sh…縦幅)

そして後はこのimagedataのdata配列をいじるだけです。

例)

var src = context.getImageData(0,0,width,height);
for (var i = 0; i < height; i++) {
    for (var j = 0; j < width; j++) {
        var idx = (j + i * width) * 4;
        var r = src[idx]; //赤
        var g = src[idx+1]; //緑
        var b = src[idx+2]; //青
        var a = src[idx+3]; //透明度
        
        var gray = (r+g+b)/3;
        src[idx] = gray;
        src[idx+1] = gray;
        src[idx+2] = gray;
        //src[idx+3] = a; //透明度は同じ値
    }
}

この例では各画素値を取得してグレースケール化しています。グレースケール化はRGBの値を足して3で割り、同じ値をその画素値とするだけです。

ここの値について色々な操作をすることで様々なフィルタを作成することができます。

画像の表示

最後に画像の表示についてですが、canvasタグをそのまま表示させている場合はすでに表示されていると思うのでもう結構なのですが、使っていない場合には表示させる解決策の一つとして指定の位置に画像を挿入させます。

canvasのtoDataURLメソッドを利用することで画像のURLが取得できるようになるのでそれをimgタグのsrcにそのまま代入してしまうと画像が表示されます。

例)

//画像タグに代入して表示
var dataurl = canvas.toDataURL();
document.getElementById("output").innerHTML = "<img src='" + dataurl + "'>";

id名がoutputのタグのinnerHTMLにtoDataURLで取得したURLをsrc属性とした画像タグを設置しました。

フィルター処理について

ソーベルフィルタの他にもエッジ検出フィルタとしてラプラシアンフィルタやプリューウィットフィルタなどの空間フィルタリングがあります。デモではソーベルフィルタを使用しています。

デモ

いままでのものを利用して作成したデモです。ファイルを読み込んだらグレースケール変換ではなく、ソーベルフィルターをかける処理を行い、その後にimgタグに代入しています。

ソーベルフィルターはエッジ検出の空間フィルタです。グレースケールではつまらないのでこちらにしました。

<div id="output">
</div>

ソース

JS側

//グレースケール変換関数
var grayFilter = function(src, dst, width, height) {
    for (var i = 0; i < height; i++) {
        for (var j = 0; j < width; j++) {
            var idx = (j + i * width) * 4;
            var gray = (src[idx] + src[idx + 1] + src[idx + 2]) / 3;
            dst[idx] = gray;
            dst[idx + 1] = gray;
            dst[idx + 2] = gray;
            dst[idx + 3] = src[idx + 3];
        }
    }
};
//ソーベルフィルタ関数
var sobelFilter = function(src, dst, width, height) {
    var weight = [
        -1,0,1,
        -2,0,2,
        -1,0,1
    ];
    for (var i = 0; i < height; i++) {
        for (var j = 0; j < width; j++) {
            var idx = (j + i * width) * 4;
            var val = [0,0,0];
            for(var k = -1; k <= 1; k++){
                for(var l = -1; l <= 1 ; l++){
                    var x = j + l;
                    var y = i + k;
                    if(x < 0 || x >= width || y < 0 || y >= height){
                        continue;
                    }
                    var idx1 = (x + y * width) * 4;
                    var idx2 = (l + 1) + (k + 1)*3;
                    val[0] += weight[idx2]*src[idx1];
                    val[1] += weight[idx2]*src[idx1 + 1];
                    val[2] += weight[idx2]*src[idx1 + 2];
                }
            }
            dst[idx] = val[0];
            dst[idx + 1] = val[1];
            dst[idx + 2] = val[2];
            dst[idx + 3] = src[idx + 3];
        }
    }
};

window.addEventListener("DOMContentLoaded", function(){
    //ファイルオープンの際のイベント
    var ofd = document.getElementById("selectfile");
    ofd.addEventListener("change", function(evt) {
        var img = null;
        var canvas = document.createElement("canvas");
        //var canvas = document.getElementById('canvas');

        var file = evt.target.files;
        var reader = new FileReader();

        //dataURL形式でファイルを読み込む
        reader.readAsDataURL(file[0]);

        //ファイルの読込が終了した時の処理
        reader.onload = function(){
            img = new Image();
            img.onload = function(){
                //キャンバスに画像をセット
                var context = canvas.getContext('2d');
                var width = img.width;
                var height = img.height;
                canvas.width = width;
                canvas.height = height;
                context.drawImage(img, 0, 0);

                //フィルター処理
                var srcData = context.getImageData(0, 0, width, height);
                var dstData = context.createImageData(width, height);
                var src = srcData.data;
                var dst = dstData.data;
                //grayFilter(src, dst, width, height);
                sobelFilter(src, dst, width, height);
                context.putImageData(dstData, 0, 0);

                //画像タグに代入して表示
                var dataurl = canvas.toDataURL();
                document.getElementById("output").innerHTML = "<img src='" + dataurl + "'>";
            }
            img.src = reader.result;
        }
    }, false);
});

HTML側

<input type="file" id="selectfile" accept='image/*'>
<div id="output" style="border: none; margin: 30px; background-color: #f8f8f8; min-height: 40px;">
</div>

ソーベルフィルタの部分では元の入力画像は変更せずに新しい空のimagedataを作成して変換した値を代入しています。こうすることで元の画像を保持することができます。(Cなどで使える画像処理ライブラリのOpenCVにある関数もこういった形になっています)

ちなみにソーベルフィルタ関数のweightの配列の値を変更することで色々なフィルタリング関数を作ることができます。

まとめ

javascriptで画像処理をするにあたって、FileAPIを使って画像ファイルを読み込み、canvasから画素単位で画像の加工を行うことでグレースケール変換やソーベルフィルタを行いました。

これをベースにして色々な画像処理を作ることができるのではないかと思います。

スポンサーリンク
Amazon

  • このエントリーをはてなブックマークに追加

関連記事

リクルート期間限定ポイントの有効な使い道

はじめに  先日クレジットカードの見直し活動の一環でポイント還元率の

初心者がお金を貯めるための節約・財テク手法まとめ【ポイント還元・投資・税金対策】

はじめに  最近なるべく節約しようと思い、お金周りに関して色々調べて

動画講座を効率的にキャプチャして復習する方法

はじめに  最近、動画媒体の講座などをたまに視聴しています。独学だと

Twitterのオリジナルサイズの画像ファイル(.jpg-large,.jpg-orig等)を一括リネームする

Twitterの画像をオリジナルサイズで保存しようとするとjpg-la

【Unity】他のスクリプトを一括で取得して有効化・無効化を制御する

Unityを使っていて他のスクリプトを一時的に停止させたいと思ったので

テレビの録画をPCやNASで共有する

今までテレビはテレビ用のモニターで見ればいいやと思っていたのですが、さ

【DeepLearning】物体検出手法のSSD(Keras版)を試す (Ubuntu14.04)

はやりのディープラーニングの物体検出手法の一つであるSSDのサンプルを

→もっと見る

  • Author : ががんぼ
    プログラミングやWeb関係で気付いたことについてメモしていく予定。だいたいが備忘録ですが、自分でサンプルを作って動かしてみたりしています。
PAGE TOP ↑