本文分享一个名为"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 直播画面相关推荐

  1. 熊猫TV直播H5播放器架构探索

    本文来自熊猫TV音视频技术专家姜雨晴在LiveVideoStackCon 2017上的分享,并有LiveVideoStack整理成文.当下,打造一款播放器已经有比较好的开源实现,但熊猫TV为什么还要自 ...

  2. 从“地球漫游计划”看城市,如何利用视频监控让城市直播更简单?

    去年,"地球漫游计划"让不少网友都一起见证了地球上最壮美.最具人类文明意义的"地球文明景观".这一新颖的地球景观介绍方式伴随网络直播一起出现在大众视野,让众多网 ...

  3. 火猫tv直播精灵 v1.1.1 官方版​

    火猫tv直播精灵 v1.1.1 官方版 软件大小:13.7MB 软件语言:简体中文 软件类别:影音其他 软件授权:官方版 更新时间:2015-03-01 应用平台:/Win8/Win7/WinXP 火 ...

  4. Python监控目录文件夹,并使用SFTP上传目录及文件到linux服务器

    Python 扫描监控本地文件夹并进行超大文件上传 方案1:WebUploader大文件分块多线程并发上传 方案2:watchdog目录文件夹监控,paramiko STFP上传服务器 方案3:优化2 ...

  5. python键盘输入代码,python监控键盘输入实例代码

    本文研究的主要是python监控键盘输入的相关代码,用到了os,sys,time等,具体实现代码如下: #!/usr/bin/env python # -*- coding: utf-8 -*- im ...

  6. Python监控目录和文件变化

    原文:https://www.cnblogs.com/lcamry/p/8392376.html Python监控目录和文件变化 一.os.listdir import os, time path_t ...

  7. python课件_讲座直播 | Python在线课堂第二周

    本周二.四两晚,由"i 学堂"与我校经济学院WISER CLUB团队联合推出的Python系列课程将继续在线开讲,内容适合Python初学者. 本周直播内容安排如下: 第三讲 Py ...

  8. python监控进程状态_python监控进程脚本

    本文实例为大家分享了python监控进程脚本的具体代码,供大家参考,具体内容如下 原理: 监控一个指定进程,每隔5秒钟获取其CPU.内存使用量超过60%即kill掉该进程,获取其句柄数,超过300也k ...

  9. python监控windows日志_Python 监控日志的简单示例

    这篇文章主要为大家详细介绍了Python 监控日志的简单示例,具有一定的参考价值,可以用来参考一下. 对python这个高级语言感兴趣的小伙伴,下面一起跟随512笔记的小编两巴掌来看看吧! 一个简易的 ...

最新文章

  1. network-manager
  2. Linux编写一个C程序HelloWorld
  3. 解析CleanMyMac隐私保护内容与使用
  4. C#开发Unity游戏教程循环遍历做出判断及Unity游戏示例
  5. x-mixed-replace - 转自博客园cnblog - Scowl Knight
  6. ThreadLocal 变量和 与线程池配合使用时可能会出现的问题
  7. CSS3学习之 animation 属性
  8. 图像处理中的通信原理——冈萨雷斯读书笔记(二)
  9. python序列类型唯一的映射类型_什么是python中唯一的映射类型
  10. 怎样解决Mac电脑键盘上的大写锁定键灯不亮?
  11. 苹果移动设备(iPhone/iPad)分辨率汇总
  12. Linux 下man 命令的使用
  13. 框架里面的标签采集不到怎么办_怎么做微信生态的全数据采集和打通?
  14. 上交所几大业务平台简介
  15. python解椭圆方程的例题_《椭圆》方程典型例题20例(含标准答案)
  16. 问题解决: ValueError: Can't Handle mix of binary and continuous
  17. JetBrains的注册
  18. logstash实例简单demo
  19. 王者荣耀测试自己本命英雄软件,王者荣耀本命英雄测试网站分享 2021本命英雄测试入口...
  20. 正则表达式re之模块函数和编译标志

热门文章

  1. Excel 拆分 分割 数据 (对数据进行分列)
  2. 高等数学学习笔记——第六十四讲——偏导数
  3. VertiGIS进入下一增长阶段,任命Andy Berry为首席执行官
  4. 智能住宅小区安防报警网络系统
  5. 回家,一朵花开的时间
  6. PostMan接口测试(很全面的接口测试教程)
  7. Linux 启动定时任务配置
  8. mysql exec call_exec和call用法详解
  9. SQL必知必会(一)SQL基础篇
  10. 浪潮存储与虚拟服务器连接失败,浪潮-异构虚拟化存储研究(示例代码)