image - quien - proyeccion mercator y peters diferencias
Imagen de deformación para aparecer en proyección cilíndrica (1)
Esta es una respuesta en 2 partes, la matemática y el código.
Mates
Me gusta este problema porque la proyección involucrada es interesante, pero las matemáticas aún pueden resolverse a mano sin demasiada dificultad. Para empezar, es importante entender por qué exactamente la imagen se deforma de la forma en que lo hace. Digamos que tenemos una imagen plana con un cilindro cóncavo sentado frente a él.
El primer paso es hacer una proyección ortográfica moviendo la imagen sobre la superficie curva.
Luego esos puntos se proyectan con perspectiva hacia el plano de la imagen. Observe en este caso que la imagen completa se reduce porque todas las partes del cilindro tienen una coordenada z mayor que el plano de la imagen. En su caso, el cilindro toca el plano de la imagen en los bordes izquierdo y derecho para que no se produzca retracción. Cuando los puntos se proyectan hacia atrás, ya no forman una línea plana en el plano de la imagen, hay una curva debido a que la coordenada z del cilindro varía con x.
El primer truco es que realmente queremos representar este proceso hacia atrás. Es posible que primero piense que desea tomar cada píxel de su imagen original y moverlo a su nueva imagen. En realidad, funciona mucho mejor si comprueba cada píxel en su nueva imagen donde apareció en la imagen antigua y establece su color. Eso significa que necesitas hacer 3 cosas.
- Configure los parámetros de su cilindro
- Proyecte un rayo desde la cámara, pasando por cada punto en la nueva imagen y encuentre sus coordenadas x, y, z en el cilindro
- Use una proyección ortográfica para mover ese rayo de regreso al plano de la imagen (solo significa que suelte el componente z)
Hacer un seguimiento de todo puede ser un poco complicado, así que intentaré usar una terminología consistente. En primer lugar, asumo que quiere garantizar que su cilindro toque su imagen en los bordes. Si eso es cierto, entonces los 2 parámetros libres que puede elegir son el radio del cilindro y la distancia focal.
La ecuación de un círculo en el plano zx es
x^2+(z-z0)^2 = r^2
suponiendo que el centro del círculo se encuentra en el eje z. Si el borde del cilindro va a tocar el borde del plano de la imagen que tiene un ancho wy una distancia focal f entonces
omega^2+(f-z0)^2 = r^2 //define omega = width/2, it cleans it up a bit
z0 = f-sqrt(r^2-omega^2)
Ahora que conocemos todos los parámetros del cilindro, pasamos al paso 2, proyectamos líneas desde la cámara, a través del plano de la imagen en la posición más cercana al cilindro en xc. Aquí hay un diagrama rápido de la terminología.
Sabemos que la línea que estamos proyectando comienza en el origen y cruza el plano de la imagen en la máxima. Podemos escribir su ecuación como
x = xim*z/f
Como queremos que la coordenada x cuando pase por el cilindro, combine las ecuaciones.
xim^2*z^2/f^2 + z^2 - 2*z*z0 +z0^2 - r^2 = 0
Puedes usar la ecuación cuadrática para resolver z y luego volver a conectarla en la ecuación lineal para obtener x. Las dos soluciones corresponden a los dos lugares donde la línea toca el círculo, ya que solo nos interesa el que sucede después del plano de la imagen, y esa siempre tendrá una coordenada x más grande, use -b + sqrt (...) . Entonces
xc = xim*z/f;
yc = yim*z/f;
El paso final para eliminar la proyección ortográfica es fácil, simplemente suelte el componente z y listo.
Código
Sé que usted dijo que no está usando openCV, pero lo usaré en mi demostración como contenedor de imágenes. Todas las operaciones se realizan píxel por píxel, por lo que no debería ser difícil convertirlo para que funcione en cualquier contenedor de imagen que esté utilizando. Primero hice una función que convierte las coordenadas de la imagen en la imagen final a las coordenadas en la imagen original. OpenCV coloca el origen de su imagen en la parte superior izquierda, por lo cual comienzo restando w / 2 y h / 2 y finalizo agregando de nuevo en
cv::Point2f convert_pt(cv::Point2f point,int w,int h)
{
//center the point at 0,0
cv::Point2f pc(point.x-w/2,point.y-h/2);
//these are your free parameters
float f = w;
float r = w;
float omega = w/2;
float z0 = f - sqrt(r*r-omega*omega);
float zc = (2*z0+sqrt(4*z0*z0-4*(pc.x*pc.x/(f*f)+1)*(z0*z0-r*r)))/(2* (pc.x*pc.x/(f*f)+1));
cv::Point2f final_point(pc.x*zc/f,pc.y*zc/f);
final_point.x += w/2;
final_point.y += h/2;
return final_point;
}
Ahora todo lo que queda es muestrear cada punto en la nueva imagen en la imagen antigua. Hay muchas maneras de hacer esto y yo hago la más simple que conozco aquí, la interpolación bilineal. Además, esto solo está configurado para funcionar en escala de grises, por lo que trabajar en color es simple, simplemente aplique el proceso a los 3 canales. Solo pensé que sería un poco más claro de esta manera.
for(int y = 0; y < height; y++)
{
for(int x = 0; x < width; x++)
{
cv::Point2f current_pos(x,y);
current_pos = convert_pt(current_pos, width, height);
cv::Point2i top_left((int)current_pos.x,(int)current_pos.y); //top left because of integer rounding
//make sure the point is actually inside the original image
if(top_left.x < 0 ||
top_left.x > width-2 ||
top_left.y < 0 ||
top_left.y > height-2)
{
continue;
}
//bilinear interpolation
float dx = current_pos.x-top_left.x;
float dy = current_pos.y-top_left.y;
float weight_tl = (1.0 - dx) * (1.0 - dy);
float weight_tr = (dx) * (1.0 - dy);
float weight_bl = (1.0 - dx) * (dy);
float weight_br = (dx) * (dy);
uchar value = weight_tl * image.at<uchar>(top_left) +
weight_tr * image.at<uchar>(top_left.y,top_left.x+1) +
weight_bl * image.at<uchar>(top_left.y+1,top_left.x) +
weight_br * image.at<uchar>(top_left.y+1,top_left.x+1);
dest_im.at<uchar>(y,x) = value;
}
}
Aquí hay una salida de muestra para f = w / 2 y r = w.
Quiero deformar una imagen plana de manera que parezca ser la proyección que proviene de un cilindro.
Tengo una imagen plana como esta:
Y quiero mostrarlo como algo así en la imagen 2D:
Estoy un poco eliminado en proyecciones geométricas. Visité algunas otras preguntas como this pero no entiendo cómo representaré estas coordenadas cilíndricas (theta y rho) en las coordenadas x, y en el plano cartesiano (x, y). ¿Podrían ayudarme con un ejemplo elaborado? Lo estoy codificando para iPhone y no estoy usando ninguna biblioteca de terceros como OpenCV, etc.
Gracias un montón.