仿射变换(Affine Transformation或 Affine Map),又称仿射映射,是指在几何中,一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间的过程。它保持了二维图形的“平直性”(即:直线经过变换之后依然是直线)和“平行性”(即:二维图形之间的相对位置关系保持不变,平行线依然是平行线,且直线上点的位置顺序不变)。
一个任意的仿射变换都能表示为乘以一个矩阵(线性变换)接着再加上一个向量(平移)的形式。
三种常见的变换形式:
- 旋转,rotation (线性变换)
- 平移,translation(向量加)
- 缩放,scale(线性变换)
仿射变换针对的是平面上的物体位姿变化,如水平/垂直方向位移、旋转、缩小/放大,常见的应用有ORC字符识别。投影变换针对的是三维空间中的位置变化,受限于物体依然是平面的,也称为二维投影变换,常见的应用有车牌识别。
使用OpenCV函数warpAffine 来实现一些简单的重映射。
使用OpenCV函数getRotationMatrix2D 来获得旋转矩阵。
warpAffine函数
void warpAffine(InputArray src,//输入图像,即源图像,填Mat类的对象即可
OutputArray dst, //函数调用后的运算结果存在这里,需和源图片有一样的尺寸和类型
InputArray M, //2×3的变换矩阵
Size dsize, //表示输出图像的尺寸
int flags=INTER_LINEAR,//插值方法的标识符。此参数有默认值INTER_LINEAR(线性插值),可选的插值方式如下:
//INTER_NEAREST - 最近邻插值
//INTER_LINEAR - 线性插值(默认值)
//INTER_AREA - 区域插值
//INTER_CUBIC –三次样条插值
//INTER_LANCZOS4 -Lanczos插值
//CV_WARP_FILL_OUTLIERS - 填充所有输出图像的象素。如果部分象素落在输入图像的边界外,那么它们的值设定为 fillval.
//CV_WARP_INVERSE_MAP –表示M为输出图像到输入图像的反变换,即 。因此可以直接用来做象素插值。否则, warpAffine函数从M矩阵得到反变换
intborderMode=BORDER_CONSTANT, //边界像素模式,默认值为BORDER_CONSTANT。
const Scalar& borderValue=Scalar())//在恒定的边界情况下取的值,默认值为Scalar(),即0
getRotationMatrix2D
Mat getRotationMatrix2D(Point2fcenter, //表示源图像的旋转中心
double angle, //旋转角度。角度为正值表示向逆时针旋转(坐标原点是左上角)
double scale)//缩放系数
代码:
#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
#include<iostream>
#include<math.h>
#include <string>
#include<fstream>
using namespace cv;
using namespace std;
using namespace cv::xfeatures2d;
int main()
{
//定义两组点,代表两个三角形
Point2f srcTriangle[3];
Point2f dstTriangle[3];
//定义一些Mat变量
Mat rotMat(2, 3, CV_32FC1);
Mat warpMat(2, 3, CV_32FC1);
Mat src, dst_warp, dst_warp_rotate;
src = imread("C:/Users/Administrator/Desktop/pic/5.jpg");
dst_warp = Mat::zeros(src.rows, src.cols, src.type());
//设置源图像和目标图像上的三组点以计算仿射变换
srcTriangle[0] = Point2f(0, 0);
srcTriangle[1] = Point2f(static_cast<float>(src.cols - 1), 0);
srcTriangle[2] = Point2f(0, static_cast<float>(src.rows - 1));
dstTriangle[0] = Point2f(static_cast<float>(src.cols*0.0), static_cast<float>(src.rows*0.33));
dstTriangle[1] = Point2f(static_cast<float>(src.cols*0.65), static_cast<float>(src.rows*0.35));
dstTriangle[2] = Point2f(static_cast<float>(src.cols*0.15), static_cast<float>(src.rows*0.6));
//仿射变换
warpMat = getAffineTransform(srcTriangle, dstTriangle);
warpAffine(src, dst_warp, warpMat, dst_warp.size());
//对图像进行缩放后再旋转
// 计算绕图像中点顺时针旋转50度缩放因子为0.6的旋转矩阵
Point center = Point(dst_warp.cols / 2, dst_warp.rows / 2);
double angle = -30.0;
double scale = 0.8;
// 通过上面的旋转细节信息求得旋转矩阵
rotMat = getRotationMatrix2D(center, angle, scale);
// 旋转已缩放后的图像
warpAffine(dst_warp, dst_warp_rotate, rotMat, dst_warp.size());
imshow("src", src);
imshow("dst1", dst_warp);
imshow("dst", dst_warp_rotate);
waitKey(0);
}
结果:
积分图计算
积分图:将图像中任意一点的像素值变成其左上角所有像素值的总和(包含其自身像素值)得出的图像就叫积分图通过积分图像,可以加快计算速度。
API:
void integral( // 积分图计算
InputArray src, // 输入图像,灰度图
OutputArray sum, // 求和,输出的尺寸比原图行列都要加1 , 不想行列变化的,自己实现积分图算法
OutputArray sqsum, // 求和的平方,输出的尺寸比原图行列都要加1
OutputArray tilted, // 旋转45°的积分图
int sdepth = -1,
int sqdepth = -1
);
代码:
#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
#include<iostream>
#include<math.h>
#include <string>
#include<fstream>
using namespace cv;
using namespace std;
using namespace cv::xfeatures2d;
int main() {
Mat src = imread("C:/Users/Administrator/Desktop/pic/1.jpg",0);
imshow("input", src);
Mat dst1 = Mat::zeros(src.rows + 1, src.cols + 1, CV_32FC1);
Mat dst2 = Mat::zeros(src.rows + 1, src.cols + 1, CV_64FC1);
integral(src, dst1, dst2);
Mat res1, res2;
normalize(dst1, res1, 0, 255, NORM_MINMAX,CV_8UC1);
imshow("dst1", dst1);
normalize(dst2, res2, 0, 255, NORM_MINMAX,CV_8UC1);
imshow("dst2", dst2);
waitKey(0);
}
结果: