简单理解

要使用摄像头,需要使用cv2.VideoCapture(0)创建VideoCapture对象,参数0指的是摄像头的编号,如果你电脑上有两个摄像头的话,访问第2个摄像头就可以传入1,依此类推。

capture.read
函数返回的第1个参数ret(return value缩写)是一个布尔值,表示当前这一帧是否获取正确。

cap.get(propId)、cap.set(propId,value)
通过cap.get(propId)可以获取摄像头的一些属性,比如捕获的分辨率,亮度和对比度等。propId是从0~18的数字,代表不同的属性,完整的属性列表可以参考:VideoCaptureProperties。也可以使用cap.set(propId,value)来修改属性值

获取本地摄像头实时视频流

这里我发现设置摄像头分辨率效果并不理想🤔,应该是受硬件限制吧

# 使用 Python3.10 版本
import cv2

# 创建一个窗口 名字叫做CameraLive
cv2.namedWindow('CameraLive', flags=cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO | cv2.WINDOW_GUI_EXPANDED)

# 创建一个VideoCapture
cap = cv2.VideoCapture(0)

print('摄像头是否开启: {}'.format(cap.isOpened()))

# 显示缓存数
print(cap.get(cv2.CAP_PROP_BUFFERSIZE))
# 设置缓存区的大小
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)

# 调节摄像头分辨率
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)

print(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
print(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

# 设置FPS
print('setfps', cap.set(cv2.CAP_PROP_FPS, 25))
print(cap.get(cv2.CAP_PROP_FPS))

while (True):
    # 逐帧捕获
    ret, frame = cap.read()  # 第一个参数返回一个布尔值(True/False),代表有没有读取到图片;第二个参数表示截取到一帧的图片
    # gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    cv2.imshow('CameraLive', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# 当一切结束后,释放VideoCapture对象
cap.release()
cv2.destroyAllWindows()

此时执行程序,会生成一个名为CameraLive的窗口,同时摄像头被调用,实时的视频流将在窗口中展示

使用Flask进行推流

但,这样还是不能满足我的要求,我想让它能在web浏览器上运行,这样在一个局域网内的所有用户都将可以使用。在网上找了找,发现Flask可以很好的解决这个问题,同时又不会造成程序过于臃肿

# Python3.10

from flask import Flask, render_template, Response
import cv2


class VideoCamera(object):
    def __init__(self):
        # 通过opencv获取实时视频流
        self.video = cv2.VideoCapture(0)

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

    def get_frame(self):
        success, image = self.video.read()
        # 因为opencv读取的图片并非jpeg格式,因此要用motion JPEG模式需要先将图片转码成jpg格式图片
        ret, jpeg = cv2.imencode('.jpg', image)
        return jpeg.tobytes()


app = Flask(__name__)


@app.route('/')  # 主页
def index():
    # jinja2模板,具体格式保存在index.html文件中
    return render_template('index.html')


def gen(camera):
    while True:
        frame = camera.get_frame()
        # 使用generator函数输出视频流, 每次请求输出的content类型是image/jpeg
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')


@app.route('/video_feed')  # 这个地址返回视频流响应
def video_feed():
    return Response(gen(VideoCamera()),
                    mimetype='multipart/x-mixed-replace; boundary=frame')


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

程序运行后,在浏览器里输入 http://127.0.0.1:9000/video_feed 即可访问主机摄像头实时视频画面


总结

进行到此,我们已经能完美在浏览器上访问视频流了,但要保证视频流的流畅,需要保证主机拥有足够的上行带宽,在程序运行的时候,据观察,以手中的MacBook Air 的720p摄像头作为实验设备,在局域网中用其他设备访问该摄像头的视频流,主机的上行带宽每个设备均会使用5~7MB/s,设备越多,主机的上行带宽的使用速率越大,这样的结果会导致如果主机的网卡性能无法满足上行带宽的最大需求,访问的设备达到一定量的时候会导致实时视频流卡顿