ラベル image の投稿を表示しています。 すべての投稿を表示
ラベル image の投稿を表示しています。 すべての投稿を表示

2015年6月24日水曜日

ハフ変換(3)-ソースコードを見てみると。。。

それではググって、サンプルのソ-スでも見てみましょう。
とりあえず、ここあたりがソースとしてはわかりやすいです。
以下、抜粋です。
※<が漢字になってます。注意。
   //直線の場合 -------------------------------------------------
      int theta,rho;
      //直線検出用頻度カウンタ
      short[][] counter=new short[THETA_MAX][2*RHO_MAX];

      for(y=0;y<YMAX;y++)
         for(x=0;x<XMAX;x++)
            if(data[y][x]==1){
               for(theta=0;theta<THETA_MAX;theta++){
                  rho=(int)(x*cs[theta]+y*sn[theta]+0.5);
                  counter[theta][rho+RHO_MAX]++;
               }
            }

まぁ、中カッコ{}の有無が気に入りませんが、
まず、XとYの2重LOOPを回しています。
これは、『画像の全ての点』を対象にしているという意味ですね。

そして、その場所に該当データがあるかどうか、実際には点があるかどうかを判定し、
 点があった場合には、1024回のLOOPを回しています。
※THETA_MAXが1024 cs,snはsin,cosを再計算させるのを防止するため(CPU負荷がかかるから)、あらかじめテーブルにいれてあります。

同じ直線上の点であれば、 counter[角度][距離]が同じなので、
++でインクリメントしていきます。 まぁここまでは、だれが書いても似たようなコードになるでしょうかあr、特に問題はありませんですね。(o^^o) 

考えるとすれば、THETA_MAXが1024となっていますが、
この適切な値はどうやって決めるかという問題になりますね。

これには、ターゲットとする画像によって違うわけです。
ここでは、800*1200くらいに考えてみると、、、
ヨコ線が左右の端部で1ピクセル上下にズレた直線と仮定すると、
atan(1/800)→0.072度
180/0.072→2500分割くらい必要になります。

よって、上記のサンプルのTHETA_MAXはdefineされていますが、
本来はここは動的にするべきものとなりますね。 

さて、上記のrhoは、原点と直線の距離になるわけですが、
どのような値をとりえるかというと、実はマイナス値もとることがわけです。
はやい話、、こういう場合です。
 
では実際に、どのような値になるかを、
プログラムで確認してみましょう。

 つづく

2015年6月22日月曜日

ハフ変換(2)-そもそもハフ変換とは何か

よっぽど完成されたライブラリを使う以外は、 自分で理解できないソースコードは書くべきでないことは言うまでもありませんが、 そもそもハフ変換による直線の抽出はどうゆう理屈かという話の第一ステップを考えてみます。(o^^o)

では、点が4つあったと考えましょう。
これです。

さて点を1点以上通る直線は、無限です。
早い話、点Aを通る直線は、点Aを中心に0~180度
の分だけ、無限に引けるわけです。

点を2点以上通る直線が何本ひけるかというと、
4本です。


  
ここでどういう考え方をするかというと、
【点Aは直線1を通る点である】ということです。

また同様に、
【点Dは直線1を通る点である】ということです。

そうすると逆に、
【直線1を通る点は、A,Dの2点ある】ということです。

また、



【直線5を通る点は、Aの1点ある】ということです。
まぁAを通る直線は無限にあるわけですが、例として書きました。

これらを集計して、
【2点以上通る線のみ有効なものとする】
と判定できれば、直線1~4の4つが集計できるという、こういうわけです。

しかしながら、そもそも【直線1】をどう定義して、
【どのように判定したら点Aを通るとわかるのか?】という話が、本題となります。

ここで、以下の図のような考え方をするわけです。
 
●直線1とは、
・水平とのなす角度が『角度1』
・原点Oとの距離が『L1』
と定義できるのですね。

