Opencv计算机视觉编程攻略-第十三节 跟踪视频中的物品

      这是opencv系列的最后一节,主要学习视频序列,上一节介绍了读取、处理和存储视频的工具,本文将介绍几种跟踪图像序列中运动物体的算法。可见运动或表观运动,是物体以不同的速度在不同的方向上移动,或者是因为相机在移动(或者两者都有)。在很多应用程序中,跟踪表观运动都是极其重要的。它可用来追踪运动中的物体,以测定它们的速度、判断它们的目的地。对于手持摄像机拍摄的视频,可以用这种方法消除抖动或减小抖动幅度,使视频更加平稳。运动估值还可用于视频编码,用以压缩视频,便于传输和存储。

目录

1. 跟踪视频中的特征点

2. 估算光流

3. 跟踪视频中的物体


1. 跟踪视频中的特征点

         被跟踪的运动可以是稀疏的(图像的少数位置上有运动,称为稀疏运动),也可以是稠密的(图像的每个像素都有运动,称为稠密运动)。在启动跟踪过程时,首先要在最初的帧中检测特征点,然后在下一帧中跟踪这些特征点,如果想找到特征点在下一帧的新位置,就必须在它原来位置的周围进行搜索。这个功能由函数cv::calcOpticalFlowPyrLK 实现。在函数中输入两个连续的帧和第一幅图像中特征点的向量,将返回新的特征点位置的向量。

      要逐帧地跟踪特征点,就必须在后续帧中定位特征点的新位置。假设每个帧中特征点的强度
值是不变的,这个过程就是寻找如下的位移(u, v):

      其中It 和It+1 分别是当前帧和下一个瞬间的帧。强度值不变的假设普遍适用于相邻图像上的
微小位移。我们可使用泰勒展开式得到近似方程式(包含图像导数):

       根据第二个方程式,可以得到另一个方程式(根据强度值不变的假设,去掉了两个表示强度
值的项)

        这就是基本的光流约束方程,也称作亮度恒定方程,Lukas-Kanade 特征跟踪算法使用了这个约束方程。除此之外,该算法还做了一个假设,即特征点邻域中所有点的位移量是相等的。因此,我们可以将光流约束应用到所有位移量为(u, v)的点(u 和v 还是未知的)。这样就得到了更多的方程式,数量超过未知数的个数(两个),因此可以在均方意义下解出这个方程组。

       在实际应用中,我们采用迭代的方法来求解。为了使搜索更高效且适应更大的位移量,OpenCV  还提供了在不同分辨率下进行计算的方法:默认的图像等级数量为3,窗口大小为15,还可以设定一个终止条件,符合这个条件时就停止迭代搜索。

      cv::calcOpticalFlowPyrLK 函数的第六个参数是剩余均方误差,用于评定跟踪的质量。第五个参数包含二值标志,表示是否成功跟踪了对应的点

// 1. 特征点检测方法
void detectFeaturePoints() {// 检测特征点cv::goodFeaturesToTrack(gray, // 图像features, // 输出检测到的特征点max_count, // 特征点的最大数量qlevel, // 质量等级minDist); // 特征点之间的最小差距
}// 2 法根据应用程序定义的条件剔除部分被跟踪的特征点。这里剔除静止的特征点(还有不能被cv::calcOpticalFlowPyrLK 函数跟踪的特征点)。我们假定静止的点属于背景部分,可以忽略:
// 判断需要保留的特征点
bool acceptTrackedPoint(int i) {return status[i] &&// 如果特征点已经移动(abs(points[0][i].x-points[1][i].x)+(abs(points[0][i].y-points[1][i].y))>2);
}// 3 处理被跟踪的特征点,具体做法是在当前帧画直线,连接特征点和它们的初始位置(即第一次检测到它们的位置):
// 处理当前跟踪的特征点
void handleTrackedPoints(cv:: Mat &frame, cv:: Mat &output) {// 遍历所有特征点for (int i= 0; i < points[1].size(); i++ ) {// 画线和圆cv::line(output, initial[i], // 初始位置points[1][i], // 新位置cv::Scalar(255,255,255));cv::circle(output, points[1][i], 3,cv::Scalar(255,255,255),-1);
}
}


2. 估算光流

        通常关注视频序列中运动的部分,即场景中不同元素的三维运动在成像平面上的投影。三维运动向量的投影图被称作运动场。但是在只有一个相机传感器的情况下,是不可能直接测量三维运动的,我们只能观察到帧与帧之间运动的亮度模式,亮度模式上的表观运动被称作光流。通常认为运动场和光流是等同的,但其实不一定:典型的例子是观察均匀的物体;例如相机在白色的墙壁前移动时就不产生光流。

      估算光流其实就是量化图像序列中亮度模式的表观运动。首先来看视频中某个时刻的一帧画
