旋转图像的数学方法 (C#)

11

我有一张图片,上面有两个点,排列大概是这样的:

|----------------|
|                |
|    .           |
|                |
|          .     |
|                |
|----------------|

我有两个点的X和Y坐标,并且我需要将图像旋转X度,使其看起来像这样:

|----------------|
|                |
|                |
|    .     .     |
|                |
|                |
|----------------|

基本上,让它们彼此对齐,这个涉及到哪些数学知识?(提供 C# 的代码示例会更好,但不是必需的)


1
这是一个旧的帖子,但我想提一下,我在这里发布了一个C# WinForms图像旋转方法:https://dev59.com/VHI95IYBdhLWcg3wxA4- - RenniePet
6个回答

15

这取决于您想要将哪个点用作旋转的“中心”。 我们将上方和左侧的点称为点A,右下方的点称为点B。 如果您想围绕点A旋转,使点B与其对齐,则计算弧度旋转角度应如下所示:

double angle = Math.Atan2(pointB.Y - pointA.Y, pointB.X - pointA.X);

我不知道你是如何处理图片的,因此以下内容仅适用于使用System.Drawing.Graphics的情况:

myImage.TranslateTransform(-pointA.X, -pointA.Y);
myImage.RotateTransform((float) angle, MatrixOrder.Append);
myImage.TranslateTransform(pointA.X, pointA.Y, MatrixOrder.Append);

希望能有所帮助。


5
抱歉,没有代码,只有策略。
您需要能够通过对原始图像进行采样来创建结果图像。您已经知道了旋转角度,现在需要创建一个映射函数,将结果映射回原始图像。
该代码将简单地扫描结果图像的每一行,并将像素映射回原始图像。您可以使用简单的方法;
for (int plotY = 0; plotY < resultHeight; plotY++)
{
   for (int plotX = 0; plotX < resultWidth; plotX++)
   {
         resultImage.PlotPixel(getOriginalPixel(plotX, plotY, angleOfRotation));
   } 
}

现在我们只需要神奇的“getOriginalPixel”方法,这就涉及到数学问题了。

如果我们将图像旋转0度,那么plotX,plotY就是原始图像的X / Y坐标。但这并不有趣。

pickX = x * cos(angle) - y * sin(angle)
pickY = y * cos(angle) + x * sin(angle)

我认为这个操作将映射到源像素。你需要检查它是否超出了范围,如果是,就返回黑色或其他内容 :)


如果超出边界,您应该返回Color.Transparent。 - MrFox

4
以下代码有效。
  Matrix mRotate = new Matrix();
    mRotate.Translate(Convert.ToInt32(Width.Value) / -2, Convert.ToInt32(Height.Value) / -2, MatrixOrder.Append);
    mRotate.RotateAt(theta, new Point(0, 0), MatrixOrder.Append);

    using (GraphicsPath gp = new GraphicsPath())
    {  // transform image points by rotation matrix
        gp.AddPolygon(new Point[] { new Point(0, 0), new Point(Convert.ToInt32(Width.Value), 0), new Point(0, Convert.ToInt32(Height.Value)) });
        gp.Transform(mRotate);
        PointF[] pts = gp.PathPoints;

        // create destination bitmap sized to contain rotated source image
        Rectangle bbox = boundingBox(bmpSrc, mRotate);
        Bitmap bmpDest = new Bitmap(bbox.Width, bbox.Height);


        using (Graphics gDest = Graphics.FromImage(bmpDest))
        {  // draw source into dest


            Matrix mDest = new Matrix();
            mDest.Translate(bmpDest.Width / 2, bmpDest.Height / 2, MatrixOrder.Append);
            gDest.Transform = mDest;
            gDest.DrawImage(bmpSrc, pts);
            gDest.DrawRectangle(Pens.Transparent, bbox);
            //drawAxes(gDest, Color.Red, 0, 0, 1, 100, "");
            return bmpDest;
        }
    }

2

首先找到中心点:

Point p = new Point((x1-x2)/2, (y1-y2)/2)

然后使用三角函数来解决角度问题。我假设我们已经将原点重新定位到中心点,所以现在我有了一个新的$x3$和$y3$对于其中一个点。

hypotenuse = SqrRt(x3^2 + y3^2)

我们需要求解未知角度TH
Sin(TH) = opposite / hypotenuse

所以为了解决TH,我们需要:
TH = Asin(y3 / hypotenuse)

TH 旋转。

参见维基百科三角函数参考


2
你需要查找几何旋转矩阵:请参考此网站获取详细的解释。 然而,为了达到最佳效果,你需要从目标转换到源,然后对每个目标像素使用该转换:
m = rotation matrix

for each point p in destination
  p' = p.m
  get pixel p' from source
  set pixle p in destination

在 .NET Framework 中,有一些方法可以实现这一功能:System.Drawing.Graphics.RotateTransformSystem.Drawing.Graphics.TranslateTransform。你需要设置一个翻译来将图像的旋转点移动到原点,然后应用旋转,最后再进行另一个平移操作将其返回到原始位置。你需要尝试使用这些函数来了解它们的行为——我现在正在工作中,没有时间编写出可用的代码。 :-(

2

进行一般的二维变换需要解决一组具有6个未知数的方程。

'x = xA + yB + C

'y = xD + yE + D

给定3个对应点,您将有6个已知数,可以解决该系统。在这种情况下,您只有4个点,因为您不关心剪切,但是您可以想象在由其他两个点形成的线的90度处引入第三个点。然后创建旋转图像就像(伪代码):

for ( y = 0; y < height; y++ )
 for ( x = 0; x < width; x++ )
  {
    newx = x*A + y*B + C;
    newy = x*D + y*D + E;
    newimage(x,y ) = oldimage( newx, newy );
  }
}

如果性能很重要,可以通过注意到 y*B 只在外层循环中发生变化,以及 newx、newy 在内层循环中只会因为常量 A 和 D 而改变,从而优化掉内层循环中的乘法操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接