图文详解OpenCV中光流以及视频特征点追踪

目录
  • 前言
  • 1. 效果图
  • 2. 原理
    • 2.1 什么是光流?光流追踪的前提、原理
    • 2.2 光流的应用
    • 2.3 光流的2种方法
  • 3. 源码
    • 3.2 稀疏光流追踪
    • 3.2 优化版稀疏光流追踪
    • 3.3 密集光流追踪
  • 总结

前言

这篇博客将介绍光流的概念以及如何使用 Lucas-Kanade 方法估计光流,并演示如何使用 cv2.calcOpticalFlowPyrLK() 来跟踪视频中的特征点。

1. 效果图

光流追踪效果图如下:

它显示了一个球在连续 5 帧中移动。箭头表示其位移矢量。

不是很严谨的——稀疏光流特征点追踪效果图如下:

它追踪了视频中多个车的主驾驶、副驾驶,以及行人的边缘角点的轨迹:

此代码不检查下一个关键点的正确程度。因此即使图像中的任何特征点消失,光流也有可能找到下一个看起来可能靠近它的点。对于稳健的跟踪,角点应该在特定的时间间隔内检测点。

过程图其一如下:

优化版的——稀疏光流特征点追踪效果如下:

找到特征点,每 30 帧对光流点向后检查,只保留还存在于屏幕中的特征点。不会存在如上图车已经过去了,还留存有长长的不正确的轨迹追踪线。

过程图其一如下:

原图 VS 密集光流追踪 gif 效果图如下:

原图 VS 密集光流Hsv效果图其一如下:

2. 原理

2.1 什么是光流?光流追踪的前提、原理

光流是由物体或相机的运动引起的图像物体在连续两帧之间的明显运动的模式。它是 2D 矢量场,其中每个矢量是一个位移矢量,显示点从第一帧到第二帧的移动。

光流追踪的前提是:1. 对象的像素强度在连续帧之间不会改变;2. 相邻像素具有相似的运动。

光流追踪的原理:

cv2.goodFeaturesToTrack() :Shi-Tomasi 角点检测器确定要追踪的特征点

cv2.calcOpticalFlowPyrLK(): 追踪视频中的稀疏特征点

cv2.calcOpticalFlowFarneback(): 追踪视频中的密集特征点

取第一帧,检测其中的一些 Shi-Tomasi 角点,使用 Lucas-Kanade 光流迭代跟踪这些点。对于函数 cv2.calcOpticalFlowPyrLK() 传递前一帧、前一个点和下一帧。它返回下一个点以及一些状态编号,如果找到下一个点,则值为 1,否则为零。然后在下一步中迭代地将这些下一个点作为前一个点传递。

使用 Harris 角点检测器 检查逆矩阵的相似性。它表示角点是更好的跟踪点。

Shi-Tomasi 角点检测器 比 Harris 角点检测器效果更好一些;

2.2 光流的应用

光流在以下领域有许多应用:

  • 运动的结构
  • 视频压缩
  • 视频稳定

2.3 光流的2种方法

OpenCV提供了俩种算法计算光流,分别通过:cv2.calcOpticalFlowPyrLK()、cv2.calcOpticalFlowFarneback实现;

  • 稀疏光流: 通过 Lucas-Kanade 方法计算稀疏特征集的光流(使用 Shi-Tomasi 算法检测到的角点)。
  • 密集光流: 通过 Gunner Farneback 来寻找密集光流。它计算帧中所有点的光流。

稀疏光流计算:

该方法传递前一帧、前一个点和下一帧;
它返回下一个点以及一些状态编号,如果找到下一个点,则值为 1,否则为零。

p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, winSize=(15, 15), maxLevel=2, criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))

- old_gray: 上一帧单通道灰度图
- frame_gray: 下一帧单通道灰度图
- prePts:p0上一帧坐标pts
- nextPts: None
- winSize: 每个金字塔级别上搜索窗口的大小
- maxLevel: 最大金字塔层数
- criteria:指定迭代搜索算法的终止条件,在指定的最大迭代次数 10 之后或搜索窗口移动小于 0.03

