2011-04-20 53 views
11

我想将一个大致矩形的区域复制到一个矩形区域。例如:如何根据简单的多边形绘制图像?

两个领域是由他们的角点定义。大方向保持(不翻转等)。

仅仅旋转源图像不起作用,因为相对的边可能具有不同的长度。

到目前为止,我发现没有办法在纯C#中执行此操作(手动像素复制除外),所以我想我不得不求助于Windows API或某个第三方库?

回答

3

一般来说,你想要做什么是地图目的地坐标到源坐标通过变换函数:

for (int y = 0; y < destHeight; y++) { 
    for (x=0; x < destWidth; x++) { 
     Color c = Transform(x, y, sourceImage, sourceTransform); 
     SetPixel(destImage, x, y, c); 
    } 
} 

让我们假设sourceTransform是一个对象,它封装了一个从urce到dest坐标(反之亦然)。

在dest坐标中工作将使您更容易在重新形成的源图像中避免该曲线,并允许您获得更好的抗锯齿效果,因为您可以将dest像素的角映射到源图像和其中的样本并插入/推断。

在你的情况下,你将有一组线性方程来做映射 - 在这种情况下,这被称为四边形翘曲 - see this previous question

8

因为我找不到答案,我自己写了一个天真的实现。它工作得很好。

例子

我画的所有的例子在油漆手工,所​​以他们不是很准确 - 它只是考不上一些基础知识。

a)轻微旋转。

来源:

source image

结果:

resulting image

b)中的各种侧

来源:

source 2

结果:

result 2

c)中视角

来源:

source 3

结果:

result 3

代码

(它的专业我的使用情况,但应该很容易适应):

// _Corners are, well, the 4 corners in the source image 
// _Px is an array of pixels extracted from the source image 

public void Rescale() 
{ 
    RescaleImage (
     _Corners[0], 
     _Corners[1], 
     _Corners[3], 
     _Corners[2], 
     100, 
     100); 
} 

private void RescaleImage (PointF TL, PointF TR, PointF LL, PointF LR, int sx, int sy) 
{ 
    var bmpOut = new Bitmap (sx, sy); 

    for (int x = 0; x < sx; x++) { 
     for (int y = 0; y < sy; y++) { 
      /* 
      * relative position 
      */ 
      double rx = (double) x/sx; 
      double ry = (double) y/sy; 

      /* 
      * get top and bottom position 
      */ 
      double topX = TL.X + rx * (TR.X - TL.X); 
      double topY = TL.Y + rx * (TR.Y - TL.Y); 
      double bottomX = LL.X + rx * (LR.X - LL.X); 
      double bottomY = LL.Y + rx * (LR.Y - LL.Y); 

      /* 
      * select center between top and bottom point 
      */ 
      double centerX = topX + ry * (bottomX - topX); 
      double centerY = topY + ry * (bottomY - topY); 

      /* 
      * store result 
      */ 
      var c = PolyColor (centerX, centerY); 
      bmpOut.SetPixel (x, y, c); 
     } 
    } 

    bmpOut.Save (_Path + "out5 rescale out.bmp"); 
} 

private Color PolyColor (double x, double y) 
{ 
    // get fractions 
    double xf = x - (int) x; 
    double yf = y - (int) y; 

    // 4 colors - we're flipping sides so we can use the distance instead of inverting it later 
    Color cTL = _Px[(int) y + 1, (int) x + 1]; 
    Color cTR = _Px[(int) y + 1, (int) x + 0]; 
    Color cLL = _Px[(int) y + 0, (int) x + 1]; 
    Color cLR = _Px[(int) y + 0, (int) x + 0]; 

    // 4 distances 
    double dTL = Math.Sqrt (xf * xf + yf * yf); 
    double dTR = Math.Sqrt ((1 - xf) * (1 - xf) + yf * yf); 
    double dLL = Math.Sqrt (xf * xf + (1 - yf) * (1 - yf)); 
    double dLR = Math.Sqrt ((1 - xf) * (1 - xf) + (1 - yf) * (1 - yf)); 

    // 4 parts 
    double factor = 1.0/(dTL + dTR + dLL + dLR); 
    dTL *= factor; 
    dTR *= factor; 
    dLL *= factor; 
    dLR *= factor; 

    // accumulate parts 
    double r = dTL * cTL.R + dTR * cTR.R + dLL * cLL.R + dLR * cLR.R; 
    double g = dTL * cTL.G + dTR * cTR.G + dLL * cLL.G + dLR * cLR.G; 
    double b = dTL * cTL.B + dTR * cTR.B + dLL * cLL.B + dLR * cLR.B; 

    Color c = Color.FromArgb ((int) (r + 0.5), (int) (g + 0.5), (int) (b + 0.5)); 

    return c; 
} 
+0

这真的很酷!阅读完这篇文章后,我能想到的唯一一句话就是“反倾斜”。良好的工作:) – Tom 2011-04-20 13:09:52

+1

我知道这是一个旧的帖子,但你是什么意思'*我们翻转两边,所以我们可以使用距离而不是稍后反转*'在你的'PolyColor'因为如果你的图像是800x600你会得到一个IndexOutOfRangeExeption – WiiMaxx 2013-07-10 09:22:29