image-processing - 如何将球面坐标转换为等距柱状投影坐标?

标签 image-processing camera coordinates projection coordinate-systems

简化问题
你如何转换 spherical coordinate (θ, φ) 到 equirectangular projection 上的位置 (x, y) (也称为“地理投影”)?
其中:

  • x 是经度,水平位置,从 -180 到 180 度。
  • y 是纬度,垂直位置,从 -90 到 90 度。
  • θ 是 theta,以度为单位的水平角,是从 (0,0,0) 到球体表面上一点的向量。
  • φ 是 phi,以度为单位的垂直角度,是从 (0,0,0) 到球体表面上一点的向量。

  • 您可以在下面找到原始问题,当时我还没有很好地理解问题,但我认为它仍然很好地展示了该解决方案的实际应用。
    上下文
    编辑:最初的问题标题是:如何以给定角度转换照片以成为全景照片的一部分?
    如果我想转换以任何给定角度拍摄的照片,以便我可以将生成的(扭曲/转换的)图像放置在等距柱状投影、立方体贴图上的相应特定位置,任何人都可以帮助我采取哪些步骤,或任何全景照片投影?
    哪个投影最容易做就足够了,因为有很多关于如何在不同投影之间转换的资源。我只是不知道如何从实际照片到这样的投影。
    可以安全地假设相机将停留在固定位置,并且可以从那里向任何方向旋转。我认为执行此操作所需的数据可能是这样的:
  • 物理相机水平角度[-180, +180] (例如 +140 度)。
  • 物理摄像头垂直角度[-90, +90] (例如-30度)。
  • 照片分辨率w x h (例如 1280x720 像素)。
  • 照片的水平角度(例如 70 度)。
  • 照片的垂直角度(例如 40 度)。
  • 镜头校正 a、b、c 参数(见下文)。

  • 我有这些数据,我想第一步是进行镜头校正,以便所有应该是直线的线实际上都是直线。这可以使用 imagemagick 来完成的 Barrel Distortion ,其中只需要填写三个参数:a、b、c。应用于图像以纠正此问题的转换很简单。
    我被困在下一步。要么我不完全理解它,要么搜索引擎没有帮助我,因为大多数结果是关于在已经给定的投影之间进行转换或使用高级应用程序将照片智能地拼接在一起。这些结果并没有帮助我回答我的问题。
    编辑:我想也许一个数字会有助于更好地解释它:)
    问题是给定的照片 红色 不能在没有变换的情况下放入等距柱状投影。下图说明了这个问题。
    Equirectangular projection with a "normal" photo
    所以,我有 红色 ,我需要把它转换成 绿色 . 蓝色 显示了变换的差异,但这取决于水平/垂直角度。

    最佳答案

    如果照片是从固定点拍摄的,并且相机只能围绕该点旋转其偏航和俯仰。然后我们可以考虑任何半径的球体(为了数学,强烈建议使用半径为 1)。照片将是此 上的矩形形状球体 (从相机的角度)。

    水平案例

    如果您正在查看地平线(赤道),则垂直像素代表纬度,水平像素代表经度。对于地平线的简单全景照片,问题不大:

    Looking at the wireframe of the geo-sphere from (0,0,0)

    在这里,我们大致看一下我们世界的地平线。即相机有角度va = ~0 .那么这很简单,因为如果我们知道照片宽 70 度,高 40 度,那么我们也知道经度范围约为 70 度,纬度范围约为 40 度。

    如果我们不在乎轻微的失真,那么计算(longitude,latitude)的公式来自任何像素 (x,y)从照片会很容易:

    photo_width_deg = 70
    photo_height_deg = 30
    photo_width_px = 1280
    photo_height_px = 720
    ha = 0
    va = 0
    longitude = photo_width_deg * (x - photo_width_px/2) / photo_width_px + ha
    latitude = photo_height_deg * (y - photo_height_px/2) / photo_height_px + va
    

    问题

    但是当我们更垂直地移动相机时,这种近似根本不起作用:

    Looking at the wireframe of the geo-sphere from (0,0,0)

    那么我们如何从 (x, y) 处的图片中转换一个像素到 (longitude, latitude)坐标给出了拍摄照片的垂直/水平角度 (va,ha) ?

    解决方案

    为我解决问题的重要思想是:你基本上有两个球体 :
  • 以相机为中心的照片球体。
  • 具有经度/纬度坐标的地理球体(等距柱状投影球体)。

  • 您知道光球上某个点的球坐标,并且您想知道该点在具有不同相机角度的地球上的位置。

    真正的问题

    我们必须意识到它是很难在两个球体之间进行任何计算 仅使用 spherical coordinates . cartesian coordinate system 的数学计算简单得多。在笛卡尔坐标系中,我们可以使用 rotation matrices 轻松地绕任何轴旋转乘以坐标向量 [x,y,z]获取旋转后的坐标。

    警告:在这里知道 非常重要。有不同的约定 关于x的含义轴,y -轴和 z -轴。不确定哪个轴是垂直轴,哪个轴指向哪里。你只需要为自己画一幅画并决定这个。如果结果是错误的,可能是因为这些被混淆了。 theta 也是如此和 phi对于球坐标。

    真正的解决方案

    所以诀窍是从光球变换到笛卡尔坐标,然后应用旋转,然后回到球坐标 :
  • 取照片上的任何像素,并计算它在水平和垂直方向上远离照片中心的相对度数。
  • 将光球球坐标转换为笛卡尔坐标([x,y,z] 向量)。
  • 像旋转相机一样将旋转矩阵应用于坐标(ha,va) .
  • 将笛卡尔坐标转换回球坐标,这些将是您的经度和纬度。

  • 示例代码
    // Photo resolution
    double img_w_px = 1280;
    double img_h_px = 720;
    // Camera field-of-view angles
    double img_ha_deg = 70;
    double img_va_deg = 40;
    // Camera rotation angles
    double hcam_deg = 230;
    double vcam_deg = 60;
    // Camera rotation angles in radians
    double hcam_rad = hcam_deg/180.0*PI;
    double vcam_rad = vcam_rad/180.0*PI;
    // Rotation around y-axis for vertical rotation of camera
    Matrix rot_y = {
        cos(vcam_rad), 0, sin(vcam_rad),
        0, 1, 0,
        -sin(vcam_rad), 0, cos(vcam_rad)
    };
    // Rotation around z-axis for horizontal rotation of camera
    Matrix rot_z = {
        cos(hcam_rad), -sin(hcam_rad), 0,
        sin(hcam_rad), cos(hcam_rad), 0,
        0, 0, 1
    };
    
    Image img = load('something.png');
    for(int i=0;i<img_h_px;++i)
    {
        for(int j=0;j<img_w_px;++j)
        {
            Pixel p = img.getPixelAt(i, j);
    
            // Calculate relative position to center in degrees
            double p_theta = (j - img_w_px / 2.0) / img_w_px * img_w_deg / 180.0 * PI;
            double p_phi = -(i - img_h_px / 2.0) / img_h_px * img_h_deg / 180.0 * PI;
    
            // Transform into cartesian coordinates
            double p_x = cos(p_phi) * cos(p_theta);
            double p_y = cos(p_phi) * sin(p_theta);
            double p_z = sin(p_phi);
            Vector p0 = {p_x, p_y, p_z};
    
            // Apply rotation matrices (note, z-axis is the vertical one)
            // First vertically
            Vector p1 = rot_y * p0;
            Vector p2 = rot_z * p1;
    
            // Transform back into spherical coordinates
            double theta = atan2(p2[1], p2[0]);
            double phi = asin(p2[2]);
    
            // Retrieve longitude,latitude
            double longitude = theta / PI * 180.0;
            double latitude = phi / PI * 180.0;
    
            // Now we can use longitude,latitude coordinates in many different projections, such as:
            // Polar projection
            {
                int polar_x_px = (0.5*PI + phi)*0.5 * cos(theta) /PI*180.0 * polar_w;
                int polar_y_px = (0.5*PI + phi)*0.5 * sin(theta) /PI*180.0 * polar_h;
                polar.setPixel(polar_x_px, polar_y_px, p.getRGB());
            }
            // Geographical (=equirectangular) projection
            {
                int geo_x_px = (longitude + 180) * geo_w;
                int geo_y_px = (latitude + 90) * geo_h;
                geo.setPixel(geo_x_px, geo_y_px, p.getRGB());
            }
            // ...
        }
    }
    

    请注意,这只是某种伪代码。建议使用矩阵库来处理矩阵和向量的乘法和旋转。

    关于image-processing - 如何将球面坐标转换为等距柱状投影坐标?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43741885/

    相关文章:

    android - 支持 Android Camera Api 和 Camera2 Api 的问题

    ios - 如何保存使用 UIImagePickerController 相机拍摄的照片以便稍后在应用程序中显示?

    cocoa - Mac OS X Cocoa,在全局屏幕坐标空间中翻转 Y 坐标

    java - 获取 tiff 文件的坐标

    安卓相机预览

    javascript - 如何使 D3 Force Directed Graph 每次在相同位置生成节点

    php - 在动态调整大小的图像上保存像素位置

    image-processing - 将具有透明度的 PNG 转换为 JPG

    java - 无法使用 ImageIO 从 Controller 写入 tif 文件

    java - OpenCV - 使用 Java 去除图像中的噪声