Comment convertir une coordonnée sphérique (θ, φ) en une position (x, y) sur une projection équirectangulaire (également appelée "projection géographique")?
Dans lequel:
Ci-dessous, vous trouverez la question d'origine, à l'époque où je ne comprenais pas bien le problème, mais je pense qu'elle est toujours bonne pour montrer ce qu'est une application pratique de cette solution.
Edit: Le titre de la question d'origine était: Comment transformer une photo à un angle donné pour devenir une partie d'une photo panoramique?
Quelqu'un peut-il m'aider avec les étapes à suivre si je souhaite transformer une photo prise à un angle donné de telle manière que je puisse placer l'image résultante (déformée/transformée) à l'emplacement spécifique correspondant sur une projection équirectangulaire, carte de cube , ou toute projection photo panoramique?
Quelle que soit la projection la plus facile à faire, cela suffit, car il existe de nombreuses ressources sur la façon de convertir entre différentes projections. Je ne sais tout simplement pas comment passer d'une photo réelle à une telle projection.
Il est prudent de supposer que la caméra restera à un emplacement fixe et peut tourner dans n'importe quelle direction à partir de là. Je pense que les données nécessaires pour ce faire sont probablement quelque chose comme ceci:
[-180, +180]
(par exemple + 140deg).[-90, +90]
(par exemple -30deg).w x h
(par exemple 1280x720 pixels).J'ai ces données, et je suppose que la première étape consiste à corriger l'objectif afin que toutes les lignes qui devraient être droites soient en fait droites. Et cela peut être fait à l'aide de imagemagick
Barrel Distortion , dans lequel vous n'avez qu'à remplir trois paramètres: a, b et c. La transformation qui est appliquée à l'image pour corriger cela est simple.
Je suis bloqué sur la prochaine étape. Soit je ne le comprends pas complètement, soit les moteurs de recherche ne m'aident pas, car la plupart des résultats concernent la conversion entre des projections déjà données ou utilisent des applications avancées pour assembler intelligemment des photos. Ces résultats ne m'ont pas aidé à répondre à ma question.
EDIT: J'ai pensé que peut-être un chiffre aiderait à mieux l'expliquer :)
Le problème est qu'une photo donnée Rouge ne peut pas être placée dans la projection équirectangulaire sans transformation. La figure ci-dessous illustre le problème.
Donc, j'ai Rouge , et je dois le transformer en Vert . Bleu montre la différence de transformation, mais cela dépend de l'angle horizontal/vertical.
Si les photos sont prises à partir d'un point fixe et que l'appareil photo ne peut que faire pivoter son lacet et s'incliner autour de ce point. Ensuite, nous pouvons considérer une sphère de n'importe quel rayon (pour les mathématiques, il est fortement recommandé d'utiliser un rayon de 1). La photo aura une forme rectangulaire sur cette sphère (du point de vue de la caméra).
Si vous regardez l'horizon (équateur), les pixels verticaux représentent la latitude et les pixels horizontaux la longitude. Pour une simple photo panoramique de l'horizon il n'y a pas beaucoup de problème:
Ici, nous regardons à peu près l'horizon de notre monde. Autrement dit, la caméra a un angle va = ~0
. Ensuite, c'est assez simple, car si nous savons que la photo est large de 70 degrés et haute de 40 degrés, nous savons également que la plage de longitude sera d'environ 70 degrés et la latitude de 40 degrés.
Si nous ne nous soucions pas d'une légère distorsion, alors la formule pour calculer le (longitude,latitude)
à partir de n'importe quel pixel (x,y)
de la photo serait facile:
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
Mais cette approximation ne fonctionne pas du tout lorsque nous déplaçons la caméra beaucoup plus verticalement:
Alors, comment pouvons-nous transformer un pixel de l'image à (x, y)
à un (longitude, latitude)
coordonnée en fonction d'un angle vertical/horizontal auquel la photo a été prise (va,ha)
?
L'idée importante qui a résolu le problème pour moi est la suivante: vous avez essentiellement deux sphères :
Vous connaissez la coordonnée sphérique d'un point sur la photo-sphère, et vous voulez savoir où se trouve ce point sur la géosphère avec l'angle de caméra différent.
Nous devons réaliser qu'il est difficile de faire des calculs entre les deux sphères en utilisant juste coordonnées sphériques . Le calcul pour le système de coordonnées cartésiennes est beaucoup plus simple. Dans le système de coordonnées cartésiennes, nous pouvons facilement tourner autour de n'importe quel axe en utilisant matrices de rotation qui sont multipliées par le vecteur de coordonnées [x,y,z]
pour récupérer les coordonnées pivotées.
Attention: Ici, il est très important de savoir que il existe différentes conventions en ce qui concerne la signification de x
- axe, y
- axe et z
- axe. On ne sait pas quel axe est vertical, et lequel indique où. Il vous suffit de faire un dessin pour vous-même et de décider à ce sujet. Si le résultat est erroné, c'est probablement parce qu'ils sont mélangés. Il en va de même pour theta
et phi
pour les coordonnées sphériques.
Donc l'astuce consiste à transformer la photo-sphère en cartésienne, puis à appliquer les rotations, puis à revenir aux coordonnées sphériques :
[x,y,z]
vecteurs).(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());
}
// ...
}
}
Remarque, ce n'est qu'une sorte de pseudo-code. Il est conseillé d'utiliser une matrice-bibliothèque qui gère vos multiplications et rotations de matrices et vecteurs.
hmm, je pense que vous devriez peut-être faire un pas en arrière. Tenez compte de l'angle de votre caméra (environ 70 mm). mais votre image de fond est à 360 degrés à l'horizontale (mais aussi à la verticale). Tenez compte des distorsions de perspective sur les deux types d'images. Pour l'image de fond, dans un sens vertical seul l'horizon n'est pas déformé verticalement. Malheureusement, ce n'est qu'une mince ligne. Plus la distorsion augmente, plus vous atteignez le haut ou le bas.
Ce n'est pas constant comme dans la distorsion en barillet, mais cela dépend de la distance verticale de l'horizon.
Je pense que la meilleure façon de réaliser la différence est de prendre une vue latérale du type d'appareil photo et de la cible sur laquelle ils sont censés se projeter, à partir de là sa trigonométrie, les mathématiques.
Notez que pour l'image 70 mm, vous devez connaître l'angle de prise de vue. (ou estimez-le)