用 Python 监控 NASA TV 直播画面
本文分享一个名为"Spacestills"的开源程序,它可以用于查看 NASA TV 的直播画面(静止帧)。
演示地址:
https://replit.com/@PaoloAmoroso/spacestills
在Replit上运行的Spacestills主窗口
这是一个具有GUI的简单系统,它访问feed流并从Web下载数据。该程序仅需350行代码,并依赖于一些开源的Python库。
关于程序
Spacestills会定期从feed流中下载NASA TV静止帧并将其显示在GUI中。
该程序可以校正帧的纵横比,并将其保存为PNG格式。它会自动下载最新的帧,并提供手动重新加载,禁用自动重新加载或更改下载频率的选项。
Spacestillsis是一个比较初级的版本,但是它可以做一些有用的事情:捕获并保存NASA TV直播的太空事件图像。太空爱好者经常在社交网络或论坛共享他们从NASA TV手动获取的屏幕截图。Spacestills节省了使用屏幕捕获工具的时间,并保存了可供共享的图像文件。您可以在Replit上在线运行Spacestills。
开发环境
笔者用Replit开发了Spacestills。Replit是云上的开发,部署和协作环境,它支持包括Python在内的数十种编程语言和框架。作为Chrome操作系统和云计算爱好者,笔者非常喜欢Replit,因为它可以在浏览器中完全正常运行,无需下载或安装任何内容。
资源和依赖包
Spacestills依赖于一些外部资源和Python库。
NASA TV feed 流
肯尼迪航天中心的网站上有一个页面,其中包含精选的NASA视频流,包括NASA电视公共频道。feed流显示最新的静止帧并自动更新。
每个feed都带有三种尺寸的帧,Spacestills依赖于具有704x408像素帧的最大NASA TV feed流。最大更新频率为每45秒一次。因此,检索最新的静止帧就像从feed流的URL下载JPEG图像一样简单。
原始图像被垂直拉伸,看起来很奇怪。因此,该程序可以通过压缩图像并生成未失真的16:9版本来校正纵横比。
Python
因PySimpleGUI的原因需要安装 Python 3.6 版本。
第三方库
Pillow:图像处理
PySimpleGUI:GUI框架(Spacestills使用Tkinter后端)
Request:HTTP请求
完整代码
from io import BytesIO
from datetime import datetime, timedelta
from pathlib import Path
import requests
from requests.exceptions import Timeout
from PIL import Image
import PySimpleGUI as sgFEED_URL = 'https://science.ksc.nasa.gov/shuttle/countdown/video/chan2large.jpg'# Frame size without and with 16:9 aspect ratio correction
WIDTH = 704
HEIGHT = 480
HEIGHT_16_9 = 396# Minimum, default, and maximum autoreload interval in seconds
MIN_DELTA = 45
DELTA = MIN_DELTA
MAX_DELTA = 300class StillFrame():"""Holds a still frame.The image is stored as a PNG PIL.Image and kept in PNG format.Attributes----------image : PIL.ImageA still frameoriginal : PIL.ImageOriginal frame with wchich the instance is initialized, cached in case ofresizing to the original sizeMethods-------bytes : Return the raw bytesresize : Resize the screenshotnew_size : Calculate new aspect ratio"""def __init__(self, image):"""Convert the image to PNG and cache the converted original.Parameters----------image : PIL.ImageImage to store"""self.image = imageself._topng()self.original = self.imagedef _topng(self):"""Convert image format of frame to PNG.Returns-------StillFrameFrame with image in PNG format"""if not self.image.format == 'PNG':png_file = BytesIO()self.image.save(png_file, 'png')png_file.seek(0)png_image = Image.open(png_file)self.image = png_imagereturn selfdef bytes(self):"""Return raw bytes of a frame image.Returns-------bytesByte stream of the frame image"""file = BytesIO()self.image.save(file, 'png')file.seek(0)return file.read()def new_size(self):"""Return image size toggled between original and 16:9.Returns-------2-tupleNew size"""size = self.image.sizeoriginal_size = self.original.sizenew_size = (WIDTH, HEIGHT_16_9) if size == original_size else (WIDTH, HEIGHT)return new_sizedef resize(self, new_size):"""Resize frame image.Parameters----------new_size : 2-tupleNew sizeReturns-------StillFrameFrame with image resized"""if not(self.image.size == new_size):self.image = self.image.resize(new_size)return selfdef make_blank_image(size=(WIDTH, HEIGHT)):"""Create a blank image with a blue background.Parameters----------size : 2-tupleImage sizeReturns-------PIL.ImageBlank image"""image = Image.new('RGB', size=size, color='blue')return imagedef download_image(url):"""Download current NASA TV image.Parameters----------url : strURL to download the image fromReturns-------PIL.ImageDownloaded image if no errors, otherwise blank image"""try:response = requests.get(url, timeout=(0.5, 0.5))if response.status_code == 200:image = Image.open(BytesIO(response.content))else:image = make_blank_image()except Timeout:image = make_blank_image()return imagedef refresh(window, resize=False, feed=FEED_URL):"""Display the latest still frame in window.Parameters----------window : sg.WindowWindow to display the still tofeed : stringFeed URLReturns-------StillFrameRefreshed screenshot"""still = StillFrame(download_image(feed))if resize:still = change_aspect_ratio(window, still, new_size=(WIDTH, HEIGHT_16_9))else:window['-IMAGE-'].update(data=still.bytes())return stilldef change_aspect_ratio(window, still, new_size=(WIDTH, HEIGHT_16_9)):"""Change the aspect ratio of the still displayed in window.Parameters----------window : sg.WindowWindow containing the stillnew_size : 2-tupleNew size of the stillReturns-------StillFrameFrame containing the resized image"""resized_still = still.resize(new_size)window['-IMAGE-'].update(data=resized_still.bytes())return resized_stilldef save(still, path):"""Save still to a file.Parameters----------still : StillFrameStill to savepath : stringFile nameReturns-------BooleanTrue if file saved with no errors"""filename = Path(path)try:with open(filename, 'wb') as file:file.write(still.bytes())saved = Trueexcept OSError:saved = Falsereturn saveddef next_timeout(delta):"""Return the moment in time right now + delta seconds from now.Parameters----------delta : intTime in seconds until the next timeoutReturns-------datetime.datetimeMoment in time of the next timeout"""rightnow = datetime.now()return rightnow + timedelta(seconds=delta)def timeout_due(next_timeout):"""Return True if the next timeout is due.Parameters----------next_timeout : datetime.datetimeReturns-------boolTrue if the next timeout is due"""rightnow = datetime.now()return rightnow >= next_timeoutdef validate_delta(value):"""Check if value is an int within the proper range for a time delta.Parameters----------value : intTime in seconds until the next timeoutReturns-------intTime in seconds until the next timeoutboolTrue if the argument is a valid time delta"""isinteger = Falsetry:isinteger = type(int(value)) is intexcept Exception:delta = DELTAdelta = int(value) if isinteger else deltaisvalid = MIN_DELTA <= delta <= MAX_DELTAdelta = delta if isvalid else DELTAreturn delta, isinteger and isvalidLAYOUT = [[sg.Image(key='-IMAGE-')],[sg.Checkbox('Correct aspect ratio', key='-RESIZE-', enable_events=True),sg.Button('Reload', key='-RELOAD-'),sg.Button('Save', key='-SAVE-'),sg.Exit()],[sg.Checkbox('Auto-reload every (seconds):', key='-AUTORELOAD-',default=True),sg.Input(DELTA, key='-DELTA-', size=(3, 1), justification='right'),sg.Button('Set', key='-UPDATE_DELTA-')]]def main(layout):"""Run event loop."""window = sg.Window('Spacestills', layout, finalize=True)current_still = refresh(window)delta = DELTAnext_reload_time = datetime.now() + timedelta(seconds=delta)while True:event, values = window.read(timeout=100)if event in (sg.WIN_CLOSED, 'Exit'):breakelif ((event == '-RELOAD-') or(values['-AUTORELOAD-'] and timeout_due(next_reload_time))):current_still = refresh(window, values['-RESIZE-'])if values['-AUTORELOAD-']:next_reload_time = next_timeout(delta)elif event == '-RESIZE-':current_still = change_aspect_ratio(window, current_still, current_still.new_size())elif event == '-SAVE-':filename = sg.popup_get_file('File name', file_types=[('PNG', '*.png')], save_as=True,title='Save image', default_extension='.png')if filename:saved = save(current_still, filename)if not saved:sg.popup_ok('Error while saving file:', filename, title='Error')elif event == '-UPDATE_DELTA-':# The current cycle should complete at the already scheduled time. So# don't update next_reload_time yet because it'll be taken care of at the# next -AUTORELOAD- or -RELOAD- event.delta, valid = validate_delta(values['-DELTA-'])if not valid:window['-DELTA-'].update(str(DELTA))window.close()del windowif __name__ == '__main__':main(LAYOUT)
更多阅读
用 XGBoost 进行时间序列预测
5分钟掌握 Python 随机爬山算法
5分钟完全读懂关联规则挖掘算法
特别推荐
点击下方阅读原文加入社区会员
用 Python 监控 NASA TV 直播画面相关推荐
- 熊猫TV直播H5播放器架构探索
本文来自熊猫TV音视频技术专家姜雨晴在LiveVideoStackCon 2017上的分享,并有LiveVideoStack整理成文.当下,打造一款播放器已经有比较好的开源实现,但熊猫TV为什么还要自 ...
- 从“地球漫游计划”看城市,如何利用视频监控让城市直播更简单?
去年,"地球漫游计划"让不少网友都一起见证了地球上最壮美.最具人类文明意义的"地球文明景观".这一新颖的地球景观介绍方式伴随网络直播一起出现在大众视野,让众多网 ...
- 火猫tv直播精灵 v1.1.1 官方版
火猫tv直播精灵 v1.1.1 官方版 软件大小:13.7MB 软件语言:简体中文 软件类别:影音其他 软件授权:官方版 更新时间:2015-03-01 应用平台:/Win8/Win7/WinXP 火 ...
- Python监控目录文件夹,并使用SFTP上传目录及文件到linux服务器
Python 扫描监控本地文件夹并进行超大文件上传 方案1:WebUploader大文件分块多线程并发上传 方案2:watchdog目录文件夹监控,paramiko STFP上传服务器 方案3:优化2 ...
- python键盘输入代码,python监控键盘输入实例代码
本文研究的主要是python监控键盘输入的相关代码,用到了os,sys,time等,具体实现代码如下: #!/usr/bin/env python # -*- coding: utf-8 -*- im ...
- Python监控目录和文件变化
原文:https://www.cnblogs.com/lcamry/p/8392376.html Python监控目录和文件变化 一.os.listdir import os, time path_t ...
- python课件_讲座直播 | Python在线课堂第二周
本周二.四两晚,由"i 学堂"与我校经济学院WISER CLUB团队联合推出的Python系列课程将继续在线开讲,内容适合Python初学者. 本周直播内容安排如下: 第三讲 Py ...
- python监控进程状态_python监控进程脚本
本文实例为大家分享了python监控进程脚本的具体代码,供大家参考,具体内容如下 原理: 监控一个指定进程,每隔5秒钟获取其CPU.内存使用量超过60%即kill掉该进程,获取其句柄数,超过300也k ...
- python监控windows日志_Python 监控日志的简单示例
这篇文章主要为大家详细介绍了Python 监控日志的简单示例,具有一定的参考价值,可以用来参考一下. 对python这个高级语言感兴趣的小伙伴,下面一起跟随512笔记的小编两巴掌来看看吧! 一个简易的 ...
最新文章
- network-manager
- Linux编写一个C程序HelloWorld
- 解析CleanMyMac隐私保护内容与使用
- C#开发Unity游戏教程循环遍历做出判断及Unity游戏示例
- x-mixed-replace - 转自博客园cnblog - Scowl Knight
- ThreadLocal 变量和 与线程池配合使用时可能会出现的问题
- CSS3学习之 animation 属性
- 图像处理中的通信原理——冈萨雷斯读书笔记(二)
- python序列类型唯一的映射类型_什么是python中唯一的映射类型
- 怎样解决Mac电脑键盘上的大写锁定键灯不亮?
- 苹果移动设备(iPhone/iPad)分辨率汇总
- Linux 下man 命令的使用
- 框架里面的标签采集不到怎么办_怎么做微信生态的全数据采集和打通?
- 上交所几大业务平台简介
- python解椭圆方程的例题_《椭圆》方程典型例题20例(含标准答案)
- 问题解决: ValueError: Can't Handle mix of binary and continuous
- JetBrains的注册
- logstash实例简单demo
- 王者荣耀测试自己本命英雄软件,王者荣耀本命英雄测试网站分享 2021本命英雄测试入口...
- 正则表达式re之模块函数和编译标志