スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

WEBカメラを扱った際わかりにくかったところ

~カメラ操作~
DirectShowについて

わかりにくいところ
・フィルタ[IBaseFilter]
ソースフィルタ(USBカメラからデータを取得)

変換フィルタ(圧縮やエフェクト処理)

レンダラフィルタ(ディスプレイに出力)

・グラフ
フィルタの集合体

・フィルタグラフマネージャ [IGraphBuilder, IMediaControl, IMediaEventEx]
グラフを作成したりその経路を制御する

・キャプチャグラフビルダ [ICaptureGraphBuilder2]
フィルタグラフマネージャに接続して操作するコンポーネント
いろいろなメソッドが用意されている

た、ただのメモです。
スポンサーサイト

BVHソフト完成

とりあえず完成した。左下の余ったスペースにこれを作った。
motion2.jpg
画像は無限枚取り込めるようにして矢印で画像を切り替えする地味なタイプにした。

AllMotionResetボタンに確認ダイアログをつけたり、保存場所を指定できるようにしたり、地味な点を改良した。

大好きな背景の色やインターフェイスなどを改良したかった(大好きな作業)けれど地味な作業があまりに続くためにこれで完成とすることにした。

モデリングも取り掛かった。以前の記事に書いたようにハイポリの方が
・点の数を無視できる
・面の重なりが汚くてもいい
・奥のほうにゴミがあってもよい

などの理由から格段に効率があがった。
フリーの素材を流用できる設計にしたのがよかったのだと思う。


ねよっと。

変なソフトほぼ完成!

今更あけました、おめでとうございます。

つ、疲れました。正月まったりしすぎたせいもあり更新されずにここまで来てしまいました。
今日からは去年のような更新になると思いいなぁ(汗)

さてさて、実装を頑張っていたのですが、1週間で終わらせるるもりが1週間半かかりまだ完成していません。
というもののほぼ完成したのでインターフェイスをアップしておきます。
motionc.jpg

っう。
過去の記事を読んだ人はこれを見てつっこみたいことでしょう。
「これ動的読み込みできてる?」と。
・・・・

実は当初は関節に色を塗ったテープを張りいろいろ試行錯誤しておりました。
このように↓
motion.jpg
しかしながら「安いカメラ」のせいで色がしっかりとれないのです!
2500円で済ませようとした私のせいもあるのでしょうが。。。

どっちにしろ体に隠れた部分はクリックして点を入れさせる仕様にする予定だったのでサックリ仕様変更して自分で全部の点をいれるように作り変えました。

ここまで作る必要があったのかどうか悩むところですが、キャプチャがない状態で3D空間でモデルをこねくり回すのもナンセンス。後々の作業効率を考えると多分ありだと思いたいです。
人形は以前トイザラスで購入したアクションフィギュア「ミクロマン」は名前の通り小さすぎたので、デッサンでよく使われるというボークスというメーカーのフィギュアを購入してみました。稼動が非常に多く満足しています。

ここまでくると二枚の画像を撮影しながら溜め込んでいき、後からそれを見ながら姿勢情報をクリックしてモーションを加えていき、最後にMakeBVHをぽちっとなという機能がほしくなります。

ということであとは2枚の画像を保存していく機能とインターフェイスを作成したら完成です。

以下は実装に伴って躓いた部分やソースでっす!

とりあえずクォータニオンをオイラー角に直すには、
クォータニオン→行列→オイラーの手順を踏みます。

//クォータニオン→オイラー(ZXY回転)
Vector3 QtoE(Quaternion q)
{
Vector3 v = Vector3.Zero;
Matrix m = Matrix.CreateFromQuaternion(q);

if (m.M32 == 1)
{
v.X = MathHelper.PiOver2;
v.Y = 0;
v.Z = MathHelper.ToDegrees((float)Math.Atan2(m.M21, m.M11));
}
else if (m.M32 == -1)
{
v.X = -MathHelper.PiOver2;
v.Y = 0;
v.Z = MathHelper.ToDegrees((float)Math.Atan2(m.M21, m.M11));
}
else
{
v.X = MathHelper.ToDegrees((float)Math.Asin(m.M32));
v.Z = MathHelper.ToDegrees((float)Math.Atan2(-m.M12, m.M22));
v.Y = MathHelper.ToDegrees((float)Math.Atan2(-m.M31, m.M33));
}
return v;
}


通常のフォームEXEでXNAの画面を表示するときは
public partial class GraphicsDeviceControl : Control
などとしてコントロールを作成するとよいようです。
そもそもイベントというものをきちんと扱ったことがなかったためにその辺の勉強から始まり、9割予想で作成したという驚愕のソフトです。
ラジオボタンのチェックが変わったら周りの黒いアクティブカーソルが切り替わるだけの操作を作るのに2時間とかかかりました。

