内容来自OpenCV-Python Tutorials 自己翻译整理

目标:

  • 了解光流的概念,使用lucas-kanade估算方法
  • 使用cv2.calcOpticalFlowPyrLK() 方法来追踪视频中的特征点

光流:
光流的概念是指在连续的两帧图像当中,由于图像中的物体移动或者摄像头的移动而使得图像中的目标的运动叫做光流。(说简单点,考虑摄像头不会动的情况,就是一个视频当中有一个运动目标,那么这个视频中的相邻两帧中运动的目标就是光流)
光流是个向量场,表示了一个点从第一帧运动到第二帧的移动。
如图:

上面的图表示了一个球在连续的5帧图像中的运动。箭头表示了它的位移向量。
光流有很多应用场景如下:

  • 运动恢复结构
  • 视频压缩
  • 视频防抖动

等等

光流法的工作原理基于如下假设:

1.连续的两帧图像之间,目标的像素亮度不改变。

2.相邻的像素之间有相似的运动。

考虑第一帧的像素I(x,y,t)I(x,y,t)I(x,y,t),表示在时间t时像素I(x,y)I(x,y)I(x,y)的值。在经过时间dtdtd_t后,此像素在下一帧移动了(dx,dy)(dx,dy)(d_x,d_y)。

因为这些像素是相同的,而且亮度不变,我们可以表示成,I(x,y,t)=I(x+dx,y+dy,t+dt)I(x,y,t)=I(x+dx,y+dy,t+dt)I(x,y,t)=I(x+d_x,y+d_y,t+d_t)。

假设移动很小,使用泰勒公式可以表示成:

I(x+Δx,y+Δy,t+Δt)=I(x,y,t)+∂I∂xΔx+∂I∂yΔy+∂I∂tΔt+H.O.TI(x+Δx,y+Δy,t+Δt)=I(x,y,t)+∂I∂xΔx+∂I∂yΔy+∂I∂tΔt+H.O.T

I(x+\Delta x,y+\Delta y,t+\Delta t)=I(x,y,t)+\frac{ \partial I}{ \partial x}\Delta x + \frac{ \partial I}{ \partial y}\Delta y + \frac{ \partial I}{ \partial t}\Delta t + H.O.T

H.O.T是高阶无穷小。

由第一个假设和使用泰勒公式展开的式子可以得到:

∂I∂xΔx+∂I∂yΔy+∂I∂tΔt=0∂I∂xΔx+∂I∂yΔy+∂I∂tΔt=0

\frac{ \partial I}{ \partial x}\Delta x + \frac{ \partial I}{ \partial y}\Delta y + \frac{ \partial I}{ \partial t}\Delta t = 0

改写成

∂I∂xΔxΔt+∂I∂yΔyΔt+∂I∂tΔtΔt=0∂I∂xΔxΔt+∂I∂yΔyΔt+∂I∂tΔtΔt=0

\frac{ \partial I}{ \partial x}\frac{\Delta x}{\Delta t} + \frac{ \partial I}{ \partial y}\frac{\Delta y}{\Delta t} + \frac{ \partial I}{ \partial t}\frac{\Delta t}{\Delta t} = 0

这里设:

∂I∂x=fx∂I∂x=fx\frac{ \partial I}{ \partial x} = f_x

同理y和t

ΔxΔt=uΔxΔt=u\frac{\Delta x}{\Delta t} = u

ΔyΔt=vΔyΔt=v\frac{\Delta y}{\Delta t} = v

fxu+fyv+ft=0fxu+fyv+ft=0f_x u + f_y v +f_t = 0

上面公式就叫做光流方程,其中fxfxf_x和fyfyf_y分别是图像的梯度,ftftf_t是是图像沿着时间的梯度。但是u和v是未知的,我们没办法用一个方程解两个未知数,那么就有了lucas-kanade这个方法来解决这个问题。

Lucas-Kanade算法:

使用第二条假设,就是所有的相邻像素都有相同的移动。LK算法使用了一个3×3的窗口大小。所以,在这个窗口当中有9个像素点满足公式
fxu+fyv+ft=0fxu+fyv+ft=0f_x u + f_y v +f_t = 0 。将点代入方程,现在的问题就变成了使用9个点求解两个未知量。

解的个数大于未知数的个数,这是个超定方程,使用最小二乘的方法来求解最优值。如下为计算得到的结果。

(图中的逆矩阵与Harris角点检测很像,说明角点是适合用来做跟踪的)

想法很简单,给出一些点用来追踪,从而获得点的光流向量。但是有另外一个问题需要解决,目前讨论的运动都是小步长的运动,如果有幅度大的运动出现,本算法就会失效。

使用的解决办法是利用图像金字塔。在金字塔顶端的小尺寸图片当中,大幅度的运动就变成了小幅度的运动。所以使用LK算法,可以得到尺度空间上的光流。

OpenCV中的LK光流:

在OpenCV库提供了一个完整的函数,cv2.calcOpticalFlowPyrLK()。

