光流法测距

光流法测距一.基于特征点的目标跟踪的一般方法基于特征点的跟踪算法大致可以分为两个步骤:1)探测当前帧的特征点;2)通过当前帧和下一帧灰度比较,估计当前帧特征点在下一帧的位置;3)过滤位置不变的特征点,余下的点就是目标了。二.光流法1.首先是假设条件:(1)亮度恒定,就是同一点随着时间的变化,其亮度不会发生改变。这是基本光流法的假定(所有光流法变种都必须满足),用于得到光流法基本方程;(2)小运动,这…

大家好,又见面了,我是你们的朋友全栈君。

光流法测距

 

.基于特征点的目标跟踪的一般方法

      基于特征点的跟踪算法大致可以分为两个步骤:

      1)探测当前帧的特征点;

      2)通过当前帧和下一帧灰度比较,估计当前帧特征点在下一帧的位置;

      3)过滤位置不变的特征点,余下的点就是目标了。

二.光流法

 1.首先是假设条件:

       (1)亮度恒定,就是同一点随着时间的变化,其亮度不会发生改变。这是基本光流法的假定(所有光流法变种都必须满足),用于得到光流法基本方程;

       (2)小运动,这个也必须满足,就是时间的变化不会引起位置的剧烈变化,这样灰度才能对位置求偏导(换句话说,小运动情况下我们才能用前后帧之间单位位置变化引起的灰度变化去近似灰度对位置的偏导数),这也是光流法不可或缺的假定;

       (3)空间一致,一个场景上邻近的点投影到图像上也是邻近点,且邻近点速度一致。这是Lucas-Kanade光流法特有的假定,因为光流法基本方程约束只有一个,而要求x,y方向的速度,有两个未知变量。我们假定特征点邻域内做相似运动,就可以连立n多个方程求取x,y方向的速度(n为特征点邻域总点数,包括该特征点)。

      2.方程求解

      多个方程求两个未知变量,又是线性方程,很容易就想到用最小二乘法,事实上opencv也是这么做的。其中,最小误差平方和为最优化指标

按照理论基础与数学方法的区别把它们分成四种:基于梯度(微分)的方法、基于匹配的方法、基于能量(频率)的方法、基于相位的方法神经动力学方法

1) 基于梯度的方法

基于梯度的方法又称为微分法,它是利用时变图像灰度(或其滤波形式)的时空微分(即时空梯度函数)来计算像素的速度矢量。

由于计算简单和较好的结果,该方法得到了广泛应用和研究。典型的代表是Horn-Schunck算法Lucas-Kanade(LK)算法

2) 基于匹配的方法

基于匹配的光流计算方法包括基于特征和区域的两种

基于特征的方法不断地对目标主要特征进行定位和跟踪,对目标大的运动和亮度变化具有鲁棒性。存在的问题是光流通常很稀疏,而且特征提取和精确匹配也十分困难。

基于区域的方法先对类似的区域进行定位,然后通过相似区域的位移计算光流。这种方法在视频编码中得到了广泛的应用。然而,它计算的光流仍不稠密。另外,这两种方法估计亚像素精度的光流也有困难,计算量很大。

3)基于能量的方法

4)基于相位的方法

5)神经动力学方法

3.稠密光流与稀疏光流

除了根据原理的不同来区分光流法外,还可以根据所形成的光流场中二维矢量的疏密程度将光流法分为稠密光流与稀疏光流两种。

稠密光流

稠密光流是一种针对图像或指定的某一片区域进行逐点匹配的图像配准方法,它计算图像

上所有的点的偏移量,从而形成一个稠密的光流场。通过这个稠密的光流场,可以进行像素级别的图像配准。

Horn-Schunck算法以及基于区域匹配的大多数光流法都属于稠密光流的范畴。

由于光流矢量稠密,所以其配准后的效果也明显优于稀疏光流配准的效果。但是其副作用也是明显的,由于要计算每个点的偏移量,其计算量也明显较大,时效性较差

稀疏光流

与稠密光流相反,稀疏光流并不对图像的每个像素点进行逐点计算。它通常需要指定一组点进行跟踪,这组点最好具有某种明显的特性,例如Harris角点等,那么跟踪就会相对稳定和可靠。稀疏跟踪的计算开销比稠密跟踪小得多。

上文提到的基于特征的匹配方法是典型的属于稀疏光流的算法

代码:

#include <opencv2/video/video.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>
#include <iostream>
#include <cstdio>
#include "stdafx.h"
#include <math.h>
using namespace std;
using namespace cv;

//opencv稀疏光流法-------物体运动跟踪:https://blog.csdn.net/qq_40238526/article/details/90183193


