pogucam 0.1.10__py3-none-any.whl → 0.1.13__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 +309 -98
- pogucam/mqimr.py +61 -0
- pogucam/mqims.py +100 -0
- {pogucam-0.1.10.dist-info → pogucam-0.1.13.dist-info}/METADATA +2 -1
- pogucam-0.1.13.dist-info/RECORD +11 -0
- pogucam-0.1.10.dist-info/RECORD +0 -9
- {pogucam-0.1.10.dist-info → pogucam-0.1.13.dist-info}/WHEEL +0 -0
- {pogucam-0.1.10.dist-info → pogucam-0.1.13.dist-info}/entry_points.txt +0 -0
pogucam/explore_u24_uni.py
CHANGED
@@ -8,7 +8,7 @@
|
|
8
8
|
############################# ]
|
9
9
|
############################# ///
|
10
10
|
############################
|
11
|
-
"""
|
11
|
+
a = """
|
12
12
|
* I keep this on legacy .venv *** either --script install - or venv!
|
13
13
|
* from astropy.io import fits *** some fits in threading done *** ... ds9.si.edu viewer DS9
|
14
14
|
* NOT NOW **** Or store as hdf5 - files with metadata - and use HDFView ... one file
|
@@ -25,7 +25,7 @@ NEW - Fits saves nice, many CUBE , use like
|
|
25
25
|
uv run --with=napari-crop --with=napari-tabu ./explore_view_napari.py ~/DATA/core6a_video0_20250604_113217.25.fits
|
26
26
|
napari-tabu :((((
|
27
27
|
DS9 ... ~/Downloads/DS9/ds9 core6a_video0_20250604_185439.68.fits just GRAY,
|
28
|
-
wine /home/ojr/.wine/dosdevices/c:/Program
|
28
|
+
wine /home/ojr/.wine/dosdevices/c:/Program Files (x86)/AvisFV30/AvisFV.exe core6a_video0_20250604_185439.68.fits
|
29
29
|
#------------
|
30
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
31
|
CROSS, FG BG....
|
@@ -83,6 +83,17 @@ import math
|
|
83
83
|
|
84
84
|
from astropy import wcs
|
85
85
|
|
86
|
+
#------------------------------------ subscibe
|
87
|
+
import struct
|
88
|
+
import paho.mqtt.client as mqtt
|
89
|
+
import numpy as np
|
90
|
+
import datetime as dt
|
91
|
+
import cv2
|
92
|
+
import threading
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
|
86
97
|
istrip = 0
|
87
98
|
UPDATE_INTERVAL = 1
|
88
99
|
#FITS_CUBE_N = 400
|
@@ -157,7 +168,10 @@ def is_int(n):
|
|
157
168
|
|
158
169
|
def guess_url( inp ):
|
159
170
|
"""
|
160
|
-
interpret url
|
171
|
+
interpret url: http: or /dev/
|
172
|
+
- low integer== dev
|
173
|
+
- int 80+ is local port; http+ is clear;
|
174
|
+
- :1883/topic/var or :1883
|
161
175
|
"""
|
162
176
|
final = inp
|
163
177
|
ip = ""
|
@@ -166,7 +180,29 @@ def guess_url( inp ):
|
|
166
180
|
if inp is None:
|
167
181
|
print("X... no url")
|
168
182
|
return None
|
169
|
-
if
|
183
|
+
if inp[0] == ":":
|
184
|
+
port = "".join(inp[1:])
|
185
|
+
path = port.split("/")[1:]
|
186
|
+
path = "/".join(path)
|
187
|
+
port = port.split("/")[0]
|
188
|
+
print("D... port is evidently given in ", inp[1:], "; port:", port, "path:" , path)
|
189
|
+
if not is_int(port):
|
190
|
+
print("X... port not integer")
|
191
|
+
return None
|
192
|
+
port = int(port)
|
193
|
+
if port == 1883:
|
194
|
+
final = f"mqtt://127.0.0.1/{path}"
|
195
|
+
print("i... local mqtt ... ", final)
|
196
|
+
return final
|
197
|
+
else:
|
198
|
+
final = f"http://127.0.0.1:{port}/{path}"
|
199
|
+
print("i... local http ... ", final)
|
200
|
+
return final
|
201
|
+
#return inp
|
202
|
+
if inp.find("/dev/video") == 0:
|
203
|
+
print("D... no discussion, videodev:", inp)
|
204
|
+
return inp
|
205
|
+
if type(inp) is int:# dev # or port #
|
170
206
|
if int(inp) < 80:
|
171
207
|
print("D... maybe video device")
|
172
208
|
videostr = f"/dev/video{inp}"
|
@@ -183,65 +219,96 @@ def guess_url( inp ):
|
|
183
219
|
return final
|
184
220
|
print("X... IDK:")
|
185
221
|
return None
|
186
|
-
|
222
|
+
# clearly mqtt
|
223
|
+
if (inp.find("mqtt://") == 0):
|
224
|
+
print("i... clearly mqtt:", inp)
|
225
|
+
return final
|
226
|
+
# clearly http
|
187
227
|
if (inp.find("http://") == 0) or (inp.find("https://") == 0):
|
188
228
|
if (inp.find(":") > 0):
|
189
229
|
return final
|
190
230
|
else:
|
191
231
|
print("X... a problem, no port demanded")
|
192
232
|
return None
|
233
|
+
# something strange, less than any IP ... IDK
|
193
234
|
elif len(inp) < 7:
|
194
|
-
print("X... TOO short ip (can be 0 for
|
235
|
+
print("X... /ShoudntBeHere/ TOO short ip (can be 0 for video0):", inp)
|
195
236
|
if is_int(inp):
|
196
|
-
print("i... but
|
197
|
-
if int(inp) > 79 and int(inp) < 64000:
|
198
|
-
print("i... I put 127.0.0.1 address ")
|
237
|
+
print("i... /ShoudntBeHere/ but this is one number, possibly port number", inp)
|
238
|
+
if int(inp) > 79 and int(inp) < 64000 and int(inp) != 1883:
|
239
|
+
print("i... /ShoudntBeHere/I put 127.0.0.1 address ")
|
199
240
|
final = f"http://127.0.0.1:{inp}/video"
|
200
241
|
return final
|
242
|
+
elif int(inp) == 1883:
|
243
|
+
print("i... mqtt is evidently required. Topic (too short inp) is: ", path)
|
244
|
+
# if inp.find("/") > 0:
|
245
|
+
# port = inp.split("/")[]
|
246
|
+
# path = join("/").portplus.split("/")[1:]
|
247
|
+
if path == "":
|
248
|
+
print("i... topic will be by default image/raw8000")
|
249
|
+
path = "image/raw8000"
|
250
|
+
ip = "127.0.0.1"
|
251
|
+
final = f"mqtt://{ip}/{path}"
|
252
|
+
print(final)
|
253
|
+
return final
|
201
254
|
elif is_int(inp):
|
202
|
-
print("D... maybe video device")
|
255
|
+
print("D... /ShoudntBeHere/maybe video device")
|
203
256
|
videostr = f"/dev/video{inp}"
|
204
257
|
if os.path.exists( videostr):
|
205
|
-
print(f"i... {videostr} exists ")
|
258
|
+
print(f"i... /ShoudntBeHere/ {videostr} exists ")
|
206
259
|
return videostr
|
207
260
|
else:
|
208
|
-
print(f"i... {videostr} DOES NOT exist !!!!! ")
|
261
|
+
print(f"i... /ShoudntBeHere/ {videostr} DOES NOT exist !!!!! ")
|
209
262
|
return videostr
|
210
263
|
else:
|
211
|
-
print("X... BAD input", inp)
|
264
|
+
print("X... /ShoudntBeHere-maybe/ BAD input", inp)
|
265
|
+
return None
|
266
|
+
# address
|
212
267
|
elif inp.find(".") < 0:
|
213
|
-
print("X... no dots in address ", inp)
|
268
|
+
print("X... no dots in the address ", inp)
|
214
269
|
return None
|
215
270
|
elif len(inp.split(".")) < 3:
|
216
|
-
print("X... not IP4 address ", inp)
|
271
|
+
print("X... too few dots, not IP4 address ", inp)
|
217
272
|
return None
|
218
273
|
# ----------------------------------------------
|
219
274
|
digit = inp.split(".")
|
220
|
-
if is_int( digit[0]) and is_int(digit[1]) and is_int(digit[2]):
|
275
|
+
if is_int( digit[0]) and is_int(digit[1]) and is_int(digit[2]): # 3 digits and d:port
|
221
276
|
# DIGITS
|
222
277
|
if is_int(digit[3]):
|
223
|
-
print("
|
278
|
+
print("i... a bit of problem, no port demanded at 3rd digit", digit, " giving 8000")
|
224
279
|
ip = inp # back to 4 digits
|
225
280
|
port = "8000"
|
226
281
|
#return None
|
227
|
-
elif digit[3].find(":") <= 0:
|
228
|
-
print("X... a bit problem, no port demanded, giving 8000")
|
282
|
+
elif digit[3].find(":") <= 0: # if not a digit, maybe path??
|
283
|
+
print("X... /ShoudntBeHere-maybe/ a bit of problem, no port demanded, giving 8000")
|
229
284
|
port = "8000"
|
230
285
|
#return None
|
231
|
-
else:# port ok
|
286
|
+
else:# port ok-extract
|
232
287
|
ip = inp.split(":")[0]
|
233
288
|
portplus = inp.split(":")[1]
|
289
|
+
# extract path
|
234
290
|
if portplus.find("/") > 0:
|
235
291
|
port = portplus.split("/")[0]
|
236
|
-
|
292
|
+
pathlist = portplus.split("/")[1:]
|
293
|
+
path = "/".join(pathlist)
|
237
294
|
else:
|
238
295
|
port = portplus
|
239
|
-
|
296
|
+
# ---
|
240
297
|
if not is_int(port):
|
241
|
-
print("X... port is not a number", port)
|
298
|
+
print("X... port is not a number:", port)
|
242
299
|
return None
|
300
|
+
# port is OK now----------------------------
|
301
|
+
port = int(port)
|
302
|
+
if port == 1883:
|
303
|
+
print("i... mqtt is evidently required. Topic is: ", path)
|
304
|
+
if path == "":
|
305
|
+
print("i... topic will be by default image/raw8000")
|
306
|
+
path = "image/raw8000"
|
307
|
+
final = f"mqtt://{ip}:{port}/{path}"
|
308
|
+
print(final)
|
309
|
+
return final
|
243
310
|
if path == "":
|
244
|
-
print("
|
311
|
+
print("i... a bit problem, no path obtained. Giving /video , but this should probably change in future")
|
245
312
|
path = "video"
|
246
313
|
|
247
314
|
final = f"http://{ip}:{port}/{path}"
|
@@ -379,32 +446,42 @@ class StreamWidget(QLabel):
|
|
379
446
|
self.device = None
|
380
447
|
self.url = None
|
381
448
|
self.post_addr = None
|
382
|
-
|
383
|
-
|
449
|
+
self.internet_mqtt = False
|
450
|
+
self.mqtt_topic = "image/raw8000" # default topic
|
451
|
+
self.mqtt_subscribed = False
|
452
|
+
self.mqtt_client = None
|
453
|
+
self.mqtt_new_image = False # trafic light
|
454
|
+
# url is mqtt:// of http:// or /dev/
|
384
455
|
if url is None :
|
385
456
|
print("X... I cannot find a relevant source kind, but program should not be here...")
|
386
457
|
sys.exit(1)
|
387
458
|
if (url is not None) and is_int(url) and int(url) < 10:
|
388
|
-
|
389
|
-
#self.url = f"/dev/video{url}"
|
390
|
-
##self.device = f"/dev/video{url}"
|
391
|
-
##self.internet_not_device = False
|
392
|
-
##self.post_addr = None #self.url.replace("/video", "/cross")
|
393
|
-
print("X... should enver get here")
|
459
|
+
print("X... should never get here")
|
394
460
|
sys.exit(1)
|
461
|
+
#--------------------- decide if device or internet http:// ----
|
395
462
|
if (url is not None) and url.find("/dev/video") >= 0:
|
396
463
|
self.device = url
|
397
464
|
self.url = url#"local"
|
398
|
-
self.internet_not_device = False #
|
465
|
+
self.internet_not_device = False #
|
399
466
|
self.post_addr = None# self.url.replace("/video", "/cross")
|
400
467
|
if not os.path.exists( self.device):
|
401
468
|
print(f"i... {self.device} DOES NOT exists ")
|
402
|
-
elif (url is not None) and url.find("http://") >= 0:
|
469
|
+
elif (url is not None) and (url.find("http://") or url.find("http://")) >= 0:
|
403
470
|
self.url = url
|
404
|
-
self.internet_not_device = True
|
471
|
+
self.internet_not_device = True #
|
405
472
|
self.post_addr = self.url.replace("/video", "/cross")
|
473
|
+
|
474
|
+
elif (url is not None) and url.find("mqtt://") >= 0:
|
475
|
+
ipdeco = url.split("mqtt://")[-1] # remove protocol
|
476
|
+
self.url = ipdeco.split("/")[0] # jsut IP address for mqtt
|
477
|
+
self.mqtt_topic = ipdeco.split("/")[1:] # IP address
|
478
|
+
self.mqtt_topic = "/".join(self.mqtt_topic)
|
479
|
+
self.internet_not_device = True #
|
480
|
+
self.internet_mqtt = True #
|
481
|
+
self.post_addr = None#self.url.replace("/video", "/cross")
|
406
482
|
else:
|
407
483
|
print(f"X... {self.url} x {self.device} ")
|
484
|
+
#---------------------------------------------------------------
|
408
485
|
self.resolution = resolution
|
409
486
|
self.fourcc = fourcc
|
410
487
|
#self.internet_not_device = internet_not_device
|
@@ -431,7 +508,7 @@ class StreamWidget(QLabel):
|
|
431
508
|
self.bytex = b"" # stream
|
432
509
|
# ----------------------------------- initial frame -------------- was always STRIPS, now NONE: helsp w VCR ----
|
433
510
|
# with big colored strips
|
434
|
-
self.frame = None#np.zeros((self.height, self.width, 3), dtype=np.uint8)
|
511
|
+
self.frame = None # do COPY() to img # np.zeros((self.height, self.width, 3), dtype=np.uint8)
|
435
512
|
self.which_error = "" # no error, can be url http...
|
436
513
|
self.frames_to_fail_max = 5 # countdown to 0 and reset CAP
|
437
514
|
self.frames_to_fail = self.frames_to_fail_max # countdown to 0 and reset CAP
|
@@ -879,6 +956,8 @@ class StreamWidget(QLabel):
|
|
879
956
|
# ---------------------------
|
880
957
|
if selfimg is None:
|
881
958
|
return
|
959
|
+
|
960
|
+
#print(f"D... overtext {self.img.shape} ")
|
882
961
|
RXT = f"XT:{self.r_xtend}" # CC LU ... for remote ok
|
883
962
|
ZOO = f"ZOO:{self.zoomme:3.1f}"
|
884
963
|
ROO = f"ROT:{self.l_rotate:3.1f}"
|
@@ -896,6 +975,7 @@ class StreamWidget(QLabel):
|
|
896
975
|
total_size = sys.getsizeof(self.FABuffer)
|
897
976
|
cur_size = len(self.FABuffer)
|
898
977
|
max_size = self.FABuffer.get_max_frames()
|
978
|
+
total_size = sys.getsizeof(selfimg) * cur_size
|
899
979
|
|
900
980
|
#if self.accum_buffer is not None:
|
901
981
|
# total_size = sys.getsizeof(self.accum_buffer) + sum(sys.getsizeof(arr) for arr in self.accum_buffer)
|
@@ -930,7 +1010,7 @@ class StreamWidget(QLabel):
|
|
930
1010
|
position = ( selfimg.shape[1] - 20, selfimg.shape[0]-1 ) # 480 on x-axis
|
931
1011
|
selfimg = iprint(selfimg, f"SAVE", font=font, position=position,color_rgb=(0,0,255) ) # bgr
|
932
1012
|
self.img = selfimg
|
933
|
-
|
1013
|
+
|
934
1014
|
|
935
1015
|
|
936
1016
|
|
@@ -1172,28 +1252,6 @@ class StreamWidget(QLabel):
|
|
1172
1252
|
#
|
1173
1253
|
# --------------------------------------------------
|
1174
1254
|
|
1175
|
-
# def make_strips(self):
|
1176
|
-
# """
|
1177
|
-
# creates self.img and strips, increments istrip and resets it
|
1178
|
-
# """
|
1179
|
-
# global istrip
|
1180
|
-
# width, height = 640, 480
|
1181
|
-
# self.img = np.zeros((height, width, 3), dtype=np.uint8)
|
1182
|
-
# strip_height = 64
|
1183
|
-
# istrip += 1
|
1184
|
-
# if istrip > strip_height * 2:
|
1185
|
-
# istrip = 0
|
1186
|
-
|
1187
|
-
# if int(istrip / strip_height) % 2== 0:
|
1188
|
-
# self.img[0 :0+min(istrip, strip_height), :] = (55, 55, 0) # Red strip (BGR)
|
1189
|
-
# else:
|
1190
|
-
# self.img[0 :0+min(istrip, strip_height), :] = (55, 150, 0) # Red strip (BGR)
|
1191
|
-
# self.img[min(istrip, strip_height):2 * strip_height, :] = (55, 55, 0) # Red strip (BGR)
|
1192
|
-
|
1193
|
-
# # Draw red and green horizontal strips
|
1194
|
-
# for i in range(0, height, strip_height * 2):
|
1195
|
-
# self.img[i + istrip:i+strip_height+ istrip, :] = (55, 150, 0) # Red strip (BGR)
|
1196
|
-
# self.img[i+strip_height+ istrip:i+ istrip+strip_height*2, :] = (55, 55, 0) # Green strip
|
1197
1255
|
|
1198
1256
|
|
1199
1257
|
# ================================================================================
|
@@ -1251,7 +1309,7 @@ class StreamWidget(QLabel):
|
|
1251
1309
|
|
1252
1310
|
def update_image(self):
|
1253
1311
|
"""
|
1254
|
-
Stream Widget!!!!! self==stream technically takes img and put pixmap
|
1312
|
+
QT6 - Stream Widget!!!!! self==stream technically takes img and put pixmap
|
1255
1313
|
"""
|
1256
1314
|
|
1257
1315
|
if self.rgb_image is None:return # 1st contact may no exist
|
@@ -1388,6 +1446,11 @@ class StreamWidget(QLabel):
|
|
1388
1446
|
#cv2.waitKey(0)
|
1389
1447
|
#cv2.destroyAllWindows()
|
1390
1448
|
|
1449
|
+
|
1450
|
+
# ================================================================================
|
1451
|
+
# COPY FRAME TO IMG .................
|
1452
|
+
# --------------------------------------------------------------------------------
|
1453
|
+
|
1391
1454
|
def use_frame(self, stripes=False):
|
1392
1455
|
"""
|
1393
1456
|
put frame on tom of img .... ONLY THIS .....
|
@@ -1408,7 +1471,7 @@ class StreamWidget(QLabel):
|
|
1408
1471
|
# ___________________ vvv started, make gray strips _____________________________
|
1409
1472
|
ret_val = False
|
1410
1473
|
if True:# self.img.shape == self.frame.shape:
|
1411
|
-
self.img = self.frame
|
1474
|
+
self.img = self.frame.copy()
|
1412
1475
|
if stripes: # These are thin gray
|
1413
1476
|
#self.vcr_pal_style(["NO SIGNAL"])
|
1414
1477
|
for y in range(0, self.img.shape[0], 16):
|
@@ -1417,25 +1480,6 @@ class StreamWidget(QLabel):
|
|
1417
1480
|
#
|
1418
1481
|
return ret_val
|
1419
1482
|
|
1420
|
-
|
1421
|
-
# ==================================================
|
1422
|
-
# FETCHING THE IMAGE FROM VIDEODEV AND ALL LOCAL-ACTIONS ------------ ALSO INITIATING THE STREAM ------------
|
1423
|
-
# --------------------------------------------------
|
1424
|
-
|
1425
|
-
def fetch_and_update(self): # * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * *
|
1426
|
-
"""
|
1427
|
-
main operation with image:: fetch_and_update + update_only
|
1428
|
-
"""
|
1429
|
-
ret = None
|
1430
|
-
#print(f".... fau : {self.internet_not_device} ")
|
1431
|
-
if self.internet_not_device:
|
1432
|
-
ret = self.fetch_only()
|
1433
|
-
else:
|
1434
|
-
#resol = "640x480"
|
1435
|
-
#resol = "1920x1080"
|
1436
|
-
ret = self.capture_only(self.resolution)
|
1437
|
-
self.update_only(ret)
|
1438
|
-
|
1439
1483
|
# ================================================================================
|
1440
1484
|
# CLICK TRICK to recover resolution in CLI
|
1441
1485
|
# --------------------------------------------------------------------------------
|
@@ -1495,16 +1539,18 @@ class StreamWidget(QLabel):
|
|
1495
1539
|
if not ret:
|
1496
1540
|
return False
|
1497
1541
|
print(f" ... {self.frame_time} ; resolution /{self.frame.shape}/ .... ", end="")
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1542
|
+
# ADD ONE FRAME !!!!!!!!
|
1543
|
+
self.l_frame_num += 1
|
1544
|
+
#avg = self.frame # once per Nx it is AVG
|
1545
|
+
#self.img = avg # normally frame. but sometimes avg == really averaged img!
|
1546
|
+
self.img = self.frame.copy()
|
1501
1547
|
return True
|
1502
1548
|
|
1503
1549
|
|
1504
1550
|
|
1505
1551
|
|
1506
1552
|
# ==================================================
|
1507
|
-
# FETCHING THE IMAGE FROM VIDEODEV ------------ ALSO INITI
|
1553
|
+
# FETCHING THE IMAGE FROM internet VIDEODEV ------------ ALSO INITI
|
1508
1554
|
# --------------------------------------------------
|
1509
1555
|
|
1510
1556
|
def fetch_only(self):
|
@@ -1603,6 +1649,178 @@ class StreamWidget(QLabel):
|
|
1603
1649
|
return ret_val
|
1604
1650
|
|
1605
1651
|
|
1652
|
+
|
1653
|
+
# ==================================================
|
1654
|
+
# FETCHING THE IMAGE FROM ------------ MQTT
|
1655
|
+
# --------------------------------------------------
|
1656
|
+
|
1657
|
+
|
1658
|
+
|
1659
|
+
def decode_payload(self, data):
|
1660
|
+
"""
|
1661
|
+
MUST BE SAME AS SENDER
|
1662
|
+
"""
|
1663
|
+
header_size = struct.calcsize('!HHQddIfff') # SAME CODE 2x
|
1664
|
+
width, height, framenumber, timestamp_ts, recording_started_ts, _, exposition, gain, gamma = struct.unpack('!HHQddIfff', data[:header_size])
|
1665
|
+
image = np.frombuffer(data[header_size:], dtype=np.uint8).reshape((height, width, 3))
|
1666
|
+
timestamp = dt.datetime.fromtimestamp(timestamp_ts)
|
1667
|
+
recording_started = dt.datetime.fromtimestamp(recording_started_ts)
|
1668
|
+
return {
|
1669
|
+
'width': width,
|
1670
|
+
'height': height,
|
1671
|
+
'framenumber': framenumber,
|
1672
|
+
'timestamp': timestamp,
|
1673
|
+
'recording_started': recording_started,
|
1674
|
+
'exposition': exposition,
|
1675
|
+
'gain': gain,
|
1676
|
+
'gamma': gamma,
|
1677
|
+
'image': image
|
1678
|
+
}
|
1679
|
+
|
1680
|
+
|
1681
|
+
def on_message(self, client, userdata, msg):
|
1682
|
+
"""
|
1683
|
+
just copy content to frame and set flag
|
1684
|
+
"""
|
1685
|
+
data = msg.payload
|
1686
|
+
#width, height = struct.unpack('!HH', data[:4])
|
1687
|
+
#image = np.frombuffer(data[4:], dtype=np.uint8).reshape((height, width, 3))
|
1688
|
+
#####image = np.frombuffer(data, dtype=np.uint8).reshape((480, 640, 3))
|
1689
|
+
data_block = self.decode_payload(data)
|
1690
|
+
self.frame_num = data_block['framenumber']
|
1691
|
+
self.frame_time = str(data_block['timestamp'])[:-4]
|
1692
|
+
#print(f"i... mqtt image: shape={image.shape} time={dt.datetime.now()}", end="\r" )
|
1693
|
+
self.frame = data_block['image'].copy()
|
1694
|
+
self.mqtt_new_image = True
|
1695
|
+
#print( flush=True)
|
1696
|
+
#cv2.imshow("Received Image", image)
|
1697
|
+
#cv2.waitKey(10) # Needed to refresh window
|
1698
|
+
|
1699
|
+
def subscribe_only(self):
|
1700
|
+
"""
|
1701
|
+
called from from fetch_and_update() / returns BOOL / I want to read image via mqtt 1883
|
1702
|
+
"""
|
1703
|
+
if not self.mqtt_subscribed:
|
1704
|
+
#self.mqtt_broker = "10.10.104.17"
|
1705
|
+
#self.mqtt_topic = "image/raw"
|
1706
|
+
|
1707
|
+
self.mqtt_client = mqtt.Client()
|
1708
|
+
self.mqtt_client.on_message = self.on_message
|
1709
|
+
|
1710
|
+
self.mqtt_client.connect(self.url, 1883, 60) # 60 seconds?
|
1711
|
+
self.mqtt_client.subscribe(self.mqtt_topic)
|
1712
|
+
print("i... ... looping inside thread... on_message active")
|
1713
|
+
self.mqtt_subscribed = True
|
1714
|
+
# Start loop in a separate thread to handle network traffic
|
1715
|
+
thread = threading.Thread(target=self.mqtt_client.loop_start)
|
1716
|
+
thread.daemon = True
|
1717
|
+
thread.start()
|
1718
|
+
#self.mqtt_client.loop_forever()
|
1719
|
+
|
1720
|
+
else:# ==================== it is subscribed =========================
|
1721
|
+
# just loop-wait for a new image
|
1722
|
+
#while True:
|
1723
|
+
#print(self.mqtt_new_image)
|
1724
|
+
if self.mqtt_new_image: # ALL ACTION HERE - use_frame; franum++ ....
|
1725
|
+
self.mqtt_new_image = False
|
1726
|
+
self.use_frame() # i copy grame
|
1727
|
+
self.l_frame_num += 1
|
1728
|
+
self.error = False
|
1729
|
+
else:
|
1730
|
+
time.sleep(0.02)
|
1731
|
+
# return False
|
1732
|
+
#break
|
1733
|
+
#time.sleep(0.1)
|
1734
|
+
return True
|
1735
|
+
|
1736
|
+
|
1737
|
+
|
1738
|
+
# ==================================================
|
1739
|
+
# FETCHING THE IMAGE FROM VIDEODEV AND ALL LOCAL-ACTIONS ------------ ALSO INITIATING THE STREAM ------------
|
1740
|
+
#
|
1741
|
+
# I think the main function here is fetch_and_update #
|
1742
|
+
# - when IP, it does fetch, when not, it grabs image.
|
1743
|
+
# -
|
1744
|
+
#
|
1745
|
+
# --------------------------------------------------
|
1746
|
+
|
1747
|
+
def fetch_and_update(self): # * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * *
|
1748
|
+
"""
|
1749
|
+
main operation with image:: fetch_and_update + update_only
|
1750
|
+
"""
|
1751
|
+
ret = None
|
1752
|
+
#print(f".... fau : {self.internet_not_device} ")
|
1753
|
+
if self.internet_not_device:
|
1754
|
+
if self.internet_mqtt:
|
1755
|
+
ret = self.subscribe_only() # MQTT
|
1756
|
+
else:
|
1757
|
+
ret = self.fetch_only() # just normaly fetch
|
1758
|
+
else:
|
1759
|
+
#resol = "640x480"
|
1760
|
+
#resol = "1920x1080"
|
1761
|
+
ret = self.capture_only(self.resolution)
|
1762
|
+
self.update_only(ret)
|
1763
|
+
|
1764
|
+
|
1765
|
+
# #!/usr/bin/env python3
|
1766
|
+
# import struct
|
1767
|
+
# import paho.mqtt.client as mqtt
|
1768
|
+
# import numpy as np
|
1769
|
+
# import datetime as dt
|
1770
|
+
# import time
|
1771
|
+
|
1772
|
+
# def provide(width, height):
|
1773
|
+
# # Create colorful structured image with gradients and sinusoidal patterns
|
1774
|
+
|
1775
|
+
# x = np.linspace(0, 4 * np.pi, width)
|
1776
|
+
# y = np.linspace(0, 4 * np.pi, height)
|
1777
|
+
# X, Y = np.meshgrid(x, y)
|
1778
|
+
# r = ((np.sin(X) + 1) * 127).astype(np.uint8)
|
1779
|
+
# g = ((np.cos(Y) + 1) * 127).astype(np.uint8)
|
1780
|
+
# b = ((np.sin(X + Y) + 1) * 127).astype(np.uint8)
|
1781
|
+
|
1782
|
+
# # Randomize grid frequency and color amplitude
|
1783
|
+
# freq_x = np.random.uniform(2, 6)
|
1784
|
+
# freq_y = np.random.uniform(2, 6)
|
1785
|
+
# amp = np.random.uniform(80, 150)
|
1786
|
+
# x = np.linspace(0, freq_x * np.pi, width)
|
1787
|
+
# y = np.linspace(0, freq_y * np.pi, height)
|
1788
|
+
# X, Y = np.meshgrid(x, y)
|
1789
|
+
# r = ((np.sin(X) + 1) * amp / 2).clip(0, 255).astype(np.uint8 )
|
1790
|
+
# g = ((np.cos(Y) + 1) * amp / 2).clip(0, 255).astype(np.uint8)
|
1791
|
+
# b = ((np.sin(X + Y) + 1) * amp / 2).clip(0, 255).astype(np.uint8)
|
1792
|
+
# image = np.stack((r, g, b), axis=2)
|
1793
|
+
# # Add random noise element
|
1794
|
+
# #noise = np.random.randint(0, 50, (height, width, 3), dtype=np.uint8)
|
1795
|
+
# #image = np.clip(image + noise, 0, 255).astype(np.uint8)
|
1796
|
+
# return image
|
1797
|
+
|
1798
|
+
# broker = "127.0.0.1"
|
1799
|
+
# topic = "image/raw"
|
1800
|
+
|
1801
|
+
# client = mqtt.Client()
|
1802
|
+
# client.connect(broker, 1883, 10)
|
1803
|
+
|
1804
|
+
# for i in range(100):
|
1805
|
+
|
1806
|
+
# # Create dummy image
|
1807
|
+
# #image = np.random.randint(0, 256, (480, 640, 3), dtype=np.uint8)
|
1808
|
+
# height,width=1080,1920
|
1809
|
+
# height,width=480,640
|
1810
|
+
# header = struct.pack('!HH', width, height) # Network byte order
|
1811
|
+
# #image = np.random.randint(0, 256, (height, width, 3), dtype=np.uint8)
|
1812
|
+
# image=provide(width, height)
|
1813
|
+
# payload = header + image.tobytes()
|
1814
|
+
# # Send raw bytes
|
1815
|
+
# print(dt.datetime.now())
|
1816
|
+
# result=client.publish(topic, payload )
|
1817
|
+
# print(dt.datetime.now())
|
1818
|
+
# #result.wait_for_publish() # Ensure message is sent before continuing
|
1819
|
+
# #print(dt.datetime.now())
|
1820
|
+
# time.sleep(0.96)
|
1821
|
+
|
1822
|
+
# client.disconnect()
|
1823
|
+
|
1606
1824
|
##################################################################
|
1607
1825
|
# # m mmmm mm m m #
|
1608
1826
|
# m m mmmm mmm# mmm mm#mm mmm m" "m #"m # # #
|
@@ -1615,8 +1833,6 @@ class StreamWidget(QLabel):
|
|
1615
1833
|
|
1616
1834
|
|
1617
1835
|
|
1618
|
-
|
1619
|
-
|
1620
1836
|
# ================================================================================
|
1621
1837
|
# All the traNSFORMS and saves are here
|
1622
1838
|
# --------------------------------------------------------------------------------
|
@@ -1630,9 +1846,10 @@ class StreamWidget(QLabel):
|
|
1630
1846
|
# ___________________________ after this point - NO REFERENCE TO FRAME ***** only img *********************
|
1631
1847
|
# ___________________________________ whatever, RET VAL ------------RET VAL ------------RET VAL ------------
|
1632
1848
|
if ret_val and not self.error: # OK
|
1849
|
+
# IMG SHOULD BE HERE READY!!!!!!!!!!!!
|
1633
1850
|
|
1634
|
-
self.l_frame_num += 1
|
1635
|
-
#print(self.l_frame_num)
|
1851
|
+
#self.l_frame_num += 1 # NOT GOOD!!!!!!!!!!!!!!!
|
1852
|
+
#print("D... ... franum", self.l_frame_num)
|
1636
1853
|
|
1637
1854
|
# ------------------------- calculate bias to see lost frames count ----------------------
|
1638
1855
|
# no change to frame
|
@@ -1878,6 +2095,7 @@ class StreamWidget(QLabel):
|
|
1878
2095
|
|
1879
2096
|
#
|
1880
2097
|
else:# --- NO IMAGE CREATED ------------------------------- ... make the image gray ...
|
2098
|
+
print(f" ret_val {ret_val} ... self.error {self.error} ")
|
1881
2099
|
#print("D... 3")
|
1882
2100
|
# Extra override with some frame
|
1883
2101
|
self.img = cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)
|
@@ -2333,22 +2551,15 @@ class StreamWidget(QLabel):
|
|
2333
2551
|
@click.option('-r', '--resolution', default="640x480", required=False, help='Resolution value')
|
2334
2552
|
@click.option('-f', '--fourcc', default="YUYV", required=False, help='YUYV or MJPG')
|
2335
2553
|
def handle_cli(url, resolution, fourcc):
|
2554
|
+
"""
|
2555
|
+
commandline is solved here; guess url
|
2556
|
+
"""
|
2336
2557
|
#app = QApplication() #
|
2337
2558
|
app = QApplication(sys.argv) # NOT clear why argv here
|
2338
|
-
#url = None
|
2339
|
-
#if len(sys.argv)> 1:
|
2340
|
-
# url = sys.argv[1]
|
2341
|
-
#else:
|
2342
|
-
# url = "http://127.0.0.1:8000/video"
|
2343
2559
|
url = guess_url(url)
|
2344
|
-
#if url is None:
|
2345
|
-
# sys.exit(1)
|
2346
2560
|
widget = None
|
2347
2561
|
|
2348
|
-
#if url is None:
|
2349
2562
|
widget = StreamWidget(url, resolution=resolution, fourcc=fourcc)
|
2350
|
-
#else:
|
2351
|
-
# widget = StreamWidget(url, internet_not_device=True)
|
2352
2563
|
widget.show()
|
2353
2564
|
sys.exit(app.exec())
|
2354
2565
|
|
pogucam/mqimr.py
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
print("""
|
4
|
+
RECEIVE IMAGE
|
5
|
+
|
6
|
+
uv run --with paho-mqtt --with=numpy --with=opencv-python --script ./mqimr.py
|
7
|
+
|
8
|
+
show an image received from mqtt
|
9
|
+
""")
|
10
|
+
|
11
|
+
|
12
|
+
import struct
|
13
|
+
import paho.mqtt.client as mqtt
|
14
|
+
import numpy as np
|
15
|
+
import datetime as dt
|
16
|
+
import cv2
|
17
|
+
|
18
|
+
broker = "10.10.104.17"
|
19
|
+
broker = "127.0.0.1"
|
20
|
+
topic = "image/raw8000"
|
21
|
+
|
22
|
+
def decode_payload(data):
|
23
|
+
header_size = struct.calcsize('!HHQddIfff')
|
24
|
+
width, height, framenumber, timestamp_ts, recording_started_ts, _, exposition, gain, gamma = struct.unpack('!HHQddIfff', data[:header_size])
|
25
|
+
image = np.frombuffer(data[header_size:], dtype=np.uint8).reshape((height, width, 3))
|
26
|
+
timestamp = dt.datetime.fromtimestamp(timestamp_ts)
|
27
|
+
recording_started = dt.datetime.fromtimestamp(recording_started_ts)
|
28
|
+
return {
|
29
|
+
'width': width,
|
30
|
+
'height': height,
|
31
|
+
'framenumber': framenumber,
|
32
|
+
'timestamp': timestamp,
|
33
|
+
'recording_started': recording_started,
|
34
|
+
'exposition': exposition,
|
35
|
+
'gain': gain,
|
36
|
+
'gamma': gamma,
|
37
|
+
'image': image
|
38
|
+
}
|
39
|
+
|
40
|
+
|
41
|
+
def on_message(client, userdata, msg):
|
42
|
+
data = msg.payload
|
43
|
+
|
44
|
+
data_block = decode_payload(data)
|
45
|
+
image = data_block['image']
|
46
|
+
#
|
47
|
+
#width, height = struct.unpack('!HH', data[:4])
|
48
|
+
#image = np.frombuffer(data[4:], dtype=np.uint8).reshape((height, width, 3))
|
49
|
+
#####image = np.frombuffer(data, dtype=np.uint8).reshape((480, 640, 3))
|
50
|
+
print("Received image shape:", image.shape, dt.datetime.now() )
|
51
|
+
print( flush=True)
|
52
|
+
|
53
|
+
cv2.imshow("Received Image", image)
|
54
|
+
cv2.waitKey(10) # Needed to refresh window
|
55
|
+
|
56
|
+
client = mqtt.Client()
|
57
|
+
client.on_message = on_message
|
58
|
+
|
59
|
+
client.connect(broker, 1883, 60)
|
60
|
+
client.subscribe(topic)
|
61
|
+
client.loop_forever()
|
pogucam/mqims.py
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
print(""" SEND IMAGE via MQTT
|
4
|
+
|
5
|
+
uv run --with paho-mqtt --with=numpy --with=opencv-python --script ./mqims.py
|
6
|
+
|
7
|
+
""")
|
8
|
+
# connect to local MQTT
|
9
|
+
broker = "127.0.0.1"
|
10
|
+
topic = "image/raw8000"
|
11
|
+
|
12
|
+
import struct
|
13
|
+
import paho.mqtt.client as mqtt
|
14
|
+
import numpy as np
|
15
|
+
import datetime as dt
|
16
|
+
import time
|
17
|
+
|
18
|
+
def create_payload(image, framenumber, timestamp, recording_started, exposition, gain, gamma):
|
19
|
+
height, width = image.shape[:2]
|
20
|
+
header = struct.pack(
|
21
|
+
'!HHQddIfff',
|
22
|
+
width,
|
23
|
+
height,
|
24
|
+
int(framenumber),
|
25
|
+
timestamp.timestamp(),
|
26
|
+
recording_started.timestamp(),
|
27
|
+
0, # padding for alignment if needed
|
28
|
+
float(exposition),
|
29
|
+
float(gain),
|
30
|
+
float(gamma)
|
31
|
+
)
|
32
|
+
payload = header + image.tobytes()
|
33
|
+
return payload
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
def provide_image(width, height):
|
38
|
+
# Add random noise element
|
39
|
+
image = np.zeros((480, 640, 3), dtype=np.uint8)
|
40
|
+
#return image # 1ms for just black
|
41
|
+
noise = np.random.randint(0, 50, (height, width, 3), dtype=np.uint8)
|
42
|
+
#image = np.clip(image + noise, 0, 255).astype(np.uint8)
|
43
|
+
#return image # 6.6ms image+noise
|
44
|
+
#---------------------------------------
|
45
|
+
# Create colorful structured image with gradients and sinusoidal patterns
|
46
|
+
x = np.linspace(0, 4 * np.pi, width)
|
47
|
+
y = np.linspace(0, 4 * np.pi, height)
|
48
|
+
X, Y = np.meshgrid(x, y)
|
49
|
+
r = ((np.sin(X) + 1) * 127).astype(np.uint8)
|
50
|
+
g = ((np.cos(Y) + 1) * 127).astype(np.uint8)
|
51
|
+
b = ((np.sin(X + Y) + 1) * 127).astype(np.uint8)
|
52
|
+
|
53
|
+
# Randomize grid frequency and color amplitude
|
54
|
+
freq_x = np.random.uniform(2, 6)
|
55
|
+
freq_y = np.random.uniform(2, 6)
|
56
|
+
amp = np.random.uniform(80, 150)
|
57
|
+
x = np.linspace(0, freq_x * np.pi, width)
|
58
|
+
y = np.linspace(0, freq_y * np.pi, height)
|
59
|
+
X, Y = np.meshgrid(x, y)
|
60
|
+
r = ((np.sin(X) + 1) * amp / 2).clip(0, 255).astype(np.uint8 )
|
61
|
+
g = ((np.cos(Y) + 1) * amp / 2).clip(0, 255).astype(np.uint8)
|
62
|
+
b = ((np.sin(X + Y) + 1) * amp / 2).clip(0, 255).astype(np.uint8)
|
63
|
+
image = np.stack((r, g, b), axis=2)
|
64
|
+
image = np.clip(image + noise, 0, 255).astype(np.uint8)
|
65
|
+
return image # alltogether 35 ms
|
66
|
+
|
67
|
+
|
68
|
+
# *********************************************************************
|
69
|
+
|
70
|
+
client = mqtt.Client()
|
71
|
+
client.connect(broker, 1883, 10)
|
72
|
+
|
73
|
+
recording_started = dt.datetime.now()
|
74
|
+
framenumber = 0
|
75
|
+
for i in range(1000):
|
76
|
+
# Create dummy image
|
77
|
+
#image = np.random.randint(0, 256, (480, 640, 3), dtype=np.uint8)
|
78
|
+
height,width=1080,1920
|
79
|
+
#
|
80
|
+
height,width=480,640
|
81
|
+
#header = struct.pack('!HH', width, height) # Network byte order
|
82
|
+
timestamp = dt.datetime.now()
|
83
|
+
framenumber += 1
|
84
|
+
#recording_started ... also timestamp
|
85
|
+
image=provide_image(width, height) # create image elsewhere
|
86
|
+
exposition, gain, gamma = 0.5, 0.5, 0.5
|
87
|
+
payload = create_payload(image, framenumber, timestamp, recording_started, exposition, gain, gamma)
|
88
|
+
#payload = header + image.tobytes()
|
89
|
+
# Send raw bytes
|
90
|
+
result=client.publish(topic, payload )
|
91
|
+
print(timestamp, end="\r")
|
92
|
+
#result.wait_for_publish() # Ensure message is sent before continuing
|
93
|
+
#print(dt.datetime.now())
|
94
|
+
|
95
|
+
#
|
96
|
+
time.sleep(0.095)
|
97
|
+
|
98
|
+
delta = dt.datetime.now() - recording_started
|
99
|
+
print(f"{delta}, {delta.total_seconds() / 1000} per frame ")
|
100
|
+
client.disconnect()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pogucam
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.13
|
4
4
|
Summary: Add your description here
|
5
5
|
Author-email: jaromrax <jaromrax@gmail.com>
|
6
6
|
Requires-Python: >=3.12
|
@@ -9,6 +9,7 @@ Requires-Dist: click>=8.2.1
|
|
9
9
|
Requires-Dist: console>=0.9911
|
10
10
|
Requires-Dist: numpy>=2.3.0
|
11
11
|
Requires-Dist: opencv-python>=4.11.0.86
|
12
|
+
Requires-Dist: paho-mqtt>=2.1.0
|
12
13
|
Requires-Dist: pillow>=11.2.1
|
13
14
|
Requires-Dist: pyqt6>=6.9.1
|
14
15
|
Requires-Dist: requests>=2.32.4
|
@@ -0,0 +1,11 @@
|
|
1
|
+
pogucam/__init__.py,sha256=Iij7VvXCrFPMtTia41mQ7LxFLaulf_fD5cb-AyjpUo0,53
|
2
|
+
pogucam/buffers.py,sha256=1JLkuenkHoA-K-uZAlNV7chHQDZLrspgT5_XOY-E-34,6692
|
3
|
+
pogucam/explore_u24_uni.py,sha256=F9HBpR1Q8Xsyn3dDQh053g3iHMFAG4O6YLC--vX1-Og,120508
|
4
|
+
pogucam/installation.md,sha256=8qspiLlYjEBx5CedRfBU7Mm0A2pz0lfAnaupZyBm5Eo,128
|
5
|
+
pogucam/mqimr.py,sha256=f48gTXng5vM-1RiNPXSA-IvAc3y6WMStbvfQ8rUV7l0,1727
|
6
|
+
pogucam/mqims.py,sha256=_G8AdfrbTrkIm2MOsq3tFOBYpiD4o58JUIvnZt0Sm7A,3293
|
7
|
+
pogucam/text_write.py,sha256=hyRyA1M5z-pda963T-k0i8fvvAlv1p3YBTZtYNdOeoE,19304
|
8
|
+
pogucam-0.1.13.dist-info/METADATA,sha256=A1K14WlN1eWHPFlFo-uCVi-VnLJkkezI0WY82GMJn7M,499
|
9
|
+
pogucam-0.1.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
10
|
+
pogucam-0.1.13.dist-info/entry_points.txt,sha256=-97N0LsXIR8h0rJMzIMuNeNwuY8LvPYPTqnXsuAnVsM,63
|
11
|
+
pogucam-0.1.13.dist-info/RECORD,,
|
pogucam-0.1.10.dist-info/RECORD
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
pogucam/__init__.py,sha256=Iij7VvXCrFPMtTia41mQ7LxFLaulf_fD5cb-AyjpUo0,53
|
2
|
-
pogucam/buffers.py,sha256=1JLkuenkHoA-K-uZAlNV7chHQDZLrspgT5_XOY-E-34,6692
|
3
|
-
pogucam/explore_u24_uni.py,sha256=_vyNd_dIpIAKuvqL7st01QX-2pjeRljZ2MEAZxM9lBk,112075
|
4
|
-
pogucam/installation.md,sha256=8qspiLlYjEBx5CedRfBU7Mm0A2pz0lfAnaupZyBm5Eo,128
|
5
|
-
pogucam/text_write.py,sha256=hyRyA1M5z-pda963T-k0i8fvvAlv1p3YBTZtYNdOeoE,19304
|
6
|
-
pogucam-0.1.10.dist-info/METADATA,sha256=-EXEmOf1UXog_njQALCJ-LbZ5edhDC5YXenGmclimuk,467
|
7
|
-
pogucam-0.1.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
8
|
-
pogucam-0.1.10.dist-info/entry_points.txt,sha256=-97N0LsXIR8h0rJMzIMuNeNwuY8LvPYPTqnXsuAnVsM,63
|
9
|
-
pogucam-0.1.10.dist-info/RECORD,,
|
File without changes
|
File without changes
|