问题

如何还原被摄像机透视的图形?

回答
从摄影机的角度还原图形,说白了就是把那些因为摄影机透视而产生的变形给“掰直”了。你想想看,你用手机拍桌子,桌子边角通常看起来是歪的,离你远的边比近的边显得更窄,对吧?这就是透视在作怪。我们要做的,就是把这种“歪”给纠正回来,让它恢复成它本来的样子,就像你站在桌子正上方俯视的时候看到的那样。

这事儿说起来有点像在跟摄影机玩“猜谜游戏”,我们需要弄清楚摄影机是怎么看到的这个世界的,然后逆着它的思路来。下面我就给你掰扯掰扯,怎么才能做到。

第一步:弄清楚摄影机的“观察角度”——找到那个神秘的“消失点”

摄影机的透视效果,其实就是源于一个叫“消失点”的概念。想象一下,你在铁路线上站着,你看远方,铁轨的两条边好像在远方汇聚到一个点上,对吧?那个点就是消失点。现实中的平行线,在摄影机里看起来就像是要汇聚到一个点上,而这个点,就是它的消失点。

平行线是关键: 很多我们想还原的图形,比如房间的墙壁、窗户框、桌子边,它们本身都是平行的。在摄影机的视角下,这些本该平行的线,会朝着不同的消失点汇聚。
找到消失点的方法:
肉眼观察 + 辅助线: 最直观的方法是在你的图形(比如照片)上画辅助线。找到那些理论上应该是平行的线(比如桌子的两条长边),沿着它们的方向画直线,看看它们在哪里“交汇”。你需要在不同的方向(比如横向和纵向)都找找,可能会有不止一个消失点。
数学计算(进阶): 如果你想更精确,就需要用到一些几何学和线性代数的知识了。这通常涉及到摄影测量学里的一些算法,但对于普通人来说,肉眼观察和辅助线是比较容易理解和操作的。

第二步:了解摄影机是如何“成像”的——“焦距”和“主点”的概念

摄影机成像,就像一个放大镜一样,但它有个“焦距”,决定了它看得有多“远”或者有多“广”。还有个“主点”,就是那个光学中心,所有的光线似乎都穿过这个点然后投射到感光元件(或者说胶片)上。

焦距(Focal Length): 焦距越短,视野越广,透视变形越明显(感觉物体离你越近,越容易被拉伸);焦距越长,视野越窄,透视变形越小(感觉像在用望远镜看)。
主点(Principal Point): 理论上,这是摄影机镜头的光学中心。它影响着图像的中心点在哪里,以及透视畸变是否对称。

第三步:反向操作——“反透视”或“去畸变”

知道了消失点和摄影机的基本参数,我们就可以开始“反向操作”了。这个过程,本质上就是对原始图像进行一种“几何变换”。

寻找“平行”的基准: 在你想要还原的图形中,尽量找到那些你应该认为是平行和垂直的线。比如桌子边、墙壁的角落。
构建“虚拟摄影机”: 你可以想象你在电脑里重新搭建一个“虚拟摄影机”和“虚拟场景”。
调整虚拟摄影机的位置和朝向: 让虚拟摄影机就像是你拍摄时摄影机的位置和角度一样,去“看”那个虚拟场景。
调整虚拟场景的“比例”和“角度”: 通过旋转和缩放虚拟场景中的物体,来匹配你照片中的物体。
“拉直”平行线: 最核心的步骤是,通过调整你的虚拟摄影机参数(比如它看向的方向、倾斜的角度),使得原本应该平行的线在最终的成像中变成平行线。
旋转或倾斜图像(但这不是最优解): 最简单粗暴的方法是直接旋转你的图像,让那些“歪斜”的边缘看起来更正。但这很容易破坏其他部分的相对关系。
更精细的变换: 更常用的方法是使用一些专门的软件(比如Photoshop,或者一些摄影测量软件),它们提供了更专业的工具来做这件事。你可以通过指定几对原本应该平行的线,让软件自动计算出需要的变换。
Homography变换(熟悉下这个词): 在计算机视觉领域,如果你的图形是平面的(比如一张纸、一个桌子表面),并且你知道它在真实世界中的四个角在照片中的位置,你就可以用一个叫做“Homography”的数学变换来把它“校正”过来,让它看起来像是一个正面拍摄的平面。这在你需要把一个照片中的平面图案“投影”到另一个平面上时也很有用。