そうすると、例えば、、100ピクセル×100ピクセルの画像を考えると、
●角度は0~180度まで考えられる
●直線と原点との距離は、最大でも141ピクセルである
ということがわかります。
そうすると、とたえ1度刻みにしたとしても、
141×180=25,380コのデータが必要になります。
0.1度刻みにすれば、253,800コのデータが必要になります。
ようするに、、
data[141][180]
のデータを持って、それぞれいくつの点を通るかを判定するという、
ものすっごくベタなことをする、
それをするのがハフ変換なのです。

つづく




2015年6月20日土曜日

ハフ変換(1)-画像の角度を判定する

久々にまともな技術系な話です。
題名はハフ変換ですが、要するにこういうことです。
スキャナでスキャンした画像とかありますよね。
それを角度がついてるときに、戻したいって話です。
つまりこういうことです。
(1)スキャンした画像

(2)なんとなく角度を算定するために直線部分を検出する

(3)検出した角度から、正しい角度を予測して回転させる

まぁこんなことやりたいわけです。

まぁ、【ハフ変換】とかぐぐれば、なんとなくヒントがみつかるわけですが、
そして、サンプルのソースとかのってたりするわけですが、
はたまたOpenCVあたりのサンプルでも似たようなものがあったりするわけですが、
やってみればわかります。
そんな簡単にはいかないと。
では、どうしましょう。
まぁ元々技術的に無理がある話でもないので、ひとつずつ解決していきましょう。

ってことで、つづく(o^^o)


2014年7月5日土曜日

新しいことを始めるときは。

新しいことを始めるとき。
今回は、AppMethodに挑戦なのですが、
プログラミングに限らず、新しいことを始めるときは、
大きく2つの方法があると思ってます。
(1)基礎から徹底的に学ぶ
(2)何かの目標をつくって、その最短距離を学ぶ。

もちろん、子供が勉強するかのごとく、(1)は良い方法というか、王道です。
しかし、大人がピアノを習うときは、(1)だと絶対めげちゃうんですよ。
だから、(2)なのです。
ハノンもやらず、『ピアノのテクニック』もやらず、
【弾きたい曲をなんとか仕上げる】
という方法をとるべきです。

ということで、【つくってみたいアプリをとにかく完成させる】ことを目標とし、
(2)の方法でAppMethodを学びます。
そして、都度、必要に迫られたときに、基本的なことを学びます。

ツール類は、最低条件として、ひととおりつかえることが大前提なので、

●STEP1
Wizardにでてくるものは、ひととおり、何もせずに(追加をね)、BUILDして実行してみる。
これです↓↓


●STEP2
『サンプル』として提供されてるものから、明らかに興味を引かないもの以外を、
BUILDして実行してみる、
これです↓↓


そこらへんを前準備としてやった後に、作りたいものを開始です。

私の場合は、最初に設計することをしません。
最後まで見通せるほど、普通の人間は、頭がよくないからです。

よく、SIerさんで、【要件定義】-【設計】-【詳細設計】-【実装】-
なんていうSTEPを、全て違う責任者がいたりして、
文書を書いてハンコをもらわないと進めなかったりして、
挙句の果てに、設計ミスがあっても戻れずに、
結果、ぐでぐでになるなんていうのは、
大きな企業ほどありがちです。
っていうか、あります。
っていうか、ありすぎます。

技術も流行りもめまぐるしくかわってる時代に、
いい年のおっちゃんがハンコを押したって、何もなりません。

まぁ結論から言えば、
設計書というのは、アプリが出来上がってから書くものなのです。

っと、話はそれましたが、
若い人には、
『常に美しい方法を選択し、感性でプログラムを書きながら、全体像を設計』
してほしいと思いますですね。
これでいいのです。
絵にすれば、こうです。(o^^o)










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年12月9日月曜日

ベクトル図形のANDをとってみる件。(2)