密集光流计算:

该方法将得到一个带有光流向量 (u,v) 的 2 通道阵列。可以找到它们的大小和方向,然后对结果进行颜色编码以实现更好的可视化。
在HSV图像中,方向对应于图像的色调,幅度对应于价值平面。

flow = cv2.calcOpticalFlowFarneback(prvs, next, None, 0.5, 3, 15, 3, 5, 1.2, 0)

- prvs: 上一帧单通道灰度图
- next: 下一帧单通道灰度图
- flow: 流 None
- pyr_scale: 0.5经典金字塔,构建金字塔缩放scale
- level:3 初始图像的金字塔层数
- winsize:3 平均窗口大小,数值越大,算法对图像的鲁棒性越强
- iterations:15 迭代次数
- poly_n:5 像素邻域的参数多边形大小,用于在每个像素中找到多项式展开式;较大的值意味着图像将使用更平滑的曲面进行近似,从而产生更高的分辨率、鲁棒算法和更模糊的运动场;通常多边形n=5或7。
- poly_sigma:1.2 高斯标准差,用于平滑导数
- flags: 可以是以下操作标志的组合:OPTFLOW_USE_INITIAL_FLOW:使用输入流作为初始流近似值。OPTFLOW_FARNEBACK_GAUSSIAN: 使用GAUSSIAN过滤器而不是相同尺寸的盒过滤器;

3. 源码

3.2 稀疏光流追踪

# 光流追踪
# 光流追踪的前提是:1. 对象的像素强度在连续帧之间不会改变;2. 相邻像素具有相似的运动。
# - cv2.goodFeaturesToTrack() 确定要追踪的特征点
# - cv2.calcOpticalFlowPyrLK() 追踪视频中的特征点

# 取第一帧,检测其中的一些 Shi-Tomasi 角点,使用 Lucas-Kanade 光流迭代跟踪这些点。
# 对于函数 cv2.calcOpticalFlowPyrLK() 传递前一帧、前一个点和下一帧。它返回下一个点以及一些状态编号,如果找到下一个点,则值为 1,否则为零。
# 然后在下一步中迭代地将这些下一个点作为前一个点传递。

# USAGE
# python video_optical_flow.py

import imutils
import numpy as np
import cv2

cap = cv2.VideoCapture('images/slow_traffic_small.mp4')

# ShiTomasi角点检测的参数
feature_params = dict(maxCorners=100,
                      qualityLevel=0.3,
                      minDistance=7,
                      blockSize=7)

# Lucas Kanada光流检测的参数
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
mask = np.zeros_like(old_frame)

num = 0
while (1):
    ret, frame = cap.read()

    if not ret:
        break

    frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 使用迭代Lucas Kanade方法计算稀疏特征集的光流
    # - old_gray: 上一帧单通道灰度图
    # - frame_gray: 下一帧单通道灰度图
    # - prePts:p0上一帧坐标pts
    # - nextPts: None
    # - winSize: 每个金字塔级别上搜索窗口的大小
    # - maxLevel: 最大金字塔层数
    # - criteria:指定迭代搜索算法的终止条件,在指定的最大迭代次数criteria.maxCount之后或搜索窗口移动小于criteria.epsilon
    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)
    cv2.imwrite('videoof-imgs/' + str(num) + '.jpg', imutils.resize(img, 500))
    print(str(num))
    num = num + 1
    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()

3.2 优化版稀疏光流追踪

