【问题标题】:How do I prevent clipping when rotating an image in C#?在 C# 中旋转图像时如何防止剪切?
【发布时间】:2024-01-10 13:18:01
【问题描述】:

我刚刚经历了一堆东西,试图弄清楚如何让图像旋转。这行得通,但现在它正在剪辑,我不确定如何让它停止......我正在使用这个 rotateImage 方法:

public static Image RotateImage(Image img, float rotationAngle)
    {
        //create an empty Bitmap image
        Bitmap bmp = new Bitmap(img.Width, img.Height);

        //turn the Bitmap into a Graphics object
        Graphics gfx = Graphics.FromImage(bmp);

        //now we set the rotation point to the center of our image
        gfx.TranslateTransform((float)bmp.Width / 2, (float)bmp.Height / 2);

        //now rotate the image
        gfx.RotateTransform(rotationAngle);

        gfx.TranslateTransform(-(float)bmp.Width / 2, -(float)bmp.Height / 2);

        //set the InterpolationMode to HighQualityBicubic so to ensure a high
        //quality image once it is transformed to the specified size
        gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;

        //now draw our new image onto the graphics object
        gfx.DrawImage(img, new System.Drawing.Point(0, 0));

        //dispose of our Graphics object
        gfx.Dispose();

        //return the image
        return bmp;
    }

我尝试将空位图放大,但这只适用于一侧,因为图像固定在位图的左上角。任何想法将不胜感激!

【问题讨论】:

标签: c# image rotation clipping


【解决方案1】:

按照以下步骤操作:

  1. 创建空的目标图像,其宽度和高度 = 旋转图像的边界框
  2. 将源图像绘制到目标图像上。源的 (0,0) 将映射到目标的 (ox,oy)
  3. 现在执行旋转目标图像的步骤。

完成上述步骤的详细信息:

W,H = 目的地的宽度和高度

w,h = 源的宽度和高度

c=|cos(θ)|

s=|sin(θ)|

w * c + h * s = W

w * s + h * c = H

ox = ( W - w ) / 2

oy = ( H - h ) / 2

