ラベル アルファチャンネル の投稿を表示しています。 すべての投稿を表示
ラベル アルファチャンネル の投稿を表示しています。 すべての投稿を表示

2014年6月26日木曜日

画像を縮小する(平均化)する件。

アルファチャンネルにアルファチャンネルを重ねる件とも似通った話ですが、画像を縮小化する場合の処理を考えてみます。
簡単に考えるために、2ピクセルを1ピクセルにすることを考えます。
もちろん、□と■を平均化すれば、中間のグレイになるだろうことはだれでも予想がつきます。
では、R1G1B1A1とR2G2B2A2を平均するにはどうすればよいか。

正しい方法ではないですが、特殊解から一般解を導くとたいてい合っている!
という方法があります。
そうすると、
0x00FFFFFF と 0xFFFFFFFF を混合すれば、、
0x80FFFFFF と なりそうなことは容易に想像できます。
※または、0x7FFFFFFF
ということは、、アルファの値は単純に平均をとるだけでいいような気がするので、
いいということにしましょう。

問題は、RGB部分のFFFFFFというところです。
これだけみると間違った方法をとってしまいそうなので、
別の特殊解を考えて見ます。そう、
0x00FF0000 と 0xFFFFFFFF を混合しても、、
0x80FFFFFF と、同じ結果となります。
当然と言えば当然で、アルファが0であれば、RGBが何であっても
表示上は何も無いので同じわけです。

さぁ、ここで、これらを黒地に置いた場合を考えると、
0x00FF0000 → 0xFF000000
0xFFFFFFFF → 0xFFFFFFFF

そうすると、前回と同じように、V1、α1、V2、α2とすると、
V1=100、α1=0
V2=100、α2=1.0
結果→α3=0.5、V3=100
とすると、どうやら、(V1α1+V2α2)/2 が、黒地に置いたときの実際の値と思われるので、
V3=((V1α1+V2α2)/2)/α3
  =((0*100)/2)/0.5=50/0.5=100 という感じと予測できます。
V1=100、α1=0.5とすれば、
V3=(150/2)/0.75=75/0.75=100 となり、V3=100、α3=0.75となるので、
これでもあってるっぽいですね。

で、結論としては、
●αは単純に平均化するのみ
●RGBはそれぞれの要素に関して、αをかけたR´G´B´を足し合わせて平均化し、
平均化されたαで割り返す
となります。












2014年6月25日水曜日

アルファチャンネルにアルファチャンネルにを重ねる件(3)

で、前回、こんなことを書いたわけです。

--------------------------------------------------
●元ピクセル
値V1、α:alp1
●のせる方のピクセル
値V2、α:alp2
としたとき合成したピクセルの値(B)は、
A = (0xff * (1-alp1) + V1 * alp1) * (1-alp2) + V2 * alp2
alp3 = alp2 + alp1*(1-alp2)
B = (A - 0xff*(1-alp3))/alp3
--------------------------------------------------
しかし、ハタと気づくわけです。
または感じるわけです。
何かが美しくないと。(o^^o)

そうです。白(0xff)の上に画像を置いた揚句、
あとからその部分を取り除くような考え方をしているわけです。
じゃぁ、黒の上に置いたらどうなるか。
そうすると、0xFFの部分が0になるわけなので、
A = (V1 * alp1) * (1-alp2) + V2 * alp2
= V1 * alp1 + V2 * alp2 - V1 * alp1 * alp2(式1)
alp3 = alp1 + alp2 - alp1*alp2(式2)
B = A /alp3
と、こうなるわけですね。
式がそれっぽくなりました。

で、検算してみると、うん。正しいようです。


ここで、式を見てみます。
式2を見ると、最終的なアルファの値は重ねる順序に関係な無いことがわかります。
式1を見ると、最終的なピクセルの値は、順序と関係する事がわかります。
そりゃあそうです。
白100%の上に赤100%を乗せれば赤になるし、逆なら白になるに決まってますよね。

2014年6月24日火曜日

アルファチャンネルにアルファチャンネルにを重ねる件(2)

で、色が違うときの話です。
まず、サンプル。

では、重なり部分のR成分の計算。
0xff * 0.5 + 0xff * 0.5 = 0xff
0x80*0.3 + 0xff * 0.7 = 0xD9
では、重なり部分は、
(0xff * 0.5 + 0xff * 0.5) * (1-0.3) + 0x80 * 0.3=0xD9
うんうん。ここまではあってますね。

では、前回と同じように合成後のアルファを考えると、
0.3 + 0.5 * (1-0.3)→0.65となる。
0.5 + 0.3 * (1-0.5)→0.65.もちろん同じ。

0xff * 0.35 + A * 0.65 = 0xD9
よって、
0xD9-0xff*(1-0.65)→0xC5
これをRGB要素全てに適用すればできる。。ハズ。