つづきです。
では、順を追ってみましょう。
まず最初に、直線を登録(setLine)しているので、
こっから、頂点と交点、即ち、直線の始点/終点/直線同士の交点を求めます。
交点の求め方とかは、ここらへんを参考にどうぞ。
そして、今度はそのY座標だけをArrayに入れて、SORTしてみます。
そうすると、こういう結果になります。
# 0 : 20.0000 - 10.0000  
# 1 : 40.0000 - 10.0000  
# 2 : 60.0000 - 20.0000  
# 3 : 36.3636 - 20.9091  
# 4 : 50.0000 - 30.0000  
# 5 : 10.0000 - 40.0000  
# 6 : 30.0000 - 40.0000  
# 7 : 60.0000 - 40.0000  
# 8 : 45.0000 - 40.0000  
# 9 : 40.0000 - 50.0000 
# 0 : 10.0000
# 1 : 20.0000
# 2 : 20.9091
# 3 : 30.0000
# 4 : 40.0000
# 5 : 50.0000

図で言えば、こういうことです。
ここで、Y座標は6コあることがわかります。
このY座標で水平な線を引き、
全ての登録した直線に対して、その直線で分割してしまいます。
ただし、水平な線は含みません。
ようするに、こういうことです。
データにすると、こういうふうになっています。
ここで、いくつかの値を一緒に登録しています。
(1)【tag】。分断する前の直線を一意に識別するための番号。
(2)【bup】。直線が、上を向いているか(終点のY座標>始点のY場表)どうか。
(3)【hnum】。その直線が、どの段(直線の中点がどのY座標の間にあるか)にあるか。
※全て、あとで使います。

# 0  tag:1  bup:1  bHz:0 hnum:1 x1:20.0 y1:10.0 x2:16.7 y2:20.0 x:18.3 y:15.0
# 1  tag:4  bup:0  bHz:0 hnum:1 x1:35.0 y1:20.0 x2:20.0 y2:10.0 x:27.5 y:15.0
# 2  tag:5  bup:1  bHz:0 hnum:1 x1:40.0 y1:10.0 x2:36.7 y2:20.0 x:38.3 y:15.0
# 3  tag:8  bup:0  bHz:0 hnum:1 x1:60.0 y1:20.0 x2:40.0 y2:10.0 x:50.0 y:15.0
# 4  tag:1  bup:1  bHz:0 hnum:2 x1:16.7 y1:20.0 x2:16.4 y2:20.9 x:16.5 y:20.5
# 5  tag:4  bup:0  bHz:0 hnum:2 x1:36.4 y1:20.9 x2:35.0 y2:20.0 x:35.7 y:20.5
# 6  tag:5  bup:1  bHz:0 hnum:2 x1:36.7 y1:20.0 x2:36.4 y2:20.9 x:36.5 y:20.5
# 7  tag:7  bup:0  bHz:0 hnum:2 x1:60.0 y1:20.9 x2:60.0 y2:20.0 x:60.0 y:20.5
# 8  tag:1  bup:1  bHz:0 hnum:3 x1:16.4 y1:20.9 x2:13.3 y2:30.0 x:14.8 y:25.5
# 9  tag:5  bup:1  bHz:0 hnum:3 x1:36.4 y1:20.9 x2:33.3 y2:30.0 x:34.8 y:25.5
# 10  tag:4  bup:0  bHz:0 hnum:3 x1:50.0 y1:30.0 x2:36.4 y2:20.9 x:43.2 y:25.5
# 11  tag:7  bup:0  bHz:0 hnum:3 x1:60.0 y1:30.0 x2:60.0 y2:20.9 x:60.0 y:25.5
# 12  tag:1  bup:1  bHz:0 hnum:4 x1:13.3 y1:30.0 x2:10.0 y2:40.0 x:11.7 y:35.0
# 13  tag:5  bup:1  bHz:0 hnum:4 x1:33.3 y1:30.0 x2:30.0 y2:40.0 x:31.7 y:35.0
# 14  tag:3  bup:0  bHz:0 hnum:4 x1:45.0 y1:40.0 x2:50.0 y2:30.0 x:47.5 y:35.0
# 15  tag:7  bup:0  bHz:0 hnum:4 x1:60.0 y1:40.0 x2:60.0 y2:30.0 x:60.0 y:35.0
# 16  tag:2  bup:1  bHz:0 hnum:5 x1:10.0 y1:40.0 x2:40.0 y2:50.0 x:25.0 y:45.0
# 17  tag:3  bup:0  bHz:0 hnum:5 x1:40.0 y1:50.0 x2:45.0 y2:40.0 x:42.5 y:45.0

