2009年4月26日日曜日

HUNT症候群続

さらに悪化するような事があれば入院の必要もあるが、軽症なのでとりあえず投薬治療で対処できるとの事。まあ良かった。ごっつい薬をもらってきた。

2009年4月24日金曜日

Hunt 症候群

Hunt症候群だそうで・・・。眠気と疲労倦怠感の方が何の進捗もないというのにややこしい病気を抱え込みたくないのだが、治すまでにけっこうな手間がかかるらしい。

2009年4月23日木曜日

何かいろいろ麻痺している

外耳炎っぽいのは耳の腫れ、リンパの腫れ、痛みともだいぶ収まったのだが、ひどい頭痛と眩暈が続いている。ただでさえろくな生活ができないというのに、全く人間的な生活ができなくなってしまった。

んでもって、昨日から、舌の左側が痺れた感じで感覚が弱く、喉の所で食べ物が留まってごっくんとできないとか、麻痺みたいな変な症状が。今日は左の目蓋の感覚が鈍くて顔を洗う時に変な違和感を感じたのだが、その後ずっと、瞬きの時に右の目蓋ばかり動くとか左目をぎゅっと閉じれないとかなんとも奇妙。あと、口と歯の間にたまったご飯粒なんかを水を口に含んで洗って飲み込む動作なんかも、いつもは造作もないのに、水を口に含んだまま複雑な動作をしようとすると左の口端から水が漏れちゃって服を汚してしまってみっともないことになる。そんなこんなで食事にすごく労力と時間がかかる。

炎症が神経まで行っちゃったのかなあ。外耳炎でこんな事になるのだろうか。

2009年4月21日火曜日

リンパの腫れ

もう三日ほど首筋のあごの付け根というか耳の下の辺りにしこりがあって腫れて痛い。何事かと思って調べてみたらリンパ節が腫れているみたい。耳もちょっと熱を持っていて一箇所チクチクして痒い所があるし外耳炎みたいな感じなのかなあ。それとも普通に風邪か。熱は(いつもの7.2度くらいの変な微熱を除いて)ないけれど。わりと咳が出て喉も痛い。それと昨日からはひどい偏頭痛。

2009年4月12日日曜日

ケーリー・ハミルトンの定理続

先日の記事の補足。

ちゅーわけで、行列Aは自分自身の固有値なんです、なにせA=Aだから、という話に見えなくもない。固有多項式を消滅させる事を言うのには、何らかの意味で固有値なのだと主張するのが文句なしに一番自然な方法だよね、というお話でした。

ねむいー

自転車で最寄り駅のショッピングセンターみたいな所まで行って1~2時間ほど歩き回るってのが以前は2~3日に一回くらいは取りやめていたのだが、主治医の助言もあって、ルーチンワークとして定着してきた。でも、毎日倦怠感や筋肉痛が積み重なっていく感じでひどい事になっている。でも、歩いたり自転車に乗ったりって事自体はちょっと息が上がり易いだけでそんなに負担じゃないんだよなあ。階段を数階分上がると脛から腿から酷い事になるけれど。むしろ身体を動かしている間は倦怠感を忘れられてかなりの気分転換になる。ただ、それで疲れが吹き飛ばせるわけでもなく帰ってからどっと疲れたり。後、何故か食事が凄い負担になって、何故か食事の後に凄く疲れる。

それと、一昨年の前半くらいまでは、歩いている間には眠気がわりと吹き飛ばせて頭がすっきりして、それでどうにか騙し騙し人生を回してこれたというのがあって、そもそも以前から割りと積極的に歩く事はしてきたのだが、近年、次第次第に歩いている間にも軽い眠気を感じるようになってきて、それが最近ではかなり強くなってきている。自転車をこいでいても目蓋が重くなるとか、本当に困ってしまう。

睡眠時間は、リズムに狂いがあるとは言え8時間くらいと十分にあるんだけど、やっぱり何処かまともに寝れていないんだろうなあ。高校の頃にいつのまにか、"眠くない時がない"というめちゃめちゃな状況が定着してしまってからこっち、コーヒーを飲もうともお茶を飲もうとも覚醒感とは本当に無縁の状態が続いている。思春期の頃は誰も彼もが授業中に居眠りしていたし、一貫校だから中学と高校の境辺りの大きなイベントというものがなかった事もあって、本当に、"気が付いてみたらいつのまにか"、という感じで、時期がはっきりしないんだよなあ。

2009年4月9日木曜日

ケーリー・ハミルトンの定理

ブラケット記法ってテンソル積を明示すると煩雑になるし省略すると混乱を生じ易くなるしで、テンソル積が絡むとおよそ良い所がない。で、テンソル積を明示 する事で劇的に見通しが良くなる例としてケーリー・ハミルトンの定理の証明を一つ。要点は、シュミットクラス作用素と、ヒルベルト空間とその双対空間との テンソル積空間のベクトルとの対応関係が、見た目で処理できるという事。物理のかけらもない線形代数のトピックでブラケット記法の良い所探しをするとか、 ニッチ市場にも程があるが。

以下テンソル積をxと書く。

n次元(実でも複素でも)縦数ベクトル空間をLとする。Lの上の線形写像の全体をMとする。Mはnxn行列の全体と見る事もできる。Mの任意の要素Aが、行列と見た時定義される自身の固有多項式を消滅させる事を示す。

まず、自明な式

AI=IA

に着目する。Mの要素に左からAをかける写像をL_A, 左からAをかける写像をR_Aとする。すると上式は

L_A(I)=R_A(I)

と書ける。

Mの要素を対応関係|φ><ψ|~|φ>x<ψ|及び線形性を手がかりにしてM'=LxL^*のベクトルに対応させる。特にLの数ベクトル空間としての標準基底を|i>とすればI=∑|i><i|には∑|i>x<i|が対応する。

L_A, R_AはLの上ではスーパーオペレーションだが、M'の上では線形写像だ。Mの上での作用として|φ><ψ|を(A|φ>)<ψ|や|φ>(<ψ|A)に写す事から、M'の上ではこれらはAxI及びIxAだ。すなわち、自明な式AI=IAからいつのまにか非自明な式

(AxI-IxA)∑|i>x<i|=0,

が導かれた。(AxI-IxA)を行列成分の行列、∑|i>x<i|を横ベクトル成分の縦ベクトルと見なそう。(AxI-IxA)の余因子行列の転置を両辺にかけると、

diag(det(AxI-IxA))∑|i>x<i|=diag(φ(A))∑|i>x<i|=0,

を得る。ここでφはAの固有多項式。この結果はφ(A)の全ての行が横零ベクトルである事を示すから、φ(A)=Oが導かれた。

2009年4月7日火曜日

ねむいー

自転車に乗っていたり歩いたりしていても眠気を感じるのだが、最近それがやばいほど強烈になってきた。個別の症状以上に、この、少しずつ人生が削り取られていくような感覚には本当にまいってしまう。

2009年4月3日金曜日

体調

最近は滅茶苦茶強い筋肉痛という事はなくって、肉体的疲労についてはやっぱりCFSの事を意識しすぎていたのかなあ、などとも思っていたのだが、昨日今日と足に筋肉痛がずっとある。そして腕と足の倦怠感が凄い。

それと頓服で出されていたドグマチールがなくなってから、二日間入眠に苦労したが、一昨日、昨日と夜すんなりと寝付けた。ただ起きるのは変わらずに13時過ぎ。やっぱドグマチール関係ないのかなあ。今までも、大体冬~春にかけては不眠が解消されて起きる時間は変わらずに過眠傾向になる事が多かったし。

で、起きた後も強烈な眠気で何も手に付かない。うとうとしちゃうわけでも居眠りしちゃうわけでもないんだけど、やっぱり眠気としか言いようがないんだよなあ。

せめて年末年始に数ヶ月間経験した(その時の処方はリボトリールとドグマチール)、日に数時間活動的な時間帯が得られるという状況がもう一度訪れれば(思えばそれで一念発起して「身も蓋もない」開発とこのブログを始めたんだっけ)、もう少し人間らしい生活ができるんだけど・・・。このブログも体調の事ばかりで随分とつまらなくなってしまった。

2009年3月31日火曜日

ねむいー

目覚めが最近ない程すっきりしていて、あれ?ラッキーって思ったのだが、その後しばらくしてから強烈な眠気。それでもって腕から足からだるいし足はちょっと筋肉痛があるし、食事しただけで息が上がるし。

目がしょぼしょぼして、目蓋が重くて、目の奥から額からつーんとして、目蓋も鼻の頭も痙攣して、ひたすらに辛い。

ちなみに、睡眠時間は、夜12時前に寝たのに寝付けたのが朝5時。で8時くらいに目が覚めた後二度寝を繰り返して正午まで寝ていた。体感的には不眠と過眠の両方の記憶があるのだが、ちゃんと計算してみると睡眠時間自体は実は標準的という何年も付き合ってきたパターン。

2009年3月30日月曜日

ヘロンの公式の一般化

ヘロンの公式の任意のn次元で通用する一般化は、計量行列の行列式の平方根(ここまでで二次元ならば平行四辺形の面積、三次元ならば平行六面体の体積になる)をとって1/n!すれば良い。

で、何で計量行列の行列式の平方根を取るのかと言えば、一つの説明はベクトルn個を適当な正規直交基底で表示した成分を並べて作った行列の行列式の絶対値がこれらが成す単位格子の体積を与えるから。

で、さらにその理由の簡単な説明を考えたのだが、一番簡単なのは、完全反対称関数の自由度がたった1しかない事に訴える事。

まず、

V=V(x_1,x_2,...x_n)

という風に単位格子の体積をn個のベクトルの関数として書く。これは、大体線形なのだが、微妙に線形じゃない。しかし、

V'=V'(x_1,x_2,...,x_n), |V'|=V

なる各引数について線形な関数を作る事ができる。このようなV'はV'と-V'の二種類ある(この辺り、ちゃんと証明しようとするとめちゃめちゃ面倒なんだろうなあ)。

V'は各ベクトルについて線形なだけでなく、完全反対称だ。n個のベクトルの完全反対称な関数というものを任意に考えると、実は自由度は1しかない。つまり、

V'(x_1,x_2,...,x_n)=A_{i_1,...,i_n}(x_1)_{i_1}...(x_n)_{i_n}

と書いた時、係数A_{i_1,...,i_n}は完全反対称テンソルε_{i_1,...,i_n}に比例する。正規直交基底を引数に取った時、1または-1にならなければならない事から、比例係数は1または-1であると分かる。これは、V'がX_{ij]=(x_i)_{j}を成分とする行列Xの行列式またはその逆符号を取った物である事を意味する。

睡眠

頓服で出されていたドグマチールを駄目元で毎日飲んでいたら、その間は毎日夜寝つけていたのだが(だだ、起きるのは昼過ぎになってしまって、年末年始の数ヶ月に経験した、朝6時~8時に活動的な時間帯が生じる、という状況にはならなかった)もらっていた10錠がなくなったら、昨晩いきなり朝5時まで寝付けないというようになってしまった。今の医師も睡眠のリズムの改善はリボトリールのお陰だったのだろう、と言っているのだけど、もう何が何だか良く分からん。寝付けるにせよ寝付けないにせよ、起きている間の強烈な眠気も相変わらずだし。

2009年3月25日水曜日

体調

じっとしていると直ぐに目蓋が重くなって、目の奥や額がつーんとして、めちゃめちゃ辛い。目を閉じると目蓋や鼻の頭の痙攣も酷い。強烈な眠気としか言いようがないのだけど、うとうとしてしまうという感じではなく、ただひたすらに辛い。横になって枕に額を押し付けたり机に突っ伏したりと、ぐるぐる繰り返して、部屋の中にいると何も手につかない。高校の頃から徐々に、本とか文書という物を集中して読めなくなって、学業の上では、人と全然違う所で奇妙な苦労を強いられた。最近はもう、コンピュータに向かうのも一苦労、本どころか漫画を読むのも一苦労という感じ。

部屋の中では何にも手につかないし、座椅子やベッドでゆっくりしようとしても腕から足から強烈な倦怠感があって、それを吹き飛ばすために何かしら活動したくてたまらなくなって、ゆっくり休むという事ができない。そんなこんなで部屋にいても気が滅入るばかり。疲れやすいというのもこの強烈な眠気とともに主訴の一つである状況ではあるのだけど、別に動けないという程では全くない(運動会の次の日のような感じ)ので、最近ではほとんど毎日外出してショッピングモールを2時間ほど歩いて、それが最大の気分転換となっている。それでゾンビのようになって帰宅するのだけど。

休んでいる間に頻尿とか過敏性腸症候群とかが治まったのと、現在の薬の処方のせいか(やっぱりドグマチールが効くみたい?)不眠が解消されたのもあって、自分の状態が整理されてきた。その結果として最近になってようやく、すごく眼に疲れや神経症状が来てそれが一番困っている点だと認識できるようになってきた。

高校の頃から、授業に全く集中できずに寝てばかりいるようになったのだが、あれも、成長期の頃のうとうとする眠気と違い、目蓋が重くて眼の奥や額がつーんとして集中できず、机に突っ伏して額を腕に押し付けて集中力をかき集めては少しだけ授業を聞いて、また集中できなくなって突っ伏して、というのを繰り返していた。何だか眠気というのとも違うとは感じつつ、やはり言語化するならば眠気としか言いようがなく、睡眠のリズムの乱れもあって、睡眠不足による眠気として無理に納得していたが、どうもその認識は間違っていたのではないかと思う。

大学の頃からは決まって年に二回程、PCのディスプレイを見ると吐き気がする程に眼がおかしくなるという事を経験するようになった。そういう場合にはPCを利用する活動を最低限に制限しても2週間くらい経たないと回復しなかった。それに、普通の人と全く逆に、子供の頃には乗り物に酔わなかったのが、高校の頃から次第に電車やバス、車に弱くなっていった。最近は電車もバスも使っていないので偶に使う時もちょっと苦手程度だが、一昨年なんかはバスに乗っている間はずっと目を閉じていないと強烈な吐き気に苦しむ羽目になった。

次回の診察では、眠気と疲れ、という風に簡単に済ませてしまわずに、眼の症状、という観点から少し相談してみよう。まあ神経症状がこういう形で現れているという話ならば、眼科で検査・治療を受けてめでたしめでたしとなるという風にはいかないのだろうけれど。

2009年3月13日金曜日

イベントの多重登録

以前、デリゲートの等価性は、呼び出し先のメソッドが同じかどうかで判断される事を書いた。

ところが、
Button1.Click+=new EventHandler(foo);
Button1.Click+=new EventHandler(foo);
すると、ボタン一回のクリックに対してfooが二回呼び出される。等価なメソッドの多重登録が許されているという事。ちょっと吃驚した。面白い仕様だ。

でも、デリゲートの等価性の帰結として、この二つのイベントハンドラを削除する時、右辺で生成したデリゲートへのインスタンスへの参照を保持しておく必要はない。
Button1.Click-=new EventHandler(foo);
Button1.Click-=new EventHandler(foo);
でOK。このために、

Button1.Click+=foo;
Button1.Click-=foo;
でfooが(右辺でEventHandlerの別々のインスタンスが暗黙に生成されるにも関わらず)削除されるんだね。

昼夜逆転

やっぱ、昼夜逆転しちゃったなあ。外出した事もあって、あまりに眠いしだるいしで、9時には寝たのだが、夜12時過ぎたくらいに目が覚めて、それから何時間もちっとも寝付けない。でも、起き出して見ると、強烈に眠くて、結局もう一度寝て、朝方に寝付けて正午まで寝ているという、投薬治療を開始するまで何年もずっと付き合ってきたパターン。

結局今日も、昼から起き出してもめちゃくちゃ眠いし、足から腕から運動会の次の日のようなだるさ。軽い筋肉痛というか張りというか・・・

以前は、夜のどうせ眠れないし少しだけ集中し易いしという時間帯を使って、アクティブな活動をほんの少しでもやっていけたのだけど、最後の方では、電気ストーブを火傷しそうなくらい顔に近付けて、ようやく集中力が確保できるという状況で、事実上は1年半前には完全に破綻していた。それから半年良く無理が通ったものだと思う。

2009年3月11日水曜日

CFSじゃない、病名も付かない、だそうで・・・。まあポジティブに、ポジティブに。

これまでの経緯
  • CFS(慢性疲労症候群)の専門医であるH医師の診察を受ける。身体表現性障害だろう、と言われ、T医師を紹介される。
  • T医師からは厳密には身体表現性障害ではない、と言われ現在の主治医の下での経過観察を続けるように言われる
  • 現在の主治医が匙を投げるそぶりを見せだす
  • H医師にもう一度かかる(今日)。病名は付け難いのだろうが、いずれにせよ精神科・心療内科での治療が適切で、特に認知療法で効果が出る可能性が高い、似たケースで数ヶ月で劇的に治癒したケースをたくさん見ていると言われ、改めてT医師を薦められた。
こちらとしても、病名を付ける事よりも治療の方針をしっかり立てたい、という希望をはっきりと伝えた上での受診だったので、T医師にお世話になれば何とかなりそう、というのは良いニュース。ただ、このまま病名宙ぶらりんというのはやっぱり外聞がなあ。

いくつかの論文を見たところ、H医師は、いくらCFSが除外診断とは言え、他にあてはまるものがない場合の落ち着きどころとしてCFSとの診断をポンポン出した上で標準的な投薬治療を機械的に行う類のCFSの専門医とは異なり、自身の経験を豊富に持っていて、それに基づいて診断・治療法の提案を明確に行える医師であるようだ。自分としても、病名を付けてもらう事よりも治療法についてのアドバイスをして欲しい、という趣旨での受診だったので、この医師の診察を受けたのは正解であったと思う。

多分私のようなケースは、機械的に診断基準を適用するならば(主治医からは精神疾患にあたらないと断言されているし)CFSに確かに該当してしまうケースなのだろうが、それでもH医師の経験から、CFSではなく、かつ、心療内科での認知療法による治療が有効なケースという判断になったという事だろう。そういう、私のような状況を多く見ているという経験の豊富さを感じる事ができたので、今の主治医の診察と比べて格段の安心感を感じる事ができた。

