Matlab相机标定例程中去畸变过程详解+程序代码实现(如何调用源程序)

最近在研究通过Matlab标定程序获得相机内参以及畸变参数之后,如何实现对一张新拍摄的照片进行去畸变。由于Matlab标定程序只能显示标定所用的图片的去畸变图像,并不能直接对由该相机拍摄的一张新图片进行去畸变,所以就想着如何从源程序入手,写一小段代码,脱离标定例程程序,直接调用去畸变源程序

首先,为了确保调用的源程序可用,大家可以先下载这个工具箱,免费下载地址:http://www.vision.caltech.edu/bouguetj/calib_doc/download/index.html,该工具箱加压后,将其路径添加到Matlab中,可直接调用其中的一些源程序。(该工具箱不是Matlab自带的相机标定,具体使用可以参考:https://www.cnblogs.com/czaoth/p/6708631.html)而本次调用的去畸变源程序主要为工具箱中的rect函数apply_distortion两个函数。(如果不想下载工具箱,直接赋值本文后面的源程序代码,创建该函数即可)

在已知内参矩阵和畸变参数的情况下去畸变的步骤:

1.首先将像素坐标系转换至相机坐标系

相信大家之前对相机标定都有一定的了解,其中内参矩阵是指摄像机坐标系到图像坐标系再到像素坐标系的转换矩阵。具体为:设摄像机坐标系下的齐次坐标为(x,y,z,1),像素坐标系的齐次坐标为(u,v,1),则有

则(x,y,z,1)'  =  inv(A)*(u,v,1)'。

2.在相机坐标系下去畸变 

由畸变模型:

xcorrected = x(1+k1r2+k2r4+k3r6)

ycorrected = y(1+k1r2+k2r4+k3r6) 可得失真图像像素点(x,y)在去畸变之后的位置(xcorrected,ycorrected),值得注意的是,该过程是对失真图像所有像素点进行操作,并不只是对提取的棋盘格角点进行操作。

其中r2 = x^2+y^2,k1,k2,k3为径向畸变参数,切向畸变在本次应用中不考虑,详细说明可以看这篇博客:https://blog.csdn.net/JennyBi/article/details/85764988

3.将校正后的相机坐标系转回像素坐标系

主要通过下式转换:

u = fx*xcorrected+cx;

v = fy*ycorrected+cy;

4.根据失真图像在校正后的像素坐标位置进行插值处理(赋值)

该程序里插值的方法主要是通过中心像素点左上角(left-up)、右上角(right-up)、左下角(left-down)、右下角的值(right-down),对中心像素点的亮度值赋值。具体如下:

a1 = (1 - alpha_y).*(1 - alpha_x);
a2 = (1 - alpha_y).*alpha_x;
a3 = alpha_y .* (1 - alpha_x);
a4 = alpha_y .* alpha_x;

Irec(ind_new) = a1 .* I(ind_lu) + a2 .* I(ind_ru) + a3 .* I(ind_ld) + a4 .* I(ind_rd);

其中alpha_x由步骤三中的u计算得出:alpha_x=u-floor(u);同理,alpha_y=v-floor(v)

至此,去畸变完成。上述过程也就是rect函数的流程,下面给出具体代码参考。

load cameraParams;%加载标定的参数结果
intrinx = cameraParams.IntrinsicMatrix';
I = imread('abc_3.bmp');
I = I(:,:,2);%除此之外,如果对三个通道同时处理,那么出来的就是彩色图

fx = intrinx(1,1);
fy = intrinx(2,2);
fc = [fx fy];
cx = intrinx(1,3);
cy = intrinx(2,3);
cc = [cx cy];
alpha_c = 0;
k21 = cameraParams.RadialDistortion(1);
k22 = cameraParams.RadialDistortion(2);
k23 = cameraParams.RadialDistortion(3);
kc = [k21 k22 k23 0 0];%k1,k2,k3,p1,p2
KK = [fc(1) alpha_c*fc(1) cc(1);0 fc(2) cc(2) ; 0 0 1];
[I2] = rect(double(I),eye(3),fc,cc,kc,alpha_c,KK);%去畸变函数

figure();
image(I2);
colormap(gray(256));
title('Undistorted image');

rect函数

function [Irec] = rect(I,R,f,c,k,alpha,KK_new);


if nargin < 5,
   k = [0;0;0;0;0];
   if nargin < 4,
      c = [0;0];
      if nargin < 3,
         f = [1;1];
         if nargin < 2,
            R = eye(3);
            if nargin < 1,
               error('ERROR: Need an image to rectify');
            end;
         end;
      end;
   end;
end;


if nargin < 7,
   if nargin < 6,
        KK_new = [f(1) 0 c(1);0 f(2) c(2);0 0 1];
   else
       KK_new = alpha; % the 6th argument is actually KK_new   
   end;
   alpha = 0;
end;

 

% Note: R is the motion of the points in space
% So: X2 = R*X where X: coord in the old reference frame, X2: coord in the new ref frame.


if ~exist('KK_new'),
   KK_new = [f(1) alpha*f(1) c(1);0 f(2) c(2);0 0 1];
