光流算法从理论到实践专题1

opencv--LK光流算法--源码解析 opencv--LK光流算法--源码解析 LK光流算法由Jean - Yves Bouguet提出,该算法是基于亮度恒定、时间连续、空间具有一致性的前提下,提出的一种基于特征点的追踪算法。该算法是将求两帧间特征点的最小误差值应用到图像上进而转换为求图像微分、差分的一种迭代算法。 光流算法用例 Mat old_frame, old_gray; vector<Point2f> p0, p1; // Take first frame and find corners i 阅读详情

资料搜索

光流估计-从传统方法到深度学习

光流法研究笔记

计算机视觉--光流法(optical flow)简介

《An Iterative Image Registration Technique with an Application to Stereo Vision》

视觉光流矢量场估计算法综述

本人总结

1、什么是光流?

       通俗来说就是,在物体运动过程中,会造成对应位置的亮度变化。例如,在停车场中地面为灰色的某一位置,停放着一辆红色保时捷,我们看该位置为红色。但过了一分钟后这辆保时捷开走了,该位置颜色变为灰色,我们在看是该位置为灰色。说白了就是,反映在我们人眼的是颜色、亮度发生变化,这就是光流。从图像角度来讲,视频图像的一帧中代表同一对象的像素点移到下一帧时该对象的像素移动量。

2、为什么要研究光流?

       光流概念是Gibson在1950年首先提出来的,发展到很多研究院对该算法进行了各种的改进,其中最新的光流算法,就是近几年通过引入神经网络提出的FlowNet/FlowNet2。为什么会到现在还是有很多科研工作者对光流算法进行研究呢!我们可以举个例子来说明这个问题:假如我们在漆黑的夜晚行走时,我们看见远方来了一个正向你逐渐靠近的人,因为周围漆黑你无法判断这个人是谁,但是当你观察几秒,发现走路的姿态可以初步判定这个人是赵三。当这个人近距离走进你的时候,发现这个人确实是赵三。从上面这个例子可以分析到,我们只考虑单张图像去做分析,有的时候很难做出结论,但是当我们考虑时间相关的一段视频,更有助于我们做分析和判断。而且随着5G技术的发展,视频传输不在是问题,计算机视觉从单张图像研究也会逐渐向视频研究展开,光流估计作为视频理解的就显得重中之重了。

3、光流算法推导及实现

3.1 Lucas-Kanade

        LK光流算法是在1981年提出,想看它对应论文的可以点击标题,就可以跳转到对应的pdf。LK算法是基于三个假设(亮度恒定、运动是小运动、空间一致)展开研究的,我们下面主要分析这三个假设的数学推导,以及思考一下为什么会使这三点。

3.1.1 亮度恒定

(1)概念

        说白了就是我们获得的视频帧率非常快,按照一般情况下视频帧率为25帧,所以两帧之间的时间间隔也就40ms,而我们可以大体认为40ms同一位置亮度的变化是基本不变的。

(2)公式转化

         根据上述的描述,我们可以将其转化为如下数学公式:I(x,y,t)=I(x+u,y+v,t+\Delta t),即t时刻(x,y)位置的亮度与t+\Delta t时刻对应位置(x+u,y+v)的亮度相同。

         上面的假设我们可以得到一个等式方程,但是我们要如何求解这个方程呢。因为一般视频中两帧之间的间隔非常短,即认为两帧之间运动非常小。

3.1.2 运动是小运动

(1)概念

          即运动的变化可以理解为是时间的导数

