一、标定板拍摄图片
拍摄图片,之后放入matlab进行特征点提取,注意提取过程中可能世界坐标系下的原点不一样。一定要避免这种情况。
标定途径1:Matlab自带的APP:Camera Calibrator
把误差大的标定图片删掉,整个误差在0.2以下就行了。最后导出数据,matlab代码如下,其中K是内参矩阵(一定记得要转置)。根据你要最终测量的面,提取外参,R是旋转矩阵,t是平移向量。我这里第55张标定图片是把标定板贴在最终被侧面上测量的,所以我的外参矩阵选第55组就行。
K = cameraParams.IntrinsicMatrix'; % 内参矩阵
R = cameraParams.RotationMatrices(:,:,55); % 指定表面的旋转矩阵
t = cameraParams.TranslationVectors(55,:); % 指定表面的平移向量
标定途径2:matlab calibration toolbox
这个toolbox一般都需要自己下载安装(免费)。但是这个安装好之后,需要自己一张图一张图自己标定,我个人觉得这个非常麻烦,而且自己标定也不能保证精度。
标定途径3:matlab 代码直接计算参数
直接上代码
numImages = 55; %标定的图片总数
files = cell(1, 55);
path = 'E:\opencv_project\SPSI\spatial_phaseshift\1125\cc限';
counter=0;
% (因为我图片总共200张,命名是1-200,然后被我删掉了很多误差大的图,最后剩下55张,命名在1-200之间)
for i = 1:200 % 图片命名最大值
currentFile = fullfile(path, sprintf('%dCamera.bmp', i));
if exist(currentFile, 'file')
counter = counter + 1;
files{counter} = currentFile;
end
end
% 对所有的标定图片进行角点检测
[imagePoints, boardSize] = detectCheckerboardPoints(files);
squareSize = 5; % 标定板的小方格边长(mm)
worldPoints = generateCheckerboardPoints(boardSize, squareSize);
I = imread(files{1});
imageSize = [size(I, 1), size(I, 2)];
cameraParams = estimateCameraParameters(imagePoints, worldPoints, 'ImageSize', imageSize); %得到相机内参
% 显示误差
figure; showReprojectionErrors(cameraParams);
title("Reprojection Errors");
% 估算外参
im = imread(files{55}); % 输入要测量的最终表面的标定图
[imagePoints, boardSize] = detectCheckerboardPoints(im);
% 获取相机内参和外参
K = cameraParams.IntrinsicMatrix';
[R, t] = extrinsics(imagePoints, worldPoints, cameraParams);
二、使用内外参计算实际距离
matlab有自带的img2world2d函数,但是这个函数在Computer Vision工具包里面,这个工具包似乎只有正版matlab才能用。。。。。我们被制裁孩子的盗版matlab就用不了了。所以我分别写了一个matlab和Python的版本。
Matlab:
function worldPoints = img2world2d(imagePoints, camExtrinsics, camIntrinsics)
% imagePoints: 输入的像素坐标,Nx2 的矩阵
% camExtrinsics: 相机外参矩阵,3x4 的矩阵
% camIntrinsics: 相机内参矩阵,3x3 的矩阵
% 将图像坐标转换为归一化坐标
normalizedPoints = [imagePoints, ones(size(imagePoints, 1), 1)] / camIntrinsics';
% 添加深度信息
normalizedPoints(:, 3) = 1;
% 利用相机外参矩阵将相机坐标映射到世界坐标
worldPointsHomogeneous = (camExtrinsics \ normalizedPoints')';
% 转换为非齐次坐标
worldPoints = worldPointsHomogeneous(:, 1:3) ./ worldPointsHomogeneous(:, 4);
end
Python:
def img2world2d(p, M, K):
# 将图像坐标转换为归一化坐标
ones_column = np.ones((1))
normalizedPoints = np.dot(np.hstack((p, ones_column)) , np.linalg.inv(np.transpose(K)))
normalizedPoints = normalizedPoints.reshape(-1, 1)
M = np.delete(M,2,axis=1)
worldPointsHomogeneous = np.linalg.solve(M,normalizedPoints)
worldPoints = worldPointsHomogeneous[:2, :] / worldPointsHomogeneous[2, :].reshape(-1, 1)
return worldPoints
目前已经实现了图片坐标到世界坐标的转化。输入两个像素点p1,p2,得到两个世界坐标点w1,w2,就可以利用范数求快速求距离了。
distance = norm(w2-w1);
三、实验验证
给被测面的标定图片(就是我标定第55张),标两个点,输入,计算实际的距离。我的标定板每个格子是5mm,所以真值大概是5*6=30mm。
最终计算结果是30.0177mm,竖着的也记得验证以下:
最终计算结果是299846mm,结果非常靠谱!!!赢!!!!!以后就可以在不改变被测面和相机的前提下,对这个被测面进行任意的检测了!!!
四、参考的matlab官方demo:
(直接是运行不了的,只能学习思路)
Measuring Planar Objects with a Calibrated Camera - MATLAB & Simulink - MathWorks 中国