世界坐标_世界坐标系 - CSDN
  • 世界坐标系、相机坐标系、图像平面坐标系

    万次阅读 多人点赞 2018-07-12 18:04:09
    一、四个坐标系简介和转换 相机模型为以后一切标定算法的关键,只有这边有相当透彻的理解,对以后的标定算法才能有更好的理解。本人研究了好长时间,几乎每天都重复看几遍,最终才会明白其推导过程。   &...

    一、四个坐标系简介和转换

    相机模型为以后一切标定算法的关键,只有这边有相当透彻的理解,对以后的标定算法才能有更好的理解。本人研究了好长时间,几乎每天都重复看几遍,最终才会明白其推导过程。
         我觉得首先我们要理解相机模型中的四个平面坐标系的关系:像素平面坐标系(u,v)、像平面坐标系(图像物理坐标第(x,y)、相机坐标系(Xc,Yc,Zc)和世界坐标系(Xw,Yw,Zw),在每一篇介绍相机模型的文章中都有介绍。
         我刚开始理解时,看着那一堆的公式十分的头晕,我相信很多初学者和我一样,但仔细想想,只不过是,我们假设了一些参数,使四个坐标系之间的坐标联系起来,这样我们就可以从拍摄的图片上一个点坐标一路反推出世界中的那个点的坐标,这样就达到了我们的目的,三维重建。而那些我们假设的参数,就是我们要标定的内外参数。


    1、像素坐标与像平面坐标系之间的关系 
          确定他们的关系之前,我们可以假设每一个像素在u轴和v轴方向上的物理尺寸为dx和dy。仔细看下他们的模型可以推出以下公式(这个还是比较好理解的):






    解释:1、dx,dy,u0,v0其实都是我们假设出来的参数,dxdy表示感光芯片上像素的实际大小,是连接像素坐标系和真实尺寸坐标系的,u0,v0是图像平面中心,最终是要我们求的内外参数。
    得出这个公式后我们可以运用线性代数的知识把方程用矩阵形式表示:

    当然我们也可以用另一种矩阵形式表示:

    2、相机坐标系与世界坐标系之间的关系 

         这两个坐标系之间的关系我们可以旋转矩阵R和平移矩阵T来得到以下关系:


                                           

                                                                        公式4

       解释:1、 在这个公式中,R为3*3矩阵,T为3*1,0为(0,0,0),简化用Lw表示后为4*4矩阵。

    3、成像投影关系(相机坐标系与像平面坐标系) 


         在相机模型中我们可以得到以下公式:


                                           

                                                                     公式5

               解释:1、

    同样我们用矩阵形式表示:

                                              

                                                                           公式6


    4、得到公式


    而我们可以将以上公式综合一下就可以得到:

      

    因此,内参数矩阵可以表示为:

    =


    外参矩阵可以表示为:,由旋转矩阵R和平移向量T组成


    当然在好多资料上都有这种做法:



    上图中表示的情况是像素坐标系和图像物理坐标系的两个坐标轴不是平行的关系,像素坐标系的两个坐标轴也不是垂直90°的关系,而图像物理坐标系的两个坐标轴是垂直关系。所以,我们在转换两个坐标轴的坐标之间的关系时就必须考虑像素坐标系两个坐标轴之间的夹角了。就有了上面的不同的内参矩阵,理解了就好了。


    二、图像坐标:我想和世界坐标谈谈(B)

             玉米将在这篇博文中,对图像坐标与世界坐标的这场对话中涉及的第二个问题:谈话方式,进行总结。世界坐标是怎样变换进摄像机,投影成图像坐标的呢?

            玉米做了一个简单的图示,在这里做一个提纲。图中显示,世界坐标系通过刚体变换到达摄像机坐标系,然后摄像机坐标系通过透视投影变换到达图像坐标系。可以看出,世界坐标与图像坐标的关系建立在刚体变换和透视投影变换的基础上。为了奖励刚体变和透视投影变换沟通了“世界上最远的距离”,玉米在图上奖励了他们两朵小红花。哈哈


     首先,让我们来看一下刚体变换是如何将世界坐标系与图像坐标系联系起来的吧。这里,先对刚体变换做一个介绍:

            刚体变换(regidbody motion):三维空间中, 当物体不发生形变时,对一个几何物体作旋转, 平移运动,称之为刚体变换

    因为世界坐标系和摄像机坐标都是右手坐标系,所以其不会发生形变。我们想把世界坐标系下的坐标转换到摄像机坐标下的坐标,如下图所示,可以通过刚体变换的方式。空间中一个坐标系,总可以通过刚体变换转换到另外一个个坐标系的。转一转,走一走,就到另外一个坐标系下了。以前可能是面朝大海,经过平移旋转,最终可能只能面朝冰山了,哈哈


    下面让我来看一下,二者之间刚体变化的数学表达。


    其中,XC代表摄像机坐标系,X代表世界坐标系。R代表旋转,T代表平移。R、T与摄像机无关,所以称这两个参数为摄像机的外参数(extrinsic parameter)可以理解为两个坐标原点之间的距离,因其受x,y,z三个方向上的分量共同控制,所以其具有三个自由度。

             R则为分别绕XYZ三轴旋转的效果之和。如下面所示:



     R=r1*r2*r3.其由三个方向的θ控制,故具有三个自由度。

               好了,刚体变换就讲完了。大家应该都了解,世界坐标系到摄像机坐标系之间的转换过程了吧。

              接下来,让我们看看摄像机坐标下的坐标如何投影到图像坐标系下,最终变为照片中的一个像素。这其中包含两个过程:一是从摄像机坐标到“空间图像坐标”(x,y)所发生的透视投影;二是从“连续图像坐标”到“离散图像坐标”(u,v)。后者我们已经在第一篇博文中解释过。所以在这里,主要介绍一下透视投影。

             透视投影(perspective projection): 用中心投影法将形体投射到投影面上,从而获得的一种较为接近视觉效果的单面投影图。有一点像皮影戏。它符合人们心理习惯,即离视点近的物体大,离视点远的物体小,不平行于成像平面的平行线会相交于消隐点(vanish point)。

             啰嗦这么多,其实大家看看示意图,看看公式,秒懂。


      以图中B(XB,YB)点为例,在小孔成像摄像机模型下(几何分析的最常用模型)。这里的f为摄像机的焦距,其属于摄像机的内参数(intrinsic parameter)。其在成像平面上的投影点b(xb,yb)的坐标利用简单的相似三角形比例关系很容易求出:


    上面两式也阐明了摄像机坐标与图像坐标之间的透视投影关系。

                好吧,现在玉米已经把图像坐标与世界坐标之间的这场对话所需经历的三个波折的过程加以了解释。即:刚体变换、透视投影、(x,y)换(u,v)(ps.这个在上一篇博文中讲过)。接下来玉米用一张图把三个过程连接起来。实现从世界坐标(X,Y,Z)到(u,v)之间的转换。让图像坐标与世界坐标直接对话。

             下图中的转换关系,都是用齐次坐标表达的,大家会发现这样的表达非常整洁。

             其实这张图显示的过程还有一个名字:摄像机模型(camera model)。其实也就是摄像机的几何模型了。

             将三者相乘,可以把这三个过程和在一起,写成一个矩阵:


    P就是世界坐标到图像坐标的直接联系人,P就表示了一个投影相机,有下面公式:


    注意在表示齐次坐标时,需要在符号上面加个小帽子。除去齐次坐标控制位P23P具有11个自由度。


           摄像机模型及其中涉及的坐标系等,是弄清3D重建几何框架的基础。可以把它们视为基本运算关系。后面对于三维重建几何框架的推导,都是要用到三个基本坐标系和摄像机模型的。

            </div>
                </div>
    
    展开全文
  • 世界坐标、相机坐标、图像坐标、像素坐标的原理、关系,并用matlab仿真 照相机是日常生活中最常见的。它能把三维的空间图片等比例缩小投影在照片上,称为一个二维图像。 以下我们就讲一讲原理,并相应的进行matlab...

    世界坐标、相机坐标、图像坐标、像素坐标的原理、关系,并用matlab仿真

    照相机是日常生活中最常见的。它能把三维的空间图片等比例缩小投影在照片上,称为一个二维图像。

    以下我们就讲一讲原理,并相应的进行matlab仿真。

    在学之前,先要了解几个概念:

    • 什么是世界坐标?
      • 也就是真实世界的立体空间坐标,是一个三维坐标系
      • Ow-XwYwZw :世界坐标系,描述相机位置,单位m
    • 什么是相机坐标?
      • 根据透镜成像原理,将世界坐标在照相机内呈现,是一个三维坐标系
      • Oc-XcYcZc  :相机坐标系,光心为原点,单位m
    • 什么是图像坐标?
      • 将相机呈现的三维坐标投影到屏幕上,而建立的新坐标系,不含高程信息,是一个二维坐标系
      • o-xy :图像坐标系,原点为成像平面中点,单位mm
    • 什么是像素坐标?
      • 将投影的图像坐标离散抽样形成的做种图片,是一个二维的坐标系
      • uv :像素坐标系,原点为图像左上角,单位pixel

    所以步骤流程就是:

      • step1:将世界坐标转化为相机坐标,等比例缩小,外加旋转平移,称之为刚体变换;
      • step2:将相机坐标转化为图像坐标,称为投影
      • step3:将图像坐标离散抽样

    模型如下图所示:

    step1:构造rigbt()函数

           世界坐标转化为相机坐标模型如下图所示:

     

    从世界坐标系变换到相机坐标系属于刚体变换:即物体不会发生形变,只需要进行旋转和平移。

    R:表示旋转矩阵

    T:表示偏移向量

    接下来分析旋转和平移

           坐标轴绕z轴旋转 θ=Theta(希腊字母读法,编程用的到),其几何模型如下图所示:

     

     公式满足:

     矩阵形式为:

    同理,绕x、y轴可以写成:

    所以刚体变化中旋转变换R=R1R2R3。

    平移矩阵T,则刚体变换可以写成:

    进一步转化,可以写成4阶矩阵:

    R矩阵是一个3×3矩阵,T是一个3×1矩阵,RT是一个4×4矩阵。

    matlab 仿真:

    function [RT] = rigbt(Phi, Psi, Theta, x0, y0, z0)
    %刚体变换函数:rigid body transformation
    %输入参数:
    %   φ=Phi       绕x轴转动的角度
    %   ψ=Psi       绕y轴转动的角度
    %   θ=Theta     绕z轴转动的角度
    %输出参数:
    %    RT 将坐标轴进行刚体变化(旋转+平移)成新的坐标轴
    %
    %% 程序
    R1 = [1 0 0; 0 cos(Phi) sin(Phi); 0 -sin(Phi) cos(Phi)];%绕X轴旋转
    R2 = [cos(Psi) 0 -sin(Psi); 0 1 0; sin(Psi) 0 cos(Psi)];%绕Y轴旋转
    R3 = [cos(Theta) sin(Theta) 0;-sin(Theta) cos(Theta) 0; 0 0 1 ];%绕Z轴旋转
    R = R3 * R1 * R2;%旋转矩阵(刚体变换一部分)
    T = [x0; y0; z0];%平移矩阵
    RT=[R T;0 0 0 1];%刚体变换矩阵
    return
    

    step2:构造proj()函数

    相机坐标投影成图像坐标,几何模型如下图所示:

    几何关系(相似)满足:

     

    即:

     

    代入:

    写成矩阵形式:

    matlab 仿真:

    function [Projection_Matrix] = proj(f)
    %相机坐标投影(projection)成图像坐标
    %输入参数
    %   f                   相机焦距(数,cm)
    %输出参数
    %   Projection_Matrix   投影成像(矩阵)
    Projection_Matrix = [f 0 0 0; 0 f 0 0; 0 0 1 0];
    return

     

    step3:构造pixel()函数

     xoy是图像坐标; uo_{uv}v是像素坐标; (u0,v0)是像素坐标的中心; p(x,y)是图像中的任意一点。

    模型如下:

     

    将x、y分成n份,每份长度为dx、dy。

    则几何公式为:

    转化为矩阵形式:

     

    matlab仿真:

    function [Pixel_Matrix] = pixel(dx,dy,u0,v0)
    %图像坐标离散化,转化为像素坐标
    %输入参数:
    %   dx      x轴方向上分辨率,像素大小
    %   dy      y轴方向上分辨率,像素大小
    %   (u0,v0) 参考坐标,图像平面中心
    if nargin==2
        u0 = 0;
        v0 = 0;
    end
    Pixel_Matrix = [1/dx 0 u0; 0 1/dy v0; 0 0 1];
    return 
    

    step4:前三步整合

    公式中, fx=f/dx,fy=f/dy。

    matlab 主程序

    % 将世界坐标转化为像素坐标的参数
    %% 程序
    clear all;close all; clc;
    % 参数
    Phi = pi/4;%绕x轴旋转角度
    Psi = pi/4;%绕y轴旋转角度
    Theta = pi/4;%绕z轴旋转角度
    x0 = 0;%x平移量
    y0 = 0;%y平移量
    z0 = 0;%z平移量
    f = 35e-3;%35mm相机
    dx = 0.026;%一个像素的长
    dy = 0.026;%一个像素的宽
    u0 = 0;%图像平面中心
    v0 =0;%图像平面中心
     
    %% step1:将世界坐标转化为相机坐标,等比例缩小,外加旋转平移(刚体变换)
    RT = rigbt(Phi, Psi, Theta, x0, y0, z0);%刚体变换矩阵
     
    %% step2:将相机坐标转化为图像坐标(投影)
    Projection_Matrix = proj(f);%投影矩阵
     
    %% step3:将图像坐标离散抽样
    Pixel_Matrix = pixel(dx,dy,u0,v0);%1像素=0.635厘米÷24≈0.026458厘米
    %% 数据整合
    Camera_Internal_Parameters = Pixel_Matrix * Projection_Matrix;%相机内参数
    Camera_External_Parameters = RT;%相机外参数
    %清除不用的变量
    clearvars -except Camera_Internal_Parameters Camera_External_Parameters
    %相机参数
    Camera_Parameters = Camera_Internal_Parameters*Camera_External_Parameters 
    clearvars -except Camera_Parameters%清除变量
    

    仿真结果:

    扩展:

    实际应用中,往往将上面的主程序main制作成一个相机参数函数camera_ parameters(),更有利于调用。可以用下面相机参数函数一个代替上面的main程序。

    matlab 仿真

    function [Camera_Parameters] = camera_parameters()
    % 将世界坐标转化为像素坐标的参数(相机参数矩阵)
    % 参数,可以省,省去的话,必须设置相应的入参
    Phi = pi/4;%绕x轴旋转角度
    Psi = pi/4;%绕y轴旋转角度
    Theta = pi/4;%绕z轴旋转角度
    x0 = 0;%x平移量
    y0 = 0;%y平移量
    z0 = 0;%z平移量
    f = 35e-3;%35mm相机
    dx = 0.026;%一个像素的长
    dy = 0.026;%一个像素的宽
    u0 = 0;
    v0 =0;
     
    %% step1:将世界坐标转化为相机坐标,等比例缩小,外加旋转平移(刚体变换)
    RT = rigbt(Phi, Psi, Theta, x0, y0, z0);%刚体变换矩阵
     
    %% step2:将相机坐标转化为图像坐标(投影)
    Projection_Matrix = proj(f);%投影矩阵
     
    %% step3:将图像坐标离散抽样
    Pixel_Matrix = pixel(dx,dy,u0,v0);%1像素=0.635厘米÷24≈0.026458厘米
    %% 数据整合
    Camera_Internal_Parameters = Pixel_Matrix * Projection_Matrix;%相机内参数
    Camera_External_Parameters = RT;%相机外参数
    %相机参数
    Camera_Parameters = Camera_Internal_Parameters*Camera_External_Parameters; 
    

    仿真结果:

     

    注:

     

                                                                                                                     给学matlab的人,包括我自己一个勉励:路漫漫其修,吾上下而求索

    展开全文
  • world为世界坐标系,可以任意指定xwx_wxw​轴和ywy_wyw​轴,为上图P点所在坐标系。 camera为相机坐标系,原点位于小孔,z轴与光轴重合,xwx_wxw​轴和ywy_wyw​轴平行投影面,为上图坐标系XcYcZcX_cY_cZ_cXc​Yc​...

    相机标定(一)——内参标定与程序实现

    相机标定(二)——图像坐标与世界坐标转换

    相机标定(三)——手眼标定

    一、坐标关系

    在这里插入图片描述
    相机中有四个坐标系,分别为worldcameraimagepixel

    • world为世界坐标系,可以任意指定xwx_w轴和ywy_w轴,为上图P点所在坐标系。
    • camera为相机坐标系,原点位于小孔,z轴与光轴重合,xwx_w轴和ywy_w轴平行投影面,为上图坐标系XcYcZcX_cY_cZ_c
    • image为图像坐标系,原点位于光轴和投影面的交点,xwx_w轴和ywy_w轴平行投影面,为上图坐标系XYZXYZ
    • pixel为像素坐标系,从小孔向投影面方向看,投影面的左上角为原点,uvuv轴和投影面两边重合,该坐标系与图像坐标系处在同一平面,但原点不同。

    二、坐标变换

    下式为像素坐标pixel与世界坐标world的变换公式,右侧第一个矩阵为相机内参数矩阵,第二个矩阵为相机外参数矩阵。
    s[uv1]=[fx0cx0fycy001][r11r12r13t1r21r22r23t2r31r32r33t3][xyz1] s\begin{bmatrix} u\\v\\1 \end{bmatrix} =\begin{bmatrix} f_x&0&c_x\\0&f_y&c_y\\0&0&1 \end{bmatrix} \begin{bmatrix} r_{11} & r_{12}&r_{13}&t_1 \\ r_{21} & r_{22}&r_{23}&t_2 \\ r_{31} & r_{32}&r_{33}&t_3 \end{bmatrix} \begin{bmatrix} x\\y\\z\\1 \end{bmatrix}

    2.1 变换流程

    Puv=KTPw P_{uv} = KTP_w

    该方程右侧隐含了一次齐次坐标到非齐次坐标的转换

    • KK内参TcamerapixelT_{camera}^{pixel}:像素坐标系相对于相机坐标系的变换(与相机和镜头有关)
    • TT外参TworldcameraT_{world}^{camera}:相机坐标系相对于世界坐标系的变换

    顺序变换

    • pixelcamera,使用内参变换

    Pcamera(3×1)=Tcamerapixel1(3×3)Ppixel(3×1)depth P_{camera}(3 \times 1) = {T_{camera}^{pixel}}^{-1}(3 \times 3)*P_{pixel}(3 \times 1)*depth

    • cameraworld,使用外参变换

    Pworld(4×1)=Tworldcamera1(4×4)Pcamera(4×1) P_{world} (4 \times 1)= {T_{world}^{camera}}^{-1}(4 \times 4)* P_{camera}(4 \times 1)

    注意:两个变换之间的矩阵大小不同,需要分开计算,从pixelcamera获得的相机坐标为非齐次,需转换为齐次坐标再进行下一步变换。而在进行从cameraworld时,需将外参矩阵转换为齐次再进行计算。(齐次坐标的分析

    直接变换
    [XYZ]=R1(M1s[uv1]t) \begin{bmatrix} X\\Y\\Z \end{bmatrix}= R^{-1}(M^{-1}*s*\begin{bmatrix} u\\v\\1 \end{bmatrix} - t)
    注意:直接变换是直接根据变换公式获得,实际上包含pixelcameracameraworld,实际上和顺序变换一样,通过顺序变换可以更清晰了解变换过程。

    2.2 参数计算

    • 内参:通过张正友标定获得

    • 外参:通过PNP估计获得

    • 深度s:深度s为目标点在相机坐标系Z方向的值

    2.3 外参计算

    • solvePnP函数

    Perspective-n-Point是通过n组给定点的世界坐标与像素坐标估计相机位置的方法。OpenCV内部提供的函数为solvePnP(),函数介绍如下:

    bool solvePnP(InputArray objectPoints, 
    	      	  InputArray imagePoints, 
    	      	  InputArray cameraMatrix, 
    	      	  InputArray distCoeffs, 
                  OutputArray rvec, 
                  OutputArray tvec, 
                  bool useExtrinsicGuess=false, 
                  int flags=ITERATIVE )
    
    • objectPoints,输入世界坐标系中点的坐标;
    • imagePoints,输入对应图像坐标系中点的坐标;
    • cameraMatrix, 相机内参数矩阵;
    • distCoeffs, 畸变系数;
    • rvec, 旋转向量,需输入一个非空Mat,需要通过cv::Rodrigues转换为旋转矩阵;
    • tvec, 平移向量,需输入一个非空Mat;
    • useExtrinsicGuess, 默认为false,如果设置为true则输出输入的旋转矩阵和平移矩阵;
    • flags,选择采用的算法;
      • CV_ITERATIVE Iterative method is based on Levenberg-Marquardt optimization. In this case the function finds such a pose that minimizes reprojection error, that is the sum of squared distances between the observed projections imagePoints and the projected (using projectPoints() ) objectPoints .
      • CV_P3P Method is based on the paper of X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang “Complete Solution Classification for the Perspective-Three-Point Problem”. In this case the function requires exactly four object and image points.
      • CV_EPNP Method has been introduced by F.Moreno-Noguer, V.Lepetit and P.Fua in the paper “EPnP: Efficient Perspective-n-Point Camera Pose Estimation”.

    注意solvePnP()的参数rvectvec应该都是double类型的

    • 程序实现
    //输入参数
    Mat cameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); /* 摄像机内参数矩阵 */
    Mat distCoeffs = Mat(1, 5, CV_32FC1, Scalar::all(0)); /* 摄像机的5个畸变系数:k1,k2,p1,p2,k3 */
    double zConst = 0;//实际坐标系的距离,若工作平面与相机距离固定可设置为0
    	
    //计算参数
    double s;
    Mat rotationMatrix = Mat (3, 3, DataType<double>::type);
    Mat tvec = Mat (3, 1, DataType<double>::type);
    void calcParameters(vector<cv::Point2f> imagePoints, vector<cv::Point3f> objectPoints)
    {
    	//计算旋转和平移
    	Mat rvec(3, 1, cv::DataType<double>::type);
    	cv::solvePnP(objectPoints, imagePoints, cameraMatrix, distCoeffs, rvec, tvec);
    	cv::Rodrigues(rvec, rotationMatrix);
    }
    

    2.4 深度计算

    理想情况下,相机与目标平面平行(只有绕Z轴的旋转),但实际上相机与目标平面不会完全平行,存在绕X和Y轴的旋转,此时深度s并不是固定值t3t_3,计算深度值为:
    s=t3+r31x+r32y+r33z s = t_3 + r_{31} * x + r_{32} * y + r_{33} * z
    若使用固定值进行变换会导致较大误差。解决方案如下:

    • 计算多个点的深度值,拟合一个最优值
    • 通过外参计算不同位置的深度(此处采用该方案)

    注意:此处环境为固定单目与固定工作平面,不同情况下获得深度方法不同。

    像素坐标pixel与世界坐标world转换公式可简化为
    s[uv1]=M(R[XYZconst]+t) s\begin{bmatrix} u\\v\\1 \end{bmatrix} =M(R\begin{bmatrix} X\\Y\\Z_{const} \end{bmatrix}+t)
    MM为相机内参数矩阵,RR为旋转矩阵,tt为平移矩阵,zconstz_{const}为目标点在世界坐标Z方向的值,此处为0。

    变换可得
    R1M1s[uv1]=[XYZconst]+R1t R^{-1}M^{-1}s\begin{bmatrix} u\\v\\1 \end{bmatrix} =\begin{bmatrix} X\\Y\\Z_{const} \end{bmatrix}+R^{-1}t
    当相机内外参已知可计算获得ss

    三、程序实现

    3.1 Matlab

    clc;
    clear;
    
    % 内参
    syms fx cx fy cy;
    M = [fx,0,cx;
        0,fy,cy;
        0,0,1];
                
    % 外参
    %旋转矩阵
    syms r11 r12 r13 r21 r22 r23 r31 r32 r33;
    R = [r11,r12,r13;
      r21,r22,r23;
      r31,r32,r33];
    %平移矩阵
    syms t1 t2 t3;
    t = [t1;
        t2;
        t3];
    %外参矩阵 
    T = [R,t;
        0,0,0,1];
    
    % 图像坐标 
    syms u v;
    imagePoint = [u;v;1];   
     
    % 计算深度
    syms zConst;
    rightMatrix = inv(R)*inv(M)*imagePoint;
    leftMatrix = inv(R)*t;
    s = (zConst + leftMatrix(3))/rightMatrix(3);
    
    % 转换世界坐标方式一
    worldPoint1 = inv(R) * (s*inv(M) * imagePoint - t);
    simplify(worldPoint1)
    
    % 转换世界坐标方式二
    cameraPoint = inv(M)* imagePoint * s;% image->camrea
    worldPoint2 = inv(T)* [cameraPoint;1];% camrea->world
    worldPoint2 = [worldPoint2(1);worldPoint2(2);worldPoint2(3)];
    simplify(worldPoint2)
    

    3.2 C++

    该程序参考《视觉SLAM十四讲》第九讲实践章:设计前端代码部分进行修改获得,去掉了李群库Sopuhus依赖,因该库在windows上调用较为麻烦,若在Linux建议采用Sopuhus

    • camera.h
    #ifndef CAMERA_H
    #define CAMERA_H
    
    #include <Eigen/Core>
    #include <Eigen/Geometry>
    using Eigen::Vector4d;
    using Eigen::Vector2d;
    using Eigen::Vector3d;
    using Eigen::Quaterniond;
    using Eigen::Matrix;
    
    class Camera
    {
    public:
        Camera();
    
        // coordinate transform: world, camera, pixel
        Vector3d world2camera( const Vector3d& p_w);
        Vector3d camera2world( const Vector3d& p_c);
        Vector2d camera2pixel( const Vector3d& p_c);
        Vector3d pixel2camera( const Vector2d& p_p); 
        Vector3d pixel2world ( const Vector2d& p_p);
        Vector2d world2pixel ( const Vector3d& p_w);
    
    	// set params
    	void setInternalParams(double fx, double cx, double fy, double cy);
    	void setExternalParams(Quaterniond Q, Vector3d t);
    	void setExternalParams(Matrix<double, 3, 3>  R, Vector3d t);
    
    	// cal depth
    	double calDepth(const Vector2d& p_p);
    
    private:
        // 内参
    	double fx_, fy_, cx_, cy_, depth_scale_;
    	Matrix<double, 3, 3> inMatrix_;
    
        // 外参
        Quaterniond Q_;
    	Matrix<double, 3, 3>  R_;
        Vector3d t_; 
    	Matrix<double, 4, 4> exMatrix_;
    };
    
    #endif // CAMERA_H
    
    • camera.cpp
    #include "camera.h"
    
    Camera::Camera(){}
    
    Vector3d Camera::world2camera ( const Vector3d& p_w)
    {
    	Vector4d p_w_q{ p_w(0,0),p_w(1,0),p_w(2,0),1};
    	Vector4d p_c_q = exMatrix_ * p_w_q;
    	return Vector3d{p_c_q(0,0),p_c_q(1,0),p_c_q(2,0)};
    }
    
    Vector3d Camera::camera2world ( const Vector3d& p_c)
    {
    	Vector4d p_c_q{ p_c(0,0),p_c(1,0),p_c(2,0),1 };
    	Vector4d p_w_q = exMatrix_.inverse() * p_c_q;
        return Vector3d{ p_w_q(0,0),p_w_q(1,0),p_w_q(2,0) };
    }
    
    Vector2d Camera::camera2pixel ( const Vector3d& p_c )
    {
        return Vector2d (
                   fx_ * p_c ( 0,0 ) / p_c ( 2,0 ) + cx_,
                   fy_ * p_c ( 1,0 ) / p_c ( 2,0 ) + cy_
               );
    }
    
    Vector3d Camera::pixel2camera ( const Vector2d& p_p)
    {
    	double depth = calDepth(p_p);
        return Vector3d (
                   ( p_p ( 0,0 )-cx_ ) *depth/fx_,
                   ( p_p ( 1,0 )-cy_ ) *depth/fy_,
                   depth
               );
    }
    
    Vector2d Camera::world2pixel ( const Vector3d& p_w)
    {
        return camera2pixel ( world2camera(p_w) );
    }
    
    Vector3d Camera::pixel2world ( const Vector2d& p_p)
    {
        return camera2world ( pixel2camera ( p_p ));
    }
    
    double Camera::calDepth(const Vector2d& p_p)
    {
    	Vector3d p_p_q{ p_p(0,0),p_p(1,0),1 };
    	Vector3d rightMatrix = R_.inverse() * inMatrix_.inverse() * p_p_q;
    	Vector3d leftMatrix = R_.inverse() * t_;
    	return leftMatrix(2,0)/rightMatrix(2,0);
    }
    
    void Camera::setInternalParams(double fx, double cx, double fy, double cy)
    {
    	fx_ = fx;
    	cx_ = cx;
    	fy_ = fy;
    	cy_ = cy;
    
    	inMatrix_ << fx, 0, cx,
    				0, fy, cy,
    				0, 0, 1;
    }
    
    void Camera::setExternalParams(Quaterniond Q, Vector3d t)
    {
    	Q_ = Q;
    	R_ = Q.normalized().toRotationMatrix();
    	setExternalParams(R_,t);
    }
    
    void Camera::setExternalParams(Matrix<double, 3, 3>  R, Vector3d t)
    {
    	t_ = t;
    	R_ = R;
    
    	exMatrix_ << R_(0, 0), R_(0, 1), R_(0, 2), t(0,0),
    		R_(1, 0), R_(1, 1), R_(1, 2), t(1,0),
    		R_(2, 0), R_(2, 1), R_(2, 2), t(2,0),
    		0, 0, 0, 1;
    }
    

    参考

    image coordinate to world coordinate opencv

    Computing x,y coordinate (3D) from image point

    单应矩阵

    camera_calibration_and_3d

    《视觉SLAM十四讲》—相机与图像+实践章:设计前端

    展开全文
  • 世界坐标系,相机坐标系和图像坐标系的转换(Python)

    千次阅读 多人点赞 2020-06-03 14:05:58
    世界坐标系,相机坐标系和图像坐标系的转换(Python) 相机内参外参说明:https://panjinquan.blog.csdn.net/article/details/102502213 计算机视觉:相机成像原理:世界坐标系、相机坐标系、图像坐标系、像素坐标系...

    世界坐标系,相机坐标系和图像坐标系的转换(Python)


    相机内参外参说明:https://panjinquan.blog.csdn.net/article/details/102502213

    计算机视觉:相机成像原理:世界坐标系、相机坐标系、图像坐标系、像素坐标系之间的转换:https://blog.csdn.net/chentravelling/article/details/53558096


    1.世界坐标->相机坐标


    2.相机坐标系->图像坐标系

    这里写图片描述

    此时投影点p的单位还是mm,并不是pixel,需要进一步转换到像素坐标系。

    3.图像坐标系与像素坐标系

    像素坐标系和图像坐标系都在成像平面上,只是各自的原点和度量单位不一样。图像坐标系的原点为相机光轴与成像平面的交点,通常情况下是成像平面的中点或者叫principal point。图像坐标系的单位是mm,属于物理单位,而像素坐标系的单位是pixel,我们平常描述一个像素点都是几行几列。所以这二者之间的转换如下:其中dx和dy表示每一列和每一行分别代表多少mm,即1pixel=dx mm 

    这里写图片描述
    那么通过上面四个坐标系的转换就可以得到一个点从世界坐标系如何转换到像素坐标系的。 

    u=\frac{x}{dx}+u_{0}=\frac{xf_{x}}{Z_{c}}+u_{0} 

    u=\frac{x}{dx}+u_{0}=\frac{xf_{x}}{Z_{c}}+u_{0}


    python代码实现:

    以下是实现变换的关键代码

    相关可视化部分已经push到github:  https://github.com/PanJinquan/python-learning-notes

    https://github.com/PanJinquan/python-learning-notes/blob/master/modules/utils_3d/camera_tools.py

    # -*- coding: utf-8 -*-
    """
    # --------------------------------------------------------
    # @Project: Integral-Human-Pose-Regression-for-3D-Human-Pose-Estimation
    # @Author : panjq
    # @E-mail : pan_jinquan@163.com
    # @Date   : 2020-02-04 16:03:01
    # @url    : https://www.jianshu.com/p/c5627ad019df
    # --------------------------------------------------------
    """
    import sys
    import os
    
    sys.path.append(os.getcwd())
    
    import cv2
    import numpy as np
    from modules.utils_3d import vis_3d as vis
    from utils import image_processing
    
    human36m_camera_intrinsic = {
        # R,旋转矩阵
        "R": [[-0.91536173, 0.40180837, 0.02574754],
              [0.05154812, 0.18037357, -0.98224649],
              [-0.39931903, -0.89778361, -0.18581953]],
        # t,平移向量
        "T": [1841.10702775, 4955.28462345, 1563.4453959],
        # 焦距,f/dx, f/dy
        "f": [1145.04940459, 1143.78109572],
        # principal point,主点,主轴与像平面的交点
        "c": [512.54150496, 515.45148698]
    
    }
    
    kinect2_camera_intrinsic = {
    
        # R,旋转矩阵
        "R": [[0.999853, -0.00340388, 0.0167495],
              [0.00300206, 0.999708, 0.0239986],
              [-0.0168257, -0.0239459, 0.999571]],
        # t,平移向量
        "T": [15.2562, 70.2212, -10.9926],
        # 焦距,f/dx, f/dy
        "f": [367.535, 367.535],
        # principal point,主点,主轴与像平面的交点
        "c": [260.166, 205.197]
    
    }
    
    camera_intrinsic = human36m_camera_intrinsic
    # camera_intrinsic = kinect2_camera_intrinsic
    
    class CameraTools(object):
    
        @staticmethod
        def convert_wc_to_cc(joint_world):
            """
            世界坐标系 -> 相机坐标系: R * (pt - T):
            joint_cam = np.dot(R, (joint_world - T).T).T
            :return:
            """
            joint_world = np.asarray(joint_world)
            R = np.asarray(camera_intrinsic["R"])
            T = np.asarray(camera_intrinsic["T"])
            joint_num = len(joint_world)
            # 世界坐标系 -> 相机坐标系
            # [R|t] world coords -> camera coords
            # joint_cam = np.zeros((joint_num, 3))  # joint camera
            # for i in range(joint_num):  # joint i
            #     joint_cam[i] = np.dot(R, joint_world[i] - T)  # R * (pt - T)
            # .T is 转置, T is translation mat
            joint_cam = np.dot(R, (joint_world - T).T).T  # R * (pt - T)
            return joint_cam
    
        @staticmethod
        def convert_cc_to_wc(joint_world):
            """
            相机坐标系 -> 世界坐标系: inv(R) * pt +T 
            joint_cam = np.dot(inv(R), joint_world.T)+T
            :return:
            """
            joint_world = np.asarray(joint_world)
            R = np.asarray(camera_intrinsic["R"])
            T = np.asarray(camera_intrinsic["T"])
            # 相机坐标系 -> 世界坐标系
            joint_cam = np.dot(np.linalg.inv(R), joint_world.T).T + T
            return joint_cam
    
        @staticmethod
        def __cam2pixel(cam_coord, f, c):
            """
            相机坐标系 -> 像素坐标系: (f / dx) * (X / Z) = f * (X / Z) / dx
            cx,ppx=260.166; cy,ppy=205.197; fx=367.535; fy=367.535
            将从3D(X,Y,Z)映射到2D像素坐标P(u,v)计算公式为:
            u = X * fx / Z + cx
            v = Y * fy / Z + cy
            D(v,u) = Z / Alpha
            =====================================================
            camera_matrix = [[428.30114, 0.,   316.41648],
                            [   0.,    427.00564, 218.34591],
                            [   0.,      0.,    1.]])
    
            fx = camera_intrinsic[0, 0]
            fy = camera_intrinsic[1, 1]
            cx = camera_intrinsic[0, 2]
            cy = camera_intrinsic[1, 2]
            =====================================================
            :param cam_coord:
            :param f: [fx,fy]
            :param c: [cx,cy]
            :return:
            """
            # 等价于:(f / dx) * (X / Z) = f * (X / Z) / dx
            # 三角变换, / dx, + center_x
            u = cam_coord[..., 0] / cam_coord[..., 2] * f[0] + c[0]
            v = cam_coord[..., 1] / cam_coord[..., 2] * f[1] + c[1]
            d = cam_coord[..., 2]
            return u, v, d
    
        @staticmethod
        def convert_cc_to_ic(joint_cam):
            """
            相机坐标系 -> 像素坐标系
            :param joint_cam:
            :return:
            """
            # 相机坐标系 -> 像素坐标系,并 get relative depth
            # Subtract center depth
            # 选择 Pelvis骨盆 所在位置作为相机中心,后面用之求relative depth
            root_idx = 0
            center_cam = joint_cam[root_idx]  # (x,y,z) mm
            joint_num = len(joint_cam)
            f = camera_intrinsic["f"]
            c = camera_intrinsic["c"]
            # joint image_dict,像素坐标系,Depth 为相对深度 mm
            joint_img = np.zeros((joint_num, 3))
            joint_img[:, 0], joint_img[:, 1], joint_img[:, 2] = CameraTools.__cam2pixel(joint_cam, f, c)  # x,y
            joint_img[:, 2] = joint_img[:, 2] - center_cam[2]  # z
            return joint_img
    
    
    def demo_for_human36m():
        joint_world = [[-91.679, 154.404, 907.261],
                       [-223.23566, 163.80551, 890.5342],
                       [-188.4703, 14.077106, 475.1688],
                       [-261.84055, 186.55286, 61.438915],
                       [39.877888, 145.00247, 923.98785],
                       [-11.675994, 160.89919, 484.39148],
                       [-51.550297, 220.14624, 35.834396],
                       [-132.34781, 215.73018, 1128.8396],
                       [-97.1674, 202.34435, 1383.1466],
                       [-112.97073, 127.96946, 1477.4457],
                       [-120.03289, 190.96477, 1573.4],
                       [25.895456, 192.35947, 1296.1571],
                       [107.10581, 116.050285, 1040.5062],
                       [129.8381, -48.024918, 850.94806],
                       [-230.36955, 203.17923, 1311.9639],
                       [-315.40536, 164.55284, 1049.1747],
                       [-350.77136, 43.442127, 831.3473],
                       [-102.237045, 197.76935, 1304.0605]]
        joint_world = np.asarray(joint_world)
        # 关节点连接线
        kps_lines = ((0, 7), (7, 8), (8, 9), (9, 10), (8, 11), (11, 12), (12, 13), (8, 14), (14, 15),
                     (15, 16), (0, 1), (1, 2), (2, 3), (0, 4), (4, 5), (5, 6))
        # show in 世界坐标系
        vis.vis_3d(joint_world, kps_lines, coordinate="WC", title="WC", set_lim=True, isshow=True)
    
        kp_vis = CameraTools()
    
        # show in 相机坐标系
        joint_cam = kp_vis.convert_wc_to_cc(joint_world)
        vis.vis_3d(joint_cam, kps_lines, coordinate="CC", title="CC", set_lim=True, isshow=True)
        joint_img = kp_vis.convert_cc_to_ic(joint_cam)
    
        joint_world1 = kp_vis.convert_cc_to_wc(joint_cam)
        vis.vis_3d(joint_world1, kps_lines, coordinate="WC", title="WC", set_lim=True, isshow=True)
    
        # show in 像素坐标系
        kpt_2d = joint_img[:, 0:2]
        image_path = "./data/s_01_act_02_subact_01_ca_02_000001.jpg"
        image = image_processing.read_image(image_path)
        image = image_processing.draw_key_point_in_image(image, key_points=[kpt_2d], pointline=kps_lines)
        image_processing.cv_show_image("image_dict", image)
    
    
    if __name__ == "__main__":
        demo_for_human36m()
    

    效果:

     

    展开全文
  • 世界坐标系与摄像机坐标系的变换

    万次阅读 2018-02-27 17:01:42
    该文章是对《计算机视觉增强现实应用概论》中《增强现实的摄像机空间理论》这一节进行摘录和公式推导。...而以实物为原点建立的坐标系称为世界坐标系。 摄像机运用棱镜将获取到的图像通过小孔成像的方式缩小...
  • 世界坐标与图像坐标

    千次阅读 2016-11-18 20:10:56
    右手坐标系 左手坐标系 伸出左手,让拇指和食指成“L”形,大拇指向右,食指向上。其余的手指指向前方。这样就建立了一个左手坐标系。拇指、食指和其余手指分别代表x,y,z轴的正方向。判断方法:在空间直角...
  • 在cocos2d-x,提供现成的两个用于...convertToNodeSpace:将世界坐标转换为本地坐标 用法: node1->convertToNodeSpace(node2->getPosition()); 注释: 将node2的世界坐标转化为相对于node1的本地坐标
  • 【Unity3D】世界坐标与屏幕坐标

    万次阅读 2017-05-08 17:42:04
    Unity3D由于是在三维世界中编程,而最终的结果是需要反馈到肉眼所示的2D屏幕之上的。...其实在于我个人看来,关键是世界坐标与屏幕坐标之间的互转。 在Unity3D中一打开就会有一个主照相机Main Camera,根
  • 使用深度图重建世界坐标

    千次阅读 2018-12-21 14:22:49
    首先建立一个绘制世界坐标的项目。 保证物体的顶点在0到1范围之内,这样颜色能够有意义的表示物体的世界坐标位置。 然后编写一个shader,用于绘制世界坐标位置: Shader &amp;amp;amp;amp;amp;amp;amp;quot...
  • 首先,对于世界坐标和局部坐标。 这是两个cube,是我们今天的主角。首先是这个父Cube,我们注意到它此时坐标为(0,0,0),由于他在根目录,所以他的transform.position就是他的世界坐标。 这里之所以用...
  • 世界坐标系和相机坐标系,图像坐标系的关系

    万次阅读 多人点赞 2016-01-26 19:36:04
    二、图像坐标:我想和世界坐标谈谈(B)  玉米将在这篇博文中,对图像坐标与世界坐标的这场对话中涉及的第二个问题:谈话方式,进行总结。世界坐标是怎样变换进摄像机,投影成图像坐标的呢?  玉米做了一个简单的...
  • 一、各坐标系介绍图像处理、立体视觉经常涉及到世界坐标系、相机坐标系、图像坐标系和像素坐标系。如下图所示:世界坐标系是为了确定相机的位置,在双目视觉中一般将世界坐标系原点定在左相机、右相机或两者X轴方向...
  • 世界坐标系、相机坐标系、图像坐标系、像素坐标系之间的转换 图像处理、立体视觉等方向常常涉及到四个坐标系:世界坐标系、相机坐标系、图像坐标系、像素坐标系。例如下图: 构建世界坐标系只是为了更好的描述...
  • 世界坐标系,相机坐标系,图像坐标系,图像像素坐标系这四个坐标系的转换实质就是刚体变换、透视投影和数字化图像这几个成像里的步骤。 一、世界坐标系到相机坐标系 世界坐标系,也称为测量坐标系,它是一个三维...
  • Unity有世界坐标、本地坐标(localposition)、UI坐标(这个我好像还没怎么用)、屏幕坐标等等,之前的一个项目需要考虑世界坐标转换为本地坐标,后来经过一番尝试,找到了一种比较取巧的方法。 首先,世界坐标是指...
  • UGUI坐标与世界坐标的转换

    万次阅读 2017-10-31 15:04:40
    UGUI坐标的转换网上网友说的很多,有正确的也有误导的,这里我通过实验来总结了一下正确的转换方式(假设你充分了解RectTransform坐标系和本地坐标、世界坐标的关系): 一、将UGUI坐标转化为世界坐标,比如说我想...
  • 1.世界坐标系 2.物体坐标系 3.惯性坐标系 二、坐标系之间的联系 一、坐标系的区别 1.世界坐标世界坐标系是一个特殊的坐标系,它建立了描述其他坐标系所需要的参考系。也就是说,可以用世界坐标系去描述...
  • LZ-Says:哎呦喂,时间,时间,慢点过~ 前言 Today,一起来了解下在Unity中的坐标系。...世界坐标系,也就是基于我们当前游戏场景而言。先来观察下某个游戏物体的坐标: 分别对应。X、Y以及Z轴。 当然...
  • 相机的成像过程涉及到四个坐标系:世界坐标系、相机坐标系、图像坐标系、像素坐标系。这篇博客介绍相机的成像过程,以及四个坐标系之间的装换关系。世界坐标系:是客观三维世界的绝对坐标系,也称客观坐标系。因为...
  • Unity 世界坐标转UI坐标

    千次阅读 2020-07-29 15:14:09
    1.World Space(世界坐标):三维空间坐标,浮点数。 2.Screen Space(屏幕坐标):以像素来定义的,以屏幕的左下角为(0,0)点,右上角为(Screen.width,Screen.height),Z的位置是以相机的世界单位来衡量的。 3...
1 2 3 4 5 ... 20
收藏数 79,025
精华内容 31,610
关键字:

世界坐标