2012年4月7日土曜日

Macでのlibjpegとかlibpngとか

凄まじくハマったので、忘れないように(AND 追加の問題発生時の対応用に)メモ。
試行錯誤しながらやったので、細かい部分は曖昧です。

(2012/07/25追記)
下記の内容をまとめました→(続・Macでのlibjpegとかlibpngとか
お急ぎの方ははまとめをご覧下さい。


何をしようとしたか

そもそもの発端は、前回までの続きとしてミクさんをプログラム的に呼び出す方法を調べていたことでした。
んで、工学ナビさんが急募してたのでMacでGLMetaseqを使ってみたとか、「ARToolKitで初音ミク」をやってみようといったエントリを参考に、環境構築をしていたわけです。
最初は順調だったんですが、工学ナビさんが急募してたのでMacでGLMetaseqを使ってみた(続き)に書かれているpng対応などを追加したあたりから雲行きが怪しく…。

最初作っていた環境はMacports経由で色々インストールしていたので、状況は全く同じはずだったんですが、なぜか実行時に"symbol not found"などと怒られ、一切実行できなくなってしまいました。

libjpeg.dylibとかlibpng.dylibのあるディレクトリにはパス通してました*1し、原因が一切思いつかないので、とりあえず最近流行りらしいし、パッケージ管理をMacportsからhomebrewに変えようと思い、一式アンインストールして入れなおすという暴挙に。

続く迷走

暴挙に出た結果としては、そもそもコンパイルすら通らなくなる有様*2…。
この辺からよく分からなくなって、brewがインストールするときに張るシンボリックリンクをImageIO.frameworkの下にいるlibjpeg.dylibとかに張り替えるとか、挙句OSのクリーンインストールまでやる始末。
また、OpenCVの中でlibjpegとか呼んでるのが悪いんじゃないかと考えて、ImageIO.frameworkの配下にいるライブラリを叩くように修正しようかと、訳のわからないことをやろうとしたりで、大迷走。

ライブラリの読み込み順

たださすがにOpenCV自体をいじるのはコスト的に美味しくないので、どうにかならないかと調べていたら、以下のエントリがまさに答えを示していました。

MacPorts のために DYLD_LIBRARY_PATH を使ってはいけない理由

曰く、
Macではランタイムライブラリの検索順序について、「man 1 dyld」 で確認することができます。
曰く、以下の順序で検索されます。
1. DYLD_LIBRARY_PATH
If it still can’t find the library, it then searches DYLD_FALLBACK_FRAMEWORK_PATH and DYLD_FALLBACK_LIBRARY_PATH in turn.
2. DYLD_FALLBACK_FRAMEWORK_PATH
By default, it is set to /Library/Frameworks:/Network/Library/Frameworks:/System/Library/Frameworks
3. DYLD_FALLBACK_LIBRARY_PATH
By default, it is set to $(HOME)/lib:/usr/local/lib:/lib:/usr/lib.
とのこと。

デフォルト状態ではDYLD_LIBRARY_PATHは設定されていないので、IDEなどで明示的に設定しない場合のライブラリ検索順序は、

  1. /Library/Frameworks:/Network/Library/Frameworks:/System/Library/Frameworks
  2. $(HOME)/lib:/usr/local/lib:/lib:/usr/lib

となるはず。
Macportsを使っていたときは、/opt/local/libの下にライブラリ一式がいたので意味がなかったのですけど、homebrewは/usr/local/libにライブラリ一式を追加してくれてます。

…ということで、これまで自力でライブラリパスを通していたのが実は裏目に出ていたようです。
これで、libjpegまわりのエラーはなくなりました。

で、libpngについてですが、homebrewの管理から外れているということは承知の上で、brew search libpngを実行したところ、こんなメッセージが。
Apple distributes libpng with OS X, you can find it in /usr/X11/lib. However not all build scripts look here, so you may need to call ENV.x11 in your formula's install function.
…おや?
そう、なにもサードパーティのlibpngを入れるまでもなく、最初から入ってたんですね。
もちろんヘッダファイルも/usr/X11/includeにしっかりいました。

で、/usr/X11/libはどこにもパス指定されていないので、コンパイル時には指定しないとダメなはずですが、今回はNetBeansがよしなに計らってくれているようで*3、うまいことできてました。
自分のために、ライブラリパスを明示的に設定しない場合と設定した場合に実行されたコマンドを貼っておきます。


  • 設定しない場合(うまく実行できる)
gcc     -framework GLUT -framework OpenGL -L/usr/X11/lib -o dist/Debug/GNU-MacOSX/glmetaseq_test build/Debug/GNU-MacOSX/_ext/2091976640/GLMetaseq.o build/Debug/GNU-MacOSX/_ext/2091976640/main.o -ljpeg -lpng
  • 設定した場合(実行できない)
gcc     -framework GLUT -framework OpenGL -L/usr/X11/lib -o dist/Debug/GNU-MacOSX/glmetaseq_test build/Debug/GNU-MacOSX/_ext/2091976640/GLMetaseq.o build/Debug/GNU-MacOSX/_ext/2091976640/main.o -L/usr/X11/lib -ljpeg -lpng
オブジェクトファイルの後ろで-Lオプションつけると何が変わるんでしたっけ…?


(2012/4/8追記)
上記/usr/X11/libへのパスですが、自分で強引に設定していたのを忘れていました…。
プロジェクトのプロパティー→構築→リンカーとたどって、普通は「追加のライブラリディレクトリ」としてパスを設定すると思いますが、今回は「追加のオプション」に -L/usr/X11/lib を追加することでうまくいっていました。(=明示的に設定している)
ただ、やっぱりオブジェクトファイルのあとで-Lオプションつけるのと何が違うのか、さっぱりです。

…動いたからよしとしましょう。

結論

今回の結論としては、

  1. デフォルトで検索されるライブラリパスはいじらないほうが吉
  2. いじる必要がある場合、他のものに出る影響を最小化するために、DYLD_FALLBACK_LIBRARY_PATHへ追加する

といったところでしょうか。
本来ならもっとスマートに解決できそうに思いますが、どうもこのへんが限界です

先のエントリの結論にも書かれてますが、こういうのは「既存ソフトウェアに影響があるので、新規ライブラリ (ports で入れたライブラリ等) はデフォルトライブラリの後で読み込むようにしましょう。」この一言につきますね。



…しかしOpenCVと組み合わせたらまた問題が出そうな気がしますね、コレ。

  • *1:これがそもそもの間違い
  • *2:こっちもシンボル見つからない系のエラーを吐いてた
  • *3:こういう姿勢がもう色々とダメ

2012年4月6日金曜日

MacBookAir(10.7 Lion)でHandyAR その3

今のところLionだからという感じの問題に遭遇していないので、タイトルが釣りっぽいなーと思ったりしながらの3日目です。

ということで今日は前回の続きということで、固まっている原因はどこか、ソースコードを確認していきたいと思います。


先に結論

とりあえず先に結論だけ書いてしまうと、固まっているように見えたのは、前処理にものすごい時間がかかってただけでしたw
ということで、無事動くまでの経緯を…。

困ったときのprintfデバッグ

まずはどこまで無事に動作するかを確認するため、ひたすらprintf()デバッグを敢行しました。
なぜかNetBeansのデバッガが使えなかったので

すると、HandRegion.cppのloadSkinColorProbTable()ってところがどうも怪しいのでよーく確認すると、

51行目〜
        printf("making a lookup table for skin color distribution ");
        _SkinColor.MakeLookUpTable();
        printf("done\n");

この部分で止まっているようです。
ということでMakeLookUpTable()を探してみると、MixGaussian.cppにあったので、中を覗くと…、

118行目〜
void MixGaussian::MakeLookUpTable()
{
    CvMat * SampleMat = cvCreateMat( 3, 1, CV_64FC1 );

    for ( int R = 0 ; R < 256 ; R ++ )
    {
        printf(".");
        for ( int G = 0 ; G < 256 ; G ++ )
        {
            for ( int B = 0 ; B < 256 ; B ++ )
            {
                cvSetReal1D( SampleMat, 0, (double)R );
                cvSetReal1D( SampleMat, 1, (double)G );
                cvSetReal1D( SampleMat, 2, (double)B );

                _Probability[R][G][B] = GetProbability( SampleMat );
            }
        }
    }
    
    cvReleaseMat( &SampleMat );
}

3重ループかー…
どうもここの計算に時間を取られていたようです。
しかもprintf(".")って、改行コード出してないからインジケータとしての意味が無いですし…。

そんな訳で、インジケータをわかりやすく(R=20ごとに改行)変更したところ、無事に動いていることが確認でき…、


てない…orz
コードを確認すると、上の処理の直後にnonskin.mgmというファイルを読んでいるのですが、追加されてませんでした。
なので、nonskin.mgmファイルを追加した後、改めて実行。

そして待つことしばし…



動いた!!!(・∀・)
でも上下反転してますね…(;´∀`)

付属のドキュメントを見ると、flipオプションというのがあり、
-  [-filp]: flip video images vertically. (Default=don’t flip)    
とのこと。

なので、実行時に -flip を渡して再度実行…、


ktkr!!!


ということで、ひとまずMacでも動くことが確認できたので、次回はミクさんに登場して頂けるように頑張りましょうw

2012年4月4日水曜日

MacBookAir(10.7 Lion)でHandyAR その2

ということで2日目です。
昨日はコンパイル通ったところまで確認できたので、今日は実際に動かしてみましょう!

実行ファイル作成

とりあえずmain書かないとお話にならないので、まずはそこからいきます。
とはいえ何も参考にしないで書ける訳がないので、デモに入っているmain.cppを覗いてみます。

…なんかそのまま使えそうなので、使ってしまいましょう。
てい。



案の定、エラー出ませんでした。
引き続き、インクルードしているヘッダ(main.h、bunny/inc/bunny.h)をすべて追加すれば準備が完了です。
実行ファイルを作りましょう。
といってもNetBeansにお任せなので、必要なパスを通して依存関係書いておけばあとは勝手に…


はいエラーw
昨日の段階でコンパイルできてたってのは幻想だったんですねーwww

…やっぱりソースの修正は必要なようです。

エラー吐いたファイルはFingertipTracker.cppだそうなので、中を見てみましょう。
エラーの箇所はこんな感じでした。

684行目〜
    if ( nPoints < NUM_FINGERTIP )
    {
        _fDetected = false;
        _nFingertips = 0;
        _nPoints = 0;
        goto Finished;
    }

772行目〜
Finished:

    //
    // Update Mode
    //
    if ( _fDetected )
    {
        _nMode = MODE_FINGERTIP_TRACKING;
    }
    else
    {
        _nMode = MODE_FINGERTIP_LOST_TRACKING;
    }

    return _fDetected;

goto文って使ってる人いたんだ、とか思いつつ、文法的には合ってるっぽいので何故にエラーが出たのかもよくわからず。

軽くぐぐってみると、どうもMac特有のエラーらしいので、goto文を使わないように修正します。
幸い、単純にエラー時の終了処理のためだけに使われているようなので、772行目以降のif文をメソッドに閉じ込めて、684行目のif文に入ったらそのメソッドを呼び出して即returnするようにしましょう。

ということで、修正後のコードは以下になります。
684行目〜
    if ( nPoints < NUM_FINGERTIP )
    {
        _fDetected = false;
        _nFingertips = 0;
        _nPoints = 0;
        updateMode();

        return _fDetected;
    }
772行目〜
    //
    // Update Mode
    //
    updateMode();

    return _fDetected;
どちらもupdateMode()というメソッドがいきなり出てきてますが、これは以下のように定義しています。

void FingertipTracker::updateMode() {
    if ( _fDetected )
    {
        _nMode = MODE_FINGERTIP_TRACKING;
    }
    else
    {
        _nMode = MODE_FINGERTIP_LOST_TRACKING;
    }
}
単にif文をメソッド化しただけ。

ということで再度ビルドしてみますが、今度はFingertipPoseEstimation.cppからエラー…
ただしこちらもgoto文関係のエラーなので、まずは関連しているラベルを確認。

209行目〜
Finished:

    return fResult;

…goto文をすべて消して、直後に全く同じreturn文を書くことで対処しました。

改めてビルドすると今度こそ…
成功!
無駄に長かったですねー…

実行してみる

はやる心を抑えつつ、早速実行してみます。


あ。


(ノ∀`)アチャー

ダメでした。
しかしエラーログ見る限り、必要なファイルが無いだけのようなので、これをプロジェクトルートに追加します。
一応、念のために必要そうなファイルはすべて追加しておきましょう。


気を取り直して再度実行します。


とりあえずエラーは出てませんし、カメラにもアクセスできている風なんですが、全然ウィンドウが表示される気配が無いです…


ということで、困ったときのアクティビティモニタ。


応答なしwww
こらダメだwwwww

中身に踏み込…もうとする

このままではどうしようもないので、頑張ってソースを読むことにします。



…が、今日は疲れたので明日の自分に期待!!

2012年4月3日火曜日

MacBookAir(10.7 Lion)でHandyAR その1

色々やろうやろうと思いつつ、気づけば数ヶ月…。
最近になってようやく仕事が一段落してきたので、いざ!ということで、やりたかったことをガシガシ進めていきます。

そんなわけでしばらくの間、タイトルの通り、LionでHandyARを動かしていきます。
当面の目標としては、このへん↓
HandyARで初音ミクが手のひらの上でウッーウッーウマウマ(゚∀゚)
を目指しておきます。
今更感が漂いすぎててアレなところはスルーで
イケそうならiPhoneでも動かしてみようと思います。

ホントはPTAMあたりを使おうかなと思ってたんですが、64bit環境だとインストールが非常に手間みたいなので、今回はHandyARを採用する方向でいきます。

HandyARの入手

HandyAR自体はいろんな方が解説されてますので、詳細は割愛しますが、カリフォルニア大のTaehee Leeという方たちが作った手法だそうです。

ソースコードの入手自体は、彼らのページから可能です。
ページ下部の"Source Code"に"HandyAR Verx.x"というリンクがあるので、そこからzipを落とすだけでOK。
おそらく"HandyAR-verx.x.zip"みたいなファイルがDLできるはずです。

とりあえず解凍してみると、なんかいろいろ入ってました。

入ってますが、今回用があるのは DemoHandyAR/DemoHandyAR/HandyAR/ 以下のファイル群になると思いますので、ここのファイルをごちゃごちゃいじっていきます。

とりあえずコンパイルしてみる

何もせずに動くならそれに越したことはないので、とりあえずコンパイルできる状態へ持っていきます。
なお、今回は個人的な趣味によりNetBeansで色々いじっていきます。

まずは HandyAR/ 以下のファイルを、ディレクトリごとプロジェクト直下にコピーしてきます。
また、上では HandyAR/ 以下だけで大丈夫そうと書きましたが、一応 ARTag/ と Capture/ もコピーしておきます。

コピーが済んだので、インクルードパスを通して、適当なファイルを開いてみると…


おや?

エラーがたくさんある上にインクルード文からも警告が…

インクルードファイル開くと…

 global.hも必要ですか…orz
ということでこれもコピー。
そして再度確認!


今度はglobal.h内部がおかしいようです。
global.hの中を覗くと…


OpenCVとglut関係でエラー吐いてます。
ただ、現時点で自分の環境にインストール済みのOpenCVはバージョン2.3なのですが、このヘッダ指定はどう見ても1.xのそれなので、激しく中身を書き換えたい衝動に…
ついでにデモ用に書かれたであろうヘッダ(cvFindExtrinsicCameraParams3.h)まであるので、うーんって感じです。

なんですが、今日はコンパイルさえ通ればよいので、バージョン古くても問題なし。
早速インクルードパスをOpenCVとglutに通し、cvFindExtrinsicCameraParams3.cpp/.hをプロジェクト直下に追加。
さらに再度確認。



ということで、無事通りました。
残りのファイルもエラー出てないな…



…と思ってたら、


エラーだらけ…orz
Capture/ 以下のFlyCap.cpp/.hがどうも他のライブラリに依存しているような感じですね。

ここで、同梱されていたドキュメント(DemoHandyAR Documentation.pdf)を読んでみると、

 Capture/
- In this directory, Capture.h and cpp files are used to capture video from a camera using OpenCV functions (and PointGrey’s SDK). In order to use PointGrey’s SDK, you need to include FlyCap.h and cpp files in the same directory into your VC++ project.
Capture/
- このディレクトリには、OpenCV(とPointGrey's SDK)を利用し、カメラからビデオキャプチャを行うために必要なCapture.h と cppファイルがあります。PointGrey's SDKを使うには、同じディレクトリのFlyCap.h と cppファイルをVC++プロジェクトに追加する必要があります。

とのこと。

ひとまずキャプチャはOpenCVだけで十分だと思いますので、上で出たエラーは無視できるようですね。
というか、調べてみるとPointGrey's SDKと言っているのは、FlyCapture SDKというものらしいんですが、どうもMacOSには対応してないみたいです。
なのでいずれにせよ上記のエラーは無視するしかないようです。南無。



なにはともあれ、これでようやく全体のコンパイルが終了(2012/4/4追記:全然コンパイルできてませんでしたw 詳細はその2へ)ということで、次回は実際に何か動かすところまで持っていこうとおもいます。
そういえばHandyARのライセンス関係ってどうなってるんだろう