python 使用FastAPI和OpenCV的视频流应用程序

0x6upsns  于 2023-01-16  发布在  Python
关注(0)|答案(2)|浏览(241)

我正在尝试呈现HTML页面,该页面显示来自网络摄像头的视频流。但是,我遇到以下错误:

500 Server Error TypeError: TemplateResponse() missing 1 required positional argument: 'context'

我的FastAPI应用程序:

from fastapi import FastAPI
import uvicorn
from fastapi import Depends, FastAPI
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
import cv2

app = FastAPI(debug=True)
templates = Jinja2Templates(directory="templates")

@app.get("/")
async def index():
    return templates.TemplateResponse("index.html")

async def gen_frames(camera_id):
    cap=  cv2.VideoCapture(0)

    while True:
        # for cap in caps:
        # # Capture frame-by-frame
        success, frame = cap.read()  # read the camera frame
        if not success:
            break
        else:
            ret, buffer = cv2.imencode('.jpg', frame)
            frame = buffer.tobytes()
            yield (b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')  
    
   if __name__ == '__main__':
    uvicorn.run(app,  host="127.0.0.1",port=8000)

我的HTML网页(index.html):

<!doctype html>
<html lang="en">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
          integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">

    <title>Multiple Live Streaming</title>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-lg-7">
            <h3 class="mt-5">Multiple Live Streaming</h3>
            <img src="{{ url_for('video_feed', id='0') }}" width="100%">
        </div>
    </div>
</div>
</body>
</html>

追溯:

iq0todco

iq0todco1#

在使用模板时,您需要传递请求。

from fastapi import Request

@app.get("/")
async def index(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

因此,您还可以使用StreamingResponse将视频作为另一条路径

from fastapi.responses import StreamingResponse

@app.get("/serve/{camera_id}", include_in_schema=False)
async def serve_video(camera_id: int):
    return StreamingResponse(gen_frames(camera_id))

然后使用 AJAX 或Axios等获取响应。

aij0ehis

aij0ehis2#

使用Templates时,需要在端点中声明一个Request参数,该参数将返回一个模板,如下所示:

from fastapi import Request

@app.get('/')
def index(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

下面给出了两个选项(包含完整的代码示例),介绍如何流式传输使用FastAPI和OpenCV的(实时)视频。选项1根据您的问题,使用HTTP协议和FastAPI/Starlette的StreamingResponse演示了一种方法。选项2使用WebSocket协议,可轻松处理高清视频流,并受FastAPI/Starlette支持(可在herehere中找到相关文档)

选项1 -使用HTTP协议

您可以在http://127.0.0.1:8000/访问实时流媒体。

应用程序.py

import cv2
import uvicorn
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
from fastapi.responses import StreamingResponse

app = FastAPI()
camera = cv2.VideoCapture(0, cv2.CAP_DSHOW)
templates = Jinja2Templates(directory="templates")

def gen_frames():
    while True:
        success, frame = camera.read()
        if not success:
            break
        else:
            ret, buffer = cv2.imencode('.jpg', frame)
            frame = buffer.tobytes()
            yield (b'--frame\r\n'
                   b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')

@app.get('/')
def index(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

@app.get('/video_feed')
def video_feed():
    return StreamingResponse(gen_frames(), media_type='multipart/x-mixed-replace; boundary=frame')

if __name__ == '__main__':
    uvicorn.run(app, host='127.0.0.1', port=8000, debug=True)

模板/索引.html

<!DOCTYPE html>
<html>
    <body>
        <div class="container">
            <h3> Live Streaming </h3>
            <img src="{{ url_for('video_feed') }}" width="50%">
        </div>
    </body>
</html>

选项2 -使用WebSocket协议

您可以在http://127.0.0.1:8000/访问实时流媒体。

应用程序.py

from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect
from fastapi.templating import Jinja2Templates
import uvicorn
import cv2

app = FastAPI()
camera = cv2.VideoCapture(0,cv2.CAP_DSHOW)
templates = Jinja2Templates(directory="templates")

@app.get('/')
def index(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

@app.websocket("/ws")
async def get_stream(websocket: WebSocket):
    await websocket.accept()
    try:
        while True:
            success, frame = camera.read()
            if not success:
                break
            else:
                ret, buffer = cv2.imencode('.jpg', frame)
                await websocket.send_bytes(buffer.tobytes())  
    except WebSocketDisconnect:
        print("Client disconnected")   
 
if __name__ == '__main__':
    uvicorn.run(app, host='127.0.0.1', port=8000)

下面是HTML模板,用于建立WebSocket连接、接收图像字节并创建Blob URL(加载图像后释放该URL,以便随后对对象进行垃圾收集,而不是不必要地保留在内存中),如here所示,以便在浏览器中显示视频帧。
模板/索引.html

<!DOCTYPE html>
<html>
    <head>
        <title>Live Streaming</title>
    </head>
    <body>
        <img id="frame" src="">
        <script>
            let ws = new WebSocket("ws://localhost:8000/ws");
            let image = document.getElementById("frame");
            image.onload = function(){
                URL.revokeObjectURL(this.src); // release the blob URL once the image is loaded
            } 
            ws.onmessage = function(event) {
                image.src = URL.createObjectURL(event.data);
            };
        </script>
    </body>
</html>

下面也是一个基于websockets库和OpenCV的Python客户端,您可以使用它连接到服务器,以便在Python应用程序中接收和显示视频帧。

客户端.py

import websockets
import asyncio
import cv2
import numpy as np

camera = cv2.VideoCapture(0, cv2.CAP_DSHOW)

async def main():
    url = 'ws://127.0.0.1:8000/ws'
    
    async with websockets.connect(url) as ws:
         #count = 1
         while True:
            contents = await ws.recv()
            arr = np.frombuffer(contents, np.uint8)
            frame = cv2.imdecode(arr, cv2.IMREAD_UNCHANGED)
            cv2.imshow('frame', frame)
            cv2.waitKey(1)
            
            #cv2.imwrite("frame%d.jpg" % count, frame)
            #count += 1
                    
asyncio.run(main())

相关问题