从3d透视重绘图像到2d

Kas*_* DK 17 delphi math geometry pascal lazarus

我需要用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;
Run Code Online (Sandbox Code Playgroud)

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

MvG*_*MvG 20

注意:一个版本的这对数学SE适当的数学排版.

计算投射变换

透视是投影变换的一种特殊情况,而投射变换又由四点定义.

步骤1:从源图像中的4个位置开始,(x1,y1)通过命名(x4,y4),您可以求解以下线性方程组:

[x1 x2 x3] [?]   [x4]
[y1 y2 y3]?[?] = [y4]
[ 1  1  1] [?]   [ 1]
Run Code Online (Sandbox Code Playgroud)

colums形成同质坐标:一个维度更多,通过添加a 1作为最后一个条目创建.在随后的步骤中,这些矢量的倍数将用于表示相同的点.有关如何将这些转换为二维坐标的示例,请参见最后一步.

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

    [??x1 ??x2 ??x3]
A = [??y1 ??y2 ??y3]
    [?    ?    ?   ]
Run Code Online (Sandbox Code Playgroud)

该矩阵将映射(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.

这是从基础向量到目标位置的映射.

第4步: 反转 B获取B?¹.

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

第5步:计算组合矩阵C = A?B?¹.

B?¹从目的地位置到基础向量的A映射,同时从那里到源位置的映射.因此,组合将目的地位置映射到源位置.

步骤6:对于(x,y)目标图像的每个像素,计算产品

[x']     [x]
[y'] = C?[y]
[z']     [1]
Run Code Online (Sandbox Code Playgroud)

这些是变换点的同质坐标.

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

sx = x'/z'
sy = y'/z'
Run Code Online (Sandbox Code Playgroud)

这被称为坐标向量的非均匀化.

如果SO 支持MathJax,所有这些数学将更容易读写 ...

选择图像大小

上述方法假定您知道目标图像中角落的位置.对于这些,您必须知道该图像的宽度和高度,在您的代码中也会用问号标记.因此,让我们假设height你的输出图像的人1,和widthsourceaspect.在这种情况下,整个区域sourceaspect也是如此.你必须按比例缩放该区域,pixelcount/sourceaspect以达到一个区域pixelcount.这意味着您必须按照该因子的平方根缩放每个边长.所以最后,你有

pixelcount = 1000000.*megapixelcount;
width  = round(sqrt(pixelcount*sourceaspect));
height = round(sqrt(pixelcount/sourceaspect));
Run Code Online (Sandbox Code Playgroud)


Leo*_*era 5

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