OpencvSharp 算子学习教案之 - Cv2.DecomposeProjectionMatrix 重载2
OpencvSharp 算子学习教案之 - Cv2.DecomposeProjectionMatrix 重载2
重载2:DecomposeProjectionMatrix(double[,] projMatrix, out double[,] cameraMatrix, out double[,] rotMatrix, out double[] transVect, out double[,] rotMatrixX, out double[,] rotMatrixY, out double[,] rotMatrixZ, out double[] eulerAngles)
大家好,Opencv在很多工程项目中都会用到,而OpencvSharp则是以C#开发与实现的Opencv操作库,对.NET开发人员友好,但很多API的中文资料、应用场景及常见坑点等缺乏系统性归纳,因此这系列博客将给大家带来Cv2及Mat对象全系列算子学习教案,供大家参考学习。
Cv2.DecomposeProjectionMatrix
- 教案版本:V1.0
- 面向对象:OpenCvSharp 初学者
- 所属模块:calib3d
- 源码位置:OpenCvSharp/Cv2/Cv2_calib3d.cs:361
1. 函数名称(带参数签名)
public static void DecomposeProjectionMatrix(
double[,] projMatrix,
out double[,] cameraMatrix,
out double[,] rotMatrix,
out double[] transVect,
out double[,] rotMatrixX,
out double[,] rotMatrixY,
out double[,] rotMatrixZ,
out double[] eulerAngles)
2. 函数用途
这个重载是纯托管数组的全量输出版本,适合:
- 纯
double[,]/double[]数据流。 - 需要同时拿到 cameraMatrix、rotMatrix、transVect、轴矩阵和 Euler 角的场景。
- 需要和 InputArray 版本做一致性对比的教学或测试场景。
它会返回:
cameraMatrix:3x3 相机内参矩阵。rotMatrix:3x3 旋转矩阵。transVect:4x1 齐次相机中心向量。rotMatrixX / rotMatrixY / rotMatrixZ:三个轴向旋转矩阵。eulerAngles:三个欧拉角(度)。
3. 函数公式
3.1 投影矩阵模型
P=K[R∣t] P = K [R \mid t] P=K[R∣t]
3.2 结构分解
M=P0:3,0:3=KR M = P_{0:3,0:3} = K R M=P0:3,0:3=KR
M=KR,K 为上三角,RTR=I M = K R,\quad K \text{ 为上三角},\quad R^T R = I M=KR,K 为上三角,RTR=I
3.3 相机中心
C~=[Cx,Cy,Cz,w]T \tilde C = [C_x, C_y, C_z, w]^T C~=[Cx,Cy,Cz,w]T
C=1w[Cx,Cy,Cz]T C = \frac{1}{w}[C_x, C_y, C_z]^T C=w1[Cx,Cy,Cz]T
3.4 Euler 角与轴矩阵
rotMatrixX / rotMatrixY / rotMatrixZ 和 eulerAngles 描述的是一组合法的姿态分解结果。由于欧拉角存在多解,OpenCV 只返回其中一种可用解。
3.5 常用质量指标
εorth=∥RTR−I∥F,εrecon=∥P−K[R∣−RC]∥F \varepsilon_{orth} = \|R^T R - I\|_F, \quad \varepsilon_{recon} = \|P - K[R \mid -RC]\|_F εorth=∥RTR−I∥F,εrecon=∥P−K[R∣−RC]∥F
4. 函数原理说明
这个重载直接在托管数组层面完成矩阵分解,底层逻辑与其他版本一致:
- 对投影矩阵左上 3x3 子矩阵执行 RQ 分解。
- 从完整 3x4 矩阵中提取齐次相机中心。
- 写出轴矩阵与 Euler 角,供教学或调试使用。
它的优点是:
- 不需要手工创建
Mat。 - 更适合单元测试和批处理。
- 输出结果完全可以直接喂给后续纯托管数组逻辑。
5. 参数含义解析
| 参数名 | 类型 | 必填 | 含义 | 典型形状 |
|---|---|---|---|---|
| projMatrix | double[,] | 是 | 输入 3x4 投影矩阵 | 3x4 |
| cameraMatrix | out double[,] | 是 | 输出相机内参矩阵 | 3x3 |
| rotMatrix | out double[,] | 是 | 输出旋转矩阵 | 3x3 |
| transVect | out double[] | 是 | 输出齐次相机中心向量 | 长度 4 |
| rotMatrixX | out double[,] | 是 | 绕 X 轴的旋转矩阵 | 3x3 |
| rotMatrixY | out double[,] | 是 | 绕 Y 轴的旋转矩阵 | 3x3 |
| rotMatrixZ | out double[,] | 是 | 绕 Z 轴的旋转矩阵 | 3x3 |
| eulerAngles | out double[] | 是 | 三个欧拉角(度) | 长度 3 |
6. 应用场景列表
| 场景名 | 场景说明 | 价值 |
|---|---|---|
| 场景A:double[,] 全量分解 | 输出所有结果 | 适合完整教学 |
| 场景B:轴矩阵质量检查 | 验证 rotMatrixX/Y/Z | 解释姿态分解 |
| 场景C:与 InputArray 重载对比 | 验证两类封装一致 | 交叉校验 |
| 场景D:扰动敏感性分析 | 观察噪声影响 | 评估稳定性 |
7. 函数使用示例(每个场景一个完整示例)
7.1 场景A:double[,] 全量分解
using System;
using OpenCvSharp;
internal static class Program
{
private static void Main()
{
// 这里构造一个 3x4 投影矩阵,作为全量重载的输入样本。
double[,] projMatrix =
{
{ 1195.0, 0.0, 640.0, 1850.0 },
{ 0.0, 1185.0, 360.0, -720.0 },
{ 0.0, 0.0, 1.0, 4.20 },
};
// 全量重载一次性输出 cameraMatrix、rotMatrix、transVect、三个轴矩阵和 Euler 角。
// 对于初学者来说,这个版本最适合观察“完整分解结果”到底长什么样。
Cv2.DecomposeProjectionMatrix(
projMatrix,
out var cameraMatrix,
out var rotMatrix,
out var transVect,
out var rotMatrixX,
out var rotMatrixY,
out var rotMatrixZ,
out var eulerAngles);
// transVect 是齐次相机中心,长度固定为 4。
Console.WriteLine($"transVect length = {transVect.Length}");
// Euler 角是度数制输出,便于阅读和教学讲解。
Console.WriteLine($"eulerAngles = [{eulerAngles[0]:F6}, {eulerAngles[1]:F6}, {eulerAngles[2]:F6}]");
}
}
7.2 场景B:轴矩阵质量检查
using OpenCvSharp;
// 为了让这个示例可以单独复制运行,这里先准备和 7.1 相同的投影矩阵。
double[,] projMatrix =
{
{ 1195.0, 0.0, 640.0, 1850.0 },
{ 0.0, 1185.0, 360.0, -720.0 },
{ 0.0, 0.0, 1.0, 4.20 },
};
// 先执行全量重载,拿到 rotMatrixX/Y/Z 和 Euler 角。
Cv2.DecomposeProjectionMatrix(
projMatrix,
out var cameraMatrix,
out var rotMatrix,
out var transVect,
out var rotMatrixX,
out var rotMatrixY,
out var rotMatrixZ,
out var eulerAngles);
// 再把左上 3x3 子矩阵提取出来,因为 RQDecomp3x3 只关心这个部分。
var left3x3 = new double[,]
{
{ projMatrix[0, 0], projMatrix[0, 1], projMatrix[0, 2] },
{ projMatrix[1, 0], projMatrix[1, 1], projMatrix[1, 2] },
{ projMatrix[2, 0], projMatrix[2, 1], projMatrix[2, 2] },
};
// 用 RQDecomp3x3 再做一次分解,方便和 DecomposeProjectionMatrix 的输出交叉验证。
var rqAngles = Cv2.RQDecomp3x3(left3x3, out var rqR, out var rqQ, out var rqX, out var rqY, out var rqZ);
// 下面用最直接的方式计算一个轴矩阵的正交误差:Qx * Qx^T 是否接近单位矩阵。
double orthErrorX = 0.0;
for (var row = 0; row < 3; row++)
{
for (var col = 0; col < 3; col++)
{
// 这一步是在手写矩阵乘法,方便理解“行乘列”到底在算什么。
double sum = 0.0;
for (var k = 0; k < 3; k++)
{
sum += rotMatrixX[row, k] * rotMatrixX[col, k];
}
// 对角线期望是 1,非对角线期望是 0。
var expected = row == col ? 1.0 : 0.0;
var diff = sum - expected;
orthErrorX += diff * diff;
}
}
7.3 场景C:与 InputArray 重载对比
using OpenCvSharp;
// 这里沿用 7.1 中的 projMatrix 样本;如果单独复制这一段,请先补上同样的 3x4 矩阵定义。
// 先把同一份输入包装成 Mat,再转成 InputArray,以便调用 InputArray 版本。
using var projMat = Mat.FromPixelData(3, 4, MatType.CV_64FC1, projMatrix);
using var projInput = InputArray.Create((Mat)projMat);
// 这些 Mat<double> 容器用于接收 InputArray 版本输出。
using var kMat = new Mat<double>();
using var rMat = new Mat<double>();
using var tMat = new Mat<double>();
using var xMat = new Mat<double>();
using var yMat = new Mat<double>();
using var zMat = new Mat<double>();
using var eMat = new Mat<double>();
// 先跑 InputArray 版本,再跑 double[,] 版本,最后比较核心结果。
Cv2.DecomposeProjectionMatrix(projInput, OutputArray.Create(kMat), OutputArray.Create(rMat), OutputArray.Create(tMat), OutputArray.Create(xMat), OutputArray.Create(yMat), OutputArray.Create(zMat), OutputArray.Create(eMat));
Cv2.DecomposeProjectionMatrix(projMatrix, out var basicK, out var basicR, out var basicT, out var basicX, out var basicY, out var basicZ, out var basicE);
7.4 场景D:扰动敏感性分析
using OpenCvSharp;
// 这里继续沿用 7.1 中的 projMatrix 样本。
// 复制原始矩阵,避免修改基础样本。
var noisyProj = (double[,])projMatrix.Clone();
// 这里故意加一点微小扰动,模拟真实测量中的噪声或数值误差。
noisyProj[0, 3] += 0.12;
noisyProj[1, 3] -= 0.08;
// 再次执行全量分解,观察 K/R/角度是否出现明显波动。
Cv2.DecomposeProjectionMatrix(noisyProj, out var noisyK, out var noisyR, out var noisyT, out var noisyX, out var noisyY, out var noisyZ, out var noisyE);
8. 函数使用注意事项
transVect仍然需要按w归一化后再使用。eulerAngles并不是唯一表示,不能和别的实现做“逐元素绝对等值”判断。- 对比姿态时,优先比较
rotMatrix和cameraMatrix,再看 Euler 角。 - 若输入矩阵包含噪声,欧拉角比矩阵更容易出现跳变。
9. 参数调优建议
- 如果你只关心核心结果,基础重载更简洁;如果你要教学/调试姿态,则优先全量重载。
- 做自动化验证时,建议同时检查
rotMatrix的正交误差和重建误差。 - 如果要和其他库对比,请先统一矩阵约定和欧拉角顺序。
10. 示例代码运行说明(按场景关键逻辑)
场景A(全量分解)
- 准备 3x4 投影矩阵。
- 调用全量重载。
- 输出所有矩阵和 Euler 角。
场景B(轴矩阵质量检查)
- 对左上 3x3 子矩阵调用
RQDecomp3x3。 - 对比轴矩阵和欧拉角。
- 验证结果一致性。
场景C(与 InputArray 对比)
- 用同一输入分别调用两种封装。
- 对比所有核心输出。
- 确认 wrapper 不改变数值行为。
场景D(扰动敏感性)
- 对输入矩阵施加小扰动。
- 重新分解。
- 观察输出变化。
11. 常见错误排查
- 错误:把 Euler 角当作唯一姿态表示
- 排查:欧拉角存在多解,优先比较旋转矩阵。
- 错误:忽略
transVect的齐次归一化- 排查:先除以
w。
- 排查:先除以
- 错误:认为全量重载和 InputArray 重载的结果可能不同
- 排查:两者底层数学过程相同,差异通常来自输入不一致或数据类型转换。
- 错误:只看一个矩阵指标
- 排查:至少同时检查
cameraMatrix、rotMatrix和重建误差。
- 排查:至少同时检查
对应 WPF 演示控件与样例代码:
- Features/Cv2DecomposeProjectionMatrixDoubleArrayFull/Cv2DecomposeProjectionMatrixDoubleArrayFullControl.xaml
- Samples/Cv2DecomposeProjectionMatrix/DecomposeProjectionMatrixDoubleFullDecomposeSample.cs
- Samples/Cv2DecomposeProjectionMatrix/DecomposeProjectionMatrixDoubleFullAxisSample.cs
- Samples/Cv2DecomposeProjectionMatrix/DecomposeProjectionMatrixDoubleFullCompareInputArraySample.cs
- Samples/Cv2DecomposeProjectionMatrix/DecomposeProjectionMatrixDoubleFullPerturbationSample.cs
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)