具体的实践步骤(以你在电脑上处理照片为例):

1. 准备一张照片: 这是你的原始素材。
2. 打开图像编辑软件: Photoshop, GIMP (免费), 或者更专业的摄影测量软件都可以。
3. 找到“透视裁剪”或“变形”工具:
Photoshop中的“透视裁剪工具” (Perspective Crop Tool): 这个工具很智能。你可以画出四个框来框住你认为应该是矩形或正方形的区域(比如桌子的表面),然后它会自动帮你计算并进行透视校正。你也可以用它来直接框选整个画面进行透视调整。
Photoshop中的“变换” (Transform) 功能(Ctrl+T 或 Cmd+T): 在变换模式下,你可以右键点击,选择“透视”(Perspective) 或“扭曲”(Distort)。
透视 (Perspective): 你可以拉动角落的控制点,来模拟调整摄影机的俯仰角度。比如,如果桌子远处的边比近处的窄,你可以拉近远处的角来让它变宽,从而模拟从正面看的效果。
扭曲 (Distort): 这个更灵活,你可以自由拉动四个角的控制点,这对于处理不规则的形状或者更复杂的透视关系时很有用。
4. 使用参考线和网格: 在软件中打开“标尺”和“参考线”,或者使用“网格”。然后根据你对真实世界中线条平行的理解,拖动参考线,将它们对齐照片中那些“应该”是平行的边缘。比如,你可以在软件中画几条垂直的参考线,然后通过变换工具,让照片中的垂直物体边缘与这些参考线平行。
5. 反复调整和对比: 这个过程需要一点耐心。你可能需要反复调整,然后暂停下来,看看效果,再继续调整。观察你的图形中,那些本应垂直的边是否变得垂直了,本应平行的线是否变得平行了。
6. 输出结果: 当你对还原的效果满意时,就可以保存你的作品了。

一些需要注意的点和“小窍门”:

图形的性质: 你还原的图形是平面还是立体?如果是平面(比如一张画、一块招牌),用Homography变换会很高效。如果是立体物体(比如桌子本身、一个盒子),则需要更复杂的3D重建或者多视图几何的方法。
原始信息是否足够: 如果你照片中的线条本身已经非常模糊,或者根本就找不到明显的平行线来作为参考,那么还原的难度会大大增加,精度也会受影响。
“完美”是相对的: 很多时候,我们只能做到“尽可能”的还原,让它看起来更符合我们的视觉习惯,达到一个比较令人满意的效果。想达到数学上的绝对精确,往往需要有更详细的原始拍摄信息(比如摄影机的精确参数)或者更多的拍摄角度。
软件功能的多样性: 除了上面提到的,很多图形处理软件还有专门的“镜头校正”功能,可以自动识别一些常见的镜头畸变(比如桶形畸变或枕形畸变),但它们通常不能解决根本的透视问题,只是辅助。

总的来说,还原被摄影机透视的图形,就是通过分析摄影机视角下的几何关系(特别是消失点和平行线的汇聚),然后利用软件工具,通过几何变换的方式,将这些汇聚的线条“拉回”平行,将扭曲的形状“掰直”,从而恢复其本来的几何形态。这是一个结合了观察力、几何理解和软件操作的过程。

网友意见

user avatar

最近在看[OpenGL的教程](

Tutorial 3 : Matrices

),有一些新的理解。补充一下

@韦易笑

大神的答案(主要是前面等式的由来)。

首先,在计算机图形学里面,经常使用4个数(齐次坐标)来表示一个3维空间的点,即使用(x,y,z,w) 来代替(x,y,z)。

This will be more clear soon, but for now, just remember this :

  • If w == 1, then the vector (x,y,z,1) is a position in space.
  • If w == 0, then the vector (x,y,z,0) is a direction.

(In fact, remember this forever.)

齐次坐标最直观的好处就是实现点的平移。例如,一个把点往x正方向平移1的矩阵:

一般地,可以把转换矩阵表示为

