利用OpenCV光流算法实现视频特征点跟踪

光流简介

        光流(optical flow)是运动物体在观察成像平面上的像素运动的瞬时速度。光流法是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的对应关系,从而计算出相邻帧之间物体的运动信息的一种方法。通常将二维图像平面特定坐标点上的灰度瞬时变化率定义为光流矢量。光流是由物体或相机的运动引起的图像物体在连续两帧之间的明显运动的模式。它是 2D 矢量场,其中每个矢量是一个位移矢量,显示点从第一帧到第二帧的移动。

        以下图片显示了计算出的光流示意图,颜色表示光流方向,颜色饱和度表示大小:

 

        参考博文:

计算机视觉大型攻略 —— 光流(1)基本原理和经典算法_光流算法_linusyue的博客-CSDN博客

光流法(optical flow)简介_Fm镄的博客-CSDN博客

opencv光流实现

        光流追踪的前提是:

1. 对象的像素强度在连续帧之间不会改变;

2. 相邻像素具有相似的运动。

 OpenCV提供了两种算法计算光流:

cv::calcOpticalFlowPyrLK()---稀疏光流: 通过 Lucas-Kanade 方法计算稀疏特征集的光流(使用 Shi-Tomasi 算法检测到的角点

cv::calcOpticalFlowFarneback--密集光流: 通过 Gunner Farneback 来寻找密集光流。它计算帧中所有点的光流。

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

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过滤器而不是相同尺寸的盒过滤器;

源码实例

稀疏光流追踪

# 光流追踪
# 光流追踪的前提是:1. 对象的像素强度在连续帧之间不会改变;2. 相邻像素具有相似的运动。
# - cv2.goodFeaturesToTrack() 确定要追踪的特征点
# - cv2.calcOpticalFlowPyrLK() 追踪视频中的特征点# 取第一帧,检测其中的一些 Shi-Tomasi 角点,使用 Lucas-Kanade 光流迭代跟踪这些点。
# 对于函数 cv2.calcOpticalFlowPyrLK() 传递前一帧、前一个点和下一帧。它返回下一个点以及一些状态编号,如果找到下一个点,则值为 1,否则为零。
# 然后在下一步中迭代地将这些下一个点作为前一个点传递。# USAGE
# python video_optical_flow.pyimport imutils
import numpy as np
import cv2cap = 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:breakframe_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)# 使用迭代Lucas Kanade方法计算稀疏特征集的光流# - old_gray: 上一帧单通道灰度图# - frame_gray: 下一帧单通道灰度图# - prePts:p0上一帧坐标pts# - nextPts: None# - winSize: 每个金字塔级别上搜索窗口的大小# - maxLevel: 最大金字塔层数# - criteria:指定迭代搜索算法的终止条件,在指定的最大迭代次数criteria.maxCount之后或搜索窗口移动小于criteria.epsilonp1, 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 + 1k = cv2.waitKey(30) & 0xffif k == 27:break# 更新之前的帧和点old_gray = frame_gray.copy()p0 = good_new.reshape(-1, 1, 2)cv2.destroyAllWindows()
cap.release()

改进版稀疏光流追踪

# 优化后的光流追踪—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_functionimport imutils
import numpy as np
import cv2def draw_str(dst, target, s):x, y = targetcv2.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 = 10self.detect_interval = 30self.tracks = []self.cam = cv2.VideoCapture(video_src)self.frame_idx = 0def run(self):while True:_ret, frame = self.cam.read()if not _ret:breakframe_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)vis = frame.copy()if len(self.tracks) > 0:img0, img1 = self.prev_gray, frame_grayp0 = 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 < 1new_tracks = []for tr, (x, y), good_flag in zip(self.tracks, p1.reshape(-1, 2), good):if not good_flag:continuetr.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_trackscv2.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[:] = 255for 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_graycv2.imshow('lk_track', vis)print(self.frame_idx)cv2.imwrite('videoOof-imgs/' + str(self.frame_idx) + '.jpg', imutils.resize(vis, 500))self.frame_idx += 1ch = cv2.waitKey(1)if ch == 27:breakdef main():import systry:video_src = sys.argv[1]except:video_src = 0App(video_src).run()print('Done')if __name__ == '__main__':print(__doc__)main()cv2.destroyAllWindows()

密集光流追踪