# 优化后的光流追踪—Lucas-Kanade tracker
# (当不见检查下一个关键点的正确程度时,即使图像中的任何特征点消失,光流也有可能找到下一个看起来可能靠近它的点。实际上对于稳健的跟踪,角点应该在特定的时间间隔内检测点。
# 找到特征点后,每 30 帧对光流点的向后检查,只选择好的。)
# Lucas Kanade稀疏光流演示。使用GoodFeatures跟踪用于跟踪初始化和匹配验证的回溯帧之间。
# Lucas-Kanade sparse optical flow demo. Uses goodFeaturesToTrack for track initialization and back-tracking for match verification between frames.

# Usage
# pyhton lk_track.py images/slow_traffic_small.mp4
# 按 ESC键退出

from __future__ import print_function

import imutils
import numpy as np
import cv2

def draw_str(dst, target, s):
    x, y = target
    cv2.putText(dst, s, (x + 1, y + 1), cv2.FONT_HERSHEY_PLAIN, 1.0, (0, 0, 0), thickness=2, lineType=cv2.LINE_AA)
    cv2.putText(dst, s, (x, y), cv2.FONT_HERSHEY_PLAIN, 1.0, (255, 255, 255), lineType=cv2.LINE_AA)

lk_params = dict(winSize=(15, 15),
                 maxLevel=2,
                 criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))

feature_params = dict(maxCorners=500,
                      qualityLevel=0.3,
                      minDistance=7,
                      blockSize=7)

class App:
    def __init__(self, video_src):
        self.track_len = 10
        self.detect_interval = 30
        self.tracks = []
        self.cam = cv2.VideoCapture(video_src)
        self.frame_idx = 0

    def run(self):
        while True:
            _ret, frame = self.cam.read()
            if not _ret:
                break

            frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            vis = frame.copy()

            if len(self.tracks) > 0:
                img0, img1 = self.prev_gray, frame_gray
                p0 = np.float32([tr[-1] for tr in self.tracks]).reshape(-1, 1, 2)
                p1, _st, _err = cv2.calcOpticalFlowPyrLK(img0, img1, p0, None, **lk_params)
                p0r, _st, _err = cv2.calcOpticalFlowPyrLK(img1, img0, p1, None, **lk_params)
                d = abs(p0 - p0r).reshape(-1, 2).max(-1)
                good = d < 1
                new_tracks = []
                for tr, (x, y), good_flag in zip(self.tracks, p1.reshape(-1, 2), good):
                    if not good_flag:
                        continue
                    tr.append((x, y))
                    if len(tr) > self.track_len:
                        del tr[0]
                    new_tracks.append(tr)
                    cv2.circle(vis, (x, y), 2, (0, 255, 0), -1)
                self.tracks = new_tracks
                cv2.polylines(vis, [np.int32(tr) for tr in self.tracks], False, (0, 255, 0))
                draw_str(vis, (20, 20), 'track count: %d' % len(self.tracks))

            if self.frame_idx % self.detect_interval == 0:
                mask = np.zeros_like(frame_gray)
                mask[:] = 255
                for x, y in [np.int32(tr[-1]) for tr in self.tracks]:
                    cv2.circle(mask, (x, y), 5, 0, -1)
                p = cv2.goodFeaturesToTrack(frame_gray, mask=mask, **feature_params)
                if p is not None:
                    for x, y in np.float32(p).reshape(-1, 2):
                        self.tracks.append([(x, y)])

            self.prev_gray = frame_gray
            cv2.imshow('lk_track', vis)
            print(self.frame_idx)
            cv2.imwrite('videoOof-imgs/' + str(self.frame_idx) + '.jpg', imutils.resize(vis, 500))
            self.frame_idx += 1

            ch = cv2.waitKey(1)
            if ch == 27:
                break

def main():
    import sys
    try:
        video_src = sys.argv[1]
    except:
        video_src = 0

    App(video_src).run()
    print('Done')

if __name__ == '__main__':
    print(__doc__)
    main()
    cv2.destroyAllWindows()

3.3 密集光流追踪