Mat frame, gray;//当前帧图片
Mat prev_frame, prev_gray;//前一帧图片
vector<Point2f> features;//保存特征点
vector<Point2f> inPoints;//初始化特征点
vector<Point2f> fpts[2];//保存当前帧和前一帧的特征点位置   //定义一个一维数组,数组元素的类型是Point2f(2维点, 含xy,浮点类型)
vector<uchar> status;//特征点跟踪标志位
vector<float> err;//误差和
double g_dPixelsPerMetric;
double pixel[550];
double deformation[550];
int d = 0;
int const maxCorners = 10;

void delectFeature(Mat &inFrame, Mat &ingray);//角点检测
void drawFeature(Mat &inFrame);//画点
void track();//运动
void drawLine();//画运动轨迹

int main()
{
	//VideoCapture capture(0);
	// 摄像头读取文件开关
	VideoCapture capture("D:/VSc++code/Vs2012code/光流法/光流法/视频2_2倍速.mp4");//调用本地视频
	if (capture.isOpened())
	{
		while (capture.read(frame)) {
			d++;
			cvtColor(frame, gray, COLOR_BGR2GRAY);
			//cout <<"帧数:"<< d <<"的特征点平均位移为"<<endl;

			if (fpts[0].size() < 1) { // 特征点损失,增加特征点  本来是40!!
				delectFeature(frame, gray);
				fpts[0].insert(fpts[0].end(), features.begin(), features.end());//在前一帧特征点向量的末尾加上检测到当前frame中的所有features
				inPoints.insert(inPoints.end(), features.begin(), features.end());//在初始特征点向量的末尾加上检测到当前frame中的所有features
			}
			else {
				putText(frame, "follow", Point(100, 100), 1, 2, Scalar(255, 0, 0), 2, 8);
			}
 
			if (prev_gray.empty()) {
				gray.copyTo(prev_gray);
			}
			track();// 光流跟踪
			
			//保存当前帧为前一帧
			gray.copyTo(prev_gray);
			frame.copyTo(prev_frame);
			imshow("1", frame);//imshow("input video", frame);??
			waitKey(27);
		}
	}
	waitKey();
	capture.release();
	return 0;
}
 
void delectFeature(Mat &inFrame, Mat &ingray) {//角点检测
	//double maxCorners = 500;//默认500
	double qualityLevel = 0.01;
	double minDistance = 10;// 小于这个就属于同一个特征点,默认10
	double blockSize = 3.0;
	double k = 0.04;
	goodFeaturesToTrack(ingray, features, maxCorners, qualityLevel, minDistance, Mat(), blockSize, false, k);//输入图像地址,输出角点vector,限定最大角点数,质量水平系数,最小距离,mask,参与角点运算的区域大小一般为3,true则使用Harris角点检测false则使用Shi Tomasi算法,使用Harris算法时使用最好使用默认值0.04
	putText(frame, "get point", Point(100, 100), 1, 2, Scalar(255, 0, 0), 2, 8);//图像,文字,文本框左下角,字体,尺寸,颜色,线宽,线型默认8
 
}
 
void drawFeature(Mat &inFrame) {
	for (size_t t = 0; t < fpts[0].size(); t++) {//size_t类似于int
		circle(inFrame, fpts[0][t], 2, Scalar(0, 255, 0), 2, 8);//绿点
	}
}
 

/*
	1.calcOpticalFlowPyrLK函数作用是对输入的特征点fpts[0],根据下一帧的图像对这些特征点判定是不是光流,
	检测结束后,status的每个下标会保存答案,再进行判断即可。
	2.initPoints集合用于存放初始化特征数据,每次的calcOpticalFlowPyrLK后都会重新更新一次,用status判断判断有没有新的特征是可以追踪的,或者用status判断哪些旧的特征可以不要了,
	3.fpts[1]集合用于存放当前帧的数据,	
*/
void track() {
	calcOpticalFlowPyrLK(prev_gray, gray, fpts[0], fpts[1], status, err);//金字塔 Lucas-Kanade 光流法。前一帧灰度图,当前帧灰度图,前一帧特征点,当前帧特征点;输出状态向量如果找到相应特征的流,则向量的每个元素设置为1,否则设置为0;error

	//int k = 0;
	特征点过滤
	//for (int i = 0; i < fpts[1].size(); i++) {
	//	double dist = abs(fpts[0][i].x- fpts[1][i].x) + abs(fpts[0][i].y - fpts[1][i].y);//asb()求绝对值
	//	if (dist > 2 && status[i]) {//&&表示逻辑与的意思,即当运算符两边的表达式的结果都为true时,整个运算结果才为true
	//		//删除损失的特征点
	//		inPoints[k] = inPoints[i];
	//		fpts[1][k++] = fpts[1][i];
	//	}
	//	//1.将用KLT算法找到的特征点集fpts[1]进行筛选,将没用的点去除,
	//	//没用的点包括距离太小没有变化和status的状态
	//	//2.将有用的点放进fpts[1]中

	//}
	//inPoints.resize(k);
	//fpts[1].resize(k);

	drawLine();
	swap(fpts[1], fpts[0]);//更新帧的特征点
}
 