一番はまったのがここです。
str_MOTION += WriteEuler.Y + " ";
str_MOTION += WriteEuler.X + " ";
str_MOTION += WriteEuler.Z + " ";

モーションの文字列を作成する際に記載するオイラー角の順がZ-X-Yオーダーの場合↑のようになります。
これのせいで1日潰れたと思います。

あと、クォータニオンから求めたオイラー角の符号を座標系によって変えなきゃいけない気がします。

あとは。。っあ!
BVHのローカル座標の作り方はどこかのサイトに「ボーンの向いている方向がZ軸に沿う」と書いてありましたが、違うようです。DirectXのローカル座標が沿うというサイトもあったので多分そっちが正解なのかもしれません。
初期姿勢がワールド座標と同等の座標系を持ち、そこから各々の回転によりローカル座標が決まります。
オフセットがどの方向であろうと初期姿勢の座標系はすべて同じです


BVHのボーン関係は書き出すと(間違った情報が)ありえない量になると思うので割愛します。


ここにきて、とりあえずBVHは書き出せたのに肝心のエルフレイナで読み込めないではないか・・
エルフレイナのBVH読み込みはまだβ版らしく不具合があるという情報もあったのでサポートのほうに問い合わせてみることにした。
エルフレイナ作者の対応は本当に早くて親切なのでいつも助かっていたりします。

明日からモデリングも同時に進行できたらいいな。。。。


ねよっと。

BVHの仕様について

せっかくボーン姿勢をXYZ座標にできたところでほかのモーションソフトで弄れないと意味がない。
フォーマットを何にするか考えてみたがエルフレイナでモーション作成する予定だったので読み込めるBVHにすることにした。
BlenderはBVHの読み込みも書き込みもあるし、丁度いいかもしれない。

さっそくフォーマット仕様を調べることにした。


~BVHフォーマットの仕様のまとめ~

・テキスト形式である
・右手系座標で軸は任意
・関節ノードに関する情報を記述
・関節回転はオイラー角で記述
・角度の単位はDegree
・HIERARCHY部=キャラクタのスケルトン断層構造を記述
・MOTION部=動きのデータを記述
・#や//や/**/でのコメントの書き込みは不可能



HIERARCHYをちょっと掘り下げる


・JOINT(必ず子を持つ)要素はOFFSETとCHANNELS

・ROOT(始点となる特殊なJOINT)要素はOFFSETとCHANNELS

・END(終点になり子を持たない特殊なJOINT)要素はOFFSET

・OFFSET(親ノードからJOINTへの3次元オフセット成分)XYZの順で記述
ROOTノードの場合は初期位置を指す

・CHANNELS(関節の自由度、位置の自由度、回転の自由度の順で定義)


例) コメントは本来NG

HIERARCHY//今からスケルトン構造を書き出すよ~
ROOT root_name//ROOTノードの名前は「root_name」です
{
//このROOTノードの位置は(0,0,0)です
OFFSET 0 0 0
//XYZ軸に関する位置、X-Y-Z順で合成されるのオイラー角回転の計6つの自由度を持つ
CHANNELS 6 Xposition Yposition Zposition Xrotation Yrotation Zrotation

End Site//子のJOINTはEND1個だけ 名前は「Site」
{
OFFSET 0 10 0//Siteの位置
}
}


予想できると思うがJOINTの親と子の記述は以下
1-2-3
の場合は
ROOT 1
{
     JOINT 2
     {
          END 3
          {
          }
     }
}
となり

  2
1<
  3
の場合は
ROOT1
{
     END 2
     {
     }
     END 3
     {
     }
}
のように記述すればOK

続いてMOTION部分の記述について触れてみる
・Frames: N(記述する総フレーム数)
・Frame Time: SPF(フレームあたりの時間長 FPSの逆数)

例)
MOTION//ここから動作の記述がはじまりますよっと
Frames: 100//モーションは100フレームあります
Frame Time: 0.033//1フレーム0.033second/sec
0 0 0 0 0 0 0 0 0 // フレーム1のスケルトンの姿勢
0 0 0 0 0 0 0 0 0 // フレーム2のスケルトンの姿勢

0 0 0 0 0 0 0 0 0 // フレーム100のスケルトンの姿勢


姿勢情報の見方は次の通りになる
左から順にファイル冒頭からでてくるCHANNELSに割り当てられた要素とリンクする
ROOT root_name
 {
     OFFSET 0 0 0
     CHANNELS 3 ここA ここB ここC
     JOINT joint1
     {
          OFFSET 0 10 0
          CHANNELS 1 ここD
          End Site
          {
               OFFSET 0 10 0
          }
     }
}

