实现效果

windows平台笔记本摄像头视频采集、人脸识别,识别后将视频推流到RTMP流媒体服务器,在任意客户端可以进行RTMP拉流播放。

效果如图:
在这里插入图片描述

使用VLC播放器进行拉流。

准备工作

需要先安装OpenCV的python包以及FFmpeg。

对于ffmpeg有两种调用方式,但这两种方式都需要先安装ffmpeg,调用的具体区别是:

  • 使用管道通信的方式,调用FFmpeg可执行文件,通过管道写入视频帧数据,交给FFmpeg编码、推流;
  • 也可以安装ffmpeg-python包,这个包封装了对FFmpeg的调用,最终也是通过管道通信实现数据传递的。

推荐直接用第一种方式。

人脸检测实现

首先要区分说明一下,人脸检测与人脸识别是不一样的。检测只是将图像中的人脸框出或作其他突出显示,人脸识别则需要预先将人脸录入,当图像、视频中出现人脸时,对人脸进行检测,再将得到数据与录入的进行匹配,识别判断,人脸考勤机是人脸识别最常见的应用。

因此,人脸识别要比人脸检测更复杂一些。

利用Python opencv的Haar特征检测,可以很方便实现人脸检测的效果。

示例代码:

import cv2

# 人脸检测器模型文件路径
# face_cascade = cv2.CascadeClassifier('C:/Users/ACER/AppData/Local/Programs/Python/Python37/Lib/site-packages/cv2/data/haarcascade_frontalface_default.xml') #pip安装opencv包路径下的模型文件
face_cascade = cv2.CascadeClassifier('D:/opencv/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml')
# 打开摄像头
cap = cv2.VideoCapture(0)

while True:
    # 读取一帧
    ret, frame = cap.read()

    # 将图片转换为灰度图像
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    # 检测人脸
    faces = face_cascade.detectMultiScale(gray, 1.3, 5)

    # 在检测到的每张人脸周围画一个矩形
    for (x,y,w,h) in faces:
        cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)

    # 显示帧
    cv2.imshow('frame',frame)

    # 如果按下q键,退出循环
    if cv2.waitKey(1) & 0xFF == 27:
        print("Press Esc, to exit.")
        break

# 释放摄像头
cap.release()

# 关闭所有窗口
cv2.destroyAllWindows()

代码流程:

  • 构建一个Haar级联分类器,调用CascadeClassifier即可,传入的参数为人脸检测器模型文件路径:

    cv2.CascadeClassifier('C:/Users/ACER/AppData/Local/Programs/Python/Python37/Lib/site-packages/cv2/data/haarcascade_frontalface_default.xml')
    cv2.CascadeClassifier('D:/opencv/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml')
    

    可以使用pip安装opencv包的路径下的模型文件,也可以使用自己安装的opencv中的模型文件,效果相同。

    'C:/Users/ACER/AppData/Local/Programs/Python/Python37/Lib/site-packages/cv2/data/haarcascade_frontalface_default.xml'是使用pip安装opencv-python时下载的;

    'D:/opencv/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml'则是我自己安装opencv库时下载的。

  • 调用VideoCapture采集视频帧,并将其转为灰度图,传递给人脸级联分类器face_cascade;

  • 得到分类后的矩形坐标后,在原有数据上绘制出矩形框,即可将人脸框出,调用imshow方法将处理后的图像显示。

调用FFmpeg实现RTMP推流

前面说了有两种方式。

1、直接调用ffmpeg命令

实现思路:调用FFmpeg,在后台开一个子进程,视频帧数据通过这个子进程标准输入写入,数据经过子进程处理后推流到RTMP服务器。

ffmpeg安装后需要添加到windows环境变量,确保在命令行可以直接调用。

import cv2
import subprocess
# 打开摄像头
cap = cv2.VideoCapture(0)

# 设置摄像头分辨率
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 2)

fps = cap.get(cv2.CAP_PROP_FPS)
print("fps:", fps)
# 设置缓冲区大小为2

# 定义视频编码器
fourcc = cv2.VideoWriter_fourcc(*'X264')

# 创建FFmpeg命令行参数
ffmpeg_cmd = ['ffmpeg',
              '-y',  # 覆盖已存在的文件
              '-f', 'rawvideo',
              '-pixel_format', 'bgr24',
              '-video_size', '640x480',
              '-i', '-',  # 从标准输入读取数据
              '-c:v', 'libx264', #使用x264编码器
              '-preset', 'ultrafast',
              '-tune', 'zerolatency',#零延迟
              '-pix_fmt', 'yuv420p',
              '-f', 'flv',
              'rtmp://120.79.54.142:1935/live/cv_demo']

