详解OpenCV中的Lucas Kanade稀疏光流单应追踪器

Posted 程序媛一枚~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了详解OpenCV中的Lucas Kanade稀疏光流单应追踪器相关的知识,希望对你有一定的参考价值。

详解OpenCV中的Lucas Kanade稀疏光流单应追踪器

这篇博客将详细介绍OpenCV中的Lucas Kanade稀疏光流单应追踪器。

  • 光流是由物体或相机的运动引起的图像物体在连续两帧之间的明显运动的模式。
  • Lucas Kanade是一个算法,用于稀疏光流的追踪;

1. 效果图

单应追踪器效果图如下:

选中随机特征点为红色,判断帧之间是否存在关联关系,存在则绘制轨迹线。每帧均可选择新的随机点作为特征点;

2. 源码

# Lucas Kanade稀疏光流单应追踪器demo
# 使用GoodFeatures用于跟踪初始化特征点和回溯帧之间的匹配验证。
# 查找引用视图和当前视图之间的单应性。
# Lucas-Kanade sparse optical flow demo. Uses goodFeaturesToTrack
# for track initialization and back-tracking for match verification
# between frames. Finds homography between reference and current views.
# 
# Usage
# python lk_homography.py images/slow_traffic_small.mp4
# 按 ESC 退出
# 空格开始追踪
# 按 r 随机切换

from __future__ import print_function

import cv2

# 在目标图像dst的target位置开始绘制文本s
import imutils


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=(19, 19),
                 maxLevel=2,
                 criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))

feature_params = dict(maxCorners=1000,
                      qualityLevel=0.01,
                      minDistance=8,
                      blockSize=19)


def checkedTrace(img0, img1, p0, back_threshold=1.0):
    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)
    status = d < back_threshold
    return p1, status


green = (0, 255, 0)
red = (0, 0, 255)


class App:
    def __init__(self, video_src):
        self.cam = self.cam = cv2.VideoCapture(video_src)
        self.p0 = None
        self.use_ransac = True

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

            # 转换灰度图
            frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            # 复制帧
            vis = frame.copy()
            if self.p0 is not None:
                p2, trace_status = checkedTrace(self.gray1, frame_gray, self.p1)

                self.p1 = p2[trace_status].copy()
                self.p0 = self.p0[trace_status].copy()
                self.gray1 = frame_gray

                if len(self.p0) < 4:
                    self.p0 = None
                    continue

                # findHomography(srcPoints, dstPoints[, method[, ransacReprojThreshold[, mask[, maxIters[, confidence]]]]]) -> retval, mask
                # 查找两个平面之间的透视变换
                # - p0: 原始平面中点的坐标,CV_32FC2或点向量
                # - p1: 目标平面中点的坐标,CV_32FC2或点向量
                # - method: 用于计算单应矩阵的方法。可以采用以下方法:
                    # 0: 使用所有点的常规方法,即最小二乘法
                    # RANSAC: 基于RANSAC的鲁棒方法
                    # LMEDS: 最小中值稳健法
                    # 基于RHO**-PROSAC的鲁棒方法
                # - ransAcreProjectThreshold: 将点对视为内部对象时允许的最大重投影错误
                H, status = cv2.findHomography(self.p0, self.p1, (0, cv2.RANSAC)[self.use_ransac], 10.0)
                h, w = frame.shape[:2]
                overlay = cv2.warpPerspective(self.frame0, H, (w, h))
                vis = cv2.addWeighted(vis, 0.5, overlay, 0.5, 0.0)

                for (x0, y0), (x1, y1), good in zip(self.p0[:, 0], self.p1[:, 0], status[:, 0]):
                    # 如果俩帧中间可回溯,有关联,绘制光流线
                    if good:
                        cv2.line(vis, (x0, y0), (x1, y1), (0, 128, 0))
                    # 可回溯,绘制绿色点,不可回溯绘制随机红色特征点
                    cv2.circle(vis, (x1, y1), 2, (red, green)[good], -1)
                draw_str(vis, (20, 20), 'track count: %d' % len(self.p1))
                if self.use_ransac:
                    draw_str(vis, (20, 40), 'RANSAC')
            else:
                # 初始化特征点(并绘制为绿色的点)
                p = cv2.goodFeaturesToTrack(frame_gray, **feature_params)
                if p is not None:
                    for x, y in p[:, 0]:
                        cv2.circle(vis, (x, y), 2, green, -1)
                    draw_str(vis, (20, 20), 'feature count: %d' % len(p))

            cv2.imshow('lk_homography', vis)
            if (num == 0):
                cv2.waitKey(0)
            if (num % 5 == 0):
                cv2.imwrite('lk_himgs/' + str(num) + ".jpg", imutils.resize(vis, width=500))
            num = num + 1

            ch = cv2.waitKey(1)
            if ch == 27:
                break
            if ch == ord(' '):
                self.frame0 = frame.copy()
                # 初始化特征点
                self.p0 = cv2.goodFeaturesToTrack(frame_gray, **feature_params)
                if self.p0 is not None:
                    self.p1 = self.p0
                    self.gray0 = frame_gray
                    self.gray1 = frame_gray
            if ch == ord('r'):
                self.use_ransac = not self.use_ransac


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()

参考

以上是关于详解OpenCV中的Lucas Kanade稀疏光流单应追踪器的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV 的 Lucas-Kanade 光流代码中的“good_new = p1[st==1]”是啥意思

OpenCV中的光流及视频特征点追踪

Lucas Kanade 密集光流

加权 Lucas Kanade - 高斯函数 MATLAB

Lucas-Kanade 方法的光流?

opencv光流法