赞
踩
目标跟踪是对摄像头视频中的移动目标进行定位的过程。实时目标跟踪是许多计算机视觉应用的重要任务,如监控、基于感知的用户界面、增强现实、基于对象的视频压缩以及辅助驾驶等。
好久之前做过一次人脸检测,里面涉及到了目标跟踪。
这次实现一般的运动物体检测,关于实现视频目标跟踪的方法有很多,当跟踪所有移动目标时,帧之间的差异会变的有用;当跟踪视频中移动的手时,基于皮肤颜色的均值漂移方法是最好的解决方案;当知道跟踪对象的一方面时,模板匹配是不错的技术。
常用的目标跟踪方法有:
背景法(本次用的方法)是:将一幅图作为背景,让后和每一帧对比;缺点是一开始存入的背景可能随光照变法而造成错误,但是可以用在光照环境稳定的地方,优点是可以检测之前背景没有的景象;
差帧法是:将前一帧和后一帧进行对比;缺点是无法对运动后突然又静止的景象进行识别,优点是光照不影响;
import cv2 import numpy as np camera = cv2.VideoCapture(0) # 参数0表示第一个摄像头 if (camera.isOpened()): # 判断视频是否打开 print('Open') else: print('摄像头未打开') #测试用,查看视频size size = (int(camera.get(cv2.CAP_PROP_FRAME_WIDTH)), int(camera.get(cv2.CAP_PROP_FRAME_HEIGHT))) print('size:'+repr(size)) # 构建椭圆结果 es = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9, 4)) kernel = np.ones((5, 5), np.uint8) background = None while True: # 读取视频流 grabbed, frame_lwpCV = camera.read() # 对帧进行预处理,>>转灰度图>>高斯滤波(降噪:摄像头震动、光照变化)。 gray_lwpCV = cv2.cvtColor(frame_lwpCV, cv2.COLOR_BGR2GRAY) gray_lwpCV = cv2.GaussianBlur(gray_lwpCV, (21, 21), 0) # 将第一帧设置为整个输入的背景 if background is None: background = gray_lwpCV continue # 对比背景之后的帧与背景之间的差异,并得到一个差分图(different map)。 # 阈值(二值化处理)>>膨胀(dilate)得到图像区域块 diff = cv2.absdiff(background, gray_lwpCV) diff = cv2.threshold(diff, 25, 255, cv2.THRESH_BINARY)[1] diff = cv2.dilate(diff, es, iterations=2) # 显示矩形框:计算一幅图像中目标的轮廓 image, contours, hierarchy = cv2.findContours(diff.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for c in contours: if cv2.contourArea(c) < 1500: # 对于矩形区域,只显示大于给定阈值的轮廓(去除微小的变化等噪点) continue (x, y, w, h) = cv2.boundingRect(c) # 该函数计算矩形的边界框 cv2.rectangle(frame_lwpCV, (x, y), (x+w, y+h), (0, 255, 0), 2) cv2.imshow('contours', frame_lwpCV) cv2.imshow('dis', diff) key = cv2.waitKey(1) & 0xFF if key == ord('q'): # 按'q'健退出循环 break # 释放资源并关闭窗口 camera.release() cv2.destroyAllWindows()
cv2.goodFeaturesToTrack(image, #单通道
maxCorners, #角点数目最大值,若检测的角点超过此值,则只返回前maxCorners个强角点
qualityLevel, #角点的品质因子
minDistance, #如果在其周围minDistance范围内存在其他更强角点,则将此角点删除
corners #存储所有角点
mask, #指定感兴趣区,若无指定,寻找全图
blockSize, #计算协方差矩阵时的窗口大小
useHarrisDetector, #bool 是否使用Harris角点检测,如不指定,则计算shi-tomasi角点
k ) #Harris角点检测需要的k值
光流法的工作原理基于如下假设:
考虑第一帧的像素 I ( x , y , t ) I(x,y,t) I(x,y,t),表示在时间t时像素 I ( x , y ) I(x,y) I(x,y) 的值。在经过时间 d t d_t dt 后,此像素在下一帧移动了 ( d x , d y ) (d_x,d_y) (dx,dy)。因为这些像素是相同的,而且亮度不变,表示成, I ( x , y , t ) = I ( x + d x , y + d y , t + d t ) I(x,y,t)=I(x+d_x,y+d_y,t+d_t) I(x,y,t)=I(x+dx,y+dy,t+dt)。
假设移动很小,使用泰勒公式可以表示成:
I
(
x
+
Δ
x
,
y
+
Δ
y
,
t
+
Δ
t
)
=
I
(
x
,
y
,
t
)
+
∂
I
∂
x
Δ
x
+
∂
I
∂
y
Δ
y
+
∂
I
∂
t
Δ
t
+
H
.
O
.
T
I(x+Δx,y+Δy,t+Δt)=I(x,y,t)+\frac{∂I}{∂x}Δx+\frac{∂I}{∂y}Δy+\frac{∂I}{∂t}Δt+H.O.T
I(x+Δx,y+Δy,t+Δt)=I(x,y,t)+∂x∂IΔx+∂y∂IΔy+∂t∂IΔt+H.O.T
H.O.T是高阶无穷小。由第一个假设和使用泰勒公式展开的式子可以得到:
∂ I ∂ x Δ x + ∂ I ∂ y Δ y + ∂ I ∂ t Δ t = 0 改 写 成 : ∂ I ∂ x Δ x Δ t + ∂ I ∂ y Δ y Δ t + ∂ I ∂ t Δ t Δ t = 0 \frac{∂I}{∂x}Δx+\frac{∂I}{∂y}Δy+\frac{∂I}{∂t}Δt=0 ~~改写成:~~ \frac{∂I}{∂x}\frac{Δx}{Δt}+\frac{∂I}{∂y}\frac{Δy}{Δt}+\frac{∂I}{∂t}\frac{Δt}{Δt}=0 ∂x∂IΔx+∂y∂IΔy+∂t∂IΔt=0 改写成: ∂x∂IΔtΔx+∂y∂IΔtΔy+∂t∂IΔtΔt=0
设: ∂ I ∂ x = f x \frac{∂I}{∂x}=f_x ∂x∂I=fx 同理得 ∂ I ∂ y = f y \frac{∂I}{∂y}=f_y ∂y∂I=fy和 ∂ I ∂ t = f t \frac{∂I}{∂t}=f_t ∂t∂I=ft
令 Δ x Δ t = u \frac{Δx}{Δt}=u ΔtΔx=u ; Δ y Δ t = v \frac{Δy}{Δt}=v ΔtΔy=v
光流方程:
f
x
u
+
f
y
v
+
f
t
=
0
f_xu+f_yv+f_t=0
fxu+fyv+ft=0 其中
f
x
f_x
fx 和
f
y
f_y
fy分别是图像的梯度,
f
t
f_t
ft 是是图像沿着时间的梯度。但是
u
u
u 和
v
v
v是未知的,我们没办法用一个方程解两个未知数,那么就有了lucas-kanade这个方法来解决这个问题。
光流是进行视频中运动对象轨迹标记的一种很常用的方法,在OpenCV中实现光流也很容易。
cv2.calcOpticalFlowPyrLK
函数计算一个稀疏特征集的光流,使用金字塔中的迭代 Lucas-Kanade 方法。
nextPts,status,err = cv2.calcOpticalFlowPyrLK(prevImg, #上一帧图片
nextImg, #当前帧图片
prevPts, #上一帧找到的特征点向量
nextPts #与返回值中的nextPtrs相同
[, status[, err[, winSize
[, maxLevel[, criteria
[, flags[, minEigThreshold]]]]]]])
返回值:
其他输入值:
OPTFLOW_USE_INITIAL_FLOW
表示使用估计值作为寻找到的初始光流,OPTFLOW_LK_GET_MIN_EIGENVALS
表示使用最小特征值作为误差测量实现原理:
首先选取第一帧,在第一帧图像中检测Shi-Tomasi角点,
然后使用LK算法来迭代的跟踪这些特征点。迭代的方式就是不断向cv2.calcOpticalFlowPyrLK()中传入上一帧图片的特征点以及当前帧的图片。
函数会返回当前帧的点,这些点带有状态1或者0,如果在当前帧找到了上一帧中的点,那么这个点的状态就是1,否则就是0。
实现流程:
import numpy as np import cv2 cap = cv2.VideoCapture('./IMG_1521.mp4') # ShiTomasi corner detection的参数 feature_params = dict(maxCorners=100, qualityLevel=0.3, minDistance=7, blockSize=7) # 光流法参数 # maxLevel 未使用的图像金字塔层数 lk_params = dict(winSize=(15, 15), maxLevel=2, criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03)) # 创建随机生成的颜色 color = np.random.randint(0, 255, (100, 3)) ret, old_frame = cap.read() # 取出视频的第一帧 old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY) # 灰度化 p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params) mask = np.zeros_like(old_frame) # 为绘制创建掩码图片 while True: _, frame = cap.read() frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 计算光流以获取点的新位置 p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params) # 选择good points good_new = p1[st == 1] good_old = p0[st == 1] # 绘制跟踪框 for i, (new, old) in enumerate(zip(good_new, good_old)): a, b = new.ravel() c, d = old.ravel() mask = cv2.line(mask, (a, b), (c, d), color[i].tolist(), 2) frame = cv2.circle(frame, (a, b), 5, color[i].tolist(), -1) img = cv2.add(frame, mask) cv2.imshow('frame', img) k = cv2.waitKey(30) # & 0xff if k == 27: break old_gray = frame_gray.copy() p0 = good_new.reshape(-1, 1, 2) cv2.destroyAllWindows() cap.release()
这是一个裸眼3D视频动画
鸣谢
https://blog.csdn.net/lwplwf/article/details/73526831
https://blog.csdn.net/tengfei461807914/article/details/80978947
https://blog.csdn.net/github_39611196/article/details/81166057
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。