他のCFSの専門医にCFSと診断されるまでドクターショッピングを繰り返すならばどこかでCFSと診断された可能性が高いと思うが、そうしたら無闇に経過を長期化させる事になってしまったかもしれない。治療という観点からは今日の診察で最良の方向性が示されたのだと思う。ただ、やっぱり病名が付きそうにないのが精神的に辛い・・・。

海外みたいに、CFSという疾病概念を、思い切って病態として拡張してしまってくれれば良いのに・・・。でもまあ、そうすると、精神衰弱とか自律神経失調とか濫用される病名をまた一つ増やす事になって医学的には好ましくないんだろうなあ。CFS自体は、海外では遺伝子のオンオフの異常という事で確定診断の仕方も含めて決着が付きつつあるらしいし。


それにしても、診察を受けて帰ってきたら肉体的疲労と精神的疲労がすごい。本当に、運動会の直後状態。いつもは運動会の次の日状態と万年徹夜明け状態で、それが少なくとも高校の頃から徐々に悪化していって、もう立ち行かなくなった。これで誰にも助けてもらえないのなら、神様を恨むよ。

2009年3月10日火曜日

昼夜逆転

昼頃まで起きられず、夜になるまで強烈に眠いのに、夜になって布団に入っても何故か3~4時くらいまで眠れないっていう感じになってきた。昼夜逆転のリズムが完全に戻り始めている。

これは医者にかかるまで年々ひどくなって、大学時代なんかは朝の5~6時くらいにならないと眠れなかったからなあ。

睡眠のリズムが改善したのがリボトリールを処方された直後だったので、前回また睡眠リズムが戻りだしたと相談したら医者はリボトリールを増やす処方にしたのだけど、やっぱりドグマチールの効果だったんじゃないかと思う(ドグマチールをエビリファイに変えてからリズムがまた狂いだした)。次回にはそう相談しよう。

2009年3月4日水曜日

アッカーマン関数続

インデックスを一つの構造体で管理するようにしてすっきりした。

struct index2D
{
public int m, n;
public override int GetHashCode()
{
return m ^ n;
}
}
static Dictionary<index2D, int> Ack = new Dictionary<index2D, int>();
static int Ackermann(index2D mn)
{
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
watch.Start();
Stack<index2D> arg = new Stack<index2D>();
arg.Clear();
arg.Push(mn);
while (arg.Count > 0) {
index2D index = arg.Peek();
index2D index_;
if (Ack.ContainsKey(index)) {
arg.Pop();
continue;
} else {
int value;
if (index.m == 0) {
value = checked(arg.Peek().n + 1);
} else if (index.n == 0) {
index_.m = index.m - 1;
index_.n = 1;
if (Ack.ContainsKey(index_)) {
value = Ack[index_];
} else {
arg.Push(index_);
continue;
}
} else {
index_.m = index.m;
index_.n = index.n - 1;
int n_;
if (Ack.ContainsKey(index_)) {
n_ = Ack[index_];
} else {
arg.Push(index_);
continue;
}
index_.m = index.m - 1;
index_.n = n_;
if (Ack.ContainsKey(index_)) {
value = Ack[index_];
} else {
arg.Push(index_);
continue;
}
}
Ack[index] = value;
continue;
}
}
watch.Stop();
Console.WriteLine(watch.Elapsed.ToString() + " elapsed.");
return Ack[mn];
}

いてー

今日も朝から疲労倦怠感、風邪気味の倦怠感、風邪気味の症状、微熱、どれも強かったのだけど、数日休んでばかりだったからか肉体的な疲労はそんなに強くなかった。それで午後から出かけて、いつも通り死にそうになって帰ってきた。でもその時点でも肉体的疲労は強くなくて、「脳の疲労」とでも言うべき一連の症状が強くなっただけだったのだが、夕食と風呂で負荷がかかった後、足がめちゃくちゃ筋肉痛。まじで痛え。

アッカーマン関数

2chのC#初心者スレに投稿したアッカーマン関数を計算するC#メソッド。
あからさまに宿題くさかったが、ジェネリッククラスのスタックを使って再帰をヒープで行う方法が提案されるなど、興味深かったので。

方針は、ハッシュを使って計算済みの値を保存し再利用する事で無駄な再帰を減らす事と、上記のヒープを使っての自前再帰。計算済みの値を再利用するので表作成にも有効。うちの環境(メモリ1GB)では
m=0でn=int.MaxValue-1まで
m=1でn=10000000くらいまで
m=2でn=5000000くらいまで
m=3でn=20まで
m=4でn=1まで
計算できた。

static Dictionary<int, Dictionary<int, int>> Ack = new Dictionary<int, Dictionary<int, int>>();

static int Ackermann(int m, int n)
{
Stack<int> argM = new Stack<int>();
Stack<int> argN = new Stack<int>();
argM.Push(m);
argN.Push(n);
while (argM.Count>0) {
if (!Ack.ContainsKey(argM.Peek())) Ack[argM.Peek()] = new Dictionary<int, int>();
if (Ack[argM.Peek()].ContainsKey(argN.Peek())) {
argM.Pop();
argN.Pop();
continue;
} else {
int value;
if (argM.Peek() == 0) {
value = checked(argN.Peek() + 1);
} else if (argN.Peek() == 0) {
if (Ack.ContainsKey(argM.Peek() - 1) && Ack[argM.Peek() - 1].ContainsKey(1)) {
value = Ack[argM.Peek() - 1][1];
} else {
argM.Push(argM.Peek() - 1);
argN.Push(1);
continue;
}
} else {
int n_;
if (Ack.ContainsKey(argM.Peek()) && Ack[argM.Peek()].ContainsKey(argN.Peek() - 1)) {
n_ = Ack[argM.Peek()][argN.Peek() - 1];
} else {
argM.Push(argM.Peek());
argN.Push(argN.Peek() - 1);
continue;
}
if (Ack.ContainsKey(argM.Peek() - 1) && Ack[argM.Peek() - 1].ContainsKey(n_)) {
value = Ack[argM.Peek() - 1][ n_];
} else {
argM.Push(argM.Peek() - 1);
argN.Push(n_);
continue;
}
}
Ack[argM.Pop()][argN.Pop()] = value;
continue;
}
}
return Ack[m][n];
}

2009年3月3日火曜日

体調

今日も、疲労的な倦怠感に加えて軽い吐き気を伴う風邪気味っぽい倦怠感があって、目が覚めてもずっと横になっていた。その後、起きて体温を測ったら37.4度。吐き気があるから食欲もなくてカップラーメンを買ってきた。

2009年3月2日月曜日

微熱

また37.5度。おい、37.5度で定着すんなよ。

でも今日は昨日程には辛くはない。ていうか久しぶりに快晴だったので外出してきた所。まあ、負荷がかかった分死にそうになって倦怠感も疲労も強いが、風邪気味特有の倦怠感ってのはない。

あと、すげー眩しいという神経症状は相変わらず。

食後とは言え、37.6度・・・。なんか珍しく胸を張って微熱と言える微熱になった。だから何だという話だが。

微熱

昨日は、朝方からいきなり37.5度で、億劫という意味の倦怠感に、風邪気味の、わずかな吐き気と熱っぽくて頭がぼーっとする、という意味の倦怠感が加わり、あまりにも体調が悪くて一日中寝ていた。そうやって全く体を動かしていないのに、夜少しすっきりしてきたら筋肉痛がめちゃくちゃ強くなって、しかも熱は全然引いていない。結局夜までずっと横になっていて、夜も早めに寝た(でも不眠気味で入眠に苦労した)。

別に肉体的疲労で立ち上がれないとかそういうのじゃないけど、一日寝ているというような事は今まで一度もなかったのでショック。

今日もこの時間帯に既に37.0度。午後にはもっとあがるんだろうなあ。既に少し気分も悪いし。

来週の診察で病名宙ぶらりん状態に決着が付けば良いんだけど・・・

2009年2月28日土曜日

日経強いなー

8000円より下での動きで日経の強さを実感するのもアレだが。

しかしこれ、いくらNYと連動せずに真面目に平均線まで反発したとは言っても円安を受けてのものだろうからなあ。市場の主体性が確立できているわけではない。このままアメリカを無視して自力で経済を上向かせて、輸出依存の体制も改善して、本来のセオリーどおりの株高・円高の形になるのは・・・一世紀くらい先?ていうかそんなの出来るの?

むしろ、一時的に見られた円安・株安というパターンがそのうち定着して、ずるずると日本経済が凋落という未来の方があり得そうだ。

2009年2月27日金曜日

三角錐のエッジを鋭くした

WPFの法線って、(面ではなく)頂点毎に指定されて、共有される頂点に対して何も指定しないと、面から計算した法線を自動的に平均化するのね。

というわけで、頂点を共有しないようにしたら、より三角錐らしくなった。
以前:

以後:

ミクさん:

コードも更新した。

2009年2月26日木曜日

継承される事が前提のクラスに、自分が何者か教える方法

スキップリストのライブラリを作る時、ノードクラスには、ユーザがカスタムして使う事を前提にバーチャルメソッドをいくつか用意した。この時、スキップリストのコンテナクラスは、アイテムを引数として受け取り、ノードを生成してリストに追加するメソッドを持つ。問題は、この、ノードの生成という処理はノードクラスの静的メソッドであるべきだという事だ。すると、ノードクラスの基底クラスに、型パラメタとして、``自分が何者であるかを教えてやる''必要が生じる。少し変態的なコードになるが、面白いテクニックなのでまとめておく。

まず、スキップリストのコンテナクラスはこんな感じだ。

public class SkipList<TItem, TNode>
where TItem:class
where TNode:SkipListNode<TItem, TNode>, new()
{
TNode header, footer;
public SkipList()
{
header=SkipListNode<TItem, TNode>.CreateHeader();
footer=SkipListNode<TItem, TNode>.CreateFooter();
...
}
public void Insert(TItem item){
...
TNode newNode=SkipListNode<TItem, TNode>.CreateNode(item);
}
}

んで、ノードクラスはこんな感じだ。

public class SkipListNode<TItem, TNode>
where TItem:class
where TNode:SkipListNode<TItem, TNode>, new()
{
private TNode thisNode;
private TItem item;
private bool isHeader, isFooter;
public static TNode CreateNode(TItem item)
{
TNode newNode=new TNode();
newNode.thisNode=newnode;
newNode.item=Item;
newNode.isHeader=false;
newNode.isFooter=false;
return newNode;
}
public static TNode CreateHeader()
{
TNode newNode=new TNode();
newNode.thisNode=newnode;
newNode.item=null;
newNode.isHeader=true;
newNode.isFooter=false;
return newNode;
}
public static TNode CreateFooter()
{
TNode newNode=new TNode();
newNode.thisNode=newnode;
newNode.item=null;
newNode.isHeader=false;
newNode.isFooter=true;
return newNode;
}
...
他のメソッド(TNode型での自身への参照が欲しければthisでなくthisNodeフィールドを使う)
...
バーチャルなメソッドとかバーチャルなプロパティとか
}

使い方としては、

MyNode:SkipListNode<MyItem, MyNode>{
オーバーライドしたりプロパティを追加したり
}
...
クラスの定義の中で
SkipList<MyItem, MyNode> myList=new SkipList<MyItem, MyNode>();
...

などと使う。

特に、SkipListNode<TItem, TNode>の定義中でthisと書くとその型はSkipListNode<TItem, TNode>型になり、TNode型にならないので、TNode型のthisNodeフィールドを用意する事で、実行時の余計なキャストが必要なくなる。さりげないがここ超重要。

私が知った事

物事は難易度に従ってシーケンシャルに並んでいるものではない。合理的であるか非合理的であるか、可能であるか、不可能であるか、二値の命題によって特徴付けられる。その真理値があまりに人と食い違うとコミュニケーションがとても困難になり、自分の人生、自分の選択について人に説明できないという事態が生じる。そのために、便宜的に難易度というシーケンシャルなパラメタが社会/コミュニティによって導入される。

問題になっているのが因果関係ならば自由意志によって対処できる。問題になっているのが統計であるのならば逃れる事はできない。しかし人は自分の人生が上手く行っている限りはあまねく人は自由意志によって行動を選択すると考えたがる傾向を持つ(例えば先日の麻生総理の医療費問題に関する発言)。

それでも私には、未だ出来る事はあるし、自由意志を用いる余地も存在する。人のキャパシティとは、かくも大きく、そして小さい。

2009年2月25日水曜日

股関節がいてー

雨が久しぶりに上がったので、無理に外出して、帰ってきて座って休憩して、そんでもって立ち上がったら、足に力が入らず、歩くのにすげー気合が必要だった。

眠気も強烈だし微熱は37.2度あって顔がほてるし喉はいがらっぽいしで、もうどうしてくれようか、状態。

その後、脱力感・倦怠感みたいなのは少しだけ(まあ元々常時持続しているわけだが)楽になってきたけど、その代わり股関節がすげー痛てえ。


薬の処方が変わってから、昼夜逆転気味のリズムが戻ってしまったので、それは良くない方向への変化だとは思うが、おかげで夜の時間帯には頭は少しだけすっきりしている。でも今日は外出したので、夜になってから筋肉痛がすごい。それと食後も微熱は引かないが、そもそも37.2度~37.4度の、微熱と胸を張って言えない謎な度合いの微熱。でも顔はほてってぼおっとする。

2009年2月24日火曜日

クオータニオンまとめ

クオータニオン関連のエントリ(1,2,3,4)がカオスなのでここにまとめる。代数的アプローチでなく、2x2複素行列の部分線形空間によるモデルを用いる具体的方法なので、全てを具体的に計算して確かめる事ができる。複素行列の転置共役を^*と書く。

SU(2)={U|UU^*=I, det(U)=1}の生成元を求める、すなわち、(I+εu)(I+εu)^*=I, det(I+εu)=1が一次近似で満たされるためのUの条件を求める。それは、u=-u^*かつTr(u)=0。このようなuの全体は、

J=[i, 0]
[0,-i]

K=[ 0,1]
[-1,0]

L=[0,i]
[i,0]

で張られる実数係数線形空間となる。イメージとしてはSU(2)のIから``遠い''要素をIから``近い''要素I+xJ+yK+zL(x,y,zは小さい)の複数個の積によって作る事ができる。唯一つの生成元xJ+yK+zLのみを用いた積(I+ε(xJ+yK+zL))^(t/εくらいの整数)は、極限として、xJ+yK+zLの指数関数exp(t(xJ+yK+zL))を与える。xJ+yK+zLは有限次元でしかも対角化可能だから、指数関数と言う場合、対角化に基づく定義でもテイラー級数による定義でも良い。

あるいは、exp(t(xJ+yK+zL))という表式は生成元により次々とSU(2)の要素を生成していった結果に加法的にパラメタ付けした結果としてシンボリックなものと捉えても良い。つまりそれはexp(0(xJ+yK+zL))=I, (I+ε(xJ+yK+zL))exp(t(xJ+yK+zL))=exp((t+ε)(xJ+yK+zL))で特徴付けられる。この特徴づけから、極限に関する議論をうっちゃれば、exp(t(xJ+yK+zL))=(I+ε(xJ+yK+zL))^(t/εくらいの整数)が分かるので、指数法則exp(t(xJ+yK+zL))exp(s(xJ+yK+zL))=exp((t+s)(xJ+yK+zL))が得られ、指数関数のシンボルを使う事は正当化される。

なお、逆にSU(2)の要素も対角化可能なので、必ず、適当な生成元の指数関数で書けると分かる。


J^2=K^2=L^2=-I, JK=Lなどにより、I, J, K, Lが張る実数係数線形空間Mの中で積は閉じる。Mの要素をクオータニオンと呼ぼう。Mは、積について閉じているからとりあえずOが零でIが単位元である代数だ。

Mが積について閉じている事から直ちに次の事が言える。(I+ε(xJ+yK+zL))^(t/ε)の展開項、あるいはexp(t(xJ+yK+zL))のテイラー展開項のいずれもI, J, K, Lの種々の積に実数を乗じた形になっているために、exp(t(xJ+yK+zL))はI, J, K, Lの実数係数の線形結合で書ける。つまり、SU(2)はMに含まれる。

#Mは有限次元線形空間の部分空間だから当然閉じているので、収束する級数の部分和がその上にあれば、極限もその上にある。

Mが転置共役についても閉じている事、Mの要素たるクオータニオンとその転置共役との積が(wI+xJ+yK+zL)(wI+xJ+yK+zL)^*=(w^2+x^2+y^2+z^2)Iという風に数ベクトルとしての通常のノルムの二乗をIに乗じた結果を与える事に気が付けば、零行列でないクオータニオンに対して逆行列を作る公式を簡単に書け、しかもその結果はMの中に入る。Mは体であり、wI+xJ+yK+zLに対する共役をwI-xJ-yK-zLと、ノルムの二乗をw^2+x^2+y^2+z^2と定義すれば、逆数は共役/ノルムの二乗で与えられる。


exp(t(xJ+yK+zL))=a0(t)I+a1(t)J+a2(t)K+a3(t)L

と書いてSU(2)の生成元の指数関数のクオータニオンとしての形を具体的に求めよう。素朴なイメージとしては、(I+δt(xJ+yK+zL))を用いて、パラメタtをδtだけ進めていく事で、単位行列から近いexp(t(xJ+yK+zL))から次第に遠いexp(t(xJ+yK+zL))まで次々と求めていく事ができる。このイメージの数学的な対応は

(I+δt(xJ+yK+zL))(a0(t)I+a1(t)I+a2(t)J+a3(t)K)
=a0(t+δt)I+a1(t+δt)J+a2(t+δt)K+a3(t+δt)L ... (1)

から導かれる微分方程式を、a0(0)=1, a1(0)=a2(0)=a3(0)=0の下で解く事だ。

以下(x,y,z)をベクトルωで表し、(a1(t),a2(t),a3(t))をベクトルα(t)で表す事にする(ベクトルを明示するうまい方法がないのでここでだけギリシャ文字はベクトルを表すことにする)。しばしばtをオミットする。

(1)式から得られる解くべき微分方程式は

da0/dt=-ω・α...(2)
dα/dt=a0ω+ω×α...(3)

