Image processing 如何将球坐标转换为等直角投影坐标? 简化问题
如何将a(θ,φ)转换为a(也称为“地理投影”)上的位置(x,y) 其中:Image processing 如何将球坐标转换为等直角投影坐标? 简化问题,image-processing,camera,coordinates,projection,coordinate-systems,Image Processing,Camera,Coordinates,Projection,Coordinate Systems,如何将a(θ,φ)转换为a(也称为“地理投影”)上的位置(x,y) 其中: x是经度,水平位置,从-180度到180度 y是纬度,垂直位置,从-90度到90度 θ是θ,以度为单位的水平角,从(0,0,0)到球面上一点的向量 φ是φ,以度为单位的垂直角,是从(0,0,0)到球体表面上一点的向量 下面你会发现原来的问题,回到我不太理解这个问题的时候,但我认为它仍然有助于展示这个解决方案的实际应用 上下文 编辑:最初的问题标题是:如何以给定角度变换照片,使其成为全景照片的一部分 如果我想变换以任
- x是经度,水平位置,从-180度到180度
- y是纬度,垂直位置,从-90度到90度
- θ是θ,以度为单位的水平角,从(0,0,0)到球面上一点的向量
- φ是φ,以度为单位的垂直角,是从(0,0,0)到球体表面上一点的向量
下面你会发现原来的问题,回到我不太理解这个问题的时候,但我认为它仍然有助于展示这个解决方案的实际应用 上下文 编辑:最初的问题标题是:如何以给定角度变换照片,使其成为全景照片的一部分 如果我想变换以任何给定角度拍摄的照片,以便将生成的(扭曲/变换)图像放置在等矩形投影、立方体地图或任何全景照片投影上相应的特定位置,有人能帮助我采取哪些步骤吗 无论哪种预测最容易实现,都已经足够好了,因为关于如何在不同的预测之间转换,有大量的资源。我只是不知道如何从一张真实的照片到这样的投影 可以安全地假设相机将停留在固定位置,并且可以从那里向任何方向旋转。我认为要做到这一点所需要的数据可能是这样的:
- 物理摄像机的水平角度
(例如+140度)[-180,+180]
- 物理摄像机的垂直角度
(例如-30度)[-90,+90]
- 照片的分辨率
(例如1280x720像素)w x h
- 照片的水平角度(例如70度)
- 照片的垂直角度(例如40度)
- 镜头校正a、b、c参数(见下文)
imagemagick
来实现,其中只需要填写三个参数:a、b和c。应用于图像以纠正此问题的变换非常简单
我被困在下一步。要么我不完全理解,要么搜索引擎帮不了我,因为大多数结果都是关于在已经给定的投影之间转换,或者使用高级应用程序将照片智能地缝合在一起。这些结果并没有帮助我回答我的问题
编辑:我想也许一个数字能更好地解释它:)
问题是给定的照片红色在没有变换的情况下无法放置到等矩形投影中。下图说明了该问题
所以,我有红色的,我需要把它转换成绿色的蓝色显示变换的差异,但这取决于水平/垂直角度。嗯,我想也许你应该后退一步。 考虑你的相机角度(70mm左右)。 但是你的背景图像是水平的360度(但也是垂直的)。 考虑这两种类型的图片的透视失真。 对于背景pict,在垂直意义上,只有地平线没有垂直扭曲。可悲的是,这只是一条细线。 随着失真的增加,到达顶部或底部的次数越多 它不像桶形失真那个样恒定,但它取决于地平线的垂直距离 我认为实现差异的最好方法是从侧面观察摄像机类型和它们应该投射到的目标,从那里可以看到三角函数和数学
请注意,对于70mm的图片,您需要知道拍摄的角度。(或估算)如果照片是从固定点拍摄的,相机只能围绕该点旋转偏航和俯仰。然后我们可以考虑任何半径的球体(对于数学来说,强烈建议使用半径为1)。照片将是这个球体上的矩形(从相机的角度) 地平线案例 如果你看的是地平线(赤道),那么垂直像素代表纬度,水平像素代表经度。对于地平线的简单全景照片,问题不大: 这里我们大致看一下我们世界的地平线。也就是说,相机具有角度
va=~0
。这很简单,因为如果我们知道照片宽70度,高40度,那么我们也知道经度范围大约是70度,纬度范围是40度
如果我们不关心轻微的失真,那么从照片的任何像素计算(经度、纬度)
的公式将很容易:
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
问题
但是,当我们将相机垂直移动得更多时,这种近似方法根本不起作用:
那么,在给定拍摄照片的垂直/水平角度下,我们如何将位于(x,y)
的图片中的像素转换为(经度,纬度)
坐标
解决方案
对我来说,解决这个问题的重要思想是:你基本上有两个球体:
- 相机位于中心的照片球体
- 地理球体(等矩形投影球体),带有经度/纬度坐标
// 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());
}
// ...
}
}