代码地址:在公众号「3D视觉工坊」,后台回复「逆相机法」,即可直接下载。
注:本文的理论主要来自于参考文献1、2,代码来源于南京理工大学左超老师课题组发表的参考文献2中,笔者在其基础上稍作修改,便于大家更好理解。
01 理论
1.1 单目标定
1.3 重建原理
需要说明的是,这个公式是对每个像素点单独计算的,这在代码里可以体现。
02 实践
2.1 标定
相机、投影仪的成像误差 相机、投影仪的相对位置关系
(1) 相机标定
num_x = 11; %number of circles in the x direction of the calibration board
num_y = 9; %number of circles in the y direction of the calibration board
dist_circ = 25; % 标定板
disp('开始相机标定...');
% 角点的像素坐标系
load('camera_imagePoints.mat'); % load the camera image points of the centers
% 角点的世界坐标系
worldPoints = generateCheckerboardPoints([num_y+1,num_x+1], dist_circ);
% 标定相机
[cameraParams,imagesUsed,estimationErrors] = estimateCameraParameters(imagePoints,worldPoints, 'EstimateTangentialDistortion', true);
figure;showReprojectionErrors(cameraParams);title('相机标定的重投影误差');
figure;showExtrinsics(cameraParams);title('相机标定的外参');
注:无论是棋盘格、环形标定板,标定的原理都是一样的,都是提取到角点/中心点的相机像素坐标下坐标,然后利用张正友标定法进行标定。
% save Rc_1, Tc_1, KK, inv_KK,
% 相机相对于世界坐标系原点的位姿(旋转+平移=外参)
Rc_1 = cameraParams.RotationMatrices(:,:,1);
Rc_1 = Rc_1'; % 转置
Tc_1 = cameraParams.TranslationVectors(1, :);
Tc_1 = Tc_1';
% 相机内参
KK = cameraParams.IntrinsicMatrix';
save('CamCalibResult.mat', 'Rc_1', 'Tc_1', 'KK');
(2) 投影仪标定
投影仪标定会复杂一点,因为它无法直接看到圆形标定板中心的像素,这个其实也很简单,需要在相机的帮助下,我们投影相移图案,利用相移法获得。
圆心的世界坐标,已知,同相机 圆心的像素坐标,未知,因为无法直接看到标定板
通过投影仪项标定板投射相移图案,相机拍摄,解码获取相位图 ==取每个圆心的相位,跟理想的相位图(投影仪)做匹配,得到圆心在投影仪下的像素坐标==
%% step2: 标定投影仪
disp('开始投影仪标定...');
% 加载投影仪下圆心的像素坐标
load('projector_imagePoints.mat'); % load the projector image points of the centers
% 圆心的世界坐标
worldPoints = generateCheckerboardPoints([num_y+1,num_x+1], dist_circ);
[prjParams,imagesUsed,estimationErrors] = estimateCameraParameters(prjPoints,worldPoints, 'EstimateTangentialDistortion', true);
figure; showReprojectionErrors(prjParams); title('投影仪的重投影误差');
figure; showExtrinsics(prjParams);title('投影仪的外参');
% 保存参数
Rc_1 = prjParams.RotationMatrices(:,:,1);
Rc_1 = Rc_1';
Tc_1 = prjParams.TranslationVectors(1, :);
Tc_1 = Tc_1';
KK = prjParams.IntrinsicMatrix';
save('PrjCalibResult.mat', 'Rc_1', 'Tc_1', 'KK');
%% step1: input parameters
width = 640; % camera width
height = 480; % camera height
prj_width = 912; % projector width
%camera: Projection matrix Pc
load('CamCalibResult.mat');
Kc = KK; % 相机内参
Ac = Kc * [Rc_1, Tc_1]; % 计算相机投影矩阵
%projector: Projection matrix Ap
load('PrjCalibResult.mat');
Kp = KK; % 投影仪内参
Ap = Kp * [Rc_1, Tc_1]; % 计算投影仪投影矩阵
Ac = Kc * [Rc_1, Tc_1];
Ap = Kp * [Rp_1, Rc_2];
% 条纹频率64,也是间距(一个周期由64个像素组成)用于计算绝对相位,另外的频率1、8用于包裹相位展开
f = 64; % 条纹频率(单个周期条纹的像素个数)
load('up_test_obj.mat');
up_test_obj = up_test_obj / f; % 将相位归一化到[0, 2pi]之间
figure; imshow(up_test_obj / (2 * pi)); colorbar; title("相位图, freq=" + num2str(f));
figure; mesh(up_test_obj); colorbar; title("相位图, freq=" + num2str(f));
% 计算投影仪坐标
x_p = up_test_obj / (2 * pi) * prj_width;
3D重建
Xws = nan(height, width);
Yws = nan(height, width);
Zws = nan(height, width);
for y = 1:height
for x = 1:width
if ~isnan(up_test_obj(y, x))
uc = x - 1;
vc = y - 1;
up = (x_p(y, x) - 1);
Eq. (32) in the reference paper.
A = [Ac(1,1) - Ac(3,1) * uc, Ac(1,2) - Ac(3,2) * uc, Ac(1,3) - Ac(3,3) * uc;
- Ac(3,1) * vc, Ac(2,2) - Ac(3,2) * vc, Ac(2,3) - Ac(3,3) * vc;
- Ap(3,1) * up, Ap(1,2) - Ap(3,2) * up, Ap(1,3) - Ap(3,3) * up];
b = [Ac(3,4) * uc - Ac(1,4);
* vc - Ac(2,4);
* up - Ap(1,4)];
XYZ_w = inv(A) * b;
x) = XYZ_w(1); Yws(y, x) = XYZ_w(2); Zws(y, x) = XYZ_w(3);
end
end
end
% 点云显示
xyzPoints(:, 1) = Xws(:);
xyzPoints(:, 2) = Yws(:);
xyzPoints(:, 3) = Zws(:);
ptCloud = pointCloud(xyzPoints);
xlimits = [min(Xws(:)), max(Xws(:))];
ylimits = [min(Yws(:)), max(Yws(:))];
zlimits = ptCloud.ZLimits;
player = pcplayer(xlimits,ylimits,zlimits);
xlabel(player.Axes,'X (mm)');
ylabel(player.Axes,'Y (mm)');
zlabel(player.Axes,'Z (mm)');
view(player,ptCloud);
03 问题
怎么投影图案,求解绝对相位? 怎么帮助投影仪“看见”标定板?进行投影仪标定? 精度如何提升,比如进行gamma校正?
04 参考文献
GPU-assisted high-resolution, real-time 3-D shape measurement,Song Zhang,OPTICS EXPRESS,2006 Calibration of fringe projection profilometry: A comparative review,Feng Shiji,Chao Zuo,Optics and Lasers in Engineering,2021
重磅!3DCVer-学术论文写作投稿 交流群已成立
扫码添加小助手微信,可申请加入3D视觉工坊-学术论文写作与投稿 微信交流群,旨在交流顶会、顶刊、SCI、EI等写作与投稿事宜。
同时也可申请加入我们的细分方向交流群,目前主要有3D视觉、CV&深度学习、SLAM、三维重建、点云后处理、自动驾驶、多传感器融合、CV入门、三维测量、VR/AR、3D人脸识别、医疗影像、缺陷检测、行人重识别、目标跟踪、视觉产品落地、视觉竞赛、车牌识别、硬件选型、学术交流、求职交流、ORB-SLAM系列源码交流、深度估计等微信群。
一定要备注:研究方向+学校/公司+昵称,例如:”3D视觉 + 上海交大 + 静静“。请按照格式备注,可快速被通过且邀请进群。原创投稿也请联系。
▲长按加微信群或投稿 ▲长按关注公众号
▲长按关注公众号