【问题标题】:Redraw image from 3d perspective to 2d将图像从 3d 透视图重绘为 2d
【发布时间】:2013-01-09 18:31:47
【问题描述】:

我需要一个用 Pascal/Delphi/Lazarus 编写的逆透视变换。见下图:

我想我需要遍历目标像素,然后计算源图像中的相应位置(以避免出现舍入误差等问题)。

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;

我需要真正的公式...所以 OpenGL 解决方案并不理想...

【问题讨论】:

    标签: delphi math geometry pascal lazarus


    【解决方案1】:

    注意:在 Math SE 上有 a version of this 具有正确的数学排版。

    计算射影变换

    透视图是projective transformation 的特例,它又由四个点定义。

    第 1 步:从源图像中的 4 个位置开始,命名为 (x1,y1)(x4,y4),您解决以下 system of linear equations

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

    列形成homogenous coordinates:多一维,通过添加1 作为最后一个条目创建。在随后的步骤中,这些向量的倍数将用于表示相同的点。有关如何将它们转换回二维坐标的示例,请参见最后一步。

    第 2 步:按您刚刚计算的系数缩放列:

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

    这个矩阵将(1,0,0)映射到(x1,y1,1)的倍数,(0,1,0)映射到(x2,y2,1)的倍数,(0,0,1)映射到(x3,y3,1)的倍数,(1,1,1)映射到(x4,y4,1)。所以它会将这四个特殊的向量(后面的解释称为基向量)映射到图像中的指定位置。

    步骤 3: 对目标图像中的相应位置重复步骤 1 和 2,以获得称为 B 的第二个矩阵。

    这是一张从基向量到目标位置的映射。

    第四步InvertB获取B⁻¹

    B 从基向量映射到目标位置,因此逆矩阵映射到相反方向。

    第 5 步:计算combined 矩阵C = A∙B⁻¹

    B⁻¹ 从目标位置映射到基向量,而A 从那里映射到源位置。因此,该组合将目标位置映射到源位置。

    第 6 步:对于目标图像的每个像素 (x,y),计算乘积

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

    这些是变换点的齐次坐标。

    第 7 步: 计算源图像中的位置,如下所示:

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

    这称为坐标向量的去均匀化

    如果是support MathJax,所有这些数学将是so much easier to read and write...

    选择图片尺寸

    上述方法假设您知道目标图像中角的位置。对于这些,您必须知道该图像的宽度和高度,这在您的代码中也用问号标记。所以让我们假设输出图像的height1,而widthsourceaspect。在这种情况下,整个区域也是sourceaspect。您必须将该区域缩放pixelcount/sourceaspect 以达到pixelcount 的区域。这意味着您必须通过该因子的平方根来缩放每个边长。所以最后,你有

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

    【讨论】:

    • 这仅在照片以正常入射时有效。如果它是从一个角度拍摄的,您还需要角落的深度 - 但是,这些可以从角度推断出来。
    • @Thomas,我不关注你的问题。我知道我的方法不能处理任何现实生活中的像差,但只要适用理想的针孔相机模型,角度就无关紧要:无论角度如何,透视都是投影变换(除非您的光轴位于平面内纸)。上述计算适用于一般的预测变换。所以不需要深度。请注意,我是在真实的投影平面中进行计算,而不是在真实的仿射 3d 空间中进行计算。会不会是你误解了这方面?
    • 假设照片是在绘图的掠射角拍摄的 - 您可以像往常一样应用旋转以获得正确对齐的照片,但仍处于掠射角,这不是所需的结果(OP想要“在纸的正上方”)。 OP 没有提到输入照片是否总是从绘图的正上方拍摄。
    • @Thomas,我正在应用投影变换,而不仅仅是旋转。它将以掠射角拍摄的矩形图像映射到填充目标图像的矩形。不要求从图纸正上方拍摄照片。
    • 啊,我明白了。但是,您将没有足够的信息来准确重建最终图像,因为您会将“压扁”像素放大为具有所需纵横比的像素,但这是不可避免的。
    【解决方案2】:

    使用Graphics32,特别是TProjectiveTransformation(与Transform 方法一起使用)。不要忘记在源图像中留下一些透明边距,以免出现锯齿状边缘。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-03
      • 2020-09-17
      • 2015-10-18
      • 2020-03-04
      • 2013-07-19
      • 1970-01-01
      • 2018-03-24
      相关资源
      最近更新 更多