使用 MATLAB 计算相机矩阵
Computing Camera Matrix Using MATLAB
我目前正在尝试给定一组世界点 (X) 及其对应的图像点 (x) 来计算相机矩阵 P。但是,在测试结果时,P(3 x 4 相机矩阵)乘以世界点并没有给我正确的对应图像点。但是,只有第一列的 PX = x。另一列不会 return 近似图像点。
代码:
X = [1 2 3; 4 5 6; 7 8 9; 1 1 1];
x = [3 2 1; 6 5 4; 1 1 1];
[mX, nX] = size(X);
[mx, nx] = size(x);
for i = 0:(nX-1)
XX{i+1} = transpose(X(1+i: 4+i));
end
for i = 0:(nx-1)
xx{i+1} = transpose(x(i+1:3+i));
end
%TODO - normalization
A = [];
%construct matrix
for i = 1:nX
A = [A; zeros(1,4) -1*(xx{i}(3)*transpose(XX{i})) xx{i}(2)*transpose(XX{i})];
A = [A; xx{i}(3)*transpose(XX{i}) zeros(1,4) -1*xx{i}(1)*transpose(XX{i})];
end
%using svd to solve for non zero solution
[u s v] = svd(A);
p = v(:, size(v,2));
p = reshape(p, 4,3)';
第一列的输出,效果很好:
>> p*XX{1}
ans =
0.0461
0.0922
0.0154
>> ans/0.0154
ans =
2.9921
5.9841
0.9974
>> xx{1}
ans =
3
6
1
第二列的输出无效:
>> p*XX{2}
ans =
0.5202
0.0867
0.1734
>> ans/0.1734
ans =
2.9999
0.5000
1.0000
>> xx{2}
ans =
6
1
2
顺便说一下,有人告诉我在计算相机矩阵之前需要对世界点和图像点进行归一化。我还没有完成这一步,也不知道该怎么做。如果这是导致问题的原因,请解释可以做什么。先感谢您。
这是因为您没有正确索引矩阵。您正在使用线性索引来访问矩阵的每一列。在这种情况下,您的 for
循环需要独立访问每一列。因此,for
循环的每次迭代都必须访问 3D 点的 4 个元素组和 2D 点的 3 个元素组。
因此,您只需为 for
循环执行此操作:
for i = 0:(nX-1)
XX{i+1} = transpose(X(4*i + 1 : 4*(i + 1)));
end
for i = 0:(nx-1)
xx{i+1} = transpose(x(3*i + 1 : 3*(i + 1)));
end
在此之后,代码应该可以正常工作。为了验证,我们可以遍历每个 3D 点并在您使用单元格时确定其等效的 2D:
out = zeros(size(xx)); % Declare output matrix
for ii = 1 : numel(XX) % For each 3D point...
out(:,ii) = p * XX{ii}; % Transform the point
out(:,ii) = out(:,ii) / out(end,ii); % Normalize
end
我们因此得到:
>> out
out =
3.0000 2.0000 1.0000
6.0000 5.0000 4.0000
1.0000 1.0000 1.0000
与你的x
比较:
>> x
x =
3 2 1
6 5 4
1 1 1
建议 - 使用矢量化
如果我能提出建议,请不要在这里使用元胞数组。您可以创建方程矩阵以使用矢量化求解。具体来说,您可以直接创建矩阵 A
而无需任何 for
循环:
A = [zeros(N, 4) -X.' bsxfun(@times, x(2,:).', X.');
X.' zeros(N, 4) bsxfun(@times, -x(1,:).', X.')];
如果您拥有 MATLAB R2016b 及更高版本,则可以使用内部广播执行此操作:
A = [zeros(N, 4) -X.' x(2,:).' .* X.';
X.' zeros(N, 4) -x(1,:).' .* X.']
请注意,由于矢量化,与原始矩阵 A
相比,您会看到行被打乱了。因为我们正在求解矩阵 A
的空值 space,所以打乱行没有任何效果。因此,您的代码可以简化为:
X = [1 2 3; 4 5 6; 7 8 9; 1 1 1];
x = [3 2 1; 6 5 4; 1 1 1];
A = [zeros(N, 4) -X.' bsxfun(@times, x(2,:).', X.');
X.' zeros(N, 4) bsxfun(@times, -x(1,:).', X.')];
% Use this for MATLAB R2016b and up
% A = [zeros(N, 4) -X.' x(2,:).' .* X.';
% X.' zeros(N, 4) -x(1,:).' .* X.']
[u, s, v] = svd(A);
p = v(:, end);
p = reshape(p, 4, 3).';
要最终计算输出矩阵,您可以使用简单的矩阵乘法。您使用单元格的事实要求您必须使用 for
循环,并且使用矩阵乘法执行此操作要快得多:
out = p * X;
然后您可以取结果的最后一行并将其他各行除以这一行。
out = bsxfun(@rdivide, out, out(end,:));
再次使用 MATLAB R2016b 及更高版本,您可以这样做:
out = out ./ out(end,:);
我目前正在尝试给定一组世界点 (X) 及其对应的图像点 (x) 来计算相机矩阵 P。但是,在测试结果时,P(3 x 4 相机矩阵)乘以世界点并没有给我正确的对应图像点。但是,只有第一列的 PX = x。另一列不会 return 近似图像点。
代码:
X = [1 2 3; 4 5 6; 7 8 9; 1 1 1];
x = [3 2 1; 6 5 4; 1 1 1];
[mX, nX] = size(X);
[mx, nx] = size(x);
for i = 0:(nX-1)
XX{i+1} = transpose(X(1+i: 4+i));
end
for i = 0:(nx-1)
xx{i+1} = transpose(x(i+1:3+i));
end
%TODO - normalization
A = [];
%construct matrix
for i = 1:nX
A = [A; zeros(1,4) -1*(xx{i}(3)*transpose(XX{i})) xx{i}(2)*transpose(XX{i})];
A = [A; xx{i}(3)*transpose(XX{i}) zeros(1,4) -1*xx{i}(1)*transpose(XX{i})];
end
%using svd to solve for non zero solution
[u s v] = svd(A);
p = v(:, size(v,2));
p = reshape(p, 4,3)';
第一列的输出,效果很好:
>> p*XX{1}
ans =
0.0461
0.0922
0.0154
>> ans/0.0154
ans =
2.9921
5.9841
0.9974
>> xx{1}
ans =
3
6
1
第二列的输出无效:
>> p*XX{2}
ans =
0.5202
0.0867
0.1734
>> ans/0.1734
ans =
2.9999
0.5000
1.0000
>> xx{2}
ans =
6
1
2
顺便说一下,有人告诉我在计算相机矩阵之前需要对世界点和图像点进行归一化。我还没有完成这一步,也不知道该怎么做。如果这是导致问题的原因,请解释可以做什么。先感谢您。
这是因为您没有正确索引矩阵。您正在使用线性索引来访问矩阵的每一列。在这种情况下,您的 for
循环需要独立访问每一列。因此,for
循环的每次迭代都必须访问 3D 点的 4 个元素组和 2D 点的 3 个元素组。
因此,您只需为 for
循环执行此操作:
for i = 0:(nX-1)
XX{i+1} = transpose(X(4*i + 1 : 4*(i + 1)));
end
for i = 0:(nx-1)
xx{i+1} = transpose(x(3*i + 1 : 3*(i + 1)));
end
在此之后,代码应该可以正常工作。为了验证,我们可以遍历每个 3D 点并在您使用单元格时确定其等效的 2D:
out = zeros(size(xx)); % Declare output matrix
for ii = 1 : numel(XX) % For each 3D point...
out(:,ii) = p * XX{ii}; % Transform the point
out(:,ii) = out(:,ii) / out(end,ii); % Normalize
end
我们因此得到:
>> out
out =
3.0000 2.0000 1.0000
6.0000 5.0000 4.0000
1.0000 1.0000 1.0000
与你的x
比较:
>> x
x =
3 2 1
6 5 4
1 1 1
建议 - 使用矢量化
如果我能提出建议,请不要在这里使用元胞数组。您可以创建方程矩阵以使用矢量化求解。具体来说,您可以直接创建矩阵 A
而无需任何 for
循环:
A = [zeros(N, 4) -X.' bsxfun(@times, x(2,:).', X.');
X.' zeros(N, 4) bsxfun(@times, -x(1,:).', X.')];
如果您拥有 MATLAB R2016b 及更高版本,则可以使用内部广播执行此操作:
A = [zeros(N, 4) -X.' x(2,:).' .* X.';
X.' zeros(N, 4) -x(1,:).' .* X.']
请注意,由于矢量化,与原始矩阵 A
相比,您会看到行被打乱了。因为我们正在求解矩阵 A
的空值 space,所以打乱行没有任何效果。因此,您的代码可以简化为:
X = [1 2 3; 4 5 6; 7 8 9; 1 1 1];
x = [3 2 1; 6 5 4; 1 1 1];
A = [zeros(N, 4) -X.' bsxfun(@times, x(2,:).', X.');
X.' zeros(N, 4) bsxfun(@times, -x(1,:).', X.')];
% Use this for MATLAB R2016b and up
% A = [zeros(N, 4) -X.' x(2,:).' .* X.';
% X.' zeros(N, 4) -x(1,:).' .* X.']
[u, s, v] = svd(A);
p = v(:, end);
p = reshape(p, 4, 3).';
要最终计算输出矩阵,您可以使用简单的矩阵乘法。您使用单元格的事实要求您必须使用 for
循环,并且使用矩阵乘法执行此操作要快得多:
out = p * X;
然后您可以取结果的最后一行并将其他各行除以这一行。
out = bsxfun(@rdivide, out, out(end,:));
再次使用 MATLAB R2016b 及更高版本,您可以这样做:
out = out ./ out(end,:);