尝试使用Opencv和Flask显示多个流

时间:2018-10-26 04:22:17

标签: python opencv flask video-streaming

我试图用opencv捕获两个rtsp流,然后将它们提供给一个简单的flask服务器。当只使用opencv时,我可以同时显示两个流,但是当我尝试通过烧瓶显示它时,它只会选择其中一个流并将其显示两次。

这是原始创作者blog

这是我的烧瓶代码:

<table border="1">
<tbody>
<tr>
<th>model</th>
<td>XS</td>
<td>X&nbsp;</td>
</tr>
<tr>
<th>Memory size</th>
<td>256gb</td>
<td>125gb</td>
</tr>
<tr>
<th>ram</th>
<td>&nbsp;4GB</td>
<td>&nbsp;3GB</td>
</tr>
<tr>
<th>price</th>
<td>&nbsp;499</td>
<td>999&nbsp;</td>
</tr>
<tr>
<th>&nbsp;store</th>
<td>&nbsp;switch</td>
<td>&nbsp;machine</td>
</tr>
</tbody>
</table>

这是camera_opencv文件

#!/usr/bin/env python
from importlib import import_module
import os
from flask import Flask, render_template, Response

# import camera driver
'''
if os.environ.get('CAMERA'):
    Camera = import_module('camera_' + os.environ['CAMERA']).Camera
else:
    from camera import Camera
'''
#
from camera_opencv import Camera1, Camera2


app = Flask(__name__)


@app.route('/')
def index():
    """Video streaming home page."""
    return render_template('index.html')


def gen(camera):
    """Video streaming generator function."""
    while True:
        frame = camera.get_frame()
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')


@app.route('/video_feed1')
def video_feed1():
    """Video streaming route. Put this in the src attribute of an img tag."""
    return Response(gen(Camera1()),
                    mimetype='multipart/x-mixed-replace; boundary=frame')

@app.route('/video_feed2')
def video_feed2():
    """Video streaming route. Put this in the src attribute of an img tag."""
    return Response(gen(Camera2()),
                    mimetype='multipart/x-mixed-replace; boundary=frame')


if __name__ == '__main__':
    app.run(host='0.0.0.0', threaded=True, port=8888)

基本相机文件

import cv2
from base_camera import BaseCamera


class Camera1(BaseCamera):
    video_source = 0


    @staticmethod
    def set_video_source(source):
        Camera1.video_source = source

    @staticmethod
    def frames():
        camera = cv2.VideoCapture(Camera1.video_source)
        if not camera.isOpened():
            raise RuntimeError('Could not start camera.')

        while True:
            # read current frame
            _, img = camera.read()

            # encode as a jpeg image and return it
            yield cv2.imencode('.jpg', img)[1].tobytes()

class Camera2(BaseCamera):
    video_source = 1

    @staticmethod
    def set_video_source(source):
        Camera2.video_source = source

    @staticmethod
    def frames():
        camera = cv2.VideoCapture(Camera2.video_source)
        if not camera.isOpened():
            raise RuntimeError('Could not start camera.')

        while True:
            # read current frame
            _, img = camera.read()

            # encode as a jpeg image and return it
            yield cv2.imencode('.jpg', img)[1].tobytes()

Index.html

import time
import threading
try:
    from greenlet import getcurrent as get_ident
except ImportError:
    try:
        from thread import get_ident
    except ImportError:
        from _thread import get_ident


class CameraEvent(object):
    """An Event-like class that signals all active clients when a new frame is
    available.
    """
    def __init__(self):
        self.events = {}

    def wait(self):
        """Invoked from each client's thread to wait for the next frame."""
        ident = get_ident()
        if ident not in self.events:
            # this is a new client
            # add an entry for it in the self.events dict
            # each entry has two elements, a threading.Event() and a timestamp
            self.events[ident] = [threading.Event(), time.time()]
        return self.events[ident][0].wait()

    def set(self):
        """Invoked by the camera thread when a new frame is available."""
        now = time.time()
        remove = None
        for ident, event in self.events.items():
            if not event[0].isSet():
                # if this client's event is not set, then set it
                # also update the last set timestamp to now
                event[0].set()
                event[1] = now
            else:
                # if the client's event is already set, it means the client
                # did not process a previous frame
                # if the event stays set for more than 5 seconds, then assume
                # the client is gone and remove it
                if now - event[1] > 5:
                    remove = ident
        if remove:
            del self.events[remove]

    def clear(self):
        """Invoked from each client's thread after a frame was processed."""
        self.events[get_ident()][0].clear()


