サイトトップ

Director Flash 書籍 業務内容 プロフィール

HTML5テクニカルノート

EaselJSのフィルタColorMatrixFilterでイメージをセピア調に変える

ID: FN1209003 Technique: HTML5 and JavaScript

DisplayObject.filtersプロパティ
文法 DisplayObjectオブジェクト.filters
プロパティ値 DisplayObjectインスタンスに適用するFilterオブジェクトを納めた配列。フィルタの効果を得るには、インスタンスに対してDisplayObject.cache()あるいはDisplayObject.updateCache()メソッドを呼出さなければならない。また、フィルタはキャッシュに定めた領域の中だけにかかる。
DisplayObject.cache()メソッド
文法 DisplayObjectオブジェクト.cache(x, y, width, height, scale)
概要

DisplayObjectインスタンスを新たなCanvasに描き、時系列で続く描画にもそのイメージを用いる。すると、動かない子インスタンスをたくさんもつ親インスタンスや込入ったShapeオブジェクトなど、変化の少ない複雑なコンテンツは素速く描ける。tickイベントのたびにコンテンツを描き直さなくて済むためである。

キャッシュしたDisplayObjectインスタンスは、移動したり回転したり、フェードイン・アウトもできる。しかし、中身が変わったときは、DisplayObject.cache()あるいはDisplayObject.updateCache()メソッドを呼出して描き直さなければならない。キャッシュする領域は、左上角のxy座標および幅と高さで定める。この矩形領域の座標は、参照するDisplayObjectインスタンスにもとづく。

引数

x − キャッシュ領域の左端標値。

y − キャッシュ領域の上端標値。

width − キャッシュ領域の幅。

height − キャッシュ領域の高さ。

scale − キャッシュ領域の拡大・縮小率。

戻り値 なし。
ColorMatrixFilter()コンストラクタ
文法 ColorMatrixFilter(matrix)
概要 カラー行列変換のColorMatrixFilterインスタンスをつくる。
引数

matrix − カラー変換行列を示すColorMatrixオブジェクトまたは行列の20成分を数値エレメントとして納めた配列。

ColorMatrix()コンストラクタ
文法 ColorMatrix(brightness, contrast, saturation, hue)
概要 カラー変換行列を示すColorMatrixインスタンスをつくる。ColorMatrixFilterオブジェクトにカラー変換を定めるために使える。
引数

brightness − 明度を示す-255から255までの数値。RGBすべてに256階調のカラー値が加算(減算)される。-255は黒(0x000000)、255は白(0xFFFFFF)ですべてのピクセルを塗りつぶすことになる。

contrast − コントラストを示す-100から100までの数値。正数はコントラストを高め、負数は低める。

saturation − 彩度を示す-100から100までの数値。正数はコントラストを高め、負数は低める。

hue − 色相を示す-180から180までの角度。


説明

フィルタはFilterオブジェクトを、まずDisplayObjectインスタンスのDisplayObject.filtersプロパティに配列で定め、そのエレメントにFilterオブジェクトを加えます。つぎに、フィルタの効果を得るには、インスタンスに対してDisplayObject.cache()メソッドを呼出さなければなりません。引数には、xy座標と幅および高さの矩形領域の情報を与えます。

DisplayObjectオブジェクト.filters = [Filterオブジェクトのエレメント];
DisplayObjectオブジェクト.cache(x座標, y座標, 幅, 高さ);

DisplayObject.filtersプロパティを配列にしたのは、複数のフィルタが重ねてかけられる仕組みにするためです。フィルタの効果をなくすには、プロパティに空の配列を設定します。

DisplayObjectオブジェクト.filters = [];

インスタンスのカラーをピクセルごとに処理するのがColorMatrixFilterオブジェクトです。コンストラクタの引数にColorMatrixオブジェクトを用いれば、明度・コントラスト・彩度・色相の4つを決めてカラーが操作できます。