これで、準備が整いました。
これから、ANDとかORの演算をすることになります。

次回につづきますが、きっと、ちょっとわかる人は、この図を見ればわかります。


つづく。



2013年12月8日日曜日

ベクトル図形のANDをとってみる件。

●図形を演算する話。
ベクタ画像を合成(演算)する件。のつづきです。
簡単に実装してみますです。
こぅいぅときゎ、まず、どう使いたいかをcodingしてみます。
とりあえず、こうしました。
aPolies A;
//1つめ
A.setLine(20,10,10,40);
A.setLine(10,40,40,50);
A.setLine(40,50,50,30);
A.setLine(50,30,20,10);
//2つめ
A.setLine(40,10,30,40);
A.setLine(30,40,60,40);
A.setLine(60,40,60,20);
A.setLine(60,20,40,10);
A.execute();
A.test();
で、test()の結果が以下です。
# moveTo [5] 36.36 20.91
# lineTo [5] 30.00 40.00
# lineTo [0] 45.00 40.00
# lineTo [3] 50.00 30.00
# lineTo [4] 36.36 20.91
それぞれのデータをグラフにすると、以下です。



実際には、7つのステップを踏んで、この座標を割り出しています。 それが、以下ですね。
# 0 : 20.0000 - 10.0000
# 1 : 40.0000 - 10.0000
# 2 : 60.0000 - 20.0000
# 3 : 36.3636 - 20.9091
# 4 : 50.0000 - 30.0000
# 5 : 10.0000 - 40.0000
# 6 : 30.0000 - 40.0000
# 7 : 60.0000 - 40.0000
# 8 : 45.0000 - 40.0000
# 9 : 40.0000 - 50.0000
------------------------
# 0 : 10.0000
# 1 : 20.0000
# 2 : 20.9091
# 3 : 30.0000
# 4 : 40.0000
# 5 : 50.0000
------------------------
# 0  tag:1  bup:1  bHorizon:0  x1:20.0 y1:10.0 x2:10.0 y2:40.0 x:15.0 y:25.0
# 1  tag:2  bup:1  bHorizon:0  x1:10.0 y1:40.0 x2:40.0 y2:50.0 x:25.0 y:45.0
# 2  tag:3  bup:0  bHorizon:0  x1:40.0 y1:50.0 x2:50.0 y2:30.0 x:45.0 y:40.0
# 3  tag:4  bup:0  bHorizon:0  x1:50.0 y1:30.0 x2:20.0 y2:10.0 x:35.0 y:20.0
# 4  tag:5  bup:1  bHorizon:0  x1:40.0 y1:10.0 x2:30.0 y2:40.0 x:35.0 y:25.0
# 5  tag:6  bup:1  bHorizon:1  x1:30.0 y1:40.0 x2:60.0 y2:40.0 x:45.0 y:40.0
# 6  tag:7  bup:0  bHorizon:0  x1:60.0 y1:40.0 x2:60.0 y2:20.0 x:60.0 y:30.0
# 7  tag:8  bup:0  bHorizon:0  x1:60.0 y1:20.0 x2:40.0 y2:10.0 x:50.0 y:15.0
------------------------
# 0  tag:1  bup:1  bHz:0 hnum:1 x1:20.0 y1:10.0 x2:16.7 y2:20.0 x:18.3 y:15.0
# 1  tag:4  bup:0  bHz:0 hnum:1 x1:35.0 y1:20.0 x2:20.0 y2:10.0 x:27.5 y:15.0
# 2  tag:5  bup:1  bHz:0 hnum:1 x1:40.0 y1:10.0 x2:36.7 y2:20.0 x:38.3 y:15.0
# 3  tag:8  bup:0  bHz:0 hnum:1 x1:60.0 y1:20.0 x2:40.0 y2:10.0 x:50.0 y:15.0
# 4  tag:1  bup:1  bHz:0 hnum:2 x1:16.7 y1:20.0 x2:16.4 y2:20.9 x:16.5 y:20.5
# 5  tag:4  bup:0  bHz:0 hnum:2 x1:36.4 y1:20.9 x2:35.0 y2:20.0 x:35.7 y:20.5
# 6  tag:5  bup:1  bHz:0 hnum:2 x1:36.7 y1:20.0 x2:36.4 y2:20.9 x:36.5 y:20.5
# 7  tag:7  bup:0  bHz:0 hnum:2 x1:60.0 y1:20.9 x2:60.0 y2:20.0 x:60.0 y:20.5
# 8  tag:1  bup:1  bHz:0 hnum:3 x1:16.4 y1:20.9 x2:13.3 y2:30.0 x:14.8 y:25.5
# 9  tag:5  bup:1  bHz:0 hnum:3 x1:36.4 y1:20.9 x2:33.3 y2:30.0 x:34.8 y:25.5
# 10  tag:4  bup:0  bHz:0 hnum:3 x1:50.0 y1:30.0 x2:36.4 y2:20.9 x:43.2 y:25.5
# 11  tag:7  bup:0  bHz:0 hnum:3 x1:60.0 y1:30.0 x2:60.0 y2:20.9 x:60.0 y:25.5
# 12  tag:1  bup:1  bHz:0 hnum:4 x1:13.3 y1:30.0 x2:10.0 y2:40.0 x:11.7 y:35.0
# 13  tag:5  bup:1  bHz:0 hnum:4 x1:33.3 y1:30.0 x2:30.0 y2:40.0 x:31.7 y:35.0
# 14  tag:3  bup:0  bHz:0 hnum:4 x1:45.0 y1:40.0 x2:50.0 y2:30.0 x:47.5 y:35.0
# 15  tag:7  bup:0  bHz:0 hnum:4 x1:60.0 y1:40.0 x2:60.0 y2:30.0 x:60.0 y:35.0
# 16  tag:2  bup:1  bHz:0 hnum:5 x1:10.0 y1:40.0 x2:40.0 y2:50.0 x:25.0 y:45.0
# 17  tag:3  bup:0  bHz:0 hnum:5 x1:40.0 y1:50.0 x2:45.0 y2:40.0 x:42.5 y:45.0
------------------------
# 0  tag:5  bup:1  bHz:0 hnum:3 x1:36.4 y1:20.9 x2:33.3 y2:30.0 check:1
# 1  tag:4  bup:0  bHz:0 hnum:3 x1:50.0 y1:30.0 x2:36.4 y2:20.9 check:1
# 2  tag:0  bup:0  bHz:1 hnum:3 x1:33.3 y1:30.0 x2:50.0 y2:30.0 check:0
# 3  tag:5  bup:1  bHz:0 hnum:4 x1:33.3 y1:30.0 x2:30.0 y2:40.0 check:1
# 4  tag:3  bup:0  bHz:0 hnum:4 x1:45.0 y1:40.0 x2:50.0 y2:30.0 check:1
# 5  tag:0  bup:0  bHz:1 hnum:4 x1:30.0 y1:40.0 x2:45.0 y2:40.0 check:1
# 6  tag:0  bup:0  bHz:1 hnum:3 x1:50.0 y1:30.0 x2:33.3 y2:30.0 check:0
------------------------
# 0  tag:5  bup:1  bHz:0 hnum:3 x1:36.4 y1:20.9 x2:33.3 y2:30.0 check:1
# 1  tag:5  bup:1  bHz:0 hnum:4 x1:33.3 y1:30.0 x2:30.0 y2:40.0 check:1
# 2  tag:0  bup:0  bHz:1 hnum:4 x1:30.0 y1:40.0 x2:45.0 y2:40.0 check:1
# 3  tag:3  bup:0  bHz:0 hnum:4 x1:45.0 y1:40.0 x2:50.0 y2:30.0 check:1
# 4  tag:4  bup:0  bHz:0 hnum:3 x1:50.0 y1:30.0 x2:36.4 y2:20.9 check:1
------------------------
# moveTo [5] 36.36 20.91
# lineTo [5] 30.00 40.00
# lineTo [0] 45.00 40.00
# lineTo [3] 50.00 30.00
# lineTo [4] 36.36 20.91
つづく。

