一、实践目标
本项目在行空板上外接USB摄像头,通过摄像头来检测目标,并进行跟踪。
二、知识目标
学习使用opencv的Lucas-Kanade tracker方法来检测目标并进行跟踪的方法。
三、实践准备
硬件清单:
软件使用:
Mind+编程软件x1
四、实践过程
1、硬件搭建
1、将摄像头接入行空板的USB接口。
2、通过USB连接线将行空板连接到计算机。
2、软件编写
第一步:打开Mind+,远程连接行空板
第二步:在“行空板的文件”中新建一个名为AI的文件夹,在其中再新建一个名为“基于行空板的opencv lk_track目标追踪”的文件夹,导入本节课的依赖文件。
第三步:编写程序
在上述文件的同级目录下新建一个项目文件,并命名为“main.py”。
示例程序:
#!/usr/bin/env python
'''
Lucas-Kanade tracker
====================
Lucas-Kanade sparse optical flow demo. Uses goodFeaturesToTrack
for track initialization and back-tracking for match verification
between frames.
Usage
-----
lk_track.py [<video_source>]
Keys
----
ESC - exit
'''
# Python 2/3 compatibility
from __future__ import print_function
# 导入所需库
import numpy as np
import cv2 as cv
# 导入其他模块
import video
from common import anorm2, draw_str
# 设置Lucas-Kanade光流跟踪参数
lk_params = dict( winSize = (15, 15),
maxLevel = 2,
criteria = (cv.TERM_CRITERIA_EPS | cv.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 = 5
self.tracks = []
# 创建视频捕获对象
self.cam = video.create_capture(video_src)
self.frame_idx = 0
# 创建全屏窗口
cv.namedWindow('lk_track',cv.WND_PROP_FULLSCREEN)
cv.setWindowProperty('lk_track', cv.WND_PROP_FULLSCREEN, cv.WINDOW_FULLSCREEN)
def run(self):
while True:
# 读取一帧图像
_ret, frame = self.cam.read()
# 转为灰度图像
frame_gray = cv.cvtColor(frame, cv.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 = cv.calcOpticalFlowPyrLK(img0, img1, p0, None, **lk_params)
# 反向计算光流
p0r, _st, _err = cv.calcOpticalFlowPyrLK(img1, img0, p1, None, **lk_params)
# 计算反向光流误差
d = abs(p0-p0r).reshape(-1, 2).max(-1)
# 筛选出误差小于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)
# 在图像上绘制跟踪点
cv.circle(vis, (int(x), int(y)), 2, (0, 255, 0), -1)
# 更新跟踪路径
self.tracks = new_tracks
# 在图像上绘制跟踪路径
cv.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]:
cv.circle(mask, (x, y), 5, 0, -1)
# 检测新的特征点
p = cv.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.frame_idx += 1
self.prev_gray = frame_gray
# 显示图像
cv.imshow('lk_track', vis)
# 按下ESC键退出
ch = cv.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()
# 关闭所有窗口
cv.destroyAllWindows()
3、运行调试
第一步:运行主程序
运行“main.py”程序,可以看到初始时屏幕上显示着摄像头拍摄到的实时画面,将小车放入画面中,小车随即被检测出并用绿色光点标记,轻轻移动小车,可以看到绿色的光点随着小车而移动,并留下了移动的轨迹,实现了目标追踪的效果。
4、程序解析
在上述的“main.py”文件中,我们主要通过opencv库来调用摄像头,获取实时视频流,在视频的每一帧中检测特征点,然后使用光流法对这些特征点进行跟踪,并将跟踪的结果显示出来。特征点的检测和跟踪是在一个无限循环中进行的,直到用户选择退出程序。整体流程如下,
① 视频源获取和处理:程序首先打开指定的视频源,可以是摄像头或者视频文件。然后以帧为单位读取视频,并对每一帧进行处理。
② 特征点检测和跟踪:对于每一帧,程序首先检查是否需要进行特征点检测。如果需要,就在当前帧中检测新的特征点,并将这些点添加到跟踪点列表中。然后,无论是否检测了新的特征点,程序都会对列表中已有的跟踪点进行跟踪。跟踪的过程是使用Lucas-Kanade光流法计算这些点在当前帧和前一帧之间的运动,得到这些点在当前帧中的位置。
③ 结果验证和显示:跟踪完成后,程序会使用反向光流进行一次验证,确保跟踪的准确性。然后在原视频帧的副本上绘制跟踪点的位置,并将结果显示在全屏窗口中。
④ 用户交互:程序会检查用户是否按下了ESC键,如果按下,则退出主循环,并结束程序。
五、知识园地
1. 了解opencv的Lucas-Kanade光流法
Lucas-Kanade(LK)光流法是一种用于估计图像序列中物体运动的经典算法。它由Bruce D. Lucas和Takeo Kanade在1981年的论文"An Iterative Image Registration Technique with an Application to Stereo Vision"中提出。
LK光流法的主要思想是在小的邻域内,所有像素的运动速度都是相同的。这个假设使得我们可以在邻域内建立一个线性系统,然后通过求解这个线性系统来得到像素的运动速度。
LK光流法的基本步骤如下:
选择特征点:在第一帧图像中选择一些特征点。这些特征点通常是角点,因为角点具有丰富的纹理信息,适合用于跟踪。
建立线性系统:对于每个特征点,选择一个小的邻域(例如5x5或7x7的窗口),然后在这个邻域内建立一个线性系统。这个线性系统由以下公式给出:
I_x * u + I_y * v = -I_t
其中,I_x和I_y分别是图像在x和y方向上的梯度,I_t是图像在时间方向上的梯度,u和v是像素在x和y方向上的运动速度。
求解线性系统:由于线性系统是过定的(即未知数的数量少于方程的数量),我们无法直接求解。因此,我们需要使用最小二乘法来求解这个线性系统,得到像素的近似运动速度。
迭代优化:由于LK光流法假设像素的运动速度在邻域内是恒定的,这个假设在实际情况中可能并不成立。因此,我们需要通过迭代优化来改进运动速度的估计。具体来说,我们可以将当前帧的特征点按照估计的运动速度进行移动,然后在新的位置重复上述步骤,直到运动速度收敛。
通过以上步骤,LK光流法可以估计出图像序列中物体的运动。在计算机视觉中,LK光流法被广泛用于各种任务,如视频压缩、视频稳定、运动跟踪等。
2. 了解opencv的goodFeaturesToTrack特征点检测
goodFeaturesToTrack是OpenCV中的一个函数,用于在图像中检测角点。它基于一种叫做Shi-Tomasi角点检测方法,这种方法是Harris角点检测方法的改进版。
角点是图像中的一种特征,具有在所有方向上变化的强度变化。在视觉任务中,角点因具有丰富的纹理信息,常被用作特征点。
goodFeaturesToTrack函数的工作原理如下:
计算图像的每个像素点的最小特征值(也就是Shi-Tomasi得分)。
对所有像素点的得分进行排序,并选取最高的几个。
为了保证选取的角点分布均匀,函数会根据设定的最小距离参数,剔除过于接近的角点。
函数的主要参数如下:
· maxCorners:要检测的角点数量的最大值。如果实际检测到的角点数量超过这个值,函数会只返回得分最高的角点。
· qualityLevel:角点质量水平的阈值。这个参数用于剔除得分低于最高得分乘以此阈值的角点。
· minDistance:可接受的最小角点间距。
通过这个函数,我们可以在图像中快速地找到一些具有代表性的特征点,用于后续的图像处理任务,如特征匹配、光流跟踪等。
附件
评论