主要内容:
学习目标
在OpenCV中我们要获取一个视频,需要创建一个VideoCapture对象,指定你要读取的视频文件:
cap = cv.VideoCapture(filepath)
参数:
视频的属性信息
2.1. 获取视频的某些属性
retval = cap.get(propId)
参数:
propId: 从0到18的数字,每个数字表示视频的属性
常用属性有:
2.2 修改视频的属性信息
cap.set(propId,value)
参数:
判断图像是否读取成功
isornot = cap.isOpened()
ret, frame = cap.read()
参数:
调用cv.imshow()显示图像,在显示图像时使用cv.waitkey()设置适当的持续时间,如果太低视频会播放的非常快,如果太高就会播放的非常慢,通常情况下我们设置25ms就可以了。
最后,调用cap.realease()将视频释放掉
示例:
我的文件路径:
import numpy as np
import cv2 as cv
# 1 读取视频
# 路径一定要确保正确,不然显示不出来窗口!
cap = cv.VideoCapture("../image/DOG.wmv")
# 2 判断是否读取成功
while (cap.isOpened()):
# 3 获取每一帧图像
ret, frame = cap.read()
# 4 是否获取成功
if ret == True:
cv.imshow("frame", frame)
if cv.waitKey(25) & 0xFF == ord("q"):
break
cap.release()
cv.destroyAllWindows()
在OpenCV中我们保存视频使用的是VedioWriter对象,在其中指定输出文件的名称,如下所示:
out = cv2.VideoWriter(filename,fourcc, fps, frameSize)
参数:
retval = cv2.VideoWriter_fourcc( c1, c2, c3, c4 )
参数:
c1,c2,c3,c4: 是视频编解码器的4字节代码,在fourcc.org中找到可用代码列表,与平台紧密相关,常用的有:
在Windows中:DIVX(.avi)
在OS中:MJPG(.mp4),DIVX(.avi),X264(.mkv)。
利用cap.read()获取视频中的每一帧图像,并使用out.write()将某一帧图像写入视频中。
使用cap.release()和out.release()释放资源。
示例:
import cv2 as cv
import numpy as np
# 1. 读取视频
cap = cv.VideoCapture("../image/DOG.wmv")
# 2. 获取图像的属性(宽和高,),并将其转换为整数
frame_width = int(cap.get(3))
frame_height = int(cap.get(4))
# 3. 创建保存视频的对象,设置编码格式,帧率,图像的宽高等
out = cv.VideoWriter('out1.avi',cv.VideoWriter_fourcc('M','J','P','G'), 10, (frame_width,frame_height))
while(True):
# 4.获取视频中的每一帧图像
ret, frame = cap.read()
if ret == True:
# 5.将每一帧图像写入到输出文件中
out.write(frame)
else:
break
# 6.释放资源
cap.release()
out.release()
cv.destroyAllWindows()
学习目标
meanshift算法的原理很简单。假设你有一堆点集,还有一个小的窗口,这个窗口可能是圆形的,现在你可能要移动这个窗口到点集密度最大的区域当中。
如下图:
最开始的窗口是蓝色圆环的区域,命名为C1。蓝色圆环的圆心用一个蓝色的矩形标注,命名为C1_o。
而窗口中所有点的点集构成的质心在蓝色圆形点C1_r处,显然圆环的形心和质心并不重合。所以,移动蓝色的窗口,使得形心与之前得到的质心重合。在新移动后的圆环的区域当中再次寻找圆环当中所包围点集的质心,然后再次移动,通常情况下,形心和质心是不重合的。不断执行上面的移动过程,直到形心和质心大致重合结束。 这样,最后圆形的窗口会落到像素分布最大的地方,也就是图中的绿色圈,命名为C2。
meanshift算法除了应用在视频追踪当中,在聚类,平滑等等各种涉及到数据以及非监督学习的场合当中均有重要应用,是一个应用广泛的算法。
图像是一个矩阵信息,如何在一个视频当中使用meanshift算法来追踪一个运动的物体呢? 大致流程如下:
首先在图像上选定一个目标区域
计算选定区域的直方图分布,一般是HSV色彩空间的直方图。
对下一帧图像b同样计算直方图分布。
计算图像b当中与选定区域直方图分布最为相似的区域,使用meanshift算法将选定区域沿着最为相似的部分进行移动,直到找到最相似的区域,便完成了在图像b中的目标追踪。
重复3到4的过程,就完成整个视频目标追踪。
通常情况下我们使用直方图反向投影得到的图像和第一帧目标对象的起始位置,当目标对象的移动会反映到直方图反向投影图中,meanshift 算法就把我们的窗口移动到反向投影图像中灰度密度最大的区域了。如下图所示:
直方图反向投影的流程是:
假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找的过程是这样的:
在OpenCV中实现Meanshift的API是:
cv.meanShift(probImage, window, criteria)
参数:
实现Meanshift的主要流程是:
示例:
import numpy as np
import cv2 as cv
# 1.获取图像
cap = cv.VideoCapture("../image/DOG.wmv")
# 2.获取第一帧图像,并指定目标位置
ret,frame = cap.read()
# 2.1 目标位置(行,高,列,宽)
r,h,c,w = 197,141,0,208
track_window = (c,r,w,h)
# 2.2 指定目标的感兴趣区域
roi = frame[r:r+h, c:c+w]
# 3. 计算直方图
# 3.1 转换色彩空间(HSV)
hsv_roi = cv.cvtColor(roi, cv.COLOR_BGR2HSV)
# 3.2 去除低亮度的值
# mask = cv.inRange(hsv_roi, np.array((0., 60.,32.)), np.array((180.,255.,255.)))
# 3.3 计算直方图
roi_hist = cv.calcHist([hsv_roi],[0],None,[180],[0,180])
# 3.4 归一化
cv.normalize(roi_hist,roi_hist,0,255,cv.NORM_MINMAX)
# 4. 目标追踪
# 4.1 设置窗口搜索终止条件:最大迭代次数,窗口中心漂移最小值
term_crit = ( cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 1 )
while(True):
# 4.2 获取每一帧图像
ret ,frame = cap.read()
if ret == True:
# 4.3 计算直方图的反向投影
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
dst = cv.calcBackProject([hsv],[0],roi_hist,[0,180],1)
# 4.4 进行meanshift追踪
ret, track_window = cv.meanShift(dst, track_window, term_crit)
# 4.5 将追踪的位置绘制在视频上,并进行显示
x,y,w,h = track_window
img2 = cv.rectangle(frame, (x,y), (x+w,y+h), 255,2)
cv.imshow('frame',img2)
if cv.waitKey(60) & 0xFF == ord('q'):
break
else:
break
# 5. 资源释放
cap.release()
cv.destroyAllWindows()
下面是三帧图像的跟踪结果:
大家认真看下上面的结果,有一个问题,就是检测的窗口的大小是固定的,而狗狗由近及远是一个逐渐变小的过程,固定的窗口是不合适的。所以我们需要根据目标的大小和角度来对窗口的大小和角度进行修正。CamShift可以帮我们解决这个问题。
CamShift算法全称是“Continuously Adaptive Mean-Shift”(连续自适应MeanShift算法),是对MeanShift算法的改进算法,可随着跟踪目标的大小变化实时调整搜索窗口的大小,具有较好的跟踪效果。
Camshift算法首先应用meanshift,一旦meanshift收敛,它就会更新窗口的大小,还计算最佳拟合椭圆的方向,从而根据目标的位置和大小更新搜索窗口。如下图所示:
Camshift在OpenCV中实现时,只需将上述的meanshift函数改为Camshift函数即可:
将Camshift中的:
# 4.4 进行meanshift追踪
ret, track_window = cv.meanShift(dst, track_window, term_crit)
# 4.5 将追踪的位置绘制在视频上,并进行显示
x,y,w,h = track_window
img2 = cv.rectangle(frame, (x,y), (x+w,y+h), 255,2)
改为:
#进行camshift追踪
ret, track_window = cv.CamShift(dst, track_window, term_crit)
# 绘制追踪结果
pts = cv.boxPoints(ret)
pts = np.int0(pts)
img2 = cv.polylines(frame,[pts],True, 255,2)
这种方法,在我那个视频里并没有meanshift方法好,后面会检测不到了,因为背景色和目标颜色接近时,容易使目标的区域变大,最终导致了目标跟踪丢失。
Meanshift和camshift算法都各有优势,自然也有劣势:
meanshift
原理:一个迭代的步骤,即先算出当前点的偏移均值,移动该点到其偏移均值,然后以此为新的起始点,继续移动,直到满足一定的条件结束。
API:cv.meanshift()
优缺点:简单,迭代次数少,但无法解决目标的遮挡问题并且不能适应运动目标的的形状和大小变化
camshift
原理:对meanshift算法的改进,首先应用meanshift,一旦meanshift收敛,它就会更新窗口的大小,还计算最佳拟合椭圆的方向,从而根据目标的位置和大小更新搜索窗口。
API:cv.camshift()
优缺点:可适应运动目标的大小形状的改变,具有较好的跟踪效果,但当背景色和目标颜色接近时,容易使目标的区域变大,最终有可能导致目标跟踪丢失
学习目标
我们使用机器学习的方法完成人脸检测,首先需要大量的正样本图像(面部图像)和负样本图像(不含面部的图像)来训练分类器。我们需要从其中提取特征。下图中的 Haar 特征会被使用,就像我们的卷积核,每一个特征是一 个值,这个值等于黑色矩形中的像素值之后减去白色矩形中的像素值之和。
Haar特征值反映了图像的灰度变化情况。例如:脸部的一些特征能由矩形特征简单的描述,眼睛要比脸颊颜色要深,鼻梁两侧比鼻梁颜色要深,嘴巴比周围颜色要深等。
Haar特征可用于于图像任意位置,大小也可以任意改变,所以矩形特征值是矩形模版类别、矩形位置和矩形大小这三个因素的函数。故类别、大小和位置的变化,使得很小的检测窗口含有非常多的矩形特征。
得到图像的特征后,训练一个决策树构建的adaboost级联决策器来识别是否为人脸。
OpenCV中自带已训练好的检测器,包括面部,眼睛,猫脸等,都保存在XML文件中,我们可以通过以下程序找到他们:
import cv2 as cv
print(cv.__file__)
找到的文件如下所示:
那我们就利用这些文件来识别人脸,眼睛等。检测流程如下:
# 实例化级联分类器
classifier =cv.CascadeClassifier( "haarcascade_frontalface_default.xml" )
# 加载分类器
classifier.load('haarcascade_frontalface_default.xml')
rect = classifier.detectMultiScale(gray, scaleFactor, minNeighbors, minSize,maxsize)
参数:
主程序如下所示:
import cv2 as cv
import matplotlib.pyplot as plt
# 1.以灰度图的形式读取图片
img = cv.imread("/content/drive/My Drive/Colab/Notebooks/OpenCV/code/image/yangzi.jpg")
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 2.实例化OpenCV人脸和眼睛识别的分类器
face_cas = cv.CascadeClassifier( "/content/drive/My Drive/Colab/Notebooks/OpenCV/code/haarcascade_frontalface_default.xml" )
face_cas.load('/content/drive/My Drive/Colab/Notebooks/OpenCV/code/haarcascade_frontalface_default.xml')
eyes_cas = cv.CascadeClassifier("/content/drive/My Drive/Colab/Notebooks/OpenCV/code/haarcascade_eye.xml")
eyes_cas.load("/content/drive/My Drive/Colab/Notebooks/OpenCV/code/haarcascade_eye.xml")
# 3.调用识别人脸
faceRects = face_cas.detectMultiScale( gray, scaleFactor=1.2, minNeighbors=3, minSize=(32, 32))
for faceRect in faceRects:
x, y, w, h = faceRect
# 框出人脸
cv.rectangle(img, (x, y), (x + h, y + w),(0,255,0), 3)
# 4.在识别出的人脸中进行眼睛的检测
roi_color = img[y:y+h, x:x+w]
roi_gray = gray[y:y+h, x:x+w]
eyes = eyes_cas.detectMultiScale(roi_gray)
for (ex,ey,ew,eh) in eyes:
cv.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)
# 5. 检测结果的绘制
plt.figure(figsize=(8,6),dpi=100)
plt.imshow(img[:,:,::-1]),plt.title('检测结果')
plt.xticks([]), plt.yticks([])
plt.show()
结果:
图片中人脸检测可以在Colab中运行,但视频中的人脸检测只好在本机的pycharm中运行了。。。。感觉cpu要炸了。
我们也可在视频中对人脸进行检测:
import cv2 as cv
import matplotlib.pyplot as plt
# 1.读取视频
cap = cv.VideoCapture("image/face_girl.mp4")
# 2.在每一帧数据中进行人脸识别
while (cap.isOpened()):
ret, frame = cap.read()
if ret == True:
gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
# 3.实例化OpenCV人脸识别的分类器
face_cas = cv.CascadeClassifier("haarcascade_frontalface_default.xml")
face_cas.load('haarcascade_frontalface_default.xml')
# 4.调用识别人脸
faceRects = face_cas.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=3, minSize=(32, 32))
for faceRect in faceRects:
x, y, w, h = faceRect
# 框出人脸
cv.rectangle(frame, (x, y), (x + h, y + w), (0, 255, 0), 3)
cv.imshow("frame", frame)
if cv.waitKey(1) & 0xFF == ord('q'):
break
# 5. 释放资源
cap.release()
cv.destroyAllWindows()
下面是我运行的结果,截取了三张图,小女孩从远处跑过来,图像逐渐放大,发现检测效果还行。不过这种代码运行起来,我的电脑散热器就响的很大声,我还是在图书馆…还好上午图书馆人少,加上我做的比较远,要不然真不敢运行!
存在问题:在检测这个的时候,会有时在其他背景的地方多一两个检测框,一些视频帧无法检测正确;另外在检测这个视频之前,我还检测了另一个视频,那个视频的人物是在左下角,而且有17s,检测不出来人脸。感觉视频越短,人物显示在中间位置的话,检测效果应该会更好!
2022/3/9 15:33 补充:
上午搞完回寝室睡觉了,然后一觉醒来突然有个灵感,我之前不是学习到了视频读取和视频保存吗,然后我就想能不能把我视频中人脸检测的结果输出并保存到一个视频中,这样方便展示。
于是我又搜索资料,改进了代码如下:
import cv2 as cv
import matplotlib.pyplot as plt
# 1.读取视频
cap = cv.VideoCapture("image/face_girl.mp4")
# 得到视频长度
length = int(cap.get(cv.CAP_PROP_FRAME_COUNT))
# 载入视频流
fourcc = cv.VideoWriter_fourcc(*'XVID')
# 第一个参数是要保存的文件的路径
# fourcc 指定编码器
# fps 要保存的视频的帧率
# frameSize 要保存的文件的画面尺寸
# isColor 指示是黑白画面还是彩色的画面
# fourcc
# fourcc 本身是一个 32 位的无符号数值,用 4 个字母表示采用的编码器。
# 常用的有 “DIVX"、”MJPG"、“XVID”、“X264"。可用的列表在这里。
# 推荐使用 ”XVID",但一般依据你的电脑环境安装了哪些编码器。
output_movie = cv.VideoWriter('girl_result.avi', fourcc, 25, (1920, 1080))
# 2.在每一帧数据中进行人脸识别
while (cap.isOpened()):
ret, frame = cap.read()
if ret == True:
gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
# 3.实例化OpenCV人脸识别的分类器
face_cas = cv.CascadeClassifier("haarcascade_frontalface_default.xml")
face_cas.load('haarcascade_frontalface_default.xml')
# 4.调用识别人脸
faceRects = face_cas.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=3, minSize=(32, 32))
for faceRect in faceRects:
x, y, w, h = faceRect
# 框出人脸
cv.rectangle(frame, (x, y), (x + h, y + w), (0, 255, 0), 3)
cv.imshow("frame", frame)
if cv.waitKey(1) & 0xFF == ord('q'):
break
output_movie.write(frame)
# 5. 释放资源
cap.release()
cv.destroyAllWindows()
然后真的成功了!
结果如下:
我是真的挺激动的,哈哈哈哈哈,以后多睡觉,兴许会冒出一些有趣的想法并着手去实现!!!!(手动狗头)
opencv中人脸识别的流程是:
# 实例化级联分类器
classifier =cv.CascadeClassifier( "haarcascade_frontalface_default.xml" )
# 加载分类器
classifier.load('haarcascade_frontalface_default.xml')
rect = classifier.detectMultiScale(gray, scaleFactor, minNeighbors, minSize,maxsize)
将检测结果绘制出来就可以了。
我们也可以在视频中进行人脸识别。