代码已开源:
https://github.com/xingxinghuo1000/meteor_monitor_scripts.git
本篇为最新代码的方案介绍和使用介绍。第一篇已经过时了,不建议看 。只看这一篇即可。
背景
著名的流星监控软件ufocapturehd2有几个缺陷,不能忍
1、吃性能,我的工控电脑2.4GHz的4核心CPU,发现流星后,录制出来的视频,会严重丢帧
2、收费。还相当的贵。大概700多元? 用破解版当然也可以,这里不推荐
使用方法
推荐硬件
主机:
windows系统,win10 win11. 小主机, cpu 2.4GHz, 4核心.
不推荐Linux系统, 因为显卡驱动不容易安装,需要较高的技术水平.
不推荐树莓派, 原因同Linux系统.
摄像头的选择
推荐使用 索尼 MX291 摄像头. CMOS底比较大,适合拍摄星空
如果您的摄像头CMOS尺寸太小, 则噪点会比较多. 所以推荐IMX291这款, 如果使用IMX485, 则更好.
如何使用
建议在windows使用,原因是显卡驱动程序比较容易安装. 在Ubuntu下intel核心显卡驱动安装比较困难,无法使用h264_qsv编码
本程序默认使用mjpeg码流, 如果cpu支持硬件编码h264,则可以尝试使用h264_qsv编码器
如果是windows环境 , 且是 Intel核心显卡, 则可以尝试h264_qsv编码, nvdia显卡尝试 h264_nvenc, amd显卡尝试 h264_amf
如果是Ubuntu环境, 请尝试 mjpeg 编码, 经过我的测试, 2.4GHzCPU 只能达到15fps, 如果需要更高比如30fps,则对CPU要求也会很高.
如果您在Ubuntu环境,安装好了显卡驱动程序, Intel核心显卡可以尝试 h264_qsv 编码, nvdia显卡尝试 h264_nvenc, amd显卡尝试 h264_amf
如果是树莓派, 且编译了带h264_omx编码的ffmpeg, 则编码器使用 h264_omx
安装ffmpeg
本程序依赖ffmpeg命令,
我目前只测试过 4.x.x 版本的ffmpeg, 不能保证 5.x 和 6.x也能正常工作
Windows环境,需要到ffmpeg官网(Download FFmpeg) 下载ffmpeg可执行程序, 下载压缩包,解压缩到某个目录, 并将目录添加到环境变量. 如何验证安装是否成功, 可以打开cmd, 输入 ffmpeg -v 看看是否能正常看到输出的版本号
Linux环境, sudo yum install ffmpeg 或者 sudo apt install ffmpeg, 验证方法, 在shell中输入 ffmpeg -v 看看是否正常显示版本号
如果是树莓派, 则需要手工编译ffmpeg, 启用h264_omx编码, 比较麻烦, 不推荐.
插入usb摄像头
将usb摄像头插入主机的usb口, 执行下面的命令, 检查系统是否能正常识别摄像头
Windows中, 打开cmd 输入 ffmpeg -list_devices true -f dshow -i dummy 看看返回结果
Linux系统中, 在shell中输入 ffmpeg -hide_banner -sources v4l2 看看返回结果
如何修改配置文件.config 参考.config.xx文件,对应你的操作系统, 配置 文件中都有说明
比如win11系统,可以参考.config.win
如果是Ubuntu系统,可以参考.config.linux
如何设置分辨率和fps?
根据自己的硬件性能,逐步调整fps到可用的程度. 如果 ffmpeg 日志中提示丢帧, 则fps设置过大, 需要降低fps, 以保证画面流畅
还要看摄像头支持哪些分辨率.如果设置错误,则无法录制视频
查看摄像头信息
根据ffmpeg显示出来的信息, 查看摄像头支持哪些分辨率和fps. 配置到.config 文件中
配置Python环境
版本: 推荐使用Python 3.10及以上
步骤1
新建virtual env. 在 detect_meteor目录下, 执行如下命令
windows下, python3 -m venv venv
Linux下, python3 -m venv venv
步骤2
安装依赖
windows下, 打开 cmd, 在 detect_meteor 目录下, 执行命令 .\venv\Script\python -m pip install -r requirement.txt
Linux下, 在 detect_meteor目录下, 执行命令 ./venv/bin/python -m pip install -r requirement.txt
步骤3
运行本程序
windows下, 双击 offline_detect_from_mp4.bat
Linux下, 在detect_meteor目录下, 执行 sh run.sh
步骤4
配置开机自动启动,
Windows下, 右键点击 offline_detect_from_mp4.bat, 发送到桌面快捷方式, 将 快捷方式,复制到 开始菜单的 启动 目录
Linux下, crontable中配置 */5 * * * * cd /home/yourname/workspace/meteor_monitor_script/detect_meteor && sh run.sh
步骤5 可选
配置 mask-1280-720.bmp 遮罩图像, 目的是排除掉画面中的一些非天空部分, 这部分可能会引起False Positive, 流星的误报
比如 画面中远处楼宇的灯光变化, 可能会让程序以为是有画面变化
问题: 在哪个目录新建 遮罩图像?
答: 在视频目标输出目录, 在配置文件的 base_output_path 选项, 该路径由您指定
问题: 遮罩图像黑色白色代表什么意思?
答: 黑色表示要遮盖的部分, 白色表示要检测的部分
原理介绍
高效的录制mp4视频
使用ffmpeg, 高效录制视频. 这部分,我没有足够能力使用python实现, 所以借助ffmpeg的能力
ffmpeg 支持多种编码格式, h264支持多种硬件加速, 比如Intel核心显卡, nvdia显卡, AMD显卡
备注: 曾经使用过一个方案 使用windows自带的相机app,进行录像. 这个方案不够稳定, 已被废弃. 该方案使用sikuli进行UI自动化操作, 非常不稳定.
为了帮您理解该项目,下面是一个代码示例片段,演示如何使用 Python 和 FFmpeg 从 USB 摄像头录制视频,并自动获取摄像头的最佳帧率和最大画面尺寸:
import subprocess import re def get_camera_info(): # 使用 ffmpeg 获取摄像头信息 cmd = ["ffmpeg", "-f", "v4l2", "-list_formats", "all", "-i", "/dev/video0"] result = subprocess.run(cmd, stderr=subprocess.PIPE, text=True) # 解析输出以获取最佳帧率和最大画面尺寸 output = result.stderr frame_rates = [] resolutions = [] for line in output.split("\n"): # 获取帧率信息 frame_rate_match = re.search(r"([0-9]+(\.[0-9]+)?) fps", line) if frame_rate_match: frame_rates.append(float(frame_rate_match.group(1))) # 获取分辨率信息 resolution_match = re.search(r"([0-9]+)x([0-9]+)", line) if resolution_match: width = int(resolution_match.group(1)) height = int(resolution_match.group(2)) resolutions.append((width, height)) best_frame_rate = max(frame_rates) if frame_rates else 30 # 如果未找到,默认为30fps max_resolution = max(resolutions, key=lambda x: x[0]*x[1]) if resolutions else (640, 480) # 默认640x480 return best_frame_rate, max_resolution def record_video(output_file, duration): best_frame_rate, max_resolution = get_camera_info() width, height = max_resolution # 使用 ffmpeg 录制视频 cmd = [ "ffmpeg", "-f", "v4l2", "-framerate", str(best_frame_rate), "-video_size", f"{width}x{height}", "-i", "/dev/video0", "-t", str(duration), "-c:v", "libx264", "-preset", "fast", "-pix_fmt", "yuv420p", output_file ] subprocess.run(cmd) # 调用示例 output_video_path = "output_video.mp4" record_duration = 10 # 录制10秒 record_video(output_video_path, record_duration)
- 获取摄像头信息 get_camera_info:
- 使用 FFmpeg 命令列出摄像头支持的所有格式。
- 解析 FFmpeg 的输出以获取帧率和分辨率信息。
- 选择最佳帧率和最大分辨率。
- 录制视频 record_video:
- 使用上面函数获取最佳帧率和最大分辨率。
- 调用 FFmpeg 命令通过 USB 摄像头录制视频,并使用 H.264 编码。
- FFmpeg 命令解释:
- -f v4l2:指定输入格式为 V4L2(Video for Linux 2)。
- -framerate:设置帧率。
- -video_size:设置分辨率。
- -i /dev/video0:指定输入设备为 USB 摄像头。
- -t:设置录制时长。
- -c:v libx264:指定视频编码器为 H.264。
- -preset fast:设置编码速度,fast 是编码速度和质量之间的平衡。
- -pix_fmt yuv420p:设置像素格式为 YUV 4:2:0。
请注意:
- /dev/video0 是 Linux 下的摄像头设备路径。对于 Windows 系统可以使用如 -i video="YOUR_CAMERA_NAME".
- 在运行代码之前,请确保你的摄像头可以被 FFmpeg 识别,并且你的系统 PATH 中包含了 FFmpeg 程序。
通过上述代码,你可以实现从 USB 摄像头录制视频,并根据摄像头的最佳帧率和最大画面尺寸来设置录制参数
离线分析流星
为什么不做实时分析?
因为硬件性能不够. 我的目的是在低配置的硬件上,运行本程序. 如果您的硬件性能已经很强悍, 则建议直接使用ufohd2
离线分析的原理
步骤1
使用典型的opencv 运动检测算法: 帧差法, 识别画面的变化。为了讲解原理,下面给一个代码示例
功能描述:
- 视频文件的读取:
- 使用 cv2.VideoCapture 打开视频文件。
- 检查视频是否成功打开。
- 帧的读取和预处理:
- 循环读取视频帧,并将其转换为灰度图像。
- 使用一个列表 frames 来存储最近的五个灰度帧。
- 判断画面变化:
- 每次读取新帧时,将其添加到 frames 列表中,并移除列表中最早的一帧,以确保列表长度为 5。
- 当 frames 列表中有五帧时,计算第一个和第五个帧之间的差异。
- 使用阈值处理和膨胀操作填补孔洞,以便更好地检测轮廓,并忽略小的变化区域。
- 查找并绘制检测到的运动区域。
- 显示结果:
- 显示当前帧和帧差图像。
- 每隔 5 帧进行一次判断,因此设置循环间隔为 30 毫秒(可根据需要调整)。
- 释放资源:
- 释放视频捕获对象和关闭所有窗口。
import cv2 def main(): # 打开视频文件 cap = cv2.VideoCapture('your_video_file.mp4') # 替换为你的视频文件路径 # 检查视频是否成功打开 if not cap.isOpened(): print("Error: Could not open video.") return # 用于存储当前帧和前五帧的列表 frames = [] while True: ret, frame = cap.read() # 如果视频读取结束,退出循环 if not ret: break # 转换为灰度图 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 将当前帧添加到帧列表中 frames.append(gray) # 保持帧列表的长度为5 if len(frames) > 5: frames.pop(0) # 计算每隔五帧之间的帧差 if len(frames) == 5: frame_diff = cv2.absdiff(frames[0], frames[4]) # 阈值处理 _, thresh = cv2.threshold(frame_diff, 25, 255, cv2.THRESH_BINARY) # 膨胀操作以填补孔洞 thresh = cv2.dilate(thresh, None, iterations=2) # 查找轮廓 contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 绘制检测到的运动区域 for contour in contours: if cv2.contourArea(contour) < 500: # 忽略小的变化区域 continue (x, y, w, h) = cv2.boundingRect(contour) cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2) # 显示结果 cv2.imshow('Motion Detection', frame) cv2.imshow('Frame Diff', frame_diff) # 每隔5帧进行判断,所以这里以5帧为间隔 if cv2.waitKey(30) & 0xFF == 27: # 按下ESC键退出 break # 释放资源 cap.release() cv2.destroyAllWindows() if __name__ == "__main__": main()
通过上述代码,你可以计算每隔五帧之间的差异,并判断画面是否有变动。你可以根据自己的需要调整阈值和帧间隔,以获得更好的检测效果
步骤2
根据一些特征,过滤掉不是流星的东西, 比如蝙蝠, 小飞虫等。该部分逻辑,不具备通用性,请参考最上方源码查看具体原理。
步骤3
使用ffmpeg 将原始视频切片, 切片后单独存储起来。 以下是示例代码
介绍:FFmpeg 是一个强大的多媒体处理工具,可以轻松实现视频的切割、转换等操作。我们可以使用 subprocess 模块来调用 FFmpeg 命令。
首先你需要确保已经安装 FFmpeg,你可以从 FFmpeg 官网 下载并安装它。
下面是一个 Python 函数示例,使用 FFmpeg 来切割视频文件并输出多个视频文件。
功能描述:
- 获取视频的帧率 (fps):
- 使用 ffprobe 命令来获取输入视频的帧率。这个步骤很重要,因为你需要用帧率来将起始帧转化为时间秒数。
- 切割视频文件:
- 循环遍历 start_frames_list,对于每个起始帧,计算其对应的时间秒数。
- 使用 ffmpeg 命令切割视频。-ss 参数指定起始时间,-t 参数指定切割时长,-c copy 参数表示直接拷贝视频流而不进行重新编码,从而加速处理。
- 调用示例:
- video_path 指定输入视频文件。
- output_directory 指定输出视频片段存放的目录。
- start_frames_list 给定起始帧列表,假设我们想从第0帧、第150帧、第300帧开始切割视频。
- duration_in_seconds 指定每段视频的时长(秒)。
import subprocess import os def cut_video(input_file, output_dir, start_frames, duration): if not os.path.exists(output_dir): os.makedirs(output_dir) # 获取视频的帧率 (fps) cmd = [ "ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=r_frame_rate", "-of", "default=noprint_wrappers=1", input_file ] result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) fps_str = result.stdout.strip().split('=')[1] fps = eval(fps_str) # 切割视频 for i, start_frame in enumerate(start_frames): start_time = start_frame / fps # 计算起始时间(秒) output_file = os.path.join(output_dir, f'output_{i}.mp4') cmd = [ "ffmpeg", "-i", input_file, "-ss", str(start_time), "-t", str(duration), "-c", "copy", output_file ] subprocess.run(cmd) # 示例调用 video_path = 'input_video.mp4' output_directory = 'output_videos' start_frames_list = [0, 150, 300] # 示例起始帧,假设我们想从第0帧、第150帧、第300帧开始切割 duration_in_seconds = 10 # 每段视频的时长为10秒 cut_video(video_path, output_directory, start_frames_list, duration_in_seconds)
总结
本文介绍了如何使用 Python 和 FFmpeg 来替代 ufocapturehd2 进行流星监控录像,并针对其高性能消耗和高价格等缺点提供了优化方案。推荐在 Windows 系统上进行部署,使用索尼 IMX291 或 IMX485 摄像头以获得高质量画面。根据硬件环境的不同,选择适合的编码器以达到性能最佳:如 Intel 核心显卡的 h264_qsv,NVIDIA 显卡的 h264_nvenc 和 AMD 显卡的 h264_amf。在 Ubuntu 和树莓派环境下,编码配置更为复杂,需要特别注意显卡驱动的安装。本方案强调离线分析以适配低性能硬件,通过逐步调整 FPS 设置确保录像的流畅性,确保在低成本的硬件环境下依然能够有效地进行高质量的流星监控录像