2013年3月12日火曜日

スマフォWEBアプリで写真をタイル表示させる件。(2)

ということで、タイル状に配置する件ですが、タテ長の場合、ヨコ長の場合に、
おおまかに言えば、【必ず入るように配置】の場合と、【部分的に正方形で表示】の場合が考えられます。まぁやりかたとしては大してかわりませんが、
【必ず入るように配置】は、以下ですね。



そして、部分画像と割り切って、正方形としてしまうのが、以下です。
スマフォのアプリなんかはこっちです。


もちろん、部分画像にした方が、切れてしまう部分はあるものの、
解像度としては高くなり有利ではあります。
でも、それは、写真の一覧だからであって、何かしらの選択の場合に、やはり全体をみて欲しいときもあります。その場合は前者になりますね。

どっちにしてもPHPでの実装は非常に簡単で、まず前者の場合。
【画像をLOAD】→【タテとヨコを測る】→【タテヨコ大きい方の数値で正方形の画像を生成】
→【LOADした画像を中央に配置】→【作成した画像をそのまま出力】
で完成。
後者の場合も、
【画像をLOAD】→【タテとヨコを測る】→【タテヨコ小さい方の数値で正方形の画像を生成】
→【LOADした画像を中央に配置】→【作成した画像をそのまま出力】