这里,我们可以创建一个简单的应用,用来追踪视频中的一些店。为了探测这些点,我们使用cv2.goodFeaturesToTrack()来实现。

首先选取第一帧,在第一帧图像中检测Shi-Tomasi角点,然后使用LK算法来迭代的跟踪这些特征点。迭代的方式就是不断向cv2.calcOpticalFlowPyrLK()中传入上一帧图片,其中的特征点以及当前帧的图片。函数会返回当前帧的点,这些点带有状态1或者0,如果在当前帧找到了上一帧中的点,那么这个点的状态就是1,否则就是0。

python的OpenCV 光流函数如下

该函数计算基于图像金字塔的稀疏光流

nextPts,status,err = cv.calcOpticalFlowPyrLK(   prevImg, nextImg, prevPts, nextPts[, status[, err[, winSize[, maxLevel[, criteria[, flags[, minEigThreshold]]]]]]])

返回值:

  • nextPtrs 输出一个二维点的向量,这个向量可以是用来作为光流算法的输入特征点,也是光流算法在当前帧找到特征点的新位置(浮点数)
  • status 标志,在当前帧当中发现的特征点标志status==1,否则为0
  • err 向量中的每个特征对应的错误率

输入值:

  • prevImg 上一帧图片
  • nextImg 当前帧图片
  • prevPts 上一帧找到的特征点向量
  • nextPts 与返回值中的nextPtrs相同
  • status 与返回的status相同
  • err 与返回的err相同
  • winSize 在计算局部连续运动的窗口尺寸(在图像金字塔中)
  • maxLevel 图像金字塔层数,0表示不使用金字塔
  • criteria 寻找光流迭代终止的条件
  • flags 有两个宏,表示两种计算方法,分别是OPTFLOW_USE_INITIAL_FLOW表示使用估计值作为寻找到的初始光流,OPTFLOW_LK_GET_MIN_EIGENVALS表示使用最小特征值作为误差测量
  • minEigThreshold 该算法计算光流方程的2×2规范化矩阵的最小特征值,除以窗口中的像素数; 如果此值小于minEigThreshold,则会过滤掉相应的功能并且不会处理该光流,因此它允许删除坏点并获得性能提升。

大致流程就是,首先获取视频或者摄像头的第一帧图像。用goodFeaturesToTrack函数获取初始化的角点,然后开始无限循环获取视频图像帧,将新图像和上一帧图像放入calcOpticalFlowPyrLK函数当中,从而获取新图像的光流。

import numpy as np
import cv2cap = cv2.VideoCapture(0)# ShiTomasi 角点检测参数
feature_params = dict( maxCorners = 100,qualityLevel = 0.3,minDistance = 7,blockSize = 7 )# lucas kanade光流法参数
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中
p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params)# 创建一个蒙版用来画轨迹
mask = np.zeros_like(old_frame)while(1):ret,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_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) & 0xffif k == 27:break# 更新上一帧的图像和追踪点old_gray = frame_gray.copy()p0 = good_new.reshape(-1,1,2)cv2.destroyAllWindows()
cap.release()

源代码是使用一个视频,我这里面改成了摄像头了。

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

f(x)~ x^{T}Ax+b^{T}x+c
A是对称矩阵,b是向量,c为标量,~表示像素邻域信息的近似
A是通过像素的邻域信息的最小二乘加权拟合得到的,权重系数与邻域的像素大小和位置有关。
如前一帧图像用 f1(x)=xTA1x+bT1x+c1f1(x)=xTA1x+b1Tx+c1f_1(x)=x^TA_1x+b_1^{T}x+c_1表示,两帧图像唯一用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)+c1f_2(x)=f_1(x-d)=(x-d)^TA_1(x-d)+b_1^T(x-d)+c_1
等价于 xTA1x+(b1−2A1d)Tx+dTA1d−bT1d+c1=xTA2x+bT2x+c2xTA1x+(b1−2A1d)Tx+dTA1d−b1Td+c1=xTA2x+b2Tx+c2x^TA_1x+(b_1-2A_1d)^Tx+d^TA_1d-b_1^Td+c_1 = x^TA_2x+b_2^Tx+c_2
因为图像场景中像素的外观信息在帧间运动不变,可以得到对应系数相同,如果 A1A1A_1非奇异,则

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

d=-\frac{1}{2}A_1^{-1}(b_2-b_1)
再经过对误差的优化和调整结合图像金字塔对图像中的特征点进行跟踪,稠密光流的大致流程就算完事了。


