【问题标题】:Python OpenCV streaming from camera - multithreading, timestamps来自相机的 Python OpenCV 流式传输 - 多线程、时间戳
【发布时间】:2019-08-01 14:56:54
【问题描述】:

我在 Raspberry Pi 3 上运行了简单的 python 脚本。该脚本负责使用 MJPEG 打开视频设备和流数据 (800x600) 到 HTTP 端点。当我收到这个流时,我的一个 Raspberry Pi 内核可以 100% 工作。可以多线程运行 OpenCV 吗?

这是我的代码

import cv2
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import time
import argparse
import socket as Socket    
camera = None  

def setUpCameraCV():
    global camera
    camera = cv2.VideoCapture(0)

class mjpgServer(BaseHTTPRequestHandler):

    ip = None
    hostname = None

    def do_GET(self):

        print('connection from:', self.address_string())

        if self.ip is None or self.hostname is None:
            self.ip, _ = 0.0.0.0
            self.hostname = Socket.gethostname()

        if self.path == '/mjpg':

            self.send_response(200)
            self.send_header('Cache-Control', 'no-cache')
            self.send_header('Pragma', 'no-cache')
            self.send_header('Connection', 'close')
            self.send_header(
                'Content-type',
                'multipart/x-mixed-replace; boundary=mjpegstream'
            )
            self.end_headers()

            while True:
                if camera:
                    ret, img = camera.read()

                else:
                    raise Exception('Error, camera not setup')

                if not ret:
                    print('no image from camera')
                    time.sleep(1)
                    continue

                ret, jpg = cv2.imencode('.jpg', img)
                
                self.end_headers()
                self.wfile.write('--mjpegstream')
                self.end_headers()

                self.send_header('Content-type', 'image/jpeg')
                self.send_header('Content-length', str(jpg.size))
                self.end_headers()
                self.wfile.write(jpg.tostring())    

def main():
    try:
        setUpCameraCV()         
        mjpgServer.ip = 0.0.0.0
        mjpgServer.hostname = Socket.gethostname()
        server = HTTPServer((ipv4, args['port']), mjpgServer)
        print("server started on {}:{}".format(Socket.gethostname(), args['port']))
        server.serve_forever()

    except KeyboardInterrupt:
        print('KeyboardInterrupt')

    server.socket.close()


if __name__ == '__main__':
    main()

另一个问题,如何在客户端(接收方)获取每一帧的时间戳?

【问题讨论】:

标签: python multithreading performance opencv raspberry-pi


【解决方案1】:

使用线程处理 I/O 繁重的操作(例如从网络摄像头读取帧)是一种经典的编程模型。由于使用cv2.VideoCapture().read() 访问网络摄像头/摄像头是一个阻塞操作,因此我们的主程序会停止,直到从摄像头设备读取帧并返回到我们的脚本。本质上,这个想法是生成另一个线程来处理 parallel 中的帧,而不是依赖单个线程(我们的“主”线程)以 sequential 顺序来抓取帧.这将允许从 I/O 线程连续读取帧,而我们的根线程处理当前帧。一旦根线程完成其帧的处理,它只需要从 I/O 线程中获取当前帧,而无需等待阻塞 I/O 操作。

因此,我们可以通过创建一个新线程来提高性能,该线程除了轮询新帧之外什么都不做,而我们的主线程处理当前帧。有关处理多个摄像头流的实现,请查看capture multiple camera streams with OpenCV

from threading import Thread
import cv2, time

class VideoStreamWidget(object):
    def __init__(self, src=0):
        self.capture = cv2.VideoCapture(src)
        # Start the thread to read frames from the video stream
        self.thread = Thread(target=self.update, args=())
        self.thread.daemon = True
        self.thread.start()

    def update(self):
        # Read the next frame from the stream in a different thread
        while True:
            if self.capture.isOpened():
                (self.status, self.frame) = self.capture.read()
            time.sleep(.01)

    def show_frame(self):
        # Display frames in main program
        cv2.imshow('frame', self.frame)
        key = cv2.waitKey(1)
        if key == ord('q'):
            self.capture.release()
            cv2.destroyAllWindows()
            exit(1)

if __name__ == '__main__':
    video_stream_widget = VideoStreamWidget()
    while True:
        try:
            video_stream_widget.show_frame()
        except AttributeError:
            pass

【讨论】:

  • 好奇,i) 为什么使用 time.sleep(.01)? ii) 从真实 IP 摄像机读取时需要它吗?因为我正在研究的解决方案需要实时,但是从更大的角度来看,这 100 毫秒的添加是一个很大的延迟。
  • 您也可以在这里提供一些专家建议:stackoverflow.com/questions/64754236/…
  • 使用 @AakashBasu time.sleep 是因为相机只能如此快速地轮询,如果缓冲区中没有新帧,则无需以荒谬的速度进行轮询。我强烈推荐它,您必须查看您的 IP 摄像机规格。大多数 IP 摄像机都在 30-60 FPS 范围内,因此更快的轮询将没有任何好处。你可以减少延迟或者完全去掉,我发现延迟对我的相机很有用
  • 对此有什么帮助吗? stackoverflow.com/questions/65121094/…
  • 所以一个好的参考点是 1 秒 / fps?例如在 30 fps 的相机上,您应该以 1/30 = 0.033 秒或更短的时间轮询?
猜你喜欢
  • 2021-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多