インスタンスのカラーをピクセルごとに処理するのがColorMatrixFilterオブジェクトです。コンストラクタの引数にはカラー変換行列を渡します。4行×5列のカラー変換行列は、20エレメントの数値が納められた行列で定めることができます。けれどもうひとつのやり方として、ColorMatrixオブジェクトを用いれば、明度・コントラスト・彩度・色相の4つの数値でカラーが決められます。

new ColorMatrixFilter(カラー変換行列)

ColorMatrix()コンストラクタには、明度・コントラスト・彩度・色相の4つの数値を引数として渡します。明度は±255の256階調、コントラストと彩度は±100のパーセンテージ、色相は±180の度数の範囲でそれぞれ定めます。

new ColorMatrix(明度, コントラスト, 彩度, 色相)

外部画像ファイルをBitmapインスタンスに読込んでCanvasに置き、そのカラーイメージをColorMatrixFilterでセピア調に変えてみます(図001)。外部画像ファイルの読込み方については、「PreloadJSで外部画像ファイルの読込みを待つ」をお読みください。

図001■BitmapインスタンスのカラーイメージをColorMatrixFilterでセピア調に変える
図001左
もと画像
図001右
セピア調にカラー変換したイメージ

まず、script要素でクラスFilterとColorMatrixFilterおよびColorMatrixを読込んでおきます[*1]。なお、行番号は後でscript要素全体を掲げるコード001にもとづきます。

  1. <script src="easeljs/filters/Filter.js"></script>
  2. <script src="easeljs/filters/ColorMatrixFilter.js"></script>
  3. <script src="easeljs/filters/ColorMatrix.js"></script>

DisplayObject.filtersプロパティに定めるフィルタの配列は、予め変数(filtersList)に入れておきます(第22行目)。そして、初期化の関数(initialize())で、フィルタとしてColorMatrixFilterオブジェクトを加えます。引数のカラー変換行列はColorMatrixオブジェクトで与え、明度50、色相-150度とします(第30行目)。Bitmapインスタンスへのフィルタの設定は、AbstractLoader.onFileLoadイベントのハンドラで行います(第28行目)。

  1. var filtersList = [];
  2. function initialize() {
  1.   var myBitmap = new Bitmap(file);
  2.   var loader = new PreloadJS(false);
  3.   loader.onFileLoad = draw;
  4.   loader.loadFile({src:file, data:myBitmap});
  5.   filtersList.push(new ColorMatrixFilter(new ColorMatrix(50, 0, 0, -150)));
  1. }

画像が読込み終わると呼出されるハンドラ(draw())は、フィルタ設定の関数(setFilter())を呼出します(第43行目)。引数は画像が読込まれたBitmapインスタンスとブール(論理)値です。第2引数がtrueなら、インスタンスのDisplayObject.filtersプロパティにフィルタとしてColorMatrixFilterを適用します(第48〜49行目)。この引数にfalseが渡されると、フィルタを解除します(第51行目)。そして、DisplayObject.cache()メソッドの呼出しにより、フィルタがかかったイメージをキャッシュします(第53行目)。

  1. function draw(eventObject) {
  2.   var myBitmap = eventObject.data;
  1.   setFilter(myBitmap, true);
  1. }
  2. function setFilter(myBitmap, set) {
  3.   var myImage = myBitmap.image;
  4.   if (set) {
  5.     myBitmap.filters = filtersList;
  6.   } else {
  7.     myBitmap.filters = [];
  8.   }
  9.   myBitmap.cache(0, 0, myImage.width, myImage.height);
  10. }

これでBitmapインスタンスに読込まれた画像のカラーイメージが、セピア調に変わります(前掲図001参照)。でき上がったscript要素全体は、つぎのコード001のとおりです。