,其中R为旋转(Rotation)矩阵,T为平移(Translation)矩阵。注意到此类矩阵相乘之后,仍然是原来的形式。

下面简单介绍一下摄像机成像原理:

The engines don’t move the ship at all. The ship stays where it is and the engines move the universe around it.

Futurama

This is the single most important tutorial of the whole set. Be sure to read it at least eight times.

涉及三个坐标系:模型坐标,世界坐标,摄像机坐标。

模型坐标为以物体为参照物的坐标系:

世界坐标系以物体外的一点作为原点:

空间中的一个点,在不同坐标系下的表示是不同的,一般情况下,坐标系之间的变换是线性的(缩放->旋转->平移),可以用矩阵表示这个变换。以图上猴子的鼻尖为例,它在模型(model)坐标系下的坐标为

,在世界(world)坐标系下的坐标为:

至于

具体是怎样的由实际变换决定。

然后,摄像机坐标系:

由世界坐标系到摄像机坐标系的转换同样是线性的:

同样是待定的。

综上:

把两个矩阵相乘,得到:

上面的公式主要是为了说明线性变换的坐标系可以用矩阵表示。然后,通过选取合适的坐标系,可以大大简化计算,以题目为例:

1. 在模型坐标系里面,选取桌面左下角为坐标系的原点,往右为x轴,往上为y轴,整个桌面在z=0平面上。

2. 在摄像机坐标系里面,选取成像焦点为原点,z=1为投影面。使得点

投影为坐标


由于Zm为0,因此削掉第三列和第四行,顺便改一下符号

然后,可以接着看韦大的第5点了。

不过我看不懂为什么第6步里面可以设M22=1的。

user avatar

多年没碰图形,不知是否存在标准做法。有也无妨,我们可以从数学上推导一下:

三维坐标(x,y,z)和纹理坐标(u, v)承线性关系。根据题主问题,可以理解为已知四个点的屏幕投影坐标(xi,yi),和对应纹理坐标(u,v),求整个纹理坐标系到屏幕坐标系的反向映射过程,即根据(u,v)求解(xi,yi)。

1. 按照纹理隐射的原理,同平面纹理坐标与空间坐标存在线性关系,设 a1-a12为常数,有:

       a1 * x +  a2 * y +  a3 * z +  a4 = u ... 线性关系 a5 * x +  a6 * y +  a7 * z +  a8 = v ... 线性关系 a9 * x + a10 * y + a11 * z + a12 = 0 ... 平面方程     

2. 求解上面的方程组,可以得到类似下面的关系,其中b1-b9为常数:

       x = b1 * u + b2 * v + b3   y = b4 * u + b5 * v + b6  z = b7 * u + b8 * v + b9      

常数 b1-b9如果展开,就是9个关于a1-a12的等式,太长了,这里不展开,有兴趣可以自己求解。

3. 屏幕上投影坐标一般是:

                  x xi = D1 * --- + C1            z                    x yi = D2 * --- + C2            z     

因为同样一个透视投影矩阵下,能隐射成屏幕上同样形状纹理的平面,在空间中存在无穷多个,而且还存在不同透视投影矩阵下,同样屏幕投影的平面存在更多无穷多个。这里我们不用去求解每个平面,直接设置 D1 = D2 = 1 且 C1 = C2 = 0 有:

             x xi = ---       z               x yi = ---       z     

4. 展开等式:

              b1 * u + b2 * v + b3 xi =  ----------------------        b7 * u + b8 * v + b9         b4 * u + b5 * v + b6 yi =  ----------------------        b7 * u + b8 * v + b9     

改变一下,用矩阵 M的各个元素 m00 - m22来代替 b1-b9。

             m00 * u + m01 * v + m02 xi = -------------------------       m20 * u + m21 * v + m22        m10 * u + m11 * v + m12 yi = -------------------------       m20 * u + m21 * v + m22     

5. 于是整个问题很简单了,就是求矩阵 M。我们知道存在矩阵 M,使得:

       / m00 m01 m02    / u  | m10 m11 m12 | . | v | = [ uo, vo, wo ] = [ xi * wo, yi * wo, wo ]  m20 m21 m22 /    1 /                 m00 * u + m01 * v + m02 xi = uo / wo = ------------------------  .......(4)                m20 * u + m21 * v + m22                 m10 * u + m11 * v + m12 yi = uo / wo = ------------------------  .......(5)                m20 * u + m21 * v + m22     

