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.
- pogucam/explore_u24_uni.py +2474 -0
- {pogucam-0.1.2.dist-info → pogucam-0.1.3.dist-info}/METADATA +1 -1
- pogucam-0.1.3.dist-info/RECORD +6 -0
- pogucam-0.1.3.dist-info/entry_points.txt +2 -0
- pogucam-0.1.2.dist-info/RECORD +0 -5
- pogucam-0.1.2.dist-info/entry_points.txt +0 -2
- {pogucam-0.1.2.dist-info → pogucam-0.1.3.dist-info}/WHEEL +0 -0
@@ -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()
|