树莓派4B--简单场景下人脸追踪
我们将利用OpenCV完成简易的人脸目标追踪,我们实现了目标追踪功能,每一个识别到的目标都有一个单独的编号,那么就可以利用这个性能搭建一个计数器。比如,如果被要求识别的目标是人脸,我们就可以搭建一个计算人数的计数器。本实验配套.
文章目录
前言
本实验将借鉴陈佳林的《智能硬件与机器视觉基于树莓派、Python 和OpenCV》这本书使用OpenCV和Python实现简单场景下人脸追踪:我们将利用OpenCV完成简易的人脸目标追踪,我们实现了目标追踪功能,每一个识别到的目标都有一个单独的编号,那么就可以利用这个性能搭建一个计数器。比如,如果被要求识别的目标是人脸,我们就可以搭建一个计算人数的计数器。
本实验配套代码CSDN地址为:
https://download.csdn.net/download/qq_50492541/74147603
本实验配套代码百度网盘地址为:
https://pan.baidu.com/s/1PBdNvfpgp9ihcg49LMezrQ
提取码:p3dm
一、环境准备
1.所需设备及安装的软件包
所需设备:树莓派4B,树莓派3代广角摄像头500W像素
安装的软件包:OpenCV 3.3或更高版本,NumPy,SciPy,imutils。在安装之前请先确认你的电脑上是否安装了Python3,同时,这些软件包的安装过程,请参照网上教程或陈佳林的《智能硬件与机器视觉基于树莓派、Python 和OpenCV》这本书第三章,这里不再赘述。
2.文件目录结构
要实现一个人脸追踪器,所需要的代码文件的目录结构如下:
- centroidtracker.py:定义了CentroidTracker类,是 实现形心追踪算法的基础。
- object_tracker.py:创建CentroidTracker的对象, 并实现人脸追踪。
- deploy.prototxt和 res10_300x300_ssd_iter_140000.caffemodel:OpenCV的 深度学习人脸检测器,将为形心追踪算法提供人脸识 别的功能。当然,这里也可以换成其他人脸识别器。
二、核心原理和效果简介
1.步骤介绍
识别视频中的人脸是OpenCV的一个经典应用,本实验我们将利用OpenCV完成简易的人脸目标追踪,实现以下几个步骤:
- 第1步:识别出视频中的某些特定目标,比如用绘制目标外界矩形的方法来表示。
- 第2步:为识别出的目标标上单独的编号。
- 第3步:目标在图像中移动时,它们的编号不发生变化。也就是说,如果找到一个1号目标,无论它在画面中怎样运动,只要不离开视野,我们应该认为它始终就是1号目标,1号目标也始终是它,而不是为它重新编号或者将其他目标编为1号。
更进一步,如果我们实现了上述的目标追踪功能,每一个识别到的目标都有一个单独的编号,那么就可以利用这个性能搭建一个计数器。比如,如果被要求识别的目标是人脸,我们就可以搭建一个计算人数的计数器。以上这些对于很多机器视觉与图像处理算法的要求都很高,会需要充足的空间和性能来运行更加强悍的深度学习算法。但是在本实验中我们直接利用Python和OpenCV实现一种简易的、便于理解的目标追踪算法——形心追踪算法。
2.定义CentroidTracker类
在centroidtracker.py中,定义了一个CentroidTracker类,这个类包含了注册、注销等功 能,用于实现形心追踪算法。#-*- coding: UTF-8 -*-
# 调用所需库
from scipy.spatial import distance as dist
from collections import OrderedDict
import numpy as np
class CentroidTracker():
def __init__(self, maxDisappeared=50):
# 初始化下一个新出现人脸的ID
# 初始化两个有序字典
# objects用来储存ID和形心坐标
# disappeared用来储存ID和对应人脸已连续消失的帧数
self.nextObjectID = 0
self.objects = OrderedDict()
self.disappeared = OrderedDict()
# 设置最大连续消失帧数
self.maxDisappeared = maxDisappeared
def register(self, centroid):
# 分别在两个字典中注册新人脸 并更新下一个新出现人脸的ID
self.objects[self.nextObjectID] = centroid
self.disappeared[self.nextObjectID] = 0
self.nextObjectID += 1
def deregister(self, objectID):
# 分别在两个字典中注销已经消失的人脸
del self.objects[objectID]
del self.disappeared[objectID]
def update(self, rects):
# 检查输入的人脸外接矩形列表是否为空
if len(rects) == 0:
# 对每一个注册人脸 标记一次消失
for objectID in self.disappeared.keys():
self.disappeared[objectID] += 1
# 当连续消失帧数超过最大值时 注销人脸
if self.disappeared[objectID] > self.maxDisappeared:
self.deregister(objectID)
# 因为没有识别到人脸 本次更新结束
return self.objects
# 对于当前帧 初始化外接矩形形心的存储矩阵
inputCentroids = np.zeros((len(rects), 2), dtype="int")
# 对于每一个矩形执行操作
for (i, (startX, startY, endX, endY)) in enumerate(rects):
# 计算形心
cX = int((startX + endX) / 2.0)
cY = int((startY + endY) / 2.0)
inputCentroids[i] = (cX, cY)
# 如果当前追踪列表为空 则说明这些矩形都是新人脸 需要注册
if len(self.objects) == 0:
for i in range(0, len(inputCentroids)):
self.register(inputCentroids[i])
# 否则需要和旧人脸进行匹配
else:
# 从字典中获取ID和对应形心坐标
objectIDs = list(self.objects.keys())
objectCentroids = list(self.objects.values())
# 计算全部已有人脸的形心与新图像中人脸的形心的距离 用于匹配
# 每一行代表一个旧人脸形心与所有新人脸形心的距离
# 元素的行代表旧人脸ID 列代表新人脸形心
D = dist.cdist(np.array(objectCentroids), inputCentroids)
# 接下来的两步用于匹配新物体和旧物体
# min(axis=1)获得由每一行中的最小值组成的向量 代表了距离每一个旧人脸最近的新人脸的距离
# argsort()将这些最小值进行排序 获得的是由相应索引值组成的向量
# rows里存储的是旧人脸的ID 排列顺序是按照‘最小距离’从小到大排序
rows = D.min(axis=1).argsort()
# argmin(axis=1)获得每一行中的最小值的索引值,代表了距离每一个旧人脸最近的新人脸的ID
# cols里存储的是新人脸的ID 按照rows排列
cols = D.argmin(axis=1)[rows]
# 为了防止重复更新、注册、注销 我们设置两个集合存储已经更新过的row和col
usedRows = set()
usedCols = set()
# 对于所有(row,col)的组合执行操作
for (row, col) in zip(rows, cols):
# row和col二者之一被检验过 就跳过
if row in usedRows or col in usedCols:
continue
# 更新已经匹配的人脸
objectID = objectIDs[row]
self.objects[objectID] = inputCentroids[col]
self.disappeared[objectID] = 0
# 更新以后将索引放入已更新的集合
usedRows.add(row)
usedCols.add(col)
# 计算未参与更新的row和col
unusedRows = set(range(0, D.shape[0])).difference(usedRows)
unusedCols = set(range(0, D.shape[1])).difference(usedCols)
# 如果距离矩阵行数大于列数 说明旧人脸数大于新人脸数
# 有一些人脸在这帧图像中消失了
if D.shape[0] >= D.shape[1]:
# 对于没有用到的旧人脸索引
for row in unusedRows:
# 获取它的ID 连续消失帧数+1
objectID = objectIDs[row]
self.disappeared[objectID] += 1
# 检查连续消失帧数是否大于最大允许值 若是则注销
if self.disappeared[objectID] > self.maxDisappeared:
self.deregister(objectID)
# 如果距离矩阵列数大于行数 说明新人脸数大于旧人脸数
# 有一些新人脸出现在了视频中 需要进行注册
else:
for col in unusedCols:
self.register(inputCentroids[col])
# 返回追踪列表
return self.objects
3.人脸追踪的实现
在object_tracker.py中,我们通过调用 CentroidTracker类和DNN人脸探测器来实现人脸追踪。当然,DNN只是人脸探测的一种方法,除此之外还有基于Haar特征的级联分类器、HOG+linear SVM、SSD、Faster R-CNN等机器学习和深度学习的方法,可以根据自己的掌握能力灵活使用。在这个文件中,我们需要做的工作包括如下几点。-
从视频图像流VideoStream中抓取图像。
-
加载并实现OpenCV中的人脸检测器。
-
实例化CentroidTracker类,实现基于形心追踪算法的人脸跟踪。
-
在图像中绘制正在跟踪的人脸外接矩形并标号。
object_tracker.py的具体代码如下所示:
from objecttracker.centroidtracker import CentroidTracker
from imutils.video import VideoStream
from imutils.video import FPS
import numpy as np
import argparse
import imutils
import cv2
import time
ap = argparse.ArgumentParser()
ap.add_argument("-p", "--prototxt", required=True,
help="path to Caffe 'deploy' prototxt file")
ap.add_argument("-m", "--model", required=True,
help="path to Caffe pre-trained model")
ap.add_argument("-c", "--confidence", type=float, default=0.5,
help="minimum probability to filter weak detections")
ap.add_argument("-v", "--video", type=str,
help="path to optinal input video file")
args = vars(ap.parse_args())
ct = CentroidTracker()
(H, W) = (None, None)
print("[INFO] loading model...")
net = cv2.dnn.readNetFromCaffe(args["prototxt"], args["model"])
if not args.get("video", False):
print("[INFO] starting video stream...")
vs = VideoStream(src=0).start()
time.sleep(1.0)
else:
vs = cv2.VideoCapture(args["video"])
fps = FPS().start()
while True:
frame = vs.read()
frame = frame[1] if args.get("video", False) else frame
if frame is None:
break
frame = imutils.resize(frame, width=400)
if W is None or H is None:
(H, W) = frame.shape[:2]
blob = cv2.dnn.blobFromImage(frame, 1.0, (W, H),(104.0, 177.0, 123.0))
net.setInput(blob)
detections = net.forward()
rects = []
for i in range(0, detections.shape[2]):
if detections[0, 0, i, 2] > args["confidence"]:
box = detections[0, 0, i, 3:7] * np.array([W, H, W, H])
rects.append(box.astype("int"))
(startX, startY, endX, endY) = box.astype("int")
cv2.rectangle(frame, (startX, startY), (endX, endY),(0, 255, 0), 2)
objects = ct.update(rects)
for (objectID, centroid) in objects.items():
text = "ID {}".format(objectID)
cv2.putText(frame, text, (centroid[0] - 10, centroid[1] - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
cv2.circle(frame, (centroid[0], centroid[1]), 4, (0, 255, 0), -1)
cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break
fps.stop()
print("[INFO] elasped time: {:.2f}".format(fps.elapsed()))
print("[INFO] approx. FPS: {:.2f}".format(fps.fps()))
if not args.get("video", False):
vs.stop()
else:
vs.release()
cv2.destroyAllWindows()
4.测试人脸跟踪效果
1. 在代码文件路径下打开终端,执行命令python object_tracker.py --prototxt deploy.prototxt --model res10_300x300_ssd_iter_140000.caffemodel --video video/test.mp4
注:test.mp4为你自己录的视频,再者注意各个文件路径。
2. 直接在object_tracker.py加载路径运行(直接调用摄像头参数测试)
ap = argparse.ArgumentParser()
ap.add_argument("-p", "--prototxt", default="deploy.prototxt",help="path to Caffe 'deploy' prototxt file")
ap.add_argument("-m", "--model", default="res10_300x300_ssd_iter_140000.caffemodel",help="path to Caffe pre-trained model")
ap.add_argument("-c", "--confidence", type=float, default=0.5,help="minimum probability to filter weak detections")
ap.add_argument("-v", "--video",type=str,help="path to optinal input video file")
args = vars(ap.parse_args())
注:注意各个文件路径。
三、模型缺陷与不足
对于我们的追踪算法,有以下两点明显的不足之处:- 我们的目标追踪需要对每一帧图像做一次目标识别。对于速度较快的识别器,如基于Haar特征的级联分类器,这并没有什么问题,但是要注意,越快的算法一般来说识别质量越差。对于一些速度较慢的目标识别器,如HOG+Linear SVM或者深度学习的方法,尤其是当你需要在计算资源有限的设备上运行时,跟踪效果会因为识别器的高时间复杂度大打折扣。
- 形心追踪算法是建立在“所有目标在相邻帧的位移相对于各个目标之间的距离来说都是小量”的假设基础上的。我们追踪的目标都是三维世界中的目标,当它们在二维图像中重叠时,就很容易发生目标编号错乱的情况,这是因为我们在计算距离时使用的是欧氏距离而没有引入别的信息。对于一些很先进的算法,目标重叠对于跟踪效果的影响也是难以解决的。
更多推荐



所有评论(0)