OpencvSharp 算子学习教案之 - Cv2.DecomposeProjectionMatrix 重载1
OpencvSharp 算子学习教案之 - Cv2.DecomposeProjectionMatrix 重载1
重载1:DecomposeProjectionMatrix(InputArray projMatrix, OutputArray cameraMatrix, OutputArray rotMatrix, OutputArray transVect, OutputArray? rotMatrixX = null, OutputArray? rotMatrixY = null, OutputArray? rotMatrixZ = null, OutputArray? eulerAngles = null)
大家好,Opencv在很多工程项目中都会用到,而OpencvSharp则是以C#开发与实现的Opencv操作库,对.NET开发人员友好,但很多API的中文资料、应用场景及常见坑点等缺乏系统性归纳,因此这系列博客将给大家带来Cv2及Mat对象全系列算子学习教案,供大家参考学习。
Cv2.DecomposeProjectionMatrix
- 教案版本:V1.0
- 面向对象:OpenCvSharp 初学者
- 所属模块:calib3d
- 源码位置:OpenCvSharp/Cv2/Cv2_calib3d.cs:304
1. 函数名称(带参数签名)
public static void DecomposeProjectionMatrix(
InputArray projMatrix,
OutputArray cameraMatrix,
OutputArray rotMatrix,
OutputArray transVect,
OutputArray? rotMatrixX = null,
OutputArray? rotMatrixY = null,
OutputArray? rotMatrixZ = null,
OutputArray? eulerAngles = null)
2. 函数用途
这个重载用于在 Mat / OutputArray 工作流中分解 3x4 投影矩阵,适合:
- 相机标定结果的后处理与验证。
- 位姿恢复、AR 投影链路和 3D 重建管线。
- 需要额外输出轴旋转矩阵和 Euler 角的教学/调试场景。
它会输出:
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]
其中:
K∈R3×3,R∈R3×3,t∈R3 K \in \mathbb{R}^{3\times3},\quad R \in \mathbb{R}^{3\times3},\quad t \in \mathbb{R}^{3} K∈R3×3,R∈R3×3,t∈R3
3.2 左上 3x3 子矩阵分解
设投影矩阵左上 3x3 子矩阵为:
M=P0:3,0:3 M = P_{0:3,0:3} M=P0:3,0:3
函数会对 M 做 RQ 分解:
M=KR M = K R M=KR
其中 K 为上三角矩阵,R 为正交矩阵。
3.3 相机中心与平移向量
transVect 保存的是齐次相机中心向量,可写为:
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
再由相机中心恢复外参平移:
t=−RC t = -R C t=−RC
3.4 Euler 角与轴矩阵
rotMatrixX / rotMatrixY / rotMatrixZ 与 eulerAngles 表示一种合法的欧拉分解结果。由于欧拉角表示并不唯一,同一姿态可能对应多组等价解。
4. 函数原理说明
该重载可以理解为“Mat 风格的投影矩阵分解入口”:
- 将
InputArray转换为内部可处理的Mat。 - 分离出左上 3x3 子矩阵,调用
RQDecomp3x3获得cameraMatrix和rotMatrix。 - 对 3x4 投影矩阵做齐次分解,提取相机中心并写入
transVect。 - 若传入
rotMatrixX / rotMatrixY / rotMatrixZ / eulerAngles,则同步写出这些可选结果。
对于初学者,最重要的是记住两点:
transVect不是直接的三维平移向量,而是齐次相机中心。cameraMatrix与rotMatrix才是最常用的核心输出。
5. 参数含义解析
| 参数名 | 类型 | 必填 | 含义 | 典型形状 |
|---|---|---|---|---|
| projMatrix | InputArray | 是 | 3x4 投影矩阵 | 3x4 |
| cameraMatrix | OutputArray | 是 | 输出相机内参矩阵 | 3x3 |
| rotMatrix | OutputArray | 是 | 输出旋转矩阵 | 3x3 |
| transVect | OutputArray | 是 | 输出齐次相机中心 | 4x1 |
| rotMatrixX | OutputArray? | 否 | 绕 X 轴的旋转矩阵 | 3x3 |
| rotMatrixY | OutputArray? | 否 | 绕 Y 轴的旋转矩阵 | 3x3 |
| rotMatrixZ | OutputArray? | 否 | 绕 Z 轴的旋转矩阵 | 3x3 |
| eulerAngles | OutputArray? | 否 | 三个欧拉角(度) | 3x1 |
6. 应用场景列表
| 场景名 | 场景说明 | 价值 |
|---|---|---|
| 场景A:InputArray 完整分解 | 输出所有核心与可选结果 | 适合教学总览 |
| 场景B:相机中心与平移恢复 | 验证 transVect 的含义 | 帮助理解齐次坐标 |
| 场景C:轴矩阵与 Euler 角 | 对比 RQ 分解结果 | 解释姿态表示 |
| 场景D:扰动敏感性分析 | 输入轻微噪声后再分解 | 观察稳定性 |
7. 函数使用示例(每个场景一个完整示例)
说明:以下示例均为完整 Program.cs 的核心逻辑,注释采用教学风格。为突出 API 重点,省略了少量重复的打印辅助代码。
7.1 场景A:InputArray 完整分解
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 },
};
// Mat.FromPixelData 把托管二维数组包装成 OpenCV 可直接读取的 Mat。
// 这里使用 3x4 的 CV_64FC1,是因为投影矩阵必须是双精度单通道。
using var projMat = Mat.FromPixelData(3, 4, MatType.CV_64FC1, projMatrix);
// InputArray 是 OpenCV 的通用输入包装器。
// 这里再包一层,是为了命中 DecomposeProjectionMatrix 的 InputArray 重载。
using var projInput = InputArray.Create((Mat)projMat);
// 以下几个 Mat<double> 是输出容器,调用结束后会装着分解结果。
using var cameraMat = new Mat<double>();
using var rotMat = new Mat<double>();
using var transMat = new Mat<double>();
// 可选输出:三个轴向旋转矩阵和 Euler 角。
// 即使某个场景暂时不展示,也建议在教学示例里把它们准备好,方便观察完整结果。
using var rotXMat = new Mat<double>();
using var rotYMat = new Mat<double>();
using var rotZMat = new Mat<double>();
using var eulerMat = new Mat<double>();
// 调用目标函数:输入投影矩阵,输出相机内参、旋转矩阵、相机中心以及可选的轴矩阵/Euler 角。
// OutputArray.Create(...) 的作用是把 Mat 包装成 OpenCV 需要的输出参数。
Cv2.DecomposeProjectionMatrix(
projInput,
OutputArray.Create(cameraMat),
OutputArray.Create(rotMat),
OutputArray.Create(transMat),
OutputArray.Create(rotXMat),
OutputArray.Create(rotYMat),
OutputArray.Create(rotZMat),
OutputArray.Create(eulerMat));
// ToRectangularArray() 把 Mat 转回 double[,],这样更适合初学者在控制台中观察。
Console.WriteLine("cameraMatrix:");
PrintMatrix(cameraMat.ToRectangularArray());
Console.WriteLine();
// 旋转矩阵也是同样方式打印,方便和教案公式逐项对照。
Console.WriteLine("rotMatrix:");
PrintMatrix(rotMat.ToRectangularArray());
}
private static void PrintMatrix(double[,] matrix)
{
// 这个小工具只是为了把矩阵打印得更整齐,便于肉眼检查每一行每一列。
for (var row = 0; row < matrix.GetLength(0); row++)
{
Console.Write("[");
for (var col = 0; col < matrix.GetLength(1); col++)
{
Console.Write(matrix[row, col].ToString("F6"));
if (col < matrix.GetLength(1) - 1)
{
Console.Write(", ");
}
}
Console.WriteLine("]");
}
}
}
7.2 场景B:相机中心与平移恢复
using OpenCvSharp;
// 为了让这个示例可以单独复制运行,这里仍然先准备同一份投影矩阵样本。
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 },
};
// 把数组包装成 Mat,再交给 InputArray 版本的分解函数。
using var projMat = Mat.FromPixelData(3, 4, MatType.CV_64FC1, projMatrix);
using var projInput = InputArray.Create((Mat)projMat);
using var cameraMat = new Mat<double>();
using var rotMat = new Mat<double>();
using var transMat = new Mat<double>();
// 执行分解后,transMat 中保存的是齐次相机中心。
Cv2.DecomposeProjectionMatrix(projInput, OutputArray.Create(cameraMat), OutputArray.Create(rotMat), OutputArray.Create(transMat));
// transVect 是 4x1 齐次向量,最后一个分量是 w。
// 先除以 w,才能得到真正的三维相机中心 C。
var transVect = transMat.ToRectangularArray();
var cameraCenter = new[]
{
transVect[0, 0] / transVect[3, 0],
transVect[1, 0] / transVect[3, 0],
transVect[2, 0] / transVect[3, 0],
};
// rotMatrix 是 3x3 旋转矩阵。
// 根据相机几何关系,平移向量可以由 t = -R * C 反推出来。
var rotMatrix = rotMat.ToRectangularArray();
var translation = new double[3];
for (var row = 0; row < 3; row++)
{
// 这里显式展开矩阵乘法,目的是让初学者看清楚每一个分量是怎么来的。
var sum = 0.0;
for (var col = 0; col < 3; col++)
{
sum += rotMatrix[row, col] * cameraCenter[col];
}
// 结果前面加负号,就是外参平移向量 t。
translation[row] = -sum;
}
7.3 场景C:轴矩阵与 Euler 角对比
using OpenCvSharp;
// 这里沿用 7.1 的 projMatrix 样本;如果单独复制,请先准备同样的 3x4 投影矩阵。
// 只取投影矩阵左上 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);
7.4 场景D:扰动敏感性分析
using OpenCvSharp;
// 这里继续沿用 7.1 / 7.2 中同一组投影矩阵样本,只是复制一份出来做扰动。
// 先复制原始矩阵,避免修改原始样本数据后影响后续对比。
var noisyProj = (double[,])projMatrix.Clone();
// 这里给投影矩阵加一点点噪声,用来模拟真实标定/测量过程中的浮点扰动。
noisyProj[0, 3] += 0.12;
noisyProj[1, 3] -= 0.08;
// 再次分解扰动后的投影矩阵,观察输出变化是否稳定。
Cv2.DecomposeProjectionMatrix(noisyProj, out var noisyK, out var noisyR, out var noisyT);
8. 函数使用注意事项
projMatrix必须是 3x4。transVect是齐次向量,使用前要先按w归一化。eulerAngles和rotMatrixX/Y/Z不是唯一解,不要硬性假设只有一组结果。- 只看欧拉角很容易误判,建议同时看矩阵误差和重建误差。
9. 参数调优建议
- 输入投影矩阵尽量使用稳定尺度,避免极小或极大数值带来条件数问题。
- 优先检查三项指标:
cameraMatrix误差、rotMatrix误差和P重建误差。 - 若要比较姿态结果,优先比较旋转矩阵,再看 Euler 角。
10. 示例代码运行说明(按场景关键逻辑)
场景A(完整分解)
- 准备 3x4 投影矩阵。
- 创建
InputArray与OutputArray容器。 - 调用函数并读取所有输出。
场景B(相机中心恢复)
- 取出
transVect。 - 先做齐次归一化得到
cameraCenter。 - 再用
t = -R * C恢复平移向量。
场景C(轴矩阵对比)
- 对左上 3x3 子矩阵调用
RQDecomp3x3。 - 对比
rotMatrixX/Y/Z和eulerAngles。 - 确认结果与
DecomposeProjectionMatrix保持一致。
场景D(扰动敏感性)
- 对投影矩阵施加微扰。
- 再次调用分解函数。
- 对比前后结果变化。
11. 常见错误排查
- 错误:输入矩阵不是 3x4
- 排查:确认投影矩阵维度是否正确。
- 错误:把
transVect误认为普通平移向量- 排查:先除以
w再恢复三维相机中心。
- 排查:先除以
- 错误:Euler 角和自己的手工公式对不上
- 排查:Euler 角存在多解,优先比较旋转矩阵。
- 错误:重建误差略大
- 排查:确认
P = K [R | t]里的t = -R C是否按正确顺序恢复。
- 排查:确认
对应 WPF 演示控件与样例代码:
- Features/Cv2DecomposeProjectionMatrixInputArray/Cv2DecomposeProjectionMatrixInputArrayControl.xaml
- Samples/Cv2DecomposeProjectionMatrix/DecomposeProjectionMatrixInputArrayDecomposeSample.cs
- Samples/Cv2DecomposeProjectionMatrix/DecomposeProjectionMatrixInputArrayCameraCenterSample.cs
- Samples/Cv2DecomposeProjectionMatrix/DecomposeProjectionMatrixInputArrayAxisSample.cs
- Samples/Cv2DecomposeProjectionMatrix/DecomposeProjectionMatrixInputArrayPerturbationSample.cs
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)