ということで、正方形にする時の辺の長さをどこをとるかだけの差です。
もちろんその場合に、サムネイル用の場合を考えれば、
あえて、縮小した大きさの正方形で作成するというのも、用途によってはアリですね。

ということで、PHPを。
エラー処理とかしてませんので(-_-;)

 // $fn:ファイルパス
 $localimage = imagecreatefrompng($fn);  // PNGの時
// $localimage = imagecreatefromjpeg($fn); // JPEGの時
 //------------------
 $ww = imagesx ( $localimage );
 $hh = imagesy ( $localimage );
 //------------------
 if($ww > $hh){
  $hh2 = $ww;
  $ww2 = $ww;
 }else{
  $ww2 = $hh;
  $hh2 = $hh;
 }
 //------------------
 $image   = imagecreatetruecolor($ww2, $hh2);
 $bgColor  = imagecolorallocate($image, 0x0, 0x0, 0x0);
 imagefill($image, 0, 0, $bgColor);   // 背景を塗ってから
// imagecolortransparent($image,$bgColor);  // 透明にしたいとき
 $bb = imagecopy($image, $localimage, 
   (int)(($ww2 - $ww)/2), (int)(($hh2 - $hh)/2),   // COPY先
   0, 0,            // COPY元
   $ww, $hh);
 if(is_resource ( $localimage )) imagedestroy($localimage);
 header('Content-type: image/jpeg');
 imagejpeg($img);

2013年3月8日金曜日

スマフォWEBアプリで写真をタイル表示させる件。

スマフォのアプリの写真をviewするやつなんか見ると、
当然のように、一覧表示があります。
一覧はだいたいタイル状になっていて、その中に画像が縮小されて表示されてる。
まぁ、あたりまえのコトのように実装されてますよね。


いや、でも、しかし、、、←今でしょう!とかじゃなくて、、、
これを、WEBアプリでやろうとすると、、いやぁー。。大変です。
そもそも、とある矩形にピッタリに入るように、タテまたはヨコを縮小表示させるなんて、
CSS的には、いくら探しても無いわけで。
じゃぁどうする!作りましょう!PHP側で。

ってコトで、2通り考えます。
ひとつは、jpegで表示。
ひとつは、pngで表示。
jpegだと透過できないので、bgcolorとあわせる必要がある。
pngだと容量が大きくなる。


 まぁ、使い分けを考えて両方できるようにしましょう。

つづく。

2013年1月15日火曜日

プロットされた点を曲線で結ぶ件。

久々の更新。 どーもFBには書くけど、BLOGの更新をしないようになってしまった。
今年はもうちょっと書こう。
 ってことで、ありがちだけど、プロットされた点を結んで曲線補完。  
