知识要点
1. OpenCV目标跟踪算法的使用大概可以分为以下几个步骤:
-
创建MultiTracker对象: trackers = cv2.legacy.MultiTracker_create()
-
读取视频或摄像头数据: cap = cv2.VideoCapture(‘./videos/soccer_02.mp4’)
-
框选ROI区域: roi = cv2.selectROI(‘frame’, frame, showCrosshair = True)
-
添加实际的追踪算法. tracker = OPENCV_OBJECT_TRACKERS[‘boosting’](), trackers.add(tracker, frame, roi)
-
对每一帧进行进行目标追踪: success, boxes = trackers.update(frame)
2. 光流估计:(稀疏光流估计算法为Lucas-Kanade算法, 比较经典)
- p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, gray, p0, None,winSize=(15,15),maxLevel=2) # p1为更新的点, st是状态
- cv2.calcOpticalFlowPyrLK() # 稀疏光流
- cv2.calcOpticalFlowFarneback() # 稠密光流
随机颜色 数组生成:
- color = np.random.randint(0, 255, (100, 3))
参考资料: 目标追踪综述 – 知乎
12 目标追踪
12.1 OpenCV目标追踪算法介绍
OpenCV上有八种不同的目标追踪算法:
- BOOSTING Tracker:和Haar cascades(AdaBoost)背后所用的机器学习算法相同,但是距其诞生已有十多年了。这一追踪器速度较慢,并且表现不好。(最低支持OpenCV 3.0.0)
- MIL Tracker:比上一个追踪器更精确,但是失败率比较高。(最低支持OpenCV 3.0.0)
- KCF Tracker:比BOOSTING和MIL都快,但是在有遮挡的情况下表现不佳。(最低支持OpenCV 3.1.0)
- CSRT Tracker:比KCF稍精确,但速度不如后者。(最低支持OpenCV 3.4.2)
- MedianFlow Tracker:出色的跟踪故障报告。当运动是可预测的并且没有遮挡时,效果非常好,但是对于快速跳动或快速移动的物体,模型会失效。(最低支持OpenCV 3.0.0)
- TLD Tracker:在多帧遮挡下效果最好。但是TLD的误报非常多,所以不推荐。(最低支持OpenCV 3.0.0)
- MOSSE Tracker:速度真心快,但是不如CSRT和KCF的准确率那么高,如果追求速度选它准没错。(最低支持OpenCV 3.4.1)
- GOTURN Tracker:这是OpenCV中唯一一深度学习为基础的目标检测器。它需要额外的模型才能运行。(最低支持OpenCV 3.2.0)
12.2 目标跟踪算法的使用
OpenCV目标跟踪算法的使用大概可以分为以下几个步骤:
-
创建MultiTracker对象.
-
读取视频或摄像头数据.
-
框选ROI区域
-
给MultiTracker对象添加实际的追踪算法.
-
对每一帧进行进行目标追踪.
下面是一个使用例子:
import cv2
import numpy as np
cap = cv2.VideoCapture('./videos/los_angeles.mp4') # 读取视频
# 定义OpenCV的七种追踪算法
OPENCV_OBJECT_TRACKERS = {
'boosting' : cv2.legacy.TrackerBoosting_create,
'csrt' : cv2.legacy.TrackerCSRT_create,
'kcf' : cv2.legacy.TrackerKCF_create,
'mil' : cv2.legacy.TrackerMIL_create,
'tld' : cv2.legacy.TrackerTLD_create,
'medianflow' : cv2.legacy.TrackerMedianFlow_create,
'mosse' :cv2.legacy.TrackerMOSSE_create}
trackers = cv2.legacy.MultiTracker_create() # 创建追踪器
while True:
flag, frame = cap.read()
if frame is None:
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 变为黑白的
# 目标追踪, # 第一帧追踪时为空,跳过
success, boxes = trackers.update(frame)
# print(boxes) # [[685. 433. 106. 84.]] 矩形位置会有调整
# 绘制追踪到的矩形区域
for box in boxes:
# box是float的数据类型
(x, y, w, h) = [int(v) for v in box]
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)
cv2.imshow('frame', frame)
key = cv2.waitKey(1)
if key == ord('s'):
# 框选ROI区域, 后两个参数的含义,是否包含中间十字和从中心点开始画
roi = cv2.selectROI('frame',frame, showCrosshair = True, fromCenter = False)
print(roi) # (1075, 326, 46, 70)
# 创建实际的追踪器
tracker = OPENCV_OBJECT_TRACKERS['boosting']()
trackers.add(tracker, frame, roi)
elif key == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
12.3 opencv 和深度学习结合 做目标识别
-
目标识别?
-
把图片变成tensor: blob = cv2.dnn.blobFromImage(img)
-
把图片给到网络预测: net.setInput(blob)
-
预测: net.forward()
import cv2
import numpy as np
img = cv2.imread('./smallcat.jpeg') # 读图片
# opencv 和深度学习结合
config = './bvlc_googlenet.prototxt' # 分类文件
model = './bvlc_googlenet.caffemodel' # 模型
net = cv2.dnn.readNetFromCaffe(config, model)
# 图片转化为tensor
blob = cv2.dnn.blobFromImage(img, 1.0, (224, 224), (104, 117, 223))
net.setInput(blob)
r = net.forward()
print(r.shape) # (1, 1000) 1000类
# 读类别 # 看下比较明显的特征
classes = []
with open('./synset_words.txt', 'r') as fp:
# find 函数返回字符串的索引 # strip 去掉结尾换行符号
classes = [x[x.find(' ') + 1:].strip() for x in fp]
# 对得到的结果排序
order = sorted(r[0], reverse = True) # 1000类
z = list(range(3)) # [0, 1, 2] # 只要前三的概率
for i in range(3):
# 返回满足条件的索引
z[i] = np.where(r[0] == order[i])[0][0]
print(f'第{i + 1}项匹配:', classes[z[i]], end = '')
print(' 类所在行:', z[i] + 1, ' ', '可能性:', order[i])
cv2.imshow('img', img)
cv2.waitKey(50000)
cv2.destroyAllWindows()
第1项匹配: tabby, tabby cat 类所在行: 282 可能性: 0.2946576
第2项匹配: Egyptian cat 类所在行: 286 可能性: 0.2306798
第3项匹配: carton 类所在行: 479 可能性: 0.061365135
13 光流估计
光流,顾名思义,光的流动。比如人眼感受到的夜空中划过的流星。在计算机视觉中,定义图像中对象的移动,这个移动可以是相机移动或者物体移动引起的。具体是指,视频图像的一帧中的代表同一对象(物体)像素点移动到下一帧的移动量,使用二维向量表示.
根据是否选取图像稀疏点进行光流估计,可以将光流估计分为稀疏光流和稠密光流.
OpenCV中提供了光流估计的接口,包括 稀疏光流估计算法 cv2.calcOpticalFlowPyrLK(),和 稠密光流估计 cv2.calcOpticalFlowFarneback()。其中稀疏光流估计算法为Lucas-Kanade算法,该算法为1981年由Lucas和Kanade两位科学家提出的,最为经典也较容易理解的算法.
13.1 传统算法 Lucas-Kanade
为了将光流估计进行建模,Lucas-Kanade做了三个重要的假设:
-
亮度恒定:同一点随着时间的变化,其亮度不会发生改变。
-
小运动:随着时间的变化不会引起位置的剧烈变化,只有小运动情况下才能用前后帧之间单位位置变化引起的灰度变化去近似灰度对位置的偏导数。
-
空间一致:一个场景上邻近的点投影到图像上也是邻近点,且邻近点速度一致。因为光流法基本方程约束只有一个,而要求x,y方向的速度,有两个未知变量。所以需要连立n多个方程求解。
cv2.calcOpticalFlowPyrLK(): 参数:
-
prevImage 前一帧图像
-
nextImage 当前帧图像
-
prevPts 待跟踪的特征点向量
-
winSize 搜索窗口的大小
-
maxLevel 最大的金字塔层数
返回值:
-
nextPts 输出跟踪特征点向量
-
status 特征点是否找到,找到的状态为1,未找到的状态为0
import numpy as np
import cv2
cap = cv2.VideoCapture('./test.avi') # 读取视频
ret, old_frame = cap.read() # 获取第一帧的图片
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY) # 变成黑白图
# 检测角点
feature_params = dict(maxCorners = 100,
qualityLevel = 0.3,
minDistance = 7)
p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params)
# 创建mask
mask = np.zeros_like(old_frame)
# 随机颜色
color = np.random.randint(0, 255, (100, 3))
while True:
ret, frame = cap.read()
if frame is None:
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 光流估计, p1为更新的点, st是状态
p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, gray, p0, None,
winSize = (15, 15), maxLevel = 2)
# print(p1.shape, '==', st.shape) # (31, 1, 2) == (31, 1)
# 哪些点找到了哪些没找到
good_new = p1[st == 1]
good_old = p0[st == 1]
# print(good_new.shape, good_old.shape) # (38, 2) (38, 2)
# 绘制轨迹
for i, (new, old) in enumerate(zip(good_new, good_old)):
a, b = new.ravel()
c, d = old.ravel()
mask = cv2.line(mask, (int(a), int(b)), (int(c), int(d)),
color[i].tolist(), 2) # 画线,随机颜色
frame = cv2.circle(frame, (int(a), int(b)), 5, color[i].tolist(), -1)
img = cv2.add(frame, mask)
cv2.imshow('frame', img)
key = cv2.waitKey(10)
if key == ord('q'):
break
# 更新每一帧
old_gray = gray.copy()
p0 = good_new.reshape(-1, 1, 2)
# print('p0de', p0.shape) # p0de (38, 1, 2)
cap.release()
cv2.destroyAllWindows()
文章出处登录后可见!