# 启动FFmpeg进程
ffmepg_process = subprocess.Popen(ffmpeg_cmd, stdin=subprocess.PIPE)
# 开始采集和推流
while True:
    # 采集一帧图像
    ret, frame = cap.read()
    if ret:
        # 通过FFmpeg编码和推流
        ffmepg_process.stdin.write(frame.tobytes())

# 停止FFmpeg进程并释放资源
ffmepg_process.stdin.close()
ffmepg_process.wait()
cap.release()

2、使用ffmpeg-python包

chatgpt给出的示例,存在一些小问题,没有使用这种方式,暂未深究。

import cv2
import ffmpeg

# 打开本地摄像头
cap = cv2.VideoCapture(0)

# 设置编码参数
output_size = (640, 480)
fps = 30
codec = "libx264"
bitrate = "1000k"

# 设置输出流
rtmp_url = "rtmp://your-rtmp-server-url.com/live/stream_key"
out = ffmpeg.output(
    ffmpeg.input('pipe:', format='rawvideo', pix_fmt='bgr24', s='{}x{}'.format(*output_size), r=fps),
    ffmpeg.format('flv'),
    rtmp_url,
    vcodec=codec,
    b=bitrate
)

# 打开输出流
process = out.run_async(pipe_stdin=True)

# 循环读取每一帧图像,并将其写入输出流
while True:
    ret, frame = cap.read()
    if not ret:
        break
    process.stdin.write(frame.tobytes())

# 关闭输入流和输出流
cap.release()
process.stdin.close()
process.wait()

拉流验证

推流成功后,可以使用VLC播放器拉流验证,使用网络串流功能,输入推流的url,播放即可。

在这里插入图片描述

狗头是自己加的:)。

视频采集+人脸检测+RTMP推流

将上面的两个例子组合一下,即可实现。

人生苦短,我用Python,直接上代码:

import cv2
import ffmpeg
import subprocess

# 人脸检测器
class FaceDetector:
    def __init__(self, module_file):
        self.module_file = module_file
        self.face_cascade = cv2.CascadeClassifier(self.module_file)

    def detectFace(self, gray_img):
        face_rect = self.face_cascade.detectMultiScale(gray_img, 1.3, 5)
        return face_rect
# 推流器
class StreamPusher:
    def __init__(self, rtmp_url):
        # 创建FFmpeg命令行参数
        ffmpeg_cmd = ['ffmpeg',
                      '-y',  # 覆盖已存在的文件
                      '-f', 'rawvideo',
                      '-pixel_format', 'bgr24',
                      '-video_size', '640x480',
                      '-i', '-',  # 从标准输入读取数据
                      '-c:v', 'libx264',
                      '-preset', 'ultrafast',
                      '-tune', 'zerolatency',
                      '-pix_fmt', 'yuv420p',
                      '-f', 'flv',
                      rtmp_url]
        print('ffmpeg_cmd:', ffmpeg_cmd)
        # 启动 ffmpeg
        self.ffmepg_process = subprocess.Popen(ffmpeg_cmd, stdin=subprocess.PIPE)

    def streamPush(self, frame):
        self.ffmepg_process.stdin.write(frame.tobytes())

# 人脸检测器模型文件路径
module_file = 'D:/opencv/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml'
rtmp_server = 'rtmp://120.79.54.142:1935/live/cv_demo'

# program entry
if __name__ == '__main__':
    dectector = FaceDetector(module_file)
    pusher = StreamPusher(rtmp_server)
    # 打开摄像头
    cap = cv2.VideoCapture(0)
    while True:
        # 读取一帧
        ret, frame = cap.read()
        # 将图片转换为灰度图像
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        # 检测人脸
        faces = dectector.detectFace(gray)
        # 在检测到的每张人脸周围画一个矩形
        for (x,y,w,h) in faces:
            cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)
        # 显示帧
        cv2.imshow('frame',frame)
        # 如果按下Esc键,退出循环
        if cv2.waitKey(1) & 0xFF == 27:
            print("Press Esc, to exit.")
            break
        pusher.streamPush(frame)
    # 释放摄像头
    cap.release()
    # 关闭所有窗口
    cv2.destroyAllWindows()

总结

使用python可以快速实现功能,但延迟还是有点高,画面也不太流畅,还有优化空间。有空还是用CPP实现一下吧。

GitHub 加速计划 / opencv31 / opencv
166
15
下载
OpenCV: 开源计算机视觉库
最近提交(Master分支:4 个月前 )
fe9405e8 * fix a small typo * removal of unused variable 6 小时前
a2a3f5e8 Improve robustness for ellipse fitting #26773 ### Pull Request Readiness Checklist Related to #26694 Current noise addition is not very good because for example it turns degenerate case of one horizontal line into degenerate case of two parallel horizontal lines Improving noise addition leads to improved robustness of algorithms See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake 9 小时前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