だ。左辺にそれぞれa0, a1, a2, a3を乗じて足しあげると0となる。これはa0^2+a1^2+a2^2+a3^2が不変である事を表す。t=0ではa0=1で他は0だから、連続性も考慮して、a0=sqrt(1-|α|^2)と書ける事が分かる。後はαの各成分だけ求めれば良い。αの微分方程式は今や

dα/dt=sqrt(1-|α|^2)ω+ω×α ... (4)

である。左からωを外積すると右辺第一項が消えて

d(ω×α)/dt=ω×(ω×α)...(5)

右辺はω×αに直交するから、|ω×α|は保存する。その値は初期条件から0である。とすれば、ω×αは零ベクトルである。この結果は保存則二つ分の情報を齎す。すなわち、αはωに比例し、解かれるべきパラメタとして比例係数のみが残る。α=f(t)ωとおくと、解かれるべき微分方程式は

f(0)=0,
df/dt=sqrt(1-f^2|ω|^2)...(6)

である。解はf(t)=|ω|^(-1)sin|ω|tである。結局、

exp(t(xJ+yK+zL))=cos(sqrt(x^2+y^2+z^2)t)
+sin(sqrt(x^2+y^2+z^2)t)(xJ+yK+zL)/sqrt(x^2+y^2+z^2)...(7)

が得られる。これはノルム1のクオータニオンで、逆にノルム1のクオータニオンは必ずこの形に書ける。つまりSU(2)の要素はノルム1のクオータニオンで逆もまたしかりだ。


最後に、x^2+y^2+z^2=1であるとして、純虚クオータニオンV=aJ+bK+cLを三次元ベクトルvと同一視した時に、(I+ε(xJ+yK+zL))V(I-ε(xJ+yK+zL))が、三次元ベクトルをω=(x,y,z)軸周りにだけ回転させる微小変換である事を示せば良い。つまり、vをv+2εω×vに写す事を言う。これでexp(t/2(xI+yJ+zK))とその共役で三次元空間ベクトルと同一視した純虚クオータニオンを挟む形の変換が三次元空間上の(x,y,z)軸周り角度tの回転変換として用いる事ができると分かる。

#この偶然に見える結果は、SU(2)とSO(3)の群としての2対1の対応関係に因っている。つまり、ベースとなっている事実は、|(x,y,z)|=1とした時SU(2)の要素exp(s(aJ+bK+cL))を((a,b,c)は必ずしも規格化されていなくてよい)別のSU(2)の要素によってユニタリ変換した結果exp(t(xJ+yK+zL))exp(s(aJ+bK+cL))exp(-t(xJ+yK+zL))が、``角速度ベクトル''(a,b,c)を右手系における右ネジ回転を基準として、(x,y,z)を軸として角2tだけ回転させるという事実だ。一般の3次元空間ベクトルも、純虚四元数と対応させてこの事実を流用すれば、回転を実行できるというわけだ。こうしてようやく、ベクトルを回転させる公式がクオータニオンとその共役で純虚クオータニオンを挟む形になっている事の奇妙さが解消される。ベクトルを直接回転しているのではなく、回転変換を別の回転変換とその逆変換で挟む``回転変換の回転変換''において``回転軸が回転される''事実が流用されているのだ。

なお、(x,y,z)が正規化されていない場合、(7)より、exp(t/2(xI+yJ+zK))は「角速度|(x,y,z)|で時間tだけ行った回転」と簡単に表現する事ができる。ただし、.NET Frameworkのクオータニオンのコンストラクタは軸ベクトルを無理やり正規化するので、軸ベクトルの代わりに角速度ベクトルを与えるという使い方はできない。これはMSDNでドキュメント化されていないので注意が必要だ。

2009年2月23日月曜日

微熱

37.2度くらいのびみょーな微熱がずっとある。

以前は、微熱は瞬間最大体温という感じで、むしろ低体温の場合が多かった。最近は寝起きと寝る前は極端な低体温だけど、日中~午後~夜の10時くらいまではずっと37度以上で、たまに36.8度の平熱くらいという感じ。でも、一番多く見るのが37.0度ぴったりで、高くても37.2度って感じで、ほーら微熱だぞー、という感じにはならないんだよなあ。顔がほてったりして集中力の欠如に輪がかかる場合には測ってみると結局平熱(36.8度)だったりするし。

ミクさんで拡張現実でWPFなWMPの視覚エフェクトプラグインの動画


視覚エフェクトの動画を作ってきた

朝から雨で気温も低いけど、37.0のびみょーな微熱とともにいつもと同じく倦怠感、目が腫れぼったい、霞む、集中できない、で家にいても何も手につかない。

居たたまれなくなって午後雨が収まってから無理に外出したらすぐにやばいほど疲れ果ててネカフェで休憩。それでふと思い付いて、PCカメラを借りて、最近製作したミクさんでARな視覚エフェクトのCamStudioによるキャプチャ動画を作ってみた。ネカフェのPCって、ゲームもできるようになっているから性能良いなあ。

ずっと座りながらの作業だったのにネカフェを出る時には足が棒のようで、目も一段と霞んで、歩くのに危険を感じる程だった。

というわけで散々苦労した挙句だが、ネット動画用の素材は揃った。

2009年2月22日日曜日

ね、ねむい・・・

ちゃんと睡眠時間は確保できているのに起きたら強烈に眠い。だるい。でも二度寝しても寝れない・・・

睡眠リズムが整った分、午後まで寝ていれば時間をやり過ごせるというわけにいかなくて却って精神的に辛い。

外国からのアクセスが増えている


いやはや、日本人より日本人じゃない人の方が当然多いわけだけど、ARToolkit+WPFの実験ページを英語で作ったら、いつのまにか海外からのアクセスの方が支配的になってきた。

ただ、Google Sitesって、何故か当のGoogleからのクロールにかかり難いんだよなあ。検索してみると、未だに古いタイトルで出てくる。だから``ARToolkitPlus WPF''で検索にかかりにくい。

2009年2月21日土曜日

ミクさんで拡張現実でWPFなWMPの視覚エフェクトプラグインを更新

更新内容は
1.IWMPEffects, IWMPEffects2をそれぞれ明示的に実装した。これで、過去のWMPでも使えるはず。
2.パラメタを少しいじった。

パラメタをいじったので、レジストリのHKCU/software/tomorrowplusplus/WMPEmitMiku以下を削除してから実行すると良いかも。

ここのWMPEmitMiku.zipです。

自分のPCだとパフォーマンスに難があるし、ネカフェだと.net framework3.5のインストールとかに時間がかかったりで、準備に時間がかかり過ぎる。

誰か、3D性能およびWEBカメラの性能の両方が良い環境の方、うまい音楽を選んでパラメタをうまくいじってCamStudioでのキャプチャ動画を作ってくれませんかねえ。CamStudioならスピーカ出力もキャプチャできるし(ただし何故かうちではできない)。

オイオイ

夜、寝られはするけれど、日中の眠気が強烈で夜になると少しすっきりするっていう、昼夜逆転気味の体内リズムが戻ってきたぞ。薬の処方が変わったせいかなあ。

2009年2月20日金曜日

ミクさんで拡張現実でWPFなWMPの視覚エフェクトプラグインを更新

更新内容は
1.パワーの対数を取るオプションを追加し、デフォルトでオンにした
2.インストールの方法に関する説明で、レジストリファイルの修正が必要な事に言及していなかったので言及した。

ここのWMPEmitMiku.zipです。

体調

今日は朝から体調激悪+微熱だったのだが、午後になってちょっとすっきりしてきた。以前は低体温=眠気で辛かったのだが、最近は微熱が体温調節の狂いというより風邪気味の症状の一環として出てくるので、普通に微熱が引く方が調子が良い。

追記:今日は眠気も風邪気味の症状も強いのに、妙にすっきりしているなあ。

ミクさんで拡張現実でWPFなWMPの視覚エフェクトプラグイン


最低限の形にはなった。指数分布に従って確率的にミクさんを放出し、その時に、帯域毎のパワーに応じて単位時間辺りのミクさん放出数を決めている。パラメタなどいじる余地がまだまだあるし、初速度を帯域のパワーと関連させるのもありだろう。

ここのWMPEmitMiku.zipがソース。バイナリも入っているので、readme-en.txtの指示に従えば遊ぶだけも可。

追記:設定をレジストリで持つようにしたら、平均ミク放出数の最小値と最大値を同じにしてしまって音楽に合わせた変化が生まれなくなってしまった。このバグは修正した。

2009年2月19日木曜日

.netアセンブリをCOMサーバとして登録する場合の注意

GUIDがあるんだから、同じ``名前空間.クラス名''のクラスでも問題ないと思ったら問題大有り。生成されるレジストリエントリは

[HKEY_CLASSES_ROOT\MyEffect]
@="WMPWPFTest.MyEffect"

[HKEY_CLASSES_ROOT\MyEffect\CLSID]
@="{F6ADD2D1-C4C0-4E4A-99DF-49AC5A90D23B}"

[HKEY_CLASSES_ROOT\CLSID\{F6ADD2D1-C4C0-4E4A-99DF-49AC5A90D23B}]
@="WMPWPFTest.MyEffect"

[HKEY_CLASSES_ROOT\CLSID\{F6ADD2D1-C4C0-4E4A-99DF-49AC5A90D23B}\InprocServer32]
@="mscoree.dll"
"ThreadingModel"="Both"
"Class"="WMPWPFTest.MyEffect"
"Assembly"="WMPWPFTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
"RuntimeVersion"="v2.0.50727"
"CodeBase"="file:///C:/Users/Ruke/Documents/Visual Studio 2008/Projects/WMPWPFTest/bin/Release/WMPWPFTest.dll"

[HKEY_CLASSES_ROOT\CLSID\{F6ADD2D1-C4C0-4E4A-99DF-49AC5A90D23B}\InprocServer32\1.0.0.0]
"Class"="WMPWPFTest.MyEffect"
"Assembly"="WMPWPFTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
"RuntimeVersion"="v2.0.50727"
"CodeBase"="file:///C:/Users/Ruke/Documents/Visual Studio 2008/Projects/WMPWPFTest/bin/Release/WMPWPFTest.dll"

と、実際に登録されるのはmscoree.dllだ。こいつが.netアセンブリをロードし、マーシャリングをする仕組みになっているっぽい。そこで、たとえGUIDがちがくとも、同じ``名前空間.クラス名''のクラスを複数登録すると、一つのCOMクライアントからそれらを同時に参照する場合に問題が生じる。多分、厳密な名前とやらを付与しても変わらない(未実験)。

そういうわけで、前のエントリで公開したWMPの視覚エフェクトのスケルトンをいじる時は名前空間やクラス名を変えて、その上でクラスのGUIDも変えるというステップを踏みましょう。

C#+WPFによるWMP VisualizerのスケルトンにIWMPEffects2を実装した

これで、他の視覚エフェクトと行儀良く共存できるようになった。
ダウンロードはこちらの WMPWPFTest.zip です。

C#とWPFによるWMPの視覚エフェクトのサンプル


サンプルコードはここのWMPWPFTEST.zipです。

今週はきわめて調子が悪いので、直ぐにというわけにはいかないけれど、これで、マーカ5つくらいを各周波数帯域に対応させてミクさんを音楽に同期して放出できる。周波数帯域の区切り方は対数で行うべきだろうな。

このコードでは、HwndSourceで作ったウィンドウをWMPの子ウィンドウにし、そのRootVisualを自作のユーザコントロールにしている。しかし、IWMPEffectsではこのウィンドウを破棄するタイミングがない。あくまでオンフレーム描画を念頭においた設計になっている。このために、一度この視覚エフェクトを選んでしまうと、他の視覚エフェクトを選んでもそのまま子ウィンドウが残ってしまう。

この問題は、IWMPEffects2では解決するようだ。CreateメソッドやDestroyメソッドが存在する。これもそのうち試してみよう。

C#+WPFによるWindows mediaplayerの視覚エフェクトのスケルトンができた


ようやっと上手くいった。HDCからウィンドウのHWNDをとり、そこからHwndSourceを作ってRootVisualを設定っていうAdam Nahanさんの方針はうまく行かなかったのは何とも謎。結局HwndSourceをコンストラクタから作ってHDCから取得したHWNDを親ウィンドウに、RootVisualを自作のUserControlにするという方針で上手く動いた。

スケルトンのソースはここのWMPWPFTEST.zipです。

それからCOMインタフェースを定義する方法をまとめておく。
1.IDLの中のinterfaceをlibraryで囲んで、midl /newtlb foo.idlのファイル名、でfoo.tlbができる。次にtlbimp foo.tlbでfoo.dllができる。
2.foo.dllをReflectorで開いて必要な定義をcsファイルにコピーし、HDCなど実質void *である型が_RemotableHandleになっている所をIntPtrに直す。

2009年2月18日水曜日

うわー

微熱が37.4度でずっと続いている。何かぼんやりすると思ったら。

HDCが_RemotableHandleになっちゃう

ので、idlの定義で
HRESULT Render([in] TimedLevel *pLevels, [in] int hdc, [in] RECT *prc);
とし、実際のRenderメソッドの実装で
IntPtr hdc_=(IntPtr)hdc;
としてみた。これ、unsafeコードじゃないんだねえ。

実際のIWMPEffectsはEffectMainPanelってユーザコントロールを作っておいて

[Guid("f6add2d1-c4c0-4e4a-99df-49ac5a90d23b")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("MyEffect")]
public class MyEffect : IWMPEffects
{
[DllImport("user32.dll")]
static extern IntPtr WindowFromDC(IntPtr hDC);
EffectMainPanel effectMainPanel;
IntPtr hwnd = IntPtr.Zero;
public MyEffect()
{
effectMainPanel = new EffectMainPanel();
}
#region IWMPEffects メンバ
...
public void Render(ref tagTimedLevel pLevels, int hdc, ref tagRECT prc)
{
IntPtr hdc_ = (IntPtr)hdc;
HwndSource.FromHwnd(WindowFromDC(hdc_)).RootVisual = effectMainPanel;
effectMainPanel.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
effectMainPanel.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
}
...
}

としてみた。で、上手くいかねー。もうちょっと調べてみよう。

C#+WPFのWindows Media Player視覚エフェクトのスケルトン

ここにWMPWPFTest.zipの名前で置いておいた。

解決

直前のエントリの問題は解決。
クラスをpublicにしていないだけだった。良かった良かった。

後は、
http://blogs.msdn.com/adam_nathan/archive/2005/10/24/using-com-interop-to-create-a-wpf-visualization-for-windows-media-player.aspx
にある。
Although IWMPEffects has several members, the most important one is Render. Windows Media Player calls your Render method regularly, giving you data about the audio, a handle to the device context (HDC) on which you need to draw, and a rectangle defining the bounds. Since WPF supports interoperability with HWNDs, I first needed to get an HWND from the HDC by PInvoking to WindowFromDC. From there, I could set my UserControl as the RootVisual for the HwndSource and update the UI appropriately. I think the obvious next step is to use data binding, but for now I've left that as an exercise for the reader. :)

に従ってWPFで視覚エフェクトを実装できる。

それから、その視覚エフェクトの登録の仕方は直前のエントリで書いた方法でregasmに作ってもらった.regファイルを(多分管理者権限が必要)登録した後、さらに
http://blogs.wankuma.com/melt/archive/2008/03/12/127425.aspx
に書いてある
この Effects キーのサブキーとしてアプリ名(今回の場合は WmpTest という名前にした)があり、そのサブキーに Property というキーがあります。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MediaPlayer\Objects\Effects\WmpTest\Property

このキーの値として classid というのがあり、ここにプラグイン本体の CLSID を記述します。

WMP はこの CLSID を使って ::CoCreateInstance をするので、CLSID に対応する dll や exe のパスもレジストリに記述しておく必要があります。

もレジストリファイルで用意しておいて登録する。これで万事OKのはず。

ところで、regasmを/regfileオプションで使ったり、HKLM\...\Effects以下への登録もregファイルを用いているのは、開発環境以外でも登録が容易であるようにするため。

なまじIDEに``COM相互運用機能の登録''なんて項目がある物だから、開発環境を使わずに登録する方法が分からずに苦労してしまった。

しかしこの方法で生成されたレジストリファイルには

[HKEY_CLASSES_ROOT\CLSID\{F6ADD2D1-C4C0-4E4A-99DF-49AC5A90D23B}\InprocServer32]
@="mscoree.dll"
"ThreadingModel"="Both"
"Class"="WMPWPFTest.MyEffect"
"Assembly"="WMPWPFTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
"RuntimeVersion"="v2.0.50727"

こんな事が書いてあるんだが、これ、GACに登録しないといけないなんて事ないよな?Assembly=の部分をフルパスにすれば良いんだよな?

いずれにせよ、インストーラは簡単な物を自作しなければいけなそうだなあ。

追記:
regasm /regfile /codebase ビルドされたdll
でフルパスでアセンブリを参照するレジストリファイルが作れる。
そうやって作ったレジストリファイルを管理者権限で登録して、上に書いたようにWindows Media Playerの視覚エフェクトに関するレジストリキーも登録して、コードの方でメソッドにとりあえずMessageBox.Show("Called!")とか書いておいたら、ちゃんと呼ばれた。良かった、良かった。

regasmって再配布してよいのだろうか・・・。

Windows Media Player 10 視覚エフェクトをC#+WPFで作る実験

自力でトライしてみたが、COMについて何も知らないので躓いている。
1.http://msdn.microsoft.com/en-us/windowsmedia/bb190309.aspxからWMP 10のSDKを落とす。
2.SDKのインクルードディレクトリにあるeffects.idlをWmpEffectsLib.idlにコピーして、

