image-processing - atributos - etiqueta img
proporciones de un rectángulo deformado en perspectiva (10)
Dada una imagen 2D de un rectángulo distorsionado por perspectiva:
Sé que la forma era originalmente un rectángulo, pero no sé su tamaño original.
Si conozco las coordenadas de píxel de las esquinas en esta imagen, ¿cómo puedo calcular las proporciones originales, es decir, el cociente (ancho / alto) del rectángulo?
(fondo: el objetivo es distorsionar automáticamente las fotos de documentos rectangulares, la detección de bordes probablemente se hará con la transformación Hough)
ACTUALIZAR:
Se ha discutido si es posible determinar la relación ancho: altura con la información dada. Mi ingenuo pensamiento fue que debe ser posible, ya que no puedo pensar en ninguna forma de proyectar, por ejemplo, un rectángulo 1: 4 en el cuadrángulo que se muestra arriba. La relación parece claramente cercana a 1: 1, por lo que debería haber una forma de determinarla matemáticamente. Sin embargo, no tengo pruebas de esto más allá de mi intuición intuitiva.
Todavía no he entendido por completo los argumentos que se presentan a continuación, pero creo que debe haber una suposición implícita de que nos falta aquí y que se interpreta de manera diferente.
Sin embargo, después de horas de búsqueda, finalmente encontré algunos documentos relevantes para el problema. Estoy luchando por entender las matemáticas utilizadas allí, hasta ahora sin éxito. Particularmente, el primer documento parece discutir exactamente lo que quería hacer, desafortunadamente sin ejemplos de código y matemática muy densa.
Zhengyou Zhang, Li-Wei He, "Escaneo de la pizarra y mejora de la imagen" http://research.microsoft.com/en-us/um/people/zhang/papers/tr03-39.pdf p.11
"Debido a la distorsión de la perspectiva, la imagen de un rectángulo parece ser un cuadrángulo. Sin embargo, dado que sabemos que es un rectángulo en el espacio, podemos estimar tanto la distancia focal de la cámara como la relación de aspecto del rectángulo".
ROBERT M. HARALICK "Determinar los parámetros de la cámara desde la perspectiva de la proyección de un rectángulo" http://portal.acm.org/citation.cfm?id=87146
"mostramos cómo usar la proyección bidimensional en perspectiva de un rectángulo de tamaño y posición desconocidos en el espacio tridimensional para determinar los parámetros del ángulo de aspecto de la cámara en relación con los planos del rectángulo".
Aquí está mi intento de responder mi pregunta después de leer el documento
- Zhengyou Zhang, Li-Wei He, "Escaneo de la pizarra y mejora de la imagen" http://research.microsoft.com/en-us/um/people/zhang/papers/tr03-39.pdf
Manipulé las ecuaciones durante un tiempo en SAGE, y se me ocurrió este pseudo-código en c-style:
// in case it matters: licensed under GPLv2 or later
// legend:
// sqr(x) = x*x
// sqrt(x) = square root of x
// let m1x,m1y ... m4x,m4y be the (x,y) pixel coordinates
// of the 4 corners of the detected quadrangle
// i.e. (m1x, m1y) are the cordinates of the first corner,
// (m2x, m2y) of the second corner and so on.
// let u0, v0 be the pixel coordinates of the principal point of the image
// for a normal camera this will be the center of the image,
// i.e. u0=IMAGEWIDTH/2; v0 =IMAGEHEIGHT/2
// This assumption does not hold if the image has been cropped asymmetrically
// first, transform the image so the principal point is at (0,0)
// this makes the following equations much easier
m1x = m1x - u0;
m1y = m1y - v0;
m2x = m2x - u0;
m2y = m2y - v0;
m3x = m3x - u0;
m3y = m3y - v0;
m4x = m4x - u0;
m4y = m4y - v0;
// temporary variables k2, k3
double k2 = ((m1y - m4y)*m3x - (m1x - m4x)*m3y + m1x*m4y - m1y*m4x) /
((m2y - m4y)*m3x - (m2x - m4x)*m3y + m2x*m4y - m2y*m4x) ;
double k3 = ((m1y - m4y)*m2x - (m1x - m4x)*m2y + m1x*m4y - m1y*m4x) /
((m3y - m4y)*m2x - (m3x - m4x)*m2y + m3x*m4y - m3y*m4x) ;
// f_squared is the focal length of the camera, squared
// if k2==1 OR k3==1 then this equation is not solvable
// if the focal length is known, then this equation is not needed
// in that case assign f_squared= sqr(focal_length)
double f_squared =
-((k3*m3y - m1y)*(k2*m2y - m1y) + (k3*m3x - m1x)*(k2*m2x - m1x)) /
((k3 - 1)*(k2 - 1)) ;
//The width/height ratio of the original rectangle
double whRatio = sqrt(
(sqr(k2 - 1) + sqr(k2*m2y - m1y)/f_squared + sqr(k2*m2x - m1x)/f_squared) /
(sqr(k3 - 1) + sqr(k3*m3y - m1y)/f_squared + sqr(k3*m3x - m1x)/f_squared)
) ;
// if k2==1 AND k3==1, then the focal length equation is not solvable
// but the focal length is not needed to calculate the ratio.
// I am still trying to figure out under which circumstances k2 and k3 become 1
// but it seems to be when the rectangle is not distorted by perspective,
// i.e. viewed straight on. Then the equation is obvious:
if (k2==1 && k3==1) whRatio = sqrt(
(sqr(m2y-m1y) + sqr(m2x-m1x)) /
(sqr(m3y-m1y) + sqr(m3x-m1x))
// After testing, I found that the above equations
// actually give the height/width ratio of the rectangle,
// not the width/height ratio.
// If someone can find the error that caused this,
// I would be most grateful.
// until then:
whRatio = 1/whRatio;
Actualización: aquí es cómo se determinaron estas ecuaciones:
El siguiente es el código en SAGE . Se puede acceder en línea en http://www.sagenb.org/home/pub/704/ . (Sage es muy útil para resolver ecuaciones y se puede usar en cualquier navegador, compruébalo)
# CALCULATING THE ASPECT RATIO OF A RECTANGLE DISTORTED BY PERSPECTIVE
#
# BIBLIOGRAPHY:
# [zhang-single]: "Single-View Geometry of A Rectangle
# With Application to Whiteboard Image Rectification"
# by Zhenggyou Zhang
# http://research.microsoft.com/users/zhang/Papers/WhiteboardRectification.pdf
# pixel coordinates of the 4 corners of the quadrangle (m1, m2, m3, m4)
# see [zhang-single] figure 1
m1x = var(''m1x'')
m1y = var(''m1y'')
m2x = var(''m2x'')
m2y = var(''m2y'')
m3x = var(''m3x'')
m3y = var(''m3y'')
m4x = var(''m4x'')
m4y = var(''m4y'')
# pixel coordinates of the principal point of the image
# for a normal camera this will be the center of the image,
# i.e. u0=IMAGEWIDTH/2; v0 =IMAGEHEIGHT/2
# This assumption does not hold if the image has been cropped asymmetrically
u0 = var(''u0'')
v0 = var(''v0'')
# pixel aspect ratio; for a normal camera pixels are square, so s=1
s = var(''s'')
# homogenous coordinates of the quadrangle
m1 = vector ([m1x,m1y,1])
m2 = vector ([m2x,m2y,1])
m3 = vector ([m3x,m3y,1])
m4 = vector ([m4x,m4y,1])
# the following equations are later used in calculating the the focal length
# and the rectangle''s aspect ratio.
# temporary variables: k2, k3, n2, n3
# see [zhang-single] Equation 11, 12
k2_ = m1.cross_product(m4).dot_product(m3) / m2.cross_product(m4).dot_product(m3)
k3_ = m1.cross_product(m4).dot_product(m2) / m3.cross_product(m4).dot_product(m2)
k2 = var(''k2'')
k3 = var(''k3'')
# see [zhang-single] Equation 14,16
n2 = k2 * m2 - m1
n3 = k3 * m3 - m1
# the focal length of the camera.
f = var(''f'')
# see [zhang-single] Equation 21
f_ = sqrt(
-1 / (
n2[2]*n3[2]*s^2
) * (
(
n2[0]*n3[0] - (n2[0]*n3[2]+n2[2]*n3[0])*u0 + n2[2]*n3[2]*u0^2
)*s^2 + (
n2[1]*n3[1] - (n2[1]*n3[2]+n2[2]*n3[1])*v0 + n2[2]*n3[2]*v0^2
)
)
)
# standard pinhole camera matrix
# see [zhang-single] Equation 1
A = matrix([[f,0,u0],[0,s*f,v0],[0,0,1]])
#the width/height ratio of the original rectangle
# see [zhang-single] Equation 20
whRatio = sqrt (
(n2*A.transpose()^(-1) * A^(-1)*n2.transpose()) /
(n3*A.transpose()^(-1) * A^(-1)*n3.transpose())
)
Las ecuaciones simplificadas en el código c están determinadas por
print "simplified equations, assuming u0=0, v0=0, s=1"
print "k2 := ", k2_
print "k3 := ", k3_
print "f := ", f_(u0=0,v0=0,s=1)
print "whRatio := ", whRatio(u0=0,v0=0,s=1)
simplified equations, assuming u0=0, v0=0, s=1
k2 := ((m1y - m4y)*m3x - (m1x - m4x)*m3y + m1x*m4y - m1y*m4x)/((m2y
- m4y)*m3x - (m2x - m4x)*m3y + m2x*m4y - m2y*m4x)
k3 := ((m1y - m4y)*m2x - (m1x - m4x)*m2y + m1x*m4y - m1y*m4x)/((m3y
- m4y)*m2x - (m3x - m4x)*m2y + m3x*m4y - m3y*m4x)
f := sqrt(-((k3*m3y - m1y)*(k2*m2y - m1y) + (k3*m3x - m1x)*(k2*m2x
- m1x))/((k3 - 1)*(k2 - 1)))
whRatio := sqrt(((k2 - 1)^2 + (k2*m2y - m1y)^2/f^2 + (k2*m2x -
m1x)^2/f^2)/((k3 - 1)^2 + (k3*m3y - m1y)^2/f^2 + (k3*m3x -
m1x)^2/f^2))
print "Everything in one equation:"
print "whRatio := ", whRatio(f=f_)(k2=k2_,k3=k3_)(u0=0,v0=0,s=1)
Everything in one equation:
whRatio := sqrt(((((m1y - m4y)*m2x - (m1x - m4x)*m2y + m1x*m4y -
m1y*m4x)/((m3y - m4y)*m2x - (m3x - m4x)*m2y + m3x*m4y - m3y*m4x) -
1)*(((m1y - m4y)*m3x - (m1x - m4x)*m3y + m1x*m4y - m1y*m4x)/((m2y -
m4y)*m3x - (m2x - m4x)*m3y + m2x*m4y - m2y*m4x) - 1)*(((m1y -
m4y)*m3x - (m1x - m4x)*m3y + m1x*m4y - m1y*m4x)*m2y/((m2y - m4y)*m3x
- (m2x - m4x)*m3y + m2x*m4y - m2y*m4x) - m1y)^2/((((m1y - m4y)*m2x -
(m1x - m4x)*m2y + m1x*m4y - m1y*m4x)*m3y/((m3y - m4y)*m2x - (m3x -
m4x)*m2y + m3x*m4y - m3y*m4x) - m1y)*(((m1y - m4y)*m3x - (m1x -
m4x)*m3y + m1x*m4y - m1y*m4x)*m2y/((m2y - m4y)*m3x - (m2x - m4x)*m3y
+ m2x*m4y - m2y*m4x) - m1y) + (((m1y - m4y)*m2x - (m1x - m4x)*m2y +
m1x*m4y - m1y*m4x)*m3x/((m3y - m4y)*m2x - (m3x - m4x)*m2y + m3x*m4y
- m3y*m4x) - m1x)*(((m1y - m4y)*m3x - (m1x - m4x)*m3y + m1x*m4y -
m1y*m4x)*m2x/((m2y - m4y)*m3x - (m2x - m4x)*m3y + m2x*m4y - m2y*m4x)
- m1x)) + (((m1y - m4y)*m2x - (m1x - m4x)*m2y + m1x*m4y -
m1y*m4x)/((m3y - m4y)*m2x - (m3x - m4x)*m2y + m3x*m4y - m3y*m4x) -
1)*(((m1y - m4y)*m3x - (m1x - m4x)*m3y + m1x*m4y - m1y*m4x)/((m2y -
m4y)*m3x - (m2x - m4x)*m3y + m2x*m4y - m2y*m4x) - 1)*(((m1y -
m4y)*m3x - (m1x - m4x)*m3y + m1x*m4y - m1y*m4x)*m2x/((m2y - m4y)*m3x
- (m2x - m4x)*m3y + m2x*m4y - m2y*m4x) - m1x)^2/((((m1y - m4y)*m2x -
(m1x - m4x)*m2y + m1x*m4y - m1y*m4x)*m3y/((m3y - m4y)*m2x - (m3x -
m4x)*m2y + m3x*m4y - m3y*m4x) - m1y)*(((m1y - m4y)*m3x - (m1x -
m4x)*m3y + m1x*m4y - m1y*m4x)*m2y/((m2y - m4y)*m3x - (m2x - m4x)*m3y
+ m2x*m4y - m2y*m4x) - m1y) + (((m1y - m4y)*m2x - (m1x - m4x)*m2y +
m1x*m4y - m1y*m4x)*m3x/((m3y - m4y)*m2x - (m3x - m4x)*m2y + m3x*m4y
- m3y*m4x) - m1x)*(((m1y - m4y)*m3x - (m1x - m4x)*m3y + m1x*m4y -
m1y*m4x)*m2x/((m2y - m4y)*m3x - (m2x - m4x)*m3y + m2x*m4y - m2y*m4x)
- m1x)) - (((m1y - m4y)*m3x - (m1x - m4x)*m3y + m1x*m4y -
m1y*m4x)/((m2y - m4y)*m3x - (m2x - m4x)*m3y + m2x*m4y - m2y*m4x) -
1)^2)/((((m1y - m4y)*m2x - (m1x - m4x)*m2y + m1x*m4y -
m1y*m4x)/((m3y - m4y)*m2x - (m3x - m4x)*m2y + m3x*m4y - m3y*m4x) -
1)*(((m1y - m4y)*m3x - (m1x - m4x)*m3y + m1x*m4y - m1y*m4x)/((m2y -
m4y)*m3x - (m2x - m4x)*m3y + m2x*m4y - m2y*m4x) - 1)*(((m1y -
m4y)*m2x - (m1x - m4x)*m2y + m1x*m4y - m1y*m4x)*m3y/((m3y - m4y)*m2x
- (m3x - m4x)*m2y + m3x*m4y - m3y*m4x) - m1y)^2/((((m1y - m4y)*m2x -
(m1x - m4x)*m2y + m1x*m4y - m1y*m4x)*m3y/((m3y - m4y)*m2x - (m3x -
m4x)*m2y + m3x*m4y - m3y*m4x) - m1y)*(((m1y - m4y)*m3x - (m1x -
m4x)*m3y + m1x*m4y - m1y*m4x)*m2y/((m2y - m4y)*m3x - (m2x - m4x)*m3y
+ m2x*m4y - m2y*m4x) - m1y) + (((m1y - m4y)*m2x - (m1x - m4x)*m2y +
m1x*m4y - m1y*m4x)*m3x/((m3y - m4y)*m2x - (m3x - m4x)*m2y + m3x*m4y
- m3y*m4x) - m1x)*(((m1y - m4y)*m3x - (m1x - m4x)*m3y + m1x*m4y -
m1y*m4x)*m2x/((m2y - m4y)*m3x - (m2x - m4x)*m3y + m2x*m4y - m2y*m4x)
- m1x)) + (((m1y - m4y)*m2x - (m1x - m4x)*m2y + m1x*m4y -
m1y*m4x)/((m3y - m4y)*m2x - (m3x - m4x)*m2y + m3x*m4y - m3y*m4x) -
1)*(((m1y - m4y)*m3x - (m1x - m4x)*m3y + m1x*m4y - m1y*m4x)/((m2y -
m4y)*m3x - (m2x - m4x)*m3y + m2x*m4y - m2y*m4x) - 1)*(((m1y -
m4y)*m2x - (m1x - m4x)*m2y + m1x*m4y - m1y*m4x)*m3x/((m3y - m4y)*m2x
- (m3x - m4x)*m2y + m3x*m4y - m3y*m4x) - m1x)^2/((((m1y - m4y)*m2x -
(m1x - m4x)*m2y + m1x*m4y - m1y*m4x)*m3y/((m3y - m4y)*m2x - (m3x -
m4x)*m2y + m3x*m4y - m3y*m4x) - m1y)*(((m1y - m4y)*m3x - (m1x -
m4x)*m3y + m1x*m4y - m1y*m4x)*m2y/((m2y - m4y)*m3x - (m2x - m4x)*m3y
+ m2x*m4y - m2y*m4x) - m1y) + (((m1y - m4y)*m2x - (m1x - m4x)*m2y +
m1x*m4y - m1y*m4x)*m3x/((m3y - m4y)*m2x - (m3x - m4x)*m2y + m3x*m4y
- m3y*m4x) - m1x)*(((m1y - m4y)*m3x - (m1x - m4x)*m3y + m1x*m4y -
m1y*m4x)*m2x/((m2y - m4y)*m3x - (m2x - m4x)*m3y + m2x*m4y - m2y*m4x)
- m1x)) - (((m1y - m4y)*m2x - (m1x - m4x)*m2y + m1x*m4y -
m1y*m4x)/((m3y - m4y)*m2x - (m3x - m4x)*m2y + m3x*m4y - m3y*m4x) -
1)^2))
# some testing:
# - choose a random rectangle,
# - project it onto a random plane,
# - insert the corners in the above equations,
# - check if the aspect ratio is correct.
from sage.plot.plot3d.transform import rotate_arbitrary
#redundandly random rotation matrix
rand_rotMatrix = /
rotate_arbitrary((uniform(-5,5),uniform(-5,5),uniform(-5,5)),uniform(-5,5)) */
rotate_arbitrary((uniform(-5,5),uniform(-5,5),uniform(-5,5)),uniform(-5,5)) */
rotate_arbitrary((uniform(-5,5),uniform(-5,5),uniform(-5,5)),uniform(-5,5))
#random translation vector
rand_transVector = vector((uniform(-10,10),uniform(-10,10),uniform(-10,10))).transpose()
#random rectangle parameters
rand_width =uniform(0.1,10)
rand_height=uniform(0.1,10)
rand_left =uniform(-10,10)
rand_top =uniform(-10,10)
#random focal length and principal point
rand_f = uniform(0.1,100)
rand_u0 = uniform(-100,100)
rand_v0 = uniform(-100,100)
# homogenous standard pinhole projection, see [zhang-single] Equation 1
hom_projection = A * rand_rotMatrix.augment(rand_transVector)
# construct a random rectangle in the plane z=0, then project it randomly
rand_m1hom = hom_projection*vector((rand_left ,rand_top ,0,1)).transpose()
rand_m2hom = hom_projection*vector((rand_left ,rand_top+rand_height,0,1)).transpose()
rand_m3hom = hom_projection*vector((rand_left+rand_width,rand_top ,0,1)).transpose()
rand_m4hom = hom_projection*vector((rand_left+rand_width,rand_top+rand_height,0,1)).transpose()
#change type from 1x3 matrix to vector
rand_m1hom = rand_m1hom.column(0)
rand_m2hom = rand_m2hom.column(0)
rand_m3hom = rand_m3hom.column(0)
rand_m4hom = rand_m4hom.column(0)
#normalize
rand_m1hom = rand_m1hom/rand_m1hom[2]
rand_m2hom = rand_m2hom/rand_m2hom[2]
rand_m3hom = rand_m3hom/rand_m3hom[2]
rand_m4hom = rand_m4hom/rand_m4hom[2]
#substitute random values for f, u0, v0
rand_m1hom = rand_m1hom(f=rand_f,s=1,u0=rand_u0,v0=rand_v0)
rand_m2hom = rand_m2hom(f=rand_f,s=1,u0=rand_u0,v0=rand_v0)
rand_m3hom = rand_m3hom(f=rand_f,s=1,u0=rand_u0,v0=rand_v0)
rand_m4hom = rand_m4hom(f=rand_f,s=1,u0=rand_u0,v0=rand_v0)
# printing the randomly choosen values
print "ground truth: f=", rand_f, "; ratio=", rand_width/rand_height
# substitute all the variables in the equations:
print "calculated: f= ",/
f_(k2=k2_,k3=k3_)(s=1,u0=rand_u0,v0=rand_v0)(
m1x=rand_m1hom[0],m1y=rand_m1hom[1],
m2x=rand_m2hom[0],m2y=rand_m2hom[1],
m3x=rand_m3hom[0],m3y=rand_m3hom[1],
m4x=rand_m4hom[0],m4y=rand_m4hom[1],
),"; 1/ratio=", /
1/whRatio(f=f_)(k2=k2_,k3=k3_)(s=1,u0=rand_u0,v0=rand_v0)(
m1x=rand_m1hom[0],m1y=rand_m1hom[1],
m2x=rand_m2hom[0],m2y=rand_m2hom[1],
m3x=rand_m3hom[0],m3y=rand_m3hom[1],
m4x=rand_m4hom[0],m4y=rand_m4hom[1],
)
print "k2 = ", k2_(
m1x=rand_m1hom[0],m1y=rand_m1hom[1],
m2x=rand_m2hom[0],m2y=rand_m2hom[1],
m3x=rand_m3hom[0],m3y=rand_m3hom[1],
m4x=rand_m4hom[0],m4y=rand_m4hom[1],
), "; k3 = ", k3_(
m1x=rand_m1hom[0],m1y=rand_m1hom[1],
m2x=rand_m2hom[0],m2y=rand_m2hom[1],
m3x=rand_m3hom[0],m3y=rand_m3hom[1],
m4x=rand_m4hom[0],m4y=rand_m4hom[1],
)
# ATTENTION: testing revealed, that the whRatio
# is actually the height/width ratio,
# not the width/height ratio
# This contradicts [zhang-single]
# if anyone can find the error that caused this, I''d be grateful
ground truth: f= 72.1045134124554 ; ratio= 3.46538779959142
calculated: f= 72.1045134125 ; 1/ratio= 3.46538779959
k2 = 0.99114614987 ; k3 = 1.57376280159
Dibuja un triángulo isósceles derecho con esos dos puntos de fuga y un tercer punto debajo del horizonte (es decir, en el mismo lado del horizonte que el rectángulo). Ese tercer punto será nuestro origen y las dos líneas para los puntos de fuga serán nuestros ejes. Llame la distancia desde el origen a un punto de fuga pi / 2. Ahora extienda los lados del rectángulo desde los puntos de fuga a los ejes, y marque dónde se cruzan los ejes. Elija un eje, mida las distancias desde las dos marcas hasta el origen, transforme esas distancias: x-> tan (x), y la diferencia será la longitud "verdadera" de ese lado. Haz lo mismo para el otro eje. Tome la proporción de esas dos longitudes y listo.
Dropbox tiene un extenso artículo en su blog de tecnología donde describen cómo resolvieron el problema para su aplicación de escáner.
https://blogs.dropbox.com/tech/2016/08/fast-document-rectification-and-enhancement/
Rectificando un documento
Suponemos que el documento de entrada es rectangular en el mundo físico, pero si no está exactamente frente a la cámara, las esquinas resultantes en la imagen serán un cuadrilátero convexo general. Entonces, para satisfacer nuestro primer objetivo, debemos deshacer la transformación geométrica aplicada por el proceso de captura. Esta transformación depende del punto de vista de la cámara en relación con el documento (estos son los llamados parámetros extrínsecos), además de cosas como la longitud focal de la cámara (los parámetros intrínsecos). Aquí hay un diagrama del escenario de captura:
Para deshacer la transformación geométrica, primero debemos determinar dichos parámetros. Si asumimos una cámara muy simétrica (sin astigmatismo, sin sesgo, etcétera), las incógnitas en este modelo son:
- la ubicación 3D de la cámara en relación con el documento (3 grados de libertad),
- la orientación 3D de la cámara en relación con el documento (3 grados de libertad),
- las dimensiones del documento (2 grados de libertad), y
- la distancia focal de la cámara (1 grado de libertad).
Por otro lado, las coordenadas xey de las cuatro esquinas de documento detectadas nos dan efectivamente ocho restricciones. Si bien hay aparentemente más incógnitas (9) que restricciones (8), las incógnitas no son variables totalmente libres: se podría imaginar escalar el documento físicamente y colocarlo más lejos de la cámara, para obtener una fotografía idéntica. Esta relación establece una restricción adicional, por lo que tenemos un sistema completamente restringido para ser resuelto. (El sistema real de ecuaciones que resolvemos involucra algunas otras consideraciones, el artículo relevante de Wikipedia ofrece un buen resumen: https://en.wikipedia.org/wiki/Camera_resectioning )
Una vez que los parámetros se han recuperado, podemos deshacer la transformación geométrica aplicada por el proceso de captura para obtener una imagen rectangular agradable. Sin embargo, este es un proceso potencialmente lento: uno buscaría, para cada píxel de salida, el valor del píxel de entrada correspondiente en la imagen de origen. Por supuesto, las GPU están específicamente diseñadas para tareas como esta: renderizar una textura en un espacio virtual. Existe una transformación de vista, que es la inversa de la transformación de cámara que acabamos de resolver, con la que se puede representar la imagen de entrada completa y obtener el documento rectificado. (Una manera fácil de ver esto es observar que una vez que tiene la imagen de entrada completa en la pantalla de su teléfono, puede inclinar y traducir el teléfono de manera que la proyección de la región del documento en la pantalla le parezca rectilínea).
Por último, recuerde que había una ambigüedad con respecto a la escala: no podemos decir si el documento era de tamaño carta (8.5 "x 11") o una cartulina (17 "x 22"), por ejemplo. ¿Cuáles deberían ser las dimensiones de la imagen de salida? Para resolver esta ambigüedad, contamos el número de píxeles dentro del cuadrilátero en la imagen de entrada, y establecemos la resolución de salida para que coincida con este conteo de píxeles. La idea es que no queremos aumentar o disminuir la resolución de la imagen demasiado.
El tamaño no es realmente necesario, y tampoco lo son las proporciones. Y saber qué lado está levantado es algo irrelevante teniendo en cuenta que está usando fotos / escaneos de documentos. Dudo que vaya a escanear la parte posterior de ellos.
"Intersección de esquina" es el método para corregir la perspectiva. Esto podría ser de ayuda:
En la pregunta de por qué los resultados dan h / w en lugar de w / h: me pregunto si la expresión de la ecuación 20 anterior es correcta. Publicado es:
whRatio = sqrt (
(n2*A.transpose()^(-1) * A^(-1)*n2.transpose()) /
(n3*A.transpose()^(-1) * A^(-1)*n3.transpose())
)
Cuando trato de ejecutar eso con OpenCV, obtengo una excepción. Pero todo funciona correctamente cuando uso la siguiente ecuación, que para mí se parece más a la ecuación 20: pero en función de la ecuación 20, parece que debería ser:
whRatio = sqrt (
(n2.transpose()*A.transpose()^(-1) * A^(-1)*n2) /
(n3.transpose()*A.transpose()^(-1) * A^(-1)*n3)
)
Necesitas más información, esa figura transformada podría provenir de cualquier paralelogramo dado una perspectiva arbitraria.
Así que supongo que primero necesitas hacer algún tipo de calibración.
Edit: para aquellos que dijeron que estaba equivocado, aquí va la prueba matemática de que hay infinitas combinaciones de rectángulos / cámaras que ceden a la misma proyección:
Para simplificar el problema (ya que solo necesitamos la relación de los lados) supongamos que nuestro rectángulo está definido por los siguientes puntos: R=[(0,0),(1,0),(1,r),(0,r)]
(esta simplificación es lo mismo que transformar cualquier problema en uno equivalente en un espacio afín).
El polígono transformado se define como: T=[(tx0,ty0),(tx1,ty1),(tx2,ty2),(tx3,ty3)]
Existe una matriz de transformación M = [[m00,m01,m02],[m10,m11,m12],[m20,m21,m22]]
que satisface (Rxi,Ryi,1)*M=wi(txi,tyi,1)''
si ampliamos la ecuación anterior para los puntos,
para R_0
obtenemos: m02-tx0*w0 = m12-ty0*w0 = m22-w0 = 0
para R_1
obtenemos: m00-tx1*w1 = m10-ty1*w1 = m20+m22-w1 = 0
para R_2
obtenemos: m00+r*m01-tx2*w2 = m10+r*m11-ty2*w2 = m20+r*m21+m22-w2 = 0
y para R_3
obtenemos: m00+r*m01-tx3*w3 = m10+r*m11-ty3*w3 = m20 + r*m21 + m22 -w3 = 0
Hasta ahora tenemos 12 ecuaciones, 14 variables desconocidas (9 de la matriz, 4 de la wi
, y 1 para la relación r
) y el resto son valores conocidos (se txi
y tyi
).
Incluso si el sistema no estuviera poco especificado, algunas de las incógnitas se multiplican entre sí ( r
y mi0
productos) haciendo que el sistema no sea lineal (se podría transformar en un sistema lineal asignando un nuevo nombre a cada producto, pero se terminará todavía con 13 incógnitas, y 3 de ellas se están ampliando a soluciones infinitas).
Si puede encontrar algún defecto en el razonamiento o las matemáticas, hágamelo saber.
Parece que todavía hay algo de confusión en este interesante problema. Quiero dar una explicación fácil de seguir cuando el problema puede y no puede ser resuelto.
Restricciones y grados de libertad
Normalmente, cuando nos enfrentamos a un problema como este, lo primero que debemos hacer es evaluar el número de grados de libertad (ND) desconocidos N y el número de ecuaciones independientes M que tenemos para restringir los DoF desconocidos. Es imposible resolver el problema si N si excede M (lo que significa que hay menos restricciones que las desconocidas). Podemos descartar todos los problemas donde este es el caso como insolubles. Si N no excede M, entonces es posible resolver el problema con una solución única, pero esto no está garantizado (consulte el penúltimo párrafo para ver un ejemplo).
Usemos p 1, p 2, p 3 y p 4 para indicar las posiciones de las 4 esquinas de la superficie plana en coordenadas mundiales. Use R y t para ser la rotación 3D y la traducción que los transforma en coordenadas de la cámara. Usemos K para denotar la matriz intrínseca de la cámara 3x3. Ignoraremos la distorsión de la lente por ahora. La posición 2D de la i ésima esquina en la imagen de la cámara está dada por q i = f ( K ( Rp i + t )) donde f es la función de proyección f (x, y, z) = (x / z, y / z ) Usando esta ecuación sabemos que cada esquina en la imagen nos da dos ecuaciones (es decir, dos restricciones) en nuestras incógnitas: una de la componente x de q i y una de la componente y. Entonces tenemos un total de 8 restricciones para trabajar. El nombre oficial de estas restricciones son las restricciones de reproyección .
Entonces, ¿cuáles son nuestros DoF desconocidos? Ciertamente, R y t son desconocidos, porque no conocemos la postura de la cámara en coordenadas mundiales. Por lo tanto, ya tenemos 6 DoF desconocidos: 3 para R (por ejemplo, yaw, pitch and roll) y 3 para t . Por lo tanto, puede haber un máximo de dos incógnitas en los términos restantes ( K , p 1, p 2, p 3, p 4).
Diferentes problemas
Podemos construir diferentes problemas dependiendo de los dos términos en ( K , p 1, p 2, p 3, p 4) que consideraremos como desconocidos. En este punto, escribamos K en la forma habitual: K = (fx, 0, cx; 0, fy, cy; 0,0,1) donde fx y fy son los términos de distancia focal (fx / fy se llama normalmente el relación de aspecto de la imagen) y (cx, cy) es el punto principal (el centro de proyección en la imagen).
Podríamos obtener un problema al tener fx y fy como nuestras dos incógnitas, y asumir (cx, cy, p 1, p 2, p 3, p 4) son todas conocidas. De hecho, este problema se utiliza y se resuelve en el método de calibración de la cámara de OpenCV, utilizando imágenes de un objetivo plano de tablero de ajedrez. Esto se usa para obtener una estimación inicial de fx y fy, suponiendo que el punto principal está en el centro de la imagen (lo cual es una suposición muy razonable para la mayoría de las cámaras).
Alternatively we can create a different problem by assuming fx=fy, which again is quite reasonable for many cameras, and assume this focal length (denoted as f) is the only unknown in K . Therefore we still have one unknowns left to play with (recall we can have a maximum of two unknowns). So let''s use this by supposing we known the shape of the plane: as a rectangle (which was the original assumption in the question). Therefore we can define the corners as follows: p 1=(0,0,0), p 2=(0,w,0), p 3=(h,0,0) and p 4=(h,w,0), where h and w denotes the height and width of the rectangle. Now, because we only have 1 unknown left, let us set this as the plane''s aspect ratio: x=w/h. Now the question is can we simultaneously recover x, f, R and t from the 8 reprojection constraints? The answer it turns out is yes! And the solution is given in Zhang''s paper cited in the question.
The scale ambiguity
One might wonder if another problem can be solved: if we assume K is known and the 2 unknowns are h and w. Can they be solved from the reprojection equations? The answer is no, and is because there is an ambiguity between the size of the plane and the plane''s depth to the camera. Specifically if we scale the corners p i by s and scale t by s, then s cancels in the reprojection equations. Therefore the absolute scale of the plane is not recoverable.
There may be other problems with different combinations for the unknown DoFs, for example having R , t , one of the principal point components and the plane"s width as unknowns. However one needs to think of which cases are of practical use. Nevertheless I haven''t yet seen a systematic set of solutions for all useful combinations!
More points
We might think that if we were to add extra point correspondences between the plane and the image, or exploit the edges of the plane, we could recover more than 8 unknown DoFs. Lamentablemente, la respuesta es no. This is because they don''t add any extra independent constraints. The reason is because the 4 corners describe completely the transform from the plane to the image. This can be seen by fitting a homography matrix using the four corners, which can then determine the positions of all other points on the plane in the image.
Puede determinar el ancho / alto mediante esta respuesta. ¿ Calcula la coordenada 3D rectangular con la coordenada su sombra? . Suponga que su rectángulo gira en el punto diagonal de intersección, calcule su ancho y alto. ¡Pero cuando se cambia la distancia entre el plano de sombra de suposición y el plano de sombra real, la proporción de rectángulo es la misma que la anchura / altura calculada!
es imposible saber el ancho de este rectángulo sin conocer la distancia de la "cámara".
un pequeño rectángulo visto desde 5 centímetros de distancia se ve igual a un gran rectángulo visto desde metros de distancia
Actualizar
Después de leer su actualización y observar la primera referencia (escaneo de la pizarra y mejora de la imagen), veo dónde está el punto que falta.
Los datos de entrada del problema son un cuádruple (A, B, C, D) Y el centro O de la imagen proyectada. En el artículo, corresponde a la suposición u0 = v0 = 0. Al agregar este punto, el problema se vuelve lo suficientemente limitado como para obtener la relación de aspecto del rectángulo.
El problema se vuelve a expresar de la siguiente manera: Dado un cuádruple (A, B, C, D) en el plano Z = 0, encuentre la posición del ojo E (0,0, h), h> 0 y un plano 3D P tal que la proyección de (A, B, C, D) en P es un rectángulo.
Tenga en cuenta que P está determinado por E: para obtener un paralelogramo, P debe contener paralelos a (EU) y (EV), donde U = (AB) x (CD) y V = (AD) x (BC).
Experimentalmente, parece que este problema tiene en general una única solución, que corresponde a un valor único de la relación w / h del rectángulo.
Publicación anterior
No, no puedes determinar la proporción rectangular de la proyección.
En el caso general, un cuádruple (A, B, C, D) de cuatro puntos no colineales del plano Z = 0 es la proyección de infinitos rectángulos, con infinitas proporciones de ancho / alto.
Considere los dos puntos de fuga U, la intersección de (AB) y (CD) y V, la intersección de (AD) y (BC), y el punto I, la intersección de las dos diagonales (AC) y (BD). Para proyectar como ABCD, un paralelogramo de centro I debe estar en un plano que contiene la línea paralela a (UV) a través del punto I. En uno de esos planos, puede encontrar muchos rectángulos que se proyectan a ABCD, todos con una relación w / h diferente.
Vea estas dos imágenes hechas con Cabri 3D. En los dos casos, ABCD no se modifica (en el plano gris Z = 0) y tampoco cambia el plano azul que contiene el rectángulo. La línea verde parcialmente oculta es la línea (UV) y la línea verde visible es paralela a ella y contiene I.