また重なってない部分を考えると、
0.3 + 0 * (1-0.3) →0.3
0xff*0.7 + A * 0.3 = 0xD9
A=0xff、アルファ:0.3
うん。できてるな。(o^^o)

ということで、
●元ピクセル
値V1、α:alp1
●のせる方のピクセル
値V2、α:alp2
としたとき合成したピクセルの値(B)は、

A = (0xff * (1-alp1) + V1 * alp1) * (1-alp2) + V2 * alp2
alp3 = alp2 + alp1*(1-alp2)
B = (A - 0xff*(1-alp3))/alp3

となります。
地道に展開したらもしかして簡単になるのでしょうか。。。
やってみますわー。

っといましたが、かここであることに気付くわけです。
で、それは次回で(o^^o)

アルファチャンネルにアルファチャンネルを重ねる件。

アルファチャンネル付き画像に、
アルファチャンネル付き画像を乗せたときの結果が、
思わしくない状況に遭遇することがあります。
では、何が正しいのかをちゃんと考えてみます。
PHP画像合成のバグもこの範疇に入ると思われます。
ということで、計算しやすくするため、以下の画像を考えます。
これは、イラレで矩形を透過属性付きで作成したもので、
黒白はα=1、
右の赤の矩形はα=80%
左と中央の赤の矩形はα=40%
としました。
そして画面キャプチャをとって、ピクセルの値を調べた値を#テキスト表示しています。


■白の上に赤40%→#FF9999。
R要素→ FF*60%+FF*40% → 0xff
GB要素→ FF*60%+00*40% → 153 → 0x99
よって、FF9999

■黒の上に赤40%→#660000。
R要素→ 00*60%+FF*40% → 0x66
GB要素→ 00*60%+00*40% → 0x00
よって、660000

■白の上に赤40%、さらに赤40%→#FF5C5C
さてここの考え方が、全てのポイントである。
GB要素→ FF*A%+00*40% → 92 → 0x5C
よって、Aは36%である。
ということは、明らかに、60%*60%である。

ようするに、アルファ:40%の画像の上に、アルファ30%の画像を載せたときの
アルファは、
30% + 40% * (100-30)% → 30+28 → 58% となる。
逆にすると、
40% + 30% * (100-40)% → 40+18 → 58% となり、やっぱり同じである。

シンプルな話なんですけどね。。。。。。(o^^o)
ただし、サンプルは同じ色のアルファだけ変えたサンプルなので、
色が違うときは、、、次回で。(-_-;)

2014年6月16日月曜日

PHPの画像合成のバグ

PHPで画像の合成なのですが、、 もちろん、
imagesavealphaと
imageAlphaBlendingを設定して、
imagecopyしたものを、
imagepng したわけですが、
こんな結果です。
●1

●2

●3


全て、中央の画像に右の画像を合成したものが左の画像なので、
本来は全て同じハズです。
違いは、中央の画像のα=0のピクセルのRGBの値が3つとも違い、
上から、0x000000(黒) 0xff0000(赤) 0xffffff(白)です。
要するにBLENDするときに、α=0の部分ももってきてしまって、
BLENDした挙句、αの値の平均でもとっているのでしょうかね。
どっちにしても、ダメっすねー。
まぁきちんと解明してみますです。(o^^o)
とりあえず、こういうバグがあるということでした。

追記
本来はこうあるべきというの作成。

上段がイラレで作成、
下段が自前のプログラムで作成。
残念ながら、僅かな誤差がありましたが(3Fと40とか^^;)、
基本的にはあっています。



2013年1月18日金曜日

ghostScriptのラスタライズが正しく透過されない件。

何の話かというと、。。。。

ghostScriptでPDFをPNG出力させてみても、バックが正しく透明にならない。
-sDEVICE=pngalpha を指定しても、『中途半端に透過』しているだけなのだ。
図形があると、そのboundingboxの背景が白くなり、早い話使い物にならない。

ということで、これを正しく透過させるべく、いろいろやってみた。
もちろん、ghostScriptのソースに手をいれればいいのだが、再配布のときにめんどいので、ここではghostScriptには手を入れずにやってみる。

しかしながら、普通に考えたら無理なのである。
透過して白なのか、白い矩形があって白なのかは、ラスタライズされた画像をみても区別がつかないのである。
で、ここでは、どうやってそうするかは別にして、PDFの中のエンティティの色を全て黒くすればそれらしくできるのである。

もちろんPDFの中身のエンティティの色を全て黒にするとなると、それはそれで面倒な話ではあるが、ここではそれには触れない。

ということで、今回やりたかったことは、
↓この画像

を使って、
↓この画像

