使用 Roll-Pitch-Yaw 角度变换图像(图像校正)

Posted

技术标签:

【中文标题】使用 Roll-Pitch-Yaw 角度变换图像(图像校正)【英文标题】:Transform Image using Roll-Pitch-Yaw angles (Image rectification) 【发布时间】:2013-12-25 01:24:18 【问题描述】:

我正在开发一个需要校正从移动相机平台拍摄的图像的应用程序。该平台测量滚动、俯仰和偏航角,我想让它看起来像从正上方拍摄的图像,通过对这些信息的某种变换。

换句话说,我想要一个平放在地面上的完美正方形,从远处以某种相机方向拍摄,然后进行变换,以便之后正方形完美对称。

我一直在尝试通过 OpenCV(C++) 和 Matlab 来做到这一点,但我似乎遗漏了一些关于如何做到这一点的基本知识。

在 Matlab 中,我尝试了以下方法:

%% Transform perspective
img = imread('my_favourite_image.jpg');
R = R_z(yaw_angle)*R_y(pitch_angle)*R_x(roll_angle);
tform = projective2d(R);   
outputImage = imwarp(img,tform);
figure(1), imshow(outputImage);

其中 R_z/y/x 是标准旋转矩阵(以度数实现)。

对于一些偏航旋转,一切正常:

R = R_z(10)*R_y(0)*R_x(0);

结果如下:

如果我尝试将图像绕 X 轴或 Y 轴旋转相同的量,我会得到如下结果:

R = R_z(10)*R_y(0)*R_x(10);

但是,如果我旋转 10 度,除以某个巨大的数字,它开始看起来不错。但话又说回来,这是一个没有任何研究价值的结果:

R = R_z(10)*R_y(0)*R_x(10/1000);

有人可以帮我理解为什么绕 X 轴或 Y 轴旋转会使转换变得疯狂吗?有没有办法解决这个问题而不用除以一些随机数和其他魔术?这是否可以使用某种欧拉参数来解决?任何帮助将不胜感激!

更新:完整设置和测量

为了完整起见,添加了完整的测试代码和初始图像,以及平台欧拉角:

代码:

%% Transform perspective
function [] = main()
    img = imread('some_image.jpg');
    R = R_z(0)*R_y(0)*R_x(10);
    tform = projective2d(R);   
    outputImage = imwarp(img,tform);
    figure(1), imshow(outputImage);
end

%% Matrix for Yaw-rotation about the Z-axis
function [R] = R_z(psi)
    R = [cosd(psi) -sind(psi) 0;
         sind(psi)  cosd(psi) 0;
         0          0         1];
end

%% Matrix for Pitch-rotation about the Y-axis
function [R] = R_y(theta)
    R = [cosd(theta)    0   sind(theta);
         0              1   0          ;
         -sind(theta)   0   cosd(theta)     ];
end

%% Matrix for Roll-rotation about the X-axis
function [R] = R_x(phi)
    R = [1  0           0;
         0  cosd(phi)   -sind(phi);
         0  sind(phi)   cosd(phi)];
end

初始图像:

在 BODY 坐标系中的相机平台测量:

Roll:     -10
Pitch:    -30
Yaw:      166 (angular deviation from north)

据我了解,偏航角与转换没有直接关系。但是,我可能对此有误。

其他信息:

我想指定使用设置的环境不包含可以可靠地用作参考的线条(海洋照片)(地平线通常不会出现在图片中)。并且初始图像中的正方形只是用来衡量变换是否正确,在真实场景中不会出现。

【问题讨论】:

你能上传原图和你的旋转矩阵实现吗? 嗨,Scap3y!我在上面的帖子中添加了该问题的完整信息和代码。 好吧,我认为你错过了一个关键步骤:找到与图像中线条水平投影相关的单应性。请参阅this link 了解如何使其工作。一旦你计算了单应性,你可以用它代替你的R矩阵,这样就可以了。 感谢先生的出色建议。我将要分析的图像中没有任何线条(实际上根本没有)。但是,我认为我可以直接从 RPY 角度创建这些校正向量。校正矩阵看起来很合理,因为它会影响变换矩阵第三行中的元素。正是这些元素实际上使当前的变换变得疯狂,除非乘以一些“随机”的小数;我相信这可能是那个/那些数字。我会回复你的结果。 啊,好吧。那会很麻烦。您可能能够在海洋图像(地平线)中获得 1 条线,但绝对不足以获得矢量(或者至少,不是对矢量的准确估计).. 一切顺利,让我知道如何事实证明。! 【参考方案1】:

所以,这就是我最终要做的事情:我认为除非您实际处理的是 3D 图像,否则校正照片的透视是一项 2D 操作。考虑到这一点,我将变换矩阵的 z 轴值替换为 0 和 1,并对图像应用 2D 仿射变换。

在测得的 Roll = -10 和 Pitch = -30 的情况下,初始图像的旋转(参见初始帖子)按以下方式完成:

R_rotation = R_y(-60)*R_x(10); 
R_2d       = [   R_rot(1,1)  R_rot(1,2) 0; 
                 R_rot(2,1)  R_rot(2,2) 0;
                 0           0          1    ] 

这意味着将相机平台旋转到虚拟相机方向,其中相机放置在场景上方,指向正下方。请注意上面矩阵中用于滚动和俯仰的值。

此外,如果旋转图像以使其与平台航向对齐,则可能会添加围绕 z 轴的旋转,给出:

R_rotation = R_y(-60)*R_x(10)*R_z(some_heading); 
R_2d       = [   R_rot(1,1)  R_rot(1,2) 0; 
                 R_rot(2,1)  R_rot(2,2) 0;
                 0           0          1    ] 