# OpenCV中的密集光流
# Lucas-Kanade 方法计算稀疏特征集的光流(使用 Shi-Tomasi 算法检测到的角点)。
# OpenCV 提供了另一种算法: Gunner Farneback 来寻找密集光流。它计算帧中所有点的光流。
# 通过cv2.calcOpticalFlowFarneback() 将得到一个带有光流向量 (u,v) 的 2 通道阵列。可以找到它们的大小和方向,然后对结果进行颜色编码以实现更好的可视化。
# 在HSV图像中,方向对应于图像的色调,幅度对应于价值平面。import cv2
import imutils
import numpy as npcap = 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] = 255num = 0
while (1):ret, frame2 = cap.read()if not ret:breaknext = 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 / 2hsv[..., 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) & 0xffnum = num + 1if k == 27:breakelif 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 = nextcap.release()
cv2.destroyAllWindows()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/38510.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大模型PEFT技术原理(二):P-Tuning、P-Tuning v2

随着预训练模型的参数越来越大&#xff0c;尤其是175B参数大小的GPT3发布以来&#xff0c;让很多中小公司和个人研究员对于大模型的全量微调望而却步&#xff0c;近年来研究者们提出了各种各样的参数高效迁移学习方法&#xff08;Parameter-efficient Transfer Learning&#x…

css鼠标样式 cursor: pointer

cursor: none; cursor:not-allowed; 禁止选择 user-select: none; pointer-events:none;禁止触发事件, 该样式会阻止默认事件的发生&#xff0c;但鼠标样式会变成箭头

Hlang社区-前端社区宣传首页实现

文章目录 前言页面结构固定钉头部轮播JS特效完整代码总结前言 这里的话,博主其实也是今年参与考研的大军之一,所以的话,是抽空去完成这个项目的,当然这个项目的肯定是可以在较短的时间内完成的。 那么废话不多说,昨天也是干到1点多,把这个首页写出来了。先看看看效果吧:…

Linux中 socket编程中多进程/多线程TCP并发服务器模型

一、循环服务器(while)【不常用】 一次只能处理一个客户端的请求&#xff0c;等这个客户端退出后&#xff0c;才能处理下一个客户端。缺点&#xff1a;循环服务器所处理的客户端不能有耗时操作。 模型 sfd socket(); bind(); listen(); while(1) {newfd accept();while(1){r…

分别在linux和windows上设置socket为阻塞模式

在 Linux 和 Windows 系统中&#xff0c;都可以将 socket 设置为非阻塞模式。 Linux平台 在 Linux 系统中&#xff0c;可以使用 fcntl 函数来设置 socket 为非阻塞模式。例如&#xff1a; int flags fcntl(socket_fd, F_GETFL, 0); fcntl(socket_fd, F_SETFL, flags | O_NO…

【问心篇】渴望、热情和选择

加班太严重完全没有时间学习&#xff0c;怎么办&#xff1f; 我真的不算聪明的人&#xff0c;但是&#xff0c;我对学习真的是有渴望的。说得好听一点&#xff0c;我希望自己在不停地成长&#xff0c;不辜负生活在这个信息化大变革的时代。说得不好的一点&#xff0c;就是我从…

断点续传的未来发展趋势与前景展望

断点续传是一种在网络传输中断后&#xff0c;能够从中断的位置继续传输的技术。它可以有效地避免因为网络不稳定、服务器故障、用户操作等原因导致的传输失败&#xff0c;节省了用户的时间和流量&#xff0c;提高了传输的效率和可靠性。断点续传在很多场景中都有广泛的应用&…

AI 绘画Stable Diffusion 研究(八)sd采样方法详解

大家好&#xff0c;我是风雨无阻。 本文适合人群&#xff1a; 希望了解stable Diffusion WebUI中提供的Sampler究竟有什么不同&#xff0c;想知道如何选用合适采样器以进一步提高出图质量的朋友。 想要进一步了解AI绘图基本原理的朋友。 对stable diffusion AI绘图感兴趣的朋…

【洛谷 P5736】【深基7.例2】质数筛 题解(埃氏筛法)

【深基7.例2】质数筛 题目描述 输入 n n n 个不大于 1 0 5 10^5 105 的正整数。要求全部储存在数组中&#xff0c;去除掉不是质数的数字&#xff0c;依次输出剩余的质数。 输入格式 第一行输入一个正整数 n n n&#xff0c;表示整数个数。 第二行输入 n n n 个正整数 …

