一、仿射变换
1.1、仿射变换简介
仿射变换(Affine Transformation)是空间直角坐标系的变换,从一个二维坐标变换到另一个二维坐标,仿射变换是一个线性变换,他保持了图像的“平行性”和“平直性”,即图像中原来的直线和平行线,变换后仍然保持原来的直线和平行线,仿射变换比较常用的特殊变换有平移(Translation)、缩放(Scale)、翻转(Flip)、旋转(Rotation)和剪切(Shear)。一个任意的仿射变换都能表示为乘以一个矩阵(线性变换)接着再加上一个向量(平移)的形式。
在进行仿射变换的关键在于求解放射矩阵M,通过其对原图像进行变换得到最后的目标图像。在OpenCV中,与仿射变换相关的函数一般涉及到warpAffine( )和getRotationMatrix2D( )这两个。下面对这两个函数进行解析。
1.2、warpAffine( )函数解析
在OpenCV中,函数warpAffine( )是来实现一些简单的重映射。它是依据下式对图像做仿射变换。
void warpAffine(InputArray src,
OutputArray dst,
Size dsize,
int flags=INTER_LINEAR,
int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())
参数解析:
第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。
第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,需和源图片有一样的尺寸和类型。
第三个参数,InputArray类型的M,2×3的变换矩阵。
第四个参数,Size类型的dsize,表示输出图像的尺寸。
第五个参数,int类型的flags,插值方法的标识符。此参数有默认值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矩阵得到反变换。
第六个参数,int类型的borderMode,边界像素模式,默认值为BORDER_CONSTANT。
第七个参数,const Scalar&类型的borderValue,在恒定的边界情况下取的值,默认值为Scalar(),即0。
1.3、getRotationMatrix2D( )函数
getRotationMatrix2D( )函数是用来计算二维旋转的变换矩阵。
Mat getRotationMatrix2D(Point2fcenter,
double angle,
double scale)
第一个参数,Point2f类型的center,表示源图像的旋转中心。
第二个参数,double类型的angle,旋转角度。角度为正值表示向逆时针旋转(坐标原点是左上角)。
第三个参数,double类型的scale,缩放系数。
1.4、仿射变换实例
1、代码
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include
using namespace cv;
using namespace std;
#define WINDOW_NAME1 "【原始图窗口】" //为窗口标题定义的宏
#define WINDOW_NAME2 "【经过Warp后的图像】" //为窗口标题定义的宏
#define WINDOW_NAME3 "【经过Warp和Rotate后的图像】" //为窗口标题定义的宏
int main()
{
//【0】改变console字体颜色
system("color 1A");
//【1】参数准备
//定义两组点,代表两个三角形
Point2f srcTriangle[3];
Point2f dstTriangle[3];
//定义一些Mat变量
Mat rotMat(2, 3, CV_32FC1);
Mat warpMat(2, 3, CV_32FC1);
Mat srcImage, dstImage_warp, dstImage_warp_rotate;
//【2】加载源图像并作一些初始化
srcImage = imread("1.jpg", 1);
if (!srcImage.data) { printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false; }
// 设置目标图像的大小和类型与源图像一致
dstImage_warp = Mat::zeros(srcImage.rows, srcImage.cols, srcImage.type());
//【3】设置源图像和目标图像上的三组点以计算仿射变换
srcTriangle[0] = Point2f(0, 0);
srcTriangle[1] = Point2f(static_cast<float>(srcImage.cols - 1), 0);
srcTriangle[2] = Point2f(0, static_cast<float>(srcImage.rows - 1));
dstTriangle[0] = Point2f(static_cast<float>(srcImage.cols*0.0), static_cast<float>(srcImage.rows*0.33));
dstTriangle[1] = Point2f(static_cast<float>(srcImage.cols*0.65), static_cast<float>(srcImage.rows*0.35));
dstTriangle[2] = Point2f(static_cast<float>(srcImage.cols*0.15), static_cast<float>(srcImage.rows*0.6));
//【4】求得仿射变换
warpMat = getAffineTransform(srcTriangle, dstTriangle);
//【5】对源图像应用刚刚求得的仿射变换
warpAffine(srcImage, dstImage_warp, warpMat, dstImage_warp.size());
//【6】对图像进行缩放后再旋转
// 计算绕图像中点顺时针旋转50度缩放因子为0.6的旋转矩阵
Point center = Point(dstImage_warp.cols / 2, dstImage_warp.rows / 2);
double angle = -30.0;
double scale = 0.8;
// 通过上面的旋转细节信息求得旋转矩阵
rotMat = getRotationMatrix2D(center, angle, scale);
// 旋转已缩放后的图像
warpAffine(dstImage_warp, dstImage_warp_rotate, rotMat, dstImage_warp.size());
//【7】显示结果
imshow(WINDOW_NAME1, srcImage);
imshow(WINDOW_NAME2, dstImage_warp);
imshow(WINDOW_NAME3, dstImage_warp_rotate);
// 等待用户按任意按键退出程序
waitKey(0);
return 0;
}
2、运行结果
(1)原图片
(2)经过warp后的图片
(3)经过warp和rotate后的图片
二、特征匹配
2.1、SURF特征点描述
在opencv中,使用SURF算法进行特征点描述主要用到了drawMatches方法、SurfDescriptorExtractor类和BruteForceMatcher类。其中drawMatches( )函数用于绘制出相匹配的两个图像的关键点,利用SurfDescriptorExtractor类进行特征向量的相关计算,利用BruteForceMatcher类主要用于特征匹配。下面对drawMatches( )函数进行分析。
void drawMatches(const Mat& img1,
constvector& keypoints1,
const Mat& img2,
constvector& keypoints2,
constvector& matches1to2,
Mat& outImg,
const Scalar&matchColor=Scalar::all(-1),
const Scalar&singlePointColor=Scalar::all(-1),
const vector<char>&matchesMask=vector<char>(),
intflags=DrawMatchesFlags::DEFAULT )
第一个参数,const Mat&类型的img1,第一幅源图像。
第二个参数,const vector&类型的keypoints1,根据第一幅源图像得到的特征点,它是一个输出参数。
第三个参数,const Mat&类型的img2,第二幅源图像。
第四个参数,const vector&类型的keypoints2,根据第二幅源图像得到的特征点,它是一个输出参数。
第五个参数,matches1to2,第一幅图像到第二幅图像的匹配点,即表示每一个图1中的特征点都在图2中有一一对应的点、
第六个参数,Mat&类型的outImg,输出图像,其内容取决于第五个参数标识符falgs。
第七个参数,const Scalar&类型的matchColor,匹配的输出颜色,即线和关键点的颜色。它有默认值Scalar::all(-1),表示颜色是随机生成的。
第八个参数,const Scalar&类型的singlePointColor,单一特征点的颜色,它也有表示随机生成颜色的默认值Scalar::all(-1)。
第九个参数,matchesMask,确定哪些匹配是会绘制出来的掩膜,如果掩膜为空,表示所有匹配都进行绘制。
第十个参数,int类型的flags,特征绘制的标识符,有默认值DrawMatchesFlags::DEFAULT。
3.2、图像特征匹配实例
1、实现图像特征匹配的步骤如下:
(1)使用 DescriptorExtractor 接口来寻找关键点对应的特征向量。
(2)使用 SurfDescriptorExtractor 以及它的函数 compute( )来完成特定的计算。
(3)使用 BruteForceMatcher 来匹配特征向量。
(4)使用函数 drawMatches 来绘制检测到的匹配点。
2、代码
#include "opencv2/core/core.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include
#include
#include
using namespace cv;
using namespace std;
int main()
{
//【0】改变console字体颜色
system("color 1A");
//【1】载入素材图
Mat srcImage1 = imread("1.jpg", 1);
Mat srcImage2 = imread("2.jpg", 1);
if (!srcImage1.data || !srcImage2.data)
{
printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false;
}
//【2】使用SURF算子检测关键点
int minHessian = 2000;//SURF算法中的hessian阈值
SurfFeatureDetector detector(minHessian);//定义一个SurfFeatureDetector(SURF) 特征检测类对象
std::vector keyPoint1, keyPoints2;//vector模板类,存放任意类型的动态数组
//【3】调用detect函数检测出SURF特征关键点,保存在vector容器中
detector.detect(srcImage1, keyPoint1);
detector.detect(srcImage2, keyPoints2);
//【4】计算描述符(特征向量)
SurfDescriptorExtractor extractor;
Mat descriptors1, descriptors2;
extractor.compute(srcImage1, keyPoint1, descriptors1);
extractor.compute(srcImage2, keyPoints2, descriptors2);
//【5】使用BruteForce进行匹配
// 实例化一个匹配器
BruteForceMatcher< L2<float> > matcher;
std::vector< DMatch > matches;
//匹配两幅图中的描述子(descriptors)
matcher.match(descriptors1, descriptors2, matches);
//【6】绘制从两个图像中匹配出的关键点
Mat imgMatches;
drawMatches(srcImage1, keyPoint1, srcImage2, keyPoints2, matches, imgMatches);//进行绘制
//【7】显示效果图
imshow("匹配图", imgMatches);
waitKey(0);
return 0;
}
3、运行效果
(1)原图
(2)特征匹配效果图