pogucam 0.1.2__py3-none-any.whl → 0.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2474 @@
1
+ ############################# /// script
2
+ ############################# requires-python = ">=3.12"
3
+ ############################# dependencies = [
4
+ ############################# "numpy",
5
+ ############################# "opencv-python",
6
+ ############################# "pyqt6",
7
+ ############################# "requests",
8
+ ############################# ]
9
+ ############################# ///
10
+ ############################
11
+ """
12
+ * I keep this on legacy .venv *** either --script install - or venv!
13
+ * from astropy.io import fits *** some fits in threading done *** ... ds9.si.edu viewer DS9
14
+ * NOT NOW **** Or store as hdf5 - files with metadata - and use HDFView ... one file
15
+ * view script to open napari inn uv ***** or napari (python) uvx --with pyqt6
16
+ FITS and napari
17
+ ... if I save as fits.writeto('image.fits') and use napari:image_data=hdul[0].data viewer.add_image()
18
+ NEW - Fits saves nice, many CUBE , use like
19
+ lbzip2 for compression
20
+ ../Downloads/DS9/ds9 core6a_10.10.104.17_8000_20250514_104549.26.jpg.fits.bz2
21
+ uv run explore_view_napari.py ~/DATA/core6a_10.10.104.44_8000_20250514_101055.37.jpg.fits.bz2
22
+ napari plugins
23
+ https://github.com/DKFZ-TMTRR/napari-nd-cropper
24
+ use shapes.... to crop!!!!
25
+ uv run --with=napari-crop --with=napari-tabu ./explore_view_napari.py ~/DATA/core6a_video0_20250604_113217.25.fits
26
+ napari-tabu :((((
27
+ DS9 ... ~/Downloads/DS9/ds9 core6a_video0_20250604_185439.68.fits just GRAY,
28
+ wine /home/ojr/.wine/dosdevices/c:/Program\ Files\ \(x86\)/AvisFV30/AvisFV.exe core6a_video0_20250604_185439.68.fits
29
+ #------------
30
+ NEW 1 2 3 4 and shift(save) are registers for rota,zoom,r_integrr,move,l_gamma (r_gain t?in future?)
31
+ CROSS, FG BG....
32
+
33
+ wget http://a:a@localhost:8000/foreground -O foreground.jpg && sudo fbi -T 1 -a foreground.jpg
34
+ ___________
35
+ idea
36
+ *MAYBE* 1. \n \r for terinal
37
+ *DONE* 2. iprint time frame DONE
38
+ *Done local* 3. !! Integrate is remote (struggle with threshold)
39
+ *Done i think* 4. remote gain! expot and gammat!?
40
+ -----
41
+ *
42
+ """
43
+ from PIL import ImageFont, ImageDraw, Image
44
+ import sys
45
+ import numpy as np
46
+ import cv2
47
+ import requests
48
+ from PyQt6.QtWidgets import QApplication, QLabel
49
+ from PyQt6.QtGui import QImage, QPixmap
50
+ from PyQt6.QtCore import Qt, QTimer
51
+ from PyQt6.QtCore import QSize
52
+
53
+
54
+ import socket
55
+ import datetime as dt
56
+ #import socket
57
+ #import cv2
58
+
59
+ import getpass
60
+ import os
61
+ import urllib
62
+ import base64
63
+ import getpass
64
+ import time
65
+
66
+ from flashcam.text_write import iprint, fonts_available, get_f_height, get_f_width, get_def_font, set_def_font
67
+ from console import fg, bg, fx
68
+
69
+ from astropy.io import fits
70
+ import threading
71
+ import json
72
+
73
+ import webbrowser
74
+ from collections import deque # acc
75
+
76
+ import click
77
+ import subprocess as sp
78
+ import re
79
+ import math
80
+
81
+ from astropy import wcs
82
+
83
+ istrip = 0
84
+ UPDATE_INTERVAL = 1
85
+ #FITS_CUBE_N = 400
86
+ #FITS_INTERVAL_SECONDS = 60
87
+
88
+ # ---------- files:
89
+ FILE_USERPASS = "~/.config/flashcam/.flashcam_upw" # will extend
90
+ FILE_REDCROSS0 = "~/.config/flashcam/crossred" # will extend
91
+ FILE_REDCROSS = "~/.config/flashcam/crossred" # will extend
92
+
93
+ # ================================================================================
94
+ # RA "00 12 45.2"
95
+ # --------------------------------------------------------------------------------
96
+ def hms_to_deg(h, m=None, s=None):
97
+ res = None
98
+ if m is None and type(h) == str:
99
+ h1, m1, s1 = h.strip().split(" ")
100
+ res = ( float(h1) + float(m1)/60 + float(s1)/3600) * 15
101
+ else:
102
+ res = (h + m/60 + s/3600) * 15
103
+ return res
104
+
105
+ # ================================================================================
106
+ # DEC " "
107
+ # --------------------------------------------------------------------------------
108
+ def dms_to_deg(d, m=None, s=None):
109
+ res = None
110
+ if m is None and type(d) == str:
111
+ d1, m1, s1 = d.strip().split(" ")
112
+ sign = 1 if float(d1) >= 0 else -1
113
+ res = float(d1) + sign * ( float(m1)/60 + float(s1)/3600)
114
+ else:
115
+ sign = 1 if d >= 0 else -1
116
+ res = d + sign * (m/60 + s/3600)
117
+ return res
118
+
119
+ # ================================================================================
120
+ #
121
+ # --------------------------------------------------------------------------------
122
+
123
+ def get_v4l2_controls( device ):
124
+ if device is not None:
125
+ if not os.path.exists(device):
126
+ print(f"X... SORRY {device} doesn not exist")
127
+ return None
128
+ output = sp.check_output(['v4l2-ctl', '-d', device , '--list-ctrls'], text=True)
129
+ controls = {}
130
+ pattern = re.compile(r'^\s*(\w+)\s+0x[0-9a-f]+ \(.*\)\s+: min=(-?\d+) max=(-?\d+) step=(\d+) default=(-?\d+) value=(-?\d+)', re.MULTILINE)
131
+ for match in pattern.finditer(output):
132
+ name = match.group(1)
133
+ controls[name] = {
134
+ 'min': int(match.group(2)),
135
+ 'max': int(match.group(3)),
136
+ 'step': int(match.group(4)),
137
+ 'default': int(match.group(5)),
138
+ 'value': int(match.group(6)),
139
+ }
140
+ return controls
141
+
142
+
143
+ def is_int(n):
144
+ if str(n).find(".")>=0: return False
145
+ if n is None:return False
146
+ try:
147
+ float_n = float(n) # 0.0
148
+ int_n = int(float_n) #0.0
149
+ except ValueError:
150
+ return False
151
+ else:
152
+ return float_n == int_n
153
+
154
+
155
+ def guess_url( inp ):
156
+ """
157
+ interpret url like http or /dev/
158
+ """
159
+ final = inp
160
+ ip = ""
161
+ port = ""
162
+ path = ""
163
+ if inp is None:
164
+ print("X... no url")
165
+ return None
166
+ if type(inp) is int:
167
+ if int(inp) < 80:
168
+ print("D... maybe video device")
169
+ videostr = f"/dev/video{inp}"
170
+ if os.path.exists( videostr):
171
+ print(f"i... {videostr} exists ")
172
+ return videostr
173
+ else:
174
+ return None
175
+ else:
176
+ print("i... maybe port...")
177
+ if int(inp) > 79 and int(inp) < 64000:
178
+ print("i... I put 127.0.0.1 address ")
179
+ final = f"http://127.0.0.1:{inp}/video"
180
+ return final
181
+ print("X... IDK:")
182
+ return None
183
+
184
+ if (inp.find("http://") == 0) or (inp.find("https://") == 0):
185
+ if (inp.find(":") > 0):
186
+ return final
187
+ else:
188
+ print("X... a problem, no port demanded")
189
+ return None
190
+ elif len(inp) < 7:
191
+ print("X... TOO short ip (can be 0 for viceo0):", inp)
192
+ if is_int(inp):
193
+ print("i... but is is one number, possibly port number", inp)
194
+ if int(inp) > 79 and int(inp) < 64000:
195
+ print("i... I put 127.0.0.1 address ")
196
+ final = f"http://127.0.0.1:{inp}/video"
197
+ return final
198
+ elif is_int(inp):
199
+ print("D... maybe video device")
200
+ videostr = f"/dev/video{inp}"
201
+ if os.path.exists( videostr):
202
+ print(f"i... {videostr} exists ")
203
+ return videostr
204
+ else:
205
+ print(f"i... {videostr} DOES NOT exist !!!!! ")
206
+ return videostr
207
+ else:
208
+ print("X... BAD input", inp)
209
+ elif inp.find(".") < 0:
210
+ print("X... no dots in address ", inp)
211
+ return None
212
+ elif len(inp.split(".")) < 3:
213
+ print("X... not IP4 address ", inp)
214
+ return None
215
+ # ----------------------------------------------
216
+ digit = inp.split(".")
217
+ if is_int( digit[0]) and is_int(digit[1]) and is_int(digit[2]):
218
+ # DIGITS
219
+ if is_int(digit[3]):
220
+ print("X... a bit problem, no port demanded at 3rd digit", digit, " giving 8000")
221
+ ip = inp # back to 4 digits
222
+ port = "8000"
223
+ #return None
224
+ elif digit[3].find(":") <= 0:
225
+ print("X... a bit problem, no port demanded, giving 8000")
226
+ port = "8000"
227
+ #return None
228
+ else:# port ok
229
+ ip = inp.split(":")[0]
230
+ portplus = inp.split(":")[1]
231
+ if portplus.find("/") > 0:
232
+ port = portplus.split("/")[0]
233
+ path = join("/").portplus.split("/")[1:]
234
+ else:
235
+ port = portplus
236
+
237
+ if not is_int(port):
238
+ print("X... port is not a number", port)
239
+ return None
240
+ if path == "":
241
+ print("X... a bit problem, no path. Giving /video")
242
+ path = "video"
243
+
244
+ final = f"http://{ip}:{port}/{path}"
245
+ print(final)
246
+ return final
247
+ else:
248
+ print("X... address is not digits, I am stopping")
249
+ return None
250
+
251
+
252
+
253
+
254
+ def adjust_gamma(image, gamma=1.0):
255
+ """
256
+ local gamma mapped to d shift-d ctrl-d
257
+ """
258
+ # build a lookup table mapping the pixel values [0, 255] to
259
+ # their adjusted gamma values
260
+ invGamma = 1.0 / gamma
261
+ table = np.array(
262
+ [((i / 255.0) ** invGamma) * 255 for i in np.arange(0, 256)]
263
+ ).astype("uint8")
264
+ # apply gamma correction using the lookup table
265
+ return cv2.LUT(image, table)
266
+
267
+
268
+ def rotate_image(image, angle):
269
+ if angle is None: return image
270
+ if abs(angle)<0.1: return image
271
+ image_center = tuple(np.array(image.shape[1::-1]) / 2)
272
+ # print( "rotate", image_center, angle )
273
+ rot_mat = cv2.getRotationMatrix2D(image_center, angle, 1.0)
274
+ #print(rot_mat)
275
+ result = cv2.warpAffine(image, rot_mat, image.shape[1::-1], flags=cv2.INTER_LINEAR)
276
+ #print("rotated by ", angle)
277
+ return result
278
+
279
+
280
+ def crosson( frame, dix, diy, color = "g", box_small = True, box_large = False):
281
+ """
282
+ two types g (just cross, r boxed cross)
283
+ """
284
+ #if color == 'r': crotype = 'line'
285
+ #
286
+ RADIUS=63
287
+ y = int(frame.shape[0]/2)
288
+ x = int(frame.shape[1]/2)
289
+
290
+ ix = x+dix
291
+ iy = y+diy
292
+
293
+ if color=="g":
294
+ lcolor=(0,255,55)
295
+ elif (color=="r"):
296
+ lcolor=(55,0,255) #BGR
297
+ else:
298
+ lcolor=(0,255,55)
299
+
300
+ crscal = 4
301
+ crnx,crny = int(64/crscal),int(48/crscal)
302
+ #if crotype == "box":
303
+ midskip = crnx
304
+ midskipy = crny
305
+ #else:
306
+ # midskip = 7
307
+ # midskipy = 7
308
+
309
+
310
+ i2=cv2.circle( frame, (ix,iy), RADIUS, lcolor, 1)
311
+ i2=cv2.line(i2, (ix-RADIUS+midskip,iy), (ix-midskip,iy), lcolor, thickness=1, lineType=8)
312
+ i2=cv2.line(i2, (ix+RADIUS-midskip,iy), (ix+midskip,iy), lcolor, thickness=1, lineType=8)
313
+
314
+ i2=cv2.line(i2, (ix,iy-RADIUS+midskipy), (ix,iy-midskipy), lcolor, thickness=1, lineType=8)
315
+ i2=cv2.line(i2, (ix,iy+RADIUS-midskipy), (ix,iy+midskipy), lcolor, thickness=1, lineType=8)
316
+
317
+ # mid
318
+ i2=cv2.line(i2, (ix,iy), (ix,iy), lcolor, thickness=1, lineType=8)
319
+
320
+ #if crotype == "box":
321
+ if box_small:
322
+ #corners # position 0.5deg from 11 deg. OK
323
+ crscal = 4 # normal original box
324
+ crscal = 3.2 # normal original box
325
+ crnx,crny = int(64/crscal),int(48/crscal)
326
+
327
+ i2=cv2.line(i2, (ix-crnx,iy-crny), (ix+crnx,iy-crny), lcolor, thickness=1, lineType=8)
328
+ i2=cv2.line(i2, (ix+crnx,iy-crny), (ix+crnx,iy+crny), lcolor, thickness=1, lineType=8)
329
+ i2=cv2.line(i2, (ix+crnx,iy+crny), (ix-crnx,iy+crny), lcolor, thickness=1, lineType=8)
330
+ i2=cv2.line(i2, (ix-crnx,iy+crny), (ix-crnx,iy-crny), lcolor, thickness=1, lineType=8)
331
+
332
+ if box_large:
333
+ #corners # position 0.5deg from 11 deg. OK
334
+ crscal = 1.4 # normal original box
335
+ crnx,crny = int(64/crscal),int(48/crscal)
336
+
337
+ i2=cv2.line(i2, (ix-crnx,iy-crny), (ix+crnx,iy-crny), lcolor, thickness=1 )
338
+ i2=cv2.line(i2, (ix+crnx,iy-crny), (ix+crnx,iy+crny), lcolor, thickness=1 )
339
+ i2=cv2.line(i2, (ix+crnx,iy+crny), (ix-crnx,iy+crny), lcolor, thickness=1 )
340
+ i2=cv2.line(i2, (ix-crnx,iy+crny), (ix-crnx,iy-crny), lcolor, thickness=1 )
341
+
342
+ return frame # CROSSON *********************************************************************
343
+
344
+
345
+
346
+ # ==========================================================================================
347
+ # CLASS
348
+ # ------------------------------------------------------------------------------------------
349
+
350
+
351
+ class AccumBuffer:
352
+ def __init__(self, frame=None, img_dtype=np.uint8):
353
+ self.frame = frame
354
+ self.img = type('img', (), {'dtype': img_dtype})()
355
+ self.accum_buffer_size = 0
356
+ self.accum_buffer = []
357
+ self.accum_count = 0
358
+ self.accum_index = 0
359
+ self.running_sum = None
360
+
361
+ def is_accum_index_at_end(self):
362
+ return self.accum_index >= self.accum_buffer_size - 1
363
+
364
+ def get_current_size(self):
365
+ return self.accum_count
366
+
367
+ def get_max_buffer_size(self):
368
+ return self.accum_buffer_size
369
+
370
+ def clear_buffer(self, some_frame):
371
+ self.accum_buffer = np.zeros((self.accum_buffer_size, *some_frame.shape), dtype=self.img.dtype)
372
+ self.accum_count = 0
373
+ self.accum_index = 0
374
+ self.running_sum = np.zeros(some_frame.shape, dtype=np.float64)
375
+
376
+ def get_frame_shape(self):
377
+ if self.frame is not None:
378
+ return self.frame.shape
379
+ else:
380
+ return None
381
+
382
+ def define_accum_buffer(self, n):
383
+ if self.frame is None:
384
+ return False
385
+ if (n == self.accum_buffer_size) and (len(self.accum_buffer) > 1):
386
+ return True
387
+ self.accum_buffer_size = n
388
+ self.accum_buffer = np.zeros((self.accum_buffer_size, *self.frame.shape), dtype=self.img.dtype)
389
+ self.accum_count = 0
390
+ self.accum_index = 0
391
+ self.running_sum = np.zeros(self.frame.shape, dtype=np.float64)
392
+ return True
393
+
394
+ def add_to_accum_buffer(self, frame):
395
+ if len(self.accum_buffer) < 1:
396
+ return False
397
+ if self.accum_count < self.accum_buffer_size:
398
+ self.running_sum += frame
399
+ self.accum_buffer[self.accum_index] = frame
400
+ self.accum_count += 1
401
+ else:
402
+ oldest_frame = self.accum_buffer[self.accum_index]
403
+ if frame.shape != oldest_frame.shape:
404
+ return False
405
+ self.running_sum += frame.astype(np.float64) - oldest_frame.astype(np.float64)
406
+ self.accum_buffer[self.accum_index] = frame
407
+ self.accum_index = (self.accum_index + 1) % self.accum_buffer_size
408
+ return True
409
+
410
+ def get_mean_accum_buffer(self):
411
+ if self.accum_count == 0:
412
+ return None
413
+ rimg = self.running_sum / self.accum_count
414
+ return rimg.astype(np.uint8)
415
+
416
+ def order_accum_buffer_frames(self):
417
+ if self.accum_count < self.accum_buffer_size:
418
+ frames_ordered = self.accum_buffer[:self.accum_count]
419
+ else:
420
+ frames_ordered = np.concatenate((self.accum_buffer[self.accum_index:], self.accum_buffer[:self.accum_index]))
421
+ for frame in frames_ordered:
422
+ yield frame
423
+
424
+ # ==========================================================================================
425
+ # CLASS
426
+ # ------------------------------------------------------------------------------------------
427
+
428
+
429
+
430
+ # if rotate == 180:
431
+ # frame = cv2.rotate(frame, cv2.ROTATE_180)
432
+ # # frame = cv2.rotate(frame, cv2.ROTATE_90_CLOCKWISE)
433
+ # elif rotate != 0:
434
+ # frame = rotate_image( frame , rotate )
435
+
436
+ # ================================================================
437
+ # Stream widget
438
+ # ----------------------------------------------------------------
439
+ class StreamWidget(QLabel):
440
+
441
+ # ==================================================
442
+ # Called with parameters
443
+ # --------------------------------------------------
444
+ def __init__(self, url, resolution="1920x1080", fourcc="YUYV"):
445
+ super().__init__()
446
+ self.setWindowTitle( url)
447
+ #self.setFixedSize(640, 480)
448
+ self.setMinimumSize(640, 480)
449
+ self.setMaximumSize(QSize(1920, 1080))
450
+ self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
451
+
452
+ # --------------------------------------------- url probnlem
453
+ self.device = None
454
+ self.url = None
455
+ self.post_addr = None
456
+ ######self.internet_not_device = None
457
+
458
+ if url is None :
459
+ print("X... I cannot find a relevant source kind, but program should not be here...")
460
+ sys.exit(1)
461
+ if (url is not None) and is_int(url) and int(url) < 10:
462
+ ##just preparing for the next 2
463
+ #self.url = f"/dev/video{url}"
464
+ ##self.device = f"/dev/video{url}"
465
+ ##self.internet_not_device = False
466
+ ##self.post_addr = None #self.url.replace("/video", "/cross")
467
+ print("X... should enver get here")
468
+ sys.exit(1)
469
+ if (url is not None) and url.find("/dev/video") >= 0:
470
+ self.device = url
471
+ self.url = url#"local"
472
+ self.internet_not_device = False # override
473
+ self.post_addr = None# self.url.replace("/video", "/cross")
474
+ if not os.path.exists( self.device):
475
+ print(f"i... {self.device} DOES NOT exists ")
476
+ elif (url is not None) and url.find("http://") >= 0:
477
+ self.url = url
478
+ self.internet_not_device = True
479
+ self.post_addr = self.url.replace("/video", "/cross")
480
+ else:
481
+ print(f"X... {self.url} x {self.device} ")
482
+ self.resolution = resolution
483
+ self.fourcc = fourcc
484
+ #self.internet_not_device = internet_not_device
485
+
486
+ #
487
+ #if (self.url is None):
488
+ # self.url = "local"
489
+ # self.internet_not_device = False # override
490
+ #if not self.internet_not_device: # no internet...try local
491
+ # self.url = "local" # LABEL IN IMAGES
492
+ #else:
493
+
494
+ # ----------------------------------------------
495
+ self.width = 640
496
+ self.height = 480
497
+
498
+ self.img = np.zeros(( self.height, self.width, 3), dtype=np.uint8)
499
+
500
+ self.timer = QTimer()
501
+ self.timer.timeout.connect(self.fetch_and_update)
502
+ self.timer.start(UPDATE_INTERVAL) # update every 100 ms
503
+ # ---------- operations
504
+ self.stream = None # initially no stream
505
+ self.bytex = b"" # stream
506
+ # ----------------------------------- initial frame -------------- was always STRIPS, now NONE: helsp w VCR ----
507
+ # with big colored strips
508
+ self.frame = None#np.zeros((self.height, self.width, 3), dtype=np.uint8)
509
+ self.which_error = "" # no error, can be url http...
510
+ self.frames_to_fail_max = 5 # countdown to 0 and reset CAP
511
+ self.frames_to_fail = self.frames_to_fail_max # countdown to 0 and reset CAP
512
+ #strip_height = 64
513
+ #for i in range(0, self.height, strip_height * 2):
514
+ # self.frame[i + istrip:i+strip_height+ istrip, :] = (185, 150, 150) # bgr
515
+ # self.frame[i+strip_height+ istrip:i+ istrip+strip_height*2, :] = (5, 5, 5) #
516
+ #-----------------------------------------------------------------------------------
517
+ # ----- this is length of one frame ad hoc
518
+ self.stream_length_init = 1024 * 50 # i had 50k all the time from 1st working versn
519
+ self.stream_length = self.stream_length_init # i had 50k all the time from 1st working versn
520
+ # --- timings
521
+ self.t_posread = dt.datetime.now() # just before have read
522
+ self.t_preread = dt.datetime.now() # just before have read
523
+ self.t_oldread = dt.datetime.now()
524
+ self.t_lasread = dt.datetime.now() # last have read
525
+ self.frame_num = " " * 7
526
+ self.frame_time = " " * 23
527
+ # Local frame numbers ------------
528
+ self.l_frame_num = 0
529
+ self.l_frame_bia = 0 # see bias
530
+ self.l_frame_offs = None # offset to remote
531
+ self.l_frame_time = dt.datetime.now()
532
+ # ------------------------- FLAGS
533
+ self.saving_all = False # every frame to jpg
534
+ self.saving_fits_only = False # but to fits
535
+ self.saving_jpg = True
536
+ self.FITS_INTERVAL_SECONDS = 60
537
+ self.saving_once = False # save one frame jpg
538
+ self.saving_laps = -1 # save no laps
539
+ self.saving_laps_last_save = dt.datetime.now() # save no laps
540
+ self.saving_transformed_image = True # idk - saving QT prepared image
541
+ self.SAVED_NOW = False # for overtext info
542
+ #
543
+ self.xtended = False # 2x local
544
+ self.flag_print = False # terminal \n
545
+ self.flag_print_over = True # overtext
546
+ self.flag_redcross = False
547
+ self.error = False
548
+ self.zoomme = 1
549
+ self.redcross = [0, 0]
550
+ # ---------------------------- REMOTE ------------------------------
551
+ self.r_integrate = 0
552
+ # - - - - - - ----------
553
+ self.r_gain = 0.5 # Remote; I keep info here
554
+ self.r_expo = 0.5 # Remote; I keep info here
555
+ self.r_gamma = 0.0 # Remote; I keep info here remote is 0
556
+ self.r_gaindef = False # Remote; I keep info here
557
+ self.r_expodef = False # Remote; I keep info here
558
+ self.r_gammadef = False # Remote; I keep info here
559
+ self.r_xtend = " " # nothing " "; LC RC CU CD ....
560
+ #-------------
561
+ self.l_gamma = 1 # i dont know, bnut probably ok for local
562
+ self.l_rotate = 0
563
+ # ------------------------- stack
564
+ self.my_img_list = [] # keeps images
565
+ self.my_tim_list = [] # keeps times of images (local)
566
+ self.rgb_image = None # for save
567
+ # ************
568
+ self.setup_dict = {}
569
+ self.setup(action="r", number=1)
570
+ self.setup(action="r", number=2)
571
+ self.setup(action="r", number=3)
572
+ self.setup(action="r", number=4)
573
+ # ------------------------ capture
574
+ self.cap = None
575
+ self.accum_n = 1
576
+ self.accum_buffer = None #deque(maxlen=self.accum_n)
577
+ self.accum_image = None
578
+ self.l_show_accum = False
579
+ # ---- np stak
580
+ self.accum_buffer_size = 0 #
581
+ self.accum_count = 0 # actual number of img in buff
582
+ # -------------------------------------------------
583
+ self.level2_buffer = None
584
+ self.level2_buffer_max_size = 10
585
+
586
+ # --------------------------------- I put it at the end so that it knows all attributes
587
+ self.update_image()
588
+
589
+
590
+
591
+ ###########################################################
592
+ # mmm m m mmmm mmmm mmmmmm mm m mmmm #
593
+ # m" " ## ## # "m #" " # #"m # # "m #
594
+ # # # ## # # # "#mmm #mmmmm # #m # # # #
595
+ # # # "" # # # "# # # # # # # #
596
+ # "mmm" # # #mmm" "mmm#" #mmmmm # ## #mmm" #
597
+ ###########################################################
598
+
599
+ # ================================================================================
600
+ # REQUEST COMMAND SEND
601
+ # --------------------------------------------------------------------------------
602
+ def send_command(self, data =None):
603
+ """
604
+ 1. sends commands via HTTP to remote
605
+ 2. for device, tries to act for some commands
606
+ """
607
+ #print(self.internet_not_device, self.internet_not_device)
608
+ # -------------------------------------------------------------- ACCUM -----------------
609
+ if self.internet_not_device:
610
+ if ('accumtxt' in data) and int(data['accumtxt']) == 1: # skip Loop 1
611
+ print("D... LOOP 1 skipped for remote send command - for some reason I send out 0 only,not 1")
612
+ pass
613
+ else:
614
+ post_response = requests.post(url=self.post_addr, data=data )
615
+ else:
616
+ # --------------------------------------------------------- JUST LOCAL DEVICE ----------------
617
+ print("X... no remote IP defined - nothing sent", data)
618
+ #--------------------------- ------------------------------------ACUUMULATE LOCALY
619
+ if ('accumtxt' in data):
620
+ val = int(data['accumtxt'])
621
+ # how to solve 0? the forgotten problem with 1 allowed here and not 0
622
+ if val == 0: val = 1 # trying to fix
623
+ if val != self.accum_n:
624
+ self.accum_n = val
625
+ #if val == 0: self.accum_n = val # This looks obsolete!; commenting out
626
+ #
627
+ ###self.accum_buffer = deque(maxlen=self.accum_n)
628
+ self.define_accum_buffer( self.accum_n )
629
+
630
+
631
+ #-----------------------------------------------------------------SWITCHRES LOCALY
632
+ if ('switch_res_on' in data) and (self.r_xtend != " ") and self.device.find("/dev/video") >= 0:
633
+ self.resolution="1920x1080" # possible when 1920 on start
634
+ self.fourcc="MJPG"
635
+ width, height = self.parse_resolution(self.resolution)
636
+ fourcc = self.fourcc #
637
+ print(f"----- {width} {height} {fourcc} ")
638
+ self.cap.release()
639
+ time.sleep(1)
640
+ self.controls_dict = get_v4l2_controls(self.device)
641
+ if self.controls_dict is None: self.which_error = "Device not found"
642
+
643
+ self.cap = cv2.VideoCapture(0, cv2.CAP_V4L2)
644
+ fourcc_code = cv2.VideoWriter_fourcc(*fourcc)
645
+ self.cap.set(cv2.CAP_PROP_FOURCC, fourcc_code)
646
+ self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
647
+ self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
648
+
649
+ self.zoome = 1
650
+ self.define_accum_buffer(0 ) # RESET BUFFER
651
+
652
+ #----------------------------------------------------------
653
+
654
+ if ('switch_res_off' in data) and (self.r_xtend == " ") and self.device.find("/dev/video") >= 0:
655
+ self.resolution="640x480"
656
+ self.fourcc="YUYV"
657
+ width, height = self.parse_resolution(self.resolution)
658
+ fourcc = self.fourcc #
659
+ print(f"----- {width} {height} {fourcc}")
660
+ self.cap.release()
661
+ time.sleep(1)
662
+ self.controls_dict = get_v4l2_controls(self.device)
663
+ if self.controls_dict is None: self.which_error = "Device not found"
664
+
665
+ self.cap = cv2.VideoCapture(0, cv2.CAP_V4L2)
666
+ fourcc_code = cv2.VideoWriter_fourcc(*fourcc)
667
+ self.cap.set(cv2.CAP_PROP_FOURCC, fourcc_code)
668
+ self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
669
+ self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
670
+
671
+ self.define_accum_buffer(0 ) # RESET BUFFER
672
+ self.zoome = 1
673
+
674
+ #----------------------------------------------------------
675
+ if ('expotxt' in data) and self.device.find("/dev/video") >= 0:
676
+ if not 'exposure_time_absolute' in self.controls_dict.keys():
677
+ pass
678
+ else:
679
+ target = data['expotxt']
680
+ contr = self.controls_dict["exposure_time_absolute"]
681
+ min_, max_ = contr['min'], contr['max']
682
+ if target < 0:
683
+ self.cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, 3) # manual
684
+ else:
685
+ self.cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, 1) # manual
686
+ rng_ = max_ - min_
687
+ exp_target = target ** 2#(math.exp(target) - 1) / (math.e - 1)
688
+ target = int(rng_ * exp_target + min_)
689
+ print(" EXPO minmax ", min_, max_, target, cv2.CAP_PROP_EXPOSURE)
690
+ self.cap.set(cv2.CAP_PROP_EXPOSURE, target)
691
+
692
+ if ('gaintxt' in data) and self.device.find("/dev/video") >= 0:
693
+ if not 'gain' in self.controls_dict.keys():
694
+ pass
695
+ else:
696
+ target = data['gaintxt']
697
+ contr = self.controls_dict["gain"]
698
+ min_, max_ = contr['min'], contr['max']
699
+ if target < 0:
700
+ target = contr["default"]
701
+ else:
702
+ min_, max_ = contr['min'], contr['max']
703
+ rng_ = max_ - min_
704
+ exp_target = target ** 2#(math.exp(target) - 1) / (math.e - 1)
705
+ target = int(rng_ * exp_target + min_)
706
+ print(" GAIN minmax ",min_, max_, target, cv2.CAP_PROP_GAIN)
707
+ self.cap.set(cv2.CAP_PROP_GAIN, target)
708
+
709
+ if ('gammatxt' in data) and self.device.find("/dev/video") >= 0:
710
+ if not 'gamma' in self.controls_dict.keys():
711
+ pass
712
+ else:
713
+ target = data['gammatxt']
714
+ contr = self.controls_dict["gamma"]
715
+ min_, max_ = contr['min'], contr['max']
716
+ if target < 0:
717
+ target = contr["default"]
718
+ else:
719
+ min_, max_ = contr['min'], contr['max']
720
+ rng_ = max_ - min_
721
+ exp_target = target ** 2#(math.exp(target) - 1) / (math.e - 1)
722
+ target = int(rng_ * exp_target + min_)
723
+ print(" GAMMA minmax ",min_, max_, target, cv2.CAP_PROP_GAMMA)
724
+ self.cap.set(cv2.CAP_PROP_GAMMA, target)
725
+
726
+
727
+
728
+ #############################################
729
+ # mmmm mmmmmm mmmmmmmmmmmmm m m mmmmm #
730
+ # #" " # # # # # # "# #
731
+ # "#mmm #mmmmm #mmmmm # # # #mmm#" #
732
+ # "# # # # # # # #
733
+ # "mmm#" #mmmmm #mmmmm # "mmmm" # #
734
+ #############################################
735
+
736
+ # ================================================================================
737
+ # SETUP SETUP SETUP
738
+ # --------------------------------------------------------------------------------
739
+
740
+ def setup(self, action="i", number=1):
741
+ """
742
+ remember zoom, center, integr locgamma loc rotate
743
+ before creating anything, be sure to load previous
744
+ """
745
+ FILENAME = os.path.expanduser(f'~/.flashcam_ng_uni_dict_{number}.json') # keys 1 2 3 4
746
+ self.post_addr = None
747
+ if self.internet_not_device:
748
+ self.post_addr = self.url.replace("/video", "/cross")
749
+
750
+ if self.url in self.setup_dict:
751
+ pass
752
+ else:
753
+ if len(self.setup_dict) == 0:
754
+ if os.path.exists(FILENAME):
755
+ with open( FILENAME, 'r') as f:
756
+ self.setup_dict = json.load(f)
757
+
758
+ self.setup_dict[self.url] = {}
759
+
760
+ # now i have it for each url
761
+ if action == "q": # quit
762
+ self.zoomme = 1
763
+ self.redcross = [0, 0]
764
+ self.l_gamma = 1
765
+ self.l_rotate = 0
766
+
767
+ self.r_integrate = 0
768
+ self.r_gain = 0.5 # self.setup_dict["gain"]
769
+ self.r_expo = 0.5 # self.setup_dict["gain"]
770
+ self.r_gamma = 0.5 # self.setup_dict["gain"]
771
+
772
+ self.r_gaindef = True # self.setup_dict["gain"]
773
+ self.r_expodef = True # self.setup_dict["gain"]
774
+ self.r_gammadef = True # self.setup_dict["gain"]
775
+ self.r_xtend = " " # no remote extense
776
+
777
+ self.send_command( data={"switch_res_off": "SWITCH_RES_OFF"})
778
+ self.r_xtend = " "
779
+ time.sleep(1.5)
780
+ self.send_command( data= {"accum": "ACCUM", "accumtxt": 0})
781
+ time.sleep(0.5)
782
+ self.send_command( data= {"expot": "EXPOT", "expotxt": -1})
783
+ time.sleep(0.5)
784
+ self.send_command( data= {"gaint": "GAINT", "gaintxt": -1})
785
+ time.sleep(0.5)
786
+ self.send_command( data= {"gammat": "GAMMAT", "gammatxt": -1})
787
+
788
+
789
+ if action == "i" or len(self.setup_dict[self.url]) == 0: # init
790
+ self.setup_dict[self.url]["zoomme"] = self.zoomme
791
+ self.setup_dict[self.url]["redcross"] = self.redcross
792
+ self.setup_dict[self.url]["l_gamma"] = self.l_gamma
793
+ self.setup_dict[self.url]["l_rotate"] = self.l_rotate
794
+ self.setup_dict[self.url]["r_integrate"] = self.r_integrate
795
+
796
+ self.setup_dict[self.url]["r_gain"] = round(self.r_gain, 3)
797
+ self.setup_dict[self.url]["r_expo"] = round(self.r_expo, 3)
798
+ self.setup_dict[self.url]["r_gamma"] = round(self.r_gamma, 3)
799
+
800
+ self.setup_dict[self.url]["r_gaindef"] = self.r_gaindef
801
+ self.setup_dict[self.url]["r_expodef"] = self.r_expodef
802
+ self.setup_dict[self.url]["r_gammadef"] =self.r_gammadef
803
+
804
+ self.setup_dict[self.url]["r_xtend"] =self.r_xtend
805
+
806
+ if action == "a": # apply
807
+ if self.url in self.setup_dict:
808
+ self.zoomme = self.setup_dict[self.url]["zoomme"]
809
+ self.redcross = self.setup_dict[self.url]["redcross"]
810
+ self.l_gamma = self.setup_dict[self.url]["l_gamma"]
811
+ self.l_rotate = self.setup_dict[self.url]["l_rotate"]
812
+ self.r_integrate = self.setup_dict[self.url]["r_integrate"]
813
+
814
+ self.r_gain = self.setup_dict[self.url]["r_gain"]
815
+ self.r_expo = self.setup_dict[self.url]["r_expo"]
816
+ self.r_gamma = self.setup_dict[self.url]["r_gamma"]
817
+
818
+ self.r_gaindef = self.setup_dict[self.url]["r_gaindef"]
819
+ self.r_expodef = self.setup_dict[self.url]["r_expodef"]
820
+ self.r_gammadef = self.setup_dict[self.url]["r_gammadef"]
821
+
822
+ print("D... xtend OLD:", self.r_xtend )
823
+ old_r_xtend = self.r_xtend
824
+ self.r_xtend = self.setup_dict[self.url]["r_xtend"]
825
+
826
+ # ------------------------------------------------------------ EXTENDING FIRST --------------------
827
+ print("D... xtend NEW", self.r_xtend )
828
+ #if (self.r_xtend == " "):
829
+ # print("D... no xtending, pass")
830
+ # pass
831
+ #else:
832
+ if old_r_xtend == self.r_xtend: # same stuff...
833
+ print("D... old == new, i digress")
834
+ pass
835
+ elif self.r_xtend == " ": # easy part - switch off ------------------------------------
836
+ print("D... xtending to ' ' (unzoom) ")
837
+ self.send_command( data={"switch_res_off": "SWITCH_RES_OFF"})
838
+ time.sleep(1.5)
839
+ elif self.r_xtend != " ": # complicated - for sure there will be high resolution ---------
840
+ print("D... target xtend is no ' '" )
841
+ if old_r_xtend != " ": # already at high resolution
842
+ if old_r_xtend[0] != self.r_xtend[0]: # LR
843
+ pass
844
+ elif old_r_xtend[1] != self.r_xtend[1]: #UD
845
+ pass
846
+ else:# not yet at high resolution
847
+ print("D... Starting High Res to 'CC'...i think ")
848
+ print("D... xtending to 'CC' (zoom) ")
849
+ self.send_command( data={"switch_res_on": "SWITCH_RES_ON"})
850
+ old_r_xtend = "CC" # Pretend this !!
851
+ print("D... old_r_xtend redefined", old_r_xtend)
852
+ time.sleep(1.9)
853
+ # ------------------------------- hi res should be ok here ---- but not the quadrant ---------
854
+
855
+ if (self.r_xtend[0] == "L" and old_r_xtend[0] == "C") or (self.r_xtend[0] == "C" and old_r_xtend[0] == "R"):
856
+ print("D... xtending to L- ")
857
+ self.send_command( data={"left": "LEFT"})
858
+ time.sleep(0.7)
859
+ if (self.r_xtend[0] == "C" and old_r_xtend[0] == "L") or (self.r_xtend[0] == "R" and old_r_xtend[0] == "C"):
860
+ print("D... xtending to R- ")
861
+ self.send_command( data={"right": "RIGHT"})
862
+ time.sleep(0.7)
863
+ if (self.r_xtend[1] == "C" and old_r_xtend[1] == "D") or (self.r_xtend[1] == "U" and old_r_xtend[1] == "C"):
864
+ print("D... xtending to -U ")
865
+ self.send_command( data={"up": "UP"})
866
+ time.sleep(0.7)
867
+ if (self.r_xtend[1] == "C" and old_r_xtend[1] == "U") or (self.r_xtend[1] == "D" and old_r_xtend[1] == "C"):
868
+ print("D... xtending to -D ")
869
+ self.send_command( data={"down": "DOWN"})
870
+ time.sleep(0.7)
871
+ else:
872
+ print("D... xtending - other situation, not extending")
873
+
874
+ # ------------------------------------------------------------ Accum second --------------------
875
+
876
+
877
+ print("D... accum ... ")
878
+ self.send_command( data= {"accum": "ACCUM", "accumtxt": int(self.r_integrate)})
879
+ time.sleep(0.5)
880
+
881
+ print("D... expot ... ")
882
+ if self.r_expodef:
883
+ self.send_command( data= {"expot": "EXPOT", "expotxt": -1})
884
+ else:
885
+ self.send_command( data= {"expot": "EXPOT", "expotxt": self.r_expo})
886
+ time.sleep(0.7)
887
+ print("D... gain ... ")
888
+ if self.r_gaindef:
889
+ self.send_command( data= {"gaint": "GAINT", "gaintxt": -1})
890
+ else:
891
+ self.send_command( data= {"gaint": "GAINT", "gaintxt": self.r_gain})
892
+ time.sleep(0.7)
893
+ print("D... gamma ... ")
894
+ if self.r_gammadef:
895
+ self.send_command( data= {"gammat": "GAMMAT", "gammatxt": -1})
896
+ else:
897
+ self.send_command( data= {"gammat": "GAMMAT", "gammatxt": self.r_gamma})
898
+ #-----------------
899
+
900
+ # ------------------------------------------------------------ Timelaps now...------------------
901
+ # !!!!!!!!!!!!!!! udelat timelaps, that would do the same good for local video
902
+
903
+ #----------------------- end of apply----
904
+
905
+ elif action == "w": # write
906
+ print(f"i... writing {FILENAME}")
907
+ with open(FILENAME, 'w') as f:
908
+ json.dump(self.setup_dict, f)
909
+
910
+ elif action == "r":
911
+ if os.path.exists(FILENAME):
912
+ with open( FILENAME, 'r') as f:
913
+ self.setup_dict = json.load(f)
914
+
915
+
916
+
917
+ ###########################################################
918
+ # m m #
919
+ # mmm m m mmm m mm mm#mm mmm m m mm#mm #
920
+ # #" "# "m m" #" # #" " # #" # #m# # #
921
+ # # # #m# #"""" # # #"""" m#m # #
922
+ # "#m#" # "#mm" # "mm "#mm" m" "m "mm #
923
+ ###########################################################
924
+
925
+
926
+ # ================================================================================
927
+ # OVERTEXT
928
+ # --------------------------------------------------------------------------------
929
+
930
+ def overttext(self, blackbar=False):
931
+ """
932
+ great font for small numbers
933
+ """
934
+ #
935
+ #
936
+ #
937
+ if self.frame is None:
938
+ return
939
+ RXT = f"XT:{self.r_xtend}" # CC LU ... for remote ok
940
+ ZOO = f"ZOO:{self.zoomme:3.1f}"
941
+ ROO = f"ROT:{self.l_rotate:3.1f}"
942
+ LGA = f"LGAM:{self.l_gamma:3.1f}"
943
+ SAV = f" "
944
+ if self.saving_all:
945
+ SAV = f"SAVING"
946
+ LAPS = f"LOO:{self.saving_laps:3d}"
947
+ #
948
+ FIT = f" "
949
+ if self.saving_fits_only:
950
+ FIT = f"FITS {self.FITS_INTERVAL_SECONDS:3d}"
951
+
952
+ total_size = 0
953
+ if self.accum_buffer is not None:
954
+ total_size = sys.getsizeof(self.accum_buffer) + sum(sys.getsizeof(arr) for arr in self.accum_buffer)
955
+ BUF = f"BUF={self.accum_count:3d}/{self.accum_n:3d} {total_size/1024/1024:5.0f} MB"
956
+ #
957
+ #
958
+ #
959
+ overtext = f"{self.frame_time} # {self.frame_num} # {self.l_frame_num:6d} # {RXT} . {ZOO} . {ROO} . {LGA} . {SAV} {FIT} . {BUF}. {LAPS}"
960
+ #
961
+ position = ( 0, self.frame.shape[0]-1 ) # 480 on x-axis
962
+ #
963
+ overlay = self.frame.copy()
964
+ if self.resolution == "1920x1080":
965
+ shade_height = 20
966
+ else:
967
+ shade_height = 10
968
+ height, width = self.frame.shape[:2]
969
+ cv2.rectangle(overlay, (0, height - shade_height), (width, height), (0, 0, 0), -1)
970
+ alpha = 0.5
971
+ if blackbar:
972
+ alpha = 0.
973
+ cv2.addWeighted(overlay, alpha, self.frame, 1 - alpha, 0, self.frame)
974
+ #
975
+ font = "di"
976
+ if self.resolution == "1920x1080":
977
+ font = "di2"
978
+ self.img = iprint(self.img, str(overtext), font=font, position=position,color_rgb=(0,255,0) )
979
+ if self.SAVED_NOW:
980
+ position = ( self.frame.shape[1] - 20, self.frame.shape[0]-1 ) # 480 on x-axis
981
+ self.img = iprint(self.img, f"SAVE", font=font, position=position,color_rgb=(0,0,255) ) # bgr
982
+
983
+ #self.img = iprint(self.img, str(overtext), font="p7", position=position,color_rgb=(0,255,0) )
984
+
985
+
986
+
987
+ # ======================================================================
988
+ # make folder give the correct timetag - original if possible
989
+ # ----------------------------------------------------------------------
990
+
991
+ def prepare_save(self, png=False, time_tag=None):
992
+ dir2create = os.path.expanduser("~/DATA/")
993
+ if not os.path.isdir(os.path.expanduser(dir2create)):
994
+ print(f"D... trying to create directory {dir2create} for saving")
995
+ # result = False
996
+ os.mkdir(os.path.expanduser(dir2create))
997
+ now = time_tag
998
+ if time_tag is None:
999
+ now = dt.datetime.now().strftime( '%Y%m%d_%H%M%S_%f')[:-4]
1000
+ else:
1001
+ # 20:23:46.12
1002
+ now = dt.datetime.now().strftime( '%Y%m%d_')+now.replace(":", "")
1003
+ ext = "jpg"
1004
+ if png:ext = "png"
1005
+ host = socket.gethostname()
1006
+ murl = ""
1007
+ if self.url.find("http://") >= 0:
1008
+ murl = self.url.split("http://")[-1]
1009
+ murl = murl.split("/")[0]
1010
+ murl = murl.replace(":", "_")
1011
+ elif self.url.find("/dev/video") >= 0:
1012
+ murl = self.url.split("/dev/")[-1]
1013
+ sfilenamea = os.path.expanduser(f"~/DATA/{host}_{murl}_{now}.{ext}" )
1014
+ return sfilenamea
1015
+
1016
+ # ======================================================================
1017
+ #
1018
+ # ----------------------------------------------------------------------
1019
+
1020
+ def save_fits_in_background(self, data_cube, fname, numero=None):
1021
+ """
1022
+ numero means slice in the cube - cube is no more though. but fits are one-by-one
1023
+ """
1024
+ newname = ""
1025
+ if numero is None:
1026
+ newfname = fname.replace(".jpg", "")
1027
+ newfname = f"{newfname}.fits"
1028
+ else:
1029
+ newfname = fname.replace(".jpg", f"_{numero:05d}")
1030
+ newfname = f"{newfname}.fits"
1031
+ #print(data_cube.shape )#q, 10, 480, 640, 3) # it sees 3 640 8
1032
+ print(f" ... {newfname} ... ")
1033
+ lencube = data_cube.shape[0] # is 3 colors (3, 480, 640)
1034
+ #print(lencube, data_cube.shape )
1035
+ width = data_cube.shape[2]
1036
+ height = data_cube.shape[1]
1037
+ def save():
1038
+
1039
+ # J2000 for KSTARS
1040
+ RA = "05 41 42.57"
1041
+ DEC = "-01 51 22.6"
1042
+ ra_deg = hms_to_deg(RA)
1043
+ dec_deg = dms_to_deg(DEC)
1044
+ #print("RA J2000 in degrees:", ra_deg)
1045
+ #print("DEC J2000 in degrees:", dec_deg)
1046
+ SCALE = 10 # size of the field
1047
+ w = wcs.WCS(naxis=2)
1048
+ w.wcs.crpix = [ int(width / 2), int(height / 2) ]
1049
+ #w.wcs.cdelt = np.array([-SCALE/3600, SCALE/3600]) # Originally but kstars reverted ///// it is always reverted 180
1050
+ w.wcs.cdelt = np.array([-SCALE/3600, -SCALE/3600]) # Perfect with the sky in kstars => but image is reverted 180 !!!
1051
+ w.wcs.crval = [ra_deg, dec_deg]
1052
+ w.wcs.ctype = ["RA---TAN", "DEC--TAN"]
1053
+ header = w.to_header()
1054
+
1055
+ #data_cuber = np.array([np.rot90(image, k=-1) for image in data_cube])
1056
+ #data_cuber = np.array([np.flipud(np.transpose(image)) for image in data_cube])
1057
+ hdu = fits.PrimaryHDU(data_cube, header=header)
1058
+ # a bit different - like in crfits
1059
+ hdul = fits.HDUList([hdu])
1060
+ hdr = hdul[0].header
1061
+
1062
+ #hdr['SIMPLE'] = True # -bit floating point
1063
+ #hdr['BITPIX'] = 8 # 480#8 # -bit floating point
1064
+ #hdr['NAXIS'] = 4 # Number of axes
1065
+ #hdr['EXTEND'] = True # Number of axes
1066
+ #hdr['NAXIS1'] = data_cube.shape[2] # Size of the first axis (width)
1067
+ #hdr['NAXIS2'] = data_cube.shape[1] # Size of the second axis (height)
1068
+ #hdr['NAXIS3'] = data_cube.shape[3] # Size of the third axis (number of images)
1069
+ #hdr['NAXIS4'] = lencube #3 # Size of the third axis (number of images)
1070
+ hdr['FOCALLEN'] = (300.0, 'Focal length in mm (works)')
1071
+ hdr['OBJECT'] = (' TestObj NGC2024', "Target Description")
1072
+ #hdr['DATA-TYP'] = ('OBJECT ', " Characteristics of this data")
1073
+ hdr['OBJCTRA'] = (RA, 'Right Ascension in hms (wrks siril)')
1074
+ hdr['OBJCTDEC'] = (DEC, 'Declination in dms (wrks siril)')
1075
+ #hdr['RA'] = (RA, 'Right Ascension in hms')
1076
+ #hdr['DEC'] = (DEC, 'Declination in dms')
1077
+ # ----- this and probably SCALE make arcsec / pixel in Siril
1078
+ # --------- BUT ALSO FORCES TO SHOW arcsec for FWHM /
1079
+ # ------------- if commented: FWHM in pixels is shown
1080
+ hdr["XPIXSZ"] = (2.9, "IMX291 pixel size in microns ( wrks)")
1081
+ hdr["YPIXSZ"] = (2.9, "IMX291 pixel size in microns ( wrks)")
1082
+ hdr["IMAGEW"] = (width, "Image width, in pixels.")
1083
+ hdr["IMAGEH"] = (height, "Image height, in pixels.")
1084
+ # Add current time to header
1085
+ current_time = dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
1086
+ hdu.header['DATE'] = current_time
1087
+ hdu.header['DATEEXA'] = current_time
1088
+ hdr['HOST'] = (socket.gethostname(), 'host computer')
1089
+ hdr['SENSOR'] = ('imx291', 'sensor used')
1090
+ #hdu.header['CREATOR'] = 'Your Name'
1091
+ #hdu.header['DATE'] = '2025-05-12'
1092
+
1093
+ hdul.writeto(newfname, overwrite=True) # .gz is too expensive too
1094
+ #print(f" x {fg.red}FITS SAVED{fg.default} {newfname}", end=" ")
1095
+ # -------------------------------------------------
1096
+ thread = threading.Thread(target=save)
1097
+ thread.start()
1098
+
1099
+
1100
+ ########################################################## #
1101
+ # " #
1102
+ # mmm mmm m m mmm mmm mmmmm mmmm #
1103
+ # # " " # "m m" #" # # # # # #" "# #
1104
+ # """m m"""# #m# #"""" # # # # # # #
1105
+ # "mmm" "mm"# # "#mm" mm#mm # # # "#m"# #
1106
+ # m # #
1107
+ # """""" "" #
1108
+ ########################################################## #
1109
+
1110
+ # ===========================================================
1111
+ #
1112
+ # -----------------------------------------------------------
1113
+ def save_img(self, fname=None, time_tag=None, silent=True, dumpbuffer=False, use_fits=False, use_buffer=None):
1114
+ """
1115
+ saving self.img
1116
+
1117
+ FITS: save all buffer members / save one as FITS (like no compression...metadata...)
1118
+ dumpbuffer:
1119
+ use_buffer is None OR THE BUFFER TO USE (level2)
1120
+ """
1121
+ fname1 = self.prepare_save( time_tag=time_tag)
1122
+ if not silent: print(fname1)
1123
+ #---------- FITS *****
1124
+ if use_fits: # --------- FITS CASE --- more complex -------------------------------
1125
+ if dumpbuffer and (not self.l_show_accum): # ------ Do Just evry frame in the buffer
1126
+ print(" xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ")
1127
+ #mylst = []
1128
+ n = 1
1129
+ for i in self.order_accum_buffer_frames(): # ITER WORKS BUT NO GREEN LABELS
1130
+ i = np.moveaxis( i, [0, 1, 2], [1, 2, 0])
1131
+ i = np.rot90(i, 2)
1132
+ #mylst.append(i)
1133
+ #data_cube3 = np.stack(mylst, axis=0)
1134
+ self.save_fits_in_background(i, fname1, numero=n)
1135
+ n += 1
1136
+ self.SAVED_NOW = True
1137
+ elif dumpbuffer and (self.l_show_accum) and (use_buffer is not None): # I WILL NOT USE THIS ***********
1138
+ print(" xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ")
1139
+ #print("D... some way to save only one AVG image per Nbuf: TO DO!") # TODO
1140
+ #mylst = []
1141
+ n = 1
1142
+ for i in use_buffer:#
1143
+ i = np.moveaxis( i, [0, 1, 2], [1, 2, 0]) # 210=480 640 3; 120== 640 480 3 otoc
1144
+ i = np.rot90(i, 2)
1145
+ #i = i[..., [2, 1, 0]]
1146
+ #mylst.append(i)
1147
+ #data_cube3 = np.stack(mylst, axis=0)
1148
+ self.save_fits_in_background(i, fname1, numero=n)
1149
+ n += 1
1150
+ self.SAVED_NOW = True # BUFFER OF BUFFERS !!!!!!! TODO
1151
+ pass
1152
+ elif (not dumpbuffer) and (not self.l_show_accum):
1153
+ print("... just one foto in FITS, why not") # trivial ------- NOBUFFER
1154
+ mylast = self.accum_buffer[self.accum_index]
1155
+ i = np.moveaxis( mylast, [0, 1, 2], [1, 2, 0])
1156
+ i = np.rot90(i, 2)
1157
+ self.save_fits_in_background(i, fname1)
1158
+ self.SAVED_NOW = True
1159
+ pass
1160
+ elif (not dumpbuffer) and (self.l_show_accum):
1161
+ print(" ... provide average, 1 image in FITS") # trivial ------- NOBUFFER
1162
+ mymean = self.get_mean_accum_buffer()
1163
+ i = np.moveaxis( mymean, [0, 1, 2], [1, 2, 0])
1164
+ i = np.rot90(i, 2)
1165
+ self.save_fits_in_background(i, fname1)
1166
+ self.SAVED_NOW = True
1167
+ pass
1168
+ # ------- JPG **** **** **** **** *** *** * * * *
1169
+ else:# --------NOT FITS
1170
+ if (dumpbuffer) and (not self.l_show_accum):
1171
+ # dump buffer - straight
1172
+ mycnt = 0
1173
+ for i in self.order_accum_buffer_frames(): # ITER WORKS BUT NO GREEN LABELS
1174
+ mycnt += 1
1175
+ fff = fname1.replace(".jpg", f"_{mycnt:03d}.jpg")
1176
+ print(fff)
1177
+ if self.saving_jpg:
1178
+ cv2.imwrite(fff, i, [int(cv2.IMWRITE_JPEG_QUALITY), 90])
1179
+ else:
1180
+ cv2.imwrite(fff.replace("jpg", "png"), i )
1181
+ self.SAVED_NOW = True
1182
+ pass
1183
+ # -----------------------------------------------
1184
+ elif (not dumpbuffer) and (not self.l_show_accum):
1185
+ # This is a simple save
1186
+ if self.saving_jpg:
1187
+ cv2.imwrite(fname1, self.img, [int(cv2.IMWRITE_JPEG_QUALITY), 90])
1188
+ else:
1189
+ cv2.imwrite(fname1.replace("jpg", "png"), self.img )
1190
+ self.SAVED_NOW = True
1191
+ pass
1192
+ elif (dumpbuffer) and (self.l_show_accum):
1193
+ print("D... the trickies - save every Nth-every new buffer - IDK ")
1194
+ self.SAVED_NOW = True
1195
+ pass
1196
+ elif (not dumpbuffer) and (self.l_show_accum):
1197
+ mymean = self.get_mean_accum_buffer()
1198
+ if self.saving_jpg:
1199
+ cv2.imwrite(fname1, mymean, [int(cv2.IMWRITE_JPEG_QUALITY), 90])
1200
+ else:
1201
+ cv2.imwrite(fname1.replace("jpg", "png"), mymean )
1202
+ self.SAVED_NOW = True
1203
+ pass
1204
+
1205
+
1206
+ # ======================================================================
1207
+ #
1208
+ # ----------------------------------------------------------------------
1209
+
1210
+ def get_passfile(self, videodev ):
1211
+ """
1212
+ only initial part of passfile.... useful for redcross
1213
+ """
1214
+ if "passfile" in locals():
1215
+ if passfile is None:
1216
+ print(f"i... nO passfile...trying {videodev} , {passfile}")
1217
+ passfile = videodev.strip("http://")
1218
+ print("i... TRYING", passfile)
1219
+ else:
1220
+ passfile = videodev.strip("http://")
1221
+ passfile = passfile.strip("/video")
1222
+ passfile = passfile.split(":")[0]
1223
+
1224
+ ### WITH A HACK - I REDEFINE REDCROSS too
1225
+ ###FILE_REDCROSS = f"{FILE_REDCROSS0}_{passfile}.txt"
1226
+
1227
+ # maybe not needed here
1228
+ #FILE_REDCROSS = get_FILE_REDCROSS(passfile)
1229
+
1230
+ return passfile
1231
+
1232
+
1233
+ # ==================================================
1234
+ #
1235
+ # --------------------------------------------------
1236
+
1237
+ # def make_strips(self):
1238
+ # """
1239
+ # creates self.img and strips, increments istrip and resets it
1240
+ # """
1241
+ # global istrip
1242
+ # width, height = 640, 480
1243
+ # self.img = np.zeros((height, width, 3), dtype=np.uint8)
1244
+ # strip_height = 64
1245
+ # istrip += 1
1246
+ # if istrip > strip_height * 2:
1247
+ # istrip = 0
1248
+
1249
+ # if int(istrip / strip_height) % 2== 0:
1250
+ # self.img[0 :0+min(istrip, strip_height), :] = (55, 55, 0) # Red strip (BGR)
1251
+ # else:
1252
+ # self.img[0 :0+min(istrip, strip_height), :] = (55, 150, 0) # Red strip (BGR)
1253
+ # self.img[min(istrip, strip_height):2 * strip_height, :] = (55, 55, 0) # Red strip (BGR)
1254
+
1255
+ # # Draw red and green horizontal strips
1256
+ # for i in range(0, height, strip_height * 2):
1257
+ # self.img[i + istrip:i+strip_height+ istrip, :] = (55, 150, 0) # Red strip (BGR)
1258
+ # self.img[i+strip_height+ istrip:i+ istrip+strip_height*2, :] = (55, 55, 0) # Green strip
1259
+
1260
+
1261
+ # ================================================================================
1262
+ # FINAL TRANSFORM
1263
+ # --------------------------------------------------------------------------------
1264
+
1265
+ def final_transformations(self, myimage ): # self.rgb_image or self.img
1266
+ """
1267
+ all independent on self.
1268
+ """
1269
+ if self.l_rotate != 0:
1270
+ myimage = rotate_image(myimage, self.l_rotate)
1271
+
1272
+ if self.l_gamma != 1:
1273
+ myimage = adjust_gamma(myimage, self.l_gamma)
1274
+
1275
+ if self.zoomme > 1:
1276
+ if len(myimage.shape)==3:
1277
+ height, width, channels = myimage.shape
1278
+ else:
1279
+ height, width = myimage.shape
1280
+ #prepare the crop
1281
+ centerX = int(height/2)
1282
+ centerY = int(width/2)
1283
+
1284
+ if (self.redcross[0]!=0) or (self.redcross[1]!=0):
1285
+ dwidth = -self.redcross[0] #negative up
1286
+ dheight = -self.redcross[1] # positive down
1287
+ T = np.float32([[1, 0, dwidth], [0, 1, dheight]])
1288
+ myimage = cv2.warpAffine(myimage, T, (myimage.shape[1], myimage.shape[0]))
1289
+
1290
+ radiusX,radiusY= int(height/2/self.zoomme),int(width/2/self.zoomme)
1291
+ minX,maxX=centerX-radiusX,centerX+radiusX
1292
+ minY,maxY=centerY-radiusY,centerY+radiusY
1293
+ cropped = myimage[minX:maxX, minY:maxY]
1294
+ myimage = cv2.resize(cropped, (width, height), interpolation=cv2.INTER_NEAREST)
1295
+ return myimage
1296
+
1297
+
1298
+
1299
+ ################################################################################
1300
+ # # m " #
1301
+ # m m mmmm mmm# mmm mm#mm mmm mmm mmmmm mmm mmmm #
1302
+ # # # #" "# #" "# " # # #" # # # # # " # #" "# #
1303
+ # # # # # # # m"""# # #"""" # # # # m"""# # # #
1304
+ # "mm"# ##m#" "#m## "mm"# "mm "#mm" mm#mm # # # "mm"# "#m"# #
1305
+ # # m # #
1306
+ # " """""" "" #
1307
+ ################################################################################
1308
+
1309
+
1310
+ # ==================================================
1311
+ # UPDATE IMAGE **************************************************** KEY ACTION TO QT
1312
+ # --------------------------------------------------
1313
+
1314
+ def update_image(self):
1315
+ """
1316
+ Stream Widget!!!!! self==stream technically takes img and put pixmap
1317
+ """
1318
+
1319
+ if self.rgb_image is None:return # 1st contact may no exist
1320
+
1321
+
1322
+ #self.rgb_image.shape
1323
+
1324
+ h, w, ch = self.rgb_image.shape
1325
+
1326
+ maxw = 1280
1327
+ if w > maxw:#1280:#640:
1328
+ rati = w / maxw
1329
+ w = maxw
1330
+ h = int(h / rati)
1331
+ self.rgb_image = cv2.resize(self.rgb_image, (w, h ), interpolation=cv2.INTER_NEAREST)
1332
+
1333
+ bytes_per_line = ch * w
1334
+ qt_image = QImage(self.rgb_image.data, w, h, bytes_per_line, QImage.Format.Format_RGB888)
1335
+
1336
+ # -- I can extrend the display window
1337
+ if self.xtended:
1338
+ qt_image = qt_image.scaled(2 * w, 2 * h)
1339
+ self.resize(2 * w, 2 * h)
1340
+ else:
1341
+ self.resize( w, h)
1342
+ self.setPixmap(QPixmap.fromImage(qt_image))
1343
+
1344
+
1345
+ # ==================================================
1346
+ #
1347
+ # --------------------------------------------------
1348
+
1349
+ def get_stream(self, videodev):
1350
+ """
1351
+ I start with password and continue with REQ
1352
+ """
1353
+ u, p = getpass.getuser(), "a"
1354
+ passfile = self.get_passfile( videodev )
1355
+ passfile = f"{FILE_USERPASS}_{passfile}"
1356
+ nopass = True
1357
+ try:
1358
+ with open(os.path.expanduser(passfile)) as f:
1359
+ #print("i... PASSWORD FILE ", passfile)
1360
+ w1 = f.readlines()
1361
+ u = w1[0].strip()
1362
+ p = w1[1].strip()
1363
+ nopass = False
1364
+ except:
1365
+ print("X... NO PASSWORD FILE (gs) ", os.path.expanduser(passfile))
1366
+ nopass = True
1367
+ if nopass:
1368
+ print("X.... user pass not found ...... can be a bit problem .... ")
1369
+ #sys.exit(1)
1370
+
1371
+
1372
+ # ----------------------------------------------------------
1373
+ request = urllib.request.Request(videodev)
1374
+ #print("D... USER/PASS", u, p)
1375
+ base64string = base64.b64encode(bytes("%s:%s" % (u, p), "ascii"))
1376
+ #print("D... stream ok1", base64string)
1377
+ request.add_header("Authorization", "Basic %s" % base64string.decode("utf-8"))
1378
+ print("D... @urlopen: ", end=" ==>> ")
1379
+ #
1380
+ ok = False
1381
+ stream = None
1382
+ self.error = False
1383
+ self.which_error = ""
1384
+ try:
1385
+ stream = urllib.request.urlopen(
1386
+ request, timeout=3
1387
+ ) # timeout to 7 from 5 sec.
1388
+ ok = True
1389
+ print(" stream ok ", end="")
1390
+ except urllib.error.HTTPError as e:
1391
+ print(f"Srv Offline {e} {videodev} ", end="\n")
1392
+ self.which_error = "http error"
1393
+ self.error = True
1394
+ # do stuff here
1395
+ except urllib.error.URLError as e:
1396
+ print(f"Srv Offline {e} {videodev} ", end="\n")
1397
+ self.which_error = "url error"
1398
+ # do stuff here
1399
+ self.error = True
1400
+ except:
1401
+ self.error = True
1402
+ self.which_error = "timeout error"
1403
+ print("X.... Timeouted on URLOPEN", end="\n")
1404
+ if nopass:
1405
+ self.which_error = f"{self.which_error} / NOPASS" # no password file
1406
+ print( f"{str(dt.datetime.now() )[:-4]}---", end="\r")
1407
+
1408
+ return stream
1409
+
1410
+
1411
+
1412
+
1413
+ # ==================================================
1414
+ # NOT USED
1415
+ # --------------------------------------------------
1416
+
1417
+ def trim_bytex(self, max_size):
1418
+ if len(self.bytex) > max_size:
1419
+ self.bytex = self.bytex[-max_size:]
1420
+
1421
+
1422
+
1423
+ # ==================================================
1424
+ # COPY frame to img
1425
+ # --------------------------------------------------
1426
+ # ===============================================================================
1427
+ # VCR SCR
1428
+ # ===============================================================================
1429
+ def vcr_pal_style(self, messages):
1430
+ w, h = 640, 480
1431
+ img = np.zeros((h, w, 3), dtype=np.uint8)
1432
+ img[:] = (255, 0, 0) # Blue background
1433
+
1434
+ color = (0, 255, 255) # Yellow text
1435
+ font = cv2.FONT_HERSHEY_SIMPLEX
1436
+ scale = 1.0
1437
+ thickness = 2
1438
+
1439
+ lines = messages#.split(',')
1440
+ n = len(lines)
1441
+ line_height = h // (n + 1)
1442
+
1443
+ for i, line in enumerate(lines, 1):
1444
+ (text_w, text_h), _ = cv2.getTextSize(line, font, scale, thickness)
1445
+ org = ((w - text_w) // 2, line_height * i + text_h // 2)
1446
+ cv2.putText(img, line, org, font, scale, color, thickness, cv2.LINE_AA)
1447
+
1448
+ self.img = img
1449
+ #cv2.imshow('VCR PAL Style', img)
1450
+ #cv2.waitKey(0)
1451
+ #cv2.destroyAllWindows()
1452
+
1453
+ def use_frame(self, stripes=False):
1454
+ """
1455
+ put frame on tom of img .... ONLY THIS .....
1456
+ """
1457
+ if self.frame is None:
1458
+ # NEVER ACTIVE.................... 1st frame is STRIPES!
1459
+ print("X... no image", end="")
1460
+ time.sleep(0.2)
1461
+ #if stripes: # THIN
1462
+ # for y in range(0, self.img.shape[0], 16):
1463
+ # self.img[y:y + 1, :, :] = np.zeros((1, self.img.shape[1], 3), dtype=np.uint8) + 120
1464
+ if self.which_error == "":
1465
+ self.vcr_pal_style(["GOT NO FRAME", "", "URL:", f"{self.url}"])
1466
+ else:
1467
+ self.vcr_pal_style([ self.which_error, "", "URL:", f"{self.url}"])
1468
+ return False
1469
+ #--------------------- ^^^ never started. Frame was always None ______________----
1470
+ # ___________________ vvv started, make gray strips _____________________________
1471
+ ret_val = False
1472
+ if True:# self.img.shape == self.frame.shape:
1473
+ self.img = self.frame
1474
+ if stripes: # These are thin gray
1475
+ #self.vcr_pal_style(["NO SIGNAL"])
1476
+ for y in range(0, self.img.shape[0], 16):
1477
+ self.img[y:y + 1, :, :] = np.zeros((1, self.img.shape[1], 3), dtype=np.uint8) + 120
1478
+ ret_val = True
1479
+ #
1480
+ return ret_val
1481
+
1482
+
1483
+ # ==================================================
1484
+ # FETCHING THE IMAGE FROM VIDEODEV AND ALL LOCAL-ACTIONS ------------ ALSO INITIATING THE STREAM ------------
1485
+ # --------------------------------------------------
1486
+
1487
+ def fetch_and_update(self): # * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * *
1488
+ """
1489
+ main operation with image:: fetch_and_update + update_only
1490
+ """
1491
+ ret = None
1492
+ #print(f".... fau : {self.internet_not_device} ")
1493
+ if self.internet_not_device:
1494
+ ret = self.fetch_only()
1495
+ else:
1496
+ #resol = "640x480"
1497
+ #resol = "1920x1080"
1498
+ ret = self.capture_only(self.resolution)
1499
+ self.update_only(ret)
1500
+
1501
+ # ================================================================================
1502
+ # CLICK TRICK to recover resolution in CLI
1503
+ # --------------------------------------------------------------------------------
1504
+
1505
+ #def parse_resolution(self, ctx, param, value):
1506
+ def parse_resolution(self, value):
1507
+ try:
1508
+ w, h = map(int, value.lower().split('x'))
1509
+ return w, h
1510
+ except Exception:
1511
+ raise click.BadParameter("Resolution must be in WIDTHxHEIGHT format, e.g. 1920x1080")
1512
+
1513
+
1514
+
1515
+ ####################################################
1516
+ # m #
1517
+ # mmm mmm mmmm mm#mm m m m mm mmm #
1518
+ # #" " " # #" "# # # # #" " #" # #
1519
+ # # m"""# # # # # # # #"""" #
1520
+ # "#mm" "mm"# ##m#" "mm "mm"# # "#mm" #
1521
+ # # #
1522
+ # " #
1523
+ ####################################################
1524
+
1525
+
1526
+ # ================================================================================
1527
+ # CAPTURE
1528
+ # --------------------------------------------------------------------------------
1529
+
1530
+ def capture_only(self, resolution="640x480", fourcc="YUYV"):
1531
+ width, height = self.parse_resolution(resolution)
1532
+ ### print(f"D... cap == {self.cap} ")
1533
+ if self.cap is None:
1534
+ fourcc = self.fourcc #
1535
+ self.controls_dict = get_v4l2_controls(self.device)
1536
+ if self.controls_dict is None: self.which_error = "Device not found"
1537
+
1538
+ self.cap = cv2.VideoCapture(0, cv2.CAP_V4L2)
1539
+ fourcc_code = cv2.VideoWriter_fourcc(*fourcc)
1540
+ self.cap.set(cv2.CAP_PROP_FOURCC, fourcc_code)
1541
+ self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
1542
+ self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
1543
+ else:
1544
+ ret, self.frame = self.cap.read()
1545
+ #### print(f"D... ret = {ret} ")
1546
+ if ret is False:
1547
+ self.frames_to_fail -=1
1548
+ if ret is False and (self.frames_to_fail <= 0): # cap is set but ret is False, no video case (wierd)
1549
+ print(f"D... reseting cap" )
1550
+ self.cap = None
1551
+ time.sleep(1)
1552
+ self.frames_to_fail = self.frames_to_fail_max
1553
+
1554
+ #self.frame_time = dt.datetime.now().strftime("%Y-%m-%d_%H:%M:%S.%f")[:-4]
1555
+ self.l_frame_time = dt.datetime.now()
1556
+ self.frame_time = self.l_frame_time.strftime("%H:%M:%S.%f")[:-4]
1557
+ if not ret:
1558
+ return False
1559
+ print(f" --- {self.frame_time} ; resolution /{self.frame.shape}/ ---- ", end="")
1560
+
1561
+ avg = self.frame # once per Nx it is AVG
1562
+ # -------------------------------------------- all buffer thing will go elsewhere--------------
1563
+ # if self.accum_n > 1:
1564
+ # #avg = self.frame.astype("float32")
1565
+ # if len(self.accum_buffer) == self.accum_n: # TRICK---- RESTARTED BUFFER EVERYTIME ----- EASY TO CONTROL SAVE
1566
+ # avg = np.mean(self.accum_buffer, axis=0).astype(np.uint8)
1567
+ # self.frame = avg
1568
+ # self.accum_image = avg # store for next loop
1569
+ # # I will use this for -bad-old-orientative-image AND SAVE!
1570
+ # # RESET BUFFER! count from Zero
1571
+ # self.accum_buffer = deque(maxlen=self.accum_n)
1572
+ # # I need to carefully sync saving
1573
+ # self.accum_buffer.append( self.frame.astype("float32") ) # always frame
1574
+ self.img = avg # normally frame. but sometimes avg == really averaged img!
1575
+ # NICE.
1576
+ # I want to see accumulated....??????
1577
+ #if self.accum_image is not None and (len(self.accum_buffer) > self.accum_n / 2):
1578
+ # self.img = self.accum_image
1579
+ return True
1580
+
1581
+
1582
+
1583
+
1584
+ # ==================================================
1585
+ # FETCHING THE IMAGE FROM VIDEODEV ------------ ALSO INITI
1586
+ # --------------------------------------------------
1587
+
1588
+ def fetch_only(self):
1589
+ """
1590
+ called from from fetch_and_update() / returns BOOL
1591
+ """
1592
+ ret_val = False
1593
+ if self.stream is None:
1594
+ #self.make_strips()
1595
+ self.stream = self.get_stream(videodev=self.url)
1596
+ if self.stream is not None:
1597
+ print("\ni.... stream acquired")
1598
+ else:
1599
+ time.sleep(1) # dont be too offensive
1600
+ return False
1601
+ if self.stream is not None:
1602
+ #else:# I have the stream -----------------------------------------
1603
+ ret_val = False
1604
+ # --------- try to grab stream ------------
1605
+ try:
1606
+ print(f"i... acq {self.stream_length/1024:.1f} kB ", end="")
1607
+ delta_wait = 0
1608
+ self.t_oldread = self.t_preread
1609
+ self.t_preread = dt.datetime.now()
1610
+ delta_loop = (self.t_preread - self.t_oldread).total_seconds() # FULL LOOP
1611
+ # ------------------------ ******
1612
+ self.bytex += self.stream.read(self.stream_length)
1613
+ # ------------------------ *******
1614
+ self.t_posread = dt.datetime.now()
1615
+ delta_read = (self.t_posread-self.t_preread).total_seconds() # wait
1616
+ if delta_read < 0.01:
1617
+ time.sleep(0.03)
1618
+ self.t_posread = dt.datetime.now()
1619
+ delta_read = (self.t_posread-self.t_preread).total_seconds() # wait
1620
+
1621
+ self.t_lasread = self.l_frame_time
1622
+ self.l_frame_time = dt.datetime.now()
1623
+ delta_frame = (self.l_frame_time - self.t_lasread).total_seconds() # TOTAL
1624
+ #
1625
+ print(f" ; {self.stream_length/delta_frame/1024/1024*8:4.2f} Mbs .. buffer tot= {len(self.bytex)/1024:4.1f} kB; Reading:{delta_read:4.2f} s.; TOT {str(delta_frame)[:4]} s.; Duty {int((delta_frame-delta_read)/delta_frame*100):3.0f} % ", end="")
1626
+ #
1627
+ except:
1628
+ self.bytex = b""
1629
+ #print()
1630
+ self.stream = None
1631
+ a = self.bytex.find(b"\xff\xd8") # frame starting
1632
+ b = self.bytex.find(b"\xff\xd9") # frame ending
1633
+ ttag = self.bytex.find( "#FRAME_ACQUISITION_TIME".encode("utf8") ) # frame ending
1634
+ webframen = " " * 7
1635
+ webframetime = " " * 23
1636
+ if ttag != -1:
1637
+ # print(f"i... FRACQT: /{ttag}/ \
1638
+ # /{bytex[ttag:ttag+32]}/----------------")
1639
+ webframen = self.bytex[ttag: ttag + 32 + 23].decode("utf8")
1640
+ self.frame_num, self.frame_time = " " * 7, " " * 23
1641
+ if "#" in webframen:
1642
+ webframen = webframen.split("#")
1643
+ # print(len(webframen), webframen)
1644
+ self.frame_num, self.frame_time = webframen[2], webframen[3]
1645
+
1646
+
1647
+ if a != -1 and b != -1: # jpg detected
1648
+ #io_none = 0
1649
+ # HERE THE TIME AND FRAME NUMBER ARE CURRENT
1650
+ print(f"#{self.frame_num} {self.frame_time} | {self.l_frame_num:7d} | {self.l_frame_time.strftime('%H:%M:%S.%f')[:-4]} ", end="")
1651
+ print(f"| LOS:{self.l_frame_bia:5d} ", end="")
1652
+
1653
+ jpg = self.bytex[a: b + 2]
1654
+ if ttag != -1:
1655
+ # this is with framen
1656
+ self.bytex = self.bytex[b + 2 :]
1657
+ self.stream_length = int((b + 2 - a) ) + 24 + 30 + 29# expected length 24 TEXT;30 FRAMNEMS; 29
1658
+ else:
1659
+ self.bytex = self.bytex[b + 2 :]
1660
+ self.stream_length = int((b + 2 - a) ) # expected length
1661
+
1662
+ if self.stream_length < 1000: # if a -b == 0 reset
1663
+ self.stream_length = self.stream_length_init
1664
+ # **************** FINISH PRINT HERE **************
1665
+ #*****************print()
1666
+ #print(len(jpg))
1667
+ #print(f"... jpg {len(jpg)} ")
1668
+
1669
+
1670
+ if len(jpg) > 1000: # was crash here
1671
+ # *******************************
1672
+ self.frame = cv2.imdecode(np.frombuffer(jpg, dtype=np.uint8), cv2.IMREAD_COLOR)
1673
+ ret_val = self.use_frame() # COPY TO IMG and RETURN VALUE!
1674
+ #
1675
+ # --- tune the size of next JPG
1676
+ #self.stream_length = int((b + 2 - a) / 2) # expected length divided by 2
1677
+ else:
1678
+ ret_val = False # small jpg is no good
1679
+ else:# no jpg detected
1680
+ ret_val = False # no change
1681
+ return ret_val
1682
+
1683
+
1684
+ ##################################################################
1685
+ # # m mmmm mm m m #
1686
+ # m m mmmm mmm# mmm mm#mm mmm m" "m #"m # # #
1687
+ # # # #" "# #" "# " # # #" # # # # #m # # #
1688
+ # # # # # # # m"""# # #"""" # # # # # # #
1689
+ # "mm"# ##m#" "#m## "mm"# "mm "#mm" #mm# # ## #mmmmm #
1690
+ # # #
1691
+ # " #
1692
+ ##################################################################
1693
+
1694
+ def define_accum_buffer(self, n ):
1695
+ # NEED TO REDEFINE ON S-X !!!!
1696
+ if (self.frame is None):#
1697
+ return False
1698
+ if (n == self.accum_buffer_size) and (len(self.accum_buffer) > 1):
1699
+ return True
1700
+ self.accum_buffer_size = n
1701
+ self.accum_buffer = np.zeros((self.accum_buffer_size, *self.frame.shape), dtype=self.img.dtype)
1702
+ self.accum_count = 0
1703
+ self.accum_index = 0
1704
+ self.running_sum = np.zeros(self.frame.shape, dtype=np.float64)
1705
+ return True
1706
+
1707
+ def add_to_accum_buffer(self, frame):
1708
+ # When adding a new frame:
1709
+ if (len(self.accum_buffer) < 1):
1710
+ return False
1711
+ #print("addind")
1712
+ if self.accum_count < self.accum_buffer_size:
1713
+ self.running_sum += frame
1714
+ self.accum_buffer[self.accum_index] = frame
1715
+ self.accum_count += 1
1716
+ else:
1717
+ oldest_frame = self.accum_buffer[self.accum_index]
1718
+ if frame.shape != oldest_frame.shape:
1719
+ #self.define_accum_buffer(self.accum_buffer_size )
1720
+ return False
1721
+ self.running_sum += frame.astype(np.float64) - oldest_frame.astype(np.float64)
1722
+ self.accum_buffer[self.accum_index] = frame
1723
+ # do as before
1724
+ #@self.accum_buffer[self.accum_index] = frame
1725
+ self.accum_index = (self.accum_index + 1) % self.accum_buffer_size
1726
+ return True
1727
+
1728
+ def get_mean_accum_buffer(self):
1729
+ if self.accum_count == 0:
1730
+ return None
1731
+ rimg = self.running_sum / self.accum_count
1732
+ return rimg.astype(np.uint8)
1733
+ # #print(self.accum_count, self.accum_buffer_size)
1734
+ # if self.accum_count > 1:
1735
+ # img = np.mean(self.accum_buffer[:self.accum_count], axis=0)
1736
+ # return img
1737
+ # return None
1738
+
1739
+ def order_accum_buffer_frames(self):
1740
+ if self.accum_count < self.accum_buffer_size:
1741
+ frames_ordered = self.accum_buffer[:self.accum_count]
1742
+ else:
1743
+ frames_ordered = np.concatenate((self.accum_buffer[self.accum_index:], self.accum_buffer[:self.accum_index]))
1744
+ for frame in frames_ordered:
1745
+ yield frame
1746
+ #return frames_ordered
1747
+
1748
+ #def iter_accum_buffer_ordered_frames(self):
1749
+ # frames_ordered = self.order_accum_buffer_frames()
1750
+
1751
+
1752
+
1753
+ # ================================================================================
1754
+ # All the traNSFORMS and saves are here
1755
+ # --------------------------------------------------------------------------------
1756
+
1757
+ def update_only(self, ret_val):
1758
+ """
1759
+ called from fetch_and_update() ; FINAL OPERATIONS ON FRAME IMG (incl SAVE) ; BEFORE QT
1760
+ """
1761
+ # print() NOT FINAL \n
1762
+ # ___________________________________ whatever, RET VAL ------------RET VAL ------------RET VAL ------------
1763
+ # ___________________________ after this point - NO REFERENCE TO FRAME ***** only img *********************
1764
+ # ___________________________________ whatever, RET VAL ------------RET VAL ------------RET VAL ------------
1765
+ if ret_val and not self.error: # OK
1766
+
1767
+ self.l_frame_num += 1
1768
+ #print(self.l_frame_num)
1769
+
1770
+ # ------------------------- calculate bias to see lost frames count ----------------------
1771
+ # no change to frame
1772
+ if self.l_frame_offs is None or (self.l_frame_bia < 0):
1773
+ try:
1774
+ self.l_frame_offs = int(self.frame_num.lstrip('0')) - self.l_frame_num
1775
+ except:
1776
+ pass
1777
+ self.l_frame_bia = 0
1778
+ if self.l_frame_offs is not None:
1779
+ try:
1780
+ self.l_frame_bia = int(self.frame_num.lstrip('0')) - self.l_frame_num - self.l_frame_offs
1781
+ except:
1782
+ pass
1783
+
1784
+ # CROSS
1785
+ if self.flag_redcross:
1786
+ self.img = crosson(self.img, self.redcross[0], self.redcross[1], color="r", box_large=True)
1787
+
1788
+
1789
+
1790
+ # Final transformations before SAVE, you save zoomed------------------TRANNS OR NOT TRANS
1791
+ if self.saving_transformed_image:
1792
+ self.img = self.final_transformations( self.img )
1793
+ if self.flag_print_over:
1794
+ self.overttext() #
1795
+ self.SAVED_NOW = False # this is for overtext RED save
1796
+
1797
+ # SAVING organization HERE
1798
+ # SAVING organization HERE
1799
+ # SAVING organization HERE
1800
+ # SAVING organization HERE
1801
+ # SAVING organization HERE
1802
+ # SAVING organization HERE
1803
+
1804
+ if self.level2_buffer is None:
1805
+ self.level2_buffer = AccumBuffer(self.img)
1806
+ self.level2_buffer.define_accum_buffer( self.level2_buffer_max_size ) # ???
1807
+
1808
+
1809
+ # DEFINE BUFFER, if not yet / if overtext is before, i have blurred timemarks
1810
+ # ---------------------------- if overtext is after , i do not have greentext on image
1811
+ if self.define_accum_buffer( self.accum_n ): # does nothing if already exists
1812
+ self.add_to_accum_buffer( self.img) # No idea why fits has wrong colors
1813
+
1814
+ if self.l_show_accum and (self.accum_n > 1) and (len(self.accum_buffer) > 1):
1815
+ rimg = self.get_mean_accum_buffer()
1816
+ if rimg is not None:
1817
+ self.img = rimg
1818
+ self.overttext(blackbar=True) # applies on img
1819
+
1820
+
1821
+
1822
+ # # -- LAPS 1s 10s 60s but also accumulated local frames
1823
+ # if self.saving_laps == 0:
1824
+ # pass
1825
+ # elif (self.saving_laps == 1) and self.accum_n > 2:
1826
+ # # this is not set to internet!!! and also it feels accum
1827
+ # now = dt.datetime.now()
1828
+ # if (now - self.saving_laps_last_save).total_seconds() > self.saving_laps: # at least 1s
1829
+ # if len(self.accum_buffer) == 1: # the trick to save only when accum image is available
1830
+ # fitag = "X "
1831
+ # if self.saving_fits_only:
1832
+ # fitag = f"{fitag}f"
1833
+ # print(fg.red, f"L{self.saving_laps}{fitag}", fg.default, end="")
1834
+ # self.saving_laps_last_save = now
1835
+ # # IT IS saving self.img.....
1836
+ # # self.img = self.accum_image # Would do just frame without real other actions...zoom...
1837
+ # self.save_img( time_tag=self.frame_time + f"A{self.accum_n}", savingjpg=False, save_accum=True) #
1838
+
1839
+ # elif self.saving_laps > 0:
1840
+ # now = dt.datetime.now()
1841
+ # if (now - self.saving_laps_last_save).total_seconds() > self.saving_laps:
1842
+ # fitag = "! "
1843
+ # if self.saving_fits_only:
1844
+ # fitag = f"{fitag}f"
1845
+ # print(fg.red, f"L{self.saving_laps}{fitag}", fg.default, end="")
1846
+ # self.saving_laps_last_save = now
1847
+ # self.save_img( time_tag=self.frame_time , savingjpg=False) #
1848
+
1849
+ # ---- just save once ----------------- -------------------------------------------- ************ "s" ***********
1850
+ if self.saving_once:
1851
+ # jpg and NO AVG
1852
+ if (self.accum_buffer_size < 2) and (not self.l_show_accum) and (not self.saving_fits_only):
1853
+ # no bufffer no loopshow no fits
1854
+ self.save_img( time_tag=self.frame_time , dumpbuffer=False, use_fits=False ) # one simple image
1855
+ print(fg.red, "s1", fg.default, end="")
1856
+ self.saving_once = False
1857
+ # jpg and NO AVG
1858
+ elif (self.accum_buffer_size >= 2) and (not self.l_show_accum) and (not self.saving_fits_only):
1859
+ self.save_img( time_tag=self.frame_time, dumpbuffer=False, use_fits=False ) # save one simple image only
1860
+ print(fg.red, "s1", fg.default, end="")
1861
+ self.saving_once = False
1862
+ # jpg and AVG
1863
+ elif (self.accum_buffer_size < 2) and (self.l_show_accum) and (not self.saving_fits_only):
1864
+ # no bufffer no loopshow no fits
1865
+ self.save_img( time_tag=self.frame_time, dumpbuffer=False, use_fits=False ) # just one simple image /lshow inside
1866
+ print(fg.red, "F1", fg.default, end="")
1867
+ self.saving_once = False
1868
+ pass
1869
+ # jpg and AVG
1870
+ elif (self.accum_buffer_size >= 2) and (self.l_show_accum) and (not self.saving_fits_only):
1871
+ # no bufffer no loopshow no fits
1872
+ if self.accum_index >= self.accum_buffer_size - 1:
1873
+ self.save_img( time_tag=self.frame_time, dumpbuffer=False, use_fits=False ) # should be 1 AVG IMG
1874
+ print(fg.red, "F1", fg.default, end="")
1875
+ self.saving_once = False
1876
+ pass
1877
+ # FITS and NO AVG ---------------------------------------------------------------------------- FITS
1878
+ elif (self.accum_buffer_size < 2) and (not self.l_show_accum) and (self.saving_fits_only):
1879
+ # no bufffer no loopshow YES fits
1880
+ self.save_img( time_tag=self.frame_time , dumpbuffer=False, use_fits=True ) # 1 img
1881
+ print(fg.red, "F1", fg.default, end="")
1882
+ self.saving_once = False
1883
+ pass
1884
+ # FITS and NO AVG
1885
+ elif (self.accum_buffer_size >= 2) and (not self.l_show_accum) and (self.saving_fits_only):
1886
+ # no bufffer no loopshow no fits
1887
+ self.save_img( time_tag=self.frame_time , dumpbuffer=True, use_fits=True ) # dump buffer once
1888
+ print(fg.red, "F1", fg.default, end="")
1889
+ self.saving_once = False
1890
+ pass
1891
+ # FITS and avg
1892
+ elif (self.accum_buffer_size < 2) and (self.l_show_accum) and (self.saving_fits_only):
1893
+ # no bufffer no loopshow no fits
1894
+ self.save_img( time_tag=self.frame_time , dumpbuffer=False, use_fits=True ) # one AVG
1895
+ print(fg.red, "F1", fg.default, end="")
1896
+ self.saving_once = False
1897
+ pass
1898
+ # FITS and avg there are more
1899
+ elif (self.accum_buffer_size >= 2) and (self.l_show_accum) and (self.saving_fits_only):
1900
+ # no bufffer no loopshow no fits
1901
+ if self.accum_index >= self.accum_buffer_size - 1:
1902
+ self.save_img( time_tag=self.frame_time , dumpbuffer=False, use_fits=True ) # many AVG IDK
1903
+ print(fg.red, "F1", fg.default, end="")
1904
+ self.saving_once = False
1905
+ pass
1906
+
1907
+
1908
+ # ---- save ALL ----------------- -------------------------------------------- ************ "shift-s" ***********
1909
+ # ---- save ALL ----------------- -------------------------------------------- ************ "shift-s" ***********
1910
+ if self.saving_all: # --------------- Shift-S-------
1911
+ # jpg and NO AVG
1912
+ if (self.accum_buffer_size < 2) and (not self.l_show_accum) and (not self.saving_fits_only):
1913
+ self.save_img( time_tag=self.frame_time, dumpbuffer=False, use_fits=False) # every frame, BURSTING JPGS!
1914
+ print(fg.red, "s!", fg.default, f"{bg.red}{fg.white}!!!!!!!!!!!!{bg.default}{fg.default}", end="\n")
1915
+ # jpg and NO AVG
1916
+ elif (self.accum_buffer_size >= 2) and (not self.l_show_accum) and (not self.saving_fits_only):
1917
+ self.save_img( time_tag=self.frame_time, dumpbuffer=True, use_fits=False ) # Dump Full Buffer and stop
1918
+ print(fg.red, "s-FuB DUMPED", fg.default, end="\n") # ONE DUMP
1919
+ self.saving_all = False
1920
+ # jpg and AVG
1921
+ elif (self.accum_buffer_size < 2) and (self.l_show_accum) and (not self.saving_fits_only):
1922
+ # no bufffer no loopshow no fits
1923
+ #self.save_img( time_tag=self.frame_time, dumpbuffer=False, use_fits=False ) # just one simple image /lshow inside
1924
+ self.saving_all = False
1925
+ pass
1926
+ # jpg and AVG
1927
+ elif (self.accum_buffer_size >= 2) and (self.l_show_accum) and (not self.saving_fits_only):
1928
+ # no bufffer no loopshow no fits
1929
+ #self.save_img( time_tag=self.frame_time, dumpbuffer=False, use_fits=False ) # should be AVG
1930
+ if self.accum_index >= self.accum_buffer_size - 1:
1931
+ self.save_img( time_tag=self.frame_time, dumpbuffer=False, use_fits=False ) # Dump Full Buffer and stop
1932
+ print(fg.red, "Save AVG evry Nth ", fg.default, end="")
1933
+ pass
1934
+ # FITS and NO AVG ---------------------------------------------------------------------------- FITS
1935
+ # FITS and NO AVG ---------------------------------------------------------------------------- FITS
1936
+ elif (self.accum_buffer_size < 2) and (not self.l_show_accum) and (self.saving_fits_only):
1937
+ print(" here fits for every image .... too low buffer --- so MAYBE ")
1938
+ # no bufffer no loopshow YES fits
1939
+ self.save_img( time_tag=self.frame_time, dumpbuffer=False, use_fits=True) # every frame, BURSTING FITS !?!?!
1940
+ #print(fg.red, "every N frames to FITS-IDK", fg.default, f"{bg.red}{fg.white}???{bg.default}{fg.default}", end="\n")
1941
+ pass
1942
+ # FITS and NO AVG
1943
+ elif (self.accum_buffer_size >= 2) and (not self.l_show_accum) and (self.saving_fits_only):
1944
+ print(fg.red," here fits for ALL BUFFER NONONO no save ", fg.default, end="")
1945
+ ## no bufffer no loopshow no fits
1946
+ #if self.accum_index >= self.accum_buffer_size - 1:
1947
+ # self.save_img( time_tag=self.frame_time , dumpbuffer=True, use_fits=True ) # dump buffer every time
1948
+ # print(fg.red, "F-FuB", fg.default , f"{bg.red}{fg.white}!!!!!!!!!!!!{bg.default}{fg.default}", end="\n")
1949
+ pass
1950
+ # FITS and avg
1951
+ elif (self.accum_buffer_size < 2) and (self.l_show_accum) and (self.saving_fits_only):
1952
+ print(fg.red, " here - too low buffer+ ACCUM => no save ", fg.default, end="")
1953
+ # no bufffer no loopshow no fits
1954
+ #self.save_img( time_tag=self.frame_time , dumpbuffer=False, use_fits=True ) # one AVG (lshow handled inside)
1955
+ #print(fg.red, "xxxx FITS -IDK", fg.default, end="")
1956
+ self.saving_all = False
1957
+ pass
1958
+ # FITS and avg
1959
+ elif (self.accum_buffer_size >= 2) and (self.l_show_accum) and (self.saving_fits_only):
1960
+ # TOO COMPLEX -------------- I CHANGE TO FITS EVERY TIME NEW BUFFER IS OK -----------
1961
+ # no bufffer no loopshow no fits
1962
+ #self.save_img( time_tag=self.frame_time , dumpbuffer=False, use_fits=True ) # many AVG IDK
1963
+ if self.accum_index >= self.accum_buffer_size - 1: # ONLY THE ACCUM FRAME!
1964
+ self.save_img( time_tag=self.frame_time , dumpbuffer=False, use_fits=True ) # SIMPLIFIED
1965
+ print(fg.red, "F-Every Nth-AVG to FITS -IDK", fg.default, end="")
1966
+ ###########################################################################################################################################
1967
+ # if self.level2_buffer.get_frame_shape() != self.img.shape: #
1968
+ # print(self.level2_buffer.get_frame_shape, self.img.shape) # CLEAR WHEN RES_CHANGE #
1969
+ # self.level2_buffer.clear_buffer(self.img) # repaired resoltution switch-crash #
1970
+ # # #
1971
+ # self.level2_buffer.add_to_accum_buffer( self.img) # ACCUMULATE #
1972
+ # print(" level2 frames*: ", self.level2_buffer.get_current_size(), end=" ") #
1973
+ # if (self.level2_buffer.is_accum_index_at_end) and ( self.level2_buffer.get_current_size() == self.level2_buffer.get_max_buffer_size()): #
1974
+ # # BUFFER OF BUFFERS !! TODO #
1975
+ # level2buff_ord = self.level2_buffer.order_accum_buffer_frames() #
1976
+ ###########################################################################################################################################
1977
+ #self.save_img( time_tag=self.frame_time , dumpbuffer=True, use_fits=True, use_buffer=level2buff_ord) # many AVG IDK
1978
+ #############################################
1979
+ # self.level2_buffer.clear_buffer(self.img) #
1980
+ #############################################
1981
+ # print(fg.red, "F-Every Nth-AVG to FITS -IDK", fg.default, end="")
1982
+ pass
1983
+
1984
+
1985
+
1986
+
1987
+
1988
+
1989
+
1990
+
1991
+
1992
+
1993
+
1994
+
1995
+
1996
+ # ------------------------------------- -------------------- TRANNS OR NOT TRANS
1997
+ if not self.saving_transformed_image:
1998
+ self.img = self.final_transformations( self.img )
1999
+ if self.flag_print_over:
2000
+ self.overttext()
2001
+ self.SAVED_NOW = False # this is for overtext RED save
2002
+
2003
+ #
2004
+ else:# --- NO IMAGE CREATED ------------------------------- ... make the image gray ...
2005
+ #print("D... 3")
2006
+ # Extra override with some frame
2007
+ self.img = cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)
2008
+ self.img = cv2.cvtColor(self.img, cv2.COLOR_GRAY2BGR) #I want to keep 3 channels
2009
+ self.use_frame(stripes=True)# img = self.frame
2010
+ #=============================== --------------------- =================== UPDATE/SHOW
2011
+ #=============================== --------------------- =================== UPDATE/SHOW
2012
+ #=============================== --------------------- =================== UPDATE/SHOW
2013
+ if self.flag_print:
2014
+ print(" ", end="\n") # FINQAL \n
2015
+ else:
2016
+ print(" ", end="\r") # FINQAL \n
2017
+
2018
+ # ------------- # rgb_image MUST EXIST ! ! ! ! !
2019
+ self.rgb_image = cv2.cvtColor(self.img, cv2.COLOR_BGR2RGB) #I need for QT
2020
+ self.update_image() # need self.rgb_image
2021
+
2022
+
2023
+
2024
+
2025
+ ###########################################################
2026
+ # # mmmmm #
2027
+ # # m mmm m m # "# m mm mmm mmm mmm #
2028
+ # # m" #" # "m m" #mmm#" #" " #" # # " # " #
2029
+ # #"# #"""" #m# # # #"""" """m """m #
2030
+ # # "m "#mm" "# # # "#mm" "mmm" "mmm" #
2031
+ # m" #
2032
+ # "" #
2033
+ ###########################################################
2034
+
2035
+ # ==================================================
2036
+ #
2037
+ # --------------------------------------------------
2038
+
2039
+ def keyPressEvent(self, event):
2040
+ key = event.key()
2041
+ modifiers = event.modifiers()
2042
+ parts = []
2043
+
2044
+ if modifiers & Qt.KeyboardModifier.ShiftModifier:
2045
+ parts.append("Shift")
2046
+ if modifiers & Qt.KeyboardModifier.ControlModifier:
2047
+ parts.append("Ctrl")
2048
+ if modifiers & Qt.KeyboardModifier.AltModifier:
2049
+ parts.append("Alt")
2050
+
2051
+ #parts.append(f"Key: {key}") # only modifiers
2052
+ parts_set = set(parts)
2053
+ if key < 256:
2054
+ self.post_addr = None
2055
+ if self.internet_not_device:
2056
+ print(self.url, self.internet_not_device)
2057
+ self.post_addr = self.url.replace("/video", "/cross")
2058
+ #post_addr = self.url.replace("/video", "/cross") # FOR REMOTE COMMANDS
2059
+
2060
+ #print(" + ".join(parts), f" /{chr(key)}/ .... {parts_set}")
2061
+ # ----------------------------------------------------------------- s savings
2062
+ if (key == Qt.Key.Key_S):
2063
+ if ( len(parts_set) == 0) :
2064
+ self.saving_once = True
2065
+ self.saving_all = False
2066
+ #self.saving_fits_only = False
2067
+ #self.saving_jpg = True
2068
+ print("i... SAVING_ONCE IMAGE ")
2069
+ #---------------- SHIFT-S: 1) IbufferON=> save every nth image; 2) ---
2070
+ elif (parts_set == {'Shift'}):
2071
+ self.saving_all = not self.saving_all
2072
+ #self.saving_fits_only = False
2073
+ print(f"i... 'SAVING_all' SET TO {self.saving_all} !!!!!FITS=={self.saving_fits_only}!!!!! ")
2074
+ if self.saving_all:
2075
+ print('ffmpeg -framerate 5 -pattern_type glob -i "*.jpg" -c:v libx264 -pix_fmt yuv420p output.mkv')
2076
+ print('ffmpeg -hide_banner -y -framerate 5 -pattern_type glob -i "*.jpg" -c:v libx264 -pix_fmt yuv420p output.mkv')
2077
+ print('flatpak run org.siril.Siril ; seuqence create')
2078
+ print('kstars (but press revert)')
2079
+ print()
2080
+ elif (parts_set == {'Ctrl'}) :
2081
+ self.saving_fits_only = not self.saving_fits_only
2082
+ self.saving_all = False
2083
+ if self.saving_fits_only:
2084
+ print(f"i... {fg.orange}FITS_ONLY set to {self.saving_fits_only}{fg.default} (interval is {self.FITS_INTERVAL_SECONDS}) ; but 'SAVING_all' SET TO ", self.saving_all)
2085
+ else:
2086
+ print(f"i... FITS_ONLY set to {fg.cyan}{self.saving_fits_only}{fg.default} (interval is {self.FITS_INTERVAL_SECONDS}) ; but 'SAVING_all' SET TO ", self.saving_all)
2087
+
2088
+ elif (parts_set == {'Ctrl', 'Shift'}) :
2089
+ self.saving_jpg = not self.saving_jpg
2090
+ self.saving_all = False
2091
+ if self.saving_jpg:
2092
+ print(f"i... {fg.green}SAVING_JPG set to {self.saving_jpg}{fg.default} (interval is {self.FITS_INTERVAL_SECONDS}) ; but 'SAVING_all' SET TO ", self.saving_all)
2093
+ else:
2094
+ print(f"i... {fg.orange}PNG!{fg.default} SAVING_JPG set to {fg.cyan}{self.saving_jpg}{fg.default} (interval is {self.FITS_INTERVAL_SECONDS}) ; but 'SAVING_all' SET TO ", self.saving_all)
2095
+
2096
+ # ----------------------------------------------------------------- x xtend x2 ot switchres resolution
2097
+ if (key == Qt.Key.Key_X):
2098
+ if ( len(parts_set) == 0):
2099
+ self.xtended = not self.xtended
2100
+ print("i... xtending IMAGE locally 2x ", self.xtended)
2101
+
2102
+ elif (parts_set == {'Shift'}):
2103
+ self.r_xtend = "CC"
2104
+ self.send_command( data={"switch_res_on": "SWITCH_RES_ON"})
2105
+ print("D.... r_xtend == CC <<========= ", self.r_xtend)
2106
+
2107
+
2108
+
2109
+ elif (parts_set == {'Ctrl'}) :
2110
+ self.r_xtend = " "
2111
+ self.send_command( data={"switch_res_off": "SWITCH_RES_OFF"})
2112
+ print("D.... r_xtend == ' ' <<======== ", self.r_xtend)
2113
+
2114
+ # ----------------------------------------------------------------- p printout
2115
+ if (key == Qt.Key.Key_P):
2116
+ if ( len(parts_set) == 0):
2117
+ self.flag_print = not self.flag_print
2118
+ #print("i... flasg print ", self.flag_print)
2119
+ elif (parts_set == {'Shift'}):
2120
+ self.flag_print_over = not self.flag_print_over
2121
+ elif (parts_set == {'Ctrl'}) :
2122
+ pass
2123
+ # ----------------------------------------------------------------- e expo
2124
+ if (key == Qt.Key.Key_E):
2125
+ if ( len(parts_set) == 0):
2126
+ if self.r_expo < 1.0: self.r_expo += 0.02
2127
+ if self.r_expo > 1: self.r_expo = 1
2128
+ self.r_expodef = False
2129
+ self.send_command( data= {"expot": "EXPOT", "expotxt":self.r_expo} )
2130
+ elif (parts_set == {'Shift'}) :
2131
+ if self.r_expo > 0.0: self.r_expo -= 0.05
2132
+ if self.r_expo < 0: self.r_expo = 0
2133
+ self.r_expodef = False
2134
+ self.send_command( data= {"expot": "EXPOT", "expotxt":self.r_expo} )
2135
+ elif (parts_set == {'Ctrl'}) :
2136
+ self.send_command( data= {"expot": "EXPOT", "expotxt": -1.0} )
2137
+ self.r_expodef = True
2138
+
2139
+ # ----------------------------------------------------------------- g gain
2140
+ if (key == Qt.Key.Key_G):
2141
+ if ( len(parts_set) == 0):
2142
+ if self.r_gain < 1.0: self.r_gain += 0.1
2143
+ if self.r_gain > 1: self.r_gain = 1
2144
+ self.send_command( data= {"gaint": "GAINT", "gaintxt":self.r_gain} )
2145
+ self.r_gaindef = False
2146
+ elif (parts_set == {'Shift'}) :
2147
+ if self.r_gain > 0.0: self.r_gain -= 0.1
2148
+ if self.r_gain < 0: self.r_gain = 0
2149
+ self.send_command( data= {"gaint": "GAINT", "gaintxt":self.r_gain} )
2150
+ self.r_gaindef = False
2151
+ elif (parts_set == {'Ctrl'}) :
2152
+ self.send_command( data= {"gaint": "GAINT", "gaintxt": -1.0} )
2153
+ self.r_gaindef = True
2154
+ # self.send_command( data= {"gain2": "GAIN2"} )
2155
+ #elif (parts_set == {'Shift'}) :
2156
+ # self.send_command( data= {"gain05": "GAIN05"})
2157
+ #elif (parts_set == {'Ctrl'}) :
2158
+ # self.send_command( data= {"gain": "GAIN"} )
2159
+ # ----------------------------------------------------------------- y gamma
2160
+ if (key == Qt.Key.Key_Y):
2161
+ if ( len(parts_set) == 0):
2162
+ if self.r_gamma < 1.0: self.r_gamma += 0.1
2163
+ if self.r_gamma > 1: self.r_gamma = 1
2164
+ self.r_gammadef = False
2165
+ self.send_command( data= {"gammat": "GAMMAT", "gammatxt":self.r_gamma} )
2166
+ elif (parts_set == {'Shift'}) :
2167
+ if self.r_gamma > 0.0: self.r_gamma -= 0.1
2168
+ if self.r_gamma < 0: self.r_gamma = 0
2169
+ self.r_gammadef = False
2170
+ self.send_command( data= {"gammat": "GAMMAT", "gammatxt":self.r_gamma} )
2171
+ elif (parts_set == {'Ctrl'}) :
2172
+ self.send_command( data= {"gammat": "GAMMAT", "gammatxt": -1.0} )
2173
+ self.r_gammadef = True
2174
+
2175
+ # self.send_command( data= {"gamma2": "GAMMA2"} )
2176
+ #elif (parts_set == {'Shift'}) :
2177
+ # self.send_command( data= {"gamma05": "GAMMA05"})
2178
+ #elif (parts_set == {'Ctrl'}) :
2179
+ # self.send_command( data= {"gamma": "GAMMA"} )
2180
+ # ----------------------------------------------------------------- d local gamma
2181
+ if (key == Qt.Key.Key_D):
2182
+ if ( len(parts_set) == 0):
2183
+ self.l_gamma = self.l_gamma * 1.4
2184
+ elif (parts_set == {'Shift'}) :
2185
+ self.l_gamma = self.l_gamma / 1.4
2186
+ elif (parts_set == {'Ctrl'}) :
2187
+ self.l_gamma = 1
2188
+
2189
+ # ----------------------------------------------------------------- w
2190
+ if (key == Qt.Key.Key_W):
2191
+ if ( len(parts_set) == 0):
2192
+ webbrowser.open(self.url.replace("/video", "")) # BRUTAL
2193
+ elif (parts_set == {'Shift'}) :
2194
+ pass
2195
+ elif (parts_set == {'Ctrl'}) :
2196
+ pass
2197
+ # ----------------------------------------------------------------- z
2198
+ if (key == Qt.Key.Key_Z):
2199
+ if ( len(parts_set) == 0):
2200
+ self.zoomme *= 1.5
2201
+ if self.zoomme > 5:
2202
+ self.zoomme = 5
2203
+ elif (parts_set == {'Shift'}) :
2204
+ if self.zoomme > 1:
2205
+ self.zoomme /= 1.5
2206
+ if self.zoomme < 1: self.zoome= 1
2207
+ elif (parts_set == {'Ctrl'}) :
2208
+ self.zoomme = 1
2209
+ # ----------------------------------------------------------------- hjkl
2210
+ # self.send_command( data={"right": "RIGHT"})
2211
+ if (key == Qt.Key.Key_H):
2212
+ if ( len(parts_set) == 0):
2213
+ self.redcross[0] -= 4
2214
+ elif (parts_set == {'Shift'}) :
2215
+ self.redcross[0] -= 17
2216
+ elif (parts_set == {'Ctrl'}) :
2217
+ self.redcross[0] -= 65
2218
+ elif (parts_set == {'Ctrl', 'Shift'}) :
2219
+ self.send_command( data={"left": "LEFT"})
2220
+ if self.r_xtend[0] == "R": self.r_xtend = "C" + self.r_xtend[1:]
2221
+ elif self.r_xtend[0] == "C": self.r_xtend = "L" + self.r_xtend[1:]
2222
+ elif self.r_xtend[0] == " ": self.r_xtend = "L" + self.r_xtend[1:]
2223
+ # ----------------------------------------------------------------- hjkl
2224
+ if (key == Qt.Key.Key_J):
2225
+ if ( len(parts_set) == 0):
2226
+ self.redcross[1] += 4 # DOWN
2227
+ elif (parts_set == {'Shift'}) :
2228
+ self.redcross[1] += 17
2229
+ elif (parts_set == {'Ctrl'}) :
2230
+ self.redcross[1] += 65
2231
+ elif (parts_set == {'Ctrl', 'Shift'}) :
2232
+ self.send_command( data={"down": "DOWN"})
2233
+ if self.r_xtend[1] == "U": self.r_xtend = self.r_xtend[:1] + "C"
2234
+ elif self.r_xtend[1] == "C": self.r_xtend = self.r_xtend[:1] + "D"
2235
+ elif self.r_xtend[1] == " ": self.r_xtend = self.r_xtend[:1] + "D"
2236
+ # ----------------------------------------------------------------- hjkl
2237
+ if (key == Qt.Key.Key_K):
2238
+ if ( len(parts_set) == 0):
2239
+ self.redcross[1] -= 4 # UP
2240
+ elif (parts_set == {'Shift'}) :
2241
+ self.redcross[1] -= 17 # UP
2242
+ elif (parts_set == {'Ctrl'}) :
2243
+ self.redcross[1] -= 65
2244
+ elif (parts_set == {'Ctrl', 'Shift'}) :
2245
+ self.send_command( data={"up": "UP"})
2246
+ if self.r_xtend[1] == "D": self.r_xtend = self.r_xtend[:1] + "C"
2247
+ elif self.r_xtend[1] == "C": self.r_xtend = self.r_xtend[:1] + "U"
2248
+ elif self.r_xtend[1] == " ": self.r_xtend = self.r_xtend[:1] + "U"
2249
+ # ----------------------------------------------------------------- hjkl
2250
+ if (key == Qt.Key.Key_L):
2251
+ if ( len(parts_set) == 0):
2252
+ self.redcross[0] += 4
2253
+ elif (parts_set == {'Shift'}) :
2254
+ self.redcross[0] += 17
2255
+ elif (parts_set == {'Ctrl'}) :
2256
+ self.redcross[0] += 65
2257
+ elif (parts_set == {'Ctrl', 'Shift'}) :
2258
+ self.send_command( data={"right": "RIGHT"})
2259
+ if self.r_xtend[0] == "L": self.r_xtend = "C" + self.r_xtend[1:]
2260
+ elif self.r_xtend[0] == "C": self.r_xtend = "R" + self.r_xtend[1:]
2261
+ elif self.r_xtend[0] == " ": self.r_xtend = "R" + self.r_xtend[1:]
2262
+ # ----------------------------------------------------------------- v GREEN CROSS
2263
+ if (key == Qt.Key.Key_V):
2264
+ if ( len(parts_set) == 0):
2265
+ self.send_command( data= {"crosson": "CROSSON"} )
2266
+ elif (parts_set == {'Shift'}) :
2267
+ #self.send_command( data= {"expo05": "EXPO05"})
2268
+ pass
2269
+ elif (parts_set == {'Ctrl'}) :
2270
+ self.send_command( data= {"crossoff": "CROSSOFF"} )
2271
+ # ----------------------------------------------------------------- c RED CROSS
2272
+ if (key == Qt.Key.Key_C):
2273
+ if ( len(parts_set) == 0):
2274
+ self.flag_redcross = True# not self.flag_redcross
2275
+ print( "i... showinf red cross", self.flag_redcross)
2276
+ elif (parts_set == {'Shift'}) :
2277
+ self.flag_redcross = False# not self.flag_redcross
2278
+ pass
2279
+ elif (parts_set == {'Ctrl'}) :
2280
+ print( "i... reset position red cross")
2281
+ self.redcross = [0, 0]
2282
+ # ----------------------------------------------------------------- i integrate accumulate
2283
+ if (key == Qt.Key.Key_I):
2284
+ # 4.6GB / 1000 640x480
2285
+ if ( len(parts_set) == 0):
2286
+ if self.r_integrate < 8:
2287
+ if self.r_integrate < 1:
2288
+ self.r_integrate = 1
2289
+ self.r_integrate = self.r_integrate * 2
2290
+ elif self.r_integrate < 40:
2291
+ self.r_integrate = self.r_integrate + 4
2292
+ elif self.r_integrate < 100:
2293
+ self.r_integrate = self.r_integrate + 8
2294
+ else:
2295
+ self.r_integrate = self.r_integrate + 16
2296
+
2297
+ print("i... remote integrate to ", self.r_integrate)
2298
+ self.send_command( data= {"accum": "ACCUM", "accumtxt": int(self.r_integrate)})
2299
+ elif (parts_set == {'Shift'}) :
2300
+ if self.r_integrate >= 4: # I am not allowed to send 1 but I dont remember why
2301
+ self.r_integrate = int(self.r_integrate / 2)
2302
+ print("i... remote integrate to ", self.r_integrate)
2303
+ self.send_command( data= {"accum": "ACCUM", "accumtxt": int(self.r_integrate)})
2304
+ else:#as crtl
2305
+ self.r_integrate = 1
2306
+ print("i... remote integrate to 0 (not 1)")
2307
+ self.send_command( data= {"accum": "ACCUM", "accumtxt": 0})
2308
+ elif (parts_set == {'Ctrl'}) :
2309
+ self.r_integrate = 1
2310
+ self.send_command( data= {"accum": "ACCUM", "accumtxt": 0})
2311
+ # 0 would be a problem (locally???); but 1 is not sent!!! ; SENDING 0, checking@send_command
2312
+ elif (parts_set == {'Ctrl', 'Shift'}) :
2313
+ self.l_show_accum = not self.l_show_accum
2314
+
2315
+ # ----------------------------------------------------------------- b
2316
+ if (key == Qt.Key.Key_B):
2317
+ if ( len(parts_set) == 0):
2318
+ self.send_command( data= {"subbg": "SUBBG"})
2319
+ elif (parts_set == {'Shift'}) :
2320
+ self.send_command( data= {"savebg": "SAVEBG"})
2321
+ elif (parts_set == {'Ctrl'}) :
2322
+ pass
2323
+ # ----------------------------------------------------------------- b
2324
+ if (key == Qt.Key.Key_F):
2325
+ if ( len(parts_set) == 0):
2326
+ self.send_command( data= {"mixfg": "MIXFG"})
2327
+ elif (parts_set == {'Shift'}) :
2328
+ self.send_command( data= {"savefg": "SAVEFG"})
2329
+ elif (parts_set == {'Ctrl'}) :
2330
+ pass
2331
+
2332
+ # ----------------------------------------------------------------- r
2333
+ if (key == Qt.Key.Key_R):
2334
+ if ( len(parts_set) == 0):
2335
+ self.l_rotate += 1
2336
+ elif (parts_set == {'Shift'}) :
2337
+ self.l_rotate -= 1
2338
+ elif (parts_set == {'Ctrl'}) :
2339
+ self.l_rotate = 0
2340
+ # ----------------------------------------------------------------- 1
2341
+ if (key == Qt.Key.Key_1) or (key == ord("!") ):
2342
+ if ( len(parts_set) == 0):
2343
+ print("i... config 1 - recall")
2344
+ self.setup("r", 1)
2345
+ self.setup("a", 1)
2346
+ elif (parts_set == {'Shift'}) :
2347
+ print("i... config! 1 - save")
2348
+ print("D... r_xtend == ", self.r_xtend)
2349
+ self.setup("i", 1)
2350
+ print("D... r_xtend == ", self.r_xtend)
2351
+ print("D.... action write .......... ")
2352
+ self.setup("w", 1)
2353
+ print("D... r_xtend == ", self.r_xtend)
2354
+ elif (parts_set == {'Ctrl'}) :
2355
+ self.setup("q")
2356
+ # ----------------------------------------------------------------- 2
2357
+ if (key == Qt.Key.Key_2) or (key == ord("@") ):
2358
+ if ( len(parts_set) == 0):
2359
+ print("i... config 2 - recall")
2360
+ self.setup("r", 2)
2361
+ self.setup("a", 2)
2362
+ elif (parts_set == {'Shift'}) :
2363
+ print("i... config! 2 - save")
2364
+ self.setup("i", 2)
2365
+ self.setup("w", 2)
2366
+ elif (parts_set == {'Ctrl'}) :
2367
+ self.setup("q")
2368
+ # ----------------------------------------------------------------- 3
2369
+ if (key == Qt.Key.Key_3) or (key == ord("#") ):
2370
+ if ( len(parts_set) == 0):
2371
+ print("i... config 3 - recall")
2372
+ self.setup("r", 3)
2373
+ self.setup("a", 3)
2374
+ elif (parts_set == {'Shift'}) :
2375
+ print("i... config! 3 - save")
2376
+ self.setup("i", 3)
2377
+ self.setup("w", 3)
2378
+ elif (parts_set == {'Ctrl'}) :
2379
+ self.setup("q")
2380
+ # ----------------------------------------------------------------- 4
2381
+ if (key == Qt.Key.Key_4) or (key == ord("$") ):
2382
+ if ( len(parts_set) == 0):
2383
+ print("i... config 4 - recall")
2384
+ self.setup("r", 4)
2385
+ self.setup("a", 4)
2386
+ elif (parts_set == {'Shift'}) :
2387
+ print("i... config! 4 - save")
2388
+ self.setup("i", 4)
2389
+ self.setup("w", 4)
2390
+ elif (parts_set == {'Ctrl'}) :
2391
+ self.setup("q") # quit-resetall
2392
+
2393
+
2394
+ # ----------------------------------------------------------------- t tests whatever
2395
+ if (key == Qt.Key.Key_T):
2396
+ if ( len(parts_set) == 0):
2397
+ #self.send_command( data= {"gaint": "GAINT", "gaintxt": float(0.123)})
2398
+ #self.send_command( data= {"expot": "EXPOT", "expotxt": float(0.1)})
2399
+ pass
2400
+ elif (parts_set == {'Shift'}) :
2401
+ #self.send_command( data= {"gaint": "GAINT", "gaintxt": float(0.723)})
2402
+ #self.send_command( data= {"expot": "EXPOT", "expotxt": float(0.7)})
2403
+ pass
2404
+ elif (parts_set == {'Ctrl'}) :
2405
+ #self.send_command( data= {"expot": "EXPOT", "expotxt": float(-1.0)})
2406
+ pass
2407
+ # ----------------------------------------------------------------- l tests whatever
2408
+ if (key == Qt.Key.Key_T):
2409
+ if ( len(parts_set) == 0):
2410
+ # THIS IS in reallity SKIPPED IN send_command ....
2411
+ # also -1 means in remote - every image
2412
+ self.send_command( data= {"timelaps": "TIMELAPS" ,"timelaps_input": "1" })
2413
+ self.saving_laps = 1
2414
+ print(f"i... TimeLapse {fg.orange} 1 second or ACCUM {fg.default} ")
2415
+ pass
2416
+ elif (parts_set == {'Shift'}) :
2417
+ self.send_command( data= {"timelaps": "TIMELAPS" ,"timelaps_input": "10" })
2418
+ self.saving_laps = 10
2419
+ print(f"i... TimeLapse {fg.orange} 10 seconds {fg.default} ")
2420
+ pass
2421
+ elif (parts_set == {'Ctrl', 'Shift'}) :
2422
+ self.send_command( data= {"timelaps": "TIMELAPS" ,"timelaps_input": "60" })
2423
+ self.saving_laps = 60
2424
+ print(f"i... TimeLapse {fg.orange} 60 seconds {fg.default} ")
2425
+ pass
2426
+ elif (parts_set == {'Ctrl'}) :
2427
+ self.send_command( data= {"timelaps": "TIMELAPS" ,"timelaps_input": "0" })
2428
+ self.saving_laps = 0
2429
+ print(f"i... TimeLapse {fg.orange} OFF {fg.default} ")
2430
+ pass
2431
+
2432
+
2433
+
2434
+ # ##################################################################### konec ##
2435
+ else:
2436
+ # shift alt ctrl
2437
+ pass #print("b + ".join(parts))
2438
+
2439
+ if key == Qt.Key.Key_Escape or (key == Qt.Key.Key_Q):
2440
+ #print(chr(key))
2441
+ QApplication.quit()
2442
+
2443
+
2444
+
2445
+ @click.command()
2446
+ @click.argument('url', default="127.0.0.1")
2447
+ @click.option('-r', '--resolution', default="640x480", required=False, help='Resolution value')
2448
+ @click.option('-f', '--fourcc', default="YUYV", required=False, help='YUYV or MJPG')
2449
+ def handle_cli(url, resolution, fourcc):
2450
+ #app = QApplication() #
2451
+ app = QApplication(sys.argv) # NOT clear why argv here
2452
+ #url = None
2453
+ #if len(sys.argv)> 1:
2454
+ # url = sys.argv[1]
2455
+ #else:
2456
+ # url = "http://127.0.0.1:8000/video"
2457
+ url = guess_url(url)
2458
+ #if url is None:
2459
+ # sys.exit(1)
2460
+ widget = None
2461
+
2462
+ #if url is None:
2463
+ widget = StreamWidget(url, resolution=resolution, fourcc=fourcc)
2464
+ #else:
2465
+ # widget = StreamWidget(url, internet_not_device=True)
2466
+ widget.show()
2467
+ sys.exit(app.exec())
2468
+
2469
+
2470
+ # =====================================================================
2471
+ # MAIN ENTRY
2472
+ # ---------------------------------------------------------------------
2473
+ if __name__ == "__main__":
2474
+ handle_cli()