6. 再此基础上,我们已知屏幕上四个端点的位置(x0, y0)-> (x3, y3), 也知道他们对应的纹理坐标,(u0, v0) -> (u3, v3)。继续设 m22 = 1,可以列出一个方程:

       / u0 v0  1  0  0  0 -u0*x0 -v0*x0  /m00 /x0 | u1 v1  1  0  0  0 -u1*x1 -v1*x1 | |m01| |x1| | u2 v2  1  0  0  0 -u2*x2 -v2*x2 | |m02| |x2| | u3 v3  1  0  0  0 -u3*x3 -v3*x3 |.|m10|=|x3| |  0  0  0 u0 v0  1 -u0*y0 -v0*y0 | |m11| |y0| |  0  0  0 u1 v1  1 -u1*y1 -v1*y1 | |m12| |y1| |  0  0  0 u2 v2  1 -u2*y2 -v2*y2 | |m20| |y2|   0  0  0 u3 v3  1 -u3*y3 -v3*y3 / m21/ y3/     

对于这个方程组,四个端点的 x, y是已知的,同时纹理坐标 u, v也是已知的。那么我们解个方程就能求出矩阵 M的各个元素。现在你已经有了 m00 - m22,有了式子4,和式子5:

           / m00 m01 m02   M = | m10 m11 m12 |   .....  已知      m20 m21 m22 /         m00 * u + m01 * v + m02 xi = -------------------------    ... (4)       m20 * u + m21 * v + m22        m10 * u + m11 * v + m12 yi = -------------------------    ... (5)       m20 * u + m21 * v + m22     

7. 在已知 M矩阵的各个元素,已知纹理坐标(u, v),你想求对应的屏幕坐标(x, y),就可以用公式(4)和公式(5)来求解了。假设纹理为 256x256大小,最终求解原纹理图片的代码应该是:

       for (int v = 0; v < 256; v++) {      for (int u = 0; u < 256; u++) {           int x = CalculateX(M, u, v);  // 带入式子(4)           int y = CalculateY(M, u, v);  // 带入式子(5)           texture[v * 256 + u] = GetPixel(x, y);      } }     

8. 优化一下,上面每个点的乘除法太多,我们需要找找关系,设:

       A = m00 * u + m01 * v + m02     B = m10 * u + m11 * v + m12     C = m20 * u + m21 * v + m22        

于是有:x = A / C, y = B / C,而随着 u坐标的均匀递增 A, B, C成线性变化,那么我们可以用差值来完成我们的优化:

       for (int v = 0; v < 256; v++) {  float A1 = m00 *   0 + m01 * v + m02;  float A2 = m00 * 256 + m01 * v + m02;  float B1 = m10 *   0 + m11 * v + m12;  float B2 = m10 * 256 + m11 * v + m12;  float C1 = m20 *   0 + m21 * v + m22;  float C2 = m20 * 256 + m21 * v + m22;   float dA = (A2 - A1) * (1 / 256.0);   // 计算步长  float dB = (B2 - B1) * (1 / 256.0);     float dC = (C2 - C1) * (1 / 256.0);     float A = A1 + 0.5;  // 方便取整时四舍五入  float B = B1 + 0.5;    float C = C1 + 0.5;   uint32_t *pixel = &texture[v * 256];   for (int u = 0; u < 256; u++) {   float i = 1.0 / C;    // 减少一次除法   int x = (int)(A * i);   int y = (int)(B * i);   pixel[0] = GetPixel(x, y);   pixel++;   A += dA;   B += dB;   C += dC;  } }     

整个程序基本上就是这个样子了,内循环已经足够紧凑。再次观察等式关系,可以发现其实:dA = m00, dB = m10, dC = m20。还可以继续进一步化解外层循环。是不是和透视纹理绘制方法很像?一个正过程,一个逆过程而已。

注意1:越界处理,简单判断下越界坐标。

注意2:如果还需要进一步提高性能,可以尝试定点数优化计算过程,尝试继续展开循环。

(完)

类似的话题

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有