演示地址:
https://replit.com/@PaoloAmoroso/spacestills
這是一個(gè)具有GUI的簡單系統(tǒng),它訪問feed流并從Web下載數(shù)據(jù)。該程序僅需350行代碼,并依賴于一些開源的Python庫。
關(guān)于程序
Spacestills會(huì)定期從feed流中下載NASA TV靜止幀并將其顯示在GUI中。
該程序可以校正幀的縱橫比,并將其保存為PNG格式。它會(huì)自動(dòng)下載最新的幀,并提供手動(dòng)重新加載,禁用自動(dòng)重新加載或更改下載頻率的選項(xiàng)。
Spacestillsis是一個(gè)比較初級的版本,但是它可以做一些有用的事情:捕獲并保存NASA TV直播的太空事件圖像。太空愛好者經(jīng)常在社交網(wǎng)絡(luò)或論壇共享他們從NASA TV手動(dòng)獲取的屏幕截圖。Spacestills節(jié)省了使用屏幕捕獲工具的時(shí)間,并保存了可供共享的圖像文件。您可以在Replit上在線運(yùn)行Spacestills。
開發(fā)環(huán)境
筆者用Replit開發(fā)了Spacestills。Replit是云上的開發(fā),部署和協(xié)作環(huán)境,它支持包括Python在內(nèi)的數(shù)十種編程語言和框架。作為Chrome操作系統(tǒng)和云計(jì)算愛好者,筆者非常喜歡Replit,因?yàn)樗梢栽跒g覽器中完全正常運(yùn)行,無需下載或安裝任何內(nèi)容。
資源和依賴包
Spacestills依賴于一些外部資源和Python庫。
NASA TV feed 流
肯尼迪航天中心的網(wǎng)站上有一個(gè)頁面,其中包含精選的NASA視頻流,包括NASA電視公共頻道。feed流顯示最新的靜止幀并自動(dòng)更新。
每個(gè)feed都帶有三種尺寸的幀,Spacestills依賴于具有704x408像素幀的最大NASA TV feed流。最大更新頻率為每45秒一次。因此,檢索最新的靜止幀就像從feed流的URL下載JPEG圖像一樣簡單。
原始圖像被垂直拉伸,看起來很奇怪。因此,該程序可以通過壓縮圖像并生成未失真的16:9版本來校正縱橫比。
Python
因PySimpleGUI的原因需要安裝 Python 3.6 版本。
第三方庫
- Pillow:圖像處理
- PySimpleGUI:GUI框架(Spacestills使用Tkinter后端)
- Request:HTTP請求
完整代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
|
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 sg FEED_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 = 300 class StillFrame(): """Holds a still frame. The image is stored as a PNG PIL.Image and kept in PNG format. Attributes ---------- image : PIL.Image A still frame original : PIL.Image Original frame with wchich the instance is initialized, cached in case of resizing to the original size Methods ------- bytes : Return the raw bytes resize : Resize the screenshot new_size : Calculate new aspect ratio """ def __init__( self , image): """Convert the image to PNG and cache the converted original. Parameters ---------- image : PIL.Image Image to store """ self .image = image self ._topng() self .original = self .image def _topng( self ): """Convert image format of frame to PNG. Returns ------- StillFrame Frame 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_image return self def bytes( self ): """Return raw bytes of a frame image. Returns ------- bytes Byte 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-tuple New size """ size = self .image.size original_size = self .original.size new_size = (WIDTH, HEIGHT_16_9) if size = = original_size else (WIDTH, HEIGHT) return new_size def resize( self , new_size): """Resize frame image. Parameters ---------- new_size : 2-tuple New size Returns ------- StillFrame Frame with image resized """ if not ( self .image.size = = new_size): self .image = self .image.resize(new_size) return self def make_blank_image(size = (WIDTH, HEIGHT)): """Create a blank image with a blue background. Parameters ---------- size : 2-tuple Image size Returns ------- PIL.Image Blank image """ image = Image.new( 'RGB' , size = size, color = 'blue' ) return image def download_image(url): """Download current NASA TV image. Parameters ---------- url : str URL to download the image from Returns ------- PIL.Image Downloaded 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 image def refresh(window, resize = False , feed = FEED_URL): """Display the latest still frame in window. Parameters ---------- window : sg.Window Window to display the still to feed : string Feed URL Returns ------- StillFrame Refreshed 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 still def change_aspect_ratio(window, still, new_size = (WIDTH, HEIGHT_16_9)): """Change the aspect ratio of the still displayed in window. Parameters ---------- window : sg.Window Window containing the still new_size : 2-tuple New size of the still Returns ------- StillFrame Frame containing the resized image """ resized_still = still.resize(new_size) window[ '-IMAGE-' ].update(data = resized_still.bytes()) return resized_still def save(still, path): """Save still to a file. Parameters ---------- still : StillFrame Still to save path : string File name Returns ------- Boolean True if file saved with no errors """ filename = Path(path) try : with open (filename, 'wb' ) as file : file .write(still.bytes()) saved = True except OSError: saved = False return saved def next_timeout(delta): """Return the moment in time right now + delta seconds from now. Parameters ---------- delta : int Time in seconds until the next timeout Returns ------- datetime.datetime Moment 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.datetime Returns ------- bool True if the next timeout is due """ rightnow = datetime.now() return rightnow > = next_timeout def validate_delta(value): """Check if value is an int within the proper range for a time delta. Parameters ---------- value : int Time in seconds until the next timeout Returns ------- int Time in seconds until the next timeout bool True if the argument is a valid time delta """ isinteger = False try : isinteger = type ( int (value)) is int except Exception: delta = DELTA delta = int (value) if isinteger else delta isvalid = MIN_DELTA < = delta < = MAX_DELTA delta = delta if isvalid else DELTA return delta, isinteger and isvalid LAYOUT = [[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 = DELTA next_reload_time = datetime.now() + timedelta(seconds = delta) while True : event, values = window.read(timeout = 100 ) if event in (sg.WIN_CLOSED, 'Exit' ): break elif ((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 window if __name__ = = '__main__' : main(LAYOUT) |
以上就是用 Python 監(jiān)控 NASA TV 直播畫面的實(shí)現(xiàn)步驟的詳細(xì)內(nèi)容,更多關(guān)于Python 監(jiān)控 NASA TV 直播畫面的資料請關(guān)注服務(wù)器之家其它相關(guān)文章!
原文鏈接:https://mp.weixin.qq.com/s/E6zS1tMHEq_cA5rELh1F0A