(2)公式展开

        根据泰勒展开可以得到I(x+u,y+v,t+\Delta t) = I(x,y,t) + I_{x}^{'}u + I_{y}^{'}v + I_{t}^{'} \Delta t。关于泰勒展开的相关知识,可以看此链接。

根据3.1.1中的等式可以将公式转化为:I(x,y,t) = I(x,y,t) + I_{x}^{'}u + I_{y}^{'}v + I_{t}^{'} \Delta t

由此可以得到公式:I_{x}^{'}u + I_{y}^{'}v + I_{t}^{'} \Delta t = 0

转化为矩阵表达:\begin{bmatrix} I_{x}^{'} & I_{y}^{'} \end{bmatrix}\begin{bmatrix} v\\ u \end{bmatrix}= -I_{t}^{'} \Delta t

其中,I_{x}^{'} 和 I_{y}^{'}分别对应在x方向和y方向的偏导数,I_{t}^{'}对应在t时刻在(x,y)处像素的时间导数。I_{t}^{'}\Delta t

在位置(x,y)的亮度差,即可以表示为\Delta I_{t}^{}。

因此公式可以转化为:\begin{bmatrix} I_{x}^{'} & I_{y}^{'} \end{bmatrix}\begin{bmatrix} v\\ u \end{bmatrix}= -\Delta I_{t}^{}

给定两张图像,我们可以已知的是I_{x}^{'} 、 I_{y}^{'}、\Delta I_{t}^{}。带求解的是u、v值,但是一个位置点只能得到一个方程,所以如何求解位置数u、v。我们肯定都会想到就是增加等式方程,但我们要怎么增加等式方程,我们都知道物以类聚,所以只要距离比较小的,我们就会认为这类性质一般都是一样的。所以,我们又提出下面的假设:空间一致

3.1.3 空间一致

(1)概念

        根据上面3.1.2中叙述的一样,取距离最近的点认为它们的性质是一致的,即满足同一个方程。因为我们可以认为(x,y)周围3*3大小或者其它大小空间中的周围点满足同一个方程。

(2)公式

根据概念可以转化为下面的公式,其中1,2,...,n代表是(x,y)周围的像素点。

\begin{bmatrix} I_{x}^{'(1)}& I_{y}^{'(1)}\\ I_{x}^{'(2)}& I_{y}^{'(2)} \\ .....&..... \\ I_{x}^{'(n)}& I_{y}^{'(n)} \end{bmatrix} \begin{bmatrix} v\\ u \end{bmatrix}= \begin{bmatrix} -\Delta I_{t}^{(1)}\\ -\Delta I_{t}^{(1)}\\ .....\\ -\Delta I_{t}^{(n)}\\ \end{bmatrix}

要求解这个超参数方程,一般做法是使用最小二乘法求解,不了解最小二乘法的看此链接。因此,上面方程转化为下面形式:

Ax=b,其中A代表\begin{bmatrix} I_{x}^{'(1)}& I_{y}^{'(1)}\\ I_{x}^{'(2)}& I_{y}^{'(2)} \\ .....&..... \\ I_{x}^{'(n)}& I_{y}^{'(n)} \end{bmatrix},x代表\begin{bmatrix} v\\ u \end{bmatrix},b代表\begin{bmatrix} -\Delta I_{t}^{(1)}\\ -\Delta I_{t}^{(1)}\\ .....\\ -\Delta I_{t}^{(n)}\\ \end{bmatrix}

根据线性代数矩阵运算知识(不懂得看此链接)可得x=(A^{T}A)^{-1}A^{T}b

为了使方程有解,(A^{T}A)^{}要求是可逆的。但是当(A^{T}A)^{}不可逆时,即可能会出现孔径的问题,如下图所示情况。所以在(A^{T}A)^{}可逆的情况下,一般都是亮度变化比较明显的位置。我们通常所用的Harris角点检测(不懂得看此链接,后面有时间我也会单独写一篇角点检测专题),就是用的是(A^{T}A)^{}可逆的性质。

3.2 OpenCV实现

下面是使用OpenCV例子,该程序中包含稠密光流和稀疏光流,只需要flow_type即可,测试了针对当前这个场景效果差不多。

#include <iostream>
#include <vector>

#include <opencv2/core.hpp>
#include <opencv2/core/utility.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/video.hpp>
#include <opencv2/cudaoptflow.hpp>
#include <opencv2/cudaimgproc.hpp>
#include <opencv2/cudaarithm.hpp>

using namespace std;
using namespace cv;
using namespace cv::cuda;

static void download(const GpuMat& d_mat, vector<Point2f>& vec)
{
	vec.resize(d_mat.cols);
	Mat mat(1, d_mat.cols, CV_32FC2, (void*)&vec[0]);
	d_mat.download(mat);
}

static void download(const GpuMat& d_mat, vector<uchar>& vec)
{
	vec.resize(d_mat.cols);
	Mat mat(1, d_mat.cols, CV_8UC1, (void*)&vec[0]);
	d_mat.download(mat);
}

static void drawArrows(Mat& frame, const vector<Point2f>& prevPts, const vector<Point2f>& nextPts, const vector<uchar>& status, Scalar line_color = Scalar(0, 0, 255))
{
	for (size_t i = 0; i < prevPts.size(); ++i)
	{
		if (status[i])
		{
			int line_thickness = 1;

			Point p = prevPts[i];
			Point q = nextPts[i];

			double angle = atan2((double)p.y - q.y, (double)p.x - q.x);

			double hypotenuse = sqrt((double)(p.y - q.y)*(p.y - q.y) + (double)(p.x - q.x)*(p.x - q.x));

			if (hypotenuse < 1.0)
				continue;

			// Here we lengthen the arrow by a factor of three.
			q.x = (int)(p.x - 3 * hypotenuse * cos(angle));
			q.y = (int)(p.y - 3 * hypotenuse * sin(angle));

			// Now we draw the main line of the arrow.
			line(frame, p, q, line_color, line_thickness);

			// Now draw the tips of the arrow. I do some scaling so that the
			// tips look proportional to the main line of the arrow.

			p.x = (int)(q.x + 9 * cos(angle + CV_PI / 4));
			p.y = (int)(q.y + 9 * sin(angle + CV_PI / 4));
			line(frame, p, q, line_color, line_thickness);

			p.x = (int)(q.x + 9 * cos(angle - CV_PI / 4));
			p.y = (int)(q.y + 9 * sin(angle - CV_PI / 4));
			line(frame, p, q, line_color, line_thickness);
		}
	}
}

inline bool isFlowCorrect(Point2f u)
{
	return !cvIsNaN(u.x) && !cvIsNaN(u.y) && fabs(u.x) < 1e9 && fabs(u.y) < 1e9;
}

static Vec3b computeColor(float fx, float fy)
{
	static bool first = true;

	// relative lengths of color transitions:
	// these are chosen based on perceptual similarity
	// (e.g. one can distinguish more shades between red and yellow
	//  than between yellow and green)
	const int RY = 15;
	const int YG = 6;
	const int GC = 4;
	const int CB = 11;
	const int BM = 13;
	const int MR = 6;
	const int NCOLS = RY + YG + GC + CB + BM + MR;
	static Vec3i colorWheel[NCOLS];

	if (first)
	{
		int k = 0;

		for (int i = 0; i < RY; ++i, ++k)
			colorWheel[k] = Vec3i(255, 255 * i / RY, 0);

		for (int i = 0; i < YG; ++i, ++k)
			colorWheel[k] = Vec3i(255 - 255 * i / YG, 255, 0);

		for (int i = 0; i < GC; ++i, ++k)
			colorWheel[k] = Vec3i(0, 255, 255 * i / GC);

		for (int i = 0; i < CB; ++i, ++k)
			colorWheel[k] = Vec3i(0, 255 - 255 * i / CB, 255);

		for (int i = 0; i < BM; ++i, ++k)
			colorWheel[k] = Vec3i(255 * i / BM, 0, 255);

		for (int i = 0; i < MR; ++i, ++k)
			colorWheel[k] = Vec3i(255, 0, 255 - 255 * i / MR);

		first = false;
	}

	const float rad = sqrt(fx * fx + fy * fy);
	const float a = atan2(-fy, -fx) / (float)CV_PI;

	const float fk = (a + 1.0f) / 2.0f * (NCOLS - 1);
	const int k0 = static_cast<int>(fk);
	const int k1 = (k0 + 1) % NCOLS;
	const float f = fk - k0;

	Vec3b pix;

	for (int b = 0; b < 3; b++)
	{
		const float col0 = colorWheel[k0][b] / 255.0f;
		const float col1 = colorWheel[k1][b] / 255.0f;

		float col = (1 - f) * col0 + f * col1;

		if (rad <= 1)
			col = 1 - rad * (1 - col); // increase saturation with radius
		else
			col *= .75; // out of range

		pix[2 - b] = static_cast<uchar>(255.0 * col);
	}

	return pix;
}

static void drawOpticalFlow(const Mat_<float>& flowx, const Mat_<float>& flowy, Mat& dst, float maxmotion = -1)
{
	dst.create(flowx.size(), CV_8UC3);
	dst.setTo(Scalar::all(0));

	// determine motion range:
	float maxrad = maxmotion;

	if (maxmotion <= 0)
	{
		maxrad = 1;
		for (int y = 0; y < flowx.rows; ++y)
		{
			for (int x = 0; x < flowx.cols; ++x)
			{
				Point2f u(flowx(y, x), flowy(y, x));

				if (!isFlowCorrect(u))
					continue;

				maxrad = max(maxrad, sqrt(u.x * u.x + u.y * u.y));
			}
		}
	}

	for (int y = 0; y < flowx.rows; ++y)
	{
		for (int x = 0; x < flowx.cols; ++x)
		{
			Point2f u(flowx(y, x), flowy(y, x));

			if (isFlowCorrect(u))
				dst.at<Vec3b>(y, x) = computeColor(u.x / maxrad, u.y / maxrad);
		}
	}
}

static void showFlow(const char* name, const GpuMat& d_flow)
{
	GpuMat planes[2];
	cuda::split(d_flow, planes);

	Mat flowx(planes[0]);
	Mat flowy(planes[1]);

	Mat out;
	drawOpticalFlow(flowx, flowy, out, 10);

	imshow(name, out);
}

template <typename T> inline T clamp(T x, T a, T b)
{
	return ((x) > (a) ? ((x) < (b) ? (x) : (b)) : (a));
}

template <typename T> inline T mapValue(T x, T a, T b, T c, T d)
{
	x = clamp(x, a, b);
	return c + (d - c) * (x - a) / (b - a);
}

int main(int argc, const char* argv[])
{
	const char* keys =
		"{ h             help   |        | print help message }"
		"{ l             left   | ../data/pic1.png       | specify left image }"
		"{ r             right  | ../data/pic2.png       | specify right image }"
		"{ flow                 | sparse | specify flow type [PyrLK] }"
		"{ gray                 |        | use grayscale sources [PyrLK Sparse] }"
		"{ win_size             | 21     | specify windows size [PyrLK] }"
		"{ max_level            | 3      | specify max level [PyrLK] }"
		"{ iters                | 30     | specify iterations count [PyrLK] }"
		"{ points               | 4000   | specify points count [GoodFeatureToTrack] }"
		"{ min_dist             | 0      | specify minimal distance between points [GoodFeatureToTrack] }";

	CommandLineParser cmd(argc, argv, keys);

	if (cmd.has("help") || !cmd.check())
	{
		cmd.printMessage();
		cmd.printErrors();
		return 0;
	}

	string fname0 = "./basketball1.png";//cmd.get<string>("left");
	string fname1 = "./basketball2.png";//cmd.get<string>("right");


	string flow_type = "dense";//cmd.get<string>("flow");
	bool is_sparse = true;
	if (flow_type == "sparse")
	{
		is_sparse = true;
	}
	else if (flow_type == "dense")
	{
		is_sparse = false;
	}
	else
	{
		cerr << "please specify 'sparse' or 'dense' as flow type" << endl;
		return -1;
	}

	bool useGray = 0;//cmd.has("gray");
	int winSize = 21;//cmd.get<int>("win_size");
	int maxLevel = 3;//cmd.get<int>("max_level");
	int iters = 30;//cmd.get<int>("iters");
	int points = 4000;//cmd.get<int>("points");
	double minDist = 0;//cmd.get<double>("min_dist");

	Mat frame0 = imread(fname0);
	Mat frame1 = imread(fname1);

	if (frame0.empty() || frame1.empty())
	{
		cout << "Can't load input images" << endl;
		return -1;
	}

	cout << "Image size : " << frame0.cols << " x " << frame0.rows << endl;
	cout << "Points count : " << points << endl;

	cout << endl;

	Mat frame0Gray;
	cv::cvtColor(frame0, frame0Gray, COLOR_BGR2GRAY);
	Mat frame1Gray;
	cv::cvtColor(frame1, frame1Gray, COLOR_BGR2GRAY);

	// goodFeaturesToTrack
	GpuMat d_frame0Gray(frame0Gray);
	GpuMat d_prevPts;

	Ptr<cuda::CornersDetector> detector = cuda::createGoodFeaturesToTrackDetector(d_frame0Gray.type(), points, 0.01, minDist);
	detector->detect(d_frame0Gray, d_prevPts);

	GpuMat d_frame0(frame0);
	GpuMat d_frame1(frame1);
	GpuMat d_frame1Gray(frame1Gray);
	GpuMat d_nextPts;
	GpuMat d_status;
	GpuMat d_flow(frame0.size(), CV_32FC2);

	if (is_sparse)
	{
		// Sparse
		Ptr<cuda::SparsePyrLKOpticalFlow> d_pyrLK_sparse = cuda::SparsePyrLKOpticalFlow::create(
			Size(winSize, winSize), maxLevel, iters);
		d_pyrLK_sparse->calc(useGray ? d_frame0Gray : d_frame0, useGray ? d_frame1Gray : d_frame1, d_prevPts, d_nextPts, d_status);

		// Draw arrows
		vector<Point2f> prevPts(d_prevPts.cols);
		download(d_prevPts, prevPts);

		vector<Point2f> nextPts(d_nextPts.cols);
		download(d_nextPts, nextPts);

		vector<uchar> status(d_status.cols);
		download(d_status, status);

		namedWindow("PyrLK [Sparse]", WINDOW_NORMAL);
		drawArrows(frame0, prevPts, nextPts, status, Scalar(255, 0, 0));
		imshow("PyrLK [Sparse]", frame0);
	}
	else
	{
		// Dense
		Ptr<cuda::DensePyrLKOpticalFlow> d_pyrLK_dense = cuda::DensePyrLKOpticalFlow::create(
			Size(winSize, winSize), maxLevel, iters);
		d_pyrLK_dense->calc(d_frame0Gray, d_frame1Gray, d_flow);

		// Draw flows
		namedWindow("PyrLK [Dense] Flow Field", WINDOW_NORMAL);
		showFlow("PyrLK [Dense] Flow Field", d_flow);
	}

	waitKey(0);

	return 0;
}

3.3 实现效果

4、常见的光流

(1)基于梯度方法

         基于梯度的方法又称为微分法,利用时变图像灰度(或者其滤波后的形式)进行时空微分(时空梯度函数)来计算像素的速度矢量。其中,最具代表的就是Horn-Schunck算法与Lucas-Kanade(LK)算法。Horn-Schunck算法在光流约束方程的基础上附加了全局平滑假设,假设整张图上光流是光滑,即物体运动矢量是平滑或者是缓慢变化的。后面很多人在此基础上进行改进,Nage采用有条件的平滑约束,即通过加权矩阵的控制对梯度进行不同平滑处理;Black和Anandan针对多运动提出分段平滑方法。

        优点:计算简单和可以达到不错的效果

(2)基于匹配的方法

         该方法目前主要有两种方法:基于特征和区域。其中基于特征方法主要是对目标的主要特征进行定位和跟踪,对大目标和特征明显具有很强的鲁棒性;基于区域方法主要是对类似的区域进行定位,然后通过相似区域的位移进行光流处理,主要在视频编码中使用;两者方法都是因为稀疏所以特征提取和匹配都很难,即使是亚像素的计算量就很大。

(3)基于能量的方法

          该方法又称为基于频率的方法,对图像进行时空滤波处理,即对时间和空间进行整合;

          缺点:降低了光流时间和空间分辨率;可靠性评价比较难;计算复杂度很大;

(4)基于相位的方法

         Fleet和Jepson使用相位的信息来进行光流计算处理;

         优点:对图像序列适用范围很宽,而且速度估计比较精确;

         缺点:模型设计合理,但是时间复杂度很高;对图像序列的时间混叠比较敏感;

(5)神经动力学方法

          该方法使用神经网络建立视觉运动感知的神经动力模型,让它对生物视觉系统功能与结构比较直接的模拟。

更多《计算机视觉与图形学》知识,可关注下方公众号:

calcOpticalFlowPyrLK()参数详解 官方文档 InputOutputArray 我emo了,什么是InputOutputArray,什么,叫TMD InputOutputArray??? 如果参数nextPts不为None,那么它将是InputArray(做为函数的初始值),并且在函数计算完毕后,nextPts的值做为OutputArray被更新。 官方文档,难以下咽 后来在B站看到一个视频 参数 含义 preImg 前一帧图像 nextImg 当前帧图像 prevPts 要跟踪,前一帧图像的,哪些特征点(向量) 阅读详情

相关推荐

TV-L1光流算法实战:从理论到Python实现(附完整代码)

本文深入解析了TV-L1光流算法的核心原理,即结合总变分正则化与L1范数数据保真项以实现鲁棒运动估计。文章提供了从能量函数推导到完整Python实现的详细教程,包含模块化代码、多尺度金字塔策略、关键参数调优指南以及与OpenCV实现的性能对比,旨在帮助开发者掌握这一经典算法并将其应用于实际计算机视觉项目。

n8m7b6v5c4的博客 964

cuda编程(7):实现LK稀疏光流算法--完整的cuda程序

一、原理: 二、算法的假设(其实这样的条件比较苛刻) 三、具体实现 main.cc #include <opencv2/opencv.hpp> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include "cudaLK.h" #include <stdio.h> #include <time.h> #include <om.

SeventhBlue 1444

TV-L1光流算法:从理论到实践的完整实现指南

本文提供了TV-L1光流算法从理论到实践的完整实现指南。详细解析了其结合L1范数数据项与总变分正则化的核心原理,以应对光照变化并保持运动边缘清晰。通过分步代码演示了算法实现,并给出了针对不同应用场景的调参实战建议,帮助开发者掌握这一鲁棒的传统光流方法。

weixin_28419039的博客 295

SLAM各种并行加速方法

点击上方“3D视觉工坊”,选择“星标”干货第一时间送达前言CPU并行加速CPU并行加速的本质就是通过硬件并发(hardware concurrency)的形式来实现。这种的操作方式是通过单...

3D视觉工坊 2609

无人机飞控之光流知识小结

要完成飞行器的定位,则必须要有位置的反馈数据。在户外,我们一般使用GPS作为位置传感器,然而,在室内,GPS无法使用,要完成定位功能,可以选用光流传感器。 本讲主要介绍如何通过下视摄像头估计飞行器的平移速度,即光流传感器。

cqszyy888888的博客 3047

在OpenCV里使用光流算法

前面学习了使用一些运动算法来检测物体的运行,其实还有别的方法来检测运动的物体。比如节日庆祝时进行烟花表演,可以看到一道道光的轨迹,这说明物体在运动,同时也看到光在运动。另外,当你坐在火车上,朝车窗外观看时,会发现近处的物体在飞速地离去,而远处的山又好像静止不动,为什么同样速度的物体会有速度上感觉的差异的呢?其实这是人眼的成像原理导致的。通过这样的原理,其实也可以反过来应用,视频里运动速度的快与慢,...

大坡3D软件开发 1411

光流算法从理论到实践专题2

1、资料搜索 总结:光流--LK光流--基于金字塔分层的LK光流--中值流 光流算法从理论到实践专题1 2、本人总结 我在“光流算法从理论到实践专题1”已经详细介绍了光流的基本概念以及最基本的LK光流算法。下面我们开始针对光流中的假设进行细化分析,其实在我给大家讲解光流概念的时候,里面就有一个存在一个不严谨的假设:假设上帧与当前帧的时间间隔比较短,但是当运动是快速运动的时候,...

CSS360的博客 702

光流 | 光流算法研究进展综述

Ix​uIy​vIt​0。

尘世冰封的专栏 812

Farneback 光流算法详解与 calcOpticalFlowFarneback 源码分析

光流基础概念真实的三维空间中,描述物体运动状态的物理概念是运动场。三维空间中的每一个点,经过某段时间的运动之后会到达一个新的位置,而这个位移过程可以用运动场来描述。而在计算机视觉的空间中,计算机所接收到的信号往往是二维图片信息。由于缺少了一个维度的信息,所以其不再适用以运动场描述。光流场(optical flow)就是用于描述三维空间中的运动物体表现到二维图像中,所反映出的像素点的运动向量场。 当描

IronYoung_不惧未来 3万+

Python实现Gunner Farneback稠密光流算法

光流技术是一种通过分析序列图像中像素点的运动信息来推断场景中物体运动的技术。其基本思想是,像素点在连续的图像帧中所代表的是同一个物理点,而这个点的运动导致了在图像平面上的位移变化。光流技术在计算机视觉和图像处理领域扮演了重要角色,它能够帮助系统理解图像序列中的动态场景变化,是实现许多高级视觉应用的关键技术。光流技术广泛应用于以下几个领域:视频监控:用于运动目标的检测与跟踪,以及行为分析和异常检测。自动驾驶:通过分析车辆和行人的运动,为自动驾驶汽车提供环境感知和避障的能力。

weixin_33173126的博客 797

多场景光流算法测试图像数据集

光流算法是一种用于估计图像序列中物体运动的技术,它通过对连续帧之间的像素点移动进行分析来推断出动态场景的运动信息。光流的计算基于这样的假设:在同一时间间隔内,相邻帧之间的像素点变化反映了物体表面的运动信息。在本章中,我们将简要介绍光流算法的历史、它在不同领域的应用、以及在计算机视觉中的重要性。接下来的章节会进一步深入探讨具体的算法原理、变种与优化策略,以及如何评估算法性能和选择适合的数据集。通过这些内容,读者将获得对光流算法全面而深刻的理解。随着时间的推移,光流算法领域不断出现新的变种和改进方法。

weixin_35414260的博客 1000

光流算法

https://blog.youkuaiyun.com/sgfmby1994/article/details/68489944 https://blog.youkuaiyun.com/zouxy09/article/details/8683859(解释的光流信息很形象) 光流(optic flow)是什么呢?名字很专业,感觉很陌生,但本质上,我们是最熟悉不过的了。因为这种视觉现象我们每天都在经历。从本质上说,光流...

邹小驴 4825

基于金字塔结构的LK光流算法MATLAB实现

虽然MATLAB提供impyramid函数,但在实际工程中,自定义金字塔构建函数可以带来更高的灵活性和性能优化空间。例如,我们可以自定义滤波器、降采样方式,甚至结合GPU加速。以下是一个优化版本的金字塔构建函数,支持自定义滤波器和缩放因子:% 根据filter_type选择滤波器elseerror('不支持的滤波器类型');end% 应用滤波% 降采样endend参数说明:指定滤波器类型,如'gaussian'或'average';

weixin_32311823的博客 972

光流算法从理论到实践专题3

1、资源搜索 光流法:Farneback 图像分析之光流之经典光流(七)--Brox算法(DeepFlow) 光流算法从理论到实践专题1 光流算法从理论到实践专题2 Farneback 光流算法详解与 calcOpticalFlowFarneback 源码分析一种高精度的TVL1光流算法涂志刚 啥是TV-L1 OpticalFlow 2、本人总结 ...

CSS360的博客 539

opencv_cuda.pdf

opencv cuda gpu加速官方教程: Using a cv::cuda::GpuMat with thrust 官方网站的pdf版本。源地址:https://docs.opencv.org/4.4.0/dd/d3d/tutorial_gpu_basics_similarity.html

用LightRAG+Ollama搭建本地知识库:从文档上传到智能问答的完整流程

本文详细介绍了如何利用LightRAG与Ollama构建企业级本地知识库,涵盖从文档预处理到智能问答的完整流程。通过实战案例展示,该方案能高效处理海量文档,实现语义搜索与知识图谱关联分析,显著提升信息检索效率。特别适合金融、医疗等领域的技术团队快速搭建智能知识管理系统。

weixin_42535461的博客 215

TV-L1光流算法实战:从理论到代码实现(附Python示例)

本文深入解析TV-L1光流算法的原理与实现,通过Python代码示例展示从图像金字塔构建到多尺度光流计算的全过程。该算法结合总变分正则化和L1范数数据项,在运动估计中表现出优秀的抗噪性能和边缘保持能力,适用于视频分析、自动驾驶等场景。文章还提供了参数调优指南和性能优化技巧,帮助开发者快速掌握这一计算机视觉核心技术。

mm9012的博客 735

VisionMaster图像源配置全攻略:从本地图片到相机SDK的保姆级教程

本文详细解析了VisionMaster图像源配置的三种主流方式:本地图片、相机直连和SDK集成。从像素格式选择到工业相机触发控制,再到SDK开发实战,提供全面的技术指导和避坑建议,帮助开发者高效实现图像处理应用的部署与优化。

yhn45678901的博客 781

【SLAM】光流 - LK光流 - 金字塔分层LK光流

在SLAM的视觉里程计中,比较常用的就是和。而直接法中,光流则是其中的重点内容,比如LSD-SLAM中就使用到了光流的方法。本文将会就光流的理论原理、公式推导进行详细的剖析,以帮助读者深刻地理解。光流是关于视域中的物体运动检测中的概念,它用来描述相对于观察者的运动所造成的观测目标、表面或边缘。简单来讲,光流描述了场景中物体运动在视觉中的变化。光流的概念,由Gibson在1950年提出,其通过相邻帧之间像素点的对应关系计算像素点的瞬时速度,从而描述物体信息。为了应用光流计算物体运动法,相邻帧需要满足两个假设:

Yngz_Miao的博客 1万+

uniapp - 获取设备麦克风权限+麦克风设备列表,用户可选择切换麦克风设备功能,选择麦克风采集设备后点击录制音频外部声音+可随时暂停/继续录音+记录音频录制时长(支持在线播放试听或下载MP3文件)

uniapp获取麦克风设备列表,uniapp录制声音,Uniapp获取麦克风权限,uniapp苹果ios安卓app获取麦克风设备列表,Uniapp H5获取浏览器麦克风并实现录音,Uniapp实现麦克风权限管理与音频录制全流程,uni-app实战案例:实现H5页面麦克风权限获取与录音功能,uni-app音频录制,uniapp麦克风授权窗口,uniapp获取iOS麦克风权限,实现前端录音并播放,把音频传给后端接口处理,获取麦克风并实现录音功能,前端获取/切换麦克风播放采集音频和采集音量大小,前端调用麦克风实现

🏆 前端领域优质创作者 2985

WorkBuddy最新更新特性(2026-07-21)

WorkBuddy最新更新特性

AI人人懂 583
上一篇: AI论文探讨室·A+·第10期 DeepStereo: Learning to Predict New Views from the World’s Imagery
下一篇: AI论文探讨室·A+·第11期 DeepMVS: Learning Multi-view Stereopsis
元宇宙MetaAI
博客等级 码龄10年 331粉丝 333原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值