面。观察当前帧的某个像素(x, y),我们要知道它在下一帧会移动到哪个位置。也就是说,这个点
的坐标在随着时间变化(表示为(x(t), y(t))),而我们要估算出这个点的速度(dx/dt, dy/dt),对应的帧中获取这个点在t 时刻的亮度,表示为I(x(t), y(t),t),根据图像亮度恒定的假设:

      这个约束条件可以用基于光流的拉普拉斯算子的公式表示:

       现在要做的就是找到光流场,使亮度恒定公式的偏差和光流向量的拉普拉斯算子都达到最
小值,估算稠密光流的方法有很多,可以使用cv::Algorithm的子类cv::DualTVL1OpticalFlow。

        所得结果是二维向量(cv::Point)组成的图像,每个二维向量表示一个像素在两个帧之
间的变化值。要展示结果,就必须显示这些向量。为此我们创建了一个函数,用来创建光流场的图像映射。为控制向量的可见性,需要使用两个参数:步长(间隔一定像素)和缩放因子,

// 1. 创建光流算法
cv::Ptr<cv::DualTVL1OpticalFlow> tvl1 = cv::createOptFlow_DualTVL1();
这个实例已经可以使用了,所以只需调用计算两个帧之间的光流场的方法即可:
cv::Mat oflow; // 二维光流向量的图像
// 计算frame1 和frame2 之间的光流
tvl1->calc(frame1, frame2, oflow);// 2. 绘制光流向量图
void drawOpticalFlow(const cv::Mat& oflow, // 光流
cv::Mat& flowImage, // 绘制的图像
int stride, // 显示向量的步长
float scale, // 放大因子
const cv::Scalar& color) // 显示向量的颜色
{
// 必要时创建图像
if (flowImage.size() != oflow.size()) {
flowImage.create(oflow.size(), CV_8UC3);
flowImage = cv::Vec3i(255,255,255);
}
// 对所有向量,以stride 作为步长
for (int y = 0; y < oflow.rows; y += stride)
for (int x = 0; x < oflow.cols; x += stride) {
// 获取向量
cv::Point2f vector = oflow.at< cv::Point2f>(y, x);
// 画线条
cv::line(flowImage, cv::Point(x,y),
cv::Point(static_cast<int>(x + scale*vector.x + 0.5),
static_cast<int>(y + scale*vector.y + 0.5)),
color);
// 画顶端圆圈
cv::circle(flowImage,
cv::Point(static_cast<int>(x + scale*vector.x + 0.5),
static_cast<int>(y + scale*vector.y + 0.5)),
1, color, -1);
}
}

      前面使用的方法被称作双DV L1 方法,由两部分组成。第一部分使用光滑约束,使光流梯度的绝对值(不是平方值)最小化;选用绝对值可以削弱平滑度带来的影响,尤其是对于不连续的区域,运动物体和背景部分的光流向量的差别很大。第二部分使用一阶泰勒近似,使亮度恒定约束公式线性化。 


3. 跟踪视频中的物体

     在很多应用程序中,更希望能够跟踪视频中一个特定的运动物体。为此要先标识出该物体,然后在很长的图像序列中对它进行跟踪。这是一个很有挑战性的课题,因为随着物体在场景中的运动,物体的图像会因视角和光照改变、非刚体运动、被遮挡等原因而不断变化