void drawLine() {
	int flag = 0;
	double sum = 0;

	for (size_t t = 0; t < fpts[1].size(); t++) {
		line(frame, inPoints[t], fpts[1][t], Scalar(0, 0, 255), 1, 8, 0);//源图像指针,线段起点,终点,线颜色BGR,线粗细,线条类型默认8,小数点位数--红线
		circle(frame, fpts[1][t], 2, Scalar(0, 255, 0), 2, 8, 0);//源图像指针,圆心坐标,半径,圆颜色BGR,线条粗细,线条类型默认8,小数点位数--绿点

		pixel[flag] = sqrt(pow(inPoints[t].x - fpts[1][t].x , 2) + pow(inPoints[t].y - fpts[1][t].y, 2));
		g_dPixelsPerMetric = (double)142.5/25; //25mm.视频3里面占据是127.5个像素;视频3_2倍速.mp4占据378.6个像素;视频2_2倍速.mp4占据142.5个像素
		deformation[flag] = pixel[flag]/(double)g_dPixelsPerMetric;
		flag ++;
		sum = sum + deformation[flag];

		if(flag % maxCorners == 0){
			//cout << "软组织形变为"<< sum/100 << "mm(每100个特征点的平均值)"<<endl;
			cout << sum/ (maxCorners-2) <<endl;//每次打印把maxCorners(500)个特征点位移的全部打印出来
			sum = 0;
			flag = 0;
		}
	}
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/163539.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • Earfcn与Frequency转换「建议收藏」

    Earfcn与Frequency转换「建议收藏」EARFCN:E-UTRAAbsoluteRadioFrequencyChannelNumber.FDL=FDL_low+0.1(NDL–NOffs-DL)FUL=FUL_low+0.1(NUL–NOffs-UL)

  • mac上传文件到aws ec2 instance「建议收藏」

    mac上传文件到aws ec2 instance「建议收藏」mac上使用ec2实例确实比较简洁:使用scp上传文件到ec2instance上:1.在ec2instance上创建上传文件目录mkdir/home/upload2.调整目录权限,用于上传使用chmod0777/home/upload这里必须调整为777权限,否则上传用户无法写入3.在本地上传文件至ec2instance

  • 帧中继_帧中继交换机

    帧中继_帧中继交换机NBMA实验实验准备:1、 四台路由器,R2配置成帧中继交换机。2、 R1,R3,R4运行OSPF。实验配置:R1:interfaceLoopback0 ipad

  • 如何使用IntelliJ IDEA 配置Maven

    如何使用IntelliJ IDEA 配置MavenIDEA全称IntelliJIDEA,是java语言开发的集成环境,IntelliJ在业界被公认为最好的Java开发工具之一,IDEA是JetBrains公司的产品,现在有逐步取代老牌Java开发工具Eclipse的趋势.那本人也是从Eclipse转到IDEA.那刚转换过来时,确实很不适应,不过好在坚持使用了几天后,确实感觉IntelliJIDEA比Eclipse更加智能.  

  • centos7安装jdk1.8教程(安装包损坏无法安装怎么办)

    在虚拟机中创建两个目录mkdir-p/export/software软件包放置的目录mkdir-p/export/servers软件安装的目录进入/export/software目录,上传jdk的安装包:jdk-8u241-linux-x64.tar.gz解压压缩包到/export/servers目录下tar-zxvfjdk-8u241-linux-x64.tar.gz-C/export/servers查看解压后的目录,目录中有jdk1.

  • 实例讨论数据可视化的配色思路怎么写_配色分析案例

    实例讨论数据可视化的配色思路怎么写_配色分析案例引子有一数据集如下:数据解读:研究对象的目标层A分为B1,B2,B3三个准则层;B1层下有C1,C2,C3,C44个指标;B2层下只有C5一个指标;B3层有C6,C7,C83个指标。指标权重是该指标在所属准则层的权重;组合权重是该指标在目标层的权重。现在,要绘制上述数据的“组合权重”的饼图。如何给这个饼图配色呢?数据可视化配色的误区下图是群友绘制的图:他自己对结果不满意,他认为是颜色搭配太丑。我们来看看,他的配色问题出在哪:颜色太

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号