Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. Delay of picture refreshing increases and suddenly not responding.
Forum Updated to NodeBB v4.3 + New Features

Delay of picture refreshing increases and suddenly not responding.

Scheduled Pinned Locked Moved Solved Qt for Python
11 Posts 4 Posters 688 Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • K Offline
    K Offline
    Kaninchen
    wrote on last edited by Kaninchen
    #1

    Hello everyone,

    my target is to show two camera videos synchronically. As mentioned in many post, I need to use Threads to actualize the window. When I ran the code below, I can see an increase of delay between camera image and shown image, and suddenly the window is not responding. After a few seconds, the window closed itself. I don't really understand the reason and it would be very nice if someone can help me.

    Update thread(problem with print)

    class UpdateThread(QThread):
        
        changePixmap = pyqtSignal(list)
        
        def __init__(self,gen:Union[Generator,callable],size:QSize,window:QWidget):
            super().__init__()
            self.gen = gen
            self.mode = isinstance(gen,Generator)
            self.size = size
            self.stop_flag = False
            self.window = window
            
        def get_next(self):
            if self.mode:
                return next(self.gen)
            else:
                return (self.gen)()
            
        def run(self) -> None:
            while not self.stop_flag:
                ims = self.get_next()
                res = []
                for i in range(len(ims)):
                    res.append(self.process_im(ims[i]))
                self.changePixmap.emit(res)
                print('"',end='')
                
                
        def process_im(self,im:np.ndarray):
            if isinstance(im,QImage):
                return im
            h,w,ch = im.shape
            im = QImage(im,w,h,ch*w,QImage.Format_RGB888)
            im = im.scaled(self.size/20*9, Qt.KeepAspectRatio)
            return im
        
        def get_latest(self):
            return self.latest
        
        def stop(self):
            self.stop_flag = True
    

    class App, the whole controller

    # using PyQt5
    class App:
        
            def __init__(self, video_save_path:str, image_save_path:str,cam_num:int,warning:bool=False):
                self.cam_num = cam_num
                self.video_path = video_save_path
                self.image_save_path = image_save_path
                self.use_warning = warning
                self.PCB_FLAG = False
                self.SAVER_FLAG = False
                self.TI_STARTED = False
                self.stop_flag = False
                self.init_camera()
                self.init_grabber_and_saver(cfg.MAX_LENGTH,self.video_path,cfg.FPS,cfg.IMAGE_SIZE)
                self.init_pcb_detector()
                self.init_gui()
        
            def init_grabber_and_saver(self,cache_len:int,video_save_path:str,fps:Union[int,float],image_size:Union[list,tuple,np.ndarray]):
                if self.gas is not None:
                    del self.gas
                self.set_video_path(video_save_path)
                video_name = [f'cam{i+1}-'+datetime.now().strftime("%Y%m%d,%H-%M") + '.mp4' for i in range(self.cam_num)]
                self.gas = GrabberAndSaver(self.gen,cache_len,self.cam_num,cfg.IMAGE_SHAPE,
                                           [cv2.VideoWriter(self.video_path + video_name[i],cv2.VideoWriter_fourcc(*'mp4v'),fps,image_size) for i in range(self.cam_num)])
                self.SAVER_FLAG = True
                self.start_origin()
        
            def init_camera(self):
                self.cam = Camera(self.cam_num)
                self.gen = self.cam.generate_image_all()
                self.gas = None
                print('###############################camera opened#############################')
        
            def init_gui(self):
                self.app = QApplication(sys.argv)
                self.window_size = self.app.screens()[0].availableSize()
                self.gui = VideoDisplay()
                self.gui.init_all_close(self.close_all)
                self.up_thread = UpdateThread(self.gas.get_latest,QSize(*cfg.IMAGE_SIZE),self.gui.wd)
                self.up_thread.changePixmap.connect(self.gui.wd.setImage)
                self.up_thread.start()
                self.gui.wd.button_connect(None,None,{0:self.pcb_detect,1:self.TI_detect})
                self.gui.show()
                print('########################gui initialization completed#####################')
        
            def init_pcb_detector(self):
                self.pcb = YOLOv7Detector(cfg.YOLOV7_PCB,(cfg.INPUT_SIZE_PCB,cfg.INPUT_SIZE_PCB),cfg.IOU_PCB,cfg.SCORE_PCB)
        
            def init_TI_detector(self):
                if not self.PCB_FLAG:
                    raise ExecutionOrderError('PCB Detection or camera calibration must be executed first.')
                moore = [Moore(cfg.MAX_LENGTH,cfg.NUM_CLASS_TIME,cfg.TOLERANCE,cfg.FPS) for _ in range(len(self.box)*self.cam_num)]
                self.ti = TIDetection(cfg.YOLOV7_TI,cfg.INPUT_SIZE_TI,cfg.IOU_TI,cfg.SCORE_TI,moore,self.image_save_path,self.warning if self.use_warning else None)
                print('########################ti initialization completed######################')
        
            def pcb_detect(self):
                ims = self.gas.get_latest()
                for im in ims:
                    self.box,score,_,_ = self.pcb.predict(im)
                    if self.box.shape[-1] == 0:
                        print("No PCB detected. Please try again with another camera direction. If this warning was shown several times, this program can't work with the pcb.")
                        if self.cam_num==2:
                            self.box = np.array([[[0,0,1440,1080]],[[0,0,1440,1080]]],dtype=int)# for tests
                        elif self.cam_num ==1:
                            self.box = np.array([[[0,0,1440,1080]]],dtype=int)# for tests
                        self.PCB_FLAG = True
                        return
                    self.PCB_FLAG = True
                    image.draw_box(Image.fromarray(im),self.box,score).show()
        
            def TI_detect(self):
                if self.TI_STARTED:
                    self.ti.stop()
                    self.TI_STARTED = False
                else:
                    self.start_TI_detect()
                    self.TI_STARTED = True
        
            def start_TI_detect(self):
                if not self.SAVER_FLAG:
                    raise ExecutionOrderError('Resource pool and video writer must be initialized before starting TI detection.')
                if hasattr(self,'pcb'):
                    del self.pcb
                self.init_TI_detector()
                self.ti_thread = Thread(target=self.ti.proceed,args=[self.box,None,self.gas.get_latest])
                self.ti_thread.start()
        
            def start_origin(self):
                self.grabber = Thread(target=self.gas.proceed)
                self.grabber.start()
        
            def warning(self):
                Msg(cfg.MSG_TITLE,cfg.MSG_TEXT,cfg.SOUND_FILE)
        
            def set_video_path(self, video_path:str):
                self.video_path = video_path if video_path.endswith(('/','\\')) else video_path + '/'
        
            def close_all(self):
                # clr = {0:'r',1:'g'}
                # for i in range(len(self.ti.times)):
                #     time_test = [self.ti.times[i][j+1]-self.ti.times[i][j] for j in range(len(self.ti.times[i])-1)]
                #     print(time_test)
                #     if len(time_test)>0:
                #         print(f'TI-processing average time needed:{sum(time_test)/len(time_test)}')
                #     plt.plot(time_test[1:],clr[i])
                # plt.savefig('test.jpg')
                self.stop_flag = True
                if hasattr(self,'ti'):
                    self.ti.stop()
                self.gas.stop()
    
            
    

    all gui classes except UpdateThread

    class VideoDisplay(QMainWindow):
        
        def __init__(self) -> None:
            super().__init__()
            self.setStyleSheet("background-color: #fff5f6;")
            self.setWindowTitle("Original Camera Video")
            self.wd = VideoWindow()
            self.setCentralWidget(self.wd)
            self.move(QPoint(60,20))
            
        def init_all_close(self,func:Callable):
            self.func = func
            
        def closeEvent(self, a0: QCloseEvent) -> None:
            self.func()
            a0.accept()
    
    class VideoWindow(QWidget):
        
        def __init__(self):
            super().__init__()
            
            self.video1 = QLabel(self)
            self.video2 = QLabel(self)
            self.gbox = QGridLayout()
            self.vbox = QVBoxLayout()
            self.b1 = QPushButton('detect PCB',self)
            self.b2 = QPushButton('start/stop',self)
            self.vbox.addWidget(self.b1)
            self.vbox.addWidget(self.b2)
            self.gbox.addWidget(self.video1,0,0,1,1)
            self.gbox.addLayout(self.vbox,0,1,1,2)
            self.gbox.addWidget(self.video2,1,1,2,2)
            self.setLayout(self.gbox)
            self.widget_dict = {
                0:self.video1,
                1:self.video2
            }
            self.button_dict = {
                0:self.b1,
                1:self.b2,
            }
            
        def button_connect(self,idx:int,func:Callable,container:dict=None):
            if container:
                for idx,func in container.items():
                    self.button_dict[idx].clicked.connect(func)
            else:
                self.button_dict[idx].clicked.connect(func)
           
        @pyqtSlot(list)     
        def setImage(self, image):
            for i in range(len(image)):
                self.widget_dict[i].setPixmap(QPixmap.fromImage(image[i]))
    

    class TI, proceed() is used by thread

    class TIDetection(YOLOv7Detector):
        
        def __init__(self, model: str, input_size: int, iou: float, score: float, automat:Union[Moore,list,tuple], image_save:str, warn:callable) -> None:
            super().__init__(model, input_size, iou, score)
            self.automat = automat if isinstance(automat,Iterable) else [automat]
            self.warn = warn
            self.res_detect = 0
            if image_save and not image_save.endswith(('/','\\')):
                image_save += '/'
            self.im_path = image_save
            self.pre = datetime.now()
            self.stop_flag = False
            self.times = [[],[]]
            
        def proceed_one(self, im:np.ndarray,au_idx:tuple):
            self.now = datetime.now()
            box,score,color,max_cls = self.predict(im)
            im = Image.fromarray(im).convert('RGB')
            if self.im_path and (self.now-self.pre).seconds>10 and max_cls > 1:
                im_name = self.im_path + self.now.strftime("%H-%M-%S")+'.jpg'
                self.pre = self.now
            else:
                im_name = None
            
            im = image.draw_box(im,box,score,color,im_name)
            ti = self._update(max_cls,au_idx)
            if ti == 4:
                self._warn()
            
            return im
                
        def proceed(self,pos:np.ndarray,gen:Generator=None, func: callable=None):
            self.times[0].append(time.time())
            self.times[1].append(time.time())
            cycle = 0
            if gen:
                while not self.stop_flag:
                    ims = next(gen)
                    for i in range(len(pos)):
                        im = ims[i]
                        boxes = pos[i]
                        for j in range(len(boxes)):
                            pcb = image.box_image(im,boxes[j])
                            self.proceed_one(pcb,i*len(boxes)+j)
                        self.times[i].append(time.time()-self.times[i][-1])
                    #print(self.times)
                    cycle += 1
                    if cycle>100:
                        break
            elif func:
                while not self.stop_flag:
                    ims = func()
                    #print(pos)
                    for i in range(len(pos)):
                        im = ims[i]
                        boxes = pos[i]
                        #print(boxes)
                        for j in range(len(boxes)):
                            pcb = image.box_image(im,boxes[j])
                            self.proceed_one(pcb,i*len(boxes)+j)
                            #print('proceeded')
                        self.times[i].append(time.time())
                        print(f'time:{time.time()},cycle{cycle}')
                    #print(self.times)
                    cycle += 1
                    if cycle>100:
                        break
            del self.model
            print('ti deleted')
                    
        def one(self,pos:np.ndarray,im:np.ndarray):
            for j in range(len(pos)):
                pcb = image.box_image(im,pos[j])
                self.proceed_one(pcb,0)
                
        def set_image_save_dir(self,image_save:str):
            self.im_path = image_save
            
        def _update(self, value:int, au_idx:int):
            return self.automat[au_idx].update(value)
        
        def get_automat_latest_condition(self,au_idx:int):
            return self.automat[au_idx].get_latest()
            
        def _warn(self):
            if not self.warn:
                return
            (self.warn)()
            
        def stop(self):
            self.stop_flag = True
    

    class GrabberAndSaver, grabs images from cameras and pushing them in a ringbuffer. And all images will be saved to a video. Method proceed is called by thread.

    class GrabberAndSaver:
        
        cache = None
        ptr_latest = 0
        ptr_earliest = 0
        
        def __init__(self,gen: Generator,cache_len:int,cam_num:int,im_shape:Union[list,tuple,np.ndarray],video_writer:Union[list,tuple]) -> None:
            self.gen = gen
            self.cache = np.zeros((cache_len,cam_num,*im_shape),dtype = np.uint8)
            self.saver = Saver(video_writer)
            self.stop_flag = False
            
        def proceed(self):
            while not self.stop_flag:
                im = next(self.gen)
                #self.saver.write(im[...,::-1])
                self._push(im)
                #print(f'thread res cache updated')
                
        def _push(self,value:Any):
            for i in range(len(value)):
                self.cache[self.ptr_latest,i] = value[i]
            if self.ptr_latest == len(self.cache) - 1:
                self.ptr_latest = 0
            else:
                self.ptr_latest += 1
            
        def get_latest(self):
            return self.cache[self.ptr_latest-1]
            
        def get_all(self):
            self.ptr_earliest += 1
            return self.cache[self.ptr_earliest-1]
        
        def stop(self):
            self.stop_flag = True
            self.saver.stop()
    

    By the way, I saw a huge improvement by updating directly in thread instead of using slots(as commented).Why does the program behaves so? Thanks for helping!!!

    JonBJ 1 Reply Last reply
    0
    • K Kaninchen

      Hello everyone,

      my target is to show two camera videos synchronically. As mentioned in many post, I need to use Threads to actualize the window. When I ran the code below, I can see an increase of delay between camera image and shown image, and suddenly the window is not responding. After a few seconds, the window closed itself. I don't really understand the reason and it would be very nice if someone can help me.

      Update thread(problem with print)

      class UpdateThread(QThread):
          
          changePixmap = pyqtSignal(list)
          
          def __init__(self,gen:Union[Generator,callable],size:QSize,window:QWidget):
              super().__init__()
              self.gen = gen
              self.mode = isinstance(gen,Generator)
              self.size = size
              self.stop_flag = False
              self.window = window
              
          def get_next(self):
              if self.mode:
                  return next(self.gen)
              else:
                  return (self.gen)()
              
          def run(self) -> None:
              while not self.stop_flag:
                  ims = self.get_next()
                  res = []
                  for i in range(len(ims)):
                      res.append(self.process_im(ims[i]))
                  self.changePixmap.emit(res)
                  print('"',end='')
                  
                  
          def process_im(self,im:np.ndarray):
              if isinstance(im,QImage):
                  return im
              h,w,ch = im.shape
              im = QImage(im,w,h,ch*w,QImage.Format_RGB888)
              im = im.scaled(self.size/20*9, Qt.KeepAspectRatio)
              return im
          
          def get_latest(self):
              return self.latest
          
          def stop(self):
              self.stop_flag = True
      

      class App, the whole controller

      # using PyQt5
      class App:
          
              def __init__(self, video_save_path:str, image_save_path:str,cam_num:int,warning:bool=False):
                  self.cam_num = cam_num
                  self.video_path = video_save_path
                  self.image_save_path = image_save_path
                  self.use_warning = warning
                  self.PCB_FLAG = False
                  self.SAVER_FLAG = False
                  self.TI_STARTED = False
                  self.stop_flag = False
                  self.init_camera()
                  self.init_grabber_and_saver(cfg.MAX_LENGTH,self.video_path,cfg.FPS,cfg.IMAGE_SIZE)
                  self.init_pcb_detector()
                  self.init_gui()
          
              def init_grabber_and_saver(self,cache_len:int,video_save_path:str,fps:Union[int,float],image_size:Union[list,tuple,np.ndarray]):
                  if self.gas is not None:
                      del self.gas
                  self.set_video_path(video_save_path)
                  video_name = [f'cam{i+1}-'+datetime.now().strftime("%Y%m%d,%H-%M") + '.mp4' for i in range(self.cam_num)]
                  self.gas = GrabberAndSaver(self.gen,cache_len,self.cam_num,cfg.IMAGE_SHAPE,
                                             [cv2.VideoWriter(self.video_path + video_name[i],cv2.VideoWriter_fourcc(*'mp4v'),fps,image_size) for i in range(self.cam_num)])
                  self.SAVER_FLAG = True
                  self.start_origin()
          
              def init_camera(self):
                  self.cam = Camera(self.cam_num)
                  self.gen = self.cam.generate_image_all()
                  self.gas = None
                  print('###############################camera opened#############################')
          
              def init_gui(self):
                  self.app = QApplication(sys.argv)
                  self.window_size = self.app.screens()[0].availableSize()
                  self.gui = VideoDisplay()
                  self.gui.init_all_close(self.close_all)
                  self.up_thread = UpdateThread(self.gas.get_latest,QSize(*cfg.IMAGE_SIZE),self.gui.wd)
                  self.up_thread.changePixmap.connect(self.gui.wd.setImage)
                  self.up_thread.start()
                  self.gui.wd.button_connect(None,None,{0:self.pcb_detect,1:self.TI_detect})
                  self.gui.show()
                  print('########################gui initialization completed#####################')
          
              def init_pcb_detector(self):
                  self.pcb = YOLOv7Detector(cfg.YOLOV7_PCB,(cfg.INPUT_SIZE_PCB,cfg.INPUT_SIZE_PCB),cfg.IOU_PCB,cfg.SCORE_PCB)
          
              def init_TI_detector(self):
                  if not self.PCB_FLAG:
                      raise ExecutionOrderError('PCB Detection or camera calibration must be executed first.')
                  moore = [Moore(cfg.MAX_LENGTH,cfg.NUM_CLASS_TIME,cfg.TOLERANCE,cfg.FPS) for _ in range(len(self.box)*self.cam_num)]
                  self.ti = TIDetection(cfg.YOLOV7_TI,cfg.INPUT_SIZE_TI,cfg.IOU_TI,cfg.SCORE_TI,moore,self.image_save_path,self.warning if self.use_warning else None)
                  print('########################ti initialization completed######################')
          
              def pcb_detect(self):
                  ims = self.gas.get_latest()
                  for im in ims:
                      self.box,score,_,_ = self.pcb.predict(im)
                      if self.box.shape[-1] == 0:
                          print("No PCB detected. Please try again with another camera direction. If this warning was shown several times, this program can't work with the pcb.")
                          if self.cam_num==2:
                              self.box = np.array([[[0,0,1440,1080]],[[0,0,1440,1080]]],dtype=int)# for tests
                          elif self.cam_num ==1:
                              self.box = np.array([[[0,0,1440,1080]]],dtype=int)# for tests
                          self.PCB_FLAG = True
                          return
                      self.PCB_FLAG = True
                      image.draw_box(Image.fromarray(im),self.box,score).show()
          
              def TI_detect(self):
                  if self.TI_STARTED:
                      self.ti.stop()
                      self.TI_STARTED = False
                  else:
                      self.start_TI_detect()
                      self.TI_STARTED = True
          
              def start_TI_detect(self):
                  if not self.SAVER_FLAG:
                      raise ExecutionOrderError('Resource pool and video writer must be initialized before starting TI detection.')
                  if hasattr(self,'pcb'):
                      del self.pcb
                  self.init_TI_detector()
                  self.ti_thread = Thread(target=self.ti.proceed,args=[self.box,None,self.gas.get_latest])
                  self.ti_thread.start()
          
              def start_origin(self):
                  self.grabber = Thread(target=self.gas.proceed)
                  self.grabber.start()
          
              def warning(self):
                  Msg(cfg.MSG_TITLE,cfg.MSG_TEXT,cfg.SOUND_FILE)
          
              def set_video_path(self, video_path:str):
                  self.video_path = video_path if video_path.endswith(('/','\\')) else video_path + '/'
          
              def close_all(self):
                  # clr = {0:'r',1:'g'}
                  # for i in range(len(self.ti.times)):
                  #     time_test = [self.ti.times[i][j+1]-self.ti.times[i][j] for j in range(len(self.ti.times[i])-1)]
                  #     print(time_test)
                  #     if len(time_test)>0:
                  #         print(f'TI-processing average time needed:{sum(time_test)/len(time_test)}')
                  #     plt.plot(time_test[1:],clr[i])
                  # plt.savefig('test.jpg')
                  self.stop_flag = True
                  if hasattr(self,'ti'):
                      self.ti.stop()
                  self.gas.stop()
      
              
      

      all gui classes except UpdateThread

      class VideoDisplay(QMainWindow):
          
          def __init__(self) -> None:
              super().__init__()
              self.setStyleSheet("background-color: #fff5f6;")
              self.setWindowTitle("Original Camera Video")
              self.wd = VideoWindow()
              self.setCentralWidget(self.wd)
              self.move(QPoint(60,20))
              
          def init_all_close(self,func:Callable):
              self.func = func
              
          def closeEvent(self, a0: QCloseEvent) -> None:
              self.func()
              a0.accept()
      
      class VideoWindow(QWidget):
          
          def __init__(self):
              super().__init__()
              
              self.video1 = QLabel(self)
              self.video2 = QLabel(self)
              self.gbox = QGridLayout()
              self.vbox = QVBoxLayout()
              self.b1 = QPushButton('detect PCB',self)
              self.b2 = QPushButton('start/stop',self)
              self.vbox.addWidget(self.b1)
              self.vbox.addWidget(self.b2)
              self.gbox.addWidget(self.video1,0,0,1,1)
              self.gbox.addLayout(self.vbox,0,1,1,2)
              self.gbox.addWidget(self.video2,1,1,2,2)
              self.setLayout(self.gbox)
              self.widget_dict = {
                  0:self.video1,
                  1:self.video2
              }
              self.button_dict = {
                  0:self.b1,
                  1:self.b2,
              }
              
          def button_connect(self,idx:int,func:Callable,container:dict=None):
              if container:
                  for idx,func in container.items():
                      self.button_dict[idx].clicked.connect(func)
              else:
                  self.button_dict[idx].clicked.connect(func)
             
          @pyqtSlot(list)     
          def setImage(self, image):
              for i in range(len(image)):
                  self.widget_dict[i].setPixmap(QPixmap.fromImage(image[i]))
      

      class TI, proceed() is used by thread

      class TIDetection(YOLOv7Detector):
          
          def __init__(self, model: str, input_size: int, iou: float, score: float, automat:Union[Moore,list,tuple], image_save:str, warn:callable) -> None:
              super().__init__(model, input_size, iou, score)
              self.automat = automat if isinstance(automat,Iterable) else [automat]
              self.warn = warn
              self.res_detect = 0
              if image_save and not image_save.endswith(('/','\\')):
                  image_save += '/'
              self.im_path = image_save
              self.pre = datetime.now()
              self.stop_flag = False
              self.times = [[],[]]
              
          def proceed_one(self, im:np.ndarray,au_idx:tuple):
              self.now = datetime.now()
              box,score,color,max_cls = self.predict(im)
              im = Image.fromarray(im).convert('RGB')
              if self.im_path and (self.now-self.pre).seconds>10 and max_cls > 1:
                  im_name = self.im_path + self.now.strftime("%H-%M-%S")+'.jpg'
                  self.pre = self.now
              else:
                  im_name = None
              
              im = image.draw_box(im,box,score,color,im_name)
              ti = self._update(max_cls,au_idx)
              if ti == 4:
                  self._warn()
              
              return im
                  
          def proceed(self,pos:np.ndarray,gen:Generator=None, func: callable=None):
              self.times[0].append(time.time())
              self.times[1].append(time.time())
              cycle = 0
              if gen:
                  while not self.stop_flag:
                      ims = next(gen)
                      for i in range(len(pos)):
                          im = ims[i]
                          boxes = pos[i]
                          for j in range(len(boxes)):
                              pcb = image.box_image(im,boxes[j])
                              self.proceed_one(pcb,i*len(boxes)+j)
                          self.times[i].append(time.time()-self.times[i][-1])
                      #print(self.times)
                      cycle += 1
                      if cycle>100:
                          break
              elif func:
                  while not self.stop_flag:
                      ims = func()
                      #print(pos)
                      for i in range(len(pos)):
                          im = ims[i]
                          boxes = pos[i]
                          #print(boxes)
                          for j in range(len(boxes)):
                              pcb = image.box_image(im,boxes[j])
                              self.proceed_one(pcb,i*len(boxes)+j)
                              #print('proceeded')
                          self.times[i].append(time.time())
                          print(f'time:{time.time()},cycle{cycle}')
                      #print(self.times)
                      cycle += 1
                      if cycle>100:
                          break
              del self.model
              print('ti deleted')
                      
          def one(self,pos:np.ndarray,im:np.ndarray):
              for j in range(len(pos)):
                  pcb = image.box_image(im,pos[j])
                  self.proceed_one(pcb,0)
                  
          def set_image_save_dir(self,image_save:str):
              self.im_path = image_save
              
          def _update(self, value:int, au_idx:int):
              return self.automat[au_idx].update(value)
          
          def get_automat_latest_condition(self,au_idx:int):
              return self.automat[au_idx].get_latest()
              
          def _warn(self):
              if not self.warn:
                  return
              (self.warn)()
              
          def stop(self):
              self.stop_flag = True
      

      class GrabberAndSaver, grabs images from cameras and pushing them in a ringbuffer. And all images will be saved to a video. Method proceed is called by thread.

      class GrabberAndSaver:
          
          cache = None
          ptr_latest = 0
          ptr_earliest = 0
          
          def __init__(self,gen: Generator,cache_len:int,cam_num:int,im_shape:Union[list,tuple,np.ndarray],video_writer:Union[list,tuple]) -> None:
              self.gen = gen
              self.cache = np.zeros((cache_len,cam_num,*im_shape),dtype = np.uint8)
              self.saver = Saver(video_writer)
              self.stop_flag = False
              
          def proceed(self):
              while not self.stop_flag:
                  im = next(self.gen)
                  #self.saver.write(im[...,::-1])
                  self._push(im)
                  #print(f'thread res cache updated')
                  
          def _push(self,value:Any):
              for i in range(len(value)):
                  self.cache[self.ptr_latest,i] = value[i]
              if self.ptr_latest == len(self.cache) - 1:
                  self.ptr_latest = 0
              else:
                  self.ptr_latest += 1
              
          def get_latest(self):
              return self.cache[self.ptr_latest-1]
              
          def get_all(self):
              self.ptr_earliest += 1
              return self.cache[self.ptr_earliest-1]
          
          def stop(self):
              self.stop_flag = True
              self.saver.stop()
      

      By the way, I saw a huge improvement by updating directly in thread instead of using slots(as commented).Why does the program behaves so? Thanks for helping!!!

      JonBJ Offline
      JonBJ Offline
      JonB
      wrote on last edited by
      #2

      @Kaninchen
      You cannot/must not access any UI element, such as a widget, in a thread in Qt. No matter how much you would like to, you can't.

      Your code passes a window:QWidget to the thread, and that goes self.window.setImage(res) (naughty you are calling that on a QWidget, I think you're getting that from VideoWindow...). Which does something to the widget, like set its pixmap. And you can't do that. So at some point your code goes wrong.....

      1 Reply Last reply
      0
      • K Offline
        K Offline
        Kaninchen
        wrote on last edited by Kaninchen
        #3

        Alright, so I have to use signal and slots as codes commented. In this case, the performance is even worse, about 70% of all time not responding. Should this be my programming error or full usage of hardware resource? The values in the system monitor is totally normal. By the way, this is my first time programming frontend. And I have changed the codes so that the whole process can be seen.

        K 1 Reply Last reply
        0
        • K Kaninchen

          Alright, so I have to use signal and slots as codes commented. In this case, the performance is even worse, about 70% of all time not responding. Should this be my programming error or full usage of hardware resource? The values in the system monitor is totally normal. By the way, this is my first time programming frontend. And I have changed the codes so that the whole process can be seen.

          K Offline
          K Offline
          Kaninchen
          wrote on last edited by
          #4

          If I add one line in UpdateThread.run(), which is print('x'), then the program runs very smoothly until I start the neural network detection thread. It's unbelievable for me.

          JonBJ 1 Reply Last reply
          0
          • K Kaninchen

            If I add one line in UpdateThread.run(), which is print('x'), then the program runs very smoothly until I start the neural network detection thread. It's unbelievable for me.

            JonBJ Offline
            JonBJ Offline
            JonB
            wrote on last edited by
            #5

            @Kaninchen said in Delay of picture refreshing increases and suddenly not responding.:

            It's unbelievable for me.

            Then stick to using print(), or similar. The issue arises when you have threads running which wish to access the Qt UI, like widgets. Which are much more complex than just printing to stdout. And can only be accessed in their own GUI thread.

            So, yes, signals are needed. Or, go find a different UI library from Qt which does allow multi-threaded access to windowing-system elements in the way you want.

            K 1 Reply Last reply
            0
            • JonBJ JonB

              @Kaninchen said in Delay of picture refreshing increases and suddenly not responding.:

              It's unbelievable for me.

              Then stick to using print(), or similar. The issue arises when you have threads running which wish to access the Qt UI, like widgets. Which are much more complex than just printing to stdout. And can only be accessed in their own GUI thread.

              So, yes, signals are needed. Or, go find a different UI library from Qt which does allow multi-threaded access to windowing-system elements in the way you want.

              K Offline
              K Offline
              Kaninchen
              wrote on last edited by
              #6

              @JonB I haven't understood well. What I replied is based on signals and slot, so there is already no GUI access outside main-thread. I'm sorry to have discarded changes in my codes. Now it should be the proper version. Since I used the proper way to actualize images, at least I think, how can stdout impact on GUI? Even if I used print() in the thread(again setImage through PyQtSlot), the window starts to be not responding after I start the detection thread. If I don't use GUI, everything goes well.

              1 Reply Last reply
              0
              • SGaistS Offline
                SGaistS Offline
                SGaist
                Lifetime Qt Champion
                wrote on last edited by
                #7

                Hi,

                What does your detection thread do ?
                How is it implemented ?
                How do you start it ?

                Interested in AI ? www.idiap.ch
                Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                K 1 Reply Last reply
                0
                • SGaistS SGaist

                  Hi,

                  What does your detection thread do ?
                  How is it implemented ?
                  How do you start it ?

                  K Offline
                  K Offline
                  Kaninchen
                  wrote on last edited by
                  #8

                  @SGaist Hi,

                  My detection thread takes batch of images from cache pool GrabberAndSaver and used the initialized YOLOv7 model to make predictions. Maybe I should explain my codes. There are two detectors in my program. One detects PCB and the other detects Flame. Both detectors are YOLOv7 model in Pytorch. PCB will be detected by clicking button. Another button controls Flame detection. Once clicked, the PCB detector will be deleted and the flame detection model will be loaded to GPU. I started the thread by using Python threads. The codes can be found in class App. By the way, the cache pool is also a python thread which is started before gui initialization. Implementation also updated .

                  1 Reply Last reply
                  0
                  • J.HilkJ Offline
                    J.HilkJ Offline
                    J.Hilk
                    Moderators
                    wrote on last edited by
                    #9
                    while not self.stop_flag:
                                ims = self.get_next()
                                res = []
                                for i in range(len(ims)):
                                    res.append(self.process_im(ims[i]))
                                self.changePixmap.emit(res)
                                print('"',end='')
                    

                    this might be incredibly fast and overwhelm the signal system. Consider timing one loop, and restricting the execution to something reasonable, once every 20 ms for example


                    Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                    Q: What's that?
                    A: It's blue light.
                    Q: What does it do?
                    A: It turns blue.

                    K 1 Reply Last reply
                    1
                    • K Offline
                      K Offline
                      Kaninchen
                      wrote on last edited by
                      #10

                      @J-Hilk
                      Thanks, I will have a try.

                      1 Reply Last reply
                      0
                      • J.HilkJ J.Hilk
                        while not self.stop_flag:
                                    ims = self.get_next()
                                    res = []
                                    for i in range(len(ims)):
                                        res.append(self.process_im(ims[i]))
                                    self.changePixmap.emit(res)
                                    print('"',end='')
                        

                        this might be incredibly fast and overwhelm the signal system. Consider timing one loop, and restricting the execution to something reasonable, once every 20 ms for example

                        K Offline
                        K Offline
                        Kaninchen
                        wrote on last edited by
                        #11

                        @J-Hilk
                        A delay of 25ms has solved my problem. Thanks very much!!

                        1 Reply Last reply
                        1

                        • Login

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved