如果平面上的点绕原点逆时针旋转θo,则其坐标调动公式为:
x‘=xcosθ+ysinθ y=-xsinθ+ycosθ
此中,(x, y)为原图坐标,,(x’, y’)为旋转后的坐标。它的逆调动公式为:
x=x‘cosθ-y‘sinθ y=x‘sinθ+y‘cosθ
矩阵形式为:
和缩放类似,旋转后的图像的像素点也需要颠末坐标转换为原始图像上的坐标来确定像素值,同样也可能找不到对应点,因此旋转也用到插值法。在此选用性能较好的双线性插值法。为提高速度,在措置惩罚惩罚旋转90o、-90o、±180o时使用了镜像来措置惩罚惩罚。
/// <summary> /// 图像旋转 /// </summary> /// <param>原始图像</param> /// <param>旋转角度</param> /// <param>方针图像</param> /// <returns>措置惩罚惩罚告成 true 掉败 false</returns> public static bool Rotation(Bitmap srcBmp, double degree, out Bitmap dstBmp) { if (srcBmp == null) { dstBmp = null; return false; } dstBmp = null; BitmapData srcBmpData = null; BitmapData dstBmpData = null; switch ((int)degree) { case 0: dstBmp = new Bitmap(srcBmp); break; case -90: dstBmp = new Bitmap(srcBmp.Height, srcBmp.Width); srcBmpData = srcBmp.LockBits(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); dstBmpData = dstBmp.LockBits(new Rectangle(0, 0, dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); unsafe { byte* ptrSrc = (byte*)srcBmpData.Scan0; byte* ptrDst = (byte*)dstBmpData.Scan0; for (int i = 0; i < srcBmp.Height; i++) { for (int j = 0; j < srcBmp.Width; j++) { ptrDst[j * dstBmpData.Stride + (dstBmp.Height - i - 1) * 3] = ptrSrc[i * srcBmpData.Stride + j * 3]; ptrDst[j * dstBmpData.Stride + (dstBmp.Height - i - 1) * 3 + 1] = ptrSrc[i * srcBmpData.Stride + j * 3 + 1]; ptrDst[j * dstBmpData.Stride + (dstBmp.Height - i - 1) * 3 + 2] = ptrSrc[i * srcBmpData.Stride + j * 3 + 2]; } } } srcBmp.UnlockBits(srcBmpData); dstBmp.UnlockBits(dstBmpData); break; case 90: dstBmp = new Bitmap(srcBmp.Height, srcBmp.Width); srcBmpData = srcBmp.LockBits(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); dstBmpData = dstBmp.LockBits(new Rectangle(0, 0, dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); unsafe { byte* ptrSrc = (byte*)srcBmpData.Scan0; byte* ptrDst = (byte*)dstBmpData.Scan0; for (int i = 0; i < srcBmp.Height; i++) { for (int j = 0; j < srcBmp.Width; j++) { ptrDst[(srcBmp.Width - j - 1) * dstBmpData.Stride + i * 3] = ptrSrc[i * srcBmpData.Stride + j * 3]; ptrDst[(srcBmp.Width - j - 1) * dstBmpData.Stride + i * 3 + 1] = ptrSrc[i * srcBmpData.Stride + j * 3 + 1]; ptrDst[(srcBmp.Width - j - 1) * dstBmpData.Stride + i * 3 + 2] = ptrSrc[i * srcBmpData.Stride + j * 3 + 2]; } } } srcBmp.UnlockBits(srcBmpData); dstBmp.UnlockBits(dstBmpData); break; case 180: case -180: dstBmp = new Bitmap(srcBmp.Width, srcBmp.Height); srcBmpData = srcBmp.LockBits(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); dstBmpData = dstBmp.LockBits(new Rectangle(0, 0, dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); unsafe { byte* ptrSrc = (byte*)srcBmpData.Scan0; byte* ptrDst = (byte*)dstBmpData.Scan0; for (int i = 0; i < srcBmp.Height; i++) { for (int j = 0; j < srcBmp.Width; j++) { ptrDst[(srcBmp.Width - i - 1) * dstBmpData.Stride + (dstBmp.Height - j - 1) * 3] = ptrSrc[i * srcBmpData.Stride + j * 3]; ptrDst[(srcBmp.Width - i - 1) * dstBmpData.Stride + (dstBmp.Height - j - 1) * 3 + 1] = ptrSrc[i * srcBmpData.Stride + j * 3 + 1]; ptrDst[(srcBmp.Width - i - 1) * dstBmpData.Stride + (dstBmp.Height - j - 1) * 3 + 2] = ptrSrc[i * srcBmpData.Stride + j * 3 + 2]; } } } srcBmp.UnlockBits(srcBmpData); dstBmp.UnlockBits(dstBmpData); break; default://任意角度 double radian = degree * Math.PI / 180.0;//将角度转换为弧度 //计算正弦和余弦 double sin = Math.Sin(radian); double cos = Math.Cos(radian); //计算旋转后的图像巨细 int widthDst = (int)(srcBmp.Height * Math.Abs(sin) + srcBmp.Width * Math.Abs(cos)); int heightDst = (int)(srcBmp.Width * Math.Abs(sin) + srcBmp.Height * Math.Abs(cos)); dstBmp = new Bitmap(widthDst, heightDst); //确定旋转点 int dx = (int)(srcBmp.Width / 2 * (1 - cos) + srcBmp.Height / 2 * sin); int dy = (int)(srcBmp.Width / 2 * (0 - sin) + srcBmp.Height / 2 * (1 - cos)); int insertBeginX = srcBmp.Width / 2 - widthDst / 2; int insertBeginY = srcBmp.Height / 2 - heightDst / 2; //插值公式所需参数 double ku = insertBeginX * cos - insertBeginY * sin + dx; double kv = insertBeginX * sin + insertBeginY * cos + dy; double cu1 = cos, cu2 = sin; double cv1 = sin, cv2 = cos; double fu, fv, a, b, F1, F2; int Iu, Iv; srcBmpData = srcBmp.LockBits(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); dstBmpData = dstBmp.LockBits(new Rectangle(0, 0, dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); unsafe { byte* ptrSrc = (byte*)srcBmpData.Scan0; byte* ptrDst = (byte*)dstBmpData.Scan0; for (int i = 0; i < heightDst; i++) { for (int j = 0; j < widthDst; j++) { fu = j * cu1 - i * cu2 + ku; fv = j * cv1 + i * cv2 + kv; if ((fv < 1) || (fv > srcBmp.Height - 1) || (fu < 1) || (fu > srcBmp.Width - 1)) { ptrDst[i * dstBmpData.Stride + j * 3] = 150; ptrDst[i * dstBmpData.Stride + j * 3 + 1] = 150; ptrDst[i * dstBmpData.Stride + j * 3 + 2] = 150; } else {//双线性插值 Iu = (int)fu; Iv = (int)fv; a = fu - Iu; b = fv - Iv; for (int k = 0; k < 3; k++) { F1 = (1 - b) * *(ptrSrc + Iv * srcBmpData.Stride + Iu * 3 + k) + b * *(ptrSrc + (Iv + 1) * srcBmpData.Stride + Iu * 3 + k); F2 = (1 - b) * *(ptrSrc + Iv * srcBmpData.Stride + (Iu + 1) * 3 + k) + b * *(ptrSrc + (Iv + 1) * srcBmpData.Stride + (Iu + 1) * 3 + k); *(ptrDst + i * dstBmpData.Stride + j * 3 + k) = (byte)((1 - a) * F1 + a * F2); } } } } } srcBmp.UnlockBits(srcBmpData); dstBmp.UnlockBits(dstBmpData); break; } return true; }