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.
@@ -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\ Files\ \(x86\)/AvisFV30/AvisFV.exe core6a_video0_20250604_185439.68.fits
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 like http or /dev/
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 type(inp) is int:
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 viceo0):", inp)
235
+ print("X... /ShoudntBeHere/ TOO short ip (can be 0 for video0):", inp)
195
236
  if is_int(inp):
196
- print("i... but is is one number, possibly port number", inp)
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("X... a bit problem, no port demanded at 3rd digit", digit, " giving 8000")
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
- path = join("/").portplus.split("/")[1:]
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("X... a bit problem, no path. Giving /video")
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
- ######self.internet_not_device = None
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
- ##just preparing for the next 2
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 # override
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
- #self.img = iprint(self.img, str(overtext), font="p7", position=position,color_rgb=(0,255,0) )
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
- avg = self.frame # once per Nx it is AVG
1500
- self.img = avg # normally frame. but sometimes avg == really averaged img!
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.10
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,,
@@ -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,,