您的位置:首页 > 运维架构

OpenCV中图像旋转(warpAffine)算法的实现过程

2014-04-08 20:05 651 查看
在OpenCV中,目前并没有现成的函数直接用来实现图像旋转,它是用仿射变换函数cv::warpAffine来实现的,此函数目前支持4种插值算法,最近邻、双线性、双三次、兰索斯插值,如果传进去的参数为基于像素区域关系插值算法(INTER_AREA),则按双线性插值。

通常使用2*3矩阵来表示仿射变换:







其中,T相当于变换前的原始图像,x,y为变换后的图像坐标。
对于cv::getRotationMatrix2D函数的实现公式为:





其中scale为缩放因子(x、y方向保持一致),angle为旋转角度(弧长),centerx,centery为旋转中心。
以lena.jpg图像旋转45度为例:

采用最近邻插值算法的实现代码为:

[cpp] view
plaincopy





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);

采用双线性插值算法的实现代码为:

[cpp] view
plaincopy





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);

其它插值算法的实现代码与双线性类似,可参考 /article/2577852.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: