Deep Sky Memories

横浜の空で撮影した星たちの思い出

動画からの流星検出 - 2021年ふたご座流星群 (2021/12/13)

去年撮影のネタはこれで最後になります。2021年12月13日の夜、ふたご座流星群を撮影しました。正直流星はあまり興味がないのであまり撮らないのですが、なんとなく気になって動画だけ撮ることにしました。

深夜 0:30 頃、ベランダに適当に南北を合わせたスカイメモSを置いて OLYMPUS OM-D E-M1 Mark II + Voigtlander Nokton 17.5mm F0.95 で、Full-HD 30fps の動画を撮影開始。60fps にしようかと思ったのですが、それだとシャッタースピードが 1/60s までになるので、さすがに F0.95 といえども星がまるで写らなかったので諦めました。

SDカードがいっぱいになるか電池が切れるまで放置、と思っていたのですが、やはり気になって見てみると50分足らずで停止している様子。センサー温度か何かの要因で自動停止したのでしょうか。もう一度撮影開始しましたが20分足らずでまた停止。寒いのでそれ以上は撮らずに撤収しました。

そのまま寝ればいいものを、撮ったら撮ったで写ってるかどうか気になって、4倍速目視でチェック。70分で4つか5つくらい確認できたのですが、さすがにこれは見逃しありそうだし、なんとか自動的に検出する方法はないかとその後調べてみました。

最初に試してみたのがこちら。地学の先生のブログ「高津科学」のエントリ。

Python + OpenCV で検出するもので、GUI付き。入力はそのままでは .mov ファイルを選択できませんが、ソースの fTyp を書き換えるか、Windows ならファイルダイアログでファイル名に *.* を入力して Enter で選択できるようになります。

試してみたのですが、自分の動画の場合はデフォルトの閾値では誤検知が多く、閾値を上げると明らかな流星を見落とすようになって、ちょうどいいパラメータが見つけられませんでした。フレーム差分から動体検知をしているようなのですが、ノイズが多いとうまくいかないっぽいのでしょうか。

次に見つけたのはこちら。アストロアーツの公式ブログのつのださんのエントリ。

こちらも Python + OpenCV ですが、直線検出で流星を検出するもの。さらに地上風景や雲などからの誤検知を防ぐ機能もあります。が、残念ながら静止画のみで動画は非対応。

検出方式はこちらの方が期待できそうな気がしたので、高津科学さんの方にこちらの検出方式を移植するか、逆にこちらのスクリプトに高津科学さんの動画読み込みを移植するか… 悩んでるうちに年が明けてしまったのですが、コマンドラインの方が好みなので後者にしました。

動画専用に退化させるのも忍びないので静止画対応も残しつつ動画対応を追加することに。*1 Python は昨年の春以来ご無沙汰で for 文の書き方すら忘れてる有様でしたが、なんとか実装。

動画のフレームは露出時間が短く、そのままでは直線検出ができないことが予想されたため、複数枚のフレームを比較明合成したものに対して検出するようにしました。また、地上風景や雲を消すための面積検出&塗りつぶしの処理がノイズに反応してるっぽいので、とりあえずその部分はコメントアウトしました。

しかし、それだけでは流星が全然検出できず。おかしいなと思い、一度動画(約10分毎に分割されたもの)の全フレームを比較明合成した画像を作ると、結構な数の流星が見えています。

ふたご座流星群の比較明合成画像 (2021/12/14 02:14-02:24)
ふたご座流星群の比較明合成画像 (2021/12/14 02:14-02:24)
Voigtlander Nokton 17.5mm F0.95 (F0.95) / Kenko-Tokina スカイメモS / OLYMPUS OM-D E-M1 Mark II (ISO6400, FHD, Super Fine, 30p), 露出: 1/30秒 x フレームを比較明合成 / 自作スクリプト(Python 3.7.11 + OpenCV 3.4.2)で画像処理

輪郭検出と直線検出だけ抜き出したスクリプトに入力してパラメータを色々試したところ、以下のことがわかりました。

  • cv2.threshold() のパラメータの問題
  • cv2.HoughLinesP() のパラメータの問題
    • threshold が大きすぎる
    • minLineLength が大きすぎる
      • --line-threshold オプションより優先されるので同オプションに小さい値を指定しても意味がない。
    • maxLineGap が指定されていない
      • 動画だとフレームを比較明合成しても流星が細切れになるので直線が途切れるのをある程度許容しないといけない

cv2.threshold() のしきい値については背景レベルに合わせて適切な値を指定しないとノイズだらけになります。ノイズがあるとそこから大量の偽直線が検出されて使い物になりません。輝度の中央値を指定すればよさそうですが、周辺減光が大きい場合は中央部にノイズが残ってしまいます。

これは以下のようにして自動的に良さそうな値を得ることができました。

  1. 画面を 5x5 に分割して分割した各画像の中央値(median)の最大値 x を得る
  2. x をしきい値として輪郭画像を得る: cv2.threshold(image, x, 255, cv2.ADAPTIVE_THRESH_MEAN_C)
  3. 輪郭画像を 5x5 に分割し分割した各画像の平均値(mean)の最大値 y を得る
    1. y が 1.0 より大きいなら、x を 1 増やして 2 に戻る
    2. y が 1.0 以下になったらその時点の x を適切なしきい値とする

cv2.HoughLinesP() のパラメータはカメラの特性や焦点距離や流星の速度が関係するので自動調整は難しそう。結局トライアンドエラーでチューニングしました。結果はこんな感じです。

比較明合成画像の流星検出
比較明合成画像の流星検出

これだとどの流星がいつ流れたのかはわからないので、つのださんのスクリプトをこれらのパラメータをコマンドラインで渡せるように改造して、また、検出結果を JSON 形式のファイルに出力するようにしました。

さらにその検出結果ファイルからダイジェスト版動画を生成するスクリプトも書きました。

その結果がこちら。スクリプトが生成した動画を ffmpeg で画質調整したものです。

流星検出のテスト
流星検出のテスト

約4秒に1個流星が流れる動画になっていますが、実際は約10分の間に8つの流星が流れています。

スクリプトでは流星の流れるフレームとその前後の2秒間のフレームを切り出してつなげた動画を生成しています。流星のマーカーとフレームのタイムスタンプも隅っこに描画しています。カメラの時計がどこまで正確かわからないのであくまで目安としてください。

スクリプトソースコードは近日中に公開します。

*1:かなりいじったのに静止画の方は一切テストできてないので、ソースコードの公開はちょっと待ってください…