# OpenCV中的密集光流
# Lucas-Kanade 方法计算稀疏特征集的光流(使用 Shi-Tomasi 算法检测到的角点)。
# OpenCV 提供了另一种算法: Gunner Farneback 来寻找密集光流。它计算帧中所有点的光流。
# 通过cv2.calcOpticalFlowFarneback() 将得到一个带有光流向量 (u,v) 的 2 通道阵列。可以找到它们的大小和方向,然后对结果进行颜色编码以实现更好的可视化。
# 在HSV图像中,方向对应于图像的色调,幅度对应于价值平面。

import cv2
import imutils
import numpy as np

cap = cv2.VideoCapture('images/slow_traffic_small.mp4')

ret, frame1 = cap.read()
prvs = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)
hsv = np.zeros_like(frame1)
hsv[..., 1] = 255

num = 0
while (1):
    ret, frame2 = cap.read()

    if not ret:
        break
    next = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)

    # 使用迭代Gunner Farneback 方法计算密集特征的光流
    # - prvs: 上一帧单通道灰度图
    # - next: 下一帧单通道灰度图
    # - flow: 流 None
    # - pyr_scale: 0.5经典金字塔,构建金字塔缩放scale
    # - level:3 初始图像的金字塔层数
    # - winsize:3 平均窗口大小,数值越大,算法对图像的鲁棒性越强
    # - iterations:15 迭代次数
    # - poly_n:5 像素邻域的参数多边形大小,用于在每个像素中找到多项式展开式;较大的值意味着图像将使用更平滑的曲面进行近似,从而产生更高的分辨率、鲁棒算法和更模糊的运动场;通常多边形n=5或7。
    # - poly_sigma:1.2 高斯标准差,用于平滑导数
    # - flags: 可以是以下操作标志的组合:OPTFLOW_USE_INITIAL_FLOW:使用输入流作为初始流近似值。OPTFLOW_FARNEBACK_GAUSSIAN: 使用GAUSSIAN过滤器而不是相同尺寸的盒过滤器;
    flow = cv2.calcOpticalFlowFarneback(prvs, next, None, 0.5, 3, 15, 3, 5, 1.2, 0)

    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('Origin VS frame2', np.hstack([frame2, rgb]))
    cv2.imwrite('dof-imgs/' + str(num) + '.jpg', imutils.resize(np.hstack([frame2, rgb]), 600))
    k = cv2.waitKey(30) & 0xff
    num = num + 1
    if k == 27:
        break
    elif k == ord('s'):
        cv2.imwrite('dof-imgs/origin VS dense optical flow HSVres' + str(num) + ".jpg",
                    imutils.resize(np.hstack([frame2, rgb]), width=800))
    prvs = next

cap.release()
cv2.destroyAllWindows()

参考 https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_video/py_lucas_kanade/py_lucas_kanade.html#lucas-kanade

总结