だとすると姿勢情報は
A B C Dの順になる

(注意)この後日に書いた記事でこの順に関しての訂正があります
流用を考えている方は必ずその記事をチェックしてください


BVH自体はテキスト形式でOKらしいので仕様が理解できてしまえば扱うのは楽そうなイメージだ。
適当なBVHソースをコピーして自前でC#から作ったものをエルフレイナに読ませたらあっさり読み込めた。
いい感じだ。

角度がオイラー角というもので扱うらしいのだが何のことをいっているのかわからない。
また数学しなきゃいけないかもしれない・・・

ねよっと。

今日は数学

昨日の続き。
とりあえず速度どうでもいいとして作ったので最適化必要な場所多数あるかもです。
例のごとくインデントはFC2に文句言ってください。

a1,a2を端点とする線分とb1,b2を端点とする線分の交差判定(2D)
bool IntersectionFlg(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2)
{
return (cross(a2 - a1, b1 - a1) * cross(a2 - a1, b2 - a1) < 0) && (cross(b2 - b1, a1 - b1) * cross(b2 - b1, a2 - b1) < 0);
}


a1,a2を端点とする線分とb1,b2を端点とする線分の交点計算(2D)
Vector2 Intersection(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2)
{
Vector2 b = b2 - b1;
float d1 = Math.Abs(cross(b, a1 - b1));
float d2 = Math.Abs(cross(b, a2 - b1));
float t = d1 / (d1 + d2);
return a1 + (a2 - a1) * t;
}


ベクトル a と b の内積(2D)
double dot(Vector2 a, Vector2 b)
{
return a.X * b.X + a.Y * b.Y;
}


ベクトル a と b の外積(2D)
double cross(Vector2 a, Vector2 b)
{
return a.X * b.Y - a.Y * b.X;
}


3次元で2つの線が一番近くなる時の距離
引数
ベクトル1上のどこかの1点
ベクトル1
ベクトル2上のどこかの一点
ベクトル2

戻り値
一番近い時の距離
float LineDistance(Vector3 a, Vector3 Va, Vector3 b, Vector3 Vb)
{
Vector3 N = Vector3.Cross(Va, Vb);
//比較した二つのベクトルが平行な場合
if (N==Vector3.Zero) { MessageBox.Show("カメラの視線が平行です 正しい結果が得られません"); }
//NをノーマライズしたときはNanが入るのでこっちで
//if(float.IsNaN(N.X)||float.IsNaN(N.Y)||float.IsNaN(N.Z)){ MessageBox.Show("カメラの視線が平行です 正しい結果が得られません"); }
float d = Math.Abs(Vector3.Dot(N,b - a))/N.Length();
return d;
}

math.jpg
ベクトル2の平面として捕らえ、外積から法線Nを求める。
coθ=d/|V3|になることから。(ってd書き込むの忘れてた。汗。求めたい赤い線の長さがdっす)
内積の公式を意識して展開
d = cosθ|V3| = cosθ|V3||N|/|N| = |V3・N|/|N|

となる。

っと、ここまで来て距離じゃなくて点が欲しいことに気づく。しまった!
疲れてきたので残りはネットで検索~と楽をしようとしてもなかなか例がない・・・

どこかの教授のどこかの説明書きみたいのから持ってきた。

V1上の最近点を「P1にパラメータs*V1を加えたもの」だと考えるとパラメータは次のように表せる。

float s = ((V2.Length() * V2Len.Length() * Vector3.Dot(P2 - P1, V1))- (Vector3.Dot(V1, V2) * Vector3.Dot(P2 - P1, V2)))/ ((V1.Length() * V1.Length() * V2.Length() * V2.Length()) - (Vector3.Dot(V1, V2) * Vector3.Dot(V1, V2)));

わかりやすく一個の式にしたはずがすごいことになっております。
書き直すのめんどいのでこのままで。

無事求まったかどうかデバッグ画面
makeview.jpg

グラフをぐりぐり動かしてどの形でもそれっぽい線を引いていたので数値的な検証が皆無だけどOKとしよう。。(いいのだろうか)

明日注文した安物WEBカメラが届く。昨日トイザラスでアクションフィギュアをまるでクリスマスプレゼントを買うような顔をして購入してきた。ナイスだ!時期!

ねよっと。



プロフィール

あしゅ

Author:あしゅ
ぷぃぷぃ日常。
いつのまにか雑記ブログに。

カテゴリ
最新記事
検索フォーム
最新コメント
リンク
このブログをリンクに追加する
ブロとも申請フォーム

この人とブロともになる

カウンター
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。