在OpenCV中,目前並沒有現成的函數直接用來實現圖像旋轉,它是用仿射變換函數cv::warpAffine來實現的,此函數目前支持4種插值算法,最近鄰、雙線性、雙三次、蘭索斯插值,如果傳進去的參數爲基於像素區域關係插值算法(INTER_AREA),則按雙線性插值。
通常使用2*3矩陣來表示仿射變換:
其中,T相當於變換前的原始圖像,x,y爲變換後的圖像座標。
對於cv::getRotationMatrix2D函數的實現公式爲:
其中scale爲縮放因子(x、y方向保持一致),angle爲旋轉角度(弧長),centerx,centery爲旋轉中心。
以lena.jpg圖像旋轉45度爲例:
採用最近鄰插值算法的實現代碼爲:
- cv::Mat matSrc = cv::imread("lena.jpg", 2 | 4);
- if (matSrc.empty()) return;
- const double degree = 45;
- double angle = degree * CV_PI / 180.;
- double alpha = cos(angle);
- double beta = sin(angle);
- int iWidth = matSrc.cols;
- int iHeight = matSrc.rows;
- int iNewWidth = cvRound(iWidth * fabs(alpha) + iHeight * fabs(beta));
- int iNewHeight = cvRound(iHeight * fabs(alpha) + iWidth * fabs(beta));
- double m[6];
- m[0] = alpha;
- m[1] = beta;
- m[2] = (1 - alpha) * iWidth / 2. - beta * iHeight / 2.;
- m[3] = -m[1];
- m[4] = m[0];
- m[5] = beta * iWidth / 2. + (1 - alpha) * iHeight / 2.;
- cv::Mat M = cv::Mat(2, 3, CV_64F, m);
- cv::Mat matDst1 = cv::Mat(cv::Size(iNewWidth, iNewHeight), matSrc.type(), cv::Scalar::all(0));
- double D = m[0]*m[4] - m[1]*m[3];
- D = D != 0 ? 1./D : 0;
- double A11 = m[4]*D, A22 = m[0]*D;
- m[0] = A11; m[1] *= -D;
- m[3] *= -D; m[4] = A22;
- double b1 = -m[0]*m[2] - m[1]*m[5];
- double b2 = -m[3]*m[2] - m[4]*m[5];
- m[2] = b1; m[5] = b2;
- int round_delta = 512;//nearest
- for (int y=0; y<iNewHeight; ++y)
- {
- for (int x=0; x<iNewWidth; ++x)
- {
- //int tmpx = cvFloor(m[0] * x + m[1] * y + m[2]);
- //int tmpy = cvFloor(m[3] * x + m[4] * y + m[5]);
- int adelta = cv::saturate_cast<int>(m[0] * x * 1024);
- int bdelta = cv::saturate_cast<int>(m[3] * x * 1024);
- int X0 = cv::saturate_cast<int>((m[1] * y + m[2]) * 1024) + round_delta;
- int Y0 = cv::saturate_cast<int>((m[4] * y + m[5]) * 1024) + round_delta;
- int X = (X0 + adelta) >> 10;
- int Y = (Y0 + bdelta) >> 10;
- if ((unsigned)X < iWidth && (unsigned)Y < iHeight)
- {
- matDst1.at<cv::Vec3b>(y, x) = matSrc.at<cv::Vec3b>(Y, X);
- }
- }
- }
- cv::imwrite("rotate_nearest_1.jpg", matDst1);
- M = cv::getRotationMatrix2D(cv::Point2f(iWidth / 2., iHeight / 2.), degree, 1);
- cv::Mat matDst2;
- cv::warpAffine(matSrc, matDst2, M, cv::Size(iNewWidth, iNewHeight), 0, 0, 0);
- cv::imwrite("rotate_nearest_2.jpg", matDst2);
- cv::Mat matSrc = cv::imread("lena.jpg", 2 | 4);
- if (matSrc.empty()) return;
- const double degree = 45;
- double angle = degree * CV_PI / 180.;
- double alpha = cos(angle);
- double beta = sin(angle);
- int iWidth = matSrc.cols;
- int iHeight = matSrc.rows;
- int iNewWidth = cvRound(iWidth * fabs(alpha) + iHeight * fabs(beta));
- int iNewHeight = cvRound(iHeight * fabs(alpha) + iWidth * fabs(beta));
- double m[6];
- m[0] = alpha;
- m[1] = beta;
- m[2] = (1 - alpha) * iWidth / 2. - beta * iHeight / 2.;
- m[3] = -m[1];
- m[4] = m[0];
- m[5] = beta * iWidth / 2. + (1 - alpha) * iHeight / 2.;
- cv::Mat M = cv::Mat(2, 3, CV_64F, m);
- cv::Mat matDst1 = cv::Mat(cv::Size(iNewWidth, iNewHeight), matSrc.type(), cv::Scalar::all(0));
- double D = m[0]*m[4] - m[1]*m[3];
- D = D != 0 ? 1./D : 0;
- double A11 = m[4]*D, A22 = m[0]*D;
- m[0] = A11; m[1] *= -D;
- m[3] *= -D; m[4] = A22;
- double b1 = -m[0]*m[2] - m[1]*m[5];
- double b2 = -m[3]*m[2] - m[4]*m[5];
- m[2] = b1; m[5] = b2;
- for (int y=0; y<iNewHeight; ++y)
- {
- for (int x=0; x<iNewWidth; ++x)
- {
- //int tmpx = cvFloor(m[0] * x + m[1] * y + m[2]);
- //int tmpy = cvFloor(m[3] * x + m[4] * y + m[5]);
- float fx = m[0] * x + m[1] * y + m[2];
- float fy = m[3] * x + m[4] * y + m[5];
- int sy = cvFloor(fy);
- fy -= sy;
- //sy = std::min(sy, iHeight-2);
- //sy = std::max(0, sy);
- if (sy < 0 || sy >= iHeight) continue;
- short cbufy[2];
- cbufy[0] = cv::saturate_cast<short>((1.f - fy) * 2048);
- cbufy[1] = 2048 - cbufy[0];
- int sx = cvFloor(fx);
- fx -= sx;
- //if (sx < 0) {
- // fx = 0, sx = 0;
- //}
- //if (sx >= iWidth - 1) {
- // fx = 0, sx = iWidth - 2;
- //}
- if (sx < 0 || sx >= iWidth) continue;
- short cbufx[2];
- cbufx[0] = cv::saturate_cast<short>((1.f - fx) * 2048);
- cbufx[1] = 2048 - cbufx[0];
- for (int k=0; k<matSrc.channels(); ++k)
- {
- if (sy == iHeight - 1 || sx == iWidth - 1) {
- continue;
- } else {
- matDst1.at<cv::Vec3b>(y, x)[k] = (matSrc.at<cv::Vec3b>(sy, sx)[k] * cbufx[0] * cbufy[0] +
- matSrc.at<cv::Vec3b>(sy+1, sx)[k] * cbufx[0] * cbufy[1] +
- matSrc.at<cv::Vec3b>(sy, sx+1)[k] * cbufx[1] * cbufy[0] +
- matSrc.at<cv::Vec3b>(sy+1, sx+1)[k] * cbufx[1] * cbufy[1]) >> 22;
- }
- }
- }
- }
- cv::imwrite("rotate_bilinear_1.jpg", matDst1);
- M = cv::getRotationMatrix2D(cv::Point2f(iWidth / 2., iHeight / 2.), degree, 1);
- cv::Mat matDst2;
- cv::warpAffine(matSrc, matDst2, M, cv::Size(iNewWidth, iNewHeight), 1, 0, 0);
- cv::imwrite("rotate_bilinear_2.jpg", matDst2);
其它插值算法的實現代碼與雙線性類似,可參考 http://blog.csdn.net/fengbingchun/article/details/17335477