C#图像旋转

如果平面上的点绕原点逆时针旋转θº,则其坐标变换公式为:

                                                                                       x'=xcosθ+ysinθ   y=-xsinθ+ycosθ

 

其中,(x, y)为原图坐标,(x’, y’)为旋转后的坐标。它的逆变换公式为:

                                                                                       x=x'cosθ-y'sinθ   y=x'sinθ+y'cosθ

 

矩阵形式为:

                                                                                     

和缩放类似,旋转后的图像的像素点也需要经过坐标转换为原始图像上的坐标来确定像素值,同样也可能找不到对应点,因此旋转也用到插值法。在此选用性能较好的双线性插值法。为提高速度,在处理旋转90º、-90º、±180º时使用了镜像来处理。

 

  1         /// <summary>
  2         /// 图像旋转
  3         /// </summary>
  4         /// <param name="srcBmp">原始图像</param>
  5         /// <param name="angle">旋转角度</param>
  6         /// <param name="dstBmp">目标图像</param>
  7         /// <returns>处理成功 true 失败 false</returns>
  8         public static bool Rotation(Bitmap srcBmp, double angle, out Bitmap dstBmp)
  9         {
 10             if (srcBmp == null)
 11             {
 12                 dstBmp = null;
 13                 return false;
 14             }
 15             dstBmp = null;
 16             BitmapData srcBmpData = null;
 17             BitmapData dstBmpData = null;
 18             switch ((int)angle)
 19             {
 20                 case 0:
 21                     dstBmp = new Bitmap(srcBmp);
 22                     break;
 23                 case -90:
 24                     dstBmp = new Bitmap(srcBmp.Height, srcBmp.Width);
 25                     srcBmpData = srcBmp.LockBits(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
 26                     dstBmpData = dstBmp.LockBits(new Rectangle(0, 0, dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
 27                     unsafe
 28                     {
 29                         byte* ptrSrc = (byte*)srcBmpData.Scan0;
 30                         byte* ptrDst = (byte*)dstBmpData.Scan0;
 31                         for (int i = 0; i < srcBmp.Height; i++)
 32                         {
 33                             for (int j = 0; j < srcBmp.Width; j++)
 34                             {
 35                                 ptrDst[j * dstBmpData.Stride + (dstBmp.Height - i - 1) * 3] = ptrSrc[i * srcBmpData.Stride + j * 3];
 36                                 ptrDst[j * dstBmpData.Stride + (dstBmp.Height - i - 1) * 3 + 1] = ptrSrc[i * srcBmpData.Stride + j * 3 + 1];
 37                                 ptrDst[j * dstBmpData.Stride + (dstBmp.Height - i - 1) * 3 + 2] = ptrSrc[i * srcBmpData.Stride + j * 3 + 2];
 38                             }
 39                         }
 40                     }
 41                     srcBmp.UnlockBits(srcBmpData);
 42                     dstBmp.UnlockBits(dstBmpData);
 43                     break;
 44                 case 90:
 45                     dstBmp = new Bitmap(srcBmp.Height, srcBmp.Width);
 46                     srcBmpData = srcBmp.LockBits(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
 47                     dstBmpData = dstBmp.LockBits(new Rectangle(0, 0, dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
 48                     unsafe
 49                     {
 50                         byte* ptrSrc = (byte*)srcBmpData.Scan0;
 51                         byte* ptrDst = (byte*)dstBmpData.Scan0;
 52                         for (int i = 0; i < srcBmp.Height; i++)
 53                         {
 54                             for (int j = 0; j < srcBmp.Width; j++)
 55                             {
 56                                 ptrDst[(srcBmp.Width - j - 1) * dstBmpData.Stride + i * 3] = ptrSrc[i * srcBmpData.Stride + j * 3];
 57                                 ptrDst[(srcBmp.Width - j - 1) * dstBmpData.Stride + i * 3 + 1] = ptrSrc[i * srcBmpData.Stride + j * 3 + 1];
 58                                 ptrDst[(srcBmp.Width - j - 1) * dstBmpData.Stride + i * 3 + 2] = ptrSrc[i * srcBmpData.Stride + j * 3 + 2];
 59                             }
 60                         }
 61                     }
 62                     srcBmp.UnlockBits(srcBmpData);
 63                     dstBmp.UnlockBits(dstBmpData);
 64                     break;
 65                 case 180:
 66                 case -180:
 67                     dstBmp = new Bitmap(srcBmp.Width, srcBmp.Height);
 68                     srcBmpData = srcBmp.LockBits(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
 69                     dstBmpData = dstBmp.LockBits(new Rectangle(0, 0, dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
 70                     unsafe
 71                     {
 72                         byte* ptrSrc = (byte*)srcBmpData.Scan0;
 73                         byte* ptrDst = (byte*)dstBmpData.Scan0;
 74                         for (int i = 0; i < srcBmp.Height; i++)
 75                         {
 76                             for (int j = 0; j < srcBmp.Width; j++)
 77                             {
 78                                 ptrDst[(srcBmp.Width - i - 1) * dstBmpData.Stride + (dstBmp.Height - j - 1) * 3] = ptrSrc[i * srcBmpData.Stride + j * 3];
 79                                 ptrDst[(srcBmp.Width - i - 1) * dstBmpData.Stride + (dstBmp.Height - j - 1) * 3 + 1] = ptrSrc[i * srcBmpData.Stride + j * 3 + 1];
 80                                 ptrDst[(srcBmp.Width - i - 1) * dstBmpData.Stride + (dstBmp.Height - j - 1) * 3 + 2] = ptrSrc[i * srcBmpData.Stride + j * 3 + 2];
 81                             }
 82                         }
 83                     }
 84                     srcBmp.UnlockBits(srcBmpData);
 85                     dstBmp.UnlockBits(dstBmpData);
 86                     break;
 87                 default://任意角度
 88                     double radian = angle * Math.PI / 180.0;//将角度转换为弧度
 89                                                              //计算正弦和余弦
 90                     double sin = Math.Sin(radian);
 91                     double cos = Math.Cos(radian);
 92                     //计算旋转后的图像大小
 93                     int widthDst = (int)(srcBmp.Height * Math.Abs(sin) + srcBmp.Width * Math.Abs(cos));
 94                     int heightDst = (int)(srcBmp.Width * Math.Abs(sin) + srcBmp.Height * Math.Abs(cos));
 95 
 96                     dstBmp = new Bitmap(widthDst, heightDst);
 97                     //确定旋转点
 98                     int dx = (int)(srcBmp.Width / 2 * (1 - cos) + srcBmp.Height / 2 * sin);
 99                     int dy = (int)(srcBmp.Width / 2 * (0 - sin) + srcBmp.Height / 2 * (1 - cos));
100 
101                     int insertBeginX = srcBmp.Width / 2 - widthDst / 2;
102                     int insertBeginY = srcBmp.Height / 2 - heightDst / 2;
103 
104                     //插值公式所需参数
105                     double ku = insertBeginX * cos - insertBeginY * sin + dx;
106                     double kv = insertBeginX * sin + insertBeginY * cos + dy;
107                     double cu1 = cos, cu2 = sin;
108                     double cv1 = sin, cv2 = cos;
109 
110                     double fu, fv, a, b, F1, F2;
111                     int Iu, Iv;
112                     srcBmpData = srcBmp.LockBits(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
113                     dstBmpData = dstBmp.LockBits(new Rectangle(0, 0, dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
114 
115                     unsafe
116                     {
117                         byte* ptrSrc = (byte*)srcBmpData.Scan0;
118                         byte* ptrDst = (byte*)dstBmpData.Scan0;
119                         for (int i = 0; i < heightDst; i++)
120                         {
121                             for (int j = 0; j < widthDst; j++)
122                             {
123                                 fu = j * cu1 - i * cu2 + ku;
124                                 fv = j * cv1 + i * cv2 + kv;
125                                 if ((fv < 1) || (fv > srcBmp.Height - 1) || (fu < 1) || (fu > srcBmp.Width - 1))
126                                 {
127 
128                                     ptrDst[i * dstBmpData.Stride + j * 3] = 150;
129                                     ptrDst[i * dstBmpData.Stride + j * 3 + 1] = 150;
130                                     ptrDst[i * dstBmpData.Stride + j * 3 + 2] = 150;
131                                 }
132                                 else
133                                 {//双线性插值
134                                     Iu = (int)fu;
135                                     Iv = (int)fv;
136                                     a = fu - Iu;
137                                     b = fv - Iv;
138                                     for (int k = 0; k < 3; k++)
139                                     {
140                                         F1 = (1 - b) * *(ptrSrc + Iv * srcBmpData.Stride + Iu * 3 + k) + b * *(ptrSrc + (Iv + 1) * srcBmpData.Stride + Iu * 3 + k);
141                                         F2 = (1 - b) * *(ptrSrc + Iv * srcBmpData.Stride + (Iu + 1) * 3 + k) + b * *(ptrSrc + (Iv + 1) * srcBmpData.Stride + (Iu + 1) * 3 + k);
142                                         *(ptrDst + i * dstBmpData.Stride + j * 3 + k) = (byte)((1 - a) * F1 + a * F2);
143                                     }
144                                 }
145                             }
146                         }
147                     }
148                     srcBmp.UnlockBits(srcBmpData);
149                     dstBmp.UnlockBits(dstBmpData);
150                     break;
151             }
152             return true;
153         }

 

posted @ 2025-04-11 16:09  funiyi816  阅读(60)  评论(0)    收藏  举报