到此这篇关于OpenCV中光流以及视频特征点追踪的文章就介绍到这了,更多相关OpenCV光流及视频特征点追踪内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 使用OpenCV实现检测和追踪车辆

    本文实例为大家分享了OpenCV实现检测和追踪车辆的具体代码,供大家参考,具体内容如下 完整源码GitHub 使用高斯混合模型(BackgroundSubtractorMOG2)对背景建模,提取出前景 使用中值滤波去掉椒盐噪声,再闭运算和开运算填充空洞 使用cvBlob库追踪车辆,我稍微修改了cvBlob源码来通过编译 由于要对背景建模,这个方法要求背景是静止的 另外不同车辆白色区域不能连通,否则会认为是同一物体 void processVideo(char* videoFilename) {

  • python+opencv实现动态物体追踪

    简单几行就可以实现对动态物体的追踪,足见opencv在图像处理上的强大. python代码: import cv2 import numpy as np camera=cv2.VideoCapture(0) firstframe=None while True: ret,frame = camera.read() if not ret: break gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) gray=cv2.GaussianBlur(gray,(21

  • Opencv光流运动物体追踪详解

    光流的概念是由一个叫Gibson的哥们在1950年提出来的.它描述是空间运动物体在观察成像平面上的像素运动的瞬时速度,利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的对应关系,从而计算出相邻帧之间物体的运动信息的一种方法.那么所说的光流到底是什么? 简单来说,上图表现的就是光流,光流描述的是图像上每个像素点的灰度的位置(速度)变化情况,光流的研究是利用图像序列中的像素强度数据的时域变化和相关性来确定各自像素位置的"运动".研究光流场的目的就是为了从

  • opencv+arduino实现物体点追踪效果

    本文所要实现的结果是:通过在摄像头中选择一个追踪点,通过pc控制摄像头的舵机,使这一点始终在图像的中心. 要点:使用光流法在舵机旋转的同时进行追踪,若该点运动,则摄像头跟踪联动. #include<opencv2\opencv.hpp> #include<opencv\cv.h> #include<opencv\highgui.h> #include<math.h> #include<Windows.h> #include<string.h

  • opencv3/C++实现光流点追踪

    光流金字塔 calcOpticalFlowPyrLK()函数参数说明: void calcOpticalFlowPyrLK( InputArray prevImg, //第一个8位输入图像或者通过 buildOpticalFlowPyramid()建立的金字塔 InputArray nextImg,//第二个输入图像或者和prevImg相同尺寸和类型的金字塔 InputArray prevPts, //二维点向量存储找到的光流:点坐标必须是单精度浮点数 InputOutputArray next

  • OpenCV3.0+Python3.6实现特定颜色的物体追踪

    一.环境 win10.Python3.6.OpenCV3.x:编译器:pycharm5.0.3 二.实现目标 根据需要追踪的物体颜色,设定阈值,在视频中框选出需要追踪的物体. 三.实现步骤 1)根据需要追踪的物体颜色,设定颜色阈值,获取追踪物体的掩膜 代码:generate_threshold.py # -*- coding : utf-8 -*- # Author: Tom Yu import cv2 import numpy as np cap = cv2.VideoCapture(0)#获

  • OpenCV 颜色追踪的示例代码

    FPS 每秒帧数 背景消除建模 BSM Background SUbtraction BS算法 图像分割(GMM-高斯混合模型) 机器学习(KNN-K临近) #include <opencv2/core/utility.hpp> #include <opencv2/tracking.hpp> #include <opencv2/videoio.hpp> #include <opencv2/highgui.hpp> #include<opencv2/op

  • 浅析Python+OpenCV使用摄像头追踪人脸面部血液变化实现脉搏评估

    使用摄像头追踪人脸由于血液流动引起的面部色素的微小变化实现实时脉搏评估. 效果如下(演示视频): 由于这是通过比较面部色素的变化评估脉搏所以光线.人体移动.不同角度.不同电脑摄像头等因素均会影响评估效果,实验原理是面部色素对比,识别效果存在一定误差,各位小伙伴且当娱乐,代码如下: import cv2 import numpy as np import dlib import time from scipy import signal # Constants WINDOW_TITLE = 'Pu

  • Python+OpenCV实现实时眼动追踪的示例代码

    使用Python+OpenCV实现实时眼动追踪,不需要高端硬件简单摄像头即可实现,效果图如下所示. 项目演示参见:https://www.bilibili.com/video/av75181965/ 项目主程序如下: import sys import cv2 import numpy as np import process from PyQt5.QtCore import QTimer from PyQt5.QtWidgets import QApplication, QMainWindow

  • 如何用OpenCV -python3实现视频物体追踪

    opencv OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux.Windows.Android和Mac OS操作系统上.它轻量级而且高效--由一系列 C 函数和少量 C++ 类构成,同时提供了Python.Ruby.MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法. OpenCV用C++语言编写,它的主要接口也是C++语言,但是依然保留了大量的C语言接口.该库也有大量的Python.Java and MATLAB/OCTAVE(版本2.

随机推荐