2010年3月31日水曜日

途中経過

 そろそろどこかにまとめておかないと後から書くのが面倒になりそうな量になってきたので書いておきます。

0. 現状

 反射音の生成と顔向きの取得を作りました。

0.0 反射音の生成

 反射音の生成にはレイトレーシングを使いました。 
レイトレーシングの計算法にはラウンチング法とイメージング法の2種類があるみたいなので両方作ってみました。
(ラウンチング法とイメージング法がどのような計算法なのかは情報数理研究所このページが図があって分かりやすいと思います。)

 いずれの方法を使っても、レイを1本計算するのにかかる時間よりも 1本のレイからデータを立体音響化する時間の方がはるかに長いため、レイの本数が少ないイメージング法の方が結果も良好で計算時間も短いという結果になりました。

 また、計算結果を聞いた感想としては
  • 思っていたよりも存在感は出ない
  • 狭い空間だとエコーっぽさも無いため、徒労感が非常に強い
  • 前から聞こえてくる感じはやや強い気がしたが、十分に時間のたった今改めて聞きなおすとただの希望的観測だった気もする
という感じでした。

  反射音が無くても後方から聞こえてくる感じはできるのに、反射音を作っても前方はイマイチよく分からない位置から聞こえてきます。
仮に反射音に前方に音を持ってくる効果があったとして、『前方の音≒後ろっぽくないところから大きく聞こえて後ろからちょっとズレた音が小さく聞こえる』と『後方の音≒後ろから大きく聞こえて後ろっぽくないところからちょっとズレた音が小さく聞こえる』なのかな、とか思いますが、これはまだ試してません。

0.0.0 ラウンチング法

とりあえず3DCGとかで使われていていっぱいソースコードがあるラウンチング法から先に手をつけましたが、計算時間が長すぎてやってられませんでした。
(5度刻みで反射回数の上限が3回のレイを飛ばして4分25秒のデータをレンダリングした場合で19時間16分41秒)

0.0.0.0 ラウンチング法での計算の流れ
  • 観測点を中心にして放射状にレイを(たくさん)飛ばす
  • 音源の位置を中心にした球とレイの当たり判定を取る
  • 当たったレイの『飛ばした方向』『反射回数』『長さ』『当たった位置と音源の中心とのズレ』を覚えておく
  • 各種パラメータを設定する
    • 『飛ばした方向』を聞こえる方向にする
    • 『長さ』だけ空気減衰させる
    • 『長さ / 音速』だけ音を遅らせる
    • 『反射率の反射回数乗 × 当たった位置と音源の中心とのズレ × 正規分布の微小面積』を音量にする
  • データを立体音響化して足し合わせる
  • アニメーションが終わるまで繰り返す
