ステンシルを着色画像に合成する

前回のつづき。

ステンシルを着色画像に直すには、まずはpythonだとPILモジュールで簡単にできる。

実際のシステムでは、CGIスクリプトが指定のステンシルを指定色で合成したものを返してくれることになっているが、その核心部分だけ書けば、下のよう。

import PIL.Image
import PIL.ImageColor

s = PIL.Image.open('stencil/%s.png' % stencil)

w = s.size[0]/snum
h = height

outimage = PIL.Image.new("RGBA", (w, h), (0,0,0,0))
for i in xrange(snum):
  a = PIL.Image.new("RGBA", (w, h), (0,0,0,0))
  a.paste(PIL.ImageColor.getrgb(colors[i]), (0,0), s.crop((i*w,0,(i+1)*w,h)))
  outimage.paste(a, (0,0), a)

outimage.save(ファイル名または標準出力, format="PNG")

あらかじめ、colors配列には適当な色データが入っていること。["#000000","#ffffff"]といった感じで。snumは色数。

ステンシルは下のような二値画像だが…
f:id:yamatt2:20140215104554p:plain

これをマスク用データとし、できあがり予定サイズの画像データに、次々と色を重ねていくのが、pasteメソッドの発行部分。下の絵のようなイメージの仕事をしている。
f:id:yamatt2:20140215111947j:plain

すべての色を塗り重ねたら、どっかにセーブするなり、標準出力にでも書き出してしまうなりして、終了。

さて、ウェブブラウザ上でも、着色具合を試しながら色指定をしたくなるはずなので、これと大体同様のことをJavaScriptでも書くことにした。これも核心だけ書くと、こんなふう。要canvasAPI。

var canvas_plt;

var disp_colored_image = function(stencil, conf, ctx_tmp, ctx_dist ) {

  ctx_tmp.drawImage(stencil, 0, 0);
  var t = ctx_tmp.createImageData(conf.w, conf.h);
  // fill in 'transparent' color
  for (var i=0;i<conf.w/6;i++) {
    for (var j=0;j<conf.h/6;j++) {
      for (var ii=0;ii<6;ii++){
        for (var jj=0;jj<6;jj++) {
          var p = ((j*6+jj)*conf.w + i*6+ii)*4;
          if (i%2 == j%2) {
            t.data[p] = 240;
            t.data[p+1] = 240;
            t.data[p+2] = 240;
          } else {
            t.data[p] = 220;
            t.data[p+1] = 220;
            t.data[p+2] = 220;
          }
          t.data[p+3] = 255;
        }
      }
    }
  }
  var pltctx = canvas_plt.getContext('2d');
  // merge images
  for (var j=0;j<conf.snum;j++) {
    r = ctx_tmp.getImageData(conf.w*j, 0, conf.w, conf.h);
    rl = r.data.length / 4;
    pltctx.fillStyle = conf.colors[j];
    pltctx.fillRect(0,0,1,1);
    var rp = pltctx.getImageData(0,0,1,1);
    for (var i=0;i<rl;i++) {
      if (r.data[i*4] != 0) {
        t.data[i*4] = rp.data[0];
        t.data[i*4+1] = rp.data[1];
        t.data[i*4+2] = rp.data[2];
        t.data[i*4+3] = 255;
      }
    }
  }
  ctx_dist.putImageData(t,0,0);
};

このdisp_colored_image関数が、stencil(画像データ)、conf(画像サイズや色の指定)、ctx_tmp(一時処理用描画コンテキスト)、ctx_dist(最終表示用描画コンテキスト)を受け取って描画処理をする。

前半部分は、単に下のような透明っぽいチェッカー模様を描こうとしてがんばっただけ。もっとシンプルに書けるんだろうけど。
f:id:yamatt2:20140215113246j:plain

後半部分で、ステンシルの「真っ黒以外のピクセル」に対応する部分について、指定色のピクセルをひとつづつ置いていくこをと繰り返し、最終的に作りたい画像のプレビューを作れるようになった。

すごく端折って書いているが、あとで自分にはわかるだろうから、まあいいや。

クリップアートサイトを作ったときの技術解説、これでおしまい。

サイトはここ。