[
object,
uuid(D3984C13-C3CB-48e2-8BE5-5168340B4F35),
helpstring("IWMPEffects: Public interface for Windows Media Player SDK."),
pointer_default(unique),
oleautomation
]
interface IWMPEffects : IUnknown
{

から

interface IWMPEffects2 : IWMPEffects
{
HRESULT SetCore( [in] IWMPCore * pPlayer );
HRESULT Create( [in] HWND hwndParent );
HRESULT Destroy();
HRESULT NotifyNewMedia( [in] IWMPMedia * pMedia );
HRESULT OnWindowMessage( [in] UINT msg, [in] WPARAM WParam, [in] LPARAM LParam, [in] LRESULT * plResultParam );
HRESULT RenderWindowed( [in] TimedLevel * pData, [in] BOOL fRequiredRender );
}

までを

[
uuid(d282e51a-5bb9-46c2-aa73-decf29cba6f2),
version(1.0)
]
library WmpEffectsLib{
...中身...
}

で囲む。なお、uuidの生成にはuuidgenまたはguidgenを使う。
2.midl /newtlb WmpEffectsLib.idlしてWmpEffectsLib.tlbを生成
3.tlbimp WmpEffectsLib.tlbしてWmpEffectsLib.dllを生成
4.WmpEffectsLib.idlを見ながらconstやenumの対応する定義をするC#コードを作成。
後有用そうなコメントもコピー。

namespace WmpEffectsLib
{
static class WmpEffectsLibUtils
{
// These flags are to be returned in the GetCapabilities method. They
// indicate to the effects host what the effect supports
public const uint EFFECT_CANGOFULLSCREEN = 0x00000001; // can the effect go full screen?
public const uint EFFECT_HASPROPERTYPAGE = 0x00000002; // does the effect have a property page?
public const uint EFFECT_VARIABLEFREQSTEP = 0x00000004; // should effect return frequency data with variable size steps?
public const uint EFFECT_WINDOWEDONLY = 0x00000008; // can the effect render only in windowed mode?

public const uint EFFECT2_FULLSCREENEXCLUSIVE = 0x00000010;


public const int SA_BUFFER_SIZE = 1024; // number of frequency/waveform samples

enum PlayerState // audio playback states
{
stop_state = 0, // audio is currently stopped
pause_state = 1, // audio is currently paused
play_state = 2 // audio is currently playing
};

// The tagTimedLevel structure is passed to the Render() method of the effect. It holds the frequency,
// waveform and state data need to render the effect.
//
// The first dimension of each array corresponds to the channel: 0-left/mono 1-right(stereo only)
// The second dimension contains the sampled levels. The frequency data ranges
// from 0..255. The wave form data represents -128..127 but is stored as
// 0..255. The state contains the current audio playback state. The time stamp provides
// the relative time of these samples in the audio stream.
}
}

5.MyEffects.csに以下を記述

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WmpEffectsLib;
using System.Runtime.InteropServices;

namespace WMPWPFTest
{
[Guid("f6add2d1-c4c0-4e4a-99df-49ac5a90d23b")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("MyEffect")]
class MyEffect:IWMPEffects
{
#region IWMPEffects メンバ

public void DisplayPropertyPage(ref _RemotableHandle hwndOwner)
{
return;
}

public void GetCapabilities(out uint pdwCapabilities)
{
pdwCapabilities = 0;
}

public void GetCurrentPreset(out int pnPreset)
{
pnPreset = 0;
}

public void GetPresetCount(out int pnPresetCount)
{
pnPresetCount = 1;
}

public void GetPresetTitle(int nPreset, out string bstrPresetTitle)
{
switch (nPreset) {
case 0: bstrPresetTitle = "test";
break;
default:
bstrPresetTitle = "test";
break;
}
}

public void GetTitle(out string bstrTitle)
{
bstrTitle = "WMPEPFTest";
}

public void GoFullscreen(int fFullScreen)
{
return;
}

public void MediaInfo(int lChannelCount, int lSampleRate, string bstrTitle)
{
return;
}

public void Render(ref tagTimedLevel pLevels, ref _RemotableHandle hdc, ref tagRECT prc)
{
return;
}

public void RenderFullScreen(ref tagTimedLevel pLevels)
{
return;
}

public void SetCurrentPreset(int nPreset)
{
return;
}

#endregion
}
}

6.プロジェクトのプロパティのアセンブリ情報で``アセンブリをCOM参照可能にする''をONにする。
7.ビルド
8.regasm /regfile bin/releaseに生成されたdll でHKCR\CLSID\にクラス情報を登録するレジストリファイルを生成

この8.で「登録する型がないため、登録スクリプトは何も作成されません」と非情なお言葉。

もうちょっとCOM参照可能なクラスを作る方法を調べてみよう。

2009年2月17日火曜日

テスト

コメントが付いたらブログの表示がされなくなったぞ。なんでだ?

追記:ああ良かった。エントリを一つ作ったら表示されるようになった。
環境依存の問題かもしれないので、今後このブログが表示されていない事があったら、メールで知らせてくださいね。

Windows Media Player 10 視覚エフェクトをC#+WPFで作るサンプル発見。ただしアーカイブが壊れている。

メモ:WMP 10のSDKはhttp://msdn.microsoft.com/en-us/windowsmedia/bb190309.aspx。

ミクさん大放出をWindwos Media Playerの視覚エフェクトに仕立てようと画策中。
ただ、COMインタフェースで、かつ、C++前提のサンプルしかない。.NETのアセンブリからはCOMインタフェースを公開する事ができるようになっているので、理論的には問題はないが、自分の知識不足で敷居が高い(COMとの相互運用およびCOMそのものについて何も知らない)。

COM InterOpのエキスパートっぽいAdama Nahanさんが、
http://blogs.msdn.com/adam_nathan/archive/2005/10/24/using-com-interop-to-create-a-wpf-visualization-for-windows-media-player.aspx
こういう記事を書いていて、C#+WPFで視覚エフェクトを作るサンプルまでアップロードされているのだが、何故かアーカイブが壊れている。怪しい英語でメールを送ってみたのだが、対応してくれるだろうか。ブログも去年から更新していないし・・・

2009年2月16日月曜日

株価

GM(笑)状態であまり反応しないね。アメリカが経済の中心じゃなくなって円高&株高のセオリー通りの状態に戻るのも遠くない未来の事なのかも。でも、日本の景気悪化って、アメリカ発の経済危機が波及したってよりも、バブル崩壊から立ち直っていないのを数値でごまかしてきたのがごまかしきれなくなったってだけだしね。

中国とかが新しいビジネスの中心地になって、そこに依存する形の経済に落ち着いたりするのが未来のあり得るシナリオかな。IT関連での世界的な存在感のなさ(どうしてネットブック市場に乗り遅れるなんて高度な芸当ができるんだ、日本のメーカは)を考えると、依存する所かふるい落とされかねないけれどね。国が音頭をとって何かしない限りは、ずっとガラパゴスのままやっていく積もりなんだろうなあ、日本のメーカは。

体調

今日は、朝の1~2時間の少しだけ頭がすっきりする時間がなくなってしまった。朝からずっと横になっていた。最近、耐え切れないほどの筋肉痛というのは週に二日くらいで、それ以外は倦怠感という以上の物ではないのだが、朝~昼にかけて関節の曲げ伸ばしに非常に苦労する。体が縮こまった感じ。WikipediaのCFSのページには「朝の体のこわばり」という症状が書いてあって、イメージし難かったんだけど、こういう感じの事なのかなあ。本当に、CFSじゃないという形で、そして展望が持てる形で、決着が付いて欲しいのだが。まあ来月の診察まではあれこれ考えてもしようがない。

そしてやはり一番辛いのが毎日徹夜明け状態である事。筋肉痛も、例えば眠りが浅かったりとそれが強かった場合に合わせて強まる感じ。目から額からつーんとして、目が霞んで、目蓋が腫れぼったくて開けておくのが大変ですごい細目になって、寝込んでしまうわけではないけれど眠気が持続して、何をしようとしたかを簡単に忘れたり、集中力が極端に落ちていたり。投薬治療のお陰か、睡眠のリズムが整った分、寝ていれば一日が過ぎ去るというわけにいかず、却って精神的に辛い。(それでミクさんやってたりするわけだが。C#は高度に構造化された高級言語で機械的に扱えるのは本当に助かる。これがC++だったら・・・今の状態で扱うのはまず無理だ。拷問の類だ。)

それと、起き抜けに風邪気味の症状が強まるのだが、一日続くわけではなく、午後までには軽い咳・痰が続くだけになる。微熱というよりもむしろ36度を切るような低体温が続き、午後遅くに少し活動し易くなってくると、普通は平熱の36.8度くらいになるのだが、時々37度を越える事がある。寝る前にはまた36.5度くらいまで下がっていく。微熱というよりも、体内リズムと体温のリズムは一致して普通に上下しているのだが、その振れ幅が大きい、つまり体温調節が上手く行っていなという感じなので、仮にCFSだとして、いわゆる感染症類似の症状というやつじゃなくて、自律神経系の問題の範疇なのだろう。リンパの腫れもないし。

というか、医学的知識がないのでリンパの腫れってのもイメージできない症状なんだが。まあ圧痛があるというので、あれば自分でそれと分かるはずの症状なのだろう。そういえば、数年前までの数年間、ちょうど今よりも風気味の症状がめちゃくちゃ強いのが続いた期間(声が枯れるまで咳が2週間くらい続くというのが年に何度もあった)に、下あごの所に金玉のようにコリコリした物が左右二つできて、首を前に傾けるとそれが押されて痛かったりしたのだが、あれってリンパ節の腫れだったのかなあ。リンパ節の位置とかってさっぱり分からないんだよなあ。

2009年2月15日日曜日

XNA3.0のランタイム

メモ:
http://www.microsoft.com/downloads/details.aspx?FamilyID=6521d889-5414-49b8-ab32-e3fff05a4c50&DisplayLang=en

一様重力化ではトルクって0なのね

表題の通り。
重心を基準とした位置ベクトルをr_i, 質量をm_iとして重力加速度ベクトルをgとすると、重心を基準としたトルクは
Σr_i×m_ig=(Σm_ir_i)×g
だけど、Σm_ir_iは、今、重心が原点だから、零ベクトルだ。

何でこんな当たり前の事知らなかったんだろう。

というわけで、剛体の回転コードを追加したとしても、初期角速度ベクトルがそのまま保存するだけというつまらない事になる。空気抵抗を進行方向からの圧力として、面毎に計算するようにするとかすれば回転するようになるわけだな?でも、面積の計算とか面倒だなあ。ヘロンの公式だっけ?そんなのを使うのか・・・。それに、空気抵抗のパラメタの調節が面倒そうだなあ。

よし、ミクさんはとりあえずこの辺で終わりとしよう。

ミクさん大放出



2009年2月14日土曜日

がー。

まじかよ。回転行列の生成元って、交換子はパウリ行列の交換関係と一致するけれど、SxSy=Szみたいなのは満たさないじゃん(一方でパウリ行列は四元数の基底として正しく使用できる)。積が閉じない以上今まで書いてきた話は当然にして全部間違っていた。SU(2)とSO(3)の局所同型の話って、完全にLie群、Lie環の話であって、一方で四元数の話は、Lie群、Lie環の話とは全然向いている方向が違うんだなあ。

クオータニオン⇒回転の対応は、じゃあそんなに自然なものじゃなくて、二段構えになっているのかな?つまり、四元数が直接対応するのはIとパウリ行列を基底とする実数係数線形空間なわけで、SU(2)がその中に入っているのは、リー環がまさにパウリ行列で張られて、かつIを加えると(交換子のみならず通常の)積が閉じている、という議論から言えてこれは比較的自然なストーリーだ。一方でさらにトレースレスのエルミート行列と三次元ベクトルを
[z/2, x/2+iy/2]
[x/2-iy/2,-z/2]
みたいな感じで対応させるとこれもこの線形空間に入っていて、しかもSU(2)は``三次元ベクトルを回転''させる、という話が介在してようやく三次元ベクトルの回転に四元数が使えるわけだ。多分そういう二段構造になっている。

ラッキーな事に、対角化でなく加法定理を調べる事を選択したから、その部分はパウリ行列の線形結合の指数関数の話にそのまま使える。ただし、パウリ行列はブロッホ球上の``点''を角速度2で回転させるようなジェネレータだから回転時間が回転角の半分で済むので、θがθ/2の形で入るわけだ。

四元数がベクトルを回転させるのって、もっと自然な話だと思ってたんだけど、偶々としか言いようがないぞ、これ。高次元に多分拡張できないし。

まあいいや、なんとなくクオータニオンの使い方は分かってきたし。でも、剛体の回転シミュレーションはまだ敷居が高いかも。まあどうせ遊びだ。気長に気長に。

追記:パウリ行列でなく、それにiを乗じた物、つまり

J=[i, 0]
[0,-i]

K=[ 0,1]
[-1,0]

L=[0,i]
[i,0]

に単位行列Iを加えた物が四元数の単位要素と対応する。考えてみればこれらをSU(2)のジェネレータとして使う場合、iを乗じてから指数の肩に乗っけるのだから、以前の加法定理の議論を流用するには、当然そうあるべきだった。これでようやく、これら単位要素は二乗すると-Iになるという虚数単位と同様の振る舞いをするようになり、I, -I, J, -J, K, -K, L, -Lの間で積が閉じる。さらに行列としての共役転置も閉じ、これは四元数の共役と対応する。後でSU(2)と四元数の対応を書くが、ユニタリ行列の逆は共役転置なので、SU(2)に対応させた四元数の逆数は共役により簡単に得られる事になる。

3次元ベクトルと行列の対応は

[ix/2 , y/2+z/2]=xJ+yK+zL
[-y/2+z/2, -ix/2 ]

となり、トレースレスの反エルミート行列となる。こうして3次元ベクトルは純四元数(I成分がない)に対応するだけでなく、見かけの上で数ベクトルとしての三次元ベクトルをそのまま四元数と同一視できる。1/2の係数は、共役転置との積が、つまり四元数としてのノルムの二乗が、ベクトルのノルムの二乗と一致するようにするためだ。

一方でSU(2)も四元数と、|(nx,ny,nz)=1|の時

exp(t(nxJ+nyK+nzL))=cos(t)I+sin(t)(nxJ+nyK+nzL)

で対応する。SU(2)が四元数に対応する、つまりI,J,K,Lが張る実係数線形空間内にある事は既に書いたI, -I, J, -J, K, -K, L, -Lの間の(交換子のみならず)積が閉じる事から直ちに言えるが、この具体的な表式からさらに次の事実を見出す事ができる。すなわち、SU(2)の要素は四元数としてノルム1であり、しかもノルム1の四元数は全てこの形に書ける。つまり、SU(2)は、四元数体の中にノルム1である物からなる部分集合として埋め込まれる。既に注意したように、SU(2)の場合、四元数としての共役と、四元数としての逆数と、行列としての共役転置と、行列としての逆行列が全て対応する。

これら二つの全く異なる意味での対応関係と、上で示した形で表したSU(2)がxJ+yK+zLの形の``三次元ベクトル''をtの2倍の角度だけ(nx,ny,nz)を軸として``回転''させるという話まで考えて、結局三つの半ば独立した話を全て合わせてようやくクオータニオンが回転変換に使用できるというわけだ。

元から全く自然なストーリではない非自明な結果であり、それ故に使い方だけを説明される場合が多いわけだが、なんとなく自然なストーリーが背後にありそうに見えてしまって、勝手な哲学を見出そうとして、それで混乱するわけだ。q v conj(q)でベクトルvが回転します、なんてあっさり書いてあると、まるで、ベクトルを四元数が``直接''回転するように見えるのでどうにか自然な説明を見出そうとしてしまいがちになるが、実際にはこのように、何段階もの対応関係を経て、ようやく四元数は回転を実行できるのだ。行列のユニタリ行列による変換だから、ユニタリ行列とその共役転置で両側から挟む形になるわけだ。

WPFのクオータニオンのコンストラクタ

リフレクタで見て見た。ちょっと修正して見やすくすると

public Quaternion(Vector3D axisOfRotation, double angleInDegrees)
{
angleInDegrees = angleInDegrees % 360.0;
double num2 = angleInDegrees * (2*Math.Pi/360.0);
double length = axisOfRotation.Length;
Vector3D vectord = (Vector3D) ((axisOfRotation / length) * Math.Sin(0.5 * num2));
this._x = vectord.X;
this._y = vectord.Y;
this._z = vectord.Z;
this._w = Math.Cos(0.5 * num2);
this._isNotDistinguishedIdentity = true;
}

つまり、まず俺の計算はやっぱりどこか間違っていて、cos, sinの中は角度/2。それからこのコンストラクタは回転軸を正規化し、angleInDegreesは正確に回転角の意味。後、in degreesかよ!!

角速度ベクトル、回転時間が与えられた時の回転行列

対角化が面倒そうだったので、加法定理を求める方針でやってみる事にした。
つまり、

(I+δt(x Sx+y Sy+z Sz))(a0(t)I+a1(t)Sx+a2(t)Sy+a3(t)Sz)
=a0(t+δt)I+a1(t+δt)Sx+a2(t+δt)Sy+a3(t+δt)Sz ... (1)

を満たし、かつa0(0)=1, a1(0)=a2(0)=a3(0)=0であるような関数(連続性とか微分可能性とかの細かな扱いは知らない。必要なら行列の指数関数の話と併用すれば論理の隙間は埋められるだろう)を求めれば良い。それが求まるとa0(t)I+a1(t)Sx+a2(t)Sy+a3(t)Szが、角速度ベクトル(x,y,z), つまり、回転軸(x,y,z)/|(x,y,z)|, 角速度|(x,y,z)|でtだけ回転した時の回転行列、つまり、回転軸(x,y,z)/|(x,y,z)|, 回転角|(x,y,z)|tの回転行列を表す。

(x,y,z)をベクトルωで表し、(a1(t),a2(t),a3(t))をベクトルα(t)で表す事にする(ベクトルを明示するうまい方法がないのでここでだけギリシャ文字はベクトルを表すことにする)。しばしばtをオミットする。

すると、(1)式から
da0/dt=-ω・α
dα/dt=a0ω+ω×α
が得られる。
左辺にそれぞれa0, a1, a2, a3を書けて足しあげると0となる。これはa0^2+a1^2+a2^2+a3^2が不変である事を表す。t=0ではa0=1で他は0だから、連続性も考慮して、
a0=sqrt(1-|α|^2)
と書ける事が分かる。後はαの各成分だけ求めれば良い。αの微分方程式は

dα/dt=sqrt(1-|α|^2)ω+ω×α ... (2)

であるから、左からωを外積すると右辺第一項が消えて
d(ω×α)/dt=ω×(ω×α)
を得る。右辺はω×αに直交するから、|ω×α|は保存する(まじめにやるならば両辺とω×αの内積を取って右辺が消滅する事をがんばって計算すればd(|ω×α|^2)/dt=0を示せる。超めんどい。)。その値はt=0を参照すれば、0である。とすれば、ω×αは零ベクトルである。(2)式はいまや

dα/dt=sqrt(1-|α|^2)ω ... (3)

となる。αの各成分は初期値が等しく、微分係数が定数にωの各成分を乗じた形になっているので、αは全ての時刻でωに比例する。α=f(t)ω, とおくとf(t)が満たす微分方程式は
f(0)=0,
df/dt=sqrt(1-f^2|ω|^2)
である。必要ならばさらにg(t)=f(t)|ω|と変数変換しても良いが、ともかく、解が
f(t)=|ω|^(-1)sin|ω|t
と求まる。結局、

exp(t(x Sx+y Sy+z Sz))=cos(sqrt(x^2+y^2+z^2)t)
+sin(sqrt(x^2+y^2+z^2)t)(x Sx+y Sy+z Sz)/sqrt(x^2+y^2+z^2)

と求まる。特に、sqrt(x^2+y^2+z^2)=1ならばexp(t(x Sx+y Sy+z Sz))は軸(x,y,z)周りの角度tだけの回転行列を表すが、それは、

exp(t(x Sx+y Sy+z Sz))=cos(t)I
+sin(t)(x Sx+y Sy+z Sz)

である。

で、記憶によればこの結果は間違っていて、cos(t/2), sin(t/2)みたいになるはずなんだけど・・・結局、そのうち対角化の方もやってみなきゃならなそう。あと、そろそろWPFのQuaternionクラスのコンストラクタに色々なパラメタを入れたり、それからRotateTransform3Dを作って行列を調べたりして、ここまでの話と照らし合わせて見よう。あっちは、右手系だからちゃんと整合せいると思うんだけど・・・。Quaternionのコンストラクタには軸と角度を与えるコンストラクタがあるのだが、正規化されていないベクトルを与えた時の挙動が少し気になる。場合によっては、これらの引数は角速度ベクトルと回転時間としての役割を持つかもしれないし、内部で強制的に正規化されるかもしれない。この辺り全くドキュメント化されていない。

あ、足が、目が・・・

調子に乗ってたら著しく体調が悪くなってきた。最近、朝起きて数十分とその後との落差が酷い。

せっかくだから剛体回転でも、と思って準備

MSDNのクオータニオン関係の説明が詳細を欠いていて、係数の役割や回転変換との関係付けなど細かな点で不明な所があるので、剛体の回転も含めて整理しようとしたのだが、やっぱり集中力が阻害されて本をまともに読めない。まあ復習だしと思って自分ではじめからやってみる。

まず、三次元回転行列とは三次元の基底ベクトルを並べて作る。つまり、その行または列は正規直交する。従って回転行列RはRR^T=Iで特徴付けられる。

単位行列Iの近くの回転行列だけを考える。つまり、式(I+εM)(I+εM)^T=Iが、εを小さくしていった時にどんな条件式と等価になるかを考える。それは、
M=-M^T
である。この条件式はMの定数倍に対して保存される。つまり、Mに対して成り立てばεMに対しても成り立つ。つまり、単位行列Iの``近く''の回転行列の集合は
{I+M, where M=-M^T, Mは``小さい''}
と書ける。そこで、線形空間
L={M, where M=-M^T}
を考える事が重要になる。

反対称という事は、対角成分は0で上三角部分を決めると下三角部分が決まるから自由に決められるパラメタは三つしかない。これは、この線形空間が三次元空間である事を意味する。なんて勿体つけなくても、基底は直ぐに見つかって、

[0, 0,0]
[0, 0,1]
[0,-1,0]

[ 0,0,1]
[ 0,0,0]
[-1,0,0]

[ 0,1,0]
[-1,0,0]
[ 0,0,0]

がLを張る。

x,y,zが対称的に扱われるように、以下、右手系における右ネジ回転を基準とする事にする。これでこのエントリにおける不明瞭な点はなくなるだろう。後でWPFのクオータニオンと対応付ける必要はあるが。また、点の座標の変換と座標系の変換を明確に区別する。

(x,y,z)にある点をx軸でθ回転した時移る先の座標(x',y',z')は

[x'] [1,0  ,0  ]
[y']=[0,cosθ,-sinθ]
[z'] [0,sinθ,cosθ ]

と表される。この事から、x軸中心の微小なθ回転は

[0,0,0]
I+[0,0,-θ]
[0,θ,0]

と表される。他の軸中心の微小回転も同様に求められる。そこでLの基底を次のように取り直すと便利だ。

[0,0, 0]
Sx=[0,0,-1]
[0,1, 0]

[ 0,0,1]
Sy=[ 0,0,0]
[-1,0,0]

[0,-1,0]
Sz=[1, 0,0]
[0, 0,0]

この場合、積はIを加えれば閉じていて、反対称である。つまり
SxSx=-I, SxSy=Sz, SySx=-Sz, ... .

恒等変換の``近く''の回転変換を次々と作用させていく事で、恒等変換から``遠く''の回転変換も作る事ができる。剛体の回転運動はこのようにイメージできる。対応する概念は単位行列の``近く''の回転行列の複数回の積だ。つまり、x(t), y(t), z(t)を与えて、
Π(I+δt(x(t_n)Sx+y(t_n)Sy+z(t_n)S_z)), where δt=t_n-t_(n-1) ...(1)
によって単位行列から``連続に''別の回転行列に移る事ができる。ここでSx, S_y, S_zが単位ラジアンあたりの各軸周りの回転に対応していた事を思い出せば、x(t), y(t), z(t)は角速度ベクトルの各成分を与えると分かる。

この計算を実行するのは難しいが、ひとつだけ分かる事は、I, Sx, Sy, Szの積が閉じているために、この結果は必ず、I, Sx, Sy, Szの線形結合になるという事だ(Lの基底と違い、Iが加わる事に注意!)。これは重要な事だ。というのも、回転行列の積を繰り返すと計算誤差により徐々に回転行列の条件=正規直交性を満たさなくなってしまうからだ。しかし、これら四つの基底に関する係数成分を時々刻々と計算するならば、計算誤差がどれだけあろうとも、それは回転変換に厳密な意味で対応し続ける!こうして四元数が登場する。

少なくとも剛体のシミュレーションに関する限り式(1)の計算結果を具体的に表す方法がない事はほとんど問題にならない。なぜならば、実際に、その時点での回転行列に恒等変換の近くの回転行列を乗じて、回転変換を更新する処理を繰り返せば良いからだ。変数に更新量を加算したり乗じたりしてループを回す類の処理は、シミュレーションにおける最も基本的なアルゴリズムで誰もが良く慣れているはずだ。また、すでに注意したように、回転行列自身の代わりに基底行列I, Sx, Sy, Szの係数を保存し、回転行列の積を計算する時には、基底行列の間の積の関係を使って係数の組から結果の係数を求めるようにすれば、係数に対応する回転行列は厳密に何らかの回転変換に対応し続ける。

存在する問題は二つだ。一つは、(1)式の積に現れる各項は近似的にしか回転行列に対応しないという事。もう一つは初期姿勢を決めるために(1)式の極限の具体的な結果が一つ欲しいという事。二つ目が解決されれば、その結果は、一つ目の問題の解決にも貢献するだろう。つまり、本物の微小な回転変換を繰り返し合成して一般の回転変換を作る事ができるようになる。

さて、(1)式の計算結果を具体的に求められる簡単なケースが一つある。それは、x(t), y(t), z(t)が一定である場合で、結果は行列の指数関数になる。その意味も明確だ。これは、角速度ベクトル[x, y, z]による、軸・角速度一定の回転を表す。特に、|[x,y,z]|=1ならば、時刻t=θにおいて角度θだけ回転した事になる。これがいわゆるクオータニオンと、軸/回転角で表した回転の関係だ。

ふう。次回は、せっかくだからSU(2)でやらずにこのまま気合で対角化して指数関数を求めるか。

2009年2月13日金曜日

ちょ、

足が、足がやばい・・・

クオータニオンを使って姿勢をランダムにした


クオータニオンで一様な回転を生成するには、単にその四つの係数を四次元球面上に一様に分布させれば良い(⇒これは誤り。回転軸を球面上一様分布で、回転角を一様分布で生成してクオータニオンを生成すべき。だったら、AxisAngleRoatate3Dで良かったんだな。まあ良いや。回転軸は確かに一様だし)。実際には、四つの係数を[0,1]の一様分布で生成する事を長さが1以下になるまで繰り返して、その後まずクオータニオンを作ってしまってからNormalize()を呼べば良い。

というわけで今現在のコードで、ミクさんのモデルデータは重心が原点にくるように移動させてあって、それをContentsとするVisual3DのTransformにTransform3DGroupを設定して、そのChildrenをRotateTransform3D⇒TranslateTransform3Dの順に設定し、この二つの変換を支配するQuaternionおよびPoint3Dを公開して、UpdatePose()を呼ぶと変換に反映するようにした。現在はPoint3Dのみを重力加速度に従って動かしているけれど、このデータ構造なら、オイラーの剛体方程式に従って剛体回転も容易に扱えるはず。やる気はしないけれど。

コードと実行ファイルはARWithWPFMikuEmitted.zipを詳細サイトのダウンロードページから取ってきてください。

ちなみに、この画像ではボタンの``Emit Miku!''の綴りが``Emmit Miku!''となっていて格好悪いけれど、アップしたソースでは修正してあります。

ARToolkitPlusで体感ゲームを作る予定は未定だけど準備だけ


ミクさんがマーカの位置からランダムな初速度を与えられて射出されて重力で自由落下するぞ。

ミクさんは三次元CG@七葉の4169さんのローポリなデータをお借りしています。

しかしWPFのStoryboard使わずに普通にオンフレームアニメーションにしてしまったので、プログラム的にはあまり面白くない。一連のARToolkit+WPFの実験はWPFを活かす事に主眼があるので続くかどうかは未定。

演算子とジェネリック

演算子の実装を表す型制約がないから何かやだねー、というお話。2chのC#板で出ていた。
この手の話は、C++風にダックタイピングが出来たら・・・という話にもなるのだが、そういう方針で、まあマクロのお化け的なC++のテンプレートにおけるダックタイピングとは違くてつまりは実行時に動的にメソッドを生成するという方針だけど、どなたかがExpression TreeのCompileメソッドを使うやり方を示していた。

で、それならCodeDOMでもできるじゃん、と思って書いたのがこんなコード。

class Multiplier<T>
{
public T Mult(T l, T r)
{
string nameOfT = typeof(T).FullName;
string assemblyOfT = typeof(T).Assembly.ManifestModule.FullyQualifiedName;
CompilerResults cr = null;

using (CodeDomProvider provider
= new CSharpCodeProvider(new Dictionary() { { "CompilerVersion", "v3.5" } })) {
CompilerParameters cp = new CompilerParameters() {
GenerateInMemory = true
};
cp.ReferencedAssemblies.AddRange(new string[] { "System.Core.dll", "mscorlib.dll", assemblyOfT });
string className = "Multiplier_" + nameOfT.Replace('.', '_');
string namespaceName="GenericTest";
string qualifiedClassName = namespaceName + "." + className;
string source = "namespace " +namespaceName+"{ public class " + className + "{public " + nameOfT + " Mult(" + nameOfT + " l, " + nameOfT + @" r){return l*r;}}}";

cr = provider.CompileAssemblyFromSource(cp, source);
if (cr.Errors.HasErrors) {
throw new Exception(String.Join(Environment.NewLine, cr.Errors.OfType().Select(e => e.ToString()).ToArray()));
}
Type typeOfMultiplier = cr.CompiledAssembly.GetType(qualifiedClassName);
Object multiplier = Activator.CreateInstance(typeOfMultiplier);
MethodInfo multiplierMethodMult = typeOfMultiplier.GetMethod("Mult");
return (T)multiplierMethodMult.Invoke(multiplier, new object[] { l, r });
}
}
}

その後、Nyaruruさんの詳しい記事を見つけたのでそれも投稿しておいた。Nyaruruさん(のさらに引用先)曰く、Expression Treeはセマンティクスをデータ構造として保持するからコードを構造を持った抽象的対象として扱えるのが利点で、作ってすぐさまコンパイルして動的メソッド生成のために使うなんてのは、邪道であるらしいのですが、まあCodeDOM使うとこんなに酷い事になりますよ、というお話でした。

ていうかこのコードはMult()呼ぶたびにコンパイルするのも酷いな。せめてstaticなコンストラクタでやるべきだ。

2009年2月11日水曜日

回ってフワフワするようにした



写真じゃ分かり難いけれど(当たり前だ)、z軸中心の回転とz軸方向の平行移動のアニメーションを加えた。さながらデジタルフィギュア。

コードはいつもの所においてある。実行ファイル入りなのでとりあえず遊びたい人もどうぞ。

追記:

ニコニコ動画にアップした。

体調

今までは疲労感=倦怠感であって、明確で強い筋肉痛というのは週に二日くらいだったのだけど、いつの間にか完全に日常化してしまっている。腕から足から酷い。それに目がショボショボして目蓋が重く集中力が極端に阻害される。この数ヶ月で良くなったのは睡眠のリズムが整った事だけど、それでも毎日徹夜明け状態は変わらない。むしろ辛ければ寝ていれば良かった時期よりも一日の過ごし方に工夫が要るのでかえって耐え難い。だからミクさんでARというのも何ともアレだが、まだできる事があるというのは良い事だと思おう。

で、ミクさんをWPFのStoryboardでアニメーションさせるのが思ったよりも難航している。まあ気長にやるさ。

2009年2月10日火曜日

ミクさんの動画


2009年2月9日月曜日

ミクさんリアルだ


今回から、カメラの位置だけでなくマーカの位置の左と右に点光源を置いたのでミクさんは下から照明されているのだが、お陰で怖い程のリアリティがある。画像じゃ伝わらないかもしれないけれど、ちゃんとマーカの動きが追従されている時、どきっとするくらいに本物らしい時がある。これは完全にモデル製作者の力量に拠ってるよなあ。日曜モデリングさまに感謝を。

追記:せっかくの拡張現実なので、現実感と非現実感が同居している感じに照明を調節してみた。

ね、ねむいー

尋常じゃなく眠い。夜までは起きている生活リズムを作りたいからと無理をしすぎかも。でもまあ今日はちょっと面白い事ができた。

ミクさんのコード

一応置いておいたけどまだやっつけなので注意。とりあえず動くとは思う。

ミクさんを表示してみた


もう何番煎じというレベルではないですが。しかしネタとしてはともかく、スケルトンが超整理されていて3D表示がWPFであるためにプログラミングという観点からはとっても可能性が広がっているのです。

さあ、データを気合入れて作って振り付けをアニメーションするのは職人に任せて、WPFを活かしたコードでのお気楽アニメーションをやってみるぞ!

ちなみに、データは日曜モデリングさまものです。というかデータの準備に、工学ナビの中の人のこのエントリを参考にしたというわけなんだけど。

mqoからxamlの3Dデータの準備の仕方のメモ。Identityを使用して、この記事を参考にした。

1.mqoをメタセコイアで読み込む。一度正方形を適当な大きさで作ってミクさんのフットプリントを確認しておく。今回のデータの場合、およそ100x100の正方形に収まるが、そのうち大半は髪の毛のボリュームなので、プログラムでスケール変換する時にmarkerwidth/50.0を係数とする。(markerwidthはARToolkitPlusの初期化時に渡すマーカのマーカ座標系における幅。)
2.Identityが吐くxamlにはエンコーディング指定がないので、日本語の材質名、物体名などは変更しておく。なお、今回のデータの場合、日本語の名前の材質は全て未使用(製作の過程で一時的に割り当てたっぽい)だったのでざっくり削除した。
3.Identityはインストール先のディレクトリのTexturesディレクトリからのみテクスチャファイルを探す。従って一度テクスチャファイルは全てコピーする。
4.Identityでmqoファイルを開く。ファイル-名前を付けて保存メニューを選択し、xamlをファイルの種類として選び、UV座標と法線にチェックを入れて保存する。この時オリジナルのテクスチャがあるディレクトリに出力すると改めてテクスチャファイルを生成しようとしてエラーが出るが別に問題はない。気になるなら別のディレクトリに出力する。
5.xamlファイルをがんばって書き換えて次のようにする。この時x:Nameは全てx:Keyに書き換える。

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<MaterialGroup x:Key="Material_miku_pose1_face" >
...
<Transform3DGroup x:Key="Transform_Character_miku_pose1_Character_0" >
...
<MeshGeometry3D x:Key="Geometry_miku_pose1_head_0" ... />
...
<Model3DGroup x:Key="ここにプログラムから参照する名前を入れる。今回はmiku_pose1とした">
...
</Model3DGroup>
</ResourceDictionary>
6:ソリューションにMIKUフォルダを作り、xamlとテクスチャファイルを全て入れる。プロパティで``ビルドアクション''を「コンテンツ」に、``出力ディレクトリにコピー''を「新しい場合はコピー」にする(注:xamlもコンパイルせずにこのようにした方が、ユーザがデータを入れ替えられて良いと思うのでそうした)。
7:Window1.xamlに
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MIKU/miku_pose1_resource.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
という記述を追加。これで、コードビハインドのWindow1クラス中で(Model3D)this.Resources["miku_pose1"]としてミクさんのモデルを参照できる。

キャリブレーションの仕方を英語で書いた

http://sites.google.com/site/augmentedrealitytestingsite/#TOC-How-to:-Camera-Calibration
にキャリブレーションの仕方を英語で書いておいた。この情報は探しても全然見つからないのでかなり有用だと思う。頭がパンクしかけたのでかなり端折ったけど、かえってこのエントリに書いたキャリブレーションの仕方の説明よりも簡略になって分かりやすくなった気がする。

mqo to xamlができるソフト

Identityというソフトがあるらしい。これでマーカーの上でミクさんを踊らしたりとかできるのかな。

連番mqo出力ができるアニメーションツール RokDeBone2なんてのもある。

ありゃ、Identityって、ただの変換ツールかと思ったらアニメーションツールなのか。それでxaml出力ができるって、アニメーションするオブジェクトをマーカに表示するだけならプログラム超簡単にならないか?

あと、メタセコイアへのポインタ


追記:Identityを使ってみたが、Viewbox3Dをルート要素としブラウザで開くと単体で動作する手取り足取り状態のxamlが出力される。これはこれで便利だが、プログラムから参照するのには不便。ここの記事にリソースディクショナリへの加工の仕方がある。

2009年2月8日日曜日

英語サイトを作った

ARToolkitPlus+WPFは必要としている人は少ないが確実に有用である類のプロジェクトなので、世界に広く公開した方が良い。それで、英語サイトを作った。
http://sites.google.com/site/augmentedrealitytestingsite/
今はアドイン「身も蓋もない」の軒先を借りている状況なので、ソースコードのコメントを英語でリライトしたらこちらに完全に移行するつもり。

追記:コメントを英語にしたので移行した。今後はソースなどはこちらにアップロードする。

デモ動画

デモ動画を作ってみた。

2009年2月7日土曜日

サンプルを更新

ここにおいてあるARToolktPlus+DShowNET+WPFのサンプルを更新した。

今回、デフォルトを自分用のキャリブレーショファイルにしてしまったので、これで上手くいかない場合は設定でLogitech_Notebook_Pro.calを選択するか、このエントリを参考に自分用のキャリブレーションファイルを作ってください。

それと、もう少しましなPCカメラ欲しいなあ。同じ30万画素でも普通はもっとましらしいし、ネット通販ならそれらでも980円で買えたりする。ちょっとGroovy GR-CAM030はひどすぎる。980円とは言え、安物買いの・・・って奴だ。

And finally...

MatlabのスクリプトってC++にコンバートした上でコンパイルするなんて事ができるので、もしかしたらと思って探したら、Camera Calibration Toolbox For MatlabのModified Versionのページで紹介されていたー

さあ、これでたぶん、カメラのキャリブレーションが自前でできるぞ。

実際にやってみた。

まず、Windows 7でやっているので、.net framework 1.1がないと怒られた。

http://www.microsoft.com/downloads/details.aspx?familyid=262D25E3-F589-4842-8157-034D1E7CF3A3&displaylang=ja

http://www.microsoft.com/downloads/details.aspx?FamilyId=04DBAF2E-61ED-43F4-8D2A-CCB2BAB7B8EB&displaylang=ja

をダウンロードしてインストール。

http://research.graphicon.ru/calibration/gml-c-camera-calibration-toolbox-5.html
から GML C++ Camera Calibration Toolbox の``Latest Binaries''をダウンロードしてインストール。

インストール先の``Calibration pattern''フォルダ中のpattern_A4.pdfを印刷。

GML Camera Calibration Toolboxを起動して、白紙のアイコンのボタンをクリック。。pattern_A4.pdfを印刷したなら、Object Sizeは7, 10, square sizeは24(実測値を使う事!)くらいを入力。

何らかの方法で印刷したパターンを撮影した画像を用意する。私が使用しているGroovy GR-CAM030では、ボタンを押すと動画のキャプチャソフトが起動する(付属CDからドライバをインストールしたわけではないのでWindows 7標準の機能かもしれない)が、ここでもう一度ボタンを押すと静止画がキャプチャできる。何枚か用意する。この時
http://research.graphicon.ru/calibration/recommendations-for-taking-calibration-photos.html
を参考にして色々な姿勢から撮影する。枚数は多い方が良い。意外と認識に失敗する事が多い。また、全ての画像のサイズは等しくする事。全ての測定結果はピクセルで表される。

プラス記号のアイコンのボタンをクリックして画像を全部追加する。チェッカーボードが二枚重なっているボタンを押すと全ての画像に対して認識が試行される。失敗した画像はマイナスのアイコンのボタンで削除。あまり成功枚数が少ないor姿勢のバリエーションが少なければ、新たに画像を追加してチェッカーボード一枚のアイコンのボタンで個別に認識を試行できる。

カメラのアイコンのボタンを押すとキャリブレーションが実行される。
で、下のようなテキストファイルを作る。分かり易いように``,''を入れたが、これは実際には書いてはいけない。

ARToolKitPlus_CamCal_Rev02
使用した画像の幅, 使用した画像の高さ, 出力されたprincipal pointの一つ目, 出力されたprincipal pointの二つ目, 出力されたfocal lengthの一つ目, 出力されたfocal lengthの二つ目, Distortionの四つの数字を順に, 0.0, 0.0, 10くらいの好きな数字


これで自分のカメラ用のキャリブレーションファイルが完成!!・・・と思う。

ちょっと気になるのは、640x480の画像を使ってGroovy GR-CAM030をキャリブレーションすると、principal pointのxが420くらいになる事。実際にずれているのか、それともパラメタの解釈か手順に問題があるのか・・・。

で、このキャリブレーションファイルを使ってARしてみた結果:

まあ特に何が変わったとも思えないけど心なしか自然になった気がする。ブラシーボ効果かもしれない。

体調

今の薬の処方になってから、朝方に少しだけ集中力が得られる時間帯ができて、それでプログラミングなんかをしたりして、そして昼頃にかけて結局いつもの持続する倦怠感・眠気、目がショボショボ、風邪っぽい症状が強まって、何も手につかないのが耐えられなくて倦怠感を吹き飛ばそうと無理やりに外出して強烈に疲れて帰ってくる、という感じになっている。人間って面白くて、調子がよければ少しだらけ気味にアクティブに動くのだけど、中途半端に調子が悪くいと、とてもアクティブに動くのだ。だから最近、外出する頻度はむしろ上がっているのだが、朝方の時間帯を除けば、結局体調は悪くなる一方。もう、自分の足で歩けるのはいつまでやら、という思いから無理に外出しているだけという感じ。

来月にもう一度CFSの専門医の診察を受ける事になっているが、診断名すらつかない状況だけでも解決して欲しいという気持ち半分、現状治療法のない病気という絶望的結論を怖がる気持ち半分というところ。

しかし最近のAR関連の活動もそうだが、人間、こんなどん底でも色々な事ができる物だ。人間というものに心底感心する。

MATLABでキャリブレーションできるらしい?

MATLAB camera calibration toolboxでキャリブレーションすると、ほとんどARToolkitPlusが必要とするのと同じ形式でパラメタが得られるらしい。

Description of Calibration Parametersの項を見ると、2次元ベクトルであるfc, cc(これがARToolkitPlusのパラメタfc_x, fc_y, cc_x, cc_yにあたるのだろう)と、5次元ベクトルであるkc, そしてスカラalpha_cが得られる結果であるらしい。

そして、First Calibration Exampleのページを見ると、
Notice that the skew coefficient alpha_c and the 6th order radial distortion coefficient (the last entry of kc) have not been estimated (this is the default mode). Therefore, the angle between the x and y pixel axes is 90 degrees. In most practical situations, this is a very good assumption.
と書いてある。つまり、alpha_cはピクセルのx軸とy軸のなす角度の90度からのズレを表していて、これは普通無視して良く、実際ARToolkitPlusのキャリブレーションパラメタに対応するパラメタはない。一方で、ARToolkitPlusはkc_1からkc_6までを要求するが、このToolboxではkc_6は計算しないのがデフォルト。という事は得られた5次元ベクトルkcの要素を順にkc_1,..., kc_5とし、kc_6は0とすれば良いわけだ。

ARToolkitPlusのカメラキャリブレーションファイルの形式
ARToolKitPlus_CamCal_Rev02
xsize ysize cc_x cc_y fc_x fc_y kc1 kc2 kc3 kc3 kc5 kc6 iter
と比較すると、結局、iterは好きな数を指定し、xsize, ysizeはキャリブレーションに用いた画像のサイズを指定し、そしてcc_x, cc_y, fc_x, fc_y, kc1,..., kc_6はこのツールボックスの出力から得られる事になる。

Matlabを持っていないので試せないけれどね。

追記:というか、ARToolkitPlusのページには、
Improved camera calibration model (MATLAB camera calibration toolbox support)

As of version 2.0, ARToolKitPlus is compatible with the camera calibration
model used by Jean-Yves Bouguet's Camera Calibration Toolbox for MATLAB. An improved version of the toolbox (highly recommended) that includes automated corner/calibration object detection is available
from the Graphics Media Lab at Moscow State University.

The ASCII-file containing the camera calibration parameters should look as follows:
[line1]: ARToolKitPlus_CamCal_Rev02
[line2]: xsize ysize cc_x cc_y fc_x fc_y kc1 kc2 kc3 kc3 kc5 kc6 iter

xsize, ysize: calibrated frame dimensions (does not have to match the frame dimensions at runtime)
cc_x, cc_y: principal point location (in pixels)
fc_x, fc_y: focal length (in pixels)
kc1..kc6: radial/tangential distortion coefficients (kc6 currently not in use)
iter: number of iterations for distortion compensation

It is strongly recommended to enable undistortion lookup tables when using the improved calibration model.
A detailed description of the calibration parameters is available here.
と書いてあるので、kc_1からkc_5までをツールボックスの出力から取得して、kc_6は0にするという事で本当にかまわないようだ。それから、iterは大きい方が計算精度が上がるが時間がかかる、と。いいなー。MATLAB欲しいなあ。

Artoolkit Plus用のキャリブレーションの仕方

Artoolkit Plus用のカメラキャリブレーションファイルの作り方だが、必要な情報としてはArtoolkit付属のキャリブレーションツールで全て得られるらしい。ただ、表示される情報とArtoolkit Plusのカメラキャリブレーションファイルのフォーマット内のパラメタとの対応が不明確。

以下推測に基づく手順:
ArtoolkitのSourceForgeのページからARToolKit-2.72.1-bin-win32.zipを取ってきて適当な場所に解凍する。これにはバイナリが入っているので、キャリブレーションの目的だけならビルドの必要はない。

工学ナビの記事のこの項にある手順を実行する。ARToolkitの置き場所などは適宜読み替える。

最後に表示された
SIZE = 640, 480
Distortion factor = 375 223.5 6.2 1.003
1323.86047 -18.27697 410.18302 0.00000
0.00000 1285.40599 -243.19888 0.00000
0.00000 0.00000 1.00000 0.00000

が重要。これは、この記事中のパラメタ名の意味で、おそらく
SIZE = xsize, ysize
Distortion factor = cc_x cc_y 謎 謎
fc_x 謎1 謎2 謎
謎4 fc_y 謎3 謎
謎5 謎6 謎 謎

と並んでいる。分からないのはkc1 kc2 kc3 kc3 kc5 kc6 iterの7個のパラメタだが、最近のカメラなら歪み補正はされているだろうので、全て0で良いだろう。まあ、私のカメラは980円の安物のGroovy GR-CAM030で歪みまくっていたが。

#たぶん謎1~謎6が何らかの順番でkc1~kc6に対応していると考えられる。

そういうわけで、このケースだと、
ARToolKitPlus_CamCal_Rev02
640 480 375 223.5 1323.86047 1285.40599 0.0 0.0 0.0 0.0 0.0 0.0 0

という内容を持ったテキストファイルを作って、たとえばGroovy GR-CAM030.calというファイル名で保存する。

ちなみにここに公開しているARToolkit(Plus)+DShowNET+WPFのVisual C# 2008用サンプルでは、ARWithWPF.exe.config中でカメラキャリブレーションファイルを指定できるようになっているので、ここで紹介している手順でカメラキャリブレーションファイルを作ったらこのファイルを編集して欲しい。

キャリブレーション前(画角が不適切):

キャリブレーション後:

2009年2月6日金曜日

GUIを付けて透明度をいじれるようにした


やっぱりシュパパッとGUIをコードできてこそWPFとの組み合わせにした意味があるよな、と主って、透明度をスライダで設定できるようにしてみた。もちろん、バインディングを使っているので追加したコードは超短い。
xamlに追加したコード:

<StackPanel Grid.Row="0" Orientation="Horizontal">
<Label Padding="5,0,0,0" VerticalAlignment="Bottom">ARオブジェクトの透明度:</Label>
<Label Padding="0,0,5,0" VerticalAlignment="Bottom"
ContentStringFormat="##0%"
Content="{Binding ElementName=OpacitySlider, Path=Value}">
</Label>
<Slider Name="OpacitySlider" VerticalAlignment="Bottom" Width="100"
Minimum="0" Maximum="1.0" TickFrequency="0.01" IsSnapToTickEnabled="True"
Value="1.0" SmallChange="0.01" LargeChange="0.1"/>
</StackPanel>

Visual Brushを作成するコード:

Label label = new Label() {
Content = textureTexts[i],
Foreground = Brushes.OrangeRed,
Padding = new Thickness(0),
};
Viewbox viewBox = new Viewbox();
viewBox.Stretch = Stretch.Uniform;
viewBox.Child = label;
Border border = new Border() {
Background = Brushes.LimeGreen,
Width = 1.0,
Height = 1.0
};
border.SetBinding(Border.OpacityProperty,new Binding("Value"){
Source=OpacitySlider
});
border.Child = viewBox;
VisualBrush labelBrush = new VisualBrush(border);
labelBrush.Viewbox = new Rect(0, 0, 1, 1);
labelBrush.Stretch = Stretch.UniformToFill;

ARToolkit(Plus)+DShowNET+WPFのサンプルを更新


冗長性が高く認識率に優れるらしいBchタイプのマーカに対応した。readme.txtを参照のこと。アーカイブはこちら

上に掲載したのはBchマーカを使った場合の実行画面。ただ、特に検出率が高まった感じはしなかった。画像ではパーフェクトだけど。

追記:ARToolkitPlusのラッパーコード中にこういう記述がある。
Id-based markers directly encode the marker id in the image.
Simple markers use 3-times redundancy to increase robustness,

Id-based markers directly encode the marker id in the image.
while BCH markers use an advanced CRC algorithm to detect and repair marker damages.

ちょっと意味がとり難いが、おそらく、Simpleマーカは冗長化した上で2Dバーコードにエンコードするアプローチなのに対し、Bchは冗長化をしない代わりにCRCチェックサムを埋め込んで成功例が少なくて良いからIDが確実に読み取れたマーカのみを許容するアルゴリズムを使っているということだろう。Simpleが512種類に対してBchが4096種類なのだから、後者の方が冗長化の度合いが高いはずがない。

configファイルを使って見つからなかったマーカの位置を推定する機能をARToolkitPlusは持っていて、たとえば剛体の姿勢測定が用途として想定されているようだが、この場合マーカの冗長性よりも使えるマーカの数を増やした上で少なくて良いからIDが正しく読み取れるマーカを見つける事の方が重要になる。なにしろ四点で剛体の姿勢は決まる!Bchはそういう用途のためのマーカシステムで、IDはどうでも良くてマーカの姿勢の検出精度だけを問題にするのならばSimpleマーカシステムの方が優れているのだろう。

というわけで、単純な遊び用途ならばBchマーカの出番はなさそうだ。剛体の姿勢検出なんて遊びじゃやらないし、第一Configファイルを用意するための準備がかなり大変だ。

ARToolkitPlus+DshowNET+WPFのソースを更新

ここにおいてあるソースを更新した。
更新内容は主にバグ取り。ウィンドウのLoadedイベントでthis.Close()すると例外が投げられるのね。Application.Current.Shutdown()するようにした。これで、キャプチャデバイスを選択しなかった場合に変な終了の仕方をしていたのが、行儀良く終了するようになった。

これでコードは相当に整理された。ARToolkit関連のサンプルコードとしては最も単純化されていてサンプルコード/スケルトンとしての役割を果たし易いと自負している。ただ、コードのコピペが多く変数名のつけ方が自分流のつけ方と整合的でない部分があるので、少しリファクタリングしておきたいところ。構造はもうこれで良いだろう。window1.xaml.csの行数もメソッド数も十分に少ない。

2009年2月5日木曜日

異なる大きさのマーカ、同じIDを持つマーカ


ArManWrap.ARTKPSetPatternWidthメソッドにはマーカの大きさの実測値を指定するのだと思っていたのだが、そうではないようだ。これは、マーカ座標系におけるマーカをあらわす正方形の大きさを指定するメソッドみたいだ。つまり、例えば10.0を指定すると、(-5,-5)-(5,-5)-(5,5)-(-5,5)を頂点とする正方形を作ってから変換行列を取得して適用してやると、ちょうど画面上でマーカと重なるようになる。

そういうわけで、使っていたマーカが一辺3cmなのでこのパラメタに30を指定していたのだが、大きさの違うマーカもそのまま認識してくれた。

また、2Dバーコードを認識するというコンセプト上、同じIDのマーカが複数あっても何の問題もなく認識してくれた。中々守備範囲が広い。

カメラの位置に点光源を置いた


ネタとしては弱くなったが、陰影がつくようになった事が分かりやすいように三角柱を置くようにした。


環境光と組み合わせると少し自然になった。もちろん、実際の光環境と不整合であるが故の気持ち悪さはどうしようもない。が、逆にそれが、バーチャルリアリティーな感じで良い。こういった類は、チープなグラフィックの方が効果が喜んでもらえるものだ。あんまり凄いグラフィックだと、photoshopで合成したみたいに見えちゃうからね。

こうしたちょっとした変更は全部xamlの変更ですんでしまう。WPF恐るべし。

高速化


ARToolkitPlusとWPFの組み合わせのサンプルのコードを少し高速化した。といっても、高速化の余地はほとんどなくて、描画のコールバックの度にキットの初期化とクリーンアップを行っていたのをウィンドウのロード時とクローズ時に行うようにしただけ。でも結構な負荷だったようでかなり動作が改善された。

それにしても今日は、午前中に横になっていたら、午後になって割と調子が良い。眼はすげー霞んでるけど。

体調

最近、朝5時くらいに眼が覚めてそれから9時くらいまでは若干比較的調子が良いのでARで遊んだりできるわけだが、今日は8時までしかもたなかった。なんか感冒様の症状が強まっていて、のどに痰が絡んで息から嫌なにおいがするし、熱はないけど熱っぽいし、眼がしょぼしょぼして霞む。ARの方も一区切りついたし今日はゆっくりしよう。

サウンドオンリーで会議中の方々


ARToolkitPlus+DShowNET+WPFのサンプル



ARTolkitPlus+DShowNET+WPFのサンプルをここに置いておいた。この構成で、全てが上手くいけば、Visual C# 2008で一発でビルドが通るはず。

遊び方:
  • data/00-04.pngを開いて、マーカの一辺が3cmくらいになるように印刷します。マーカーを少し白い余白が残るように切り取ります。
  • ソースに興味があるならばARWithWPF.slnを開きます。三つのプロジェクトが入っていますが、全てビルドしてみましょう。上手くいけば一発で通ります。ソースに興味がなければ、バイナリ入りなので次に進みます。
  • PCカメラの接続を確認してARWithWPF/bin/release/ARWithWPF.exeを実行します。設定ダイアログは初期値のままでかまいません。
  • マーカを何枚か平らな場所に置いてカメラに映るように配置します。文字が表示されたら成功!ID0番のマーカは少し認識し難いです。

なお、今回から設定をアプリケーションスコープにした。変更の必要があれば、開発環境からデフォルト値を変更するか、bin/release/ARWithWPF.exe.configを書き換えてください。

2009年2月4日水曜日

ARToolkit:複数のマーカ



複数のマーカを検出する場合、conigファイルを作って、使う2次元バーコードのIDを列挙しないといけないみたい。512個を自由に、というわけにはいかないのか・・・。

まあそれほど問題にはならないだろう。

追記:ぐわー、いじってたら動かなくなった。しかも肝心な部分でコード履歴がない。最新のはまだシングルマーカの。やり直しだ。ただ、サンプルからほとんどコピペで上手く行くのは分かったのでもうどうにかなるだろう。

ARToolkitの実験の続き:2DバーコードのIDをテクスチャにした


これで複数マーカー対応にした時に楽しいはずだ。

ちなみにこのテクスチャはLabelオブジェクトを使ってVisualBrushを作る事で超手抜きで実現している。WPFのリッチさが早速発揮されているというわけ。Direct3DやOpenGLで文字をテクスチャにしろと言われたら、技術的な問題は何もないにしても、とても手間がかかるだろう。

ARToolkitの実験ソース置き場を作った

これからはここに置く。現時点でのソースを置いておいた。まじでVisual C# express 2008オンリーでビルドできます。巷のARToolkit関連のサンプルと比べるとビルドにたどり着くまでの敷居は圧倒的に低いでしょう。

マーカも0番から4番までのシートを同梱しました。一辺が3cmくらいになるように印刷すると設定を変えなくてすみます。あるいは、どこだか(c:\documents and settings\ユーザ名\Local Settings\Application Data\ARWithWPFの下のどこか)に作られるuser.configファイルで設定を変えてください。もちろん、開発環境からプロジェクトのプロパティ→設定タブで設定を変えてもかまいません。

それと、とりあえず、3D表示するオブジェクトの大きさが決めうちになっていたのをマーカーの大きさに合わせるようにしたらかなり良くフィットするようになった。カメラのキャリブレーションの必要は私の環境ではとりあえず必要ないようだ。

DirectShowの経験が不足していてグラフやらピンやらさっぱり分からないので人のサンプルコードを丸写し。そのためか終了時にキャプチャを終了させる関数を呼び出したら帰ってこなくなることがある。開発環境から実行しているならデバッグメニューから強制終了してください。

TODO:何もしないとActiveMovie Windowとやらが表示されてキャプチャ結果が映されてうざいので散々苦労して非表示にする(存在はしたまま)事ができたのだが、そもそもグラフとやらの組み方次第では出てこないようにできると思うので調べておこう。

ARToolkitPlusとDShowNETとWPFの組み合わせで動いたー



やっぱりカメラのキャリブレーションファイルの形式がARToolkit本家とARToolkitPlusとで違っていた。でも、ARToolkitPlusについてきたキャリプレーションツールはビルドにてこずっている。ていうか無理。もうプリプロセッサが存在する言語と付き合うのは無理。ヘッダファイルという存在が吐き気がする。

というわけでとりあえずARToolkitPlusに付属の適当なカメラのキャリブレーションファイルを使用してみたらあっさりと上手く行ってしまった。イエイ!正しいキャリブレーションじゃないので少し歪むのはご愛嬌。

面白いのは、本家のARToolkitはパターンファイルをプログラムで読み込んで、それと同じパターンをマーカとして撮影すると認識されるという仕組みなのに対し、ARToolkitでは二次元バーコードを用いる。つまり、プログラム側でパターンを登録する必要なしに、Simpleバージョンのコードなら512種類、BCHバージョンなら4096種類のマーカを自由に使える。プログラム側では認識したマーカのid番号が取得できる。この画像ではウィンドウのタイトルに、4番目のidのマーカが認識されたと表示されているが、0番から4番まで撮影するマーカを取り替えるとちゃんとこのタイトルの表示が変わってちょっとうれしい。

こうなると、当面の目標は複数のマーカを識別して、その上にバーコードに対応する番号を描画するプログラムを作る事だな。

それはそうと、キャリプレーションはどうにかしたいなあ。


改めてポインタを。
ARToolkitPlusの.net用ラッパー:
http://www.brains-n-brawn.com/default.aspx?vDir=wpfaugreal
DShowNET:
http://www.codeproject.com/KB/directx/directshownet.aspx
DShowNETのサンプル:
http://web.sfc.keio.ac.jp/~shokai/archives/2007/01/directshownet-capture-save-image.html

NyARToolkitがいやだったのはそもそもDirectXの経験がない事もあるのだが、DirectX SDKが必要で環境が特異的になってしまう事。一方でARToolkitPlusの.netラッパー+DShowNET+WPFの組み合わせならば、事実上は標準の.net framework環境とbrains-N-brawnによる``ARToolKitPlus - modified to be a DLL and export C-style functions that can be pInvoked from C#''だけで完結する。今のソリューション構成はARToolkitPlusの.netラッパーとDShowNETのプロジェクトをそれぞれ独立させて、それらのビルドターゲットアセンブリを自分のプロジェクトから参照するようにしているが、これは必要ならばひとつのプロジェクトにまとめる事すらできる。

ARToolkitを使う上では、導入しなければいけない物が多すぎて開発のスタートアップ段階で躓きやすいのと、人に配布するとき何をどうすれば良いのかさっぱりわからないというのが問題だと思うのだが、私が今やっているARToolkitPlusの.netラッパー+DShowNET+WPFの組み合わせならば、標準の.net framework環境とbrains-N-brawnによるARToolKitPlusのdllで全て完結する。しかも3DについてはWPFによって容易なコーディングが可能だ。初音ミクがウマウマする動画がニコニコ動画で流行った後、ARToolkitはアカデミックな領域以外ではやはり敷居が高くて一時的なブームに終わった感があるが、この試みをもう少し続けていけば、かなり敷居を下げられると思う。

あれれ?

かなりコードが整理されてきて、もう動かない理由なんか何もないのだが、うーん、サンプルと同じ事をキャプチャした動画に対してやっているだけなのに動かない。

ひょっとしたらカメラのキャリブレーションファイルに、ARToolkitPlusとARToolkitで互換性がないのかもしれないと思ってARToolkitPlusを落としてきたが、ツールのバイナリがなくC++のコードのみ。これでもARToolkitと違って整理されたクラスライブラリになっているので一部では歓迎されたようだが、俺としては、もうC#以外触りたくない。せめてJavaくらいだ、許せるのは。あ、VBのVer.6までは別格ね。

というポリシーを曲げて現在Visual C++ 2008 express editionをインストール中。プリプロセッサ使いまくりのコードだったらやだなあ。ビルド通すのに数日かかったりしないだろうな。

directshow関連

ARToolkitをWPFで動かすサンプルが手に入ってわりとうまく行っているので、PCカメラからのキャプチャの方法をメモ:

http://web.sfc.keio.ac.jp/~shokai/archives/2007/01/directshownet-capture-save-image.html
http://www.codeproject.com/KB/directx/directshownet.aspx
http://www.codeproject.com/KB/directx/directxcapture.aspx

2009年2月3日火曜日

ARToolkit+WPFのサンプルらしきものを見つけた

http://www.hitlabnz.org/forum/showthread.php?t=546
http://www.brains-n-brawn.com/default.aspx?vDir=wpfaugreal

NyARToolkit with WPFの途中経過

980円で買ってきた超安物のPCカメラでもサンプルプログラムはちゃんと動いてなかなか面白かった。

自分で書いたWPFアプリケーションのコードも、PCカメラからキャプチャした動画を表示してマーカーを検出する所までは簡単にできた。

問題は、やはりマーカー座標系からカメラ座標系への変換と射影変換。

private Matrix3D NyARDoubleMatrix34ToMatrix3D(NyARDoubleMatrix34 mat)
{
return new System.Windows.Media.Media3D.Matrix3D
(mat.m00,mat.m10,mat.m20,0,
mat.m01,mat.m11,mat.m21,0,
mat.m02, mat.m12, mat.m22, 0,
mat.m03,mat.m13,mat.m23,1);
}

こういうメソッドを作っておいてViewport3D.Cameraの子要素にしたMatrixCameraオブジェクトであるMainViewport3DCameraに対して

//AR用カメラパラメタファイルをロード
m_ap = new NyARParam();
m_ap.loadARParamFromFile(Properties.Settings.Default.AR_CAMERA_FILE);
m_ap.changeScreenSize(Properties.Settings.Default.Width, Properties.Settings.Default.Height);

//投影行列をAR用カメラパラメタから設定
NyARPerspectiveProjectionMatrix ProjectionMatrix = m_ap.getPerspectiveProjectionMatrix();
MainViewport3DCamera.ProjectionMatrix = NyARDoubleMatrix34ToMatrix3D(ProjectionMatrix);

こんな感じで射影行列を設定。んでもってModelVisual3DオブジェクトであるcubeVisual3Dに対して

cubeVisual3D.Transform=new MatrixTransform3D(NyARDoubleMatrix34ToMatrix3D(result_mat));

こんな感じで変換行列を設定。

コードとしてはこんな感じで良いはずなのだが、何かを勘違いしていて動かない。

そもそも変換行列の類は意味合いがライブラリ/フレームワークでみんな異なるからなあ。でも、行列の行-列番号のアサインとかちゃんと確かめたんだけどなあ。

ARTOOLKITとNyARTOOLKIT事始め

NyARTOOLKIT For C#を使い、キャプチャはキット任せ、グラフィックはWPFの3D描画を使って何かやってみよう作戦の覚書:

NyARTOOLKITを使うので、ARTOOLKITはソースは必要なく、キャリブレーションやパターンデータファイルの作成用ツールをバイナリで入手できれば十分。なのでSourceForgeのページからARToolKit-2.72.1-bin-win32.zipをダウンロードして適当な場所に解凍しておく。

これらバイナリの使用にはGLUTが必要。Nateのページからglut-3.7.6-bin.zipをダウンロードして中身のglut32.dllをsystem32フォルダにコピー。Windows SDKをインストールしているので、せっかくだからついでにc:\program files\Microsoft SDKs\Windows\v6.1フォルダ以下のInclude\glフォルダにglut.hを、Libフォルダにglut.libをコピーしておく。

NyARTOOLKITはDirectX SDKを必要とする。ここからDirectX SDK (November 2008)をダウンロード。

NyARTOOLKITはここからNyARToolkitCS-2.1.1.zipをダウンロード。適当に解凍。DirectX SDKが導入されていれば直ちにforFW2.0\NyARToolkitCS.slnをビルド可能。せっかくなのでここにあるUma2Desktop(for Windows(.Net Framework))をダウンロード。これも直ちにビルド可能。

TODO:
1.描画をWPFにやらせたいので、NyARToolkitのCaptureTestサンプルアプリケーションを下にスケルトンを作る
2.工学ナビの記事を下にARToolkit付属のバイナリを使ってパターンファイルとキャリブレーションファイルを作ってみる。

2009年2月2日月曜日

スパム?

アクセス解析を見ていると定期的に
http://www.blogger.com/navbar.g?targetBlogID=ほにゃららほにゃらら
こんな感じの参照元からのアクセスのバーストがある。何だろうこれ。スパムにしては効率悪いよなあ。

と思っていたら今日モノホンのコメントスパムが。Cheap Air Jordan 、Nike Dunk and Silver Tiffanyがどーたらこーたら。容赦なく削除した。blogspotってcaptcha通らないとコメントできなかったよなあ。こうしてまたひとつ、スパムボットが知性認定を受けたのであった。

#captchaってのは本来、高度な人工知能は知性を持つと言えるのか、との問いに対し、「ある種のテストをパスするのならば実際上区別しようがなくなるので知性があると認めるしかないだろう」と、確かチューリングが提案した概念。すなわちその満たすべき性質とは、(1)人間は必ずパスできる(2)ショボイ人工知能にはパスできない、である。だからcaptchaが破られるのは人工知能の性能向上という喜ばしい事態だし、人間ですらパスするのが容易でないテストを作ってスパム耐性を高めるというのは本来の意味のcaptchaとしては本末転倒なのだ。俺、最近しょっちゅうcaptchaに弾かれるのだが、本当に人間なのか不安になるぞ。
#で、「んなこと言ったって機械は機械だよ。知性だなんて認めたくないよやだやだ」と駄々をこねたのがいわゆる中国人の部屋のパラドックス。パラドックスというより、アンドロイド差別宣言だね。

し、しどい・・・


金曜日にロングポジションが完璧に失敗して12000円の追証が発生。でもどう考えても平均線からの強い反発なので今日の下げで追証解消できるだろうと7980でドテンショートした。

で、今日の動き。何だよこれ。9時30分くらいに7800を割ったタイミングで返済すれば余裕で首はつながった。でも、一度返済してしまうと大引けまで資金が拘束されるので前場引けかせめて10時半まで引っ張る事に決めていたのでそのまま放置していたらひたすら強い上げ相場に。今日上げる意味がさっぱり分からない。これ絶対後場で前場安値の7790を割ってダダ下げの展開になるよ。ムキー。

まあ、現在の健康状態ではデイトレはどうにも負担になりすぎるので、手を引く良い機会になったということで。

製作者がこの体たらくでは「身も蓋もない」の説得力も何もあったものじゃないわけですが、まあ皆さん私を反面教師にして、欲を出してオーバーナイトなんてせずにデイトレに徹して、「身も蓋もない」を活用してください。こういう展開になった以上ちょっとずるい引用の仕方だけど、「身も蓋もない」をデイトレに本格的に活用した三週間の記録はこちらにあります。

本当に、スイングスタイルに切り替えた途端に外しまくって一週間で一気に損が膨らんだのには肝が冷えた。レバレッジの大きさを全然理解していなかった。しかしデイトレに限ればならば225先物は実に魅力的な投資対象になり得る。いつか機会があったらまた参戦したいな。誰か種になるくらいの量の寄付をアドインに対してしてくれないかなあ。

というわけでタイジョー。ちゃんちゃん。

2009年2月1日日曜日

はっちゅう君プラスのアドインライブラリのAddInFormの使い方(改)

何度か取り上げたAddInFormの拡張の仕方。はっちゅう君プラス本体を最小化した時にフロートウィンドウに対するドックインジケータが表示されるというのは勘違いだった。よって、(1)フロート時に最前面に表示する(2)フロートウィンドウが起動時に復元された時サイズも正しく復元する、の二点のみを解決すれば良い。コードはこんな感じ。

public class ほにゃらら : AddInForm
{
#region メソッド
private void setIndependentWindowOrDependentWindow()
{
if (this.DockState == DockState.Float) {
this.TopMost = true;
this.TopLevel = true;
} else {
this.TopMost = false;
this.TopLevel = false;
}
}
#endregion

#region カスタムしたメソッド
protected override string GetPersistString()
{
//インスタンス毎に保持する設定を永続化文字列に", "に続けて繋げて返す。
return base.GetPersistString() + ", " + ほにゃらら;
}
protected override void OnDockStateChanged(EventArgs e)
{
setIndependentWindowOrDependentWindow();
base.OnDockStateChanged(e);
}
protected override void OnSizeChanged(EventArgs e)
{
if (this.DockState == DockState.Float) {
this.ClientSize = this.ClientSize;
}
base.OnSizeChanged(e);
}
protected override void OnLoad(EventArgs e)
{
setIndependentWindowOrDependentWindow();
base.OnLoad(e);
}
#endregion
}

ただ、起動時に復元されたフローティングフォームの最前面表示の設定(TopMost=true)が有効にならない問題は未だ解決せず。まあ、ビルトインの注文フォーム(フロート)でも同じ問題があるし、ここまでやれば御の字だろう。


追記:上記問題は、setIndependentWindowOrDependentWindowメソッドを

private void setIndependentWindowOrDependentWindow()
{
if (this.DockState == DockState.Float) {
this.TopMost = true;
this.TopLevel = true;
} else {
this.TopMost = true;//ポイント!
this.TopLevel = false;
}
}

と変更すると解決するようだ。どうやら、はっちゅう君プラスの起動時にフォームが復元される時、DockStateがDockState.Floatにセットされていないのが問題のようだが、ドッキングフォームの場合TopMostプロパティに意味はないので、フロート状態であるか否かに関わらずTopMostプロパティをtrueにしてやれば良いという事らしい。

最近の体調

最近、朝6時くらいには目が覚めてしまって、それから1~2時間、思考力・集中力の阻害される症状が和らぐ時間帯がある。その代わりこの時間帯は筋肉痛がひどく、耐え難い。

2009年1月31日土曜日

windows7 メモ

入れたもの:
  • ネット関係
    • Firefoxと自分用プロファイル
    • はっちゅう君プラス
  • 画像関係
    • paint.net
    • picasa3
  • 動画関係
    • CCCP(media player classicもばっちり動作)
    • どこかかから見つけてきたcoreavcdecoder.ax(power shellを管理者権限で起動してregsvr32する事が必要。管理者でログインしていてもregsvr32へのD&Dでは権限昇格が自動で行われない操作のためエラーとなる)を入れる。H.264にはこれを使うのでffdshowの設定でH.264はdisabledに。
    • mediacoder(MPUIもばっちり動作)
    • super C(起動すらせず)
  • 開発環境
    • Visual C# 2008 express edition
    • .net reflectorとアドインのbaml viewer
    • sandcastle
    • sandcastle help file builder
  • ユーティリティ
    • Noah
    • ImgBurn
    • Virtual CloneDrive(Daemon toolsは動作せず!)

ガジェットはネットから落とした物は画面が表示されないものばかり。ネットで調べてwindows 7で動作報告があるものを中心に試したので自分の環境固有の問題かも。

geoshellを卒業したいので、CPU・メモリのメータとネットワーク転送量のメータは必須。前者に関してはこれは動作した。後者に関しては選択肢はnetwork utilization gadgetをこの記事からダウンロードするしかない(何故かWindows Live Gelleryから消えている)が、自分の環境では文字のみの表示となった。ほか、gigazineの記事で表示されているもののうち、Presto's Hard Drive Monitor、System Controlも同じ現象。これらの問題はたぶんグラフィックカードのドライバの問題だと思うのでアップデート待ち。

追記:再起動するだけでガジェットに文字しか表示されない問題は解決した。Presto's Hard Drive Monitorはgadget.cs中でbackground idをターゲットとするスタイルにWidth:130;の設定を追加する必要があった。

新しいタスクバーは永らく使っていたDesktop Spiceの代替として使って余りある。とても便利だしクール。

ずっとWindows Xpを標準とはかけ離れてカスタマイズして使ってきた(代替シェルのgeoshellとランチャーのDesktop Spiceとファイラの「あふ」が中心)けれど、これでようやく標準の範囲で自分の満足の行く環境が作れそう。あと問題はファイラだけど、エクスプローラは特別使いやすくなってはいないし、いくつか検討してみよう。

最後に引っかかった事:
  • Realtekのオンボードオーディオは、以前はアナログ出力時に同時にS/PDIF出力もされていたのだが、出力先がアナログとデジタルに分かれるようになった。後者を選択しないとアナログ音声のS/PDIF出力もAC3等のS/PDIFパススルーもできない。その選択をした上でffdshowのオーディオの設定でAC3やDTSをS/PDIFにすればパススルーされる。
  • 電源ボタンを押した時に選択ダイアログを表示するオプションが無くなった!!!これは最低の改悪。フィードバックを送信しておいた。
  • 隠しフォルダに頼りすぎた設計になっている。何でいまさらdesktop.iniをフル活用してんの?おかげで、フォルダオプションから隠しファイル・フォルダを表示する設定にして常用できない

2009年1月30日金曜日

windows7


遅ればせながらWindows7を入れてみたのではっちゅう君プラスと自作のアドインの見た目を。自分でコントロールのテンプレートを定義した部分はもともとLunaやAero似なので、わりと不自然じゃなく見た目が格好良くなる。

四本値を表す四辺形はWPFの3D機能を使っているのだが、Vista以上だとアンチエイリアスがかかるという噂をようやく確かめる事ができた。この画像ではわかりにくいが、ちゃんとアンチエイリアスされている。

今日の戦績

下には買い建て玉ホールドと書いたのだけれど、さすがにドテンショートした。しかし、何じゃこのまったく動かない相場は。ひどいよ・・・

さて、これで追証が若干発生したので、追加差し入れをしないならば、微益での決済はできない。月曜日の正午までに+7850まで下げてくれないと退場。

トレードを、お金を飲み込むパチスロもどきにする気はないので、一枚ぎりぎり建てられる所から初めて、また一枚分の証拠金を割ってしまったらすっぱりと縁を切るつもりだったので、証拠金の差し入れはしないつもり。

でも、これで月曜日に下げなかったら本当に詐欺だよなあ。平均線からの反落くらいはセオリー通りに強い動きになって欲しい。もしそうなったらもう少しだけ命が繋がる。


一昨日の8150Sホールドで正解だったんじゃん。225先物こえー。短期~中期ホールド戦略に切り替えたとたんに一週間で退場になった。ちょっと頭を冷やします。

「身も蓋もない」のユーザの皆さんはくれぐれもデイトレに徹してくださいね。


一応ドレッシング期待で買い建て玉を今日一日はホールドする。

2009年1月29日木曜日

銘柄リストアドインのフロート状態


銘柄リストアドインをフロート状態にすると、注文フォームと同じで、はっちゅう君プラスを最小化しても最前面に表示され続けます。さすがにこれ単体でトレードするのには無理があると思いますが、他のツールと併用する場合には便利でしょう。

今日の戦績

夕場途中にて:
---------------------------

---------------------------
トータル損益は-13596円です。

このうち手数料の支払いが96円含まれます。

買い注文回数は2回, 売り注文回数は0回です。

未返済の建て玉1枚の見込み損益は手数料を含まずに-12548円です。


---------------------------
OK
---------------------------
8150の売り建て玉を持越しした状態でNYが超調子が良く、日経もGU。それでも十分こらえて現物への鞘寄せを待って8285でドテンロング。なのに、その後は笑うしかない嫌がらせのような値動き。8150くらいまで下げた後上げるけれど上げきれず寄り天。ちなみに、この間外出しておりました。何じゃそりゃ。

もう散々。先週まででデイトレにおける「身も蓋もない」の力は実証できたので、今週は体調が劇悪な事もあり短期~中期トレードに切り替えたら一気に利益が消え去った。日経225のレバレッジは大きいので、短期~中期トレードでやれるのは、証拠金が本当の意味での担保になっているお金持ちな人だけなんだね。高い授業料でした。

というわけで、デイトレを集中してやれる体調になれるまではトレード自体お休みしよう。

ただ、さすがに悔しいので今日の買い建てはホールド。上手く行って月末のドレッシング上げも来るならば8500付近まで行って利益になるけれど、今晩のNYと為替次第の完璧丁半バクチだ。こんなのは投資でも何でもない。

銘柄リストアドインにデータの自動更新機能をつけました


これで、データの自動更新と銘柄のダブルクリックによる注文フォームの起動が実装されたので、株価ボードと胸を張ってよべるようになったぜ。

いつもの所のファイル置き場からどうぞ。

売り建て持越ししたらダウ超強い&日経GUで、現物鞘寄せを待ってドテンしたら何故かその後下げる展開となり、とてもこんな事書いている場合じゃなかったりする。あーあ、今日よほど上手くやらないと今月の利益全部消えたな。後は明日のドレッシング期待で買い持ち越しくらいしかないけれど、明らかにリスクが大きいよなあ。

2009年1月28日水曜日

ダウ上げてるし・・・

先週まででデイトレにおける「身も蓋もない」の力は立証できたため、今週は気がゆるんで楽して設けようとオーバーナイトをやりすぎた。明日は痛い目を見ることになりそうだ。

「身も蓋もない」のユーザの皆さんは、この辺りの教訓を考慮しつつ、デイトレでのトレンドフォローに徹してくださいね。

今日の戦績

大引け後:
Lv1.00(miniの平均発注枚数。整数部が最大発注可能枚数の半分の切り上げ値)
---------------------------

---------------------------
トータル損益は-980円です。

このうち手数料の支払いが480円含まれます。

買い注文回数は5回, 売り注文回数は5回です。

未返済の建て玉1枚の見込み損益は手数料を含まずに+3000円です。


---------------------------
OK
---------------------------
微損で売り建て場所を8030から8150に変換できた。後は7000円台復帰まで気長に待とう。とりあえず7950になれば昨日の2万円の損が取り戻せる。そこから先、7500円とか目指してくれれば御の字。金曜日のドレッシング買いの前に、つまり明日、ある程度下げてくれれば良いのだけど。

言っておくがこれ、上げている間ノーポジにしておいたとかそんな簡単な話じゃなくて、初め1万円損した後前場から後場にかけての上げでしつこくロングを取り続けてその損を取り返した上で売り建てし直したんだぞ。それなりに苦労しているんだぞ。最近の、ロングを怖がっている事に対する反省がなかったら多分もっと損していたんだぞ。

体調

最近、朝に目は覚めるのだが朝から夜までずっと倦怠感が最悪。目がしょぼしょぼして目を開けているのすら大変。それとパソコンのモニタを見ていても目がぼやけて焦点が合わない感じで大変。何に手をつけるにも苦労するのでWEBをぼけっとみていがちなのと、それでトレードに参戦しがちになってしまうのだが、最近ではできるだけモニタを見る時間を減らすように心がけている。でもって、午後には強烈に眠たくなって午後と夕食後はわりと寝ている。何故か夜を回ってくると倦怠感が少し和らぐ。目はショボショボするし目~額がつーんとして何をするにも苦労するのは変わりないのだけど、それでも何だか寝られなくなって起きている。でも、2時くらいにはまた強烈に眠くなって寝てしまう。どこまでが薬の副作用でどこまでが元からの症状なのやら。

2009年1月27日火曜日

今日の戦績

夕場引け後
Lv1.00(miniの平均発注枚数。整数部が最大発注可能枚数の半分の切り上げ値)
---------------------------

---------------------------
トータル損益は-18220円です。

このうち手数料の支払いが720円含まれます。

買い注文回数は7回, 売り注文回数は8回です。

未返済の建て玉1枚の見込み損益は手数料を含まずに-500円です。


---------------------------
OK
---------------------------
欧州が凄いことになっているのと、為替も少し円高に触れた事でもう少し大きく戻すかと思ったけど、8000付近まで。8000付近からの反発を使って上手い事やろうとしたが、1000円ひねりだせただけ。8030S持ち越し。7800の売り建て玉を馬鹿みたいに持ち越すのに比べると4500円だけ有利になった。

今晩のダウ暴落しないかな。7500くらいまで。それで明日の日経が寄り7600円から始まれば御の字。

Lv1.00(miniの平均発注枚数。整数部が最大発注可能枚数の半分の切り上げ値)
---------------------------

---------------------------
トータル損益は-19624円です。

このうち手数料の支払いが624円含まれます。

買い注文回数は6回, 売り注文回数は7回です。

未返済の建て玉1枚の見込み損益は手数料を含まずに-8548円です。


---------------------------
OK
---------------------------

後場は体調が少し良かったので細かいトレードでデイトレもどき。やった分は全勝したけど8000-8100の中で小幅にとっていったので26000円の損を20000円以下に持っていくまでしかできなかった。8035で売り建てし直したので7800Sでホールドするより正味3500円得をした形になっているが、それはあくまで売り建てホールドが正解としての話。先物高いなあ。

売り建てホールドで負ける相場環境とも思えないけど、各種メディアが、今日の上げに対して、問題はまだまだ山積みで上値余地は限られているとか総悲観で、大体あいつらがそういう風に言い出すと景気良く上げるわけで。MA接近までは強気ショートも強気ロングもできないという観察を冷静にしつつ、だったら参加するなよと思いつつ、それでもついポチッとなしてしまうのであった。

それと、昨日大引けまでで今月の益率が22%を超えたのだが、やはり昨日は株でも為替でも大きく利益を出した向きがいるようで、トレードアイランドの順位はむしろ下がってしまった。それでこの損が取り戻せないとさらに下がるぞ。ショボーン。

後場寄りGUキター。まいったなあ。とりあえず8000ぴったりでドテンしたら、何か大幅に遅延して8010になりやがった。これ下手をうつと今月の益が全部消えるぞ。既に損が2万円を超えた。

2万円損しようと、デイトレを超集中してまじめにやれば一週間あれば十分取り返せるんだが、ちょっと今の体調ではデイトレを本気でやるのは厳しい。MA接近までロングをホールド戦略で行くか?でも、今日の上げって完璧為替連動だし、決算発表も色々あるし、不安材料山積み。今日中に利確の上でショートにドテンできる場面があれば何も思い悩む必要はなくなるのだが・・・

それにしても、こういうやり方だと「身も蓋もない」を全然活かせなくてつまらないなあ。先週までのトレードで「身も蓋もない」の力は実証できたと思うので、デイトレーダーの皆さん、ド下手な私の事は気にせず、どうかご活用下さい。

前場中:思いの他高く寄った上に最初の30分で下げて始まったので、ちょうど分かり易い7800でショート。25, 75MAがいずれもはるか上方なのが気になるが、まあ短期~中期のホールドすれば必ず利益になるだろうと、これはしばらく放置の積もりでいた。しかし前場で7900にトライされてさすがに7895でドテンロング。突破できそうにないのを見計らって7875でショートにドテン。ただホールドした場合に比べて2000円損するのと引き換えにリスクを減らした・・・のか?何かまた7900にトライしているし、ただの往復ビンタだったりして(笑)

ただ最近の体調だとあんまり激しくデイトレはできないしなあ。資金に余裕が出来てきたので1000円単位のGU, GDはないと踏んで短期~中期ホールドでスイングっぽくやっていって益が出せるならそっちのが楽で良い。だからしばらく実験的に試してみるつもり。今月はちょっとラッキーにも勝ちすぎたのでそういった実験でいくらか利益を減らしても良いだろう。

追記:結局7900を突破して素直に上げ続けているなあ。7895でロングした後7935で指しておいたのだが、今ちょうどそこまで上げた所。やっぱり昨日不安感を感じた、ショート寄りのバイアスが悪さをしている。結局今日の前場は最初の30分以外はただ上げるだけの相場なのにずっと逆行してしまったし、一度だけ取ったロングポジションは損きりして利益になっていない。何だか、どこかで大怪我しそうで嫌な感じだなあ。明日GUってそんなにあり得るとは思えないけれど、頭を冷やす意味も込めて、今日含み損が大きくなっても引けで切ろうかなあ。

前場寄り前:
テクニカル的には三日連続GUでMAに接近した後再び下落するってシナリオになりそうな状況だけど、今の相場環境でロングポジションを短期でも取るなんてできねーよ。

場中に超短期でロングポジションを取りつつ、引け際の急落なんかを逃さずショートしていかないと利益にならない難しい局面だ。見逃すのも一つの手かなあ。

2009年1月26日月曜日

銘柄リストアドインを更新


一つ前のエントリの成果を受けて、銘柄リストアドインのリスト部分の見た目は完全にはっちゅう君プラスの配色に準拠しました。はっちゅう君プラスのツール-オプションメニューから配色を選択すると、それに応じてちゃんと変わります。

いつもの所のファイル置き場からどうぞ。

列の区切りに罫線が惹かれてないのが心残りだけど、これ、GridViewのスタイルかテンプレートに手を出さないといけないのかなあ。まあ深入りはしないでおこう。

ListViewを縞々にして罫線を描いて選択行の背景色を任意に設定する

ListViewを縞々にするのは色々やり方があるけれど、汎用性という意味では、ItemContainerStyleSelectorを使う方法が一番良い。罫線を引く時に、BorderThicknessを最初は1, 1, 1, 1にして残りは全て1, 0, 1, 1にするなんて事はAlternationCountなんかを使っては実現できない。

しかし、ListViewの.ItemContainerStyleSelectorを設定すると、ItemContainerStyleの設定が無視される(カスケーディングされない)し、ListViewItemをターゲットにしたデフォルトスタイルを設定していも何故かそれも無視されてシステムのデフォルトのスタイルになってしまう。このため、このエントリのタイトルに書いた事を全部実現するのは結構大変。

結局ListViewのリソースにクラシックスタイルからキー名GridViewItemContainerStyleKeyのテンプレートを丸ごとコピーしてトリガーを削除したテンプレートを書いておいて、ItemContainerStyleSelectorではそれをTemplateプロパティに割り当てた上でIsSelectedプロパティに対するトリガーも設定し、さらに縞々や罫線の設定をするという方針で解決した。

xamlは例えばこんな感じ:

<ListView Grid.Row="3" Name="StockList" ItemsSource="{Binding}" VerticalAlignment="Stretch" SelectionMode="Single" Background="#FFEDFEED">
<ListView.Resources>
<ControlTemplate TargetType="{x:Type ListViewItem}"
x:Key="ListViewItemWithoutAnyTrigger">
<Border Name="Bd"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="true">
<GridViewRowPresenter
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
</ControlTemplate>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Header="コード" Width="Auto"
DisplayMemberBinding="{Binding Path=Code}" />
・・・ほにゃらら・・・
</GridView>
</ListView.View>
</ListView>

で、StyleSelectorはこんな感じ:

public class StockListItemStyleSelector : StyleSelector
{
public Color Back1 = Colors.White, Fore1 = Colors.Black,
Back2 = Colors.White, Fore2 = Colors.Black,
BackSelection = Colors.Red, ForeSelection = Colors.Green,
Rule = Colors.Beige;
public override Style SelectStyle(object item, DependencyObject container)
{
ListView listView = ItemsControl.ItemsControlFromItemContainer(container) as ListView;
int index = listView.ItemContainerGenerator.IndexFromContainer(container);

Style stockListItemStyle = new Style(typeof(ListViewItem));

//トリガーを除いたテンプレートに設定
stockListItemStyle.Setters.Add(new Setter(ListViewItem.TemplateProperty,
listView.Resources["ListViewItemWithoutAnyTrigger"]));

//背景色、前景色を縞々にする
stockListItemStyle.Setters
.Add(new Setter(ListViewItem.BackgroundProperty,
new SolidColorBrush(index % 2 == 0 ? Back1 : Back2)));
stockListItemStyle.Setters
.Add(new Setter(ListViewItem.ForegroundProperty,
new SolidColorBrush(index % 2 == 0 ? Fore1 : Fore2)));

//罫線を設定する
stockListItemStyle.Setters
.Add(new Setter(ListViewItem.BorderBrushProperty,
new SolidColorBrush(Rule)));
stockListItemStyle.Setters
.Add(new Setter(ListViewItem.BorderThicknessProperty,
new Thickness(index==0?1:0, 1, 1, 1)));

//選択行の色を設定する
Trigger selectionTrigger = new Trigger() {
Property = ListViewItem.IsSelectedProperty,
Value = true
};
selectionTrigger.Setters.Add(new Setter(ListViewItem.BackgroundProperty,
new SolidColorBrush(BackSelection)));
selectionTrigger.Setters.Add(new Setter(ListViewItem.ForegroundProperty,
new SolidColorBrush(ForeSelection)));
stockListItemStyle.Triggers.Add(selectionTrigger);

return stockListItemStyle;
}
}

これでstockListPanel.StockList.ItemContainerStyleSelector = new StockListItemStyleSelector() {Back1=ほにゃらら・・・};とやれば良い。

良かった良かった。