将图像从3d透视图重新绘制到2d [英] Redraw image from 3d perspective to 2d

查看:170
本文介绍了将图像从3d透视图重新绘制到2d的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要用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 * ???; //我不怎么计算这个
destinationbitmap.height = megapixelcount * sourceaspect * ???; //我不知道如何计算这个
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];
结束
result:= destinationbitmap;
结束

我需要真正的公式...所以一个OpenGL解决方案不会很理想...


解决方案

注意: 这个版本,并在Math SE上进行适当的数学排版。



计算投影变换



透视图是投影转换,而这又由四点定义。



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

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

列形成均匀坐标:一维度更多,通过添加 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 的第二个矩阵。



这是从基础向量到目标位置的地图。



步骤4: 反转 B 获得 B -1



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



步骤5:计算组合矩阵 C = A∙B -1



B -1 从目的地位置映射到基本向量, ile A 映射到源位置。所以组合将目标位置映射到源位置。



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

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

这些是您转换点的同质坐标。



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

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

这是坐标向量的 dehomogenization



所有这些数学都将更容易阅读和写入如果SO要支持MathJax ...



选择图像大小



上面的aproach假设你知道你的角落在目标图像中的位置。对于这些,您必须知道该图像的宽度和高度,它也由您的代码中的问号标记。所以让我们假设输出图像的高度 1 ,而 width sourceaspect 。在这种情况下,整体面积也是 sourceaspect 。您必须将该区域的比例调整为 pixelcount / sourceaspect ,以达到 pixelcount 的区域。这意味着您必须按照该因子的平方根来缩放每个边长。所以最后你有

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


I need an inverse perspective transform written in Pascal/Delphi/Lazarus. See the following image:

I think I need to walk through destination pixels and then calculate the corresponding position in the source image (To avoid problems with rounding errors etc.).

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;

I need the real formula... So an OpenGL solution would not be ideal...

解决方案

Note: There is a version of this with proper math typesetting on the Math SE.

Computing a projective transformation

A perspective is a special case of a projective transformation, which in turn is defined by four points.

Step 1: Starting with the 4 positions in the source image, named (x1,y1) through (x4,y4), you solve the following system of linear equations:

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

The colums form homogenous coordinates: one dimension more, created by adding a 1 as the last entry. In subsequent steps, multiples of these vectors will be used to denote the same points. See the last step for an example of how to turn these back into two-dimensional coordinates.

Step 2: Scale the columns by the coefficients you just computed:

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

This matrix will map (1,0,0) to a multiple of (x1,y1,1), (0,1,0) to a multiple of (x2,y2,1), (0,0,1) to a multiple of (x3,y3,1) and (1,1,1) to (x4,y4,1). So it will map these four special vectors (called basis vectors in subsequent explanations) to the specified positions in the image.

Step 3: Repeat steps 1 and 2 for the corresponding positions in the destination image, in order to obtain a second matrix called B.

This is a map from basis vectors to destination positions.

Step 4: Invert B to obtain B⁻¹.

B maps from basis vectors to the destination positions, so the inverse matrix maps in the reverse direction.

Step 5: Compute the combined Matrix C = A∙B⁻¹.

B⁻¹ maps from destination positions to basis vectors, while A maps from there to source positions. So the combination maps destination positions to source positions.

Step 6: For every pixel (x,y) of the destination image, compute the product

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

These are the homogenous coordinates of your transformed point.

Step 7: Compute the position in the source image like this:

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

This is called dehomogenization of the coordinate vector.

All this math would be so much easier to read and write if SO were to support MathJax

Choosing the image size

The above aproach assumes that you know the location of your corners in the destination image. For these you have to know the width and height of that image, which is marked by question marks in your code as well. So let's assume the height of your output image were 1, and the width were sourceaspect. In that case, the overall area would be sourceaspect as well. You have to scale that area by a factor of pixelcount/sourceaspect to achieve an area of pixelcount. Which means that you have to scale each edge length by the square root of that factor. So in the end, you have

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

这篇关于将图像从3d透视图重新绘制到2d的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