天文」カテゴリーアーカイブ

流星観測データの処理手順

STEP-1 pythonスクリプト(detectMETEORa.py)でIPカメラから画像を取得し、動体検知のアルゴリズムを中心とした処理で、流星らしい動画(avi形式)を保存する。

STEP-2 記録した個々の動画をstreamlitスクリプト(play.py)でGUI操作で再生し、目視で流星と判断した動画について、COPYボタンをクリックして、1個のaviファイルから1枚のjpg画像を比較明合成アルゴリズムで生成し、同時にavi形式の動画からh264形式の動画(mp4形式)へ変換して保存する。

STEP-3 jpgファイルとmp4ファイルを、Webブラウザーでインタラクティブに閲覧できるよう、あらかじめ用意したフォルダーへCopyする。

ブラウザーで閲覧するためのphpコード ▶ボタンをクリックして、表示されている画像の動画を再生することが可能です。表示対象の画像を月単位で選択できます。

<?php

$day= new DateTime();

if (isset($_POST['month']) && ($_POST['month']!="")){
        $month=$_POST['month'];
        $f_month=str_replace("-","",$month);
} else {
        $month="";
}

echo "<div>";
echo "<H2>動画を再生するには、各画像左下の再生ボタンをクリックして下さい。</H2>";
echo "<LI>画像上へマウスオーバーするとファイル名を表示します。<BR>";
echo "ファイル名[カメラ名_検知フレーム数_総フレーム数_YYYYMMDD_HHmmSS.mp4]<BR>";
echo "<form method =\"POST\">\n";
echo "<BR><LABEL Date>月を変更するには年月欄の右端をクリックしてください。</LABEL>";
echo "<input type=\"month\" name=\"month\" value=$month>";
echo "<input type =\"submit\"  value =\"表示\">";
echo "</form></dev>";
//echo "$f_month<BR>\n";
$images = glob('meteor/COMP/*jpg');
$n=0;
foreach($images as $v) {
        if (strpos($v,$f_month)) {
                $tmp=explode(".",$v);
                $mp4=$tmp[0].".mp4";
                $mp4=str_replace("COMP","BEST2",$mp4);
                //echo "$mp4<BR>";
                $tmp=explode("/",$mp4);
                $title=$tmp[2];
                $msg="<video controls muted title=$title width='480' height='280' src=$mp4 poster=$v></video>";
                echo $msg;
                if ($n % 2 == 1){
                        echo "<BR>\n";
                }
                $n++;
        }
}
?>

ffmpeg -i infielavi -c:v libx264 -c:a copy -y out-file.mp4

惑星直列

明け方の東の空に惑星が勢ぞろい(6月27日、3時20分頃)スマホ(Pixel6)で撮影してみた。

この画像では表現できていないが、デジカメCoolpix P950で倍率を上げて撮影したら、海王星、天王星も確認することができた。

raspberry P4 64bit gnome環境でkstarsでekosのアイコン(一部)が表示されない。

sudo apt-get install breeze-icon-themeで解決

Missing button icons in EKOS under Ubuntu 15.10

https://indilib.org/forum/ekos/988-missing-button-icons-in-ekos-under-ubuntu-15-10/50577.html

Kstars Integration issue with Gnome desktop

https://indilib.org/forum/ekos/3679-kstars-integration-issue-with-gnome-desktop.html

ISSの太陽面通過の撮影

国際宇宙ステーションが、太陽や月面の前を通過する日時・場所を、ISS TRANSIT FINDERで知ることができます。

11月25日に、自宅から少し離れた河川敷公園で、 国際宇宙ステーションの太陽面の通過を観測できそうなので、器材一式(カメラ、小型赤道儀、三脚)を持って撮影にトライしてみました。 今回のタイミングでは、ISSまでの距離が1600Km以上と、かなり遠いためISSらしい機影までは、確認に至りませんでしたが、撮影までの一連の流れを確認できました。下の画面では縮小されて見ずらいので、youtubeで表示した方が見やすいと思います。ISSは、右下から左上方向に移動します。

