利用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点多,把这个首页写出来了。先看看看效果吧:…

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

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

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

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

手撕LFU缓存

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

Deep Learning With Pytorch - 最基本的感知机、贯序模型/分类、拟合

文章目录 如何利用pytorch创建一个简单的网络模型&#xff1f;Step1. 感知机&#xff0c;多层感知机&#xff08;MLP&#xff09;的基本结构Step2. 超平面 ω T ⋅ x b 0 \omega^{T}xb0 ωT⋅xb0 or ω T ⋅ x b \omega^{T}xb ωT⋅xb感知机函数 Step3. 利用感知机进行决策…

SpringBoot整合Minio

SpringBoot整合Minio 在企业开发中&#xff0c;我们经常会使用到文件存储的业务&#xff0c;Minio就是一个不错的文件存储工具&#xff0c;下面我们来看看如何在SpringBoot中整合Minio POM pom文件指定SpringBoot项目所依赖的软件工具包 <?xml version"1.0" …

DaVinci Resolve Studio 18 for Mac 达芬奇调色

DaVinci Resolve Studio 18是一款专业的视频编辑和调色软件&#xff0c;适用于电影、电视节目、广告等各种视觉媒体的制作。它具有完整的后期制作功能&#xff0c;包括剪辑、调色、特效、音频处理等。 以下是DaVinci Resolve Studio 18的主要特点&#xff1a; - 提供了全面的视…

Jmeter-压测时接口按照顺序执行-临界部分控制器

文章目录 临界部分控制器存在问题 临界部分控制器 在进行压力测试时&#xff0c;需要按照顺序进行压测&#xff0c;比如按照接口1、接口2、接口3、接口4 进行执行 查询结果是很混乱的&#xff0c;如果请求次数少&#xff0c;可能会按照顺序执行&#xff0c;但是随着次数增加&a…

Python-OpenCV中的图像处理-模板匹配

Python-OpenCV中的图像处理-模板匹配 模板匹配单对象的模板匹配多对象的模板匹配 模板匹配 使用模板匹配可以在一幅图像中查找目标函数&#xff1a; cv2.matchTemplate()&#xff0c; cv2.minMaxLoc()模板匹配是用来在一副大图中搜寻查找模版图像位置的方法。 OpenCV 为我们提…

无线充电底座

<项目>无线充电器 前言 个人DIY的无线充电底座&#xff08;带磁吸&#xff09;&#xff0c;基于IP6829方案。 Drawn By:67373 硬件部分 3D模型 资料开源链接 https://github.com/linggan17/WirelessCharge

面试热题(每日温度)

请根据每日 气温 列表 temperatures &#xff0c;重新生成一个列表&#xff0c;要求其对应位置的输出为&#xff1a;要想观测到更高的气温&#xff0c;至少需要等待的天数。如果气温在这之后都不会升高&#xff0c;请在该位置用 0 来代替。 输入: temperatures [73,74,75,71,69…

SpringBoot + Mybatis多数据源

一、配置文件 spring: # datasource: # username: root # password: 123456 # url: jdbc:mysql://127.0.0.1:3306/jun01?characterEncodingutf-8&serverTimezoneUTC # driver-class-name: com.mysql.cj.jdbc.Driverdatasource:# 数据源1onedata:jdbc-url: j…

SCF金融公链新加坡启动会 链结创新驱动未来

新加坡迎来一场引人瞩目的金融科技盛会&#xff0c;SCF金融公链启动会于2023年8月13日盛大举行。这一受瞩目的活动将为金融科技领域注入新的活力&#xff0c;并为广大投资者、合作伙伴以及关注区块链发展的人士提供一个难得的交流平台。 在SCF金融公链启动会上&#xff0c; Wil…