HTML5でConvolutionFilter

HTML5のcanvasを使って簡単な画像処理をやってみます。
試してみたのは基本的な畳み込みフィルタ。
今回はAS3のConvolutionFilter風のインタフェースで作りました。

Demo: HTML5 Image Processing (Convolution Filter) (Firefox3.5とSafari4で動作確認しています)

汎用的に作ろうとすると少し時間がかかりそうだったので、ひとまず畳み込みフィルタの部分だけ。
引数はAS3のものよりずっと少なく、matrix, divisor, bias の3つ。
matrixXやmatrixYも用意してないのでフィルタカーネルのサイズは3×3限定だし、
異常系の処理も不十分で修正が必要ですが;;
他のフィルタ類は時間を見つけて作ってみようと思います。

・クライアント例

var context = document.getElementById('Canvas').getContext('2d');
var bmp = new Bitmap('sample.jpg', context);
var matrix = [-1, -1, -1,  -1,  8, -1,  -1, -1, -1]; // フィルタカーネル
var divisor = 1;
var bias = 0;
bmp.applyFilter(new ConvolutionFilter(matrix, divisor, bias));

bitmap.js


var Class = {
  create : function() {
    var properties = arguments[0];
    function self() {
      this.initialize.apply(this, arguments);
    }
    for(var i in properties) {
      self.prototype[i] = properties[i];
    }
    if(!self.prototype.initialize) {
      self.prototype.initialize = function() {};
    }
    return self;
  }
};

var Bitmap = Class.create({
  initialize : function(source, context) {
    this.bitmapData = new Image();
    this.bitmapData.src = source;
    this.context = context;
    this.bitmapData.addEventListener('error',
                                     function() { alert("can't load image"); },
                                     false);
  },
  applyFilter : function(filter) {
    try {
      if(this.bitmapData.complete) {
        this.context.drawImage(this.bitmapData, 0, 0);
        var w = this.bitmapData.width, h = this.bitmapData.height;
        var src = this.context.getImageData(0, 0, w, h);
        var dst = this.context.createImageData(w, h);
        filter.apply(src, dst);
        this.context.putImageData(dst, 0, 0);
      }else {
        throw new Error("load image incomplete");
      }
    }catch(e) {
      throw e;
    }
  }
});

var ConvolutionFilter = Class.create({
  initialize : function(matrix, divisor, bias) {
    this.matrix = matrix;
    this.divisor = divisor;
    this.bias = bias;
  },
  apply : function(src, dst) {
    var w = src.width, h = src.height;
    var srcData = src.data;
    var dstData = dst.data;

    for(var y=1;y<h-1;++y) {
      for(var x=1;x<w-1;++x) {
        var idx = 0;
        var r = 0, g = 0, b = 0;
        //var d = 0;
        var i = (y*w + x) << 2;
        for(var ky=-1;ky<=1;++ky) {
          for(var kx=-1;kx<=1;++kx) {
            var pos = (ky*w << 2) + (kx << 2);
            r += srcData[i + pos]*this.matrix[idx];
            g += srcData[i + pos + 1]*this.matrix[idx];
            b += srcData[i + pos + 2]*this.matrix[idx];
            //d += srcData[i + pos]*this.matrix[idx++];
            idx++;
          }
        }
        dstData[i] = r/this.divisor + this.bias;
        dstData[i + 1] = g/this.divisor + this.bias;
        dstData[i + 2] = b/this.divisor + this.bias;
        dstData[i + 3] = 255;
      }
    }
    // for Firefox
    dstData.forEach(function(n, i, arr) { arr[i] = n<0 ? 0 : n>255 ? 255 : n; });
  }
});

ポイントを絞って簡単に説明を。
(以下 context は getContext(’2d’) で取得したオブジェクト)

* imagedata = context.createImageData(sw, sh)
幅sw, 高さshのImageDataオブジェクトを生成します。
ピクセルデータ(imageData.data)はすべて0(黒)になっています。

* imageData = context.getImageData(sx, sy, sw, sh)
(x, y)=(sx, sy) を基準に幅sw, 高さshの領域のピクセルデータを取得できます(imageData.data)。
これはAS3における BitmapData.getPixels() のようなもので、ARGB32bitのデータを扱い、
インデックスは 0~幅×高さ×4-1 まで。

* context.putImageData(imagedata, dx, dy)
指定されたImageDataオブジェクトを (x, y)=(dx, dy) を基準に描画します。

・参考記事
createImageData, getImageData, putImageData メソッド – Canvasリファレンス – HTML5.JP
・関連記事
HTML5の勉強

 

Tags: ,

Comments

No comments so far.

  • Leave a Reply
     
    Your gravatar
    Your Name