Hatena::ブログ(Diary)

flashrod このページをアンテナに追加 RSSフィード

2006-11-18

flashrod2006-11-18

[] AS3で「ナンシー小関パッチもん版画」作成ソフトを作る 18:00  AS3で「ナンシー小関風パッチもん版画」作成ソフトを作るを含むブックマーク

今年の夏ごろ話題になったあのソフトをAS3に移植するというちょっと無謀な企画だ。

「ナンシー”小”関 風 パッチもん版画」作成ソフト

そこから辿ってソースファイルnancyKOseki20060719.cppを得ることができる。意外と短いので驚いた。画像処理はOpenCVを使っているので、OpenCVの仕様さえ分かれば、なんとか似たことはAS3でも可能だ。

さてC++を読むのは結構つらかったのだけど何とか読みきった。ナンシー小関風画像処理の概略はこうだ。たぶん。

  1. b0 ← Glayscale(ソースビットマップ)
  2. b1 ← GaussianBlur(b0, 細かさ)
  3. b2 ← GaussianBlur(b0, 細かさ+線の太さ)
  4. b3 ← Sub(b1, b2)
  5. b4 ← Threshhold(b3, 線際)
  6. b5 ← Nancy(b4, b1, b2)
  7. b6 ← Open(b5, 単純さ)
  8. b7 ← Close(b6, 単純さ)

このなかで、Sub()とNancy()以外は今までの画像処理入門で作ってきたのを使いまわせる。

GaussianBlur()はちょっと違うかもしれないけど、AS3リファレンスによるとflash.filters.BlurFilterをBitmapFilterQuality.HIGHで使うと「ガウスぼかしに近似したものになります」と書いてあるのでこれをありがたく使わせてもらう。

Sub (差)

ここではSubは部分の意味じゃなくて単純に引き算なのだ。二つの画像から、画素の明るさを単純に引き算した画像を作る。OpenCVのcvSubについて色々調べてたら、引いた結果値がマイナスになることは無く、0で自動的にクリッピングされるとのことらしい。

        /** 引き算
         * @param dst デスティネーション
         * @param src1 引かれる側
         * @param src2 引く側
         */
        public function Sub(dst:BitmapData, src1:BitmapData, src2:BitmapData):void {
            for (var x:int = 0; x < src1.width; x++) {
                for (var y:int = 0; y < src1.height; y++) {
                    var c1:int = src1.getPixel(x, y) & 255;
                    var c2:int = src2.getPixel(x, y) & 255;
                    var c:int = c1 - c2;
                    c = c < 0 ? 0 : c;
                    dst.setPixel(x, y, (c << 16) | (c << 8) | c);
                }
            }
        }

入出力画像は同じ幅と高さであることを前提にしている。

また入力画像はグレースケールであることを前提にしているので青成分しか見てない。

Nancy

さて肝の Nancy() なのだが、これはcppのソースをじっくり見て、たぶんこんなので同じ処理になっていると思う。

        /** エッジ部分とベタ部分の合成をする
         * @param dst デスティネーション
         * @param src0 線際閾値処理の結果
         * @param src1 細かさ平滑化の結果
         * @param src2 細かさ+太さ平滑化の結果
         * @param fill ベタ
         */
        public function Koseki(dst:BitmapData, src0:BitmapData, src1:BitmapData, src2:BitmapData, fill:int):void {
            for (var x:int = 0; x < src0.width; x++) {
                for (var y:int = 0; y < src0.height; y++) {
                    var s0:int = src0.getPixel(x, y) & 255;
                    var s1:int = src1.getPixel(x, y) & 255;
                    var s2:int = src2.getPixel(x, y) & 255;
                    var d:int;
                    if (s2 > s1 || s2 < fill)
                        if (s2 < fill)
                            if (s0 == 255)
                                d = 255;
                            else
                                d = 0;
                        else
                            if (s0 == 0)
                                d = 0;
                            else
                                d = 255;
                    else
                        if (s2 > 255 - fill)
                            if (s0 == 0)
                                d = 255;
                            else
                                d = 0;
                        else
                            if (s0 == 0)
                                d = 255;
                            else
                                d = 0;
                    dst.setPixel(x, y, (d << 16) | (d << 8) | d);
                }
            }
        }

例によって入出力画像は全部同じ幅と高さ、グレースケールであることを前提にしている。

デモ

さて本家は画像ファイルをDnDすればいいようになってたけど、Flashでは無理なので、カメラ画像を取り込むようにした。

http://space.geocities.jp/flashr0d/nancy.html

スライダを動かすと微調整できる。

なんだか本家ほど精度が良くない気がするんだけど、これは主に画像処理に関するオイラの技能不足ということで、ひとつよしなに。