今回の条件は、
【XによってYが一意に決まること。】
よって、曲線としては滑らかであっても、XによってYが2つ3つ値をとるようでは困るので、
 制御点の取り方を工夫。 

また、最初と最後(点A-B間、D-E間)に関しては、 制御点を2つ取れないので、
この部分は2次Bスプラインで考える。
1つの点を共用して3次(ベジェ)で考えてもいいのだが、
やってみたらあまり滑らかな線にならなかったので、2次とした。

 流れ。
A,B,C,D,Eが与えられる。
B,C,D(最初と最後を除いた点)については、その左右に制御点をとる。
制御点の取り方だが、Bを例にして考えるならば、B1-B-B2は一直線となり、
直線A-Cと平行になるような点をとる。
あとは、B1-B、B-B2の長さだが、B1-B2が直線A-Cの長さの固定倍とし、
Bが内分する点となるようにするのだが、 今回はX座標の比率(X(B-A):X(C-B)の比率となるように、 B1,B2をとってみた。 

まぁ所詮、曲線補完なので、何が正しいってわけじゃないので、
今回の要件はこの程度で。。

 2次のBスプラインの説明は、
http://opentype.jp/fontguide_doc2.htm
ベジェの説明は、
http://opentype.jp/fontguide_doc3.htm

がわかりやすい。と思う。思うだけかも。。

2012年11月26日月曜日

千社札再び。

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

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


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



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


図形(ポリライン)部分


文字部分


マスク部分


全体のObjectTree


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


2012年4月16日月曜日

FLEXで本のページをめくるように見えるGUIな件。の2

とりあえずの完成形。
こんな感じで。

なんでいまさらこんなのつくったのかって、、、だから、、、
10000ページでも右綴でも左綴でも天綴でもできるようにしたからです。


その1

その2

2011年9月8日木曜日

FLASHでGUIの4

HttpServer関連と、Loader関連をひとつのクラスにしようと思ったけど、
まぁ使い方を考えると、それぞれ別でもイイ気がしてきたので、別で。
基本はHttpServerと同じ。内部でLoaderクラスを呼ぶだけ。
Httpのときと同じように、ひとつずつ動作させる。
こんな感じで。
// ------------------------------------------------------------------------------
function spLoadImage(){
  own = this;
  init();
}
// ------------------------------------------------------------------------------
public function ldInit(e:Event):void{
  trace(e);
}
// ------------------------------------------------------------------------------
public static function getEnum(n:int):String{
  switch(n){
    case eNotBitmap:    return  "Not Bitmap Resource";
    case eIOError:      return  "IO Error";
    case eSecurityError:  return  "Securit Error";
    default:        return  "Undifiend Error";
  }
}
// ------------------------------------------------------------------------------
private function ldComplete(e:Event):void{
  trace(e);
  if(loader.content is Bitmap){
    var lp:Object = loader.content;  //  var lp:Bitmapだとエラー
    var bit:Bitmap = new Bitmap(lp.bitmapData.clone());
    lp.bitmapData.dispose();
    okFunction(localVal,bit);
  }else{
    ngFunction(eNotBitmap,  getEnum(eNotBitmap));
  }
  nextGo();
}
// ------------------------------------------------------------------------------
private function ldIOError(e:IOErrorEvent):void{
  ngFunction(eIOError,e.text);
  nextGo();
}
// ------------------------------------------------------------------------------
private function nextGo():void{
  trace("--nextGo--");
  loader.contentLoaderInfo.removeEventListener(Event.INIT,        ldInit);
  loader.contentLoaderInfo.removeEventListener(Event.COMPLETE,      ldComplete);
  loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR,    ldIOError);
  bRun = false;
  execute();
}
// ------------------------------------------------------------------------------
private function init():void{
  uVar  = new URLVariables;
  datAr  = new Array;
}
// ------------------------------------------------------------------------------
public function setData(url:String,  comtype:uint,  okRes:Function,  ngRes:Function,  args:String,method:String="POST"):void{
  datAr.unshift(
    {
      "url":    url,
      "comtype":  comtype,
      "okFunc":  okRes,
      "ngFunc":  ngRes,
      "args":    args,
      "method":  method
    }
  );
  //  Array  //  pop()  最後を取り出して削除  //  unshift(... args):uint 最初に追加
}
// ------------------------------------------------------------------------------
public function execute():void{
  setTimeout(executeSub, tInterval);
}
// ------------------------------------------------------------------------------
private function executeSub():void{
  if(datAr.length < 1){
    //  END
  }else{
    if(bRun){
      setTimeout(executeSub, tInterval);
    }else{
      trace("☆executeSub "+datAr.length);
      bRun = true;
      var o:Object  = datAr.pop();
      loader = new Loader;
      loader.contentLoaderInfo.addEventListener(Event.INIT,        ldInit);
      loader.contentLoaderInfo.addEventListener(Event.COMPLETE,      ldComplete);
      loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR,  ldIOError);
      var UR:URLRequest    = new URLRequest(o.url);
      if(o.method == "GET")  UR.method = URLRequestMethod.GET;
      else          UR.method = URLRequestMethod.POST;
      uVar = new URLVariables;  //  何か設定することになるだろう。
      uVar.args = o.args;
      UR.data=uVar;        //  
      okFunction  = o.okFunc;
      ngFunction  = o.ngFunc;
      localVal  = o.comtype;
      try{
        loader.load(UR);
      }
      catch (error:SecurityError){  //  err:Error
        ngFunction(eSecurityError,getEnum(eSecurityError));
      }
    }
  }
}
// ------------------------------------------------------------------------------

