def c__activate(self, n_buffer: int = None, image_dimensions: tuple = None, shmem_name: str = None): # if not defined, use default values # if n_buffer is None: n_buffer = self.n_buffer # if image_dimensions is None: image_dimensions = self.image_dimensions # if shmem_name is None: shmem_name = self.shmem_name self.logger.debug("c__activate") self.listening = True # self.image_dimensions = image_dimensions self.client = ShmemRGBClient( name=shmem_name, n_ringbuffer=n_buffer, # size of ring buffer width=image_dimensions[0], height=image_dimensions[1], # client timeouts if nothing has been received in 1000 milliseconds mstimeout=int(self.timeout * 1000), verbose=self.shmem_verbose) self.shmem_name = shmem_name self.n_buffer = n_buffer self.image_dimensions = image_dimensions self.postActivate_()
def run(self): self.client = ShmemRGBClient( name=self.shmem_name, n_ringbuffer=self.shmem_n_buffer, width=self.width, height=self.height, # client timeouts if nothing has been received in 1000 milliseconds mstimeout=1000, verbose=False) while self.loop: """ print("Idling") time.sleep(1) """ #""" index, meta = self.client.pullFrameThread( ) # releases Python GIL while waiting for a frame if (index is None): # print(self.pre, "VideoShmemThread: client timed out..") pass else: # print(self.pre, "VideoShmemThread: client index, w, h =", index, meta.width, meta.height) data = self.client.shmem_list[index] img = data.reshape((meta.height, meta.width, 3)) pixmap = numpy2QPixmap(img) self.signals.pixmap.emit(pixmap) # """ print(self.pre, "exit")
def run(self): self.client = ShmemRGBClient( name=self.shmem_name, n_ringbuffer=self.shmem_n_buffer, width=self.width, height=self.height, # client timeouts if nothing has been received in 1000 milliseconds mstimeout=1000, # mstimeout=100, verbose=False) while self.loop: """ print("Idling") time.sleep(1) """ # """ # print("pullFrameThread>") index, meta = self.client.pullFrameThread( # index, meta = self.client.pullFrame( ) # releases Python GIL while waiting for a frame # print("<pullFrameThread") if (index is None): # print(self.pre, "VideoShmemThread: client timed out..") pass else: # print(self.pre, "VideoShmemThread: client index, w, h =", index, meta.width, meta.height) data = self.client.shmem_list[index] # print(data[0:10]) self.img = data.copy().reshape((meta.height, meta.width, 3)) # print(img[0:10]) """WARNING - reshape does not necessarily create a copy of the data, but uses memview instead - imagine that a memview is passed donwstreams, where it eventually goes to another python thread - ..but in that pullFrameThread call above, we have released the GIL, so it migh happen that the cpp code is modifying the data while the downstream python thread is accessing it simultaneously and - ..crassssh """ """DEBUG: test without actually sending the object into Qt infrastructure # which one of these two lines is the problem? pixmap = numpy2QPixmap(img) self.signals.pixmap.emit(pixmap) """ # send numpy array into the signal/slot system # instead of QPixmap..? #""" self.signals.image.emit(self.img) #""" # could also just send the # index of the shmem array # & do everything at the canvas widget # # 1) 12h test without pixmap or signal # 1b) use here self.pixmap instead of pixmap, in order to keep the reference # https://wiki.python.org/moin/PyQt/Threading%2C_Signals_and_Slots # 2) 12h test sending the img # 3) 12h test sending the index only # # """ print(self.pre, "exit")
def preRun_(self): """Create the shared memory client after fork """ super().preRun_() self.client = ShmemRGBClient( name=self.shmem_name, n_ringbuffer=self.n_buffer, # size of ring buffer width=self.image_dimensions[0], height=self.image_dimensions[1], mstimeout= 1000, # client timeouts if nothing has been received in 1000 milliseconds verbose=False)
def create_client_(self): self.client = ShmemRGBClient( name=self.shmem_name, n_ringbuffer=self.n_buffer, # size of ring buffer width=self.image_dimensions[0], height=self.image_dimensions[1], # client timeouts if nothing has been received in 1000 milliseconds mstimeout=1000, verbose=False) self.postCreateClient_()
def c__registerClient(self, n_buffer: int = None, image_dimensions: tuple = None, shmem_name: str = None, ipc_index: int = None): """Shared mem info is given. Now we can create the shmem client There can be several shmem clients """ self.logger.debug("c__registerClient") event_fd, pipe = singleton.ipc.get2(ipc_index) singleton.ipc.wait( ipc_index) # wait till the shmem server has been created # this flag is controlled by QShmemClientProcess.c__setMasterProcess singleton.ipc.clear(ipc_index) shmem_client = ShmemRGBClient( name=shmem_name, n_ringbuffer=n_buffer, # size of ring buffer width=image_dimensions[0], height=image_dimensions[1], # client timeouts if nothing has been received in 1000 milliseconds mstimeout=1000, verbose=False) shmem_client.useEventFd(event_fd) fd = event_fd.getFd() client = self.Client(fd=fd, pipe=pipe, shmem_client=shmem_client) self.clients[ipc_index] = client self.clients_by_fd[fd] = client self.logger.debug("c__registerClient: fd=%s", fd) self.rlis.append(fd) if len(self.clients) == 1: self.logger.debug("c__registerClient: first client registered") self.firstClientRegistered_() self.logger.debug("c__registerClient: number of clients is %s", len(self.clients))
def create_client_(self): print( "inside create_client of multiprocess \n shmem name is {} for process number " .format(self.shmem_name)) self.client = ShmemRGBClient( name=self.shmem_name, n_ringbuffer=self.n_buffer, # size of ring buffer width=self.image_dimensions[0], height=self.image_dimensions[1], # client timeouts if nothing has been received in 1000 milliseconds mstimeout=1000, verbose=False) self.postCreateClient_()
def activate_(self, n_buffer, image_dimensions, shmem_name): """Shared mem info is given. Now we can create the shmem client """ self.active = True self.image_dimensions = image_dimensions self.client = ShmemRGBClient( name=shmem_name, n_ringbuffer=n_buffer, # size of ring buffer width=image_dimensions[0], height=image_dimensions[1], # client timeouts if nothing has been received in 1000 milliseconds mstimeout=1000, verbose=False) self.postActivate_()
def c__activateRGB24Client(self, name=None, n_ringbuffer=None, width=None, height=None, ipc_index=None ): """This will activate a shared memory client that reads RGB24 frames from shared memory libValkka c++ side (as defined in your filterchain) """ print("c__activateRGB24Client called with", name, n_ringbuffer, width, height) client = ShmemRGBClient( name=name, n_ringbuffer=n_ringbuffer, width=width, height=height, mstimeout=self.mstimeout, verbose=False ) eventfd = getEventFd(ipc_index) client.useEventFd(eventfd) # do not forget! # let's get a posix file descriptor, i.e. a plain integer: fd = eventfd.getFd() self.client_by_fd[fd] = client
The parameters used both in the server side (above) and on the client side (below) **must be exactly the same** and the client program should be started *after* the server program (and while the server is running). Otherwise undefined behaviour will occur. The used shmem_name(s) should be same in both server and client, but different for another server/client pair. <rtf>""" from valkka.api2 import ShmemRGBClient width =1920//4 height =1080//4 shmem_name ="lesson_4" # This identifies posix shared memory - must be same as in the server side shmem_buffers =10 # Size of the shmem ringbuffer client=ShmemRGBClient( name =shmem_name, n_ringbuffer =shmem_buffers, width =width, height =height, mstimeout =1000, # client timeouts if nothing has been received in 1000 milliseconds verbose =False ) """<rtf> The *mstimeout* defines the semaphore timeout in milliseconds, i.e. the time when the client returns even if no frame was received: <rtf>""" while True: index, meta = client.pullFrame() # index, meta = client.pullFrameThread() # alternative method for multithreading if index is None: print("timeout") else: data = client.shmem_list[index][0:meta.size] print("data : ",data[0:min(10,meta.size)])
class QShmemProcess(QMultiProcess): """A multiprocess with Qt signals and reading RGB images from shared memory. Shared memory client is instantiated on demand (by calling activate) """ timeout = 1.0 class Signals(QtCore.QObject): pong = QtCore.Signal(object) # demo outgoing signal # **** define here backend methods that correspond to incoming slots # **** def c__ping(self, message=""): print("c__ping:", message) self.send_out__(MessageObject("pong", lis=[1, 2, 3])) def c__activate(self, n_buffer: int = None, image_dimensions: tuple = None, shmem_name: str = None): # if not defined, use default values # if n_buffer is None: n_buffer = self.n_buffer # if image_dimensions is None: image_dimensions = self.image_dimensions # if shmem_name is None: shmem_name = self.shmem_name self.logger.debug("c__activate") self.listening = True # self.image_dimensions = image_dimensions self.client = ShmemRGBClient( name=shmem_name, n_ringbuffer=n_buffer, # size of ring buffer width=image_dimensions[0], height=image_dimensions[1], # client timeouts if nothing has been received in 1000 milliseconds mstimeout=int(self.timeout * 1000), verbose=self.shmem_verbose) self.shmem_name = shmem_name self.n_buffer = n_buffer self.image_dimensions = image_dimensions self.postActivate_() def c__deactivate(self): """Init shmem variables to None """ self.logger.debug("c__deactivate") self.preDeactivate_() self.listening = False # self.image_dimensions = None self.client = None # shared memory client created when activate is called # **** parameter_defs = { # "n_buffer" : (int, 10), # "image_dimensions" : (tuple, (1920//4, 1080//4)), # "image_dimensions" : tuple, "shmem_verbose": (bool, False), # "shmem_name" : None } def __init__(self, name="QShmemProcess", **kwargs): super().__init__(name) parameterInitCheck(QShmemProcess.parameter_defs, kwargs, self) """ if self.shmem_name is None: self.shmem_name = "valkkashmemclient"+str(id(self)) else: assert(isinstance(self.shmem_name, str)) self.shmem_name_default = self.shmem_name """ def preRun_(self): self.logger.debug("preRun_") self.c__deactivate() # init variables def postRun_(self): """Clear shmem variables """ self.c__deactivate() def run(self): self.preRun_() while self.loop: if self.listening: self.cycle_() self.readPipes__(timeout=0) # timeout = 0 == just poll else: self.readPipes__(timeout=self.timeout) # timeout of 1 sec self.postRun_() # indicate front end qt thread to exit self.back_pipe.send(None) self.logger.debug("bye!") def cycle_(self): """Receives frames from the shmem client and does something with them Typically launch qt signals """ index, meta = self.client.pullFrame() if (index is None): self.logger.debug("Client timed out..") return self.logger.debug("Client index = %s", index) if meta.size < 1: return data = self.client.shmem_list[index][0:meta.size] img = data.reshape((meta.height, meta.width, 3)) """ # WARNING: the x-server doesn't like this, i.e., we're creating a window from a separate python multiprocess, so the program will crash print(self.pre,"Visualizing with OpenCV") cv2.imshow("openCV_window",img) cv2.waitKey(1) """ self.logger.debug("got frame %s", img.shape) # res=self.analyzer(img) # does something .. returns something .. def postActivate_(self): """Whatever you need to do after creating the shmem client. Overwrite in child classes """ pass def preDeactivate_(self): """Whatever you need to do prior to deactivating the shmem client. Overwrite in child classes """ pass # *** frontend *** def activate(self, **kwargs): # save values to front-end also: self.shmem_name = kwargs["shmem_name"] self.n_buffer = kwargs["n_buffer"] self.image_dimensions = kwargs["image_dimensions"] # this sets the values at back-end: self.sendMessageToBack(MessageObject("activate", **kwargs)) def deactivate(self): self.sendMessageToBack(MessageObject("deactivate")) def ping(self, message=""): """Test call """ self.sendMessageToBack(MessageObject("ping", message=message))
class VideoShmemThread(QtCore.QThread): """A Thread that creates a Valkka shmem client, reads frames from it & fires them as qt signals Connect VideoShmemThread to SimpleVideoWidget.set_pixmap_slot """ class Signals(QtCore.QObject): pixmap = QtCore.Signal(object) exit = QtCore.Signal() def __init__(self, shmem_name: str, shmem_n_buffer: int, width: int, height: int, verbose=False): super().__init__() self.pre = "VideoShmemThread: " self.shmem_name = shmem_name self.shmem_n_buffer = shmem_n_buffer self.width = width self.height = height self.verbose = verbose self.loop = True self.signals = self.Signals() self.signals.exit.connect(self.exit_slot_) def run(self): self.client = ShmemRGBClient( name=self.shmem_name, n_ringbuffer=self.shmem_n_buffer, width=self.width, height=self.height, # client timeouts if nothing has been received in 1000 milliseconds mstimeout=1000, verbose=False) while self.loop: """ print("Idling") time.sleep(1) """ #""" index, meta = self.client.pullFrameThread( ) # releases Python GIL while waiting for a frame if (index is None): # print(self.pre, "VideoShmemThread: client timed out..") pass else: # print(self.pre, "VideoShmemThread: client index, w, h =", index, meta.width, meta.height) data = self.client.shmem_list[index] img = data.reshape((meta.height, meta.width, 3)) pixmap = numpy2QPixmap(img) self.signals.pixmap.emit(pixmap) # """ print(self.pre, "exit") def exit_slot_(self): self.loop = False def stop(self): self.requestStop() self.waitStop() def requestStop(self): self.signals.exit.emit() def waitStop(self): self.wait()
class QValkkaShmemProcess(ValkkaProcess): """A multiprocess with Qt signals and reading RGB images from shared memory """ incoming_signal_defs = { # each key corresponds to a front- and backend methods "test_": { "test_int": int, "test_str": str }, "stop_": [], "ping_": { "message": str } } outgoing_signal_defs = {"pong_o": {"message": str}} # For each outgoing signal, create a Qt signal with the same name. The # frontend Qt thread will read processes communication pipe and emit these # signals. class Signals(QtCore.QObject): # pong_o =QtCore.pyqtSignal(object) pong_o = QtCore.Signal(object) parameter_defs = { "n_buffer": (int, 10), "image_dimensions": tuple, "shmem_name": str, "verbose": (bool, False) } def __init__(self, name, affinity=-1, **kwargs): super().__init__(name, affinity, **kwargs) self.signals = self.Signals() parameterInitCheck(QValkkaShmemProcess.parameter_defs, kwargs, self) typeCheck(self.image_dimensions[0], int) typeCheck(self.image_dimensions[1], int) def report(self, *args): if (self.verbose): print(self.pre, *args) def preRun_(self): """Create the shared memory client immediately after fork """ self.report("preRun_") super().preRun_() self.client = ShmemRGBClient( name=self.shmem_name, n_ringbuffer=self.n_buffer, # size of ring buffer width=self.image_dimensions[0], height=self.image_dimensions[1], # client timeouts if nothing has been received in 1000 milliseconds mstimeout=1000, verbose=False) def cycle_(self): index, isize = self.client.pull() if (index is None): print(self.pre, "Client timed out..") else: print(self.pre, "Client index, size =", index, isize) data = self.client.shmem_list[index] img = data.reshape( (self.image_dimensions[1], self.image_dimensions[0], 3)) """ # WARNING: the x-server doesn't like this, i.e., we're creating a window from a separate python multiprocess, so the program will crash print(self.pre,"Visualizing with OpenCV") cv2.imshow("openCV_window",img) cv2.waitKey(1) """ print(self.pre, ">>>", data[0:10]) # res=self.analyzer(img) # does something .. returns something .. # *** backend methods corresponding to incoming signals *** def stop_(self): self.running = False def test_(self, test_int=0, test_str="nada"): print(self.pre, "test_ signal received with", test_int, test_str) def ping_(self, message="nada"): print(self.pre, "At backend: ping_ received", message, "sending it back to front") self.sendSignal_(name="pong_o", message=message) # ** frontend methods launching incoming signals def stop(self): self.sendSignal(name="stop_") def test(self, **kwargs): dictionaryCheck(self.incoming_signal_defs["test_"], kwargs) kwargs["name"] = "test_" self.sendSignal(**kwargs) def ping(self, **kwargs): dictionaryCheck(self.incoming_signal_defs["ping_"], kwargs) kwargs["name"] = "ping_" self.sendSignal(**kwargs) # ** frontend methods handling received outgoing signals *** def pong_o(self, message="nada"): print(self.pre, "At frontend: pong got message", message) ns = Namespace() ns.message = message self.signals.pong_o.emit(ns)