2017年10月30日月曜日

OpenCV で星座検出(3) - すごいぞ先行研究

引き続き星座検出のプログラムについて考察を重ねます。
前回最後に課題に挙げた
  • 安定化
の手法について書いていきます。


1. 問題点おさらい

安定化以前に、前回の記事時点でどのような問題点があったかお伝えしておきます。
  • 一つめの星発見時の拘束条件について
詳しくは(1)を読むとわかりやすいですが、写真がどのような画角で撮られているか特定できない以上、星座がどういうサイズで捉えられているかわかりません。前回時点では、画像から距離を測ってそれを上限として探索するというインチキをしていました。実行時間短縮のためにどのように制限をかければいいか非常に悩ましいところです。
  • 「本物」の特定について
上の拘束条件の厳しさにもよりますが、総当たりで探索する限り、「それっぽい偽物」が多数見つかります。前回時点では、拘束条件を既知の情報で厳しくして、本物を見つけ次第終了するというインチキをしていました。拘束条件と密接にかかわるため、同時に考えたいところです。
  •  実行時間の長さについて
 デバッグのめんどくささからも深刻な問題です。上記二つのインチキが通用する画像では、10秒~30秒程度(それでも長い)で完了していましたが、通用しない画像では200秒、300秒を越えるものもありました。あんまり実用的と言えないので、ぜひ直したいです。

以上のインチキをしないで済むプログラムを目指します。

2. 拘束条件を厳しくしてみる

この時点での拘束条件は、今の星から次の星を探索する際、「角度±3度の誤差までは許容」「距離2割の誤差までは許容」というわりと緩いものでした。この条件でインチキをやめて調べてみると……
うわっ
うわうわ
ご覧の有様です。
なのでとりあえず拘束条件を厳しくしてみる……ということをしてみたのですが、そもそも発見できなくなったりして元も子もありません。レンズや撮り方による違いを容認するためにも、拘束条件はある程度ゆるくあるべきなのではないか?とも思います。「距離の誤差を1割まで許容」「最も近い5つの星まで探索」という条件を付け、あとは適宜変えたり変えなかったりしながら別の案を考えることにしました。

3. 尤度を計算してみる

尤度(ゆうど:likelihood)は、統計学などで用いられる「もっともらしさ」を表す値のことをいいます。よく意味はわかっていませんが、なんかかっこいいし簡潔にすむのでこの単語を使います。この記事においては「どれくらい目的の星座に近いものが見つかったか」を示す値だと思ってください。
見つかったいて座(仮)それぞれについて、適切な尤度を設定することができれば、プログラムも人間のように「多分これだろ」ができるようになりますね。
ある星から次の星を見つける時の判断材料は3つ、前の星との距離$d$、前のベクトルとの角度$\theta$、基準点との距離$d_s$でした。とりあえずは単純にこれらの誤差($\epsilon$とする)を足し合わせれば、どれくらい違うかが数として表れそうです。値が低ければ本物というわけです。 \begin{equation} L=\sum_{i=0}^{N}(\epsilon_{di}+\epsilon_{\theta i}+\epsilon_{d_{s}i}) \end{equation} ところがこの式では、「偽物」のほうが値が低く出たりして困りました。それもそのはずで、これだとたくさん星を発見したのに積み重なった分が多く出てしまいます。
たくさん星を見つけてくれたらボーナスを与えられるような関数にしたいですね。現在の仕様だと、その星座だと断定するための「検出部」(いて座の場合南斗六星にあたる部分)があるため、ここの一つ目と二つ目の星を除いた星の数を$n$としましょう(一つ目と二つ目を除くのは検出用関数始動のタイミングの都合)。 \begin{equation} L=\frac{\sum_{i=0}^{N}(\epsilon_{di}+\epsilon_{\theta i}+\epsilon_{d_{s}i})}{n} \end{equation} これでいけるだろうと思いましたが、まだ値がうまいこと出ません。さらに考えてみると、検出のうえで最初の方に誤差が大きいと、連鎖的にどんどん違う星を辿ってしまい非常にまずいです。なので、最初の方に誤差が出ると値が大きく出るように調節します。具体的には、現在辿っている星が何個目なのか($n_{i}$)を用いて以下のような式とします。 \begin{equation} L=\sum_{i=0}^{N}\left\{\frac{(\epsilon_{di}+\epsilon_{\theta i}+\epsilon_{d_{s}i})}{n_{i}/n}\right\} \end{equation} これだと、検出部においては分母が1より小さくなるため、{}内は大きな値になります。検出部を抜けると分母は1より大きくなるため、{}内は小さいです。たくさん星を見つけているものがかなり有利になりました。
(3)式まで来ると、はっきり検出できているものでは1より小さい値が出て、そうでないものは1より大きい値が出るため、非常にわかりやすくなりました。あとは、1より小さいものが見つかればその時点で出力し終了、見つからなければそれっぽいもの(2より小さいとか)を保存しておいて尤度が最小のものを出力すればいいというわけです。この関数のおかげで、拘束条件を緩めても一番それらしいものを持ってきてくれるので、ずいぶん安定して出力が得られるようになりました。ほんとは今の時代、こういう線引きを考えるのこそプログラムがやってくれそうなもんですが、知識がないので自分で考えてます。
知人が違うレンズで撮ったものも検出!

