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
  • このエントリーをはてなブックマークに追加

関連記事

【jQuery】サイドバー固定に役立つサイト集

縦スクロール 縦方向にスクロールして指定の位置に来たら要素を固定させる方法のリンク集を紹介させてい

記事を読む

YoutubeのURLリンクを外部プレイヤーのものに正規表現で置換する

Youtubeのアドレスを正規表現を使って抽出して外部プレイヤーのフレームに変換・置換するスクリプト

記事を読む

【javascript】正規表現文字置換簡易ツール

jQueryで作成した正規表現による文字抽出用スクリプトです。 (使い方) 「正規表

記事を読む

【3DCG・MMD】カメラレンズによる見え方シミュレータ作成について

カメラのレンズが違うことでどのくらい出来上がる写真に差が出てくるのかを確かめることのできるシミュレー

記事を読む

【jQuery UI】Sliderのオプション変更やイベントを取得をする方法

jQuery UIを使ってスライダー(トラックバー)を作ります。その際に起こるイベントとしてスライダ

記事を読む

スマホ向け幅固定サイトの回転時の幅対応方法

幅固定サイトでのスマホ回転時Webサイトの横幅を合わせる方法のメモ。 下記2点の設定が必要です。

記事を読む

【javascript】privateのメソッドからpublicなメンバを呼ぶ

javascriptでのクラス定義 javascriptではいわゆる正確なクラスは作れませんが、そ

記事を読む

PHPでページリロードをしようと思ったができなかったので

header("Location: ".$_SERVER); とすればページをリロードでき

記事を読む

monacaでWebサイトをアプリ化

monacaを使ってWebサイトをアプリ化するための叩き台としてのメモです。 Webサイトのア

記事を読む

javascriptにおけるfor文の書き方

1. for文とは ある条件が正しいときに処理を繰り返すというループを記述するための文です。 似

記事を読む

NVIDIA DIGITSをSupervisorを使って自動起動する

はじめに 先日Ubuntu14.04にDIGITS 5をGPUなしP

【2017年度】GeForce 1080 Ti搭載BTOパソコン比較

はじめに 最近ディープラーニング用にGPUのないPCにDIGITSを

Windows用のシンプルなストップウォッチソフト紹介

Windows(10)でも使える使いやすいKAZAMITimerという

MNISTをDIGITS 5のCPUのみで試す(GPUなし)

はじめに DIGITSを使えばGUI操作で簡単にディープラーニングが

Windowsユーザが初めてMacBookを購入したときの注意点や感想

最近12インチのMacBookを購入しました。今までWindowsしか

Redmine3.2をUbuntu14.04にインストールして躓いた所についてメモ

はじめに Ubuntu14.04 ServerにRedmine3.2

UWSCを使ったソフト自動立ち上げ【SoftEther VPN Server】

やりたいこと 最近SoftEtherという筑波大学発のベンチャー企業

→もっと見る

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