end;


[nr,nc] = size(I);%畸变图像尺寸

Irec = 255*ones(nr,nc);%创建去畸变图像尺寸
%% 像素系到相机坐标系的转换
[mx,my] = meshgrid(1:nc, 1:nr);
px = reshape(mx',nc*nr,1);%将mx’按列顺序,将其变为nc*nr行,1列的矩阵
py = reshape(my',nc*nr,1);

rays = inv(KK_new)*[(px - 1)';(py - 1)';ones(1,length(px))];


% Rotation: (or affine transformation):

rays2 = R'*rays;

x = [rays2(1,:)./rays2(3,:);rays2(2,:)./rays2(3,:)];
%% 在相机坐标系下去畸变(主要是找失真像素点在未失真情况下上的坐标)

% Add distortion:
xd = apply_distortion(x,k);

%% 将相机坐标系转回像素坐标系
% Reconvert in pixels:

px2 = f(1)*(xd(1,:)+alpha*xd(2,:))+c(1);
py2 = f(2)*xd(2,:)+c(2);

%% 对像素值进行插值处理
% Interpolate between the closest pixels:

px_0 = floor(px2);


py_0 = floor(py2);
py_1 = py_0 + 1;


good_points = find((px_0 >= 0) & (px_0 <= (nc-2)) & (py_0 >= 0) & (py_0 <= (nr-2)));

px2 = px2(good_points);
py2 = py2(good_points);
px_0 = px_0(good_points);
py_0 = py_0(good_points);

alpha_x = px2 - px_0;
alpha_y = py2 - py_0;

a1 = (1 - alpha_y).*(1 - alpha_x);
a2 = (1 - alpha_y).*alpha_x;
a3 = alpha_y .* (1 - alpha_x);
a4 = alpha_y .* alpha_x;

ind_lu = px_0 * nr + py_0 + 1;
ind_ru = (px_0 + 1) * nr + py_0 + 1;
ind_ld = px_0 * nr + (py_0 + 1) + 1;
ind_rd = (px_0 + 1) * nr + (py_0 + 1) + 1;

ind_new = (px(good_points)-1)*nr + py(good_points);

 

Irec(ind_new) = a1 .* I(ind_lu) + a2 .* I(ind_ru) + a3 .* I(ind_ld) + a4 .* I(ind_rd);

 

return;


% Convert in indices:

fact = 3;

[XX,YY]= meshgrid(1:nc,1:nr);
[XXi,YYi]= meshgrid(1:1/fact:nc,1:1/fact:nr);

%tic;
Iinterp = interp2(XX,YY,I,XXi,YYi); 
%toc

[nri,nci] = size(Iinterp);


ind_col = round(fact*(f(1)*xd(1,:)+c(1)))+1;
ind_row = round(fact*(f(2)*xd(2,:)+c(2)))+1;

good_points = find((ind_col >=1)&(ind_col<=nci)&(ind_row >=1)& (ind_row <=nri));
 

apply_distort函数

function [xd,dxddk] = apply_distortion(x,k)


% Complete the distortion vector if you are using the simple distortion model:
length_k = length(k);
if length_k <5 ,
    k = [k ; zeros(5-length_k,1)];
end;


[m,n] = size(x);

% Add distortion:

r2 = x(1,:).^2 + x(2,:).^2;

r4 = r2.^2;

r6 = r2.^3;


% Radial distortion:

cdist = 1 + k(1) * r2 + k(2) * r4 + k(3) * r6;
% cdist = 1 + k(1) * r2 + k(2) * r4 + k(5) * r6;%原版

if nargout > 1,
    dcdistdk = [ r2' r4' zeros(n,2) r6'];
end;


xd1 = x .* (ones(2,1)*cdist);

coeff = (reshape([cdist;cdist],2*n,1)*ones(1,3));

if nargout > 1,
    dxd1dk = zeros(2*n,5);
    dxd1dk(1:2:end,:) = (x(1,:)'*ones(1,5)) .* dcdistdk;
    dxd1dk(2:2:end,:) = (x(2,:)'*ones(1,5)) .* dcdistdk;
end;


% tangential distortion:

a1 = 2.*x(1,:).*x(2,:);
a2 = r2 + 2*x(1,:).^2;
a3 = r2 + 2*x(2,:).^2;

% delta_x = [k(3)*a1 + k(4)*a2 ;
%    k(3) * a3 + k(4)*a1];%原版
delta_x = [k(4)*a1 + k(5)*a2 ;
   k(4) * a3 + k(5)*a1];

aa = (2*k(3)*x(2,:)+6*k(4)*x(1,:))'*ones(1,3);
bb = (2*k(3)*x(1,:)+2*k(4)*x(2,:))'*ones(1,3);
cc = (6*k(3)*x(2,:)+2*k(4)*x(1,:))'*ones(1,3);

if nargout > 1,
    ddelta_xdk = zeros(2*n,5);
    ddelta_xdk(1:2:end,3) = a1';
    ddelta_xdk(1:2:end,4) = a2';
    ddelta_xdk(2:2:end,3) = a3';
    ddelta_xdk(2:2:end,4) = a1';
end;

xd = xd1 + delta_x;

if nargout > 1,
    dxddk = dxd1dk + ddelta_xdk ;
    if length_k < 5,
        dxddk = dxddk(:,1:length_k);
    end;
end;


return;

% Test of the Jacobians:

n = 10;

lk = 1;

x = 10*randn(2,n);
k = 0.5*randn(lk,1);

[xd,dxddk] = apply_distortion(x,k);


% Test on k: OK!!

dk = 0.001 * norm(k)*randn(lk,1);
k2 = k + dk;

[x2] = apply_distortion(x,k2);

x_pred = xd + reshape(dxddk * dk,2,n);


norm(x2-xd)/norm(x2 - x_pred)

 

实验结果 

 失真图像

去畸变之后 

  • 15
    点赞
  • 91
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
### 回答1: OpenCV是一个开源的计算机视觉库,提供了很多计算机视觉相关的函数和算法。相机标定计算机视觉中一个重要的步骤,用于确定相机的内参和外参参数,从而提高计算机视觉的精确度。 opencv相机标定例程是使用OpenCV库函数进行相机标定的示例代码。它包含了一系列对相机进行标定的步骤: 1. 收集标定图像:首先需要收集一组标定图像,这些图像应该包含特定的模式,比如棋盘格。这些图像可以通过相机拍摄或者从其他来源获取。 2. 提取角点:使用OpenCV的函数在标定图像中提取角点。这些角点的提取可以通过函数`findChessboardCorners`来实现,它会返回检测到的角点的坐标。 3. 标定相机:使用提取到的角点坐标,调用OpenCV的函数`calibrateCamera`完成相机的标定。该函数将返回相机的内参矩阵,畸变系数和旋转矩阵。 4. 评估标定结果:标定完成后,可以使用OpenCV的函数`getOptimalNewCameraMatrix`来优化内参矩阵,并使用`initUndistorRectifyMap`函数生成畸变校正的映射。 5. 应用标定结果:使用标定得到的内参矩阵和畸变系数,可以对图像进行畸变校正,使得图像不再有畸变。 OpenCV相机标定例程提供了一个完整的流程,帮助用户准确地对相机进行标定,从而提高计算机视觉算法的准确性和鲁棒性。同时,用户可以根据自己的需求,对例程进行修改和扩展,以适应具体的应用场景。 ### 回答2: OpenCV相机标定例程是一个用于校准相机的程序。相机标定是指确定相机内部和外部参数的过程,以便在三维世界中更准确地测量物体或跟踪物体。 首先,我们需要收集一些被称为棋盘格的二维图像。在例程中,我们使用棋盘格作为标定目标,因为它具有规则的结构和易于检测的特征。然后,我们将这些图像加载到程序中进行处理。 接下来,我们使用OpenCV的标定函数来计算相机的内部参数,例如焦距、主点坐标和径向畸变系数。这些参数将用于校正图像并更准确地测量物体。 在这个例程中,我们使用棋盘格的角点作为特征点来进行标定。我们可以通过使用OpenCV的函数在棋盘格图像中检测角点。然后,我们将这些角点的二维像素坐标与它们在三维空间中的真实坐标进行匹配。 通过对多个图像进行角点的检测和匹配,我们可以获得足够的数据来计算相机的内部参数。一旦内部参数被计算出来,我们就可以将其保存在文件中以备将来使用。 通过相机标定例程,我们可以获得相机的校准参数,从而提高图像的质量和精度。这在计算机视觉应用中特别重要,例如目标跟踪、SLAM(同时定位与地图构建)和立体视觉。 ### 回答3: OpenCV相机标定例程是一种用于标定相机内外参数的方法。相机标定是指确定相机的一些固有参数,如焦距、畸变等,以便于后续图像处理或计算机视觉任务的进行。 OpenCV提供了一个相机标定例程函数`calibrateCamera()`,通过将相机拍摄的多张已知世界坐标和相应图像坐标的图像进行处理,来估计相机的内外参数。 首先,需要收集一组已知世界坐标和相应图像坐标的图像样本。已知世界坐标通常需要以某个坐标系为基准,例如一个棋盘格。然后,通过使用OpenCV提供的函数`findChessboardCorners()`来检测图像中棋盘格角点的位置。 接下来,通过调用`calibrateCamera()`函数,将已知的世界坐标和对应的图像坐标作为输入参数,获得相机的内外参数。该函数将返回相机的一些参数,如相机矩阵、畸变系数、旋转矩阵和平移向量等。 最后,可以使用相机的内外参数进行图像处理或计算机视觉任务。例如,可以通过使用函数`undistort()`对相机采集到的图像进行去畸变操作,从而使图像更符合实际场景。 总之,OpenCV相机标定例程是一种用于确定相机内外参数的方法,通过使用已知的世界坐标和图像坐标的样本,可以得到相机的一些固有参数,以便于后续的图像处理或计算机视觉任务。
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值