平移的矩阵:
缩放的矩阵:
旋转矩阵:
有的公式两个β的符号相反,并不是公式不正确,而是表示顺时针或逆时针旋转
理论:
那么,如果想要对一个图片进行旋转操作,由于opencv的原点设置在左上角,需要将图像的中心移动到 (width2,height2) ( w i d t h 2 , h e i g h t 2 ) 处,其中width和height表示图像的宽度和高度。
然后对图像进行旋转
旋转后在移动会左上角
如果想要缩放,那么在最后乘上一个缩放矩阵即可
那么公式叠在一起就是(此处先不考虑缩放)
x,y表示要横向和纵向要平移的长度
公式M整理后为
那么,一个坐标点 (X,Y) ( X , Y ) 经过M旋转后的公式就应该是
在运算的过程中,把M的齐次部分取消,变成
注意,有的博客里面的公式M写错了,例如此篇
http://lib.csdn.net/article/opencv/28860
不过这篇博客代码整理的还是挺好的,而且代码里面的东西没写错-_-
代码实现:
在CV的源码当中,使用了几个技巧
对矩阵M求逆矩阵是什么个意思呢?
此部分代码
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));
/*
m0 m1 m2 = alpha beta ...
m3 m4 m5 -beta alpha ...
*/
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));
//求M的逆矩阵,即将m变成m的逆
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;
上面代码部分可以见到m中存储的是旋转矩阵M,D中存储的是m[0] * m[4] - m[1] * m[3],通过带入α和β可以知道D等于1
当然,这是在图像缩放大小都等于1的情况下,也就是图像不进行缩放变换,如果添加的图像的缩放,那么 D=1ab D = 1 a b
接下来说一下为什么要计算逆矩阵?
在图像的变换中,原始图像是S,目标图像是D
同时,设D(x,y)表示图像D的第x行,第y列,同理S
按照道理,应该应用如下数学公式计算,即
D(x,y)=MS(x,y) D ( x , y ) = M S ( x , y )
但是,我们在代码当中枚举的行和列是目标图像D的行和列
也就是我想要知道在图像D(x,y)处的像素值,对应于原始图像S处像素值S(x,y)经过旋转平移缩放等一系列操作后的值应该是多少呢?
不聪明的小伙伴(比如我),估计也能想出来的,答案就是原始图像的逆操作。
代码:
#include
#include
#include
#include
#include
#include
using namespace cv;
using namespace std;
void WarpAffine(const Mat &src, Mat &dst,double *m)
{
int iNewWidth = cvRound(src.cols * fabs(m[0]) + src.rows * fabs(m[1]));//旋转后新图像的大小
int iNewHeight = cvRound(src.rows * fabs(m[0]) + src.cols * fabs(m[1]));
/*
m0 m1 m2 = alpha beta ...
m3 m4 m5 -beta alpha ...
*/
dst.create(Size(iNewWidth, iNewHeight), src.type());
//求M的逆矩阵
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;//由于数据扩大了1024倍,此部分相当于对X0和Y0增加0.5
for (int y = 0; yfor (int x = 0; xint 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 < src.cols && (unsigned)Y < src.rows)
{
dst.at(y, x) = src.at(Y, X);//src对应逆旋转操作后的像素点
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
Mat src = imread("src.jpg");
Mat dst;
double degree = 45;
double angle = degree * CV_PI / 180.;
double alpha = cos(angle);
double beta = sin(angle);
double m[6];
m[0] = alpha;
m[1] = beta;
m[2] = (1 - alpha) * src.cols / 2. - beta * src.rows / 2.;
m[3] = -m[1];
m[4] = m[0];
m[5] = beta * src.cols / 2. + (1 - alpha) * src.rows / 2.;
WarpAffine(src, dst,m);
imshow("dst2", src);
imshow("dst", dst);
waitKey();
system("pause");
return 0;
}
指针遍历
void WarpAffine(const Mat &src, Mat &dst,double *m)
{
int iNewWidth = cvRound(src.cols * fabs(m[0]) + src.rows * fabs(m[1]));//旋转后新图像的大小
int iNewHeight = cvRound(src.rows * fabs(m[0]) + src.cols * fabs(m[1]));
/*
m0 m1 m2 = alpha beta ...
m3 m4 m5 -beta alpha ...
*/
dst.create(Size(iNewWidth, iNewHeight), src.type());
//求M的逆矩阵
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;
uchar *ps = src.data;
uchar *pd = dst.data;
int channel = src.channels();
int round_delta = 512;//由于数据扩大了1024倍,此部分相当于对X0和Y0增加0.5
for (int y = 0; yfor (int x = 0; xint 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 < src.cols && (unsigned)Y < src.rows)
{
for (int c = 0; c < channel; c++)
{
pd[(y * dst.cols + x) * channel + c] = ps[(Y * src.cols + X) * channel + c];
}
}
}
}
}