import cv2	 # type: ignore
import numpy as np
# Illustration of the Median Tracker principle
image1 = cv2.imread("E:/CODE/images/goose/goose130.bmp", 0)
image_show = cv2.imread("E:/CODE/images/goose/goose130.bmp")
#define a regular grid of points
grid = []
x,y,width,height = 290, 100, 65, 40
for i in range(10):for j in range(10):p = (x+i*width/10,y+j*height/10)grid.append(p)
grid = np.array(grid, dtype=np.float32)
#track in next image
image2 = cv2.imread("E:/CODE/images/goose/goose131.bmp",0)
lk_params = []lk_params = dict(winSize=(10, 10),maxLevel=2,criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
# ShiTomasi corner detection的参数
feature_params = dict(maxCorners=300,qualityLevel=0.3,minDistance=7,blockSize=7)p0 = cv2.goodFeaturesToTrack(image1, mask=None, **feature_params)
#grid 从22*2 维度调整为22*1*2
grid = grid.reshape(-1,1,2)
kp2, st, err =cv2.calcOpticalFlowPyrLK(image1, image2, grid, None, **lk_params)   
#good_new = kp2[st == 1]
for i in grid:cv2.circle(image_show, (int(i[0][0]),int(i[0][1])), 1, (255, 255, 255), 3)for i in kp2:cv2.circle(image_show, (int(i[0][0]),int(i[0][1])), 1, (255, 0, 255), 3)cv2.imshow("Tracked points", image_show)cv2.waitKey()

       开始跟踪前,要先在一个帧中标识出物体,然后从这个位置开始跟踪。标识物体的方法就是指定一个包含该物体的矩形(YOLO),而跟踪模块的任务就是在后续的帧中重新识别出这个物体。OpenCV 中的物体跟踪框架类cv::Tracker 包含两个主方法,一个是init 方法,用于定义初始目标矩形;另一个是update 方法,输出新的帧中对应的矩形。中值流量跟踪算法的基础是特征点跟踪。它先在被跟踪物体上定义一个点阵。你也可以改为检测物体的兴趣点,例如采用第8 章介绍的FAST 算子检测兴趣点。但是使用预定位置的点有很多好处:它不需要计算兴趣点,因而节约了时间;它可以确保用于跟踪的点的数量足够多,还能确保这些点分布在整个物体上。默认情况下,中值流量法采用10×10 的点阵。

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

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

相关文章

001 蓝桥杯嵌入式赛道备赛——基础

个人笔记&#xff0c;不扭扭捏捏&#xff0c;一口气到位。方便自己也方便大家 00 时钟线 cubeMX已经完成了大多数工作 01 LED&#xff08;GPIO输出&#xff09; 在使用LED的时候先把SN74HC573锁存器PD2置高电平&#xff0c;然后写入LED所要的高低电平&#xff0c;然后置PD2低…

案例-索引对于并发Insert性能优化测试

前言 最近因业务并发量上升,开发反馈对订单表Insert性能降低。应开发要求对涉及Insert的表进行分析并提供优化方案。   一般对Insert 影响基本都在索引,涉及表已按创建日期做了分区表,索引全部为普通索引未做分区索引。 优化建议: 1、将UNIQUE改为HASH(64) GLOBAL IND…

【技术文章的标准结构与内容指南】

技术文章的标准结构与内容指南 技术文章是传递专业知识、分享实践经验的重要媒介。一篇高质量的技术文章不仅能够帮助读者解决问题&#xff0c;还能促进技术交流与创新。以下是技术文章通常包含的核心内容与结构指南。 1. 标题 一个好的技术文章标题应当&#xff1a; 简洁明…

豪越消防一体化安全管控平台:构建消防“一张图”新生态

在城市化进程加速、建筑规模与功能日益复杂的当下&#xff0c;消防救援工作面临着诸多严峻挑战。火灾隐患如同隐藏在暗处的“定时炸弹”&#xff0c;广泛分布于城市的各个角落&#xff0c;想要快速、精准定位绝非易事。信息传递的不顺畅更是雪上加霜&#xff0c;导致救援效率大…

重学Redis:Redis常用数据类型+存储结构(源码篇)

一、SDS 1&#xff0c;SDS源码解读 sds (Simple Dynamic String)&#xff0c;Simple的意思是简单&#xff0c;Dynamic即动态&#xff0c;意味着其具有动态增加空间的能力&#xff0c;扩容不需要使用者关心。String是字符串的意思。说白了就是用C语言自己封装了一个字符串类型&a…

抖音IP属地可以随便选择地址吗?深度解析

在当今社交媒体盛行的时代&#xff0c;抖音作为受欢迎的短视频平台之一&#xff0c;其IP属地显示功能引发了广泛关注。许多用户好奇&#xff1a;抖音的IP属地是否可以随意更改&#xff1f;是否存在方法可以“伪装”自己的位置&#xff1f;‌本文将深入探讨这一话题。 一、抖音I…

SOLID原则详解:提升软件设计质量的关键

前言 关于设计原则SOLID具体指的是什么&#xff0c;怎么理解这些设计原则&#xff0c;我觉得有必要记录一笔&#xff0c;毕竟这个设计原则确实经常在关键技术文档中提及&#xff0c;在编程思想中提及&#xff0c;在日常的开发中使用&#xff0c;但是对我来说&#xff0c;似乎知…

如何使用 ONLYOFFICE 恢复之前的文件版本?

如何使用 ONLYOFFICE 恢复之前的文件版本&#xff1f; https://www.onlyoffice.com/blog/zh-hans/2023/04/how-to-use-version-history

简简单单实现一个Python+Selenium的自动化测试框架

什么是Selenium&#xff1f; Selenium是一个基于浏览器的自动化测试工具&#xff0c;它提供了一种跨平台、跨浏览器的端到端的web自动化解决方案。Selenium主要包括三部分&#xff1a;Selenium IDE、Selenium WebDriver 和Selenium Grid。 Selenium IDE&#xff1a;Firefox的…

Java设计模式之中介者模式:从入门到架构级实践

一、什么是中介者模式&#xff1f; 中介者模式&#xff08;Mediator Pattern&#xff09;是一种行为型设计模式&#xff0c;其核心思想是通过引入一个中介对象来封装多个对象之间的交互关系。这种模式将原本复杂的网状通信结构转换为星型结构&#xff0c;类似于现实生活中的机…

Trinity三位一体开源程序是可解释的 AI 分析工具和 3D 可视化

一、软件介绍 文末提供源码和程序下载学习 Trinity三位一体开源程序是可解释的 AI 分析工具和 3D 可视化。Trinity 提供性能分析和 XAI 工具&#xff0c;非常适合深度学习系统或其他执行复杂分类或解码的模型。 二、软件作用和特征 Trinity 通过结合具有超维感知能力的不同交…

LeetCode 热题 100_单词拆分(86_139_中等_C++)(动态规划)

LeetCode 热题 100_单词拆分&#xff08;86_139&#xff09; 题目描述&#xff1a;输入输出样例&#xff1a;题解&#xff1a;解题思路&#xff1a;思路一&#xff08;动态规划&#xff09;&#xff1a; 代码实现代码实现&#xff08;思路一&#xff08;动态规划&#xff09;&a…

VM虚拟机安装及Ubuntu安装配置

VM虚拟机安装及Ubuntu安装配置 1、VM虚拟机安装2、创建虚拟机3、Ubuntu系统安装4、编译环境配置4.1 、Ubuntu和 Windows文件互传 文件互传4.1.1、 开启Ubunt下的FTP服务 4.2、 Ubuntu下NFS和SSH服务开启4.2.1、 NFS服务开启4.2.2、 SSH服务开启 4.3、 交叉编译器安装4.3.1 安装…

【KWDB 创作者计划】_产品技术解读_1

【KWDB 创作者计划】_产品技术解读_1 一、存储引擎:高性能混合存储架构1. 存储模型设计2. 存储压缩与编码3. 持久化策略二、KWDB 组件源码解析1. 核心模块分层架构2. 关键组件源码剖析三、KWDB 特性代码通读1. 实时分析能力(Real-Time OLAP)2. 混合负载隔离(HTAP)3. 智能索…

高速电路中的电阻、电容的选型及应用

2.1 电阻的应用 2.1.1 与电阻相关的经典案例 如果说芯片是电路的骨架&#xff0c;那么电阻就是在芯片之间起连接作用的关节。电阻的阻值、布放位置等&#xff0c;对设计的成功起着至关重要的作用。 【案例2.1】串联电阻过大&#xff0c;导致板间告警失败 某产品由业务板和主…

springBoot接入文心一言

文章目录 效果接入步骤项目接入配置类&#xff1a;WenXinYiYan前端vue代码js代码 后端mapper层service层controller层 测试代码 效果 先来看一下最后实现的效果 &#xff08;1&#xff09;未点击前的功能页面 &#xff08;2&#xff09;点击后的页面 &#xff08;3&#xff…

css解决边框四个角有颜色

效果 html <div class"gradient-corner">2021年</div>css background:/* 左上角横线 */linear-gradient(90deg, rgb(5, 150, 247) 9px, transparent 0) 0 0,/* 左上角竖线 */linear-gradient(0deg, rgb(5, 150, 247) 9px, transparent 0) 0 0,/* 右上…

自动化三维扫描:CASAIM外观尺寸智能检测

制造业向智能化、数字化加速转型&#xff0c;传统检测方式因效率低、精度差、数据断层等问题&#xff0c;已难以满足现代工业对精密测量与实时质控的需求。CASAIM依托前沿技术实力&#xff0c;以自动化三维扫描为核心&#xff0c;为工业检测提供了从数据采集到智能分析的全流程…

突破亚马逊壁垒,Web Unlocker API 助您轻松获取数据

目录 一、Web Unlocker API简介二、开始使用Web Unlocker API1、首先进入控制台页面&#xff0c;点击左侧第一个tab键“代理 & 抓取基础设施”&#xff0c;找到“网页解锁器”&#xff0c;开始使用。2、进入网页解锁器页面后&#xff0c;填写通道名称&#xff0c;添加简短描…

【力扣05】最长回文子串

0. 引言 ●子串(substring&#xff09;&#xff1a;原始字符串的一个连续子集; ●子序列&#xff08;subsequence&#xff09;&#xff1a;原始字符串的一个子集。 1. 什么叫回文串&#xff1f; 如果一个字符串正着读和反着读是一样的&#xff0c;那它就是回文串。[1] 例如&…