コード001■フィルタColorMatrixFilterとColorMatrixオブジェクトでカラーイメージをセピア調に変える
  1. <script>
  2. var createjs = window;
  3. </script>
  4. <script src="easeljs/utils/UID.js"></script>
  5. <script src="easeljs/geom/Matrix2D.js"></script>
  6. <script src="easeljs/events/MouseEvent.js"></script>
  7. <script src="easeljs/display/DisplayObject.js"></script>
  8. <script src="easeljs/display/Container.js"></script>
  9. <script src="easeljs/display/Stage.js"></script>
  10. <script src="easeljs/display/Bitmap.js"></script>
  11. <script src="easeljs/filters/Filter.js"></script>
  12. <script src="easeljs/filters/ColorMatrixFilter.js"></script>
  13. <script src="easeljs/filters/ColorMatrix.js"></script>
  14. <script src="preloadjs/AbstractLoader.js"></script>
  15. <script src="preloadjs/PreloadJS.js"></script>
  16. <script src="preloadjs/TagLoader.js"></script>
  17. <script src="preloadjs/XHRLoader.js"></script>
  18. <script>
  19. var stage;
  20. var file = "images/Pen.png";
  21. var filtersApplied = false;
  22. var filtersList = [];
  23. function initialize() {
  24.   var canvasElement = document.getElementById("myCanvas");
  25.   stage = new Stage(canvasElement);
  26.   var myBitmap = new Bitmap(file);
  27.   var loader = new PreloadJS(false);
  28.   loader.onFileLoad = draw;
  29.   loader.loadFile({src:file, data:myBitmap});
  30.   filtersList.push(new ColorMatrixFilter(new ColorMatrix(50, 0, 0, -150)));
  31.   setAppearance(myBitmap, canvasElement.width / 2, canvasElement.height / 2);
  32. }
  33. function setAppearance(instance, nX, nY) {
  34.   instance.x = nX;
  35.   instance.y = nY;
  36.   stage.addChild(instance);
  37. }
  38. function draw(eventObject) {
  39.   var myBitmap = eventObject.data;
  40.   var myImage = eventObject.result;
  41.   myBitmap.x -= myImage.width / 2;
  42.   myBitmap.y -= myImage.height / 2;
  43.   setFilter(myBitmap, true);
  44.   stage.update();
  45. }
  46. function setFilter(myBitmap, set) {
  47.   var myImage = myBitmap.image;
  48.   if (set) {
  49.     myBitmap.filters = filtersList;
  50.   } else {
  51.     myBitmap.filters = [];
  52.   }
  53.   myBitmap.cache(0, 0, myImage.width, myImage.height);
  54. }
  55. </script>

ColorMatrixFilter()コンストラクタには引数のカラー変換行列を配列で渡すことができます。もともと4行×5列の行列で表される各数値を、第1行目から順につなげて20エレメントの配列にするのです。セピア調にカラー変換する行列はつぎのとおりです。

0.393   0.769   0.189   0   0  
0.349 0.686 0.168 0 0
0.272 0.534 0.131 0 0
0 0 0 1 0

前掲コード001でColorMatrixFilter()コンストラクタの引数にColorMatrixオブジェクトでなく、このカラー変換行列を配列で渡すように書替えると、つぎのようになります。カラーイメージも、よりセピア調の雰囲気が増します(図002)。

  1. function initialize() {
      var colorSepia = [
        0.393, 0.769, 0.189, 0, 0,
        0.349, 0.686, 0.168, 0, 0,
        0.272, 0.534, 0.131, 0, 0,
        0, 0, 0, 1, 0
        ];
      // filtersList.push(new ColorMatrixFilter(new ColorMatrix(50, 0, 0, -150)));
  1.   filtersList.push(new ColorMatrixFilter(colorSepia));
  1. }

図002■カラー変換行列を配列で定めてセピア調に変える
図002

