Volver a dibujar la imagen de la perspectiva 3D a 2d

Necesito una transformación de perspectiva inversa escrita en Pascal / Delphi / Lazarus. Ver la siguiente imagen:

proceso de imagen

Creo que tengo que caminar a través de los píxeles de destino y luego calcular la posición correspondiente en la imagen de origen (para evitar problemas con los errores de redondeo, etc.).

function redraw_3d_to_2d(sourcebitmap:tbitmap, sourceaspect:extended, point_a, point_b, point_c, point_d:tpoint, megapixelcount:integer):tbitmap; var destinationbitmap:tbitmap; x,y,sx,sy:integer; begin destinationbitmap:=tbitmap.create; destinationbitmap.width=megapixelcount*sourceaspect*???; // I dont how to calculate this destinationbitmap.height=megapixelcount*sourceaspect*???; // I dont how to calculate this for x:=0 to destinationbitmap.width-1 do for y:=0 to destinationbitmap.height-1 do begin sx:=??; sy:=??; destinationbitmap.canvas.pixels[x,y]=sourcebitmap.canvas.pixels[sx,sy]; end; result:=destinationbitmap; end; 

Necesito la fórmula real … Entonces una solución OpenGL no sería ideal …

Nota: Hay una versión de esto con la composición matemática adecuada en Math SE.

Computing una transformación proyectiva

Una perspectiva es un caso especial de una transformación proyectiva , que a su vez está definida por cuatro puntos.

Paso 1: comenzando con las 4 posiciones en la imagen de origen, nombradas (x1,y1) a (x4,y4) , resuelve el siguiente sistema de ecuaciones lineales :

 [x1 x2 x3] [λ] [x4] [y1 y2 y3]∙[μ] = [y4] [ 1 1 1] [τ] [ 1] 

Las columnas forman coordenadas homogéneas : una dimensión más, creada al agregar un 1 como última entrada. En pasos posteriores, los múltiplos de estos vectores se usarán para denotar los mismos puntos. Vea el último paso para ver un ejemplo de cómo volver a convertir estas coordenadas en dos dimensiones.

Paso 2: Escala las columnas por los coeficientes que acabas de calcular:

  [λ∙x1 μ∙x2 τ∙x3] A = [λ∙y1 μ∙y2 τ∙y3] [λ μ τ ] 

Esta matriz (1,0,0) a un múltiplo de (x1,y1,1) , (0,1,0) a un múltiplo de (x2,y2,1) , (0,0,1) a un múltiplo de (x3,y3,1) y (1,1,1) a (x4,y4,1) . Por lo tanto, asignará estos cuatro vectores especiales (llamados vectores de base en explicaciones posteriores) a las posiciones especificadas en la imagen.

Paso 3: repite los pasos 1 y 2 para las posiciones correspondientes en la imagen de destino, para obtener una segunda matriz llamada B

Este es un mapa de vectores de base a posiciones de destino.

Paso 4: Invierte B para obtener B⁻¹ .

B asigna desde vectores de base a las posiciones de destino, por lo que la matriz inversa se mapea en la dirección inversa.

Paso 5: Calcule la Matriz combinada C = A∙B⁻¹ .

B⁻¹ mapas de las posiciones de destino a los vectores de base, mientras que A asigna desde allí a las posiciones de origen. Entonces, la combinación asigna las posiciones de destino a las posiciones de origen.

Paso 6: para cada píxel (x,y) de la imagen de destino, calcule el producto

 [x'] [x] [y'] = C∙[y] [z'] [1] 

Estas son las coordenadas homogéneas de tu punto transformado.

Paso 7: calcule la posición en la imagen de origen de esta manera:

 sx = x'/z' sy = y'/z' 

Esto se llama deshomogeneización del vector coordinado.

Toda esta matemática sería mucho más fácil de leer y escribir si SO fuera compatible con MathJax …

Elegir el tamaño de la imagen

El enfoque anterior asume que usted sabe la ubicación de sus esquinas en la imagen de destino. Para esto, debe conocer el ancho y el alto de esa imagen, que también está marcado con signos de interrogación en su código. Asummos que la height de la imagen de salida fue 1 , y el width fue de sourceaspect . En ese caso, el área general también sería sourceaspect . Tienes que escalar esa área por un factor de pixelcount/sourceaspect para lograr un área de pixelcount . Lo que significa que tiene que escalar cada longitud de borde por la raíz cuadrada de ese factor. Entonces, al final, tienes

 pixelcount = 1000000.*megapixelcount; width = round(sqrt(pixelcount*sourceaspect)); height = round(sqrt(pixelcount/sourceaspect)); 

Use Graphics32 , específicamente TProjectiveTransformation (para usar con el método Transform ). No olvides dejar un margen transparente en la imagen de origen para que no tengas bordes dentados.