を生成することである。
そのためには、結論から言えば
↓こういう画像

が必要になるのである。


理論的には、。。。。

透過属性をもつPNG画像を、何かの画像の上に置くならば、その1ピクセルに着目すると、
PIXELの色は、
元々のPIXELの色*(1-PNGの当該PIXELのアルファ値)+PNGの当該PIXELの色*PNGの当該PIXELのアルファ値
である。<br/>
よってこれを逆算すればいいのだ。

以下は実際のコードのLOOPの中で、1ピクセルに対しての処理を行っている部分である。
unsigned int alp = bvalue ^ 0xff;

unsigned char* bp = (unsigned char*)pN;
unsigned int BB = bp[0];
unsigned int GG = bp[1];
unsigned int RR = bp[2];

int vBB = (0xff * (BB - 0xff + alp)) / alp;
int vGG = (0xff * (GG - 0xff + alp)) / alp;
int vRR = (0xff * (RR - 0xff + alp)) / alp;

if(vBB < 0)  vBB = 0; if(vBB > 255) vBB = 255;//いらない気が^^;
if(vGG < 0)  vGG = 0; if(vGG > 255) vGG = 255;//いらない気が^^;
if(vRR < 0)  vRR = 0; if(vRR > 255) vRR = 255;//いらない気が^^;

unsigned int vv = (alp << 24) | (vRR << 16) | (vGG << 8) | vBB;


2012年11月26日月曜日

千社札再び。

ということで、ひさびさの更新になってしまいまして(-_-;)

こんなのをつくってみました。


GUIで文字は入力しづらいので(マスクやらなにやらあるから。。)一括入力します。



分解するとこうなってます。


図形(ポリライン)部分


文字部分


マスク部分


全体のObjectTree


家紋の画像は、都度つくるのはめんどいので、
【グレイスケールPNGで作成して、階調をアルファチャンネルに変換して、RGBで色をのせる】
ということをやってます。


2011年4月30日土曜日

画像をぼかす。。とは?3

ということで、【キレイにボカす】ということで、ガウス消去ガウスぼかしをいってみましょう。
まず、これ↓がガウス分布というやつです。
どうやってつくったかっていうと、

double ar = 1.0; // 1.0  2.0  2.8
for(int a = 0;a<100;a++){
    double aa = (double)a / 10.0;
    double gausu = exp( - (double)aa * aa / (2.0 * ar * ar ) );
    printf("gausu:%.6f\n",gausu);
}
こんなことを書いて、テキストファイルにリダイレクトして、openOfficeの表計算に貼って、
グラフ化して、でもこれだとグラフは右半分だけだから、
グラフィック系S/Wで対象な図形にしたわけです。

これがどう関係するかということですが、
前回は、上下左右のピクセルの色をとってきて、それを平均化することで、
ぼかしてたわけです。
これを、上下左右を**ピクセルづつとってきたときに、
遠い方のピクセルの影響度を少なくしようという方法です。
上の分布のように、中心を100%として、中心のピクセルの影響度を100%としたときの、
周囲のピクセルの影響度を減らしていくのです。

この場合も、前回と同様に、X方向にガウスぼかしをしたあと、
Y方向にガウスぼかしをすることによって、
上下左右を一度にぼかした結果と等価になるのです。
なぜかはここでは問題ではないのです。

ということで、やってみると、、、





やっぱりキレイにボケてます。
ちなみに前回のガウスぼかしじゃないやつ↓


ただし、欠点もないわけではなく、同じようなボケ精度(どのくらい判別できないか)
という点で考えると、ガウスぼかしの方が範囲を広くとらないと、
シャープになってしまいます。
よって範囲を広く取ると計算量が増えるわけですね。
もちろん、部分的に使うという使い方もできるので、
そうすれば計算量はたいして増えませんよね。

まぁでも、用途によってそこらへんは使い分ければいいわけなので、
この話はこのくらいで。

ちなみに、逆ガウスぼかしをしてみたところ、、
(※離れるほど重みを増やす)
そもそも何をやってるかわからなっちゃいましたね
↓↓↓↓↓






2011年4月29日金曜日

画像をぼかす。。とは?2


前回はモノクロだったので、カラーに適用するとこうなりますよ、、的な話。
もちろん、平均はR,G,Bと別々に平均をとりますよ。
αチャンネルはどうしましょうかね。まぁとりあえずは元のままでいいでしょう。
(平均を取らずにもとのピクセルのα値をそのまま使用する)
但し、正しくはというか、画像の合成という話題になったりすると、
αチャンネルをもつ画像同士の合成なんかを考えなきゃならないので、
そのとき考えましょう。
このへんのことは、正しく処理されていないS/Wをときどき見かけますんで、
っていうか、自分でも何度もバグを出しましたけどね^^;^^;^^^;

