立体视觉入门指南(6):对级约束与Fusiello法极线校正
校正目的:对两幅图像的二维匹配搜索变成一维,节省计算量,排除虚假匹配点,成平行视图。
算法流程:
坐标系:平行视图的极点位于无穷远处,极线水平对准。
- 轴:与基线平行 。
- 轴:与X轴正交。在Fusiello法中k为旧的Z轴所表示的单位向量。
- 轴:
- 则旋转矩阵:
-
则左右两个图像新的旋转矩阵:
- 新的内参矩阵: ,且把倾斜因子设置为0。
- 原始的相对位姿:
- 新的基线:
// compute relative pose of the given two cameras
template<typename TYPE>
inline void ComputeRelativeRotation(const TMatrix<TYPE,3,3>& Ri, const TMatrix<TYPE,3,3>& Rj, TMatrix<TYPE,3,3>& Rij) {
Rij = Rj * Ri.t();
} // ComputeRelativeRotation
template<typename TYPE>
inline void ComputeRelativePose(const TMatrix<TYPE,3,3>& Ri, const TPoint3<TYPE>& Ci, const TMatrix<TYPE,3,3>& Rj, const TPoint3<TYPE>& Cj, TMatrix<TYPE,3,3>& Rij, TPoint3<TYPE>& Cij) {
Rij = Rj * Ri.t();
Cij = Ri * (Cj - Ci);
} // ComputeRelativePose
// see: "A compact algorithm for rectification of stereo pairs", A. Fusiello, E. Trucco, and A. Verri, 2000
// 极线校正
REAL Camera::StereoRectifyFusiello(const cv::Size& size1, const Camera& camera1, const cv::Size& size2, const Camera& camera2, Matrix3x3& R1, Matrix3x3& R2, Matrix3x3& K1, Matrix3x3& K2)
{
// compute relative pose
// 计算相对位姿
RMatrix poseR;
CMatrix poseC;
ComputeRelativePose(camera1.R, camera1.C, camera2.R, camera2.C, poseR, poseC);
// new x axis (baseline, from C1 to C2)
// 新的x轴,基线方向
const Point3 v1(camera2.C-camera1.C);
// new y axes (orthogonal to old z and new x)
// 新的y轴,垂直旧的Z轴(光轴)和新的X轴
const Point3 v2(camera1.Direction().cross(v1));
// new z axes (no choice, orthogonal to baseline and y)
// 新的Z轴,垂直上面两个新轴
const Point3 v3(v1.cross(v2));
// new extrinsic (translation unchanged)
// 新的外参,平移不变
RMatrix R;
R.SetFromRowVectors(normalized(v1), normalized(v2), normalized(v3));
// new intrinsic (arbitrary)
// 新的内参
K1 = camera1.K; K1(0,1) = 0;
K2 = camera2.K; K2(0,1) = 0;
K1(1,1) = K2(1,1) = (camera1.K(1,1)+camera2.K(1,1))/2;
// new rotations
// 新的选择从校正前的相机坐标系转到校正后的相机坐标系
R1 = R*camera1.R.t();
R2 = R*camera2.R.t();
#if 0
// new projection matrices
PMatrix P1, P2;
AssembleProjectionMatrix(K1, R, camera1.C, P1);
AssembleProjectionMatrix(K2, R, camera2.C, P2);
// rectifying image transformation
#if 0
const Matrix3x3 H1((PMatrix::EMat(P1).leftCols<3>()*(PMatrix::EMat(camera1.P).leftCols<3>().inverse())).eval());
const Matrix3x3 H2((PMatrix::EMat(P2).leftCols<3>()*(PMatrix::EMat(camera2.P).leftCols<3>().inverse())).eval());
#else
const Matrix3x3 H1(K1*R*camera1.R.t()*camera1.GetInvK());
const Matrix3x3 H2(K2*R*camera2.R.t()*camera2.GetInvK());
#endif
#endif
// 计算新的基线距离
const Point3 t(R2 * (poseR*(-poseC)));
ASSERT(ISEQUAL(-t.x, norm(v1)) && ISZERO(t.y) && ISZERO(t.z));
return t.x;
} // StereoRectifyFusiello