0.0.0.0.0 当たった位置と音源の中心とのズレ×正規分布の微小面積の意味
     レイが当たらない場合は音が聞こえないので(当然ですが…)、レイとレイの隙間に音源が入ると本来聞こえるはずの位置でも音が途切れてしまいます。
     そこで、音源を点ではなく球にする事でこの問題を解消したのですが、その副作用で『当たったレイ』が沢山発生してしまいました。これ らの音量を100%にして単純に足し算してしまうと あっという間に音量が10,000%とかになって鼓膜が壊れそうなとんでもないことになってしまいます。
    そこで、『当たった位置と音源の中心とのズレ×正規分布の微小面積』とする事で何本レイが当たっても音量が爆発してしまわないように調整しています。
     正規分布の確率密度関数は(確率なので)面積が100%以上にならず、何本レイが当たったとしても爆音にはなりません。(分散の値は当たり判定に使う球の半径を、微小面積の幅はレイとレイの隙間の大きさを考えながら勘で決めました)

    7/22追
      防衛省のサイトにあった資料(PDF)によれば、これにはガウシアンビームモデルという名前があったみたいです。

    0.0.0.1 ラウンチング法の何が問題か

      音源と観測点を結ぶレイが一本に定まらないため、以下のような問題が出てきます。
    • 足し合わせても爆音にならないように音量を調整してあげないといけない
      • 調整に確率密度関数を使うと点の位置が分かっているのに『点がそこにある確率』で調整してるような感じで、何か無駄な計算してる気になってくる
    • 当たったレイの本数の回数だけ立体音響化の計算をするので、ものすごい時間がかかる
     なんとなく、ラウンチング法は点と点の経路を求める用途に向いていないのではないか、という気がしました。

    0.0.1 イメージング法

      ラウンチング法で計算時間が酷い事になったので、イメージング法も試しました。
      ラウンチング法と同様の計算をした場合でも21分しかかからず、18時間も早くなりました。
    これは、0.0で書いたように、レイを1本計算するのにかかる時間よりも1本のレイからデータを立体音響化する時間の方がはるかに長いのが原因です。
    (イメージング法は反射面が増えると計算量が一気に増えるらしいのですが、今回は部屋を単純な直方体にしているので問題になりませんでした)

    0.0.1.0 イメージング法の計算の流れ
    • 音源と観測点を結ぶ直線の『方向』『反射回数』『長さ』を覚えておく
    • ラウンチング法と同様にパラメータを設定する
    • データを立体音響化して足し合わせる
    • 壁に反射した音源の位置を計算して↑を計算する
    • 設定した反射回数の上限に至るまで↑を繰り返す
    • アニメーションが終わるまで繰り返す
    0.0.2 レイトレーシングまとめ
    • イメージング法の方が良い
      • 反射を計算する環境が複雑になるとラウンチング法が逆転する可能性がある
    • リアルタイムで計算するのは今のところ無理
      • 早かったイメージング法でも4分の計算に21分
      • 大体5倍の時間がかかっている
    0.1 顔方向の取得

      顔方向を取得し、Java3Dのカメラ方向を制御するプログラムを作りました。
      頭にWebカメラをつけてNyARToolkitを使う方法と画像認識を使う方法の2種類を試した結果、頭を自由に動かせる画像認識を使うことにしました。


    0.1.0 NyARToolkitを使って顔の向きを取得する

      手やヒモを使って頭にWebカメラを固定して、画面に表示したマーカーの姿勢をNyARToolkitを使って取得し、カメラの向きを設定します。
    ライブラリにJava3D版が付属していたので、プログラムを作るのは楽でした。

    0.1.0.0 NyARToolkitの問題点
    • 常にカメラがマーカーをとっている必要がある
      • 頭の動きが制限されてしまって意外と不便
        • 頭の周りを囲うように複数のマーカーを配置すれば良い?
    • 認識がちょっとシビア
      • カメラが静止していてもカタカタ揺れたりする
        • 設定(調整)の問題かもしれないが、よく分からない
    0.1.1 画像認識



      画像認識ではOpenCVなどのライブラリが使えるかと思っていたのですが、Javaから呼び出す良い方法が見つからなかったので、画像処理部分も手前味噌で作りました。
      JavaでWebカメラの画像を取得する方法についてはにゃんたこす!徒然草USBカメラで画像処理(Java)を参考にしました。
      MAlibアプリケーションのgnodempaで使われている肌色抽出部分を参考にして顔認識を作ってみましたが、背景の木製品を誤認識してしまってうまくいきませんでした。
    対策のため、黒いニット帽を用意して黒い部分(頭)を抽出するようにしています。(確認しやすいように表示では白黒反転しています)
    黒色フィルタを通して残った部分の中で面積が一番大きいものを頭ということにして、その中心を位置、中心と重心の差を向きにしています。

    0.1.1.0 画像認識の問題点
    • 静止していてもカタカタ揺れる
      • 現象的にはARToolkitと似たような症状
      • 蛍光灯のチカチカで閾値付近の色が安定してないのかも
      • この動画を見るとカルマンフィルタというのを使えればよさそうな気がする
        • カルマンフィルタの理屈が難しい
    0.1.2 顔方向の取得まとめ
    • ARToolkitはこの用途には向いてない気がした
      • 常にマーカーの方向を向いているような用途向き(当たり前か…)
    • Java3Dに対応させておいて良かった
      • NyARToolkitから画像認識に切り替えるのが楽だった
    • 重い
      • 『Java3D + 画像認識』、『Java3D + 立体音響化』は処理落ちせずに動くが、『Java3D + 画像認識 + 立体音響化』はカクカクでしんどい
      • サーバーとクライアントを作って複数PCで負荷を分散する?
      • GPGPUやってみる?
    •  画像認識の時間間隔を短くするとリアルに聞こえる
      • その分音がガクガクになってるので、ちゃんとした音だったらどうなのか気になる
      • レイテンシがすごく大事な気がする
      • 人にこの感覚を伝えるのは難しそう
    1. 今後の予定

      予定は未定

    1.0 GPGPUを使えないか

      玄人思考の安いグラボがCUDAに対応してたので、JavaからCUDAを呼び出すJCudaを試そうと思っています。
      FFTにはJTransformsを使っているのですが、ベンチマークを見る限りだとJCufftの方が速いっぽいですし、画像認識部分をどうにかCUDAで処理するとかすれば何とかなるかもしれないような気がしないでもない…

    X. その他

      前回のエントリを読んでいて、Java3DのConeSoundへの対応がほったらかしになってることに気がつきました。
    早めにやっとこう。