暫く使っていた Atomcam2 が強烈な雨風に負けて水没してしまいリニューアルしました。
Atomcam2 購入
- WiFi 接続初期設定、タイムスタンプ OFF
- Atom ロゴ OFF
- ナイトビジョンオート、LED Off (一度 on にして off)
- 常時録画
- 動画検出 OFF
- sd カードフォーマット、atomcam tools 導入
適当な星空が撮影されるまで放置
撮影した動画像から wcs 生成
- 星空が撮影された適当な動画像を Atomcam2 から取得
$ wget http://atomcam.local/sdcard/record/20231024/22/00.mp4
- ノイズを軽減するために動画像から加算平均画像を生成
import cv2
import numpy as np
capture = cv2.VideoCapture("00.mp4")
width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = int(capture.get(cv2.CAP_PROP_FPS))
r = np.zeros([height, width, 3], dtype=np.float32)
while True:
(s, frame) = capture.read()
if not s:
break
r += frame
r /= r.max()
cv2.imwrite("average.png", (r * 255).astype(np.uint8))
$ solve-field --scale-units arcsecperpix --scale-high 190 --scale-low 170 average.png --overwrite --downsample 1
Atomcam2 のレンズは大体 180 arcsec/pixel 前後のようです。なので --scale-units
で arcsecperpix を指定し、 --scale-high
,--scale-low
を用いて検索範囲を 170〜190 に限定することで solve-field での解決を早める期待ができます。
このコマンドラインで必ず解決できるわけではありません。特に歪みの少ない中心付近に解決に十分な星が検出できなければ解決に失敗する場合が多いと思います。
solve-field で無事解決ができると wcs と、アノテーションされた画像が取得できます。
これで問題なければ、以降出力された wcs を利用していけば良いのですが、残念ながら中心から離れるほどレンズの歪みの影響を受けて大きくずれていくという状態になるかと思います。
なのでここからはなるべく歪みに対応しズレが小さくなることが期待できる wcs を生成します。
プレートソルブせず人力で位置合わせをして wcs を生成
プレートソルブを用いて自動で位置を検出すると歪みに対応しきれないので手動で位置を設定して wcs を生成するようにします。
- 画像に写っている星の画面上の (x, y) 座標位置を抽出
やり方は色々あるかと思いますが、ここでは安直に二値化して findContours して momtent で座標を決定しています。
import cv2
import pandas as pd
data = cv2.imread("average.png")
b = cv2.threshold(cv2.cvtColor(data, cv2.COLOR_BGR2GRAY), 127, 255, cv2.THRESH_BINARY)[1]
contours = cv2.findContours(b, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[0]
stars = []
for c in contours:
m = cv2.moments(c)
if m['m00'] > 0:
x,y= m['m10']/m['m00'] , m['m01']/m['m00']
stars.append([x, y])
cv2.circle(data, (round(x), round(y)), 2, (255, 0, 0), thickness=5)
pd.DataFrame(stars, columns=["x", "y"])
SEP とか使ったほうが良いのかな?
index |
x |
y |
0 |
1893.6666666666665 |
1078.6666666666665 |
1 |
1676.3333333333333 |
1078.6666666666665 |
2 |
48.33333333333333 |
1078.6666666666665 |
3 |
37.5 |
1078.5 |
4 |
1916.9629629629626 |
1076.7037037037035 |
5 |
1866.3333333333333 |
1075.6666666666665 |
6 |
19.866666666666667 |
1075.8666666666666 |
7 |
1915.6538461538462 |
1072.4615384615386 |
8 |
769.6527777777777 |
1067.9166666666667 |
9 |
1533.9375 |
1052.3125 |
10 |
784.7619047619047 |
1039.5238095238094 |
後略
- 抽出された星の(x, y) 座標に、その星自体の赤緯/赤経(ra, dec)の座標を紐付け fits 形式で保存します。
ここでは
map.fits
として保存しています。
from astropy.table import Table
import pandas as pd
map = pd.read_csv("map.csv", header=None, names=['INDEX_RA', 'INDEX_DEC', 'FIELD_X', 'FIELD_Y']).astype("double")
Table.from_pandas(map).write("map.fits", format="fits", overwrite=True)
index |
INDEX_RA |
INDEX_DEC |
FIELD_X |
FIELD_Y |
0 |
89.93015897 |
37.21276409 |
769.2764227642276 |
1068.3495934959349 |
1 |
73.5628985 |
2.44067149 |
1534.048611111111 |
1052.125 |
2 |
87.7600428 |
37.30568175 |
784.6428571428571 |
1039.571428571429 |
3 |
81.57290804 |
28.60787346 |
989.452380952381 |
1030.2380952380952 |
4 |
72.80152507 |
5.60510146 |
1480.952380952381 |
1024.571428571429 |
5 |
87.87246934 |
39.14847936 |
751.3859649122807 |
1021.8070175438596 |
6 |
87.29358126 |
39.18113322 |
755.5555555555555 |
1014.2222222222222 |
7 |
72.45890935 |
6.96124744 |
1458.7395833333333 |
1011.375 |
8 |
72.65300943 |
8.90025258 |
1421.3333333333333 |
1005.1333333333332 |
9 |
96.22459158 |
49.28789903 |
518.6969696969696 |
995.6969696969696 |
10 |
89.88237261 |
44.94743492 |
636.4642857142857 |
982.1666666666666 |
後略、実際には 85 程のマップを作成しています。
- Astrometry.net の fit-wcs を用いて wcs を生成します。
$ fit-wcs -W 1920 -H 1080 -s 4 -c map.fits -o new.wcs
-s
オプションは SIP の歪み係数の次数の設定。どの程度が良いかはいろいろ試して出力を比較し決定しました。今回は概ね4を指定すると期待する出力に最も近くなりました。
- 生成された wcs から Astrometry.net の plot-constellations を用いてアノテーションを生成します
$ pngtopnm average.png | plot-constellations -w new.wcs -o test.png -C -N -B -i -
こういった wcs で何ができるか
ずれの小さいアノテーションを被せられるようになり、頑張るとこういう動画が作れるようになったりします。
よしなしごと
- 要は fit-wcs を使えばプレートソルブせず別途作成した座標のマップから wcs を生成できるという話なのだけれど、何故か fit-wcs に関するドキュメントが見つけられない。元々は SIP の対応を実装から読んでいてたまたま見つけて使ってみたらそれなりに動きそうという感じだった。