【OpenCV】利用Flask+Python+OpenCV实现摄像头读取图像帧网页视频流

4 篇文章 16 订阅
3 篇文章 0 订阅

利用Flask+Python+OpenCV实现摄像头读取图像帧网页视频流
github链接:https://github.com/Windxy/Flask_videostream_object_detection

1.环境准备

python3.x
cv2
flask

2.代码

文件树:

----文件名\
    |----templates\
    |    |----index.html
    |----app.py
    |----base_camera.py
    |----camera_opencv.py
2.1 index.html
<html>
  <head>
    <title>视频流演示</title>
  </head>
  <body>
    <h1>视频流演示</h1>
    <img src="{{ url_for('video_feed') }}">
  </body>
</html>

说明:该代码块输入在templates文件夹下的index.html

  • 其中,图片的路径,对应flask中的url_for函数,这个函数用于给指定函数(video_feed函数)构造url链接,下面看一下app.py中的相关函数
2.2 app.py
from importlib import import_module
import os
from flask import Flask, render_template, Response
from camera_opencv import Camera

app = Flask(__name__)

@app.route('/')
def index():
    """主页"""
    return render_template('index.html')

def gen(camera):
    """视频流生成"""
    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_feed')
def video_feed():
    """视频流的路线。将其放在img标记的src属性中。"""
    return Response(gen(Camera()),
                    mimetype='multipart/x-mixed-replace; boundary=frame')

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

说明:主函数中,定义了服务器的host和port,127.0.0.1代表本地host,9011可以改为其他没有被占用的端口

  • 函数一开始,执行@app.route('/')下的函数,即index(),对应index.html
  • index.html中,图片的链接对应flask中的url_for(),在该函数中的video_feed()中构造了图片的链接
  • video_feed中,使用Response()函数返回数据,而由gen()函数流式地yeild出对应的数据
  • Camera()中主要利用到opencv中的read函数,读取图像数据
2.3 camera_opencv.py
import os
import cv2
from base_camera import BaseCamera


class Camera(BaseCamera):
    video_source = 0

    def __init__(self):
        if os.environ.get('OPENCV_CAMERA_SOURCE'):
            Camera.set_video_source(int(os.environ['OPENCV_CAMERA_SOURCE']))
        super(Camera, self).__init__()

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

    @staticmethod
    def frames():
        camera = cv2.VideoCapture(Camera.video_source)
        if not camera.isOpened():
            raise RuntimeError('无法打开摄像头')

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

            # encode 成为 jpeg image 然后 return it
            yield cv2.imencode('.jpg', img)[1].tobytes()

  • 本段代码中,利用cv2.VideoCapture(0)read()函数不断读取摄像头中的图像数据
  • 然后利用cv2.imencode函数将其编码成jpeg图像并返回
2.4 base_camera.py
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

3.效果

(打码处理)
在这里插入图片描述

4.github链接

https://github.com/Windxy/Flask_videostream_object_detection

5.参考

https://blog.miguelgrinberg.com/post/video-streaming-with-flask

要在 Flask 网页中显示视频图像,可以使用 OpenCV 库来读取视频文件并将其转换为图像,然后使用 Flask 的路由和模板渲染功能将图像嵌入到 HTML 页面中。 以下是一个简单的示例代码: ```python import cv2 from flask import Flask, render_template, Response app = Flask(__name__) # 读取视频文件 cap = cv2.VideoCapture('video.mp4') # 从视频中读取 def gen_frames(): while True: success, frame = cap.read() if not success: break else: # 将转换为 PNG 格式 ret, buffer = cv2.imencode('.png', frame) frame = buffer.tobytes() yield (b'--frame\r\n' b'Content-Type: image/png\r\n\r\n' + frame + b'\r\n') # 定义路由,渲染模板 @app.route('/') def index(): return render_template('index.html') # 定义路由,返回图像流 @app.route('/video_feed') def video_feed(): return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame') if __name__ == '__main__': app.run(debug=True) ``` 在此示例中,我们首先使用 OpenCV 库从视频文件中读取,并将每个转换为 PNG 格式。然后,我们定义两个 Flask 路由:一个用于渲染 HTML 模板,另一个用于返回图像流。最后,我们使用 Flask 的 `Response` 对象将流返回给前端页面。 在 HTML 模板中,我们可以使用 `<img>` 标签来显示图像。我们将图像的 URL 设置为 `/video_feed`,并在 `src` 属性中使用 JavaScript 实时更新图像。 ```html <!doctype html> <html> <head> <title>Video Streaming Demonstration</title> </head> <body> <h1>Video Streaming Demonstration</h1> <img src="{{ url_for('video_feed') }}"> </body> </html> ``` 这是一个简单的示例,你可以根据自己的需求进行修改和扩展。
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小风_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值