LK光流法的详细解析

发布时间:2023-05-18

一、LK光流法三个假设

LK (Lucas-Kanade) 光流法是一种经典的光流估计方法,它的基本思想是假设当前帧的邻域基本保持不变,那么在相邻两帧之间发生的小运动可以用邻域内每个像素的位移来描述。LK光流法主要基于以下三个假设:

1. 亮度恒定假设

它假设在时间和空间上相邻的像素点之间的亮度是恒定的,即对于同一物体表面上的点,在单位时间内,它们在图像上呈现的亮度不变,即 I(x, y, t) = I(x+dx, y+dy, t+dt),其中 (x,y) 是该像素的坐标,t 是时间,I(x, y, t) 是在该坐标和时间下的灰度值。

2. 运动场稠密性假设

它假设观测场景的所有像素都有运动,即在图像中的所有像素都有相应的运动向量。

3. 空间邻域一致性假设

它假设位于空间邻域内的像素点具有相似的运动向量。

二、KLT光流法

KLT (Kanade-Lucas-Tomasi) 光流法是在LK光流法的基础上发展而来的一种光流算法,主要用于检测角点并跟踪它们在不同帧之间的运动。 具体而言,KLT光流法通过对图像中的关键点进行跟踪,来实现处于场景中的关键物体跟踪。其基本思路是选取具有高变化率的像素点,即角点,作为特征点进行跟踪。 在实际应用中,KLT光流法最常用于运动图像中关键特征的跟踪,如人脸、机器人等。

三、LK光流法估计旋转

LK光流法虽然可以用于多种目标跟踪问题,但是它存在一些问题。例如,当物体在原地旋转时,它的像素值的亮度分布不会改变,这样LK光流法就无法捕捉到这些运动信息。 为了解决这个问题,LK光流法被更新成可以估计旋转的方法,这个方法被称为LK-R (Lucas-Kanade-Rotations) 光流法。 在LK-R光流法中,旋转被认为是惯性运动的一种形式。当物体发生旋转时,像素的亮度不会改变,但是它们会沿着圆周运动。因此,LK-R光流法不仅可以通过估计位移来捕捉水平和垂直运动的信息,还可以通过估计旋转角度来捕捉目标的旋转运动信息。

代码示例

import cv2
cap = cv2.VideoCapture(0)
# previous frame
ret, previous_frame = cap.read()
previous_frame_gray = cv2.cvtColor(previous_frame, cv2.COLOR_BGR2GRAY)
# set first frame
# to get initial keypoints and previous frame
lk_params = dict(winSize=(15, 15),
                 maxLevel=4,
                 criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
previous_keypoints = cv2.goodFeaturesToTrack(previous_frame_gray, 100, 0.3, 7)
previous_keypoints = previous_keypoints.reshape(-1, 1, 2)
# loop over video frames
while cap.isOpened():
    ret, frame = cap.read()
    if ret:
        # convert to grayscale
        current_frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        # calculate optical flow
        current_keypoints, status, errors = cv2.calcOpticalFlowPyrLK(previous_frame_gray, current_frame_gray,
                                                                     previous_keypoints, None, **lk_params)
        # select good keypoints
        good_new = current_keypoints[status == 1]
        good_old = previous_keypoints[status == 1]
        # draw flow
        for i, (new, old) in enumerate(zip(good_new, good_old)):
            pa, pb = new.ravel()
            qa, qb = old.ravel()
            mask = cv2.line(mask, (pa, pb), (qa, qb), (0, 255, 0), 2)
            frame = cv2.circle(frame, (pa, pb), 5, (0, 0, 255), -1)
        # show flow
        img = cv2.add(frame, mask)
        cv2.imshow('flow', img)
        # update the previous frame and keypoints
        previous_frame_gray = current_frame_gray.copy()
        previous_keypoints = good_new.reshape(-1, 1, 2)
        # press "q" to quit
        if cv2.waitKey(100) & 0xFF == ord('q'):
            break
    else:
        break
# release the capture and close the window
cap.release()
cv2.destroyAllWindows()