課題

カメラの撮影パラメータの適切化(動画/静止画、Iシャッター速度、撮影モード(連写/高速連写など、、、)

撮影器材

カメラ:NIKON P950、赤道儀:Az-GTi

おまけ:撮影の準備中に航空機が太陽面を通過する映像を記録できました。

streamlitで流星観測データを表示

# -*- coding: utf-8 -*-

import streamlit as st
import time
import datetime
import os
import glob
import cv2
import re
import numpy as np
from PIL import Image
from datetime import datetime, date, time

PATH='/home/metro//DATA/'

def comp_b2(A,B):
# 比較明合成処理
# https://nyanpyou.hatenablog.com/entry/2020/03/20/132937
#
    gray_img1 = cv2.cvtColor(A, cv2.COLOR_BGR2GRAY)
    gray_img2 = cv2.cvtColor(B, cv2.COLOR_BGR2GRAY)
#グレースケールの比較で作成したimg1用のマスク(img1の方が明るい画素を示す)
    mask_img1 = np.where(gray_img1>gray_img2, 255, 0).astype(np.uint8)
#img2用のマスク(0と255を入れ替え)(img2の方が明るい画素を示す)
    mask_img2 = np.where(mask_img1==255, 0, 255).astype(np.uint8)

#作成したマスクを使って元画像から抜き出し
    masked_img1 = cv2.bitwise_and(A, A, mask=mask_img1)
    masked_img2 = cv2.bitwise_and(B, B, mask=mask_img2)

    img3 = masked_img1 + masked_img2
    return img3

def disp(device):
        n=0
        cap = cv2.VideoCapture(device)
        W = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        H = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        W2=int(W/2)
        H2=int(H/2)
        image_loc = st.empty()
        prev=None
        while cap.isOpened:
            ret, img = cap.read()
            if ret:
                if W==1920:
                    img=cv2.resize(img, dsize=(W2, H2))
                #time.sleep(0.01)
                if prev is None:
                    prev = img.copy()
                else:
                    E = comp_b2(prev,img)
                    prev = E
                img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
                image_loc.image(img)
            else:
                break

        cap.release()
        image_loc = st.empty()
        img = Image.fromarray(cv2.cvtColor(prev, cv2.COLOR_BGR2RGB))
        image_loc.image(img)
    #st.button('Replay')

def main():
    st.header("流星観測データの表示")
    col1, col2, col3 = st.columns([1,1,3])
    with col1:
        date=st.date_input('DATE')
    path=PATH+date.strftime("%Y%m%d")
    selected=[]
    f_name=[]
    TL=[]
    if os.path.exists(path):
        files=glob.glob(path+'/*avi')
        # time filter : m[4] is time field.
        if files is not(None):
            for opt in files:
                m=re.split('[_.]',opt)
                TL.append(int(int(m[4])/10000))
            TL=list(set(TL))                        # sortして重複を削除
            # 処理対象の時間帯を選択するセレクトBOXの表示
            with col2:
                selected_item = st.selectbox('TIME',TL)
            selT = int(selected_item)
            for opt in files:
                m=re.split('[_.]',opt)
                if len(m)>=4:
                    t = int(int(m[4])/10000)
            #if not(t>60000 and t<180000):
                    if t==selT:
                        selected.append(opt)

            if selected is not(None):
                for name in selected:
                    f_name.append(name.rsplit('/',1)[1])
                with col3:
                    option = st.selectbox('FILE to DISPLAY',f_name)
            if option is not(None):
                disp(path+'/'+option)
    else:
        st.write('No data exists!')

if __name__ == '__main__':
    main()

streamlitで動画を再生

# -*- coding: utf-8 -*-

import streamlit as st
import time
import datetime
import os
import glob
import cv2
from PIL import Image
from datetime import datetime, date, time

PATH='/home/mars/pWork/DATA/'

def disp(device):
    cap = cv2.VideoCapture(device)
    image_loc = st.empty()
    while cap.isOpened:
        ret, img = cap.read()
        if ret:
            img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
            image_loc.image(img)
        else:
            break

    cap.release()
    st.button('Replay')

def main():
    st.header("流星観測データの表示")
    date=st.date_input('Select date')
    path=PATH+date.strftime("%Y%m%d")
    #st.write(path)
    if os.path.exists(path):
        files=glob.glob(path+'/*avi')
        option = st.selectbox('Select file:',files)
        disp(option)
    else:
        st.write('No data exists!')

if __name__ == '__main__':
    main()

ファイルの選択対象を、様々な条件で絞りこむコードを追加したい。

cudaとThread対応のテスト

複数台のネットワークカメラに対応し、cuda(GPU)とThread機能を活用できるバージョン。

import cv2
import numpy as np
import time
import datetime
import os
import sys
GPU=True
THREAD=True
if len(sys.argv)>1:
    if sys.argv[1]=='A':
        cPATH='rtsp://5173:6703@192.168.68.74/live'
    else:
        cPATH='rtsp://admin:@192.168.68.128:554/1/h264major'
else:
        cPATH='rtsp://5173:6703@192.168.68.74/live'
PATH="/home/mars/pWork/DATA"
#

class ThreadingVideoCapture:
    def __init__(self, src, max_queue_size=256):
        self.video = cv2.VideoCapture(src)
        self.q = queue.Queue(maxsize=max_queue_size)
        self.stopped = False

    def start(self):
        thread = threading.Thread(target=self.update, daemon=True)
        thread.start()
        return self

    def update(self):
        while True:
            if self.stopped:
                return
            if not self.q.full():
                ok, frame = self.video.read()
                self.q.put((ok, frame))
                if not ok:
                    self.stop()
                    return

    def read(self):
        return self.q.get()

    def stop(self):
        self.stopped = True

    def release(self):
        self.stopped = True
        self.video.release()

    def isOpened(self):
        return self.video.isOpened()

    def get(self, i):
        return self.video.get(i)

def key(k):
    global th, tc,track,reverse
    if k == ord('2'):
        th = th - 1
    elif k == ord('3'):
        th = th + 1
    elif k == ord('4'):
        tc = tc -5
    elif k == ord('5'):
        tc = tc +5
    elif k == ord('t'):
        track = not track
    elif k == ord('r'):
        reverse = not reverse

fontFace =cv2.FONT_HERSHEY_SIMPLEX
track, reverse = False,False
avg=None
writer = None
th = 30
tc = 30
x,y=0,0
detect_counts = 0
red,blue,green = (0,0,255),(255,0,0),(0,255,0)
time_start = time.time()
frame=0
log=PATH+'/metro.log'
if cPATH=='rtsp://5173:6703@192.168.68.74/live':
    TITLE="ATOM"
    HEAD ='ATOM'
else:
    TITLE="ONVIF"
    HEAD='ONVIF'

if THREAD:
    import threading
    import queue
    TITLE=TITLE+"-T"
    capture = ThreadingVideoCapture(cPATH)
    capture.start()
    if not capture.isOpened():
        raise RuntimeError
else:
    capture=cv2.VideoCapture(cPATH)

if GPU:
    TITLE=TITLE+"-G"
    img_gpu_src = cv2.cuda_GpuMat() # Allocate device memory only once, as memory allocation seems to take time...
    img_gpu_dst = cv2.cuda_GpuMat()
    img_gpu_gray= cv2.cuda_GpuMat()

W = capture.get(cv2.CAP_PROP_FRAME_WIDTH)
H = capture.get(cv2.CAP_PROP_FRAME_HEIGHT)
W2=int(W/2)
H2=int(H/2)
fourcc = cv2.VideoWriter_fourcc(*"XVID")
print('Camera:',cPATH)
print('Size:',W,H)
while(True):
    ret, img = capture.read()
    if ret:
        org = img.copy()
        if GPU:
            img_gpu_src.upload(img)
            img_gpu_dst = cv2.cuda.resize(img_gpu_src, dsize=(W2, H2))
            img_gpu_dst = cv2.cuda_GpuMat(img_gpu_dst,[0,int(H2*0.85)],[0,W2])
            img_gpu_gray=cv2.cuda.cvtColor(img_gpu_dst,cv2.COLOR_BGR2GRAY)
            org_img=img_gpu_dst.download()
            gray = img_gpu_gray.download()
        else:
            org_img = cv2.resize(img, dsize=(W2, H2))
            org_img=img[0:int(H2*0.85),0:W2]
            gray = cv2.cvtColor(org_img, cv2.COLOR_BGR2GRAY)
        if reverse:
            gray=cv2.bitwise_not(gray)
            avg=cv2.bitwise_not(avg)
        if avg is None:
            avg = gray.copy().astype("float")
            continue

        #wtiter,fname = moving(img,avg)
        cv2.accumulateWeighted(gray, avg, 0.5)
        frameDelta = cv2.absdiff(gray, cv2.convertScaleAbs(avg))
        thresh = cv2.threshold(frameDelta, th, 255, cv2.THRESH_BINARY)[1]

        contours,hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        detect=False
        for i in range(0,len(contours)):
            if len(contours[i]) > 0:
                 if cv2.contourArea(contours[i]) > tc:
                    detect=True
                    time_start = time.time()
                    if writer is None and track:
                        detect_counts = 0
                        now=datetime.datetime.today()
                        date=now.strftime("%Y%m%d")
                        cDIR=PATH+'/'+date
                        if not(os.path.exists(cDIR)):
                            os.mkdir(cDIR)
                        fname=cDIR+'/'+ HEAD + now .strftime("%Y%m%d_%H%M%S")+".avi"
                        writer = cv2.VideoWriter(fname, fourcc, 15, (int(W), int(H)))
                    rect = contours[i]
                    x, y, w, h = cv2.boundingRect(rect)
                    cv2.rectangle(org_img, (x-w, y-h), (x + w*2, y + h*2), red, 3)
        if detect:
            detect_counts=detect_counts + 1
        if time.time() -  time_start  > 5:
            if writer is not None:
                writer.release()
                new_name=fname.replace(HEAD,HEAD+'_' + f'{detect_counts:04}'+'_')
                os.rename(fname,new_name)
                frame=0
                writer = None
        now=datetime.datetime.today()
        text=now.strftime("%Y%m%d %H%M%S")+' No:'+str(frame)+ ' '+" TH:"+str(th)+" SZ:"+str(tc)
        org_img = cv2.putText(org_img, text, (30,50), fontFace,1,color=green)
        org = cv2.putText(org, text, (30,50), fontFace,1,color=green)
        text1="REC:"+str(track) + "  reverse:" + str(reverse)
        if writer is not None:
            frame=frame+1
            text1=fname+' '+text1
        org_img = cv2.putText(org_img, text1, (30,80), fontFace,1,color=green)
        #cv2.imshow('thresh-level',thresh)

        cv2.imshow(TITLE,org_img)

        if writer is not None:
            writer.write(org)
    else:
        now=datetime.datetime.today()
        date=now.strftime("%Y%m%d_%H%M%S")
        print('reconnect:',date)
        capture.release()
        avg=None
        if THREAD:
            capture = ThreadingVideoCapture(cPATH)
            capture.start()
        else:
            capture = cv2.VideoCapture(cPATH)
    k=cv2.waitKey(1) & 0xFF
    key(k)
    if k== ord('q'):
        break

capture.release()
if writer is not None:
    writer.release()
    new_name=fname.replace(HEAD,HEAD+'_' + f'{detect_counts:04}'+'_')
    os.rename(fname,new_name)
cv2.destroyAllWindows()