2024-5-14 11:25:32 [显示全部楼层]
4864浏览
查看: 4864|回复: 0

[M10教程] 基于行空板的opencv lk_track目标追踪

[复制链接]

一、实践目标
本项目在行空板上外接USB摄像头,通过摄像头来检测目标,并进行跟踪。

二、知识目标
学习使用opencv的Lucas-Kanade tracker方法来检测目标并进行跟踪的方法。

三、实践准备
硬件清单:


软件使用:
Mind+编程软件x1


四、实践过程

1、硬件搭建
1、将摄像头接入行空板的USB接口。


2、通过USB连接线将行空板连接到计算机。


2、软件编写
第一步:打开Mind+,远程连接行空板


第二步:在“行空板的文件”中新建一个名为AI的文件夹,在其中再新建一个名为“基于行空板的opencv lk_track目标追踪”的文件夹,导入本节课的依赖文件。


第三步:编写程序
在上述文件的同级目录下新建一个项目文件,并命名为“main.py”。
示例程序:
  1. #!/usr/bin/env python
  2. '''
  3. Lucas-Kanade tracker
  4. ====================
  5. Lucas-Kanade sparse optical flow demo. Uses goodFeaturesToTrack
  6. for track initialization and back-tracking for match verification
  7. between frames.
  8. Usage
  9. -----
  10. lk_track.py [<video_source>]
  11. Keys
  12. ----
  13. ESC - exit
  14. '''
  15. # Python 2/3 compatibility
  16. from __future__ import print_function
  17. # 导入所需库
  18. import numpy as np
  19. import cv2 as cv
  20. # 导入其他模块
  21. import video
  22. from common import anorm2, draw_str
  23. # 设置Lucas-Kanade光流跟踪参数
  24. lk_params = dict( winSize  = (15, 15),
  25.                   maxLevel = 2,
  26.                   criteria = (cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 0.03))
  27. # 设置角点检测参数
  28. feature_params = dict( maxCorners = 500,
  29.                        qualityLevel = 0.3,
  30.                        minDistance = 7,
  31.                        blockSize = 7 )
  32. class App:
  33.     def __init__(self, video_src):
  34.         # 初始化相关参数
  35.         self.track_len = 10
  36.         self.detect_interval = 5
  37.         self.tracks = []
  38.         # 创建视频捕获对象
  39.         self.cam = video.create_capture(video_src)
  40.         self.frame_idx = 0
  41.         # 创建全屏窗口
  42.         cv.namedWindow('lk_track',cv.WND_PROP_FULLSCREEN)
  43.         cv.setWindowProperty('lk_track', cv.WND_PROP_FULLSCREEN, cv.WINDOW_FULLSCREEN)
  44.     def run(self):
  45.         while True:
  46.             # 读取一帧图像
  47.             _ret, frame = self.cam.read()
  48.             # 转为灰度图像
  49.             frame_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
  50.             vis = frame.copy()
  51.             # 如果有跟踪点
  52.             if len(self.tracks) > 0:
  53.                 # 获取前后帧图像
  54.                 img0, img1 = self.prev_gray, frame_gray
  55.                 # 获取跟踪点坐标
  56.                 p0 = np.float32([tr[-1] for tr in self.tracks]).reshape(-1, 1, 2)
  57.                 # 计算光流
  58.                 p1, _st, _err = cv.calcOpticalFlowPyrLK(img0, img1, p0, None, **lk_params)
  59.                 # 反向计算光流
  60.                 p0r, _st, _err = cv.calcOpticalFlowPyrLK(img1, img0, p1, None, **lk_params)
  61.                 # 计算反向光流误差
  62.                 d = abs(p0-p0r).reshape(-1, 2).max(-1)
  63.                 # 筛选出误差小于1的跟踪点
  64.                 good = d < 1
  65.                 new_tracks = []
  66.                 for tr, (x, y), good_flag in zip(self.tracks, p1.reshape(-1, 2), good):
  67.                     # 如果误差过大,放弃该跟踪点
  68.                     if not good_flag:
  69.                         continue
  70.                     # 添加新的跟踪点
  71.                     tr.append((x, y))
  72.                     # 如果跟踪点过多,删除旧的跟踪点
  73.                     if len(tr) > self.track_len:
  74.                         del tr[0]
  75.                     # 添加新的跟踪路径
  76.                     new_tracks.append(tr)
  77.                     # 在图像上绘制跟踪点
  78.                     cv.circle(vis, (int(x), int(y)), 2, (0, 255, 0), -1)
  79.                 # 更新跟踪路径
  80.                 self.tracks = new_tracks
  81.                 # 在图像上绘制跟踪路径
  82.                 cv.polylines(vis, [np.int32(tr) for tr in self.tracks], False, (0, 255, 0))
  83.                 # 在图像上显示跟踪点数目
  84.                 draw_str(vis, (20, 20), 'track count: %d' % len(self.tracks))
  85.             # 在固定帧数间隔后检测新的特征点
  86.             if self.frame_idx % self.detect_interval == 0:
  87.                 # 创建掩码
  88.                 mask = np.zeros_like(frame_gray)
  89.                 mask[:] = 255
  90.                 # 对已有跟踪点周围区域进行掩蔽
  91.                 for x, y in [np.int32(tr[-1]) for tr in self.tracks]:
  92.                     cv.circle(mask, (x, y), 5, 0, -1)
  93.                 # 检测新的特征点
  94.                 p = cv.goodFeaturesToTrack(frame_gray, mask = mask, **feature_params)
  95.                 # 如果检测到特征点,添加到跟踪点中
  96.                 if p is not None:
  97.                     for x, y in np.float32(p).reshape(-1, 2):
  98.                         self.tracks.append([(x, y)])
  99.             # 更新帧数和前一帧图像
  100.             self.frame_idx += 1
  101.             self.prev_gray = frame_gray
  102.             # 显示图像
  103.             cv.imshow('lk_track', vis)
  104.             # 按下ESC键退出
  105.             ch = cv.waitKey(1)
  106.             if ch == 27:
  107.                 break
  108. def main():
  109.     import sys
  110.     try:
  111.         video_src = sys.argv[1]
  112.     except:
  113.         video_src = 0
  114.     # 运行程序
  115.     App(video_src).run()
  116.     print('Done')
  117. if __name__ == '__main__':
  118.     print(__doc__)
  119.     main()
  120.     # 关闭所有窗口
  121.     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:可接受的最小角点间距。
通过这个函数,我们可以在图像中快速地找到一些具有代表性的特征点,用于后续的图像处理任务,如特征匹配、光流跟踪等。

project.zip

8.49 KB, 下载次数: 2319

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail