基于树莓派的流星雨监测系统(RMS)的进一步改造(1)

简介: 本文介绍了如何搭建和改造流星雨监测系统,主要涉及两个步骤。首先,文章提供了访问[此处链接](https://blog.csdn.net/delacroix_xu/article/details/119813807)来了解如何搭建系统。接着,针对系统输出的.bin文件格式,作者改造了FRbinViewer.py脚本,增加了输出MP4和GIF格式的功能。改造后的脚本可以根据用户选择将检测到的流星雨帧保存为.gif或.mp4格式,并提供了相应的参数设置。此外,文章还包含了代码示例以展示如何实现这一功能。

如何搭建流星雨监测系统,传送门:https://blog.csdn.net/delacroix_xu/article/details/119813807


进一步改造系列文章,第二篇,传送门:https://blog.csdn.net/delacroix_xu/article/details/119744925


背景:

近期开始使用一个开源项目,在树莓派4B上玩耍。监测流星雨并存储下来。


https://github.com/CroatianMeteorNetwork/RMS


但该项目有个令人不爽的地方,存储下来的是.bin文件,一种自研的格式,我希望能输出gif或者mp4,方便分享到社交媒体上。


FRbinViewer.py 增加功能


1、输出 MP4格式的文件


新增参数 -f avi 实际输出的是mp4文件, 该参数需要配合 --extract 参数一起使用。该功能会在.bin文件同一级目录下,生成对应的mp4文件


举例说明:


python Utils/FRbinViewer.py  ~/RMS_data/ArchivedFiles/XX_0001_20210723/  --extract -f avi --hide


--hide 表示不显示到屏幕上


2、输出gif文件


新增参数 -f gif 。会在.bin文件同一级目录下,生成对应的gif文件


具体使用方法,同 -f avi




""" Showing fireball detections from FR bin files. """
 
# RPi Meteor Station
# Copyright (C) 2017  Dario Zubovic, Denis Vida
# 
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
from __future__ import print_function, absolute_import, division
 
import os
import sys
 
sys.path.append("./")
import argparse
 
import cv2
from cv2 import VideoWriter, VideoWriter_fourcc
from PIL import Image as PILImage
import numpy as np
import Utils.ConvertPng2Avi as cp2a
import imageio
import shutil
 
import RMS.ConfigReader as cr
from RMS.Formats import FFfile, FRbin
 
 
def view(dir_path, ff_path, fr_path, config, save_frames=False, extract_format='png', hide=False,
    avg_background=False):
    """ Shows the detected fireball stored in the FR file. 
    
    Arguments:
        dir_path: [str] Current directory.
        ff: [str] path to the FF bin file
        fr: [str] path to the FR bin file
        config: [conf object] configuration structure
    Keyword arguments:
        save_frames: [bool] Save FR frames to disk. False by defualt.
        extract_format: [str] Format of saved images. png by default.
        hide: [bool] Don't show frames on the screen.
        avg_background: [bool] Avepixel as background. False by default, in which case the maxpixel will be
            used.
    """
 
    if extract_format is None:
        extract_format = 'png'
    
    name = fr_path
    fr = FRbin.read(dir_path, fr_path)
 
    print('------------------------')
    print('Showing file:', fr_path)
 
 
    if ff_path is None:
        #background = np.zeros((config.height, config.width), np.uint8)
 
        # Get the maximum extent of meteor frames
        y_size = max([max(np.array(fr.yc[i]) + np.array(fr.size[i])//2) for i in range(fr.lines)])
        x_size = max([max(np.array(fr.xc[i]) + np.array(fr.size[i])//2) for i in range(fr.lines)])
 
        # Make the image square
        img_size = max(y_size, x_size)
 
        background = np.zeros((img_size, img_size), np.uint8)
 
    else:
        if avg_background:
            background = FFfile.read(dir_path, ff_path).avepixel
        else:
            background = FFfile.read(dir_path, ff_path).maxpixel
    
    print("Number of lines:", fr.lines)
    
    first_image = True
    wait_time = 2*int(1000.0/config.fps)
 
    pause_flag = False
 
    
    print("output format: ", extract_format)
    for current_line in range(fr.lines):
 
        # if output format is gif , then declare this variable
        if extract_format == 'gif':
            gif_frames = []
        
        # if outpu format is avi, then declare this variable
        if extract_format == 'avi':
            videoWriter = None
            temp_dir = os.path.join(dir_path, "TEMP_DIR_" + fr_path.replace('.bin', ''))
            if os.path.exists(temp_dir):
                shutil.rmtree(temp_dir)
            
        print('Frame,  Y ,  X , size')
        
        for z in range(fr.frameNum[current_line]):
 
            # Get the center position of the detection on the current frame
            yc = fr.yc[current_line][z]
            xc = fr.xc[current_line][z]
 
            # Get the frame number
            t = fr.t[current_line][z]
 
            # Get the size of the window
            size = fr.size[current_line][z]
            
            print("  {:3d}, {:3d}, {:3d}, {:d}".format(t, yc, xc, size))
 
            img = np.copy(background)
            
            # Paste the frames onto the big image
            y_img = np.arange(yc - size//2, yc + size//2)
            x_img = np.arange(xc - size//2,  xc + size//2)
 
            Y_img, X_img = np.meshgrid(y_img, x_img)
 
            y_frame = np.arange(len(y_img))
            x_frame = np.arange(len(x_img))
 
            Y_frame, X_frame = np.meshgrid(y_frame, x_frame)                
 
            img[Y_img, X_img] = fr.frames[current_line][z][Y_frame, X_frame]
 
 
            # Save frame to disk
            if save_frames:
                if extract_format == "png":
                    frame_file_name = fr_path.replace('.bin', '') \
                        + "_line_{:02d}_frame_{:03d}.{:s}".format(current_line, t, extract_format)
                    cv2.imwrite(os.path.join(dir_path, frame_file_name), img)
                elif extract_format == 'gif':
                    gif_frames.append(img)
                elif extract_format == "avi":
                    
                    if not os.path.exists(temp_dir):
                        os.makedirs(temp_dir)
                    frame_file_name = os.path.join(
                        temp_dir, 
                        "line_{:02d}_frame_{:03d}.png".format(current_line, t)
                    )
                    cv2.imwrite(frame_file_name, img)
                
                
 
            if not hide:
            
                # Show the frame
                try:
                    cv2.imshow(name, img)
                except:
                    print("imshow not available in OpenCV, Rebuild the library with Windows, GTK+ 2.x or Cocoa support. If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config, then re-run cmake or configure script in function 'cvShowImage'")
                    hide = True
                    first_image = False
                    continue
 
                # If this is the first image, move it to the upper left corner
                if first_image:
                    cv2.moveWindow(name, 0, 0)
                    first_image = False
 
 
                if pause_flag:
                    wait_time = 0
                else:
                    wait_time = 2*int(1000.0/config.fps)
 
                # Space key: pause display. 
                # 1: previous file. 
                # 2: next line. 
                # q: Quit.
                key = cv2.waitKey(wait_time) & 0xFF
 
                if key == ord("1"): 
                    cv2.destroyWindow(name)
                    return -1
 
                elif key == ord("2"): 
                    break
 
                elif key == ord(" "): 
                    
                    # Pause/unpause video
                    pause_flag = not pause_flag
 
                elif key == ord("q") : 
                    os._exit(0)
        
        # if gif format is set, then output gif file
        if extract_format == 'gif':
            
            gif_name = fr_path.replace('.bin', '.gif')
            gif_path = os.path.join(dir_path, gif_name)
            print(" try output gif file: ", gif_path)
            imageio.mimsave(gif_path, gif_frames, 'GIF', duration=2*1.0/config.fps)
        
        # release handle
        if extract_format == 'avi':
            temp_dir = os.path.join(dir_path, "TEMP_DIR_" + fr_path.replace('.bin', ''))
            cp2a.convert_from_dir(
                temp_dir, 
                "",
                os.path.join(dir_path, fr_path.replace('.bin', '.mp4')),
                config.fps,
                True
            )
            
            
    if not hide:
        cv2.destroyWindow(name)
            
 
if __name__ == "__main__":
 
    ### COMMAND LINE ARGUMENTS
 
    # Init the command line arguments parser
    arg_parser = argparse.ArgumentParser(description="""Show reconstructed fireball detections from FR files.
        Key mapping:
            Space: pause display.
            1: previous file.
            2: next line.
            q: Quit.
            """, formatter_class=argparse.RawTextHelpFormatter)
 
    arg_parser.add_argument('dir_path', nargs=1, metavar='DIR_PATH', type=str, \
        help='Path to the directory which contains FR bin files.')
 
    arg_parser.add_argument('-e', '--extract', action="store_true", \
        help="Save frames from FR files to disk.")
 
    arg_parser.add_argument('-a', '--avg', action="store_true", \
        help="Average pixel as the background instead of maxpixel.")
 
    arg_parser.add_argument('-x', '--hide', action="store_true", \
        help="Do not show frames on the screen.")
    
    arg_parser.add_argument('-f', '--extractformat', metavar='EXTRACT_FORMAT', help="""Image format for extracted files. png by default. gif is supported """)
 
    # Parse the command line arguments
    cml_args = arg_parser.parse_args()
 
    #########################
 
    dir_path = cml_args.dir_path[0]
 
    # Load the configuration file
    config = cr.parse(".config")
 
    
 
    # Get the list of FR bin files (fireball detections) in the given directory
    fr_list = [fr for fr in os.listdir(dir_path) if fr[0:2]=="FR" and fr.endswith('bin')]
    fr_list = sorted(fr_list)
 
    if not fr_list:
 
        print("No files found!")
        sys.exit()
 
    # Get the list of FF bin files (compressed video frames)
    ff_list = [ff for ff in os.listdir(dir_path) if FFfile.validFFName(ff)]
    ff_list = sorted(ff_list)
 
 
    i = 0
 
    while True:
 
        # Break the loop if at the end
        if i >= len(fr_list):
            break
 
        fr = fr_list[i]
 
        ff_match = None
 
        # Strip extensions
        fr_name = ".".join(fr.split('.')[:-1]).replace('FR', '').strip("_")
 
        # Find the matching FF bin to the given FR bin
        for ff in ff_list:
 
            # Strip extensions
            ff_name = ".".join(ff.split('.')[:-1]).replace('FF', "").strip("_")
 
 
            if ff_name[2:] == fr_name[2:]:
                ff_match = ff
                break
        print("ff_match:", ff_match)
        # View the fireball detection
        retval = view(dir_path, ff_match, fr, config, save_frames=cml_args.extract, \
            extract_format=cml_args.extractformat, hide=cml_args.hide, avg_background=cml_args.avg)
 
        # Return to previous file
        if retval == -1:
            i -= 2
 
        if i < 0:
            i = 0
 
        i += 1
相关文章
|
物联网 Java Linux
一文读懂物联网 MQTT 协议之实战篇
一文读懂物联网 MQTT 协议之实战篇
686 1
|
Java 计算机视觉
实现邮箱验证(邮箱验证码登录)
我们要实现web或者Java的发送邮箱验证码到邮箱上进行验证。当然我们需要做一下前提的准备,也就是先要导我们的jar包,然后再进行下一步的操作。
|
存储 缓存 安全
Docker Debian安装Docker
Docker Debian安装Docker
3557 1
|
Web App开发 Shell 数据安全/隐私保护
CURL常用命令
下载单个文件,默认将输出打印到标准输出中(STDOUT)中 curl http://www.centos.org 通过-o/-O选项保存下载的文件到指定的文件中: -o:将文件保存为命令行中指定的文件名的文件中 -O:使用URL中默认的文件名保存文件到本地 1 # 将文件下载到本地并命名为mygettext.
8381 0
|
3月前
|
SQL Java 数据库
2025 年 Java 从零基础小白到编程高手的详细学习路线攻略
2025年Java学习路线涵盖基础语法、面向对象、数据库、JavaWeb、Spring全家桶、分布式、云原生与高并发技术,结合实战项目与源码分析,助力零基础学员系统掌握Java开发技能,从入门到精通,全面提升竞争力,顺利进阶编程高手。
720 1
|
SQL 监控 关系型数据库
为 MySQL/MariaDB 开启 Binlog 功能
说到 Binlog 就不得不提一下 MySQL Server 的四种类型的日志:Error Log、General Query Log、Slow Query Log 和 Binary Log 。
5852 0
|
11月前
|
存储 前端开发 Java
Harry技术添加存储(minio、aliyun oss)、短信sms(aliyun、模拟)、邮件发送等功能
### SpringBoot3 + Vue3 前后端分离的Java快速开发框架更新 本次更新主要包含以下内容: 1. **端口修改**:为避免与Minio存储服务冲突,后端启动端口从9000改为9999。 2. **添加存储支持**:集成Minio和阿里云OSS对象存储服务,详细配置请参考相关文档。 3. **短信服务**:接入阿里云短信服务,并增加模拟发送功能,方便本地测试。 4. **邮件发送**:引入邮件发送功能,支持简单文本邮件和带附件邮件。 5. **完善个人中心**:优化个人中心页面,提升用户体验。
396 85
Harry技术添加存储(minio、aliyun oss)、短信sms(aliyun、模拟)、邮件发送等功能
|
人工智能 监控 数据库
LLM 应用可观测性:从 Trace 视角展开的探索与实践之旅
基于大语言模型的应用在性能、成本、效果等方面存在一系列实际痛点,本文通过分析 LLM 应用模式以及关注点差异来阐明可观测技术挑战,近期阿里云可观测推出了面向 LLM 应用的可观测解决方案以及最佳实践,一起来了解下吧。
20350 119
LLM 应用可观测性:从 Trace 视角展开的探索与实践之旅
|
SQL Java 关系型数据库
【Java】已解决Java中的com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException异常
【Java】已解决Java中的com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException异常
1516 0
Axure 自定义元件库
Axure 自定义元件库
409 0
Axure 自定义元件库