lsb_slam Tracking线程 SE3Tracking 欧式变换矩阵跟踪参考帧 加权高斯牛顿优化算法WLM 最小二乘优化 归一化方差的光度误差函数 偏导数雅克比矩阵J 线性方程组LDLT求解

github

算法推导

LSD-SLAM的Tracking是算法框架中三大部分之一。其主要实现函数为SlamSystem::trackFrame

这个函数的代码主要分为如下几个步骤实现:

  1. 构造新的图像帧:把当前图像构建为新的图像帧
  2. 更新参考帧:如果当前参考帧不是最近的关键帧,则更新参考帧
  3. 初始化位姿:把上一帧与参考帧的位姿当做初始位姿
  4. SE3求解:调用SE3Tracker计算当前帧和参考帧间的位姿变换
  5. 判断是否跟踪失败:根据跟踪的像素点个数多少以及跟踪质量来判断
  6. 关键帧筛选:通过计算得分确定是否构造新的关键帧

接下来主要介绍SE3求解

欧式变换矩阵 R,t 跟踪求解 SE3Tracker.cpp

   这里 是 SE3跟踪求解  欧式变换矩阵求解 

  * LSD-SLAM在位姿估计中采用了直接法,也就是通过最小化光度误差来求解两帧图像间的位姿变化。
  * 并且采用了LM算法迭代求解。
  * 误差函数 E(se3) = SUM((Iref(pi) - I(W(pi,Dref(pi),se3))^2)
  * (参考帧下的像素值-参考帧3d点变换到当前帧下的像素值)^2 
  * 也就是 所有匹配点 的光度误差和
  * LSD-SLAM在求解两帧图像间的SE3变换主要在SE3Tracker类中进行实现。

求解步骤:

  * 1. 首先在参考帧下根据深度信息计算出3d点云
  * 2. 其次,计算参考帧下的点变换到当前帧下的亚像素值,进而计算初始 光度匹配误差err
  * 3. 再次,利用误差对单个3d点的导数信息计算单个误差的可信程度,进行加权后方差归一化,
  *   得到误差置信度权重w,为了减少外点(outliers)对算法的影响
  * 4. 然后,利用误差函数对误差向量求导数,求得各个点的误差之间的协方差矩阵,
       也是雅克比矩阵J,计算系数A 和 b 
  * 5. 最后计算 变换矩阵 se3的 更新量 dse3 
  *    dse3 = -(J转置*J)逆矩阵*J转置*w*err  直接求逆矩阵计算量较大
  *      也可以写成:
  *           J转置*J * dse3 = -J转置*w*error
  *       紧凑形式:
  *             A * dse3 = b
  *       使用 LDLT分解 A = LDU=LDL转置=LDLT,求解线性方程组 A * x = b
  *       记录 u = LD, D为对角矩阵 L为下三角矩阵 L转置为上三角矩阵
  *             v  =  L转置
  *       将A分解为上面两个矩阵 相乘 A = u * v
  *       Ax = b就可以化为u(v*x) = u*y = b
  *       先求解 y
  *       得到   y = v*x
  *       再求解 x 即 dse3 ,是欧式变换矩阵se3的更新量
  * 
  * 6. 然后对 变换矩阵 左乘 se3指数映射 进行 更新 
  *   se3 指数映射到SE3 通过 李群乘法直接 对 变换矩阵 左乘更新 

该类中有四个函数比较重要,分别为

  *  1. SE3Tracker::trackFrame              主调函数
  *  2. SE3Tracker::calcResidualAndBuffers  被调用计算 
  *     初始误差 err 参考帧3d点变换到当前帧图像坐标系下的残差(灰度误差) 和 梯度(对应点像素梯度) 
  *  3. SE3Tracker::calcWeightsAndResidual 被调用计算    
  *     误差可信度权重w 并对误差方差归一化得到加权平均后的误差
  *  4. SE3Tracker::calculateWarpUpdate   被调用计算    
  *   误差关系矩阵 协方差矩阵 雅克比矩阵J 以及A=J转置*J b=-J转置*w*error 求接线性方程组

SE3Tracker::trackFrame

  *  图像金字塔迭代level-4到level-1
  
  *  Step1: 对参考帧当前层构造点云(reference->makePointCloud) 
  *        利用逆深度信息 和 相应的像素坐标 以及相机内参数得到 在参考帧坐标下的3d点    
  *        (X,Y,Z) = D  *  (u,v,1)*K逆 =1/(1/D)* (u*fx_inv + cx_inv, v*fy_inv + cy_inv, 1)
  
  *  Step2: 计算变换到当前帧的灰度匹配残差和 变换点的灰度梯度(calcResidualAndBuffers)
  *         计算参考帧3d点变换到当前帧图像坐标系下的残差(灰度误差) 和 梯度(对应点像素梯度) 
  *         1. 参考帧3d点 R,t变换到 当前相机坐标系下 
  *              Eigen::Matrix3f rotMat = referenceToFrame.rotationMatrix();// 旋转矩阵
  *             Eigen::Vector3f transVec = referenceToFrame.translation();// 平移向量
  *                Eigen::Vector3f Wxp = rotMat * (*refPoint) + transVec;// 欧式变换
  *         2. 再 投影到 当前相机 像素平面下
  *                float u_new = (Wxp[0]/Wxp[2])*fx_l + cx_l;// float 浮点数类型
  *                float v_new = (Wxp[1]/Wxp[2])*fy_l + cy_l;
  *         3. 根据浮点数坐标 四周的四个整数点坐标的梯度 使用位置差加权进行求和得到亚像素灰度值 和 亚像素 梯度值
  *                x=u_new;
  *                y=v_new;
  *                int ix = (int)x;// 取整数    左上方点
  *                int iy = (int)y;
  *                float dx = x - ix;// 差值
  *                float dy = y - iy;
  *                float dxdy = dx*dy;
  *                // map 像素梯度  指针
  *                const Eigen::Vector4f* bp = mat +ix+iy*width;// 左上方点 像素位置 梯度的 指针
  *                // 使用 左上方点 右上方点  右下方点 左下方点 的灰度梯度信息 
  *                // 以及 相应位置差值作为权重值 线性加权获取 亚像素梯度信息
  *                resInterp=dxdy * *(const Eigen::Vector3f*)(bp+1+width)    // 右下方点
  *                         + (dy-dxdy) * *(const Eigen::Vector3f*)(bp+width)// 左下方点
  *                         + (dx-dxdy) * *(const Eigen::Vector3f*)(bp+1)    // 右上方点
  *                         + (1-dx-dy+dxdy) * *(const Eigen::Vector3f*)(bp);// 左上方点
  * 
  *               需要注意的是,在给梯度变量赋值的时候,这里乘了焦距
  *            这里求得 亚像素梯度之后 又乘上了 相机内参数,
  *               因为在求 误差偏导数时需要 需要用到 dx*fx  dy*fy  
  *               *(buf_warped_dx+idx) = fx_l * resInterp[0];// 当前帧匹配点亚像素 梯度 gx = dx*fx  
  *               *(buf_warped_dy+idx) = fy_l * resInterp[1];// 匹配点亚像素 梯度               gy = dy*fy
  *               *(buf_warped_residual+idx) = residual;// 对应 匹配点 像素误差
  *               这里的   dx = resInterp[0],  dy =  resInterp[0] 亚像素梯度
  * 
  *                (buf_d+idx) = 1.0f / (*refPoint)[2];  // 参考帧 Z轴 倒数  = 参考帧逆深度
  *               *(buf_idepthVar+idx) = (*refColVar)[1];// 参考帧逆深度方差
  *      4.  记录变换 
  *        a. 对参考帧灰度 进行一次仿射变换后 和 当前帧亚像素灰度做差得到 残差(灰度误差)
  *                  现在求得的残差只是纯粹的光度误差
  *            float c1 = affineEstimation_a * (*refColVar)[0] + affineEstimation_b;// 参考帧 灰度[0]  仿射变换后
  *                  float c2 = resInterp[2];//  当前帧 亚像素 第三维度是图像 灰度值
  *                  float residual = c1 - c2;// 匹配点对 的灰度误差值  
  *                                      
  *                 疑问1:代码中对参考帧的灰度做了一个仿射变换的处理,这里的原理是什么?
  *                     在SVO代码中的确有考虑到两帧见位姿相差过大,
  *                     因此通过在空间上的仿射变换之后再求灰度的操作。
  *                     但是在这里的代码中没有看出具体原理。
  *             b. 两帧下 3d点坐标的 Z轴深度值 的变化比例
  *                float depthChange = (*refPoint)[2] / Wxp[2];
  *                usageCount += depthChange < 1 ? depthChange : 1;//记录深度改变的比例  
  *        c. 匹配点总数
  *                buf_warped_size = idx;// 匹配点数量=包括好的匹配点和不好的匹配点数量
  
  *  Step3: 计算 方差归一化加权残差和 使用误差方差 对 误差归一化 后求和 (calcWeightsAndResidual)
  *          计算误差权重,归一化方差以及Huber-weight,最终把这个系数存在数组 buf_weight_p 中
  *      每个像素点都有一个光度匹配误差,而每个点匹配的可信度根据其偏导数可以求得,
  *      加权每一个误差 然后求所有点 光度匹配误差的均值
  *       误差导数
             drpdd = dr = 
              -(dx*fx*(tx*z' - tz*x')/(z'^2*d)  + dy*fy*(ty*z' - tz*y')/(z'^2*d))
  *         = -(gx*(tx*z' - tz*x')/(z'^2*d)  + gy*(ty*z' - tz*y')/(z'^2*d))
  *               dx, dy 为参考帧3d点映射到当前帧图像平面后的梯度
  *              fx,  fy 相机内参数
  *              tx,ty,tz 为参考帧到当前帧的平移变换(忽略旋转变换) 
                  t  = translation()
  *              x',y',z'  为参考帧3d点变换到当前帧坐标系下的3d点     
                  x' = Wxp[0] , y' = Wxp[1] , z' = Wxp[2] 
  *              d = d = *(buf_d) 为参考帧3d点在参考帧下的逆深度
  *               gx = *(buf_warped_dx)
  *               gy = *(buf_warped_dy)
  *              方差平方   float s = settings.var_weight  *  *(buf_idepthVar+i);// 参考帧 逆深度方差  平方
  *             误差权重为加权方差平方的倒数 
                    float w_p = 1.0f / ((cameraPixelNoise2) + s * drpdd * drpdd);// 误差权重 方差倒数
  *              初步误差   
                          rp =*( buf_warped_residual) ; //初步误差
  *              加权的误差 
                          float weighted_rp = fabs(rp*sqrtf(w_p));// |r|加权的误差
  *              Huber-weight权重  
  *              float wh = fabs(weighted_rp < (settings.huber_d/2) ? 1 : (settings.huber_d/2) / weighted_rp);
  *              记录 *(buf_weight_p+i) = wh * w_p;    
                          // 乘上 Huber-weight 权重 得到最终误差权重避免外点的影响
  *            求和  sumRes += wh * w_p * rp*rp; // 加权误差和
  *              取均值 return sumRes / buf_warped_size;// 返回加权误差均值         
                 lastErr =  sumRes / buf_warped_size;
              
  *  Step4: 计算雅克比向量以及A和b(calculateWarpUpdate)
  *        J转置*J * dse3 = -J转置*w*lastErr
  *         对于参考帧中的一个3D点pi位置处的残差求雅可比,这里需要使用链式求导法则 
  *                       Ji =      1/z'*dx*fx + 0* dy *fy
  *                                  0* dx *fx +  1/z' *dy *fy
  *                         -1 *    -x'/z'^2 *dx*fx  - y'/z'^2 *dy*fy 
  *                                 -x'*y'/z'^2 *dx*fx - (1+y'^2/z'^2) *dy*fy
  *                                 (1+x'^2/z'^2)*dx*fx + x'*y'/z'^2*dy*fy
  *                                - y'/z' *dx*fx + x'/z' * dy*fy
  *
  *                       A = J *J转置*(*(buf_weight_p+i))
  *                       b = -J转置*(*(buf_weight_p+i))*lastErr
  * 
  *  Step5: 计算得到收敛的delta,并且更新SE3(dse3 = A.ldlt().solve(b))
  *      for(int i=0;i<6;i++) A(i,i) *= 1+LM_lambda;// (A+λI)   列文伯格马尔夸克更新算法 LM 调整量 
  *          Vector6 inc = A.ldlt().solve(b);// LDLT矩阵分解求解 dse3 李代数更新量
             Sophus::SE3f new_referenceToFrame = Sophus::SE3f::exp((inc)) * referenceToFrame; 
  *     
  *  重复Step2-Step5直到收敛或者达到最大迭代次数
  * 
  *计算下一层金字塔
#include "SE3Tracker.h"
#include <opencv2/highgui/highgui.hpp>
#include "DataStructures/Frame.h"
#include "Tracking/TrackingReference.h"
#include "util/globalFuncs.h"
#include "IOWrapper/ImageDisplay.h"
#include "Tracking/least_squares.h"

#include <Eigen/Core>

namespace lsd_slam
{

// 指令集优化 X86 SSE指令集优化  ARM NENO指令集优化
// 通过callOptimized这个宏调用 calcResidualAndBuffers 这个函数进行优化操作
#if defined(ENABLE_NEON)
	#define callOptimized(function, arguments) function##NEON arguments
#else
	#if defined(ENABLE_SSE)
		#define callOptimized(function, arguments) (USESSE ? function##SSE arguments : function arguments)
	#else
		#define callOptimized(function, arguments) function arguments
	#endif
#endif

// 使用图像存储 参数和 相机内参数初始化
SE3Tracker::SE3Tracker(int w, int h, Eigen::Matrix3f K)
{
  // 图像尺寸
	width = w;
	height = h;
// 相机内参数
	this->K = K;
	fx = K(0,0);
	fy = K(1,1);
	cx = K(0,2);
	cy = K(1,2);
// 系统全局配置文件 稠密深度跟踪器设置类
	// 定义了一些Tracking相关的设定,比如最大迭代次数,
	// 认为收敛的阈值(百分比形式,有些数据认为98%收敛,有些是99%),
	// 还有huber距离所需参数等
	settings = DenseDepthTrackerSettings();
	//settings.maxItsPerLvl[0] = 2;
// 相机内参数 逆  倒数
	KInv = K.inverse();
	fxi = KInv(0,0);
	fyi = KInv(1,1);
	cxi = KInv(0,2);
	cyi = KInv(1,2);

// 分配内存
	buf_warped_residual = (float*)Eigen::internal::aligned_malloc(w*h*sizeof(float));
	// 梯度
	buf_warped_dx = (float*)Eigen::internal::aligned_malloc(w*h*sizeof(float));
	buf_warped_dy = (float*)Eigen::internal::aligned_malloc(w*h*sizeof(float));
	// 坐标
	buf_warped_x = (float*)Eigen::internal::aligned_malloc(w*h*sizeof(float));
	buf_warped_y = (float*)Eigen::internal::aligned_malloc(w*h*sizeof(float));
	buf_warped_z = (float*)Eigen::internal::aligned_malloc(w*h*sizeof(float));
       // 逆深度
	buf_d = (float*)Eigen::internal::aligned_malloc(w*h*sizeof(float));
	// 逆深度方差
	buf_idepthVar = (float*)Eigen::internal::aligned_malloc(w*h*sizeof(float));
	// 计算权重
	buf_weight_p = (float*)Eigen::internal::aligned_malloc(w*h*sizeof(float));

	buf_warped_size = 0;
	
// 可视化Tracking的迭代过程 需要的 图像
	debugImageWeights = cv::Mat(height,width,CV_8UC3);
	debugImageResiduals = cv::Mat(height,width,CV_8UC3);// 残差
	debugImageSecondFrame = cv::Mat(height,width,CV_8UC3);
	debugImageOldImageWarped = cv::Mat(height,width,CV_8UC3);
	debugImageOldImageSource = cv::Mat(height,width,CV_8UC3);
	
	lastResidual = 0;
	iterationNumber = 0;
	pointUsage = 0;
	lastGoodCount = lastBadCount = 0;// 计数器

	diverged = false;
}
// 类析构函数
SE3Tracker::~SE3Tracker()
{
  // cv::Mat 对象 自带的 释放存储空间的 方法
	debugImageResiduals.release();
	debugImageWeights.release();
	debugImageSecondFrame.release();
	debugImageOldImageSource.release();
	debugImageOldImageWarped.release();
// Eigen对象的 内存释放 方法
	Eigen::internal::aligned_free((void*)buf_warped_residual);
	Eigen::internal::aligned_free((void*)buf_warped_dx);// 梯度
	Eigen::internal::aligned_free((void*)buf_warped_dy);
	Eigen::internal::aligned_free((void*)buf_warped_x);// 坐标
	Eigen::internal::aligned_free((void*)buf_warped_y);
	Eigen::internal::aligned_free((void*)buf_warped_z);

	Eigen::internal::aligned_free((void*)buf_d);// 逆深度
	Eigen::internal::aligned_free((void*)buf_idepthVar);// 逆深度方差
	Eigen::internal::aligned_free((void*)buf_weight_p);// 权重
}

// tracks a frame.
// first_frame has depth, second_frame DOES NOT have depth.
// 第一帧 参考帧 3d点 按照 欧式变换矩阵 以及当前帧的第4层相机内参数 变换到图像平面下
// 看映射后的坐标是否合理,以及看两个坐标系下点坐标的 Z轴深度信息的变化
float SE3Tracker::checkPermaRefOverlap(
		Frame* reference,
		SE3 referenceToFrameOrg)
{
	Sophus::SE3f referenceToFrame = referenceToFrameOrg.cast<float>();// 欧式变换矩阵
	// 上锁
	boost::unique_lock<boost::mutex> lock2 = boost::unique_lock<boost::mutex>(reference->permaRef_mutex);

	int w2 = reference->width(QUICK_KF_CHECK_LVL)-1;// 第4层
	int h2 = reference->height(QUICK_KF_CHECK_LVL)-1;
	Eigen::Matrix3f KLvl = reference->K(QUICK_KF_CHECK_LVL);
	float fx_l = KLvl(0,0);// 第四层 相机内参数 
	float fy_l = KLvl(1,1);
	float cx_l = KLvl(0,2);
	float cy_l = KLvl(1,2);

	Eigen::Matrix3f rotMat = referenceToFrame.rotationMatrix();// 旋转矩阵
	Eigen::Vector3f transVec = referenceToFrame.translation();// 平移向量
	
	const Eigen::Vector3f* refPoint = reference->permaRef_posData;// 参考帧3d点   存储的起始指针
	const Eigen::Vector3f* refPoint_max = reference->permaRef_posData + reference->permaRefNumPts;// 最大位置


	float usageCount = 0;
	for(;refPoint<refPoint_max; refPoint++)
	{
		Eigen::Vector3f Wxp = rotMat * (*refPoint) + transVec;// 变换到当前图像坐标系下 的坐标
		float u_new = (Wxp[0]/Wxp[2])*fx_l + cx_l;// 对应图像上的像素坐标
		float v_new = (Wxp[1]/Wxp[2])*fy_l + cy_l;
		if((u_new > 0 && v_new > 0 && u_new < w2 && v_new < h2))//3d点 映射到 图像平面上 2d点坐标需要在合理范围内
		{
			float depthChange = (*refPoint)[2] / Wxp[2];// 两个坐标系下 逆深度的变换比例
			usageCount += depthChange < 1 ? depthChange : 1;// 按照最大值1  记录总和
		}
	}
	pointUsage = usageCount / (float)reference->permaRefNumPts;//  变换 均值
	return pointUsage;
}


// tracks a frame.
// first_frame has depth, second_frame DOES NOT have depth.
SE3 SE3Tracker::trackFrameOnPermaref(
		Frame* reference,
		Frame* frame,
		SE3 referenceToFrameOrg)
{

	Sophus::SE3f referenceToFrame = referenceToFrameOrg.cast<float>();

	boost::shared_lock<boost::shared_mutex> lock = frame->getActiveLock();
	boost::unique_lock<boost::mutex> lock2 = boost::unique_lock<boost::mutex>(reference->permaRef_mutex);

	affineEstimation_a = 1; affineEstimation_b = 0;

	NormalEquationsLeastSquares ls;
	diverged = false;
	trackingWasGood = true;

	callOptimized(calcResidualAndBuffers, (reference->permaRef_posData, reference->permaRef_colorAndVarData, 0, reference->permaRefNumPts, frame, referenceToFrame, QUICK_KF_CHECK_LVL, false));
	if(buf_warped_size < MIN_GOODPERALL_PIXEL_ABSMIN * (width>>QUICK_KF_CHECK_LVL)*(height>>QUICK_KF_CHECK_LVL))
	{
		diverged = true;
		trackingWasGood = false;
		return SE3();
	}
	if(useAffineLightningEstimation)
	{
		affineEstimation_a = affineEstimation_a_lastIt;
		affineEstimation_b = affineEstimation_b_lastIt;
	}
	float lastErr = callOptimized(calcWeightsAndResidual,(referenceToFrame));

	float LM_lambda = settings.lambdaInitialTestTrack;

	for(int iteration=0; iteration < settings.maxItsTestTrack; iteration++)
	{
		callOptimized(calculateWarpUpdate,(ls));


		int incTry=0;
		while(true)
		{
			// solve LS system with current lambda
			Vector6 b = -ls.b;
			Matrix6x6 A = ls.A;
			for(int i=0;i<6;i++) A(i,i) *= 1+LM_lambda;
			Vector6 inc = A.ldlt().solve(b);
			incTry++;

			// apply increment. pretty sure this way round is correct, but hard to test.
			Sophus::SE3f new_referenceToFrame = Sophus::SE3f::exp((inc)) * referenceToFrame;

			// re-evaluate residual
			callOptimized(calcResidualAndBuffers, (reference->permaRef_posData, reference->permaRef_colorAndVarData, 0, reference->permaRefNumPts, frame, new_referenceToFrame, QUICK_KF_CHECK_LVL, false));
			if(buf_warped_size < MIN_GOODPERALL_PIXEL_ABSMIN * (width>>QUICK_KF_CHECK_LVL)*(height>>QUICK_KF_CHECK_LVL))
			{
				diverged = true;
				trackingWasGood = false;
				return SE3();
			}
			float error = callOptimized(calcWeightsAndResidual,(new_referenceToFrame));


			// accept inc?
			if(error < lastErr)
			{
				// accept inc
				referenceToFrame = new_referenceToFrame;
				if(useAffineLightningEstimation)
				{
					affineEstimation_a = affineEstimation_a_lastIt;
					affineEstimation_b = affineEstimation_b_lastIt;
				}
				// converged?
				if(error / lastErr > settings.convergenceEpsTestTrack)
					iteration = settings.maxItsTestTrack;


				lastErr = error;


				if(LM_lambda <= 0.2)
					LM_lambda = 0;
				else
					LM_lambda *= settings.lambdaSuccessFac;

				break;
			}
			else
			{
				if(!(inc.dot(inc) > settings.stepSizeMinTestTrack))
				{
					iteration = settings.maxItsTestTrack;
					break;
				}

				if(LM_lambda == 0)
					LM_lambda = 0.2;
				else
					LM_lambda *= std::pow(settings.lambdaFailFac, incTry);
			}
		}
	}

	lastResidual = lastErr;

	trackingWasGood = !diverged
			&& lastGoodCount / (frame->width(QUICK_KF_CHECK_LVL)*frame->height(QUICK_KF_CHECK_LVL)) > MIN_GOODPERALL_PIXEL
			&& lastGoodCount / (lastGoodCount + lastBadCount) > MIN_GOODPERGOODBAD_PIXEL;

	return toSophus(referenceToFrame);
}

////////////////////////////////////////////////////////////////////////////////////////
////////////////// 最重要的跟踪匹配 函数////////////////////////////////////////////
// SE3Tracker::trackFrame函数的主体是一个for循环,从图像金字塔的高层level-4开始遍历直到底层level-1。
// 在循环内实现加权高斯牛顿优化算法(Weighted Gauss-Newton Optimization),
// 其实就是增加了鲁棒函数的高斯牛顿。
// tracks a frame.
// first_frame has depth, second_frame DOES NOT have depth.
SE3 SE3Tracker::trackFrame(
		TrackingReference* reference,//  参考帧,第一帧 含有 逆深度信息
		Frame* frame,//  当前帧 第二帧  没有深度 需要匹配后 获取 
		const SE3& frameToReference_initialEstimate)// 初始变换矩阵  fram 到参考帧 的 变换矩阵
// 对于初始位姿,LSD-SLAM中使用了上一帧和参考帧间的位姿作为当前位姿估计的初值
{
// 内存上锁
	boost::shared_lock<boost::shared_mutex> lock = frame->getActiveLock();
	diverged = false;
	trackingWasGood = true;
	affineEstimation_a = 1; affineEstimation_b = 0;// 仿射变换参数
// 保存 跟踪 信息
	if(saveAllTrackingStages)
	{
		saveAllTrackingStages = false;
		saveAllTrackingStagesInternal = true;
	}
// 初始化 跟踪迭代信息	
	if (plotTrackingIterationInfo)
	{
		const float* frameImage = frame->image();// 当前帧 第0层 图像
		for (int row = 0; row < height; ++ row)// 每行
			for (int col = 0; col < width; ++ col)// 每列
			  // 按照初始图像像素灰度值 设置 颜色
				setPixelInCvMat(&debugImageSecondFrame,getGrayCvPixel(frameImage[col+row*width]), col, row, 1);
	}

	// ============ track frame ============
// 首先将初始估计记录下来(记录了参考帧到当前帧的 欧式变换矩阵)
	// 参考帧 到 当前帧  fram 的 变换矩阵
	Sophus::SE3f referenceToFrame = frameToReference_initialEstimate.inverse().cast<float>();
	NormalEquationsLeastSquares ls;// 然后定义一个6自由度矩阵的误差判别计算对象ls
	
	int numCalcResidualCalls[PYRAMID_LEVELS];// cell数量  5层 金字塔
	int numCalcWarpUpdateCalls[PYRAMID_LEVELS];// 

	float last_residual = 0;// 最终的残差

// 函数的主体是一个for循环
	for(int lvl=SE3TRACKING_MAX_LEVEL-1;lvl >= SE3TRACKING_MIN_LEVEL;lvl--)// 在每一层金字塔上进行跟踪
	{
           // 从最高层的 金字塔图像(尺寸最小) 开始 向下计算
	     // 从图像金字塔的高层level-4开始遍历直到底层level-1
		numCalcResidualCalls[lvl] = 0;
		numCalcWarpUpdateCalls[lvl] = 0;
// 【1】 有参考帧逆深度信息和对应像素点坐标 计算 在参考帧坐标系下的3d 点
		reference->makePointCloud(lvl);
//【2】计算参考帧3d点变换到当前帧图像坐标系下的残差(灰度误差) 和 梯度(对应点像素梯度) 
		// 计算匹配 点对 像素误差  在这个函数中,把 buf_warped 相关的参数全部更新,并且更新了上次的相似变换参数等
		callOptimized(calcResidualAndBuffers, (reference->posData[lvl], reference->colorAndVarData[lvl], SE3TRACKING_MIN_LEVEL == lvl ? reference->pointPosInXYGrid[lvl] : 0, reference->numData[lvl], frame, referenceToFrame, lvl, (plotTracking && lvl == SE3TRACKING_MIN_LEVEL)));
		// buf_warped_size 记录了 匹配点数量 包括好的匹配点 和 不好的匹配点 数量 (只要投影后在图像范围内就可以)
		if(buf_warped_size < MIN_GOODPERALL_PIXEL_ABSMIN * (width>>lvl)*(height>>lvl))
		{// 如果匹配点数量过少,已经少于了这一层图像像素数量的1%),那么我们认为差别太大,Tracking失败,返回一个空的SE3
			diverged = true;
			trackingWasGood = false;// Tracking失败
			return SE3();// 返回一个空的SE3
		}
        // 使用 仿射变换参数
        // 如果使用了,那么把通过calcResidualAndBuffers函数更新的affineEstimation_a_lastIt以及affineEstimation_b_lastIt,赋值给仿射变换系数
		if(useAffineLightningEstimation)
		{
			affineEstimation_a = affineEstimation_a_lastIt;
			affineEstimation_b = affineEstimation_b_lastIt;
		}
// 【3】使用误差协方差(误差求导) 对方差归一化 后求和 调用calcWeightsAndResidual得到误差,并记录调用次数
              // 以及误差权重矩阵  避免外点的影响 
		float lastErr = callOptimized(calcWeightsAndResidual,(referenceToFrame));

		numCalcResidualCalls[lvl]++;

		float LM_lambda = settings.lambdaInitial[lvl];// λ  LM优化的 调整量
// 【4】每一层进行 LM优化 (A+λI)x=b 在最大迭代次数范围内进行优化 
		for(int iteration=0; iteration < settings.maxItsPerLvl[lvl]; iteration++)
		{
                 // 计算 加权平均误差 以及误差权重
			callOptimized(calculateWarpUpdate,(ls));

			numCalcWarpUpdateCalls[lvl]++;

			iterationNumber = iteration;// 迭代次数

			int incTry=0;
			while(true)
			{
				// solve LS system with current lambda
				Vector6 b = -ls.b;
				Matrix6x6 A = ls.A;
			// 列文伯格马尔夸克优化算法更新
				for(int i=0;i<6;i++) A(i,i) *= 1+LM_lambda;// (A+λI)  LM 调整量 
				Vector6 inc = A.ldlt().solve(b);// LDLT矩阵分解求解 dse3 李代数更新量
				incTry++;
                         // 对 变换矩阵 左乘 se3指数映射 进行 更新 
				// apply increment. pretty sure this way round is correct, but hard to test.
				Sophus::SE3f new_referenceToFrame = Sophus::SE3f::exp((inc)) * referenceToFrame;
				//Sophus::SE3f new_referenceToFrame = referenceToFrame * Sophus::SE3f::exp((inc));


				// re-evaluate residual
				callOptimized(calcResidualAndBuffers, (reference->posData[lvl], reference->colorAndVarData[lvl], SE3TRACKING_MIN_LEVEL == lvl ? reference->pointPosInXYGrid[lvl] : 0, reference->numData[lvl], frame, new_referenceToFrame, lvl, (plotTracking && lvl == SE3TRACKING_MIN_LEVEL)));
				if(buf_warped_size < MIN_GOODPERALL_PIXEL_ABSMIN* (width>>lvl)*(height>>lvl))
				{// 匹配点数量记录 包括好的匹配点 和 不好的匹配点 数量
				  // 匹配点数量少于阈值  优化失败
					diverged = true;
					trackingWasGood = false;// 优化失败
					return SE3();// 返回空
				}
                               // 新变换下的误差
				float error = callOptimized(calcWeightsAndResidual,(new_referenceToFrame));
				numCalcResidualCalls[lvl]++;
				// accept inc?
		// 误差变小了
				if(error < lastErr)// 误差变小了
				{
					// accept inc
				       // 更新 新求解的 变换矩阵 
					referenceToFrame = new_referenceToFrame;
					if(useAffineLightningEstimation)
					{// 更新 仿射变换系数
						affineEstimation_a = affineEstimation_a_lastIt;
						affineEstimation_b = affineEstimation_b_lastIt;
					}
					// 打印调试信息
					if(enablePrintDebugInfo && printTrackingIterationInfo)
					{
						// debug output
						printf("(%d-%d): ACCEPTED increment of %f with lambda %.1f, residual: %f -> %f\n",
								lvl,iteration, sqrt(inc.dot(inc)), LM_lambda, lastErr, error);

						printf("         p=%.4f %.4f %.4f %.4f %.4f %.4f\n",
								referenceToFrame.log()[0],referenceToFrame.log()[1],referenceToFrame.log()[2],
								referenceToFrame.log()[3],referenceToFrame.log()[4],referenceToFrame.log()[5]);
					}

					// converged?
					if(error / lastErr > settings.convergenceEps[lvl])
					{
						if(enablePrintDebugInfo && printTrackingIterationInfo)
						{
							printf("(%d-%d): FINISHED pyramid level (last residual reduction too small).\n",
									lvl,iteration);
						}
						iteration = settings.maxItsPerLvl[lvl];
					}

					last_residual = lastErr = error;


					if(LM_lambda <= 0.2)
						LM_lambda = 0;
					else
						LM_lambda *= settings.lambdaSuccessFac;

					break;
				}
		// 误差反而没有减少
				else
				{
					if(enablePrintDebugInfo && printTrackingIterationInfo)
					{
						printf("(%d-%d): REJECTED increment of %f with lambda %.1f, (residual: %f -> %f)\n",
								lvl,iteration, sqrt(inc.dot(inc)), LM_lambda, lastErr, error);
					}

					if(!(inc.dot(inc) > settings.stepSizeMin[lvl]))
					{
						if(enablePrintDebugInfo && printTrackingIterationInfo)
						{
							printf("(%d-%d): FINISHED pyramid level (stepsize too small).\n",
									lvl,iteration);
						}
						iteration = settings.maxItsPerLvl[lvl];
						break;
					}
                                        // 调整 LM 参数
					if(LM_lambda == 0)
						LM_lambda = 0.2;
					else
						LM_lambda *= std::pow(settings.lambdaFailFac, incTry);
				}
			}
		}
	}

	if(plotTracking)
		Util::displayImage("TrackingResidual", debugImageResiduals, false);


	if(enablePrintDebugInfo && printTrackingIterationInfo)
	{
		printf("Tracking: ");
			for(int lvl=PYRAMID_LEVELS-1;lvl >= 0;lvl--)
			{
				printf("lvl %d: %d (%d); ",
					lvl,
					numCalcResidualCalls[lvl],
					numCalcWarpUpdateCalls[lvl]);
			}

		printf("\n");
	}

	saveAllTrackingStagesInternal = false;

	lastResidual = last_residual;

	trackingWasGood = !diverged
			&& lastGoodCount / (frame->width(SE3TRACKING_MIN_LEVEL)*frame->height(SE3TRACKING_MIN_LEVEL)) > MIN_GOODPERALL_PIXEL
			&& lastGoodCount / (lastGoodCount + lastBadCount) > MIN_GOODPERGOODBAD_PIXEL;

	if(trackingWasGood)
		reference->keyframe->numFramesTrackedOnThis++;

	frame->initialTrackedResidual = lastResidual / pointUsage;
	frame->pose->thisToParent_raw = sim3FromSE3(toSophus(referenceToFrame.inverse()),1);
	frame->pose->trackingParent = reference->keyframe->pose;
	return toSophus(referenceToFrame.inverse());
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// 每个像素点都有一个光度匹配误差,而每个点匹配的可信度根据其偏导数可以求得,
// 加权每一个误差 然后求所有点 光度匹配误差的均值
// 再者 计算误差的权重,在优化更新函数中作为权重
	
// 使用 误差方差对方差归一化 后求和
// 计算权重  和 像素匹配误差
// 该函数的功能是计算归一化方差的光度误差系数,也就是计算公式(14),
// 并且乘以了Huber-weight,最终把这个系数存在数组buf_weight_p中。
// 可能是考虑参考帧到当前帧的位姿变换比较小,
// 所以作者只考虑了位移t而忽略旋转R。
// 这样使得式子(14)中的偏导的形式简单了很多。
// 这里主要求 误差函数的导数 即得到 误差的协方差矩阵
#if defined(ENABLE_SSE)
float SE3Tracker::calcWeightsAndResidualSSE(
		const Sophus::SE3f& referenceToFrame)
{
	const __m128 txs = _mm_set1_ps((float)(referenceToFrame.translation()[0]));
	const __m128 tys = _mm_set1_ps((float)(referenceToFrame.translation()[1]));
	const __m128 tzs = _mm_set1_ps((float)(referenceToFrame.translation()[2]));

	const __m128 zeros = _mm_set1_ps(0.0f);
	const __m128 ones = _mm_set1_ps(1.0f);


	const __m128 depthVarFacs = _mm_set1_ps((float)settings.var_weight);// float depthVarFac = var_weight;	// the depth var is over-confident. this is a constant multiplier to remedy that.... HACK
	const __m128 sigma_i2s = _mm_set1_ps((float)cameraPixelNoise2);


	const __m128 huber_res_ponlys = _mm_set1_ps((float)(settings.huber_d/2));

	__m128 sumResP = zeros;


	float sumRes = 0;

	for(int i=0;i<buf_warped_size-3;i+=4)
	{
//		float px = *(buf_warped_x+i);	// x'
//		float py = *(buf_warped_y+i);	// y'
//		float pz = *(buf_warped_z+i);	// z'
//		float d = *(buf_d+i);	// d
//		float rp = *(buf_warped_residual+i); // r_p
//		float gx = *(buf_warped_dx+i);	// \delta_x I
//		float gy = *(buf_warped_dy+i);  // \delta_y I
//		float s = depthVarFac * *(buf_idepthVar+i);	// \sigma_d^2


		// calc dw/dd (first 2 components):
		__m128 pzs = _mm_load_ps(buf_warped_z+i);	// z'
		__m128 pz2ds = _mm_rcp_ps(_mm_mul_ps(_mm_mul_ps(pzs, pzs), _mm_load_ps(buf_d+i)));  // 1 / (z' * z' * d)
		__m128 g0s = _mm_sub_ps(_mm_mul_ps(pzs, txs), _mm_mul_ps(_mm_load_ps(buf_warped_x+i), tzs));
		g0s = _mm_mul_ps(g0s,pz2ds); //float g0 = (tx * pz - tz * px) / (pz*pz*d);

		 //float g1 = (ty * pz - tz * py) / (pz*pz*d);
		__m128 g1s = _mm_sub_ps(_mm_mul_ps(pzs, tys), _mm_mul_ps(_mm_load_ps(buf_warped_y+i), tzs));
		g1s = _mm_mul_ps(g1s,pz2ds);

		 // float drpdd = gx * g0 + gy * g1;	// ommitting the minus
		__m128 drpdds = _mm_add_ps(
				_mm_mul_ps(g0s, _mm_load_ps(buf_warped_dx+i)),
				_mm_mul_ps(g1s, _mm_load_ps(buf_warped_dy+i)));

		 //float w_p = 1.0f / (sigma_i2 + s * drpdd * drpdd);
		__m128 w_ps = _mm_rcp_ps(_mm_add_ps(sigma_i2s,
				_mm_mul_ps(drpdds,
						_mm_mul_ps(drpdds,
								_mm_mul_ps(depthVarFacs,
										_mm_load_ps(buf_idepthVar+i))))));


		//float weighted_rp = fabs(rp*sqrtf(w_p));
		__m128 weighted_rps = _mm_mul_ps(_mm_load_ps(buf_warped_residual+i),
				_mm_sqrt_ps(w_ps));
		weighted_rps = _mm_max_ps(weighted_rps, _mm_sub_ps(zeros,weighted_rps));


		//float wh = fabs(weighted_rp < huber_res_ponly ? 1 : huber_res_ponly / weighted_rp);
		__m128 whs = _mm_cmplt_ps(weighted_rps, huber_res_ponlys);	// bitmask 0xFFFFFFFF for 1, 0x000000 for huber_res_ponly / weighted_rp
		whs = _mm_or_ps(
				_mm_and_ps(whs, ones),
				_mm_andnot_ps(whs, _mm_mul_ps(huber_res_ponlys, _mm_rcp_ps(weighted_rps))));




		// sumRes.sumResP += wh * w_p * rp*rp;
		if(i+3 < buf_warped_size)
			sumResP = _mm_add_ps(sumResP,
					_mm_mul_ps(whs, _mm_mul_ps(weighted_rps, weighted_rps)));

		// *(buf_weight_p+i) = wh * w_p;
		_mm_store_ps(buf_weight_p+i, _mm_mul_ps(whs, w_ps) );
	}
	sumRes = SSEE(sumResP,0) + SSEE(sumResP,1) + SSEE(sumResP,2) + SSEE(sumResP,3);

	return sumRes / ((buf_warped_size >> 2)<<2);
}
#endif
	
#if defined(ENABLE_NEON)
float SE3Tracker::calcWeightsAndResidualNEON(
		const Sophus::SE3f& referenceToFrame)
{
	float tx = referenceToFrame.translation()[0];
	float ty = referenceToFrame.translation()[1];
	float tz = referenceToFrame.translation()[2];


	float constants[] = {
		tx, ty, tz, settings.var_weight,
		cameraPixelNoise2, settings.huber_d/2, -1, -1 // last values are currently unused
	};
	// This could also become a constant if one register could be made free for it somehow
	float cutoff_res_ponly4[4] = {10000, 10000, 10000, 10000}; // removed
	float* cur_buf_warped_z = buf_warped_z;
	float* cur_buf_warped_x = buf_warped_x;
	float* cur_buf_warped_y = buf_warped_y;
	float* cur_buf_warped_dx = buf_warped_dx;
	float* cur_buf_warped_dy = buf_warped_dy;
	float* cur_buf_warped_residual = buf_warped_residual;
	float* cur_buf_d = buf_d;
	float* cur_buf_idepthVar = buf_idepthVar;
	float* cur_buf_weight_p = buf_weight_p;
	int loop_count = buf_warped_size / 4;
	int remaining = buf_warped_size - 4 * loop_count;
	float sum_vector[] = {0, 0, 0, 0};
	
	float sumRes=0;

	
#ifdef DEBUG
	loop_count = 0;
	remaining = buf_warped_size;
#else
	if (loop_count > 0)
	{
		__asm__ __volatile__
		(
			// Extract constants
			"vldmia   %[constants], {q8-q9}              \n\t" // constants(q8-q9)
			"vdup.32  q13, d18[0]                        \n\t" // extract sigma_i2 x 4 to q13
			"vdup.32  q14, d18[1]                        \n\t" // extract huber_res_ponly x 4 to q14
			//"vdup.32  ???, d19[0]                        \n\t" // extract cutoff_res_ponly x 4 to ???
			"vdup.32  q9, d16[0]                         \n\t" // extract tx x 4 to q9, overwrite!
			"vdup.32  q10, d16[1]                        \n\t" // extract ty x 4 to q10
			"vdup.32  q11, d17[0]                        \n\t" // extract tz x 4 to q11
			"vdup.32  q8, d17[1]                         \n\t" // extract depthVarFac x 4 to q8, overwrite!
			
			"veor     q15, q15, q15                      \n\t" // set sumRes.sumResP(q15) to zero (by xor with itself)
			".loopcalcWeightsAndResidualNEON:            \n\t"
			
				"vldmia   %[buf_idepthVar]!, {q7}           \n\t" // s(q7)
				"vldmia   %[buf_warped_z]!, {q2}            \n\t" // pz(q2)
				"vldmia   %[buf_d]!, {q3}                   \n\t" // d(q3)
				"vldmia   %[buf_warped_x]!, {q0}            \n\t" // px(q0)
				"vldmia   %[buf_warped_y]!, {q1}            \n\t" // py(q1)
				"vldmia   %[buf_warped_residual]!, {q4}     \n\t" // rp(q4)
				"vldmia   %[buf_warped_dx]!, {q5}           \n\t" // gx(q5)
				"vldmia   %[buf_warped_dy]!, {q6}           \n\t" // gy(q6)
		
				"vmul.f32 q7, q7, q8                        \n\t" // s *= depthVarFac
				"vmul.f32 q12, q2, q2                       \n\t" // pz*pz (q12, temp)
				"vmul.f32 q3, q12, q3                       \n\t" // pz*pz*d (q3)
		
				"vrecpe.f32 q3, q3                          \n\t" // 1/(pz*pz*d) (q3)
				"vmul.f32 q12, q9, q2                       \n\t" // tx*pz (q12)
				"vmls.f32 q12, q11, q0                      \n\t" // tx*pz - tz*px (q12) [multiply and subtract] {free: q0}
				"vmul.f32 q0, q10, q2                       \n\t" // ty*pz (q0) {free: q2}
				"vmls.f32 q0, q11, q1                       \n\t" // ty*pz - tz*py (q0) {free: q1}
				"vmul.f32 q12, q12, q3                      \n\t" // g0 (q12)
				"vmul.f32 q0, q0, q3                        \n\t" // g1 (q0)
		
				"vmul.f32 q12, q12, q5                      \n\t" // gx * g0 (q12) {free: q5}
				"vldmia %[cutoff_res_ponly4], {q5}          \n\t" // cutoff_res_ponly (q5), load for later
				"vmla.f32 q12, q6, q0                       \n\t" // drpdd = gx * g0 + gy * g1 (q12) {free: q6, q0}
				
				"vmov.f32 q1, #1.0                          \n\t" // 1.0 (q1), will be used later
		
				"vmul.f32 q12, q12, q12                     \n\t" // drpdd*drpdd (q12)
				"vmul.f32 q12, q12, q7                      \n\t" // s*drpdd*drpdd (q12)
				"vadd.f32 q12, q12, q13                     \n\t" // sigma_i2 + s*drpdd*drpdd (q12)
				"vrecpe.f32 q12, q12                        \n\t" // w_p = 1/(sigma_i2 + s*drpdd*drpdd) (q12) {free: q7}
		
				// float weighted_rp = fabs(rp*sqrtf(w_p));
				"vrsqrte.f32 q7, q12                        \n\t" // 1 / sqrtf(w_p) (q7)
				"vrecpe.f32 q7, q7                          \n\t" // sqrtf(w_p) (q7)
				"vmul.f32 q7, q7, q4                        \n\t" // rp*sqrtf(w_p) (q7)
				"vabs.f32 q7, q7                            \n\t" // weighted_rp (q7)
		
				// float wh = fabs(weighted_rp < huber_res_ponly ? 1 : huber_res_ponly / weighted_rp);
				"vrecpe.f32 q6, q7                          \n\t" // 1 / weighted_rp (q6)
				"vmul.f32 q6, q6, q14                       \n\t" // huber_res_ponly / weighted_rp (q6)
				"vclt.f32 q0, q7, q14                       \n\t" // weighted_rp < huber_res_ponly ? all bits 1 : all bits 0 (q0)
				"vbsl     q0, q1, q6                        \n\t" // sets elements in q0 to 1(q1) where above condition is true, and to q6 where it is false {free: q6}
				"vabs.f32 q0, q0                            \n\t" // wh (q0)
		
				// sumRes.sumResP += wh * w_p * rp*rp
				"vmul.f32 q4, q4, q4                        \n\t" // rp*rp (q4)
				"vmul.f32 q4, q4, q12                       \n\t" // w_p*rp*rp (q4)
				"vmla.f32 q15, q4, q0                       \n\t" // sumRes.sumResP += wh*w_p*rp*rp (q15) {free: q4}
				
				// if(weighted_rp > cutoff_res_ponly)
				//     wh = 0;
				// *(buf_weight_p+i) = wh * w_p;
				"vcle.f32 q4, q7, q5                        \n\t" // mask in q4: ! (weighted_rp > cutoff_res_ponly)
				"vmul.f32 q0, q0, q12                       \n\t" // wh * w_p (q0)
				"vand     q0, q0, q4                        \n\t" // set q0 to 0 where condition for q4 failed (i.e. weighted_rp > cutoff_res_ponly)
				"vstmia   %[buf_weight_p]!, {q0}            \n\t"
				
			"subs     %[loop_count], %[loop_count], #1    \n\t"
			"bne      .loopcalcWeightsAndResidualNEON     \n\t"
				
			"vstmia   %[sum_vector], {q15}                \n\t"

		: /* outputs */ [buf_warped_z]"+&r"(cur_buf_warped_z),
						[buf_warped_x]"+&r"(cur_buf_warped_x),
						[buf_warped_y]"+&r"(cur_buf_warped_y),
						[buf_warped_dx]"+&r"(cur_buf_warped_dx),
						[buf_warped_dy]"+&r"(cur_buf_warped_dy),
						[buf_d]"+&r"(cur_buf_d),
						[buf_warped_residual]"+&r"(cur_buf_warped_residual),
						[buf_idepthVar]"+&r"(cur_buf_idepthVar),
						[buf_weight_p]"+&r"(cur_buf_weight_p),
						[loop_count]"+&r"(loop_count)
		: /* inputs  */ [constants]"r"(constants),
						[cutoff_res_ponly4]"r"(cutoff_res_ponly4),
						[sum_vector]"r"(sum_vector)
		: /* clobber */ "memory", "cc",
						"q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
		);
		
		sumRes += sum_vector[0] + sum_vector[1] + sum_vector[2] + sum_vector[3];
	}
#endif

	for(int i=buf_warped_size-remaining; i<buf_warped_size; i++)
	{
		float px = *(buf_warped_x+i);	// x'
		float py = *(buf_warped_y+i);	// y'
		float pz = *(buf_warped_z+i);	// z'
		float d = *(buf_d+i);	// d
		float rp = *(buf_warped_residual+i); // r_p
		float gx = *(buf_warped_dx+i);	// \delta_x I
		float gy = *(buf_warped_dy+i);  // \delta_y I
		float s = settings.var_weight * *(buf_idepthVar+i);	// \sigma_d^2


		// calc dw/dd (first 2 components):
		float g0 = (tx * pz - tz * px) / (pz*pz*d);
		float g1 = (ty * pz - tz * py) / (pz*pz*d);


		// calc w_p
		float drpdd = gx * g0 + gy * g1;	// ommitting the minus
		float w_p = 1.0f / (cameraPixelNoise2 + s * drpdd * drpdd);
		float weighted_rp = fabs(rp*sqrtf(w_p));

		float wh = fabs(weighted_rp < (settings.huber_d/2) ? 1 : (settings.huber_d/2) / weighted_rp);

		sumRes += wh * w_p * rp*rp;

		*(buf_weight_p+i) = wh * w_p;
	}

	return sumRes / buf_warped_size;
}
#endif
///////////////////////////////////////////////////////////////////
// 每个像素点都有一个光度匹配误差,而每个点匹配的可信度根据其偏导数可以求得,
// 加权每一个误差 然后求所有点 光度匹配误差的均值
// 再者 计算误差的权重,在优化更新函数中作为权重
	
// 使用 误差方差对方差归一化 后求和
// 计算权重  和 像素匹配误差
// 该函数的功能是计算归一化方差的光度误差系数,也就是计算公式(14),
// 并且乘以了Huber-weight,最终把这个系数存在数组buf_weight_p中。
// 可能是考虑参考帧到当前帧的位姿变换比较小,
// 所以作者只考虑了位移t而忽略旋转R。
// 这样使得式子(14)中的偏导的形式简单了很多。
// 这里主要求 误差函数的导数 即得到 误差的协方差矩阵
float SE3Tracker::calcWeightsAndResidual(
		const Sophus::SE3f& referenceToFrame)// 参考帧 到 当前帧 欧式变换矩阵
{
  // 参考帧 到 当前帧 平移向量
	float tx = referenceToFrame.translation()[0];
	float ty = referenceToFrame.translation()[1];
	float tz = referenceToFrame.translation()[2];

	float sumRes = 0;
// 每个像素点都有一个光度匹配误差,而每个点匹配的可信度根据其偏导数可以求得,
// 加权每一个误差 然后求所有点 光度匹配误差的均值 
	for(int i=0;i<buf_warped_size;i++)// 对于初步匹配得 每一个 匹配点
	{
	  // 参考帧 映射到 当前帧 坐标系下的 3d 坐标
		float px = *(buf_warped_x+i);	// x'
		float py = *(buf_warped_y+i);	// y'
		float pz = *(buf_warped_z+i);	// z'
		
		float d = *(buf_d+i);	// d          	参考帧 Z轴 倒数  = 参考帧 逆深度
		float rp = *(buf_warped_residual+i); // r_p   	匹配点对 像素匹配误差
		
		float gx = *(buf_warped_dx+i);	// \delta_x I 	gx = dx*fx当前帧 亚像素梯度值gx仿射变换后(  乘以 相机内参数)
		float gy = *(buf_warped_dy+i);  // \delta_y I       	gy = gy*fy
		
		float s = settings.var_weight  *  *(buf_idepthVar+i);	// \sigma_d^2    参考帧 逆深度 方差  平方
// 参考帧3D点通过Rt映射到当前帧坐标系下在通过相机内参数映射到当前帧图像平面上
// 的到所谓的光度误差 该误差对参考帧p点处的逆深度D求偏导数得到
// dE =  -(dx*fx * (tx*z' - tz*x')/(z'^2*d)  + dy*fy * (ty*z' - tz*y')/(z'^2*d))

		// calc dw/dd (first 2 components):
		float g0 = (tx * pz - tz * px) / (pz*pz*d);//   
		float g1 = (ty * pz - tz * py) / (pz*pz*d);
		
		// 误差 偏导数的结果 calc w_p
		// 正如源码所注释,这里省略了负号
		float drpdd = gx * g0 + gy * g1;	// ommitting the minus
		
		float w_p = 1.0f / ((cameraPixelNoise2) + s * drpdd * drpdd);// 误差权重 方差倒数

		float weighted_rp = fabs(rp*sqrtf(w_p));// |r|加权的误差
		// 为了减少外点(outliers)对算法的影响,论文中使用了迭代变权重最小二乘
                // 其实在实现的时候,这里给的权重就是Huber-weight。 
		float wh = fabs(weighted_rp < (settings.huber_d/2) ? 1 : (settings.huber_d/2) / weighted_rp);

		//sumRes += wh * w_p * rp*rp;
		//*(buf_weight_p+i) = wh * w_p;// // 乘上 Huber-weight 权重 的最终误差
		*(buf_weight_p+i) = wh * w_p;// // 乘上 Huber-weight 权重 的最终误差
		sumRes += *(buf_weight_p+i) * rp*rp;// 加权误差和
		
	}

	return sumRes / buf_warped_size;// 返回加权误差均值
}
/////////////////////////////////////////////////////////////////////////////////////////////////


// 如果要可视化Tracking的迭代过程,那么第一步自然是把debug相关的参数都设置进去,
// 否则直接进行下一步,这个操作是通过调用calcResidualAndBuffers_debugStart()实现的
void SE3Tracker::calcResidualAndBuffers_debugStart()
{
  // 是否需要 可视化Tracking的迭代过程
	if(plotTrackingIterationInfo || saveAllTrackingStagesInternal)
	{
		int other = saveAllTrackingStagesInternal ? 255 : 0;
		// 填充图像
		fillCvMat(&debugImageResiduals,cv::Vec3b(other,other,255));
		fillCvMat(&debugImageWeights,cv::Vec3b(other,other,255));
		fillCvMat(&debugImageOldImageSource,cv::Vec3b(other,other,255));
		fillCvMat(&debugImageOldImageWarped,cv::Vec3b(other,other,255));
	}
}

// 完成匹配误差计算  保存误差信息图片
void SE3Tracker::calcResidualAndBuffers_debugFinish(int w)
{
  // 显示跟踪 优化迭代信息
	if(plotTrackingIterationInfo)
	{
		Util::displayImage( "Weights", debugImageWeights );
		Util::displayImage( "second_frame", debugImageSecondFrame );
		Util::displayImage( "Intensities of second_frame at transformed positions", debugImageOldImageSource );
		Util::displayImage( "Intensities of second_frame at pointcloud in first_frame", debugImageOldImageWarped );
		Util::displayImage( "Residuals", debugImageResiduals );

		// wait for key and handle it
		bool looping = true;
		while(looping)
		{
			int k = Util::waitKey(1);
			if(k == -1)
			{
				if(autoRunWithinFrame)
					break;
				else
					continue;
			}

			char key = k;
			if(key == ' ')
				looping = false;
			else
				handleKey(k);// 处理
		}
	}
// 保存跟踪迭代信息 图像
	if(saveAllTrackingStagesInternal)
	{
		char charbuf[500];

		snprintf(charbuf,500,"save/%sresidual-%d-%d.png",packagePath.c_str(),w,iterationNumber);// 格式化字符串 图片名
		cv::imwrite(charbuf,debugImageResiduals);// 写图片  参差

		snprintf(charbuf,500,"save/%swarped-%d-%d.png",packagePath.c_str(),w,iterationNumber);
		cv::imwrite(charbuf,debugImageOldImageWarped);

		snprintf(charbuf,500,"save/%sweights-%d-%d.png",packagePath.c_str(),w,iterationNumber);
		cv::imwrite(charbuf,debugImageWeights);// 权重图片

		printf("saved three images for lvl %d, iteration %d\n",w,iterationNumber);// 打印信息
	}
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////  跟踪完成获取 变换矩阵 和 3d 点后 反投影到当前帧  计算 灰度匹配误差 ////////////////////////////
// calcResidualAndBuffers  X86平台 SSE指令集优化
#if defined(ENABLE_SSE)
float SE3Tracker::calcResidualAndBuffersSSE(
		const Eigen::Vector3f* refPoint,
		const Eigen::Vector2f* refColVar,
		int* idxBuf,
		int refNum,
		Frame* frame,
		const Sophus::SE3f& referenceToFrame,
		int level,
		bool plotResidual)
{
	return calcResidualAndBuffers(refPoint, refColVar, idxBuf, refNum, frame, referenceToFrame, level, plotResidual);
}
#endif
// calcResidualAndBuffers ARM平台 NENO指令集优化
#if defined(ENABLE_NEON)
float SE3Tracker::calcResidualAndBuffersNEON(
		const Eigen::Vector3f* refPoint,
		const Eigen::Vector2f* refColVar,
		int* idxBuf,
		int refNum,
		Frame* frame,
		const Sophus::SE3f& referenceToFrame,
		int level,
		bool plotResidual)
{
	return calcResidualAndBuffers(refPoint, refColVar, idxBuf, refNum, frame, referenceToFrame, level, plotResidual);
}
#endif

// #ifdef的使用和#if defined()的用法一致
// #ifndef又和#if !defined()的用法一致。

// 优化相关的函数,参数共有8个,在后面可以看到,其他函数通过  callOptimized 这个宏调用了这个函数进行优化操作
// 匹配帧通过 变换矩阵 和 相机内参数 投影到 当前图像平面上(值为float小数)
// 而原图像上的像素点坐标为 整数值,每一个像素位置对应有,梯度信息
// 那么 浮点数像素坐标对应的像素 是多少呢,根据浮点数坐标 四周的四个整数点坐标的梯度 加权和 后计算 灰度匹配误差
// 根据变换矩阵和原3D点和灰度值 计算匹配点对之间得 像素匹配误差  以及 匹配点对 好坏标志图
/*
 input:
 refPoint    	 参考帧 3d坐标 起始 地址指针
 refColVar   	 参考帧 灰度和 逆深度方差 起始地址
 idxBuf    	匹配点好坏标志图 指针
 frame    	当前帧 指针
 referenceToFrame 参考帧 变换到 当前帧下得 欧式变换矩阵 引用 
 level       金字塔层级
 plotResidual   匹配误差图标志
 */
float SE3Tracker::calcResidualAndBuffers(
		const Eigen::Vector3f* refPoint,// 参考帧 3d坐标 起始 地址
		const Eigen::Vector2f* refColVar,// 参考帧 灰度和 逆深度方差 起始地址
		int* idxBuf,// 匹配点好坏标志图 指针
		int refNum,// 参考帧 3d点数量
		Frame* frame,// 当前帧
		const Sophus::SE3f& referenceToFrame,// 参考帧 变换到 当前帧下得 欧式变换矩阵 
		int level,// 金字塔层级
		bool plotResidual)//显示 匹配误差图标志
{
// 如果要可视化Tracking的迭代过程,那么第一步自然是把debug相关的参数都设置进去,
// 否则直接进行下一步,这个操作是通过调用calcResidualAndBuffers_debugStart()实现的
	calcResidualAndBuffers_debugStart();
// 然后判断是否可视化残差,如果需要可视化,那么初始化残差
	if(plotResidual)
		debugImageResiduals.setTo(0);
// 本地参数设置 
	int w = frame->width(level);// 图像尺寸
	int h = frame->height(level);
	Eigen::Matrix3f KLvl = frame->K(level);// 相机内参数
	float fx_l = KLvl(0,0);
	float fy_l = KLvl(1,1);
	float cx_l = KLvl(0,2);
	float cy_l = KLvl(1,2);

	Eigen::Matrix3f rotMat = referenceToFrame.rotationMatrix();// 旋转矩阵
	Eigen::Vector3f transVec = referenceToFrame.translation();// 平移向量
	
	const Eigen::Vector3f* refPoint_max = refPoint + refNum;// 参考帧的 3d点坐标 存储的最大位置

	const Eigen::Vector4f* frame_gradients = frame->gradients(level);// 当前帧 像素梯度 获取 关键点
// 然后定义后续所要使用的变量
	int idx=0;
	float sumResUnweighted = 0;
	bool* isGoodOutBuffer = idxBuf != 0 ? frame->refPixelWasGood() : 0;// 匹配点 对 匹配效果好坏
	int goodCount = 0;
	int badCount = 0;
	float sumSignedRes = 0;
	float sxx=0,syy=0,sx=0,sy=0,sw=0;
	float usageCount = 0;

	for(;refPoint<refPoint_max; refPoint++, refColVar++, idxBuf++)// 对于每一个 参考帧的 3d点坐标
	{
           // 参考帧3d点 R,t变换到 当前相机坐标系下
		Eigen::Vector3f Wxp = rotMat * (*refPoint) + transVec;
		//  在 投影到 当前相机 像素平面下
		float u_new = (Wxp[0]/Wxp[2])*fx_l + cx_l;// float 浮点数类型
		float v_new = (Wxp[1]/Wxp[2])*fy_l + cy_l;

		// step 1a: coordinates have to be in image:
		// (inverse test to exclude NANs)
		//  投影后点在 图像和合理范围内  判断当前点是否投影到图像中
		if(!(u_new > 1 && v_new > 1 && u_new < w-2 && v_new < h-2))
		{
			if(isGoodOutBuffer != 0)// 指针部位空
				isGoodOutBuffer[*idxBuf] = false;// 并标记
			continue;
		}
// 使用 当前点 右方点  右下方点 正下方点 的灰度梯度信息 以及 相应位置差值作为权重值 线性加权获取加权梯度信息
// 然后差值得到亚像素精度级别的 梯度(注意深度的第三个维度是图像数据)
// 浮点数像素坐标对应的像素 是多少呢,根据浮点数坐标 四周的四个整数点坐标的梯度 加权和
		Eigen::Vector3f resInterp = getInterpolatedElement43(frame_gradients, u_new, v_new, w);
		
// 之后把参考图像数据做一次 仿射操作,
		// 疑问1:代码中对参考帧的灰度做了一个仿射变换的处理,这里的原理是什么?
		//在SVO代码中的确有考虑到两帧见位姿相差过大,因此通过在空间上的仿射变换之后再求灰度的操作。
		// 但是在这里的代码中没有看出具体原理。
		float c1 = affineEstimation_a * (*refColVar)[0] + affineEstimation_b;// 参考帧 灰度[0]  ;   逆深度方差[1] 
		float c2 = resInterp[2];//  当前帧 亚像素 第三维度是图像 灰度值
		float residual = c1 - c2;// 匹配点对 的灰度  误差值
// 通过差值自适应算得权重     也就是说,误差越大(匹配效果差),权重越小 
		float weight = fabsf(residual) < 5.0f ? 1 : 5.0f / fabsf(residual);// 误差倒数为权重,误差小于5的分子为1 , 大于5的分子为5 
     // 这些参数用来迭代计算 下一次 的 仿射变换系数 
		sxx += c1*c1*weight;// 参考帧 灰度值平方和               带误差倒数 权重
		syy += c2*c2*weight;// 匹配帧 亚像素 灰度值平方 和 带误差倒数 权重
		sx += c1*weight;// 参考帧 灰度值 和                             带误差倒数 权重
		sy += c2*weight;// 匹配帧 亚像素 灰度值 和                带误差倒数 权重
		sw += weight;//  误差倒数 权重  和
		
// 然后判断这个匹配点是好是坏,也是个自适应的阈值,这个阈值为一个最大的差异常数,加上 梯度值平和 乘以一个比例系数,  
              // 这个和 匹配点对 的灰度误差值平方  比较,如果残差的平方小于它,那么认为这个点的估计比较好,然后再把这个判断赋值给isGoodOutBuffer[*idxBuf]
               // 匹配点对 的灰度  误差值 平和  小于  与 当前帧 点 的x和y方向梯度平和 相关数  时  为好的匹配点
		bool isGood = residual*residual / (MAX_DIFF_CONSTANT + MAX_DIFF_GRAD_MULT*(resInterp[0]*resInterp[0] + resInterp[1]*resInterp[1])) < 1;
		if(isGoodOutBuffer != 0)
			isGoodOutBuffer[*idxBuf] = isGood;// 记录 匹配点对好坏标志
// 之后记录计算得到的这一帧改变的值
		*(buf_warped_x+idx) = Wxp(0);// 参考帧 3d点 通过R,t变换矩阵 变换到 当前图像坐标系下的坐标值  X
		*(buf_warped_y+idx) = Wxp(1);// Y
		*(buf_warped_z+idx) = Wxp(2);// Z
		
// 这里求得 亚像素梯度之后 又乘上了 相机内参数,因为在求 误差偏导数时需要 需要用到 dx*fx  dy*fy  
		// calcWeightsAndResidual 求误差导数来求 误差的协方差矩阵 后归一化误差
		*(buf_warped_dx+idx) = fx_l * resInterp[0];// 当前帧匹配点亚像素 梯度 gx = dx*fx
		*(buf_warped_dy+idx) = fy_l * resInterp[1];// 匹配点亚像素 梯度             gy = dy*fy  
		*(buf_warped_residual+idx) = residual;// 对应 匹配点 像素误差

		*(buf_d+idx) = 1.0f / (*refPoint)[2];// 参考帧 Z轴 倒数  = 参考帧逆深度
		*(buf_idepthVar+idx) = (*refColVar)[1];// 参考帧逆深度方差
		idx++;// 点 ++

// 之后再记录 匹配点像素误差的平方和,以 及匹配点像素误差值(带符号)
		if(isGood)
		{
			sumResUnweighted += residual*residual;// 较好的  匹配点像素误差的平方和
			sumSignedRes += residual;// 较好的 匹配点像素误差和
			goodCount++;// 较好的匹配点对 计数
		}
		else
			badCount++;// 不好的匹配点对 计数
// 匹配点 变换前后 深度得变换  checkPermaRefOverlap()函数也有类似的计算 
		float depthChange = (*refPoint)[2] / Wxp[2];	// if depth becomes larger: pixel becomes "smaller", hence count it less.
		usageCount += depthChange < 1 ? depthChange : 1;//   记录深度改变的比例

// 调试  如果设置了画图,就把他们可视化出来
		// DEBUG STUFF
		if(plotTrackingIterationInfo || plotResidual)
		{
			// for debug plot only: find x,y again.
			// horribly inefficient, but who cares at this point...
			Eigen::Vector3f point = KLvl * (*refPoint);// 参考帧下 3d点对应的 像素点2d坐标
			int x = point[0] / point[2] + 0.5f;
			int y = point[1] / point[2] + 0.5f;

			if(plotTrackingIterationInfo)
			{
			  // 设置参考帧和 当前帧 关键点的 图像  使用 亚像素灰度值作为 点颜色
				setPixelInCvMat(&debugImageOldImageSource,getGrayCvPixel((float)resInterp[2]),u_new+0.5,v_new+0.5,(width/w));// 当前图像下的 关键点2d 坐标
				setPixelInCvMat(&debugImageOldImageWarped,getGrayCvPixel((float)resInterp[2]),x,y,(width/w));// 参考帧下得 关键点2d 坐标
			}
			if(isGood)// 匹配点对灰度像素匹配误差较小  显示  匹配误差图像
				setPixelInCvMat(&debugImageResiduals,getGrayCvPixel(residual+128),x,y,(width/w));// 误差越小 颜色越靠中间颜色 
			else
				setPixelInCvMat(&debugImageResiduals,cv::Vec3b(0,0,255),x,y,(width/w));// 误差较大的点 显示蓝色 rgb

		}
	}

	buf_warped_size = idx;// 匹配点数量= 包括好的匹配点 和 不好的匹配点 数量

	pointUsage = usageCount / (float)refNum;// 深度改变均值
	lastGoodCount = goodCount;// 好的匹配点计数
	lastBadCount = badCount;// 不好得匹配点计数
	lastMeanRes = sumSignedRes / goodCount;// 匹配点像素误差均值
	
// 计算迭代之后得到的 下一次得仿射变换系数
	affineEstimation_a_lastIt = sqrtf((syy - sy*sy/sw) / (sxx - sx*sx/sw));
	affineEstimation_b_lastIt = (sy - affineEstimation_a_lastIt*sx)/sw;

	calcResidualAndBuffers_debugFinish(w);// 主要用来 保存调试信息 图像

	return sumResUnweighted / goodCount; // 匹配点像素误差的平方和 均值
}

	
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 计算雅克比矩阵J 线性方程系数A,和偏置b,使用LDLT矩阵分解求解方程得到 李代数更新量 dse3
// 利用误差函数对误差向量求导数,求得各个点的误差之间的 协方差矩阵,也是雅克比矩阵J
// 是计算雅可比以及最小二乘方程的系数
// 对于参考帧中的一个3D点pi位置处的残差求雅可比,这里需要使用链式求导法则 
// Ji =      1/z' *dx *fx + 0* dy *fy
//                 0*dx *fx +  1/z' *dy *fy
//   -1 *   - x'/z'^2 *dx*fx  - y'/z'^2 *dy*fy 
//            - x'*y'/z'^2 *dx*fx - (1+y'^2/z'^2) *dy*fy
//             (1+x'^2/z'^2)*dx*fx + x'*y'/z'^2*dy*fy
//              - y'/z' *dx*fx + x'/z' * dy*fy
// A = J *J转置*(*(buf_weight_p+i))
// b = -J转置*(*(buf_weight_p+i))*lastErr
// Vector6 inc = A.ldlt().solve(b);// LDLT矩阵分解求解 dse3 李代数更新量
#if defined(ENABLE_SSE)
Vector6 SE3Tracker::calculateWarpUpdateSSE(
		NormalEquationsLeastSquares &ls)
{
	ls.initialize(width*height);

//	printf("wupd SSE\n");
	for(int i=0;i<buf_warped_size-3;i+=4)
	{
		Vector6 v1,v2,v3,v4;
		__m128 val1, val2, val3, val4;

		// redefine pz
		__m128 pz = _mm_load_ps(buf_warped_z+i);
		pz = _mm_rcp_ps(pz);						// pz := 1/z


		__m128 gx = _mm_load_ps(buf_warped_dx+i);
		val1 = _mm_mul_ps(pz, gx);			// gx / z => SET [0]
		//v[0] = z*gx;
		v1[0] = SSEE(val1,0);
		v2[0] = SSEE(val1,1);
		v3[0] = SSEE(val1,2);
		v4[0] = SSEE(val1,3);




		__m128 gy = _mm_load_ps(buf_warped_dy+i);
		val1 = _mm_mul_ps(pz, gy);					// gy / z => SET [1]
		//v[1] = z*gy;
		v1[1] = SSEE(val1,0);
		v2[1] = SSEE(val1,1);
		v3[1] = SSEE(val1,2);
		v4[1] = SSEE(val1,3);


		__m128 px = _mm_load_ps(buf_warped_x+i);
		val1 = _mm_mul_ps(px, gy);
		val1 = _mm_mul_ps(val1, pz);	//  px * gy * z
		__m128 py = _mm_load_ps(buf_warped_y+i);
		val2 = _mm_mul_ps(py, gx);
		val2 = _mm_mul_ps(val2, pz);	//  py * gx * z
		val1 = _mm_sub_ps(val1, val2);  // px * gy * z - py * gx * z => SET [5]
		//v[5] = -py * z * gx +  px * z * gy;
		v1[5] = SSEE(val1,0);
		v2[5] = SSEE(val1,1);
		v3[5] = SSEE(val1,2);
		v4[5] = SSEE(val1,3);


		// redefine pz
		pz = _mm_mul_ps(pz,pz); 		// pz := 1/(z*z)

		// will use these for the following calculations a lot.
		val1 = _mm_mul_ps(px, gx);
		val1 = _mm_mul_ps(val1, pz);		// px * z_sqr * gx
		val2 = _mm_mul_ps(py, gy);
		val2 = _mm_mul_ps(val2, pz);		// py * z_sqr * gy


		val3 = _mm_add_ps(val1, val2);
		val3 = _mm_sub_ps(_mm_setr_ps(0,0,0,0),val3);	//-px * z_sqr * gx -py * z_sqr * gy
		//v[2] = -px * z_sqr * gx -py * z_sqr * gy;	=> SET [2]
		v1[2] = SSEE(val3,0);
		v2[2] = SSEE(val3,1);
		v3[2] = SSEE(val3,2);
		v4[2] = SSEE(val3,3);


		val3 = _mm_mul_ps(val1, py); // px * z_sqr * gx * py
		val4 = _mm_add_ps(gy, val3); // gy + px * z_sqr * gx * py
		val3 = _mm_mul_ps(val2, py); // py * py * z_sqr * gy
		val4 = _mm_add_ps(val3, val4); // gy + px * z_sqr * gx * py + py * py * z_sqr * gy
		val4 = _mm_sub_ps(_mm_setr_ps(0,0,0,0),val4); //val4 = -val4.
		//v[3] = -px * py * z_sqr * gx +
		//       -py * py * z_sqr * gy +
		//       -gy;		=> SET [3]
		v1[3] = SSEE(val4,0);
		v2[3] = SSEE(val4,1);
		v3[3] = SSEE(val4,2);
		v4[3] = SSEE(val4,3);


		val3 = _mm_mul_ps(val1, px); // px * px * z_sqr * gx
		val4 = _mm_add_ps(gx, val3); // gx + px * px * z_sqr * gx
		val3 = _mm_mul_ps(val2, px); // px * py * z_sqr * gy
		val4 = _mm_add_ps(val4, val3); // gx + px * px * z_sqr * gx + px * py * z_sqr * gy
		//v[4] = px * px * z_sqr * gx +
		//	   px * py * z_sqr * gy +
		//	   gx;				=> SET [4]
		v1[4] = SSEE(val4,0);
		v2[4] = SSEE(val4,1);
		v3[4] = SSEE(val4,2);
		v4[4] = SSEE(val4,3);

		// step 6: integrate into A and b:
		ls.update(v1, *(buf_warped_residual+i+0), *(buf_weight_p+i+0));

		if(i+1>=buf_warped_size) break;
		ls.update(v2, *(buf_warped_residual+i+1), *(buf_weight_p+i+1));

		if(i+2>=buf_warped_size) break;
		ls.update(v3, *(buf_warped_residual+i+2), *(buf_weight_p+i+2));

		if(i+3>=buf_warped_size) break;
		ls.update(v4, *(buf_warped_residual+i+3), *(buf_weight_p+i+3));
	}
	Vector6 result;

	// solve ls
	ls.finish();
	ls.solve(result);

	return result;
}
#endif


#if defined(ENABLE_NEON)
Vector6 SE3Tracker::calculateWarpUpdateNEON(
		NormalEquationsLeastSquares &ls)
{
//	weightEstimator.reset();
//	weightEstimator.estimateDistributionNEON(buf_warped_residual, buf_warped_size);
//	weightEstimator.calcWeightsNEON(buf_warped_residual, buf_warped_weights, buf_warped_size);

	ls.initialize(width*height);
	
	float* cur_buf_warped_z = buf_warped_z;
	float* cur_buf_warped_x = buf_warped_x;
	float* cur_buf_warped_y = buf_warped_y;
	float* cur_buf_warped_dx = buf_warped_dx;
	float* cur_buf_warped_dy = buf_warped_dy;
	Vector6 v1,v2,v3,v4;
	float* v1_ptr;
	float* v2_ptr;
	float* v3_ptr;
	float* v4_ptr;
	for(int i=0;i<buf_warped_size;i+=4)
	{
		v1_ptr = &v1[0];
		v2_ptr = &v2[0];
		v3_ptr = &v3[0];
		v4_ptr = &v4[0];
	
		__asm__ __volatile__
		(
			"vldmia   %[buf_warped_z]!, {q10}            \n\t" // pz(q10)
			"vrecpe.f32 q10, q10                         \n\t" // z(q10)
			
			"vldmia   %[buf_warped_dx]!, {q11}           \n\t" // gx(q11)
			"vmul.f32 q0, q10, q11                       \n\t" // q0 = z*gx // = v[0]
			
			"vldmia   %[buf_warped_dy]!, {q12}           \n\t" // gy(q12)
			"vmul.f32 q1, q10, q12                       \n\t" // q1 = z*gy // = v[1]
			
			"vldmia   %[buf_warped_x]!, {q13}            \n\t" // px(q13)
			"vmul.f32 q5, q13, q12                       \n\t" // q5 = px * gy
			"vmul.f32 q5, q5, q10                        \n\t" // q5 = q5 * z = px * gy * z
			
			"vldmia   %[buf_warped_y]!, {q14}            \n\t" // py(q14)
			"vmul.f32 q3, q14, q11                       \n\t" // q3 = py * gx
			"vmls.f32 q5, q3, q10                        \n\t" // q5 = px * gy * z - py * gx * z // = v[5] (vmls: multiply and subtract from result)
			
			"vmul.f32 q10, q10, q10                      \n\t" // q10 = 1/(pz*pz)
		
			"vmul.f32 q6, q13, q11                       \n\t"
			"vmul.f32 q6, q6, q10                        \n\t" // q6 = val1 in SSE version = px * z_sqr * gx
			
			"vmul.f32 q7, q14, q12                       \n\t"
			"vmul.f32 q7, q7, q10                        \n\t" // q7 = val2 in SSE version = py * z_sqr * gy
			
			"vadd.f32 q2, q6, q7                         \n\t"
			"vneg.f32 q2, q2                             \n\t" // q2 = -px * z_sqr * gx -py * z_sqr * gy // = v[2]
			
			"vmul.f32 q8, q6, q14                        \n\t" // val3(q8) = px * z_sqr * gx * py
			"vadd.f32 q9, q12, q8                        \n\t" // val4(q9) = gy + px * z_sqr * gx * py
			"vmul.f32 q8, q7, q14                        \n\t" // val3(q8) = py * py * z_sqr * gy
			"vadd.f32 q9, q8, q9                         \n\t" // val4(q9) = gy + px * z_sqr * gx * py + py * py * z_sqr * gy
			"vneg.f32 q3, q9                             \n\t" // q3 = v[3]
			
			"vst4.32 {d0[0], d2[0], d4[0], d6[0]}, [%[v1]]! \n\t" // store v[0] .. v[3] for 1st value and inc pointer
			"vst4.32 {d0[1], d2[1], d4[1], d6[1]}, [%[v2]]! \n\t" // store v[0] .. v[3] for 2nd value and inc pointer
			"vst4.32 {d1[0], d3[0], d5[0], d7[0]}, [%[v3]]! \n\t" // store v[0] .. v[3] for 3rd value and inc pointer
			"vst4.32 {d1[1], d3[1], d5[1], d7[1]}, [%[v4]]! \n\t" // store v[0] .. v[3] for 4th value and inc pointer
			
			"vmul.f32 q8, q6, q13                        \n\t" // val3(q8) = px * px * z_sqr * gx
			"vadd.f32 q9, q11, q8                        \n\t" // val4(q9) = gx + px * px * z_sqr * gx
			"vmul.f32 q8, q7, q13                        \n\t" // val3(q8) = px * py * z_sqr * gy
			"vadd.f32 q4, q9, q8                         \n\t" // q4 = v[4]
			
			"vst2.32 {d8[0], d10[0]}, [%[v1]]               \n\t" // store v[4], v[5] for 1st value
			"vst2.32 {d8[1], d10[1]}, [%[v2]]               \n\t" // store v[4], v[5] for 2nd value
			"vst2.32 {d9[0], d11[0]}, [%[v3]]               \n\t" // store v[4], v[5] for 3rd value
			"vst2.32 {d9[1], d11[1]}, [%[v4]]               \n\t" // store v[4], v[5] for 4th value

        : /* outputs */ [buf_warped_z]"+r"(cur_buf_warped_z),
		                [buf_warped_x]"+r"(cur_buf_warped_x),
		                [buf_warped_y]"+r"(cur_buf_warped_y),
		                [buf_warped_dx]"+r"(cur_buf_warped_dx),
		                [buf_warped_dy]"+r"(cur_buf_warped_dy),
		                [v1]"+r"(v1_ptr),
		                [v2]"+r"(v2_ptr),
		                [v3]"+r"(v3_ptr),
		                [v4]"+r"(v4_ptr)
        : /* inputs  */ 
        : /* clobber */ "memory", "cc", // TODO: is cc necessary?
	                    "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9", "q10", "q11", "q12", "q13", "q14"
		);
		

		// step 6: integrate into A and b:
		if(!(i+3>=buf_warped_size))
		{
			ls.update(v1, *(buf_warped_residual+i+0), *(buf_weight_p+i+0));
			ls.update(v2, *(buf_warped_residual+i+1), *(buf_weight_p+i+1));
			ls.update(v3, *(buf_warped_residual+i+2), *(buf_weight_p+i+2));
			ls.update(v4, *(buf_warped_residual+i+3), *(buf_weight_p+i+3));
		}
		else
		{
			ls.update(v1, *(buf_warped_residual+i+0), *(buf_weight_p+i+0));

			if(i+1>=buf_warped_size) break;
			ls.update(v2, *(buf_warped_residual+i+1), *(buf_weight_p+i+1));

			if(i+2>=buf_warped_size) break;
			ls.update(v3, *(buf_warped_residual+i+2), *(buf_weight_p+i+2));

			if(i+3>=buf_warped_size) break;
			ls.update(v4, *(buf_warped_residual+i+3), *(buf_weight_p+i+3));
		}
	}
	Vector6 result;

	// solve ls
	ls.finish();
	ls.solve(result);

	return result;
}
#endif

// 计算雅克比矩阵J 线性方程系数A,和偏置b,使用LDLT矩阵分解求解方程得到 李代数更新量 dse3
// 利用误差函数对误差向量求导数,求得各个点的误差之间的 协方差矩阵,也是雅克比矩阵J
// 是计算雅可比以及最小二乘方程的系数
// 对于参考帧中的一个3D点pi位置处的残差求雅可比,这里需要使用链式求导法则 
// Ji =      1/z' *dx *fx + 0* dy *fy
//                 0*dx *fx +  1/z' *dy *fy
//   -1 *   - x'/z'^2 *dx*fx  - y'/z'^2 *dy*fy 
//            - x'*y'/z'^2 *dx*fx - (1+y'^2/z'^2) *dy*fy
//             (1+x'^2/z'^2)*dx*fx + x'*y'/z'^2*dy*fy
//              - y'/z' *dx*fx + x'/z' * dy*fy
// A = J *J转置*(*(buf_weight_p+i))
// b = -J转置*(*(buf_weight_p+i))*lastErr
// Vector6 inc = A.ldlt().solve(b);// LDLT矩阵分解求解 dse3 李代数更新量
Vector6 SE3Tracker::calculateWarpUpdate(
		NormalEquationsLeastSquares &ls)
{
//	weightEstimator.reset();
//	weightEstimator.estimateDistribution(buf_warped_residual, buf_warped_size);
//	weightEstimator.calcWeights(buf_warped_residual, buf_warped_weights, buf_warped_size);
//
	ls.initialize(width*height);
	for(int i=0;i<buf_warped_size;i++)
	{
		float px = *(buf_warped_x+i);//x'
		float py = *(buf_warped_y+i);//y'
		float pz = *(buf_warped_z+i);//z'
		float r =  *(buf_warped_residual+i);//误差
		float gx = *(buf_warped_dx+i);// dx*fx
		float gy = *(buf_warped_dy+i);//dy*fx
		// step 3 + step 5 comp 6d error vector

		float z = 1.0f / pz;// 1/z'
		float z_sqr = 1.0f / (pz*pz);//1/z'^2
		Vector6 v;
		v[0] = z*gx + 0;
		v[1] = 0 +         z*gy;
		v[2] = (-px * z_sqr) * gx +
			  (-py * z_sqr) * gy;
		v[3] = (-px * py * z_sqr) * gx +
			  (-(1.0 + py * py * z_sqr)) * gy;
		v[4] = (1.0 + px * px * z_sqr) * gx +
			  (px * py * z_sqr) * gy;
		v[5] = (-py * z) * gx +
			  (px * z) * gy;

		// step 6: integrate into A and b:
			  // J转置*J * dse3 = -J转置*w*error
			  // v为求得的雅克比矩阵J  r为误差  *(buf_weight_p+i)为误差权重 
		ls.update(v, r, *(buf_weight_p+i));// 这里计算的是 A 和 b的和 以及加权误差平方和 error 
	}
// 欧式变换矩阵se3 更新后的量 
	Vector6 result;
	// solve ls
	// 对求得的 A 和 b的和以及加权误差平方和 error  求均值
	ls.finish();
	// LDLT求解线性方程组 A*x=b    x = A.ldlt().solve(b);
	ls.solve(result);
	return result;
 }

}


猜你喜欢

转载自blog.csdn.net/xiaoxiaowenqiang/article/details/80490311