(资料图片)
演示地址:
https://replit.com/@PaoloAmoroso/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请求完整代码
fromioimportBytesIOfromdatetimeimportdatetime,timedeltafrompathlibimportPathimportrequestsfromrequests.exceptionsimportTimeoutfromPILimportImageimportPySimpleGUIassgFEED_URL="https://science.ksc.nasa.gov/shuttle/countdown/video/chan2large.jpg"#Framesizewithoutandwith16:9aspectratiocorrectionWIDTH=704HEIGHT=480HEIGHT_16_9=396#Minimum,default,andmaximumautoreloadintervalinsecondsMIN_DELTA=45DELTA=MIN_DELTAMAX_DELTA=300classStillFrame():"""Holdsastillframe.TheimageisstoredasaPNGPIL.ImageandkeptinPNGformat.Attributes----------image:PIL.ImageAstillframeoriginal:PIL.ImageOriginalframewithwchichtheinstanceisinitialized,cachedincaseofresizingtotheoriginalsizeMethods-------bytes:Returntherawbytesresize:Resizethescreenshotnew_size:Calculatenewaspectratio"""def__init__(self,image):"""ConverttheimagetoPNGandcachetheconvertedoriginal.Parameters----------image:PIL.ImageImagetostore"""self.image=imageself._topng()self.original=self.imagedef_topng(self):"""ConvertimageformatofframetoPNG.Returns-------StillFrameFramewithimageinPNGformat"""ifnotself.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_imagereturnselfdefbytes(self):"""Returnrawbytesofaframeimage.Returns-------bytesBytestreamoftheframeimage"""file=BytesIO()self.image.save(file,"png")file.seek(0)returnfile.read()defnew_size(self):"""Returnimagesizetoggledbetweenoriginaland16:9.Returns-------2-tupleNewsize"""size=self.image.sizeoriginal_size=self.original.sizenew_size=(WIDTH,HEIGHT_16_9)ifsize==original_sizeelse(WIDTH,HEIGHT)returnnew_sizedefresize(self,new_size):"""Resizeframeimage.Parameters----------new_size:2-tupleNewsizeReturns-------StillFrameFramewithimageresized"""ifnot(self.image.size==new_size):self.image=self.image.resize(new_size)returnselfdefmake_blank_image(size=(WIDTH,HEIGHT)):"""Createablankimagewithabluebackground.Parameters----------size:2-tupleImagesizeReturns-------PIL.ImageBlankimage"""image=Image.new("RGB",size=size,color="blue")returnimagedefdownload_image(url):"""DownloadcurrentNASATVimage.Parameters----------url:strURLtodownloadtheimagefromReturns-------PIL.ImageDownloadedimageifnoerrors,otherwiseblankimage"""try:response=requests.get(url,timeout=(0.5,0.5))ifresponse.status_code==200:image=Image.open(BytesIO(response.content))else:image=make_blank_image()exceptTimeout:image=make_blank_image()returnimagedefrefresh(window,resize=False,feed=FEED_URL):"""Displaythelateststillframeinwindow.Parameters----------window:sg.WindowWindowtodisplaythestilltofeed:stringFeedURLReturns-------StillFrameRefreshedscreenshot"""still=StillFrame(download_image(feed))ifresize:still=change_aspect_ratio(window,still,new_size=(WIDTH,HEIGHT_16_9))else:window["-IMAGE-"].update(data=still.bytes())returnstilldefchange_aspect_ratio(window,still,new_size=(WIDTH,HEIGHT_16_9)):"""Changetheaspectratioofthestilldisplayedinwindow.Parameters----------window:sg.WindowWindowcontainingthestillnew_size:2-tupleNewsizeofthestillReturns-------StillFrameFramecontainingtheresizedimage"""resized_still=still.resize(new_size)window["-IMAGE-"].update(data=resized_still.bytes())returnresized_stilldefsave(still,path):"""Savestilltoafile.Parameters----------still:StillFrameStilltosavepath:stringFilenameReturns-------BooleanTrueiffilesavedwithnoerrors"""filename=Path(path)try:withopen(filename,"wb")asfile:file.write(still.bytes())saved=TrueexceptOSError:saved=Falsereturnsaveddefnext_timeout(delta):"""Returnthemomentintimerightnow+deltasecondsfromnow.Parameters----------delta:intTimeinsecondsuntilthenexttimeoutReturns-------datetime.datetimeMomentintimeofthenexttimeout"""rightnow=datetime.now()returnrightnow+timedelta(seconds=delta)deftimeout_due(next_timeout):"""ReturnTrueifthenexttimeoutisdue.Parameters----------next_timeout:datetime.datetimeReturns-------boolTrueifthenexttimeoutisdue"""rightnow=datetime.now()returnrightnow>=next_timeoutdefvalidate_delta(value):"""Checkifvalueisanintwithintheproperrangeforatimedelta.Parameters----------value:intTimeinsecondsuntilthenexttimeoutReturns-------intTimeinsecondsuntilthenexttimeoutboolTrueiftheargumentisavalidtimedelta"""isinteger=Falsetry:isinteger=type(int(value))isintexceptException:delta=DELTAdelta=int(value)ifisintegerelsedeltaisvalid=MIN_DELTA<=delta<=MAX_DELTAdelta=deltaifisvalidelseDELTAreturndelta,isintegerandisvalidLAYOUT=[[sg.Image(key="-IMAGE-")],[sg.Checkbox("Correctaspectratio",key="-RESIZE-",enable_events=True),sg.Button("Reload",key="-RELOAD-"),sg.Button("Save",key="-SAVE-"),sg.Exit()],[sg.Checkbox("Auto-reloadevery(seconds):",key="-AUTORELOAD-",default=True),sg.Input(DELTA,key="-DELTA-",size=(3,1),justification="right"),sg.Button("Set",key="-UPDATE_DELTA-")]]defmain(layout):"""Runeventloop."""window=sg.Window("Spacestills",layout,finalize=True)current_still=refresh(window)delta=DELTAnext_reload_time=datetime.now()+timedelta(seconds=delta)whileTrue:event,values=window.read(timeout=100)ifeventin(sg.WIN_CLOSED,"Exit"):breakelif((event=="-RELOAD-")or(values["-AUTORELOAD-"]andtimeout_due(next_reload_time))):current_still=refresh(window,values["-RESIZE-"])ifvalues["-AUTORELOAD-"]:next_reload_time=next_timeout(delta)elifevent=="-RESIZE-":current_still=change_aspect_ratio(window,current_still,current_still.new_size())elifevent=="-SAVE-":filename=sg.popup_get_file("Filename",file_types=[("PNG","*.png")],save_as=True,title="Saveimage",default_extension=".png")iffilename:saved=save(current_still,filename)ifnotsaved:sg.popup_ok("Errorwhilesavingfile:",filename,title="Error")elifevent=="-UPDATE_DELTA-":#Thecurrentcycleshouldcompleteatthealreadyscheduledtime.So#don"tupdatenext_reload_timeyetbecauseit"llbetakencareofatthe#next-AUTORELOAD-or-RELOAD-event.delta,valid=validate_delta(values["-DELTA-"])ifnotvalid:window["-DELTA-"].update(str(DELTA))window.close()delwindowif__name__=="__main__":main(LAYOUT)
以上就是用 Python 监控 NASA TV 直播画面的实现步骤的详细内容,更多关于Python 监控 NASA TV 直播画面的资料请关注其它相关文章!