OpenCV中的稠密光流

OpenCV中的稠密光流:

LK算法计算的是稀疏的特征点光流,如样例当中计算的是使用 Shi-Tomasi算法得到的特征点。opencv当总提供了查找稠密光流的方法。该方法计算一帧图像当中的所有点。该方法是基于Gunner Farneback提出的一篇论文Two-Frame Motion Estimation Based on Polynomial Expansion。


Farneback稠密光流的主要思想是利用多项式对每个像素的邻域信息进行近似表示,例如考虑二次多项式。

f(x) xTAx+bTx+cf(x) xTAx+bTx+c


A是对称矩阵,b是向量,c为标量,~表示像素邻域信息的近似
A是通过像素的邻域信息的最小二乘加权拟合得到的,权重系数与邻域的像素大小和位置有关。
如前一帧图像用f1(x)=xTA1x+bT1x+c1f1(x)=xTA1x+b1Tx+c1表示,两帧图像唯一用d表示
那么f2(x)=f1(x−d)=(x−d)TA1(x−d)+bT1(x−d)+c1f2(x)=f1(x−d)=(x−d)TA1(x−d)+b1T(x−d)+c1
等价于xTA1x+(b1−2A1d)Tx+dTA1d−bT1d+c1=xTA2x+bT2x+c2xTA1x+(b1−2A1d)Tx+dTA1d−b1Td+c1=xTA2x+b2Tx+c2
因为图像场景中像素的外观信息在帧间运动不变,可以得到对应系数相同,如果A1A1非奇异,则

d=−12A−11(b2−b1)d=−12A1−1(b2−b1)


再经过对误差的优化和调整结合图像金字塔对图像中的特征点进行跟踪,稠密光流的大致流程就算完事了。

 


下面样例显示如何找到稠密光流,我们得到的一个两个通道的向量(u,v)。得到的该向量的大小和方向。用不同的颜色编码来使其可视化。
方向与Hue值相关,大小与Value值相关。

使用calcOpticalFlowFarneback函数得到

flow=cv.calcOpticalFlowFarneback(prev, next, flow, pyr_scale, levels, winsize, iterations, poly_n, poly_sigma, flags)

返回值是每个像素点的位移

参数

  • prev 输入8位单通道图片
  • next 下一帧图片,格式与prev相同
  • flow 与返回值相同,得到一个CV_32FC2格式的光流图,与prev大小相同
  • pyr_scale 构建图像金字塔尺度
  • levels 图像金字塔层数
  • winsize 窗口尺寸,值越大探测高速运动的物体越容易,但是越模糊,同时对噪声的容错性越强
  • iterations 对每层金字塔的迭代次数
  • poly_n 每个像素中找到多项式展开的邻域像素的大小。越大越光滑,也越稳定
  • poly_sigma 高斯标准差,用来平滑倒数
  • flags 光流的方式,有OPTFLOW_USE_INITIAL_FLOW 和OPTFLOW_FARNEBACK_GAUSSIAN 两种
import numpy as np
import cv2


cap = cv2.VideoCapture(0)

#获取第一帧
ret, frame1 = cap.read()
prvs = cv2.cvtColor(frame1,cv2.COLOR_BGR2GRAY)
hsv = np.zeros_like(frame1)

#遍历每一行的第1列
hsv[...,1] = 255


while(1):
    ret, frame2 = cap.read()
    next = cv2.cvtColor(frame2,cv2.COLOR_BGR2GRAY)

    #返回一个两通道的光流向量,实际上是每个点的像素位移值
    flow = cv2.calcOpticalFlowFarneback(prvs,next, None, 0.5, 3, 15, 3, 5, 1.2, 0)

    #print(flow.shape)
    print(flow)

    #笛卡尔坐标转换为极坐标,获得极轴和极角
    mag, ang = cv2.cartToPolar(flow[...,0], flow[...,1])
    hsv[...,0] = ang*180/np.pi/2
    hsv[...,2] = cv2.normalize(mag,None,0,255,cv2.NORM_MINMAX)
    rgb = cv2.cvtColor(hsv,cv2.COLOR_HSV2BGR)

    cv2.imshow('frame2',rgb)
    k = cv2.waitKey(30) & 0xff
    if k == 27:
        break
    elif k == ord('s'):
        cv2.imwrite('opticalfb.png',frame2)
        cv2.imwrite('opticalhsv.png',rgb)
    prvs = next

cap.release()
cv2.destroyAllWindows()

 

你可能感兴趣的:(OpenCV杂谈)