2011年5月20日金曜日

2次元グラフィックライブラリを作る的なはなし(3)

単純に横方向へスキャンするときの欠点。

横方向へスキャンをして、面積比でアルファチャンネルを決定するだけでも、
垂直に近い辺に関しては、結構キレイに表示できます。

元の線は、こんな感じですかねー。

しかしこのやりかただけでは、大きな欠点がありますね。
それは、水平に近いせんの場合です。

こんな場合ですね。


の部分が、ガタガタです。ね 


本当はこうしたいところなのです

単純なスキャンだけではすまなそうですね。
はい、ダメですね。
じゃぁ、どうしましょうねー。
どこに書いてあったってわけじゃぁないんですけど、私はこう考えました。

ずいぶん前に紹介した絶版の本、
によると、1ピクセルを、さらに細かくスキャンする方法、
例としては、4回スキャンして足し合わせるようなことが紹介されていました。
しかしそれでは、グラデっぽく表示されるピクセルが4ドットを超える場合に、
やっぱり、多少ガタガタしてきますね。
だからといって、数十回やればいいかっていう、交点を求めるコストを考えると、
それはやりすぎです。

ということで、1ピクセルの中で交点を、2点求め、直線補完をします。

今度はピクセルの中間ではなく、1ピクセルのはじまり近くと終わり近くの
2つの位置でスキャンし、交点を2つ求めます。
それが、A,Bですね。

そうしたら、下図のように色わけした部分(青・黄)部分の面積比(↓図のCに対する面積比)で、
アルファチャンネルを決定します。


こんな程度でも、結構、イイ感じになります。

まぁ、これはこれで問題があるにはあるんですけどね。^^;^^;




2011年5月18日水曜日

2次元グラフィックライブラリを作る的なはなし(2)

前回は矩形だから簡単だった。
でも三角形とかだったらどうなる。。?

こういうのを考えると、ピクセルを考えなければ
↓ 

でも実際は1ピクセル以下は無理だから、
↓ 

でもアンチエイリアスというのをかけて
↓ 

これも実は、前回と同じように、スキャンするのです。

でも前回と違って、スキャンの交点に、小数点が入ってしまいますね。

交点に小数点以下がなければ、
って感じですが、ある場合はこうします。
↓ 

即ち、交点を求め(A)、そこにタテ線を入れて(B)
その、色を塗る面積の比率で、色の濃さを決定します。
実際には、上図では、白と紫の中間の色のようになりますが、
実際はアルファチャンネルにするべきです。
そうでないと、キャンバス色が白でないと、おかしな話になります。

つづく。。。