How to make a python script run two processes simultaneously?

Asked

Viewed 1,560 times

1

A little while ago I learned to capture videos via webcam using Python with the Opencv library. After that, I had the idea of making a script that automatically starts and ends the recording of a video. Started the program, the capture would start, but would only start recording if the captured frame met a certain condition I created (based on a detection function I did). Video recording would automatically end when the captured frames did not contain what the detector 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 detector function in a process other than the process of recording frames so that frames are recorded without interruption?

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

  • An "intuition" is that you do not immediately process each frame of the video, but put it on a list to be processed when it is processed (ideally, in a separate thread even). If I have time today I see if I can answer.

  • I don’t think it is necessary to store each frame in a list, I think it is enough to create a function that makes the detector function read each frame and make this function run in a different process. But I don’t know how you do it. I tried to read the documentation of the multiprocessing module, but besides being beginner in the area, I do not know much English and I end up understanding very little.

  • Well, your problem today is precisely the fact that your processing of the frames takes longer than capturing them. If you use another thread but block it in a call between threads, your end result will be the same: frames "skipped".

  • 1

    Ah, another thing: I used one thread different, and not a different process. That is, it runs everything in the same process, but in parallel. I believe that multiprocessing in this case is wrong, because there will be a frequent communication of potentially large data (the frames of the video, besides being captured 20x per second, have 480 thousand pixels - being conservative and considering a very low resolution of 800x600).

1 answer

2


Here’s an example of code that reads on a separate thread. The idea of storing the tables read in a list (which in practice acts as a fila fifo) is because your processing of the frames received in the main thread will take longer than the capture, so you should not prevent the capture from taking place.

Remarks:

  1. Note that in your final system, you should be able to process the rest of the frames still "lined up" after the system is closed. This example doesn’t do that, he just illustrates how work with both threads and a row between them.

  2. Python has a class Queue, which is a queue with priorities, size limitation and other details. It’s good to know that it exists, but in this instance 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')

Browser other questions tagged

You are not signed in. Login or sign up in order to post.