使用OpenCV(Python)从Flask服务器读取MJPEG流

时间:2018-02-21 15:04:28

标签: python opencv flask mjpeg

我正在使用Flask和flask-restful生成一个MJPEG流。出于原因,我想在另一个Python程序中捕获此流,我使用OpenCV(3)。 问题是请求的第一帧进展顺利。另一方面,未正确接收请求的第二帧(延迟之后),并抛出错误:

[mpjpeg @ 0000017a86f524a0] Expected boundary '--' not found, instead found a line of 82 bytes

多次。

我相信这是因为手动设置了帧的边界。我将把违规代码放在下面。

MJPEG流生成:

## Controller for the streaming of content.
class StreamContent(Resource):
    @classmethod
    def setVis(self, vis):
        self.savedVis = vis

    def get(self):
        return Response(gen(VideoCamera(self.savedVis)),
                        mimetype='multipart/x-mixed-replace; boundary=frame')


## Generate a new VideoCamera and stream the retrieved frames.    
def gen(camera):
    frame = camera.getFrame()
    while frame != None:
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
        time.sleep(0.07)
        frame = camera.getFrame()

## Allows for the reading of video frames.
class VideoCamera(object):
    def __init__(self, vis):
        #object we retrieve the frame from.
        self.vis = vis

    ## Get the current frame.
    def getFrame(self):
        image = self.vis.mat_frame_with_overlay
        # We are using Motion JPEG, but OpenCV defaults to capture raw images,
        # so we must encode it into JPEG in order to correctly display the
        # video/image stream.
        ret, jpeg = cv2.imencode('.jpg', image)
        return jpeg.tobytes()

MJPEG流检索:

"""
Get a single frame from the camera.
"""        
class Image(Resource):
    def get(self):
        camera = VideoCamera()
        return Response(camera.getSingleFrame(), mimetype='image/jpeg')

"""
Contains methods for retrieving video information from a source.
"""
class VideoCamera(object):
    def __del__(self):
        self.video.release()

    @classmethod
    def setVideo(self, video):
        self.video = video

    ## Get the current frame.
    def getSingleFrame(self):
        self.startVideoFromSource(self.video)
        ret, image = self.video.read()
        time.sleep(0.5)
        ret, image = self.video.read()
        # We are using Motion JPEG, but OpenCV defaults to capture raw images,
        # so we must encode it into JPEG in order to correctly display the
        # video/image stream.
        ret, jpeg = cv2.imencode('.jpg', image)
        self.stopVideo()
        return jpeg.tobytes()

    def stopVideo(self):
        self.video.release()

5 个答案:

答案 0 :(得分:5)

更改帧发生器对我有用:

'name' => [
        '_empty' => 'Name cannot be empty'
    ]

答案 1 :(得分:1)

Anabad(和其他人):

Oof,这个问题已经有一段时间了。如果我没记错的话,简短的回答是:不,我从来没有能够让它正常工作。

多个程序可以同时访问摄像机(当多次向API发送请求,多个线程开始读取摄像机时)摄像机无法处理。正确处理此问题的最佳方法(在我看来)是在相应的类中读取相机的自己的线程,并使用API​​的观察者模式。每当有来自客户端的新请求来读取相机时,观察者将在新帧可用后发送它们。

这解决了多个类实例/线程访问摄像机的问题,这就是为什么这不起作用的原因。 解决这个问题,它应该可以正常工作。

答案 2 :(得分:1)

我对此并不陌生,但这绝对是一个多线程问题,因为它仅在我重新加载页面时才会发生。 轻松解决:

camera.py

import cv2, threading

lock = threading.Lock()
camIpLink = 'http://user:password@my.cam.lan.ip/with/video/footage'
cap = cv2.VideoCapture(camIpLink)

def getFrame():
    global cap
    with lock:
        while True:
            try:
                return bytes(cv2.imencode('.jpg', cap.read()[1])[1])
            except:
                print("Frame exception")
                cap.release()
                cap = cv2.VideoCapture(camIpLink)

server.py

from camera import getFrame
def gen():
    while True:
        frame = getFrame()
        try:
            yield (b'--frame\r\n'
                    b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
        except:
            print("Yield Exception")
            return

@app.route("/cam", methods=['GET']) # todo authentication
def camVideoFootage():
    return Response(gen(),
                    mimetype='multipart/x-mixed-replace; boundary=frame')

我通过反复试验进行了一些错误处理。 希望有帮助!

答案 3 :(得分:0)

也许回答为时已晚,但我遇到了同样的问题并找到了解决方案。

错误[mpjpeg @ 0000017a86f524a0] Expected boundary '--' not found, instead found a line of 82 bytes是来自ffmpeg的错误消息,OpenCV将其用作mjpeg图像解码器作为后端。

这意味着图像以mpjpeg(=多段jpeg数据)的形式流式传输,但是找不到分隔每个jpeg图像的边界(因此解码器无法解码该图像)。

边界应以--开头,但是问题中编写的服务器在此处声明边界仅为framemimetype='multipart/x-mixed-replace; boundary=frame') 这部分应该像mimetype='multipart/x-mixed-replace; boundary=--frame')

我还发现边界和图像数据之间的线分隔是强制性的。 (由于Ubuntu 18.04和更高版本提供了ffmpeg?) 请查看mjpg服务器的另一种实现。 (https://github.com/ros-drivers/video_stream_opencv/blob/e6ab82a88dca53172dc2d892cd3decd98660c447/test/mjpg_server.py#L75

希望这会有所帮助。

答案 4 :(得分:0)

我知道这有点具体,但是我在Mobotix相机上遇到了这个错误,不得不在流URL needlength中传递一个附加参数,要求它向我发送图像边界。

这样,我就可以使用OpenCV进行阅读,而不会出现边界错误。

除了相机的帮助页面上,没有在其他任何地方记录此参数:

http://camera_url/cgi-bin/faststream.jpg?help

它说:

需要的长度
需要内容长度
为服务器推送流中的每个帧发送HTTP内容长度。
注意:此选项对浏览器无效。

所以我不得不将流URL修改为:

http://camera_url/control/faststream.jpg?stream=full&needlength

我的猜测是其他情况可能也有类似的原因,而OpenCV找不到所需的图像边界标记。