1.cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params) 用于获得光流估计所需要的角点
参数说明:old_gray表示输入图片,mask表示掩模,feature_params:maxCorners=100角点的最大个数,qualityLevel=0.3角点品质,minDistance=7即在这个范围内只存在一个品质最好的角点
2. pl, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params) 用于获得光流检测后的角点位置
参数说明:pl表示光流检测后的角点位置,st表示是否是运动的角点,err表示是否出错,old_gray表示输入前一帧图片,frame_gray表示后一帧图片,p0表示需要检测的角点,lk_params:winSize表示选择多少个点进行u和v的求解,maxLevel表示空间金字塔的层数
3. cv2.add(frame, mask) # 将两个图像的像素进行加和操作
参数说明:frame表示输入图片,mask表示掩模
光流估计:通过当前时刻与前一时刻的亮度不变的特性I(x, y, t) = I(x+∆x, y+∆y, t+∆t) 使用lucas-kanade算法进行求解问题, 我们需要求得的是x,y方向的速度
下面是lucas-kanade的推导公式, 即位置发生变动时,其像素点的大小没有发生变化,
I(x, y, t) = I(x+dx, y+dy, t+dt)
= I(x, y, t) + Ixdx + Iydy + Itdt 使用泰勒基数进行展开
对上式进行化解即:
Ixdx + Iydy + Itdt = 0 Ix表示x轴的梯度方向,Iy表示y轴的梯度方向,It表示单位时间上的像素点的变化
如果我们使用前后两帧的变化, 那么dx和dy也就是表示x轴和y轴的速度,返回的结果是dx和dy,即在x轴和y轴方向上移动的步数,我们就可以知道目标的位置了
下面是实际求解u和v的过程,根据上述的空间一致性,我们选择出关键点周围的25个点进行求解,即(5, 5)的方框, 构造Au = b 进行求解,我们可以看出(A^T*A)^-1
但是当前像素点不一定可逆,如果保证可逆呢,即A^T*A的特征值λ1和λ2接近相等且较大,符合条件的就是角点, 因此使用角点去求解u和v
代码:
第一步:使用cv2.capture读入视频
第二步:构造角点检测所需参数, 构造lucas kanade参数
第三步:拿到第一帧图像,并做灰度化, 作为光流检测的前一帧图像
第四步:使用cv2.goodFeaturesToTrack获得光流检测所需要的角点
第五步:构造一个mask用于画直线
第六步:读取一张图片,进行灰度化,作为光流检测的后一帧图像
第七步:使用cv2.caclOpticalFlowPyrLK进行光流检测
第八步:使用st==1获得运动后的角点,原始的角点位置
第九步:循环获得角点的位置,在mask图上画line,在后一帧图像上画角点
第十步:使用cv2.add()将mask和frame的像素点相加并进行展示
第十一步:使用后一帧的图像更新前一帧的图像,同时使用运动的后一帧的角点位置来代替光流检测需要的角点
import numpy as np import cv2 # 第一步:视频的读入 cap = cv2.VideoCapture('test.avi') # 第二步:构建角点检测所需参数 feature_params = dict(maxCorners=100, qualityLevel=0.3, minDistance=7) # lucas kanade参数 lk_params = dict(winSize=(15, 15), maxLevel=2) # 随机颜色条 color = np.random.randint(0, 255, (100, 3)) # 第三步:拿到第一帧图像并灰度化作为前一帧图片 ret, old_frame = cap.read() old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY) # 第四步:返回所有检测特征点,需要输入图片,角点的最大数量,品质因子,minDistance=7如果这个角点里有比这个强的就不要这个弱的 p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params) # 第五步:创建一个mask, 用于进行横线的绘制 mask = np.zeros_like(old_frame) while(True): # 第六步:读取图片灰度化作为后一张图片的输入 ret, frame = cap.read() frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 第七步:进行光流检测需要输入前一帧和当前图像及前一帧检测到的角点 pl, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params) # 第八步:读取运动了的角点st == 1表示检测到的运动物体,即v和u表示为0 good_new = pl[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(150) & 0xff if k == 27: break # 第十一步:更新前一帧图片和角点的位置 old_gray = frame_gray.copy() p0 = good_new.reshape(-1, 1, 2) # p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params) cv2.destroyAllWindows() cap.release()