ってことで、あとはサンプルの画像のみで。







↑これがガウスぼかし(半径15)
カラーですると、単純に前後左右で矩形で平均をとったものと
歴然の差がでますね。^^;^^;^^;
なんか、、【キレイにボケてる】感があります。

うん。しょうがない。次回はガウスぼかしだな^^;^^;


画像をぼかす。。とは?

画像をぼかす。。って、ちょこちょこ書いてたわけですが、
技術的には、非常に簡単な考え方です。

下図の黒い部分がドット(ピクセル)だとすると、
その周辺を含めた平均を出し、その値に変換してしまうのです。
要するに、赤いピクセルに着目し(下図の)、
その周辺、たとえば前後左右を1ピクセルずつ考慮すると、
9ピクセルの矩形が考えられます(下図の)。
これの平均を出して、変換後のピクセルの色にしてしまうだけです。
この画像を前後左右1ピクセルを考慮すると、
こうなります。


どうようにして、こんな画像を用意して、
影響範囲を前後左右1ピクセルの3*3=9ドットの平均すると
↓ 
影響範囲を前後左右2ピクセルの5*5=25ドットの平均すると
↓  

影響範囲を前後左右3ピクセルの7*7=49ドットの平均すると
↓  
影響範囲を前後左右4ピクセルの9*9=81ドットの平均すると
↓   


影響範囲を前後左右7ピクセルの15*15=225ドットの平均すると
↓    

影響範囲を前後左右15ピクセルの31*31=961ドットの平均すると
↓   


っとこれでわかるように、演算量は、2乗に比例するような気もするのですが、
実は、タテと横を別々に行っており、最初にヨコでぼかした画像を生成し、
それをさらにタテにぼかして画像を生成します。
よって、計算量は、2乗ではなく、2Nですかね。。

間単に、【平均】なんて書いてしまいましたけど、平均のとりかたに、
重みをつけたりすると、微妙に結果がかわります。

そこらへんは、、、まぁ、いろいろ調べてみましょう^^;^^;
その中のひとつに、【ガウスぼかし】なるものがあります。
まぁたいしたものでもないのですが、たまにはSRCを乗せると。。
二重のLOOPを2回まわしてるだけですね。^^;
int minusv = (haba - 1) / 2;
    for(int yy = 0;yy<hh;yy++){
        for(int xx = 0;xx<ww;xx++){
            for(int z=0;z<haba;z++){
                int idxx = xx + z - minusv;
                int idxy = yy;// + z - 2;
                if(getPixelIndex(G,idxx,idxy)){
                    uc[z] = G.GetPixelsDirect3(idxx,idxy);
                }
            }
            unsigned int ucc = getBock5(uc,haba);
            GO.SetPixels32(xx,yy,ucc);
        }
    }
    for(int yy = 0;yy<hh;yy++){
        for(int xx = 0;xx<ww;xx++){
            for(int z=0;z<haba;z++){
                int idxx = xx;// + z - 2;
                int idxy = yy + z - minusv;
                if(getPixelIndex(GO,idxx,idxy)){
                    uc[z] = GO.GetPixelsDirect3(idxx,idxy);
                }
            }
            unsigned int ucc = getBock5(uc,haba);
            G.SetPixels32(xx,yy,ucc);
        }
    }

2011年4月23日土曜日

【αちゃんねる】な件。3

さて今回は前回のつづきで、グレースケール画像のふちどりです。
これは、こういうことをしたいからですね。
(※画像がちょっとキタナイのは圧縮の高いJPEGだから^^;)

これも、もと画像から作成することができます。
元画像
まず、元画像をボカします。
ここでは、ガウスぼかしをしました。
それがこれ
そうしたらこの画像の中の白でない部分を黒に変換します。
拡大してみると(下図)、入り組んだところは、こういう風になってるので、
この白に近いグレーも黒にします。



おどろくなかれ、白でない部分ってこんなにあるんですね。
しかし、白でない部分を黒にしたってことは、白と黒の2値、
即ち、#000000と#ffffffの2つだけなので、ギザギザになってしまいますね。
よってこれをさらにガウスぼかしします
そうこれを前回やった、グレースケールをαチャンネルに置き換え、
さらに白に乗せ、それを黒い画像に置くと、こうなります。

最後に、赤を乗せた葵の御紋をのせれば、
完成ですね。

で、千社札っぽくしてみます。
そんでもって、前回のと比べてみましょう。。
ぜんぜんイイっすね。ふちどりした方が。。^^;

ということで、、
【結論。】
【モノクロのグレースケール画像さえ準備すれば、
それに色をつけたり、ふちどりしたりしたパーツとして、
貼りこむことが可能である】

でもどうやって?という話はまた今度。