class BaseCamera(object):
    thread = None  # background thread that reads frames from camera
    frame = None  # current frame is stored here by background thread
    last_access = 0  # time of last client access to the camera
    event = CameraEvent()

    def __init__(self):
        """Start the background camera thread if it isn't running yet."""
        if BaseCamera.thread is None:
            BaseCamera.last_access = time.time()

            # start background frame thread
            BaseCamera.thread = threading.Thread(target=self._thread)
            BaseCamera.thread.start()

            # wait until frames are available
            while self.get_frame() is None:
                time.sleep(0)

    def get_frame(self):
        """Return the current camera frame."""
        BaseCamera.last_access = time.time()

        # wait for a signal from the camera thread
        BaseCamera.event.wait()
        BaseCamera.event.clear()

        return BaseCamera.frame

    @staticmethod
    def frames():
        """"Generator that returns frames from the camera."""
        raise RuntimeError('Must be implemented by subclasses.')

    @classmethod
    def _thread(cls):
        """Camera background thread."""
        print('Starting camera thread.')
        frames_iterator = cls.frames()
        for frame in frames_iterator:
            BaseCamera.frame = frame
            BaseCamera.event.set()  # send signal to clients
            time.sleep(0)

            # if there hasn't been any clients asking for frames in
            # the last 10 seconds then stop the thread
            if time.time() - BaseCamera.last_access > 10:
                frames_iterator.close()
                print('Stopping camera thread due to inactivity.')
                break
        BaseCamera.thread = None

2 个答案:

答案 0 :(得分:0)

所以我设法设法解决了。无论出于什么原因,我都无法解决,该应用无法单独处理多个流。

因此,我更改了相机类,并向其中添加了多个源,并使用numpy.hstack(())将两个帧合并在一起,从而返回了一个唯一的流。

如果有人可以在这里提供帮助,将不胜感激,因为我的方法根本无法扩展。

import cv2
from base_camera import BaseCamera
import numpy as np


class Camera(BaseCamera):
    video_source1 = 0
    video_source2 = 1

    @staticmethod
    def set_video_source(sources):
        Camera.video_source1 = sources[0]
        Camera.video_source2 = sources[1]

    @staticmethod
    def frames():
        camera1 = cv2.VideoCapture(Camera.video_source1)
        camera2 = cv2.VideoCapture(Camera.video_source2)
        if not (camera1.isOpened() or camera2.isOpened()):
            raise RuntimeError('Could not start camera.')

        while True:
            # read current frame
            _, img1 = camera1.read()
            _, img2 = camera2.read()
            img1 = cv2.resize(img1, (704, 396))
            img2 = cv2.resize(img2, (704, 396))
            img = np.hstack((img1, img2))

            # encode as a jpeg image and return it
            yield cv2.imencode('.jpg', img)[1].tobytes()

答案 1 :(得分:0)

根据博客,我使用两个生成器并将图像发送到index.html。而且我可以看到两个流。

def generate2():
    # it is a generator
    global outputFrame2, lock

    while True:
        with lock:
            if outputFrame is None:
                continue

            (flag, encodedImage2) = cv2.imencode(".jpg", outputFrame2)
            if not flag:
                continue

        yield(b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + 
            bytearray(encodedImage2) + b'\r\n')

这是videofeed2

@app.route("/video_feed2")
def video_feed2():
    return Response(generate2(),
        mimetype = "multipart/x-mixed-replace; boundary=frame")