效果
↓ 旋转、平移、缩放 叠加之后的效果
原理 二维齐次变换矩阵
实现
↑ 将旋转、平移添加到二维齐次变换矩阵
↑ 将缩放添加到二维其次变换矩阵
代码
1、只是初步实现了,存在一些小毛刺需要优化,优化问题有时间再水一篇;
2、为了处理速度尽可能快,几乎所有的运算都直接在原地做了;
3、图像旋转的本质还是求点的映射,这段代码理论上可以修改一下直接给点、轮廓等类型使用。
#include<iostream>
#include<opencv2/opencv.hpp>
#include<math.h>
using namespace cv;
using namespace std;
const float PI = acos(-1);
float rad(float angle){ return angle*PI / 180; } //求角度的弧度值
float deg(float radian){ return radian * 180 / PI; }//求弧度的角度值
/*
显示3×3矩阵
*/
void show(float *a)
{
for (int i = 0; i < 9; ++i)
{
if (i && i % 3 == 0) cout << endl;
cout << a[i] << " ";
}
cout << endl;
}
/*
计算矩阵的左乘
@ lastMatrix :输入左矩阵 3×3
@ currentMatrix :输入右矩阵 和 输出 3×3
*/
void leftMult_3By3(float * lastMatrix,float * currentMatrix)
{
float arrayCopy[9];
memcpy(arrayCopy, currentMatrix, 9 * sizeof(float));
for (int i = 0; i < 9; ++i)
{
int row = i / 3;
int col = i % 3;
currentMatrix[i] = 0;
for (int j = 0; j < 3; ++j)
{
currentMatrix[i] += lastMatrix[row * 3 + j] * arrayCopy[j * 3 + col];
}
}
return;
}
/*
计算矩阵的右乘
@ currentMatrix :输入左矩阵 和 输出 3×3
@ nextMatrix :输入右矩阵 3×3
*/
void rightMult_3By3(float * currentMatrix, float * nextMatrix)
{
float arrayCopy[9];
memcpy(arrayCopy, currentMatrix, 9 * sizeof(float));
for (int i = 0; i < 9; ++i)
{
int row = i / 3;
int col = i % 3;
currentMatrix[i] = 0;
for (int j = 0; j < 3; ++j)
{
currentMatrix[i] += arrayCopy[row * 3 + j] * nextMatrix[j * 3 + col];
}
}
return;
}
/*
生成二维变换的齐次变换矩阵。
@ 返回值 : 三阶单位矩阵
*/
float * homTransMatrixIdentity()
{
float * f_temp = new float[9];
f_temp[0] = 1;
f_temp[1] = 0;
f_temp[2] = 0;
f_temp[3] = 0;
f_temp[4] = 1;
f_temp[5] = 0;
f_temp[6] = 0;
f_temp[7] = 0;
f_temp[8] = 1;
return f_temp;
}
/*
释放二维变换的齐次变换矩阵。
@ hom2DTransMatrix
*/
void homTransMatrixRelease(float * hom2DTransMatrix){ delete hom2DTransMatrix; return; }
/*
向齐次二维变换矩阵添加一个平移。
@ dx :x方向上的平移
@ dy :y方向上的平移
@ hom2DTransMatrix :输入和输出变换矩阵
*/
void homTransMatrixTranslation(int dx, int dy, float * hom2DTransMatrix)
{
hom2DTransMatrix[2] += dx;
hom2DTransMatrix[5] += dy;
return;
}
/*
向齐次二维变换矩阵添加一个旋转。
@ phiRad : 旋转的弧度
@ px :变换不动点的x坐标
@ py :变换不动点的y坐标
@ hom2DTransMatrix :输入和输出变换矩阵
*/
void homTransMatrixRotate(float phiRad, int px, int py, float * hom2DMatrixInputAndOutput)
{
float fLeft[9] = { 1, 0, px, 0, 1, py, 0, 0, 1 };
float fRight[9] = { 1, 0, -px, 0, 1, -py, 0, 0, 1 };
float fRotate[9] = { cos(phiRad), -sin(phiRad), 0,sin(phiRad), cos(phiRad), 0,0, 0, 1 };
leftMult_3By3(fLeft, fRotate);
rightMult_3By3(fRotate, fRight);
leftMult_3By3(fRotate, hom2DMatrixInputAndOutput);
return;
}
/*
向齐次二维变换矩阵添加一个缩放。
@ sx : x方向伸缩比例
@ sy : y方向伸缩比例
@ px :变换不动点的x坐标
@ py :变换不动点的y坐标
@ hom2DTransMatrix :输入和输出变换矩阵
*/
void homTransMatrixScale(float sx, float sy, int px, int py, float * hom2DMatrixInputAndOutput)
{
float fLeft[9] = { 1, 0, px, 0, 1, py, 0, 0, 1 };
float fRight[9] = { 1, 0, -px, 0, 1, -py, 0, 0, 1 };
float fScale[9] = { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
leftMult_3By3(fLeft, fScale);
rightMult_3By3(fScale, fRight);
leftMult_3By3(fScale, hom2DMatrixInputAndOutput);
return;
}
/*
对图像应用任意仿射2D变换。
@ src : 输入图像
@ dst : 输出图像
@ hom2DMatrix :输入变换矩阵
*/
void affineImage(const Mat & src, Mat & dst, const float * hom2DMatrix)
{
//P1求矩阵的逆矩阵
//https://www.shuxuele.com/algebra/matrix-inverse-minors-cofactors-adjugate.html
float InverseMatrix[9];
memcpy(InverseMatrix, hom2DMatrix, 9 * sizeof(float));
//求矩阵的行列式
float det = hom2DMatrix[0] * hom2DMatrix[4] * hom2DMatrix[8] +
hom2DMatrix[1] * hom2DMatrix[5] * hom2DMatrix[6] +
hom2DMatrix[2] * hom2DMatrix[3] * hom2DMatrix[7] -
hom2DMatrix[2] * hom2DMatrix[4] * hom2DMatrix[6] -
hom2DMatrix[0] * hom2DMatrix[5] * hom2DMatrix[7] -
hom2DMatrix[1] * hom2DMatrix[3] * hom2DMatrix[8];
//P1 求矩阵的余子式矩阵和代数余子式阵列 求伴随矩阵
InverseMatrix[0] = (hom2DMatrix[4] * hom2DMatrix[8] - hom2DMatrix[5] * hom2DMatrix[7]) / det;
InverseMatrix[3] = (hom2DMatrix[5] * hom2DMatrix[6] - hom2DMatrix[3] * hom2DMatrix[8]) / det;
InverseMatrix[6] = (hom2DMatrix[3] * hom2DMatrix[7] - hom2DMatrix[4] * hom2DMatrix[6]) / det;
InverseMatrix[1] = (hom2DMatrix[2] * hom2DMatrix[7] - hom2DMatrix[1] * hom2DMatrix[8]) / det;
InverseMatrix[4] = (hom2DMatrix[0] * hom2DMatrix[8] - hom2DMatrix[2] * hom2DMatrix[6]) / det;
InverseMatrix[7] = (hom2DMatrix[1] * hom2DMatrix[6] - hom2DMatrix[0] * hom2DMatrix[7]) / det;
InverseMatrix[2] = (hom2DMatrix[1] * hom2DMatrix[5] - hom2DMatrix[2] * hom2DMatrix[4]) / det;
InverseMatrix[5] = (hom2DMatrix[2] * hom2DMatrix[3] - hom2DMatrix[0] * hom2DMatrix[5]) / det;
InverseMatrix[8] = (hom2DMatrix[0] * hom2DMatrix[4] - hom2DMatrix[1] * hom2DMatrix[3]) / det;
//P2 获取像素在src的位置,赋值像素
dst = Mat::zeros(src.size(), CV_8UC3);
for (int row = 0; row < dst.rows; ++row)
{
for (int col = 0; col < dst.cols; ++col)
{
int colFrom = round(col*InverseMatrix[0] + row*InverseMatrix[1] + InverseMatrix[2]);
if ((colFrom < 0 || colFrom >= src.cols)) continue;
int rowFrom = round(col*InverseMatrix[3] + row*InverseMatrix[4] + InverseMatrix[5]);
if (rowFrom < 0 || rowFrom >= src.rows)
{
continue;
}
dst.at<Vec3b>(row, col) = src.at<Vec3b>(rowFrom, colFrom);
}
}
}
void test1()
{
float * e = homTransMatrixIdentity();
homTransMatrixRotate(rad(-45), 100, 100, e);
homTransMatrixTranslation(100, 100, e);
homTransMatrixScale(0.8, 0.75, 200, 200, e);
show(e);
Mat imageImput = imread("5.jpg");
Mat imageAffine;
affineImage(imageImput, imageAffine, e);
imshow("imageImput", imageImput);
imshow("imageAffine", imageAffine);
homTransMatrixRelease(e);
waitKey();
return;
}
int main()
{
test1();
return 0;
}