このページの最終更新
 2008/09/14

Paint.NET公式サイト↓
マニュアルメニュー TOP 機能と使い方 プラグイン レタッチ例 効果使用例 ギャラリー
ゼロから作るオリジナルプラグイン
4.線画の抽出プラグインを作る!(実用プログラム作成編)

 前回、抽出の解析にちょっと時間を割きましたが、プログラム自体はたった4行という簡単なものでした。どうせなら、白以外のセカンダリカラー背景に描いた好きな色のプライマリーカラーの線画も抽出できるようにしたいです。今回は息抜きの絵は一切無し!チョイガチです!

(1)今回も分析!

 私の知る限り、白黒以外は、背景色に描いた滑らかな線を透明レイヤに描いた線に戻す方法は有りません。でも、透明レイヤに描いた滑らかな線を単色背景レイヤにブレンド標準で合成する事は出来ます。これは背景レイヤに直接線を描いたのと同じ色になります。つまり、合成の仕組みが分かれば、逆の操作をし、透明レイヤに描いた線のように出来るはずです。

分析方法は合成前と合成後の色を比較して関係式を見つけるという、まっ前回とやる事は一緒ですが、さすがに今回は結構面倒でした。
透明レイヤに塗られた色が背景色と合成されるとき、透明レイヤに塗られた色に透明度の割合を掛けたモノと、背景レイヤの背景色に透明度の減算した割合をかけ、その二つを足した色にしてるのです。こういうのは、日本語だとよけい、分からないですね(笑)簡単な式で説明できます。

透明レイヤの線の色(プライマリカラー) Pc=(PrimaryColor)
透明レイヤの線の透明度         α
背景色(セカンダリカラー)         Sc=(SecondaryColor)
合成レイヤの線の色            Cc=(CurrentPixel
Color)

Cc = Pc×(α/255)+Sc×(1−α/255)・・・・・・・(1)

 今やろうとしているのは、背景色セカンダリカラーScに直接描画色プライマリカラーPcで描かれた線の色Ccを透明レイヤに描いた線の色に戻す事です。透明レイヤに描いた線の色は、白黒のときと同じで、どのピクセルもRGBは同じです。(でも、実際はカラーの場合、微妙〜に1とか2変わってる場合があります)アンチエイリアスのところだけ、透明度が違うだけです。ですから、上の式から透明度を求めて、それぞれのピクセルをプライマリカラーのRGBに置き換えて、計算で求めた透明度αを入れてあげれば、透明レイヤに描いた線にようになるはずです。つまり、カラーの線の抽出が出来るはずです。αは上式より

α = 255(Cc−Sc)/(Pc−Sc)・・・・・・・(2)

ここまで、分かればプログラムはできたも同然ですね!(って、ここまで解析するのに2時間かかりました(汗))
さぁ!プログラムを・・・ちょっと待った!前回の白黒の式と整合性はあるのか確かめないといけませんね。
プライマリカラー黒は0、よってPc=0、セカンダリカラー白は255、よってSc=255です。これを上式に代入します。

α = 255(Cc−255)/(0−255)
α = 255−Cc
・・・・・・・(3)

前回の白黒限定式との整合性を確認できました。数学の授業みたい?プログラムってほとんど数学みたいなもんですからね(^^;)

(2)日本語プログラム


日本語プログラムもそろそろ、簡略化しましょう。やりたいことを整理するための覚え書きですからね。
(2)式をつくり、それぞれのピクセルにαを代入し、プライマリカラーに置き換えるというのがメインプログラムになります。

1.(Cc−Sc)の値を入れる新しい変数dcs(RGB)を宣言し、(CurrentPixel.RGB)−(SecondaryColor.RGB)を代入する
2.(Pc−Sc)の値を入れる新しい変数dps(RGB)を宣言し、(PrimaryColor.RGB)−(SecondaryColor.RGB)を代入する
3.(2)式で求めるαの値を入れる新しい変数ra(RGB)を宣言し、初期値=0を代入する
4.(Pc−Sc)=0でないとき、(2)で求めた値をra(RGB)に代入する

5.実際に使用する透明度を入れる新しい変数raを宣言し、初期値=0を代入する
6.ra(RGB)の3つの値を0を除いて、算術平均しraに代入する
7.CurrentPixel.Aにraを代入する。
8.CurrentPixel.RGBに
PrimaryColor.RGBを代入する

実際のプログラムでは、RGB毎に書き、さらに条件が色々あるので結構長くなります。

