Как я могу улучшить свой Python OpenCV видео-поток? - PullRequest
2 голосов
/ 17 мая 2019

Я работал над проектом, в котором я использую Raspberry Pi для отправки живого видео на мой сервер.Это работает, но не так, как мне бы хотелось.Проблема в основном в скорости.Прямо сейчас я могу отправить видео поток 640x480 со скоростью около 3,5 кадров в секунду и 1920x1080 со скоростью около 0,5 кадров в секунду, что ужасно.Поскольку я не профессионал, я подумал, что должен быть способ улучшить мой код.

Отправитель (Raspberry pi):

def send_stream():
    connection = True
    while connection:
        ret,frame = cap.read()
        if ret:
            # You might want to enable this while testing.
            # cv2.imshow('camera', frame)
            b_frame = pickle.dumps(frame)
            b_size = len(b_frame)
            try:
                s.sendall(struct.pack("<L", b_size) + b_frame)
            except socket.error:
                print("Socket Error!")
                connection = False

        else:
            print("Received no frame from camera, exiting.")
            exit()

Получатель (сервер):

    def recv_stream(self):
        payload_size = struct.calcsize("<L")
        data = b''
        while True:
            try:
                start_time = datetime.datetime.now()
                # keep receiving data until it gets the size of the msg.
                while len(data) < payload_size:
                    data += self.connection.recv(4096)
                # Get the frame size and remove it from the data.
                frame_size = struct.unpack("<L", data[:payload_size])[0]
                data = data[payload_size:]
                # Keep receiving data until the frame size is reached.
                while len(data) < frame_size:
                    data += self.connection.recv(32768)
                # Cut the frame to the beginning of the next frame.
                frame_data = data[:frame_size]
                data = data[frame_size:]

                frame = pickle.loads(frame_data)
                frame = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)

                end_time = datetime.datetime.now()
                fps = 1/(end_time-start_time).total_seconds()
                print("Fps: ",round(fps,2))

                self.detect_motion(frame,fps)

                self.current_frame = frame

            except (socket.error,socket.timeout) as e:
                # The timeout got reached or the client disconnected. Clean up the mess.
                print("Cleaning up: ",e)
                try:
                    self.connection.close()
                except socket.error:
                    pass
                self.is_connected = False
                break

Ответы [ 2 ]

0 голосов
/ 24 мая 2019

После долгих поисков в интернете я нашел быстрое решение, которое удваивает частоту кадров (это все еще слишком мало: 1,1 кадра в секунду при 1080p).Я перестал использовать pickle и использовал base64.по-видимому, травление изображения занимает некоторое время.В любом случае, это мой новый код:

Отправитель (Raspberry pi):

def send_stream():
global connected
connection = True
while connection:
    if last_frame is not None:

        # You might want to uncomment these lines while testing.
        # cv2.imshow('camera', frame)
        # cv2.waitKey(1)
        frame = last_frame

        # The old pickling method.
        #b_frame = pickle.dumps(frame)

        encoded, buffer = cv2.imencode('.jpg', frame)
        b_frame = base64.b64encode(buffer)

        b_size = len(b_frame)
        print("Frame size = ",b_size)
        try:
            s.sendall(struct.pack("<L", b_size) + b_frame)
        except socket.error:
            print("Socket Error!")
            connection = False
            connected = False
            s.close()
            return "Socket Error"
    else:
        return "Received no frame from camera"

Получатель (сервер):

    def recv_stream(self):
    payload_size = struct.calcsize("<L")
    data = b''
    while True:
        try:
            start_time = datetime.datetime.now()
            # keep receiving data until it gets the size of the msg.
            while len(data) < payload_size:
                data += self.connection.recv(4096)
            # Get the frame size and remove it from the data.
            frame_size = struct.unpack("<L", data[:payload_size])[0]
            data = data[payload_size:]
            # Keep receiving data until the frame size is reached.
            while len(data) < frame_size:
                data += self.connection.recv(131072)
            # Cut the frame to the beginning of the next frame.
            frame_data = data[:frame_size]
            data = data[frame_size:]

            # using the old pickling method.
            # frame = pickle.loads(frame_data)

            # Converting the image to be sent.
            img = base64.b64decode(frame_data)
            npimg = np.fromstring(img, dtype=np.uint8)
            frame = cv2.imdecode(npimg, 1)

            frame = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)

            end_time = datetime.datetime.now()
            fps = 1/(end_time-start_time).total_seconds()
            print("Fps: ",round(fps,2))
            self.detect_motion(frame,fps)

            self.current_frame = frame

        except (socket.error,socket.timeout) as e:
            # The timeout got reached or the client disconnected. Clean up the mess.
            print("Cleaning up: ",e)
            try:
                self.connection.close()
            except socket.error:
                pass
            self.is_connected = False
            break

Я также увеличил размер пакетакоторый увеличил fps при отправке с моей локальной машины на мою локальную машину во время тестирования, но это ничего не изменило при использовании raspberry pi.

Вы можете увидеть полный код на моем github: https://github.com/Ruud14/SecurityCamera

0 голосов
/ 17 мая 2019

Одна потенциальная причина может быть из-за задержки ввода / вывода при чтении кадров. Поскольку cv2.VideoCapture().read() является операцией блокировки, основная программа останавливается до тех пор, пока кадр не будет считан с устройства камеры и не возвращен. Метод повышения производительности - создание другого потока для обработки захвата кадров в параллельно вместо того, чтобы полагаться на один поток для захвата кадров в последовательном порядке. Мы можем повысить производительность, создав новый поток, который опрашивает только новые кадры, в то время как основной поток обрабатывает / отображает самый последний кадр.

Ваш текущий подход (последовательный):

Тема 1: Захват кадра -> Обработка кадра -> Участок

Предлагаемый подход (параллельный):

Тема 1: Схватить кадр

from threading import Thread
import time

def get_frames():
    while True:
        ret, frame = cap.read()
        time.sleep(.01)

thread_frames = Thread(target=self.get_frames, args=())
thread_frames.daemon = True
thread_frames.start()

Тема 2: Технологическая рамка -> График

def process_frames():
    while True:
        # Grab most recent frame
        # Process/plot frame
        ...

Имея отдельные потоки, ваша программа будет параллельной, поскольку всегда будет кадр, готовый для обработки, вместо того, чтобы ждать, пока кадр будет прочитан, прежде чем можно будет выполнить обработку.

Примечание: Этот метод даст вам повышение производительности на основе уменьшения задержки ввода / вывода. Это не является истинным увеличением FPS, так как это резкое снижение задержки (кадр всегда доступен для обработки; нам не нужно опрашивать устройство камеры и ждать, пока ввод / вывод полный).

...