本次主要讲解计算本质矩阵和计算单应矩阵中的第一步,特征点坐标归一化Initializer::Normalize
首先我们应该明白为什么要对特征点的坐标进行归一化:根据<<计算机视觉中的多视图几何>>一书中所说,对于一个直接线性求解本质矩阵和单应矩阵构成的方程组
A
x
=
0
Ax=0
Ax=0
求解不稳定的原因是系数矩阵A
的条件较差,也就是原始图像的像素坐标组成的矩阵A
的条件较差,而造成这个的直接原因就是像素点的齐次坐标各个分量的数量级相差较大,所以在进行求解之前对像素坐标进行归一化可以大大减少噪声的干扰,提高求解精度。
像素坐标的归一化分为三个步骤
- 像素坐标去均值化
- 计算一阶绝对矩
- 像素坐标除以一阶绝对矩
对应的代码分为以下几步
- 计算特征点X,Y坐标的均值 meanX, meanY
// Step 1 计算特征点X,Y坐标的均值 meanX, meanY float meanX = 0; float meanY = 0; //获取特征点的数量 const int N = vKeys.size(); //设置用来存储归一后特征点的向量大小,和归一化前保持一致 vNormalizedPoints.resize(N); //开始遍历所有的特征点 for(int i=0; i<N; i++) { //分别累加特征点的X、Y坐标 meanX += vKeys[i].pt.x; meanY += vKeys[i].pt.y; } //计算X、Y坐标的均值 meanX = meanX/N; meanY = meanY/N;
- 特征点坐标去均值并计算特征点X,Y坐标的一阶绝对矩
// Step 2 计算特征点X,Y坐标离均值的平均偏离程度 meanDevX, meanDevY,注意不是标准差 float meanDevX = 0; float meanDevY = 0; // 将原始特征点减去均值坐标,使x坐标和y坐标均值分别为0 for(int i=0; i<N; i++) { vNormalizedPoints[i].x = vKeys[i].pt.x - meanX; vNormalizedPoints[i].y = vKeys[i].pt.y - meanY; //累计这些特征点偏离横纵坐标均值的程度 meanDevX += fabs(vNormalizedPoints[i].x); meanDevY += fabs(vNormalizedPoints[i].y); } // 求出平均到每个点上,其坐标偏离横纵坐标均值的程度;将其倒数作为一个尺度缩放因子 meanDevX = meanDevX/N; meanDevY = meanDevY/N; float sX = 1.0/meanDevX; float sY = 1.0/meanDevY;
- 对特征点的像素坐标除以一阶绝对矩
// Step 3 将x坐标和y坐标分别进行尺度归一化,使得x坐标和y坐标的一阶绝对矩分别为1 // 这里所谓的一阶绝对矩其实就是随机变量到取值的中心的绝对值的平均值(期望) for(int i=0; i<N; i++) { //对,就是简单地对特征点的坐标进行进一步的缩放 vNormalizedPoints[i].x = vNormalizedPoints[i].x * sX; vNormalizedPoints[i].y = vNormalizedPoints[i].y * sY; }
- 计算从原始坐标到归一化坐标的归一化矩阵
// Step 4 计算归一化矩阵:其实就是前面做的操作用矩阵变换来表示而已 // |sX 0 -meanx*sX| // |0 sY -meany*sY| // |0 0 1 | T = cv::Mat::eye(3,3,CV_32F); T.at<float>(0,0) = sX; T.at<float>(1,1) = sY; T.at<float>(0,2) = -meanX*sX; T.at<float>(1,2) = -meanY*sY;