(3)C#の型宣言

今までは、サンプルプログラムの変数だけ使っていましたが、新しい変数を使わないとこのプログラムはできません。新しい変数を使うためには型を宣言しなければなりません。サンプルの中で「int x 」や「int y」というのがでてきますが、このintが型宣言です。意味は
int=「これから使う変数は整数型です」(正確には4byte符号付整数型)
今回は割り算があるので、実数も使いたいです。つまり小数点以下も計算したいのです。この場合は
double=「これから使う変数は実数型です」(正確には 8byte浮動小数点型)
これらを宣言すれば、自由な変数を設定できます。

(3)メインプログラム

double dcsr = (CurrentPixel.R - SecondaryColor.R);
double dcsg = (CurrentPixel.G - SecondaryColor.G);
double dcsb = (CurrentPixel.B - SecondaryColor.B);
double dpsr = (PrimaryColor.R - SecondaryColor.R);
double dpsg = (PrimaryColor.G - SecondaryColor.G);
double dpsb = (PrimaryColor.B - SecondaryColor.B);

double rar = 0;
double rag = 0;
double rab = 0;
if (dpsr != 0) rar =(255*(dcsr/dpsr));
if (dpsg != 0) rag =(255*(dcsg/dpsg));
if (dpsb != 0) rab =(255*(dcsb/dpsb));

int ra = 0;
if ((rar != 0)&(rag != 0)&(rab != 0))ra = (byte)((rar+rag+rab)/3);
if ((rar != 0)&(rag != 0)&(rab == 0))ra = (byte)((rar+rag)/2);
if ((rar != 0)&(rag == 0)&(rab != 0))ra = (byte)((rar+rab)/2);
if ((rar != 0)&(rag == 0)&(rab == 0))ra = (byte)(rar);
if ((rar == 0)&(rag != 0)&(rab != 0))ra = (byte)((rag+rab)/2);
if ((rar == 0)&(rag != 0)&(rab == 0))ra = (byte)(rag);
if ((rar == 0)&(rag == 0)&(rab != 0))ra = (byte)(rab);

CurrentPixel.A = (byte)ra;

CurrentPixel.R = (byte)PrimaryColor.R;
CurrentPixel.G = (byte)PrimaryColor.G;
CurrentPixel.B = (byte)PrimaryColor.B;
さっそく、CodeLabに書いていきます。
右のプログラムが
CurrentPixel = src[x,y]; 〜 dst[x,y] = CurrentPixel;
の間に入るわけです。今回は文字列にしたので、試してみたい方はコピペしてください。

長いですが、日本語プログラムで書いた事をRGB毎に処理してるだけです。
このプログラムで分かったんですが、dcsやdpsは整数でいいはずなんですが、intを宣言してしまうと、割り算のとき具合が悪いんです。良く分かってませんがそれでdoubleを宣言しています。if文はこのシリーズ2回目で使ったので、読んだ方は分かると思います。&はそのままアンド。論理積です。
つまり、
if (A&B) C 「AかつB なら Cの処理をしなさい」

スペースが余ったので、ちょい別のコマンドの説明も私が理解してる範囲で加えましょう。たぶん、もう大体は分かってると思いますが・・・

for (int y = rect.Top; y < rect.Bottom; y++)
forというのは繰り返し処理
y方向は上から下に繰り返してくださいみたいなことですね。
for (int x = rect.Left; x < rect.Right; x++)
x方向は左から右に繰り返してください
CurrentPixel = src[x,y];
カレントピクセルにxy座標のピクセルの情報を代入しなさい
dst[x,y] = CurrentPixel;
カレントピクセルの情報をxy座標のピクセルに代入しなさい

800x600だったら、480000回も繰り返してるワケですね。

 ところで、なんで「メインプログラム」としてるかってのは、これで確かに目的達成なんですが、実用としては、色々処理条件を付けたいわけです。これは、ワンクリックエフェクトなので、間違って2回掛けるとおかしな事になってしまいます。それを回避したり、処理を速める工夫をしたりしたいわけです。

今回のまとめ

●新しい変数を使う時は、型宣言する。
●int 4byte符号付整数型
●double 8byte浮動小数点型

if (A&B) C 「AかつB なら Cの処理をしなさい」

次回は処理条件を加えていよいよ線画抽出プラグインを完成させます!

前の記事に戻る  目次へ戻る  次の記事へ進む
inserted by FC2 system