How to make a script in python run two processes simultaneously?

1

Soon I learned to capture videos through the webcam using Python with the OpenCV library. After that, I had the idea of making a script that automatically starts and stops recording a video. Once the program started, the capture would start, but would only begin to record if the captured frame satisfied certain condition I created (based on a detection function I did). Video recording would automatically terminate when the captured frames did not contain what the detection function is programmed to detect. The detector function returns True if the image satisfies my condition and False if it does not satisfy. The script is as follows:

def webvideo(path):
    import sys,cv2
    begin=False
    cap=cv2.VideoCapture(0)
    if not cap.isOpened():
        print('Não foi possível abrir a web cam.')
        sys.exit(-1)
    fourcc = cv2.VideoWriter_fourcc(*'XVID')
    out = cv2.VideoWriter(path,fourcc, 20.0, (640,480))
    while True:
        ret, frame = cap.read()
        if ret:
            cv2.imwrite('temp.jpg',frame)
            if not begin:
                if funcao_detectora('temp.jpg'):
                    begin=True
            else:
                if not funcao_detectora('temp.jpg'):
                    break
            if begin:
                out.write(frame)
        else:
            break
    cap.release()
    out.release()

webvideo('teste.avi')

The problem is that the detector function reads each frame, which takes a while. Thus, it takes a while for the next frame to be captured and the video looks like a sequence of fairly time-spaced photos. Question: How do I run the detecting function in a process other than the process of recording the frames so that the frames are recorded without interruption?

NOTE: It is not necessary to apply the multithreading knowledge to solve this specific case, but if someone shows an example that I understand and can apply to my script, I will also accept it.

    
asked by anonymous 18.01.2017 / 16:18

1 answer

2

Here is an example code that reads into a separate thread. The idea of storing the tables read in a list (which, in practice, works like a FIFO queue ) is because your processing the frames received in the main thread will take longer than the capture, so you should not prevent the capture from occurring.

  

Comments:

     
  • Note that in your final system, you should be able to process the rest of the frames still "queued" after the system is   locked up. This example does not do this , it just illustrates how   work with the two threads and a queue between them.

  •   
  • Python has a class Queue , which is a queue with priorities, size and other details. It is good to know that it exists,   but in this example it was unnecessary.

  •   
    import sys
    import threading
    import cv2
    
    # ==========================================
    class WebcamError(ValueError):
        pass
    
    # ==========================================
    class WebcamCap(threading.Thread):
    
        # --------------------------------------
        def __init__(self, webcam = 0):
            super().__init__(None, self)
    
            self._webcam = webcam
            '''Id da Webcam a ser utilizada. O default é 0 (principal).'''
    
            self._video = cv2.VideoCapture(self._webcam)
            '''Objeto para captura do vídeo.'''
    
            if not self._video.isOpened():
                raise WebcamError('Não foi possível iniciar a Webcam.')
    
            self._lock = threading.Lock()
            '''Mutex usado para a sincronização entre as threads.'''
    
            self._frames = []
            '''Lista de quadros capturados e aptos a serem processados.'''
    
            self._started = threading.Event()
            '''Evento usado para controlar a inicialização da câmera.'''
    
            self._running = False
            '''Flag usada para indicar e controlar se a Thread está em execução.'''
    
        # --------------------------------------
        def __del__(self):
            self._video.release()
    
        # --------------------------------------
        def start(self):
            if not self.isRunning():
                super().start()
                # Aguarda até que a câmera seja inicializada corretamente
                self._started.clear()
                self._started.wait()
    
        # --------------------------------------
        def stop(self):
            self._lock.acquire()
            self._running = False
            self._lock.release()
    
        # --------------------------------------
        def isRunning(self):
            self._lock.acquire()
            ret = self._running
            self._lock.release()
            return ret
    
        # --------------------------------------
        def pop(self):
            self._lock.acquire()
            try:
                frame = self._frames.pop()
            except:
                frame = None
            self._lock.release()
            return frame
    
        # --------------------------------------
        def run(self):
            # Força a leitura do primeiro quadro, já que a inicialização da câmera
            # demora um pouco
            ret, frame = self._video.read()
            if ret:
                self._frames.append(frame)
                self._running = True
                self._started.set() # Evento indicando a inicialização
            else:
                raise WebcamError('Não foi possível acessar a Webcam.')
    
            # Loop principal de leitura
            while self.isRunning():
                ret, frame = self._video.read()
                if ret:
                    self._lock.acquire()
                    self._frames.append(frame)
                    self._lock.release()
    
    # ==========================================
    def webvideo(path):
    
        fps = 20 # Taxa de reprodução (em quadros por segundo)
        delay = int(1 / fps * 1000) # Tempo de 1 quadro em milisegundos
    
        fourcc = cv2.VideoWriter_fourcc(*'XVID')
        out = cv2.VideoWriter(path, fourcc, fps, (640,480))
    
        try:
            cam = WebcamCap()
        except WebcamError as e:
            print(e.message)
            sys.exit(-1)
    
        cam.start()
        while True:
    
            frame = cam.pop()
            if frame is not None:
                out.write(frame)
    
                cv2.imshow('Webcam', frame)
    
                if cv2.waitKey(delay) == ord('q'):
                    break
            else:
                print('Erro capturando video da Webcam')
                break
    
        cam.stop()
        out.release()
    
    webvideo('teste.avi')
    
        
    18.01.2017 / 18:38