请注意,这不会改变实际图像 - 它只会旋转它。

因此,围绕 Y 轴和 X 轴旋转的初始图像如下所示:

如上所示,进行这种转换的完整代码是:

% Load image
img = imread('initial_image.jpg'); 

% Full rotation matrix. Z-axis included, but not used.
R_rot = R_y(-60)*R_x(10)*R_z(0); 

% Strip the values related to the Z-axis from R_rot
R_2d  = [   R_rot(1,1)  R_rot(1,2) 0; 
            R_rot(2,1)  R_rot(2,2) 0;
            0           0          1    ]; 

% Generate transformation matrix, and warp (matlab syntax)
tform = affine2d(R_2d);
outputImage = imwarp(img,tform);

% Display image
figure(1), imshow(outputImage);



%*** Rotation Matrix Functions ***%

%% Matrix for Yaw-rotation about the Z-axis
function [R] = R_z(psi)
    R = [cosd(psi) -sind(psi) 0;
         sind(psi)  cosd(psi) 0;
         0          0         1];
end

%% Matrix for Pitch-rotation about the Y-axis
function [R] = R_y(theta)
    R = [cosd(theta)    0   sind(theta);
         0              1   0          ;
         -sind(theta)   0   cosd(theta)     ];
end

%% Matrix for Roll-rotation about the X-axis
function [R] = R_x(phi)
    R = [1  0           0;
         0  cosd(phi)   -sind(phi);
         0  sind(phi)   cosd(phi)];
end

感谢大家的支持,希望对大家有所帮助!

【讨论】:

好的,我看到你做了 +10,因为 Roll 是 -10 (-10+10=0),而 -60 因为 Pitch 是 -30 (-30-60=-90)。但是为什么要轮换the remainder of (90-pitch) degrees in the counter-clockwise direction of the y-axis, and the positive roll angle clockwise of the x-axis?为什么不尝试将横滚和俯仰都归零呢?为什么将 Pitch 设置为 -90 度而不是 0 可以解决这个问题?【参考方案2】:

我认为您可以通过这种方式进行转换:

1) 让你有四个 3d 点 A(-1,-1,0), B(1,-1,0), C(1,1,0) 和 D(-1,1,0 )。您可以取任意 4 个非共线点。它们与图像无关。

2)您有变换矩阵,因此您可以通过将点坐标乘以变换矩阵来设置相机。您将获得相对于相机位置/方向的 3d 坐标。

3) 您需要将点投影到屏幕平面。最简单的方法是使用正投影(忽略深度坐标)。在这个阶段,您将获得变换点的 2D 投影。

4) 一旦你有 2 组 2D 点坐标(步骤 1 中没有第三坐标的集合和步骤 3 中的集合),你可以以标准方式计算单应矩阵。

5) 对图像应用逆同形变换。

【讨论】:

你说得对,这是 2D 变换。谢谢! :)【参考方案3】:

您需要估计一个单应性。对于现成的 Matlab 解决方案,请参阅来自 http://www.robots.ox.ac.uk/~vgg/hzbook/code/ 的函数 vgg_H_from_x_lin.m

有关理论,请研究计算机视觉教科书,例如在http://szeliski.org/Book/ 或http://programmingcomputervision.com/downloads/ProgrammingComputerVision_CCdraft.pdf 第 3 章中免费提供的一本

【讨论】:

【参考方案4】:

也许由于我对相机参数的误解,我的回答不正确,但我想知道 Yaw/Pitch/Roll 是否与您的对象的位置有关。我用general rotations的公式,我的代码如下(旋转函数R_xR_yR_z是从你那里复制过来的,我这里没有贴)

close all
file='http://i.stack.imgur.com/m5e01.jpg'; % original image
I=imread(file);

R_rot = R_x(-10)*R_y(-30)*R_z(166);
R_rot = inv(R_rot);

R_2d  = [   R_rot(1,1)  R_rot(1,2) 0; 
            R_rot(2,1)  R_rot(2,2) 0;
            0           0          1    ]; 


T = maketform('affine',R_2d);

transformedI = imtransform(I,T);
        figure, imshow(I), figure, imshow(transformedI)

结果:

这表明您仍然需要一些旋转操作才能在您的脑海中获得“正确”的对齐方式(但可能不需要相机脑海中的正确位置)。 所以我把R_rot = inv(R_rot);改成R_rot = inv(R_rot)*R_x(-5)*R_y(25)*R_z(180);,现在它给了我:

看起来更像你想要的。 谢谢。

【讨论】:

感谢您的回答!刚才我或多或少地得出了相同的结论(请参阅此线程的第三篇文章)。如果您将旋转按 YXZ 顺序放置,则旋转值是有意义的,并考虑相机帧的实际目标状态是什么(相机从场景上方直接指向下方)。为此,您必须沿 y 轴的逆时针方向将相机框架旋转(90 度)度的剩余部分,并沿 x 轴的顺时针方向旋转正滚动角。偏航旋转是非常可选的,只有当你有一个寻北平台时才有意义。 :-)

以上是关于使用 Roll-Pitch-Yaw 角度变换图像(图像校正)的主要内容,如果未能解决你的问题,请参考以下文章

图像的几何变换

如何使用椭圆计算透视变换

OPENCV图像变换-1

图像旋转平移缩放变换矩阵计算及案例

利用OpenCV实现旋转文本图像矫正的原理及OpenCV代码

图片处理-opencv-12.图像傅里叶变换