实现效果
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实现一下吧。