jquery如何修改选中状态

jquery修改选中状态的方法&#xff1a;1、使用addClass和removeClass方法&#xff0c;可以向选中的元素添加一个多个类名&#xff0c;从而改变其样式或状态&#xff1b;2、使用toggleClass方法&#xff0c;可以在选中元素上添加或移除一个类名&#xff0c;如果该类名已经存在&a…

208. 实现 Trie (前缀树)

Trie&#xff08;发音类似 "try"&#xff09;或者说 前缀树 是一种树形数据结构&#xff0c;用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景&#xff0c;例如自动补完和拼写检查。 请你实现 Trie 类&#xff1a; Trie() 初始化前缀树对象…

手撕LFU缓存

手撕LRU缓存_右大臣的博客-CSDN博客 是LRU的升级&#xff0c;多了一个访问次数的维度 实现 LFUCache 类&#xff1a; LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象int get(int key) - 如果键 key 存在于缓存中&#xff0c;则获取键的值&#xff0c;否则返…

C# Lamda到底是什么?!

lamda作为匿名函数&#xff0c;现在已经能够出现子啊C#程序的任何可能位置&#xff0c;它可能作为参数为委托或其他函数复制&#xff0c;或者单独作为表达式&#xff0c;或者承担一些类似C中内联函数的一些作用承担一些简单计算。熟练的使用Lamda表达式能够让减少代码的冗余&am…

Django图书商城系统实战开发-总结经验之后端开发

Django图书商城系统实战开发-总结经验之后端开发 简介 在这篇博客中&#xff0c;我将总结经验分享后端开发Django图书商城系统的过程。在开发过程中&#xff0c;我遇到了各种挑战和问题&#xff0c;并且通过实践获得了宝贵的经验和教训。通过本文&#xff0c;我希望能帮助读者…

vue3+vite配置vantUI主题

❓在项目中统一配置UI主题色&#xff0c;各个组件配色统一修改 vantUI按需安装 参考vantUI文档 创建vantVar.less文件夹进行样式编写 vantVar.less :root:root{//导航--van-nav-bar-height: 44px;//按钮--van-button-primary-color: #ffffff;--van-button-primary-backgr…

linux——mysql的高可用MHA

目录 一、概述 一、概念 二、组成 三、特点 四、工作原理 二、案例 三、构建MHA 一、基础环境 二、ssh免密登录 三、主从复制 master slave1 四、MHA安装 一、环境 二、安装node 三、安装manager 一、概述 一、概念 MHA&#xff08;MasterHigh Availability&a…

力扣 198. 打家劫舍

题目来源&#xff1a;https://leetcode.cn/problems/house-robber/description/ C题解&#xff1a;因为是间接偷窃&#xff0c;所以偷nums[i]家前&#xff0c;一定偷过第i-2或者i-3家&#xff0c;因为i-1不能偷。 例如12345共5家&#xff0c;先偷第1家&#xff0c;那么2不能偷…

(三)Unity开发Vision Pro——入门

3.入门 1.入门 本节涵盖了几个重要主题&#xff0c;可帮助您加快visionOS 平台开发速度。在这里&#xff0c;您将找到构建第一个 Unity PolySpatial XR 应用程序的分步指南的链接&#xff0c;以及 PolySpatial XR 开发时的一些开发最佳实践。 2.开发与迭代 有关先决条件、开…

显卡nvidia-smi后 提示Faild 解决过程,包含卸载重装NVIDIA驱动步骤

显卡异常: 显卡nvidia-smi后 提示Faild 解决过程&#xff0c;卸载重装nvidia驱动步骤 文章目录 显卡异常: 显卡nvidia-smi后 提示Faild 解决过程&#xff0c;卸载重装nvidia驱动步骤 [toc]1 缘由2 解决过程3 过程所需命令4 解决4.1 把该显卡重新拔插一下卸载NVIDIA驱动的方法&a…

单元测试优化:为什么要对程序进行测试?测试有什么好处?

单元测试&#xff08;Unit Testing&#xff09;又称为模块测试, 是针对程序模块&#xff08;软件设计的最小单位&#xff09;来进行正确性检验的测试工作。 程序单元是应用的最小可测试部件。简单来说&#xff0c;就是测试数据的稳定性是否达到程序的预期。 我们日常开发时可能…