2012年8月のバージョンアップで、CreateJS Suiteのすべてのクラスに名前空間としてcreatejsが加わることになりました。けれど、前掲コード001では、これまでと同じスクリプトで動くように、つぎのようなscript要素に加えたJavaScriptで名前空間を省いています(第1〜3行目)。名前空間について詳しくは、「CreateJS Suiteのクラスに名前空間が設定される」をお読みください。

  1. <script>
  2. var createjs = window;
  3. </script>

前述のカラー変換行列を配列で定める場合について、ご参考までに名前空間createjsを加えたscript要素全体は、つぎのコード002のようになります。コンストラクタを始めとするクラスすべてに、名前空間createjsを添えます。

コード002■カラー変換行列は配列で定めて名前空間createjsを加える
  1. <script src="easeljs/utils/UID.js"></script>
  2. <script src="easeljs/geom/Matrix2D.js"></script>
  3. <script src="easeljs/events/MouseEvent.js"></script>
  4. <script src="easeljs/display/DisplayObject.js"></script>
  5. <script src="easeljs/display/Container.js"></script>
  6. <script src="easeljs/display/Stage.js"></script>
  7. <script src="easeljs/display/Bitmap.js"></script>
  8. <script src="easeljs/filters/Filter.js"></script>
  9. <script src="easeljs/filters/ColorMatrixFilter.js"></script>
  10. <script src="easeljs/filters/ColorMatrix.js"></script>
  11. <script src="preloadjs/AbstractLoader.js"></script>
  12. <script src="preloadjs/PreloadJS.js"></script>
  13. <script src="preloadjs/TagLoader.js"></script>
  14. <script src="preloadjs/XHRLoader.js"></script>
  15. <script>
  16. var stage;
  17. var file = "images/Pen.png";
  18. var filtersApplied = false;
  19. var filtersList = [];
  20. function initialize() {
  21.   var canvasElement = document.getElementById("myCanvas");
  22.   stage = new createjs.Stage(canvasElement);
  23.   var myBitmap = new createjs.Bitmap(file);
  24.   var loader = new createjs.PreloadJS(false);
  25.   loader.onFileLoad = draw;
  26.   loader.loadFile({src:file, data:myBitmap});
  27.   var colorSepia = [
        0.393, 0.769, 0.189, 0, 0,
        0.349, 0.686, 0.168, 0, 0,
        0.272, 0.534, 0.131, 0, 0,
        0, 0, 0, 1, 0
        ];
  28.   filtersList.push(new createjs.ColorMatrixFilter(colorSepia));
  29.   setAppearance(myBitmap, canvasElement.width / 2, canvasElement.height / 2);
  30. }
  31. function setAppearance(instance, nX, nY) {
  32.   instance.x = nX;
  33.   instance.y = nY;
  34.   stage.addChild(instance);
  35. }
  36. function draw(eventObject) {
  37.   var myBitmap = eventObject.data;
  38.   var myImage = eventObject.result;
  39.   myBitmap.x -= myImage.width / 2;
  40.   myBitmap.y -= myImage.height / 2;
  41.   setFilter(myBitmap, true);
  42.   stage.update();
  43. }
  44. function setFilter(myBitmap, set) {
  45.   var myImage = myBitmap.image;
  46.   if (set) {
  47.     myBitmap.filters = filtersList;
  48.   } else {
  49.     myBitmap.filters = [];
  50.   }
  51.   myBitmap.cache(0, 0, myImage.width, myImage.height);
  52. }
  53. </script>

[*1] EaselJSライブラリのコンパクト(min)版JSファイル(図003)は、ライブラリのほぼすべてのクラスがひとつのファイルに凝縮されています(「EaselJSライブラリの短縮版JSファイルについて」参照)。ただし、このJSファイルにはフィルタのクラス(filtersフォルダ)は含まれません。したがって、コンパクト版のJSファイルを読込んだ場合であっても、用いるフィルタのクラスは別にscript要素で加えてください。

図003■libフォルダに納められたEaselJSライブラリのコンパクト版JavaScriptファイル

