2011-10-04
■[programming][fortran] コードの見直し(リファクタリング?)
機能追加に伴ってコードがゴチャゴチャしてきた。見づらいだけならいいが、さらなる機能追加が容易でなくなってきて、やばい。機能追加が容易なように改良する必要がある。
「気象庁コーディングルール(ルビ:バイブル)」を再度見ている。
変数参照型モジュール内の共有変数の値の設定・変更は、同じモジュール内の サブルーチンでしか行ってはいけない。外部からは値を参照するのみにする。
http://www.mri-jma.go.jp/Project/mrinpd/coderule.html
「外部とのやりとりはなし。外部に許可するのは参照 (read only) のみ」。しかし read only てのはグローバル変数のプロパティとしてはないので(引数なら intent(in) があるが)、「運用でなんとかする」つまり「たくさんあるグローバル変数を、ひとつも、誰も、外部で変えないことを祈る」ってことだよね。うーむ、危ないような。
と思ったけどよく考えたら自分も似たようなことしてたわ。ただ、自分の場合は下のような「パッケージ型モジュール」の共有変数をモジュール外で read only 許可している。なのでコードが見事にスパゲティ…。
パッケージ型モジュール内の共有変数や局所サブルーチンにはprivate 属性を つけ、モジュール内でのみ使用し外部から直接使用しないようにする。外部との やりとりは、初期化サブルーチンと実行サブルーチンだけを通じて行う。
http://www.mri-jma.go.jp/Project/mrinpd/coderule.html
初期化サブルーチンで共有変数の初期値の設定を行う。
実行サブルーチンはパッケージ型モジュール内に1個もしくは複数個あり、パ ッケージ型モジュールのメインの部分である。引数を通じて外部からデータを入 力し、計算を行い、引数を通じて外部に必要なデータを出力する。
「外部とのやりとりは引数でだけ」。
つまり気象庁コーディングルールでは、「別の module の共有変数を呼び出して値を変える」ことは認めていない。別の module の共有変数の値を変えるには、「その module 内の subroutine を call する」しかない(function は基本使わないので置いとく)。
「パッケージ型は完全にカプセル化」し、グローバル変数の参照は変数参照型モジュールからのみ。変数参照型モジュールって何よ、というと、
変数参照型モジュールの共有変数は、プログラム実行時に最初に一度だけ値を 設定し、それ以後変更しないのが理想である。値の変更がある変数については、 変数参照型モジュールではなく、引数でサブルーチンに渡す
http://www.mri-jma.go.jp/Project/mrinpd/coderule.html
ってことは…必然的に引数多めになるよねこれ、やっぱり。それしかないのかなぁ。それか、「パッケージを大きくする」かなぁ。
牛島本で言うところの「グローバル変数モジュール」の最大の問題はやはり「いつどこでだれがその変数を変えてるかわからない」ということだと思う。それを排除する意味で「変数参照型モジュール」は賢いけれど、モジュール外で値を変えられない…
というか、「値を変えるような subroutine 群は一つのモジュールに突っ込め」ってことなのかなぁ。まぁそうすりゃ楽だよなぁ。パッケージ型モジュールのスコープをそうやって決めてしまう(いまより大きめになる)のが一番だろうか。でも「巨大なパッケージ内の共有変数」って結局どこで変更されたかわかりづらいよね…意味無いじゃん。
なんで引数が嫌かって言うと、理由はいくつかあるけれど、最大のものは、これも牛島本にあったことなんだけれど、「いくつも subroutine の階層をくだっていかないといけない場合に、途中の階層では使わない変数も全て引数としてバケツリレーしなきゃいけない」ってこと。同じくらい嫌なのが、引数で配列を渡す場合、allocatable だとかなり気を使うってこと。
あとは形状引継ぎ配列は使わないので、全部の subroutine に配列の上下限をキッチリ書いているわけだけれど、一箇所でも上限か下限を変えたら全部書きなおさなきゃいけないってのも地味にメンドイ。まぁ一括置換しろって話だけどそれってなんか…。include使うのも微妙だし。
いろいろ考えたけど、モジュールをいくつかマージしてやや大きめなパッケージ型モジュールを作ることにした。グローバル変数モジュールの採用はまだ保留中。そもそもF77であったcommon文を一度グローバル変数モジュールにしたけれど、変数の値の変更タイミングがわかりづらすぎて使用を止めた経緯があるので。
追記:セーブとロードの subroutine のみ特別扱いとして、パッケージ型モジュールからの use を許すかどうか迷ってる。こうすると、無駄な途中の引数バケツリレーをかなり省略できる。ただ、このためには private を public にしなきゃいけないのが微妙…。
2011-10-01
■[programming][fortran] restart方法の見直し
@yuuriiさんにだいぶ前に教わった方法に変更中。すなわち、計算は必ず load で始めて、計算開始か再開かの違いは load する file が「初期条件data」か「途中のdata」かの違いのみとする、ということ。
擬似コード的に書くと、
if new game 変数をごちゃごちゃ計算する 初期条件dataをsave endif load(初期条件data または途中data を読み込む) do main loop メインの計算 途中dataをsave enddo
という感じ。
save dataのファイルサイズ膨大になるかと思ってたけどそんなに変わらなかった。考えたらすごく昔に読んだ河村先生の本とかのコードもこんな感じだったかもしれない。
あ、こうなってくると、初期条件の計算部分は独立なプログラムにしてもいいのか。まぁ独立モジュールくらいにしとくか。
2011-08-22
■[research] 作業メモ:Phantom -> DippMotionで昆虫の模様から位置情報取得
Phantom でのキャプチャ設定
画像処理
下の方にある Image processing ボタンから。gamma が最初1.41になっていることに注意。Set default を押すと 1.0 になる。フィルタは、元画像をそのまま見るなら Sharpness を、輪郭を抽出するなら Edge Hipass 3x3 か Edge Laplacian 3x3 がよさそう(いま見てる動画だと、どっちでも同じに見える)。
保存
右のほうの Convert から。必要な範囲よりやや長めの範囲を切り出す。avi にする場合はフォーマット選択ダイアログが出てくるので適当に選ぶ。ビットマップで保存する場合は、連番ファイルにするために、ファイル名に +3 を付ける。たとえば N1_cam1_+3.bmp という名前にすると、 N1_cam1_001.bmp, N1_cam1_002.bmp, ... という連番ファイルが生成される。
DippMotionPRO での作業メモ
フォルダ関係
いろんなファイルを保存したり読み込むダイアログで、デフォルトが
- 前回の作業フォルダ
- C:\Program Files\Ditect\DippMotionProKP
- C:\Documents and Settings\ユーザ名\Application Data\Ditect\DippMotionProKP
のどれか(あるいはその下のフォルダ)なっていることがある。このせいでリンク切れになって「なんか動画動かないなぁ?」とかよくなる。というかこのへんいじったらさっき画像が全部消滅したよ・・・
そこで、DippMotion を起動したらまずメニューの「オプション > 一時ファイル格納場所指定」から、自分の目の届くところにしておく。
レンズキャリブレーションについて
全部の撮影で同じ組み合わせのはずなので、一度作ったファイル(カメラに対応した3つの .dxc ファイル)を使い回せばいい。具体的な方法は、Dippを開いた初期画面の右にあるアウトラインペインで、それぞれのカメラを展開してから、「キャリブレーションデータ」を右クリックして「ロード」。
3Dキャリブレーションについて
キャリブーレションパラメタの計算を行ったら、条件が同じ複数回の撮影に使いまわすために、保存しておく。これはカメラごとなので注意。計算方法について、説明書には漸近法の方が良さそうに書いてあるが、少なくともこれまでに行ったケースでは線形法の方が明らかに良かった。計算は線形法が圧倒的に速い。
2Dデータ入力について
カメラを開いてからまずやること:
- 画面下の「画像フィット」チェックボックスを切り、メニューの「表示 > 拡大」から 4x4 あたりを選択
- メニューの「属性 > 座標入力方向の指定」で、デフォルトが「カラム単位」(縦に進む)になっているのを「部位単位」(横に進む)に変更
残念ながら、こういうインタフェース部分の設定は保存されないので、カメラを開くたびに毎回やる必要がある。なぜかカーソルの設定だけは保存されるようだが…。
重要:保存について。2Dデータ入力画面から保存する場合、デフォルトのフォルダは「一時ファイル格納場所指定」の下のフォルダになっている(名前が同じなので騙される)。自分のプロジェクト下のフォルダに保存されるのは、一度2Dデータ入力画面を閉じてホーム画面に戻り、プロジェクトの保存を行った瞬間。もしプロジェクトの保存を行わずにDippを終了すると一時ファイルの方にしか保存されていない状態になる。このままもし他のプロジェクトを開くと、データは全て吹っ飛ぶ。「ホーム画面に戻ってプロジェクトを保存」は癖にしたほうがよさそう(というか、一時ファイルでなく直接保存すりゃいいのか?)。
重要:部位の命名について。メニューの「属性 > 最大表示セル数」から、「部位方向(ライン)」の数を必ず現在の部位数よりも多くしてから、左下の部位名の欄をダブルクリックして命名する。もし、「部位方向(ライン)」の数が現在の部位数よりも少ない場合は、正しく命名できないだけでなく、5つ上の部位名が上書きされてひどいことになる。謎仕様というかどうみてもバグ。
重要:左下の「追尾」チェックボックスについて。修正追尾での挙動はまだハッキリわからないが、ここにチェックが付いているものを優先的に(?)追尾していくようで、間違うと非常に危険。これから打っていく点のみにチェックが付いていることを常に確認すること。ミスるとセーブした時点からやりおなしになる(ので、少なくとも部位一つ終わるごとにセーブ必須)。
チョウの翅の模様追尾について。全部手動でやると、却ってガタガタになると思われる。面倒なようでも自動追尾を併用したセミオート的なのが結局はよさそう。
とりあえず相関追尾を使ってみている。実際には常時修正追尾で行う。なので右手はマウス、左手は矢印の左右キー。相関の閾値は0.9程度で初めて、進まなくなったら0.8程度まで下げる感じ。探索範囲については、XY方向へのベクトル的なものを指定する「指定方向」を推奨。ただし、追尾点の移動方向に合わせてときどき微調整が必要。追尾範囲(赤の四角)は小さめにしたほうが高い相関値でもうまく追尾できることが多いようだ。
拡大表示で打ちたいところに打つためには、カーソルの中心を打ちたいピクセルに完全に重ねるのではなく、0.5ピクセル程度(?)左上にずらすように打つ必要がある、ようだ。
ポイント表示モードについて。最初は「全ポイント表示」で打っていく。混んできて見づらくなったら「ポイント表示」かも併用。「軌跡表示」は異常なガタつきを見つけるのにはいいかも。
3D再構築したとき
「すべて」で表示したときに、なぜか2Dとの同期再生ができなかった。2D解析結果を一つ(カメラ1でよさそう)開いて、めんどうだけどメニューから画像と2D座標ファイルを読み込む。それから閉じてまた「すべて」を開くと、なぜか他のカメラも含めてうまく同期再生できてた。謎だ・・・。
カメラ2台以上で追えていない(入力していない)時刻があったら、その時刻は点が消える。変なところに飛んでいたら、キャリブレーションがおかしいかも・・・