4. 先行研究を漁る

尤度を考えることで一つ目と二つ目の問題は解決したみたいなので、あとは実行時間を短くするのみです。原因はだいたいわかっていて、総当たりする星の数が多すぎることです。画像のレベルを閾値としている時点で、切り出せる星の数には限界があり、いくら暗い星といえども観測地が暗ければ読み取れてしまいます。レベル補正以外の手法で暗い星を排除する必要があります。

4-1. ガンマ補正をかける

最初に自分で思いついたのが、「明るすぎてたくさん星を検出しちゃうなら、画像自体を暗くすればいい」ということでした。画像編集においてガンマ補正というのが有名で、openCV でも関数が用意されているので簡単にできます。
これをレベルの閾値を上げていくループに組み込んでみましたが、暗くなりすぎることもありあまり安定しませんでした。

これ以上どんなに考えても思いつかなかったので類似のプログラムがインターネットに転がっていないか探しました。事前にやれという話だ。

4-2. メディアンフィルタをかける

小さい星を削除するために、f(i, j)に大きさ5×5のメディアンフィルタをかける。 - 2次元パターン平面を利用した点パターンのマッチング法とその星座検出への適用について On the Method of Point Pattern Matching Using2-D Pattern Space and Its Application to Detection of a Constellation http://jairo.nii.ac.jp/0090/00002347

この文献自体はプログラムを作り始める前から知っていましたが、内容が難しくてわからない(情けない)のであまり読んでいませんでした。ここに来て頼ってみると、小さい星を削除するプロセスがあったので真似してみようと思いました。メディアンフィルタは、注目画素の周り数マスを参照して中央値で塗るやつで、ノイズ除去などに使われるフィルタです。
結論から言うと、入力される画像のサイズや、画像に対する星の大きさが一定でないため、フィルタのサイズが定まらずに明るい星までつぶしてしまうことが多く使えませんでした。

4-3. 輪郭の大きさから明るさを判断する

Thus, we used local thresholding algorithm to detect all the blobs in the images. Next, we sorted the detected blobs by size and selected the 100 brightest stars as input to the algorithm. - Detecting constellations in night sky images Project report Advanced Algorithmics (MTAT.03.238) http://lepo.it.da.ut.ee/~timo_p/constellations/petmanson-krips-algo-project.pdf
そもそも英語で文献を検索しようという発想がなかった(情けない)のでここにきて初めて読んだ文献です。2011年のものですがほぼ似たようなことをしているようですね(悔しい)。上記引用部がポイントで、輪郭として星を検出したのち、輪郭の大きさ=明るさとして、輪郭が大きいもののうち上から100個をとりだして探索対象としています。これは盲点でしかも納得できるやりかただったので舌を巻きました。
実際の実装としては、これまでのレベル閾値+ガンマ補正で光害を対象から削除し(上記文献は星景写真を対象としていない?のかこのプロセスはない)、 そのうえで輪郭の大きさ(ここでは面積)を見て上から100個取り出すようにしました。するとなんということでしょう。今まで星が多くて検出が厳しかった画像からもいとも容易く検出できるようになったのです。しかもかかっても30秒。
星がたくさんあってもこのとおり
明るさベスト100を取り出すことにより無駄な探索を控え、精度でも時間でも元の手法に勝る……さすが文献としてネットに上がってるだけのことはありますね。すごいぞ先行研究。

5. できぐあい

今回の変更で自信をもって「星座を検出してるんだぞ」といえるプログラムになったと思います。あとは他のいて座写真でも問題なく動くかどうかの確認と、検出できる星座を増やすことです。どちらにせよ、インプットの画像が増えないとどうしようもないため、微調整を除いてほとんどやることはないかもしれません。星座が増えてきたら複数の星座を一気に検出できるようにしたりもしたいですね。
また今回、友人から写真を提供してもらってペルセウス座のデータを入れることができました。

きれいですね~
3の画像も含め、論文だったら謝辞に名前が載っているところですが、 いちブログですみません。引き続き星座の写った写真を提供してもらえると嬉しいです!

0 件のコメント:

コメントを投稿