コンパクト版がフィルタのクラスを含まないのは、EaselJSライブラリのファイルサイズを小さく抑えるためです。したがって、今のところフィルタクラスをコンパクト版に含める予定はないとのことです(CreateJS Support「Filters with easeljs-0.5.0.min.js」参照)。


数学解説

カラー変換行列は、4行×5列の数学の行列で表されます。行列の中の各数値は「成分」と呼ばれ、文字で表すときは行番号と列番号を添える(aij)のが一般です。

a11 a12 a13 a14 a15
a21 a22 a23 a24 a25
a31 a32 a33 a34 a35
a41 a42 a43 a44 a45

変換もと画像のRGBAカラー値を(r, g, b, a)とすると、変換後のカラー値(r', g', b', a')は行列の成分をつぎのように用いた式で求められます。

r' = a11×r + a12×g + a13×b + a14×a + a15
g' = a21×r + a22×g + a23×b + a24×a + a25
b' = a31×r + a22×g + a33×b + a34×a + a35
a' = a41×r + a22×g + a43×b + a44×a + a45

もっとも、カラー変換行列の考え方を知るにはもう少し簡略化して、RGBカラーに関わる3行×3列の正方行列(行数と列数が等しい行列)の演算を理解すればよいでしょう。RGBカラーを3次の列ベクトルとすると、行列によるカラー変換はつぎのように、カラー変換行列と変換もとカラーの列ベクトルとの積として表されます。

r' = a11 a12 a13 r
g' a21 a22 a23 g
b' a31 a32 a33 b
= a11×r + a12×g + a13×b
a21×r + a22×g + a23×b
a31×r + a22×g + a33×b

計算式を確かめたところで、変換行列がRGBカラーをどう変えるのか、具体的な例で考えましょう。まず、対角成分が1であとの成分が0の行列は「単位行列」と呼ばれます。RGBカラーの列ベクトルを乗じても、変換したベクトル成分のRGBカラー値はもとのまま変わりません。

  1   0   0   r = r + 0×g + 0×b = r
0 1 0 g r + 1×g + 0×b g
0 0 1 b r + 0×g + 1×b b

つぎに、グレースケールに変換する場合を考えてみます。まず、もと画像のr成分からです。カラー変換行列の第1列目は、もと画像のRカラー値を変換後のRGBにどう配分するか示していると捉えることができます。グレースケールにするには、3つの比率を等しくします。仮に1/3ずつとしておきます。残るふたつのカラーも同じように考えて、9成分すべて1/3にすれば、グレースケールに変換されます。

  r
           
  1/3         r' = r / 3  
1/3     g' = r / 3  
1/3     b' = r / 3  

もっとも、全成分を1/3として変換したグレースケールのイメージをもと画像と比べてみると、階調のバランスがよくありません。それは、同じカラー値でも、緑は明るめに、青は暗めに見えるからです。したがって、緑のカラー値は少し高くし、青のカラー値は抑えた方がよいのです。NTSCの規格では、つぎのような成分値になります。

  0.298912   0.586611   0.114478  
0.298912 0.586611 0.114478
0.298912 0.586611 0.114478

RGBカラーの変換行列にアルファを加えてRGBAにすると、4行×4列の正方行列になります。値を変換する計算の考え方は、これまでご説明したRGBカラーと変わりません。けれど、ColorMatrixFilterクラスで用いる4行×5列のカラー変換行列における第5列目は違います。正方行列による演算は、RGB(A)ベクトルとの乗算です。すると、もと画像のカラーが黒(0x0)のときは、0に何を掛けても0ですから、値の変えようがありません。そこで第5列を増やして、加算(あるいは減算)する成分としたのです。


作成者: 野中文雄
更新日: 2013年2月4日 注[*1]を追加。
作成日: 2012年9月6日


Copyright © 2001-2012 Fumio Nonaka.  All rights reserved.