LockBits 图像旋转方法不起作用?

Posted

技术标签:

【中文标题】LockBits 图像旋转方法不起作用?【英文标题】:LockBits image rotation method not working? 【发布时间】:2010-10-05 00:00:38 【问题描述】:

大家好。在厌倦了 Get/Set Pixel 和 RotateTransfom 的缓慢性能和古怪行为之后,我求助于使用 LockBits 进行 2d 位图图像旋转。所以这是我想出的代码,据我估计,它应该可以完美运行。没有。

private static void InternalRotateImage(Bitmap originalBitmap, Bitmap rotatedBitmap, PointF centerPoint, float theta)
    
        BitmapData originalData = originalBitmap.LockBits(
            new Rectangle(0, 0, originalBitmap.Width, originalBitmap.Height),
            ImageLockMode.ReadOnly,
            originalBitmap.PixelFormat);

        BitmapData rotatedData = rotatedBitmap.LockBits(
            new Rectangle(0, 0, rotatedBitmap.Width, rotatedBitmap.Height),
            ImageLockMode.WriteOnly,
            rotatedBitmap.PixelFormat);

        unsafe
        
            byte[,] A = new byte[originalData.Height * 2, originalBitmap.Width * 2];
            byte[,] R = new byte[originalData.Height * 2, originalBitmap.Width * 2];
            byte[,] G = new byte[originalData.Height * 2, originalBitmap.Width * 2];
            byte[,] B = new byte[originalData.Height * 2, originalBitmap.Width * 2];

            for (int y = 0; y < originalData.Height; y++)
            
                byte* row = (byte*)originalData.Scan0 + (y * originalData.Stride);
                for (int x = 0; x < originalData.Width; x++)
                
                    B[y, x] = row[x * 4];
                    G[y, x] = row[x * 4 + 1];
                    R[y, x] = row[x * 4 + 2];
                    A[y, x] = row[x * 4 + 3];
                
            

            for (int y = 0; y < rotatedData.Height; y++)
            
                byte* row = (byte*)rotatedData.Scan0 + (y * rotatedData.Stride);
                for (int x = 0; x < rotatedData.Width; x++)
                
                    int newy = (int)Math.Abs((Math.Cos(theta) * (x - centerPoint.X) - Math.Sin(theta) * (y - centerPoint.Y) + centerPoint.X));
                    int newx = (int)Math.Abs((Math.Sin(theta) * (x - centerPoint.X) + Math.Cos(theta) * (y - centerPoint.Y) + centerPoint.Y));

                    row[x * 4] = B[newy, newx];
                    row[x * 4 + 1] = G[newy, newx];
                    row[x * 4 + 2] = R[newy, newx];
                    row[x * 4 + 3] = A[newy, newx];
                
            

        
            originalBitmap.UnlockBits(originalData);
            rotatedBitmap.UnlockBits(rotatedData);
        

有人有什么想法吗?我很新鲜。提前致谢!

编辑: 这就是我最终使用的(非常感谢 Hans Passant):

private Image RotateImage(Image img, float rotationAngle)
    
        Image image = new Bitmap(img.Width * 2, img.Height * 2);
        Graphics gfx = Graphics.FromImage(image);

        int center = (int)Math.Sqrt(img.Width * img.Width + img.Height * img.Height) / 2;
        gfx.TranslateTransform(center, center);
        gfx.RotateTransform(rotationAngle);
        gfx.DrawImage(img, -img.Width / 2, -img.Height / 2);

        return image;
    

这和他的一样,只是基于每张图片,而不是表格。

【问题讨论】:

你想用这四个byte[,] A = new byte[... 行来完成什么?我不明白你为什么要创建一个二维字节数组,其中一维是原始位图高度的两倍,第二维是高度乘以宽度。 对不起,高度宽度是旧的编辑。我对其进行了编辑以反映正确的内容...并且我将它们都乘以 2 以确保它们可以处理旋转范围。 【参考方案1】:

你正在给自己挖一个更深的洞。这很早就出错了,旋转位图的大小不是宽度 x 高度。这也是非常低效的。您需要启动 RotateTransform,同时使用 TranslateTransform 并选择正确的图像绘制位置也很重要。

这是一个示例 Windows 窗体应用程序,它围绕其中心点旋转位图,偏移量刚好足以在旋转时触及窗体的内边缘。在表单上放置一个计时器并使用 Project + Properties 的 Resource 选项卡添加一个图像资源。将其命名为 SampleImage,它不必是方形的。使代码如下所示:

public partial class Form1 : Form 
    private float mDegrees;
    private Image mBmp;
    public Form1() 
        InitializeComponent();
        mBmp = Properties.Resources.SampleImage;
        timer1.Enabled = true;
        timer1.Interval = 50;
        timer1.Tick += new System.EventHandler(this.timer1_Tick);
        this.DoubleBuffered = true;
    
    private void timer1_Tick(object sender, EventArgs e) 
        mDegrees += 3.0F;
        this.Invalidate();
    
    protected override void OnPaint(PaintEventArgs e) 
        int center = (int)Math.Sqrt(mBmp.Width * mBmp.Width + mBmp.Height * mBmp.Height) / 2;
        e.Graphics.TranslateTransform(center, center);
        e.Graphics.RotateTransform(mDegrees);
        e.Graphics.DrawImage(mBmp, -mBmp.Width/2, -mBmp.Height/2);
    

您可以通过创建 32bppPArgb 格式的位图来加快绘图速度,我跳过了这一步。

【讨论】:

只是我,还是第一行第一列在旋转过程中搞砸了?就我而言,我用空像素对其进行了修复,并将我的图像设置为比我需要的大一个像素。 不确定你的意思。如果图像大小可被 2 整除,则许多人需要将 center 舍入。只有奇数大小的位图具有完美的中心像素。或者使用这些采用浮点数的方法的重载。 @Hans:任何一张图片,第一行第一列都是扭曲的。自己看吧……

以上是关于LockBits 图像旋转方法不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

ImageMagick 和 PHP 正确旋转图像

拉动刷新时旋转图像

C/C++ 旋转 BMP 图像

如何在 jquery 中旋转图像?

在旋转滑块上方放置图像

如何使用 Pygame 围绕其中心旋转图像?