透视变形矩形的比例
给定一个矩形扭曲的二维图片:
我知道这个形状最初是一个矩形,但我不知道它的原始大小。
如果我知道这幅图中angular点的像素坐标,我怎样才能计算出原来的比例,即矩形的商(宽度/高度)?
(背景:目标是自动不失真矩形文件的照片,边缘检测可能会做hough变换)
更新:
有一些讨论是否可以根据给出的信息来确定宽高比。 我的天真的想法是,这一定是可能的,因为我想不出一个1:4矩形投影到上面描述的四边形的方法。 这个比例显然接近1:1,所以应该有一种方法来确定它的math。 然而我没有证据certificate这超出了我的直觉猜测。
我还没有完全理解下面提出的论点,但是我认为我们必须有一个隐含的假设,就是我们在这里失踪了,这个假设是不同的。
但经过几个小时的search,我终于find了一些与这个问题有关的文件。 我正努力去理解那里使用的math,迄今为止没有成功。 特别是第一篇论文似乎正在讨论我想要做的事情,不幸的是没有代码例子和非常密集的math。
-
张正友,何立伟,“白板扫描与图像增强” en-us/um/people/zhang/papers/tr03-39.html p.11
“由于透视变形,矩形的图像看起来像是一个四边形,但是由于我们知道它是一个空间上的矩形,所以我们能够估计出摄像机的焦距和矩形的高宽比。
-
ROBERT M. HARALICK“从矩形透视投影确定相机参数” http://portal.acm.org/citation.cfm?id=87146
“我们展示了如何在3D空间中使用未知大小和位置的矩形的2D透视投影来确定相对于矩形平面的摄像机视angular参数。
这是我读完这篇论文后回答我的问题的尝试
- 张正友,何立伟,“白板扫描与图像增强” en-us/um/people/zhang/papers/tr03-39.html
我在SAGE中操作了一段时间的方程,并用C风格提出了这个伪代码:
// 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 // ie (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, // ie 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, // ie 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;
更新:这里是如何确定这些方程:
以下是SAGE中的代码。 可以通过http://www.sagenb.org/home/pub/704/在线访问; 。 (圣人解决方程真的很有用,可以在任何浏览器中使用,请查看)
# 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, # ie 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()) )
c代码中的简化方程式,
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
更新
阅读完更新后,查看第一个参考(白板扫描和图像增强),我可以看到缺失点在哪里。
问题的input数据是四元组(A,B,C,D),以及投影图像的中心O. 在文章中,它对应于假设u0 = v0 = 0。 加上这一点,这个问题就会受到限制,从而得到矩形的长宽比。
问题重申如下:给定Z = 0平面中的四重(A,B,C,D),find眼睛位置E(0,0,h),h> 0和3D平面P, (A,B,C,D)在P上的投影是一个矩形。
请注意,P由E确定:为了得到平行四边形,P必须包含与(EU)和(EV)平行,其中U =(AB)x(CD)和V =(AD)x(BC)。
在实验上,似乎这个问题通常有一个独特的解决scheme,对应于矩形的w / h比率的唯一值。
上一篇文章
不,您无法从投影中确定矩形比例。
在一般情况下,Z = 0平面的四个非共线点的四元(A,B,C,D)是无限多个矩形的投影,具有无限多的宽高比。
考虑两个消失点U,(AB)和(CD)和V的交点,(AD)和(BC)的交点以及两个对angular线(AC)和(BD)的交点I。 为了投影为ABCD,平行四边形的中心I必须位于包含平行于(UV)的线通过点I的平面上。在一个这样的平面上,可以find许多投影到ABCD的矩形,所有矩形都具有不同的w / h比率。
看到用Cabri 3D完成的这两个图像。 在两种情况下,ABCD不变(在灰色的Z = 0平面上),而包含矩形的蓝色平面也不变。 部分隐藏的绿线是(UV)线,可见的绿线与之平行并包含I.
大小不是真的需要,也不是比例。 考虑到他正在使用文件的照片/扫描,知道哪一边是不相关的。 我怀疑他会扫描他们的背面。
“angular相交”是纠正视angular的方法。 这可能有帮助:
如何在2D中绘制透视正确的网格
关于为什么结果给出h / w而不是w / h的问题:我想知道上面的公式20的expression是否正确。 发布是:
whRatio = sqrt ( (n2*A.transpose()^(-1) * A^(-1)*n2.transpose()) / (n3*A.transpose()^(-1) * A^(-1)*n3.transpose()) )
当我尝试用OpenCV来执行这个时,我得到一个exception。 但是,当我使用下面的公式,对于我来说更像公式20:但是基于公式20,它看起来应该是:
whRatio = sqrt ( (n2.transpose()*A.transpose()^(-1) * A^(-1)*n2) / (n3.transpose()*A.transpose()^(-1) * A^(-1)*n3) )
您可以通过此答案确定宽度/高度使用坐标阴影计算矩形3D坐标? 。 假设矩形在交点对angular点上旋转,计算它的宽度和高度。 但是当你改变假设阴影平面到真实阴影平面之间的距离时,矩形的比例与计算的宽度/高度是相同的!
不知道“相机”的距离是不可能知道这个矩形的宽度的。
从5厘米的距离观看一个小矩形,看起来就像从几米远看到的一个巨大的矩形一样
用这两个消失点和地平线以下的第三个点(即与矩形相同的一侧)画一个等腰三angular形。 那第三点将是我们的起源,到消失点的两条线将成为我们的轴线。 调用从原点到π/ 2的消失点的距离。 现在将矩形的边从消失点延伸到轴,并标记它们与轴相交的位置。 select一个轴,测量从两个标记到原点的距离,变换这些距离:x-> tan(x),差值将是该边的“真实”长度。 对另一个轴做同样的事情。 拿这两个长度的比例,你就完成了。
你需要更多的信息,转换的数字可以来自任意angular度的任何平行四边形。
所以我想你需要先做一些校准。
编辑:对于那些说我错了,这里有mathcertificate,矩形/相机的无限组合产生相同的投影:
为了简化问题(因为我们只需要边的比例),假设我们的矩形由以下几点定义: R=[(0,0),(1,0),(1,r),(0,r)]
(这种简化与将任何问题转换为仿射空间中的等价问题相同)。
变换的多边形定义为: T=[(tx0,ty0),(tx1,ty1),(tx2,ty2),(tx3,ty3)]
存在满足(Rxi,Ryi,1)*M=wi(txi,tyi,1)'
的变换matrixM = [[m00,m01,m02],[m10,m11,m12],[m20,m21,m22]]
(Rxi,Ryi,1)*M=wi(txi,tyi,1)'
如果我们扩展以上方程的点,
对于R_0
我们得到: m02-tx0*w0 = m12-ty0*w0 = m22-w0 = 0
对于R_1
我们得到: m00-tx1*w1 = m10-ty1*w1 = m20+m22-w1 = 0
对于R_2
我们得到: m00+r*m01-tx2*w2 = m10+r*m11-ty2*w2 = m20+r*m21+m22-w2 = 0
对于R_3
我们得到: m00+r*m01-tx3*w3 = m10+r*m11-ty3*w3 = m20 + r*m21 + m22 -w3 = 0
到目前为止,我们有12个方程,14个未知variables(matrix9个, wi
4个,比率r
1个),其余为已知值( txi
和txi
给出)。
即使系统没有被低估,一些未知数在它们之间相乘( r
和mi0
乘积)使得系统是非线性的(你可以把它转换成为每个产品分配一个新名字的线性系统,但是你会结束仍然有13个未知数,其中3个扩展到无限的解决scheme)。
如果你能在推理或math中发现任何缺陷,请告诉我。
Dropbox在他们的科技博客上有一篇广泛的文章,他们描述了他们如何解决扫描仪应用程序的问题。
https://blogs.dropbox.com/tech/2016/08/fast-document-rectification-and-enhancement/
整理文件
我们假设input文档在物理世界中是矩形的,但是如果它不完全面向相机,则图像中产生的angular将是一般的凸四边形。 因此,为了满足我们的第一个目标,我们必须撤消捕获过程所应用的几何变换。 除了相机的焦距(内在参数)之外,这种变换取决于相机相对于文档的视点(这些是所谓的外部参数)。 以下是捕获scheme的图表:
为了撤销几何变换,我们必须首先确定上述参数。 如果我们假设一个很好的对称相机(没有散光,没有歪斜等),这个模型中的未知数是:
- 相机相对于文档的3D位置(3个自由度),
- 相机相对于文档的3D方向(3个自由度),
- 文件的尺寸(2个自由度),和
- 摄像机的焦距(1自由度)。
另一方面,四个检测到的文档angular落的x和y坐标有效地给我们八个约束。 虽然看起来比约束(8)看起来有更多的未知数(9),但是未知数并不是完全自由的variables – 人们可以想象将文档实际缩放并将其放置在远离相机的位置,以获得相同的照片。 这种关系造成了额外的约束,所以我们有一个完全受限的系统来解决。 (我们解决scheme的实际系统涉及一些其他的考虑;相关的维基百科文章给出了一个很好的总结: https : //en.wikipedia.org/wiki/Camera_resectioning )
一旦参数已经恢复,我们可以撤消捕捉过程所应用的几何变换,以获得一个不错的矩形图像。 然而,这可能是一个耗时的过程:每个输出像素都会查找源图像中相应input像素的值。 当然,GPU是专门为这样的任务devise的:在虚拟空间中渲染纹理。 存在一个视图变换 – 这正好是我们刚刚解决的相机变换的逆问题 – 用这个变换可以渲染完整的input图像并获得纠正后的文档。 (一个简单的方法可以看到,一旦您的手机屏幕上有完整的input图像,您可以倾斜和平移手机,使得屏幕上的文档区域的投影显示为直线。
最后要回顾一下,在规模方面存在一个模棱两可的情况:例如,我们不能确定这个文件是一个字母大小的纸张(8.5“×11”)还是一个海报板(17“×22”)。 输出图像的尺寸应该是多less? 为了解决这个模糊问题,我们计算input图像中四边形内像素的数量,并将输出分辨率设置为与此像素数相匹配。 我们的想法是,我们不想过多采样或下采样图像。