下面样例显示如何找到稠密光流,我们得到的一个两个通道的向量(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 cv2cap = cv2.VideoCapture(0)#获取第一帧
ret, frame1 = cap.read()
prvs = cv2.cvtColor(frame1,cv2.COLOR_BGR2GRAY)
hsv = np.zeros_like(frame1)#遍历每一行的第1列
hsv[...,1] = 255while(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/2hsv[...,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) & 0xffif k == 27:breakelif k == ord('s'):cv2.imwrite('opticalfb.png',frame2)cv2.imwrite('opticalhsv.png',rgb)prvs = nextcap.release()
cv2.destroyAllWindows()


python opencv入门 光流法(41)相关推荐

  1. python笛卡尔转换极坐标_[4] opencv: pythonDIS光流法与笛卡尔坐标转为极坐标

    [4] opencv: pythonDIS光流法与笛卡尔坐标转为极坐标 [4] opencv: pythonDIS光流法与笛卡尔坐标转为极坐标 目录1, 笛卡尔转为极坐标 2, DIS光流算法 1, ...

  2. OpenCV 使用光流法检测物体运动

    OpenCV 可以使用光流法检测物体运动,贴上代码以及效果. // opticalflow.cpp : 定义控制台应用程序的入口点. //#include "stdafx.h"// ...

  3. python——opencv入门(一)

    1. OpenCV的结构 和Python一样,当前的OpenCV也有两个大版本,OpenCV2和OpenCV3.相比OpenCV2,OpenCV3提供了更强的功能和更多方便的特性.不过考虑到和深度学习 ...

  4. OpenCV之光流法运动目标跟踪

    [光流Optical Flow]的概念是Gibson在1950年首先提出来的.它是空间运动物体在观察成像平面上的像素运动的瞬时速度,是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一 ...

  5. opencv c++ 光流法、稀疏光流法、稠密光流法、均值迁移追踪(meanshift、camshift)

    1.概念 参考: (70条消息) 什么是光流法_张年糕慢慢走的博客-CSDN博客_光流法 (70条消息) 计算机视觉--光流法(optical flow)简介_T-Jhon的博客-CSDN博客_光流法 ...

  6. python OpenCV 入门教程等你来取

    虽然python 很强大,而且也有自己的图像处理库PIL,但是相对于OpenCV 来讲,OpenCV更加强大, 可以做更多更复杂的应用,比如人脸识别等.跟很多开源软件一样OpenCV 也提供了完善的p ...

  7. python opencv 入门 绘图函数 (3)

    内容来自OpenCV-Python Tutorials 自己翻译整理 目标: 使用opencv画几何图形 学习如下函数 cv2.line(), cv2.circle() , cv2.rectangle ...

  8. python openCv 入门学习-RGB转HSV、掩膜的简单理解(二)

    前言 RGB 颜色空间是大家最熟悉的颜色空间,即三基色空间,任何一种颜色都可以由该三种 颜色 混合而成.然而一般对颜色空间的图像进行有效处理都是在 HSV 空间进行的,HSV(色 调 Hue, 饱和度 ...

  9. python opencv入门 Meanshift 和 Camshift 算法(40)

    内容来自OpenCV-Python Tutorials 自己翻译整理 目标: 在本章,学习Meanshift算法和Camshift算法来寻找和追踪视频中的目标物体. Meanshift算法: mean ...

最新文章

  1. 【SSH项目实战】国税协同平台-26.分页功能编写
  2. `Caused by: java.lang.ClassNotFoundException: javax.xml.bind.DatatypeConverter
  3. 深度学习在CTR预估中的应用 | CTR深度模型大盘点
  4. 通用权限实现的核心设计思想
  5. 数据库 CURD测试题【中等】
  6. MyBatis 配置文件 用户密码加密存储
  7. android wifi热点项目总结,高通Android wifi移植和wifi热点问题总结
  8. 电脑任务栏跑到右边去了_为什么程序员下班后只关显示器从不关电脑?
  9. v-show组件 vue_Vue.js教程--基础(实例 模版语法template computed, watch v-if, v-show v-for, 一个组件的v-for.)...
  10. JAVA总裁--Java数组基础知识
  11. hide your website's wordpress info/path/way
  12. python函数定义及调用-Python函数的基本定义和调用以及内置函数
  13. 详细解说STL string
  14. [活动]问卷调查:打造更好用的Visual Studio 2008
  15. java restsharp_RestSharp 一个.NET(C#)的HTTP辅助类组件
  16. html 下拉框设置提示语,为 Html 的 Select 加一个提示语和输入方法
  17. 组装计算机什么配置比较好,组装电脑什么配置好 组装电脑配置推荐
  18. 机械臂抓取学习笔记二
  19. winForm c#导出Excel
  20. 使用 Go 构建 Kubernetes 应用

热门文章

  1. 上篇: php 微信公众号 基于Thinkphp3.2框架开发
  2. 2020年河南省计算机对口升学分数线,2020年河南省单招分数线出来了吗?
  3. 平面设计常见的配色方案及色标
  4. 23-1-18 PDManer 工具
  5. 守望先锋:射线命中位置,相距距离,根据射线命中位置和相距距离找到对应实体在数组中的值
  6. OneNote 深度评测:使用资源、插件、模版
  7. python urllib之parse中urlencode、quote、unquote使用方法
  8. Android BLE蓝牙踩坑总结
  9. 微星服务器主板s0591显示35,微星MS-S0131服务器主板性能升华
  10. 链表(线性表的一种存储结构)