在Opencv初接触,图片的基本操作这篇手记中,我介绍了一些图片的基本操作,视频可以看作是一帧一帧的图片,因此图片操作其实是视频操作的基础,这篇手记就来讲讲OpenCV中的视频操作,并实现一个用笔记本自带的摄像头探测人脸的小功能,最后会把人脸提取出来并保存,作为后续用卷积神经网络进行人脸识别的训练数据。
本地视频读取
OpenCV中是通过创建一个VideoCapture
对象来捕获视频,这里先以读取和播放本地视频为例:
import cv2
cap = cv2.VideoCapture('clementi sonatina in f major, op. 36 no. 4.mp4') # 注意这里视频放在当前工作目录下,VideoCapture的参数就是视频文件名
while(cap.isOpened()): # isOpened方法判断视频是否成功打开
ret, frame = cap.read() # read方法返回一个表示视频是否正确读取的布尔值和一帧图像
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 这里用cvtColor(cvt就是convert的缩写)方法进行色彩空间的转换,这里是从BGR空间转换到灰度空间
cv2.imshow('frame', gray) # 通过imshow显示一帧图像
if cv2.waitKey(1) & 0xFF == ord('q'): # 一帧显示一毫秒,通过上面的while循环不断地显示下一帧,从而形成动态的视频;按q键退出循环,关闭视频。
break
cap.release()
cv2.destroyAllWindows()
通过笔记本摄像头捕获实时视频流
通过上面读取本地视频的代码,可以理解OpenCV中显示视频的原理就是通过循环不断显示下一帧图像从而得到动态的视频,下面,只需改变VideoCapture
的参数就可以捕获笔记本电脑摄像头的实时视频流:
import cv2 #引入cv2,也就是引入opencv的一些包和处理类,不然下面的一些操作都无法完成
#打开摄像头的方法,window_name为显示窗口名,video_id为你设备摄像头的id,默认为0或-1,如果引用usb可能会改变为1,等
def openvideo(window_name ,video_id):
cv2.namedWindow(window_name) # 创建一个窗口
cap=cv2.VideoCapture(video_id) # 获取摄像头
while cap.isOpened():
ok,frame=cap.read() # ok表示摄像头读取状态,frame表示摄像头读取的图像
if not ok :
break
cv2.imshow(window_name,frame) # 将图像矩阵显示在一个窗口中
c=cv2.waitKey(10) # 等待10ms,10ms内没有按键操作就进入下一次while循环,从而得到10ms一帧的效果,waitKey返回在键盘上按的键
if c & 0xFF==ord('q'): # 按键q后break
break
# 释放资源
cap.release()
cv2.destroyWindow(window_name)
print("cam closed")
# 主程序调用方法运行
if __name__ == '__main__': # __name__ 是当前模块名,当模块被直接运行时模块名为 __main__ 。这句话的意思就是,当模块被直接运行时,以下代码块将被运行,当模块是被导入时,代码块不被运行。
print ('open camera...')
openvideo('mycam' ,0)
从实时视频流中探测人脸
如果你跟着上面的代码成功运行到了这一步,应该已经可以在打开的摄像头窗口里看到自己的脸了,下面要做的就是从里面探测到你的脸了:
import cv2
def facedetect(windowname,camera_id):
#命名和打开摄像头
cv2.namedWindow(windowname) # 创建一个已windowname为名字的窗口
cap=cv2.VideoCapture(camera_id) # camera_id为设备摄像头的id,默认是0,如果有usb摄像头可能会变为1
# Face Detection using Haar Cascades http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_objdetect/py_face_detection/py_face_detection.html?highlight=cascadeclassifier
classfier=cv2.CascadeClassifier('haarcascade_frontalface_alt2.xml') # 加载分类器,分类器位置可以自行更改,注意这里用opencv库文件夹下的绝对路径也不行,在库文件夹里找到这个文件复制到这个程序的同目录下,参考:https://blog.csdn.net/GAN_player/article/details/77993872
color=(0,225,0)#人脸框的颜色,采用rgb模型,这里表示g取255,为绿色框
while cap.isOpened():
ok,frame=cap.read() # 读取一帧数据,ok表示摄像头读取状态,frame表示摄像头读取的图像矩阵mat类型
if not ok:
break
# 灰度化在后面检测时可以降低计算量,cvtColor changing colorspace
gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)#图像灰度化,cv2.cvtColor(input_image, flag) where flag determines the type of conversion.
这一部分代码在前面捕获实时视频流的基础上加载了OpenCV所提供的用于对象检测的分类器,注意分类器文件haarcascade_frontalface_alt2.xml可以在OpenCV包文件夹下找到,要放到当前工作目录下才能使用,这个分类器就是用来探测人脸的,在探测到人脸后,我们要在视频窗口中把人脸部分框出来,color
变量就用来指定框的颜色,接着我们就来看看得到人脸框的代码:
# detectMultiScale完成人脸探测工作,returns the positions of detected faces as Rect(x,y,w,h),x、y是左上角起始坐标,h、w是高和宽
# grey是要识别的图像数据,scaleFactor图像缩放比例,可以理解为同一个物体与相机距离不同,其大小亦不同,必须将其缩放到一定大小才方便识别,该参数指定每次缩放的比例
faceRects=classfier.detectMultiScale(gray,scaleFactor=1.2,minNeighbors=3,minSize=(32,32)) # 利用分类器检测灰度图像中的人脸矩阵数,1.2和3分别为图片缩放比例和需要检测的有效点数
if len(faceRects)>0:#大于0则检测到人脸
for faceRect in faceRects:# 可能检测到多个人脸,用for循环单独框出每一张人脸
x,y,w,h=faceRect#获取框的左上的坐标,框的长宽
# cv2.rectangle()完成画框的工作,这里外扩了10个像素以框出比人脸稍大一点的区域,从而得到相对完整一点的人脸图像;cv2.rectangle()函数的最后两个参数一个用于指定矩形边框的颜色,一个用于指定矩形边框线条的粗细程度。
cv2.rectangle(frame,(x-10,y-10),(x+w-10,y+h-10),color,2)
cv2.imshow(windowname,frame) # 显示图像
c=cv2.waitKey(10)
if c&0xFF==ord('q'): # 退出条件
break
cap.release()#释放摄像头并销毁所有窗口
cv2.destroyAllWindows()
if __name__ == '__main__':#主程序
print ('face detecting... ')
facedetect('facedetect',0)
现在,运行上面的程序,应该就能在视频窗口里看到你的脸被绿色的框框起来了,下一步我们要把框出来的人脸保存下来。
保存人脸图像数据
上面的程序里我们已经得到了人脸区域的起始坐标和宽高,只需利用Opencv初接触,图片的基本操作这篇手记中提到的访问图片特定像素和区域的方法把这一区域单独拿出来存成图片就行了,这里的关键代码是:
image = frame[y:y+h, x:x+w] # 将当前帧含人脸部分保存为图片,注意这里存的还是彩色图片,前面检测时灰度化是为了降低计算量;这里访问的是从y位开始到y+h-1位
cv2.imwrite(image_name, image)
最后给出捕获笔记本摄像头探测人脸并存储人脸图片程序的完整代码,这也是这个人脸识别项目中的第一部分功能:
import cv2
def getTrainingData(window_name, camera_id, path_name, max_num): # path_name是图片存储目录,max_num是需要捕捉的图片数量
cv2.namedWindow(window_name) # 创建窗口
cap = cv2.VideoCapture(camera_id) # 打开摄像头
classifier = cv2.CascadeClassifier('haarcascade_frontalface_alt2.xml') # 加载分类器
color = (0,255,0) # 人脸矩形框的颜色
num = 0 # 记录存储的图片数量
while cap.isOpened():
ok, frame = cap.read()
if not ok:
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 灰度化
faceRects=classifier.detectMultiScale(gray,scaleFactor=1.2,minNeighbors=3,minSize=(32,32))
if len(faceRects) > 0:
for faceRect in faceRects:
x,y,w,h = faceRect
# 捕捉到的图片的名字,这里用到了格式化字符串的输出
image_name = '%s%d.jpg' % (path_name, num) # 注意这里图片名一定要加上扩展名,否则后面imwrite的时候会报错:could not find a writer for the specified extension in function cv::imwrite_ 参考:https://stackoverflow.com/questions/9868963/cvimwrite-could-not-find-a-writer-for-the-specified-extension
image = frame[y:y+h, x:x+w] # 将当前帧含人脸部分保存为图片,注意这里存的还是彩色图片,前面检测时灰度化是为了降低计算量;这里访问的是从y位开始到y+h-1位
cv2.imwrite(image_name, image)
num += 1
# 超过指定最大保存数量则退出循环
if num > max_num:
break
cv2.rectangle(frame, (x,y), (x+w,y+h), color, 2) # 画出矩形框
font = cv2.FONT_HERSHEY_SIMPLEX # 获取内置字体
cv2.putText(frame, ('%d'%num