【讨论】:

    【解决方案2】:

    旋转的图像可能需要包含更大的位图而不进行剪切。计算边界的一种相当简单的方法是将变换矩阵应用于原始边界矩形的角。由于新的位图更大,因此必须将原始图像绘制成居中,而不是在 (0,0) 处。

    这在您的代码的以下修订中得到了证明:

    public static Image RotateImage(Image img, float rotationAngle)
    {
        int minx = int.MaxValue, maxx = int.MinValue, miny = int.MaxValue, maxy = int.MinValue;
    
        using (Bitmap bmp = new Bitmap(1, 1)) // Dummy bitmap, so we can use TransformPoints to figure out the correct size.
        {
            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.TranslateTransform((float)img.Width / 2, (float)img.Height / 2);
                g.RotateTransform(rotationAngle);
                g.TranslateTransform(-(float)img.Width / 2, -(float)img.Height / 2);
    
                Point[] pts = new Point[4];
                pts[0] = new Point(0, 0);
                pts[1] = new Point(img.Width, 0);
                pts[2] = new Point(img.Width, img.Height);
                pts[3] = new Point(0, img.Height);
                g.TransformPoints(CoordinateSpace.Device, CoordinateSpace.World, pts);
    
                foreach (Point pt in pts)
                {
                    minx = Math.Min(minx, pt.X);
                    maxx = Math.Max(maxx, pt.X);
                    miny = Math.Min(miny, pt.Y);
                    maxy = Math.Max(maxy, pt.Y);
                }
            }
        }
    
        Bitmap bmp2 = new Bitmap(maxx - minx, maxy - miny);
        using (Graphics g = Graphics.FromImage(bmp2))
        {
            g.TranslateTransform((float)bmp2.Width / 2, (float)bmp2.Height / 2);
            g.RotateTransform(rotationAngle);
            g.TranslateTransform(-(float)bmp2.Width / 2, -(float)bmp2.Height / 2);
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.DrawImage(img, bmp2.Width / 2 - img.Width / 2, bmp2.Height / 2 - img.Height / 2);
        }
    
        return bmp2;
    }
    

    【讨论】:

    • 我不太明白你在那个方法中所做的一切,但我做了更改,它仍然剪辑,但现在它尴尬地旋转......
    • 我没有看到剪裁问题。如果我在 (0,0) 处绘制生成的位图,旋转将出现错误,但输出图像确实包含以适当角度旋转的原始图像,并且它具有最小界限。如果这会导致显示问题,则需要计算正确的显示原点或始终使用足够大的输出位图来处理任何旋转角度。
    • 嗯,也许这与我在图片框内执行此操作有关......是的,它必须是因为它根据我的图片框的大小进行剪辑。有没有办法可以将图像放在图片框的不同部分?
    【解决方案3】:

    我从另一个网站找到了一些帮助。以下是我最终为那些想知道的人所做的:

    // Rotates the input image by theta degrees around center.
        public static Bitmap RotateImage(Bitmap bmpSrc, float theta)
        {
            Matrix mRotate = new Matrix();
            mRotate.Translate(bmpSrc.Width / -2, bmpSrc.Height / -2, MatrixOrder.Append);
            mRotate.RotateAt(theta, new System.Drawing.Point(0, 0), MatrixOrder.Append);
            using (GraphicsPath gp = new GraphicsPath())
            {  // transform image points by rotation matrix
                gp.AddPolygon(new System.Drawing.Point[] { new System.Drawing.Point(0, 0), new System.Drawing.Point(bmpSrc.Width, 0), new System.Drawing.Point(0, bmpSrc.Height) });
                gp.Transform(mRotate);
                System.Drawing.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);
                    return bmpDest;
                }
            }
        }
    
        private static Rectangle boundingBox(Image img, Matrix matrix)
        {
            GraphicsUnit gu = new GraphicsUnit();
            Rectangle rImg = Rectangle.Round(img.GetBounds(ref gu));
    
            // Transform the four points of the image, to get the resized bounding box.
            System.Drawing.Point topLeft = new System.Drawing.Point(rImg.Left, rImg.Top);
            System.Drawing.Point topRight = new System.Drawing.Point(rImg.Right, rImg.Top);
            System.Drawing.Point bottomRight = new System.Drawing.Point(rImg.Right, rImg.Bottom);
            System.Drawing.Point bottomLeft = new System.Drawing.Point(rImg.Left, rImg.Bottom);
            System.Drawing.Point[] points = new System.Drawing.Point[] { topLeft, topRight, bottomRight, bottomLeft };
            GraphicsPath gp = new GraphicsPath(points,
                                                                new byte[] { (byte)PathPointType.Start, (byte)PathPointType.Line, (byte)PathPointType.Line, (byte)PathPointType.Line });
            gp.Transform(matrix);
            return Rectangle.Round(gp.GetBounds());
        }
    

    【讨论】:

    • 重要的是在这里指定 Matrix 是 System.Drawing.Drawing2D.Matrix,而不是 System.Windows.Media.Matrix。
    • 至今仍是(2 年后)这个问题的最佳答案。
    • 3 年后是唯一无需进一步修改即可立即工作的答案。
    • 我也在寻找一个好的 C# 图像旋转解决方案并尝试了这个:虽然它可以正确旋转图像,但它会返回一个周围有很大空白空间的图像。我确实知道旋转后的图像不一定具有相同的尺寸,但为什么它周围有一个空边框?
    • 6 年后,这个解决方案也适用于我,thnx
    【解决方案4】:
    public Bitmap rotateImage(Bitmap b, float angle)
        {
            if (angle > 0)
            {
                int l = b.Width;
                int h = b.Height;
                double an = angle * Math.PI / 180;
                double cos = Math.Abs(Math.Cos(an));
                double sin = Math.Abs(Math.Sin(an));
                int nl = (int)(l * cos + h * sin);
                int nh = (int)(l * sin + h * cos);
                Bitmap returnBitmap = new Bitmap(nl, nh);
                Graphics g = Graphics.FromImage(returnBitmap);
                g.TranslateTransform((float)(nl-l) / 2, (float)(nh-h) / 2);
                g.TranslateTransform((float)b.Width / 2, (float)b.Height / 2);
                g.RotateTransform(angle);
                g.TranslateTransform(-(float)b.Width / 2, -(float)b.Height / 2);
                g.DrawImage(b, new Point(0, 0));
                return returnBitmap;
            }
            else return b;
        }
    

    【讨论】:

    • 这篇文章完全有必要在不裁剪的情况下旋转。干得好@Naveen!
    • 感谢,它构成了我最终在这里提出的版本的核心:*.com/questions/2163829/…