放射变换相关函数warpAffine和getRotationMatrix2D,SURF(特征点描述)算法在
OpenCV中进一步的体现与应用。
一、仿射变换
1.1 初识仿射变换
仿射变换(Affine Transformation或Affine Map),又称仿射映射,是指在几何中,一个向量
空间进行一次线性变换并接上一个平移,保持了二维图形的“平直性”(即:直线经过变换以后依然是直线)
和“平行性”(即:二维图形之间的相对位置关系保持不变,平行线依然是平行线,且直线上点的位置顺序不变)
一个任意的仿射变化都能表示为乘以一个矩阵(线性变换)接着在加上一个向量(平移)的形式。
则,常见的仿射变换有如下三种:
旋转,rotation(线性变换)
平移,translation(向量加)
缩放,scale(线性变换)
仿射变换代表两幅图之间的一种映射关系。
1.2、仿射变换相关的函数使用
OpenCV仿射变换相关的函数一般涉及到warpAffine和getRotationMatrix2D:
使用warpAffine来实现一些简单的重映射。
使用getRotationMatrix2D来获得旋转矩阵
二、warpAffine函数详解
warpAffine函数的作用是依据如下式子,对图像做仿射变换。
dst(x,y)= src(M11x + M12y +M13,M21x + M22y +M23)
函数原型如下:
void warpAffine( InputArray src, OutputArray dst, InputArray M, Size dsize,
int flags=INTER_LINEAR, intborderMode=BORDER_CONSTANT,const Scalar& borderValue=Scalar())
第一个参数:src,输入图像,Mat类对象
第二个参数: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。
三、getRotationMatrix2D
计算二维旋转变换矩阵。变换会将旋转中心映射到它自身。
Mat getRotationMatrix2D(Point2f center,double angle,double sclae)
第一个参数,Point2f类型的center,表示源图像的旋转中心。
第二个参数,double类型的angle,旋转角度,角度为正值表示向逆时针旋转(坐标原点是左上角)
第三个参数,double类型的scale,缩放系数。
#include
#include
#include
#include
using namespace std;
using namespace cv;
//------------------------- 宏定义部分 -------------------------------//
#define WindowName1 "源始图窗口"
#define WindowName2 "经过warp后的图像窗口"
#define WindowName3 "经过warp和rotate后的图像"
//---------------------------全局函数声明部分 --------------------------------//
static void HelpText();
// 描述:控制台应用程序入口函数,程序从这里开始
int main()
{
HelpText();
// 参数准备
// 定义两组点,代表两个三角形
Point2f srcTriangle[3];
Point2f dstTriangle[3];
//定义一些Mat变量
Mat rotMat(2,3,CV_32FC1); // 通过Mat类的构造函数对rotMat进行初始化
Mat warpMat(2,3,CV_32FC1);
Mat srcImage,dstImage_warp,dstImage_warp_rotate;
// 加载源图像并做一些初始化
srcImage = imread("D:\\photo\\bird.jpg");
if(!srcImage.data) { cout << "srcImage not load " << endl; return false;}
// 设置目标图像的大小、类型和源图像一致
dstImage_warp = Mat::zeros(srcImage.size(),srcImage.type());
// 设置源图像和目标图像上的三组点以计算仿射变换
srcTriangle[0] = Point2f(0,0);
srcTriangle[1] = Point2f(static_cast(srcImage.cols - 1),0);
srcTriangle[2] = Point2f(0,static_cast(srcImage.rows - 1));
dstTriangle[0] = Point2f(static_cast(srcImage.cols * 0.0),static_cast(srcImage.rows * 0.33));
dstTriangle[1] = Point2f(static_cast(srcImage.cols * 0.56),static_cast(srcImage.rows * 0.23));
dstTriangle[2] = Point2f(static_cast(srcImage.cols * 0.15),static_cast(srcImage.rows * 0.63));
// 求得仿射变换矩阵
warpMat = getAffineTransform( srcTriangle,dstTriangle);
// 对源图像应用刚刚求得的仿射变换
warpAffine(srcImage,dstImage_warp,warpMat,dstImage_warp.size());
// 对图像进行缩放后再旋转
// 计算绕图像中点逆时针旋转45°,缩放因子为0.5d的旋转矩阵
Point center = Point( dstImage_warp.cols/2, dstImage_warp.rows/2);
double angle = 45.0;
double scale = 0.5;
// 通过上面的旋转细节信息求得旋转矩阵
rotMat = getRotationMatrix2D( center,angle,scale);
// 旋转已缩放后的图像
warpAffine( dstImage_warp, dstImage_warp_rotate,rotMat,dstImage_warp_rotate.size());
// 显示窗口
imshow( WindowName1,srcImage);
imshow( WindowName2,dstImage_warp);
imshow( WindowName3,dstImage_warp_rotate);
waitKey(0);
return 0;
}
static void HelpText()
{
// 输出一些帮助信息
cout << "欢迎来到【仿射变换】示例程序" << endl;
}
四、SURF特征点描述
SURF,英文全称为SpeededUp Robust Features,直译为“加速版的具有鲁棒性的特征”算法,
由Bay在2006年首次提出。SURF是尺度不变特征变换算法(SIFT算法)的加速版。一般来说,
标准的SURF算子比SIFT算子快好几倍,并且在多幅图片下具有更好的稳定性。SURF最大的特点
在用采用了harr特征以及积分图像的概念,这大大加快了程序的运行时间。SURF可以应用于计算机
视觉的物体识别以及3D重构中。
SURF算法部分,常常涉及到SURF、SurfFeatureDetector、SurfDescriptorExtractor这三个 类 ,后面两个是前一个的别名,三者是等价的。
drawKeypoints函数详解
void drawKeypoints( const Mat& image,const vector
constScalar& color=Scalar::all(-1), int flags=DrawMatchesFlags::DEFAULT)
第一个参数,const Mat&类型的src,输入图像。
第二个参数,const vector
输出参数。
第三个参数,Mat&类型的outImage,输出图像,其内容取决于第五个参数标识符flags。
第四个参数,const Scalar&类型的color,关键点的颜色,有默认值Scalar::all(-1).
第五个参数,int类型的flags,绘制关键点的特征标识符,有默认值DrawMatchesFlags::DEFAULT.
struct DrawMatchesFlags
{
enum
{
DEFAULT = 0, // Output image matrix will be created (Mat::create),
// i.e. existing memory of output image may be reused.
// Two source images, matches, and single keypoints
// will be drawn.
// For each keypoint, only the center point will be
// drawn (without a circle around the keypoint with the
// keypoint size and orientation).
DRAW_OVER_OUTIMG = 1, // Output image matrix will not be
// created (using Mat::create). Matches will be drawn
// on existing content of output image.
NOT_DRAW_SINGLE_POINTS = 2, // Single keypoints will not be drawn.
DRAW_RICH_KEYPOINTS = 4 // For each keypoint, the circle around
// keypoint with keypoint size and orientation will
// be drawn.
};
};
SURF特征点检测的程序的流程:
(1)使用FeatureDetector接口来发现感兴趣点。
(2)使用SurFeatureDetector以及其函数detect来实现检测过程
(3)使用函数drawKeypoints绘制检测到的关键点
//-------------------------------- SURF特征点检测 -------------------------------------------//
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main(int argc,char** argv)
{
Mat srcImage = imread("D:\\photo\\bird1.jpg");
if(!srcImage.data){ cout << "读取图片错误,请确定目录下是否有指定图片存在" << endl;}
imshow("源始图",srcImage);
// 定义需要用到的变量和类
// minHessian的值一般在400~800之间。minHessian是一个阈值,它决定了哪些值是你接受的关键点。minHessian值越高,得到的关键点越少,但是关键点也就更好,反之,minHessian值越低,得到的关键点越多,关键点质量越差。
int minHessian = 600; // 定义SURF中hessian阈值特征点检测算子
// 定义一个SurfFeatureDetector(SURF)特征检测类对象
SurfFeatureDetector detector( minHessian);
std::vector keypoints; // vector模板类是能够存放任意类型的动态数组,能够增加和压缩数据
// 调用detect函数检测出SURF特征关键点,保存在vector容器中
detector.detect( srcImage,keypoints);
// 绘制特征关键点
Mat img_keypoints;
drawKeypoints( srcImage,keypoints,img_keypoints,Scalar::all(-1),DrawMatchesFlags::DEFAULT);
// 显示效果图
imshow("特征点检测效果图",img_keypoints);
waitKey(0);
return 0;
}
四、SURF特征匹配
SURF算法为每个检测到的特征定义了位置和尺度,尺度值可用于定义围绕特征点的窗口大小,
不论物体的尺度在窗口是什么样的,都将包含相同视觉信息,这些信息用于表示特征点
以使得他们与众不同。
在特征匹配中,特征描述子通常是用于N维向量,在光照不变以及少许透视变形的情况下很理想。
另外,优质的描述子可以通过简单的距离测量进行比较,比如欧式距离,在特征匹配算法中,用处很大。
在OpenCV中,使用SURF进行特征点描述主要是drawMatches方法和BruteForceMatcher类运用。
drawMatches用于绘制出相匹配的两个图像的关键点:
void drawMatches(const Mat& img1, constvector
constvector
Mat& outImage, const Scalar&matchColor=Scalar::all(-1), const Scalar&singlePointColor=Scalar::all(-1),
const vector
C++: void drawMatches(const Mat& img1,
constvector& keypoints1,
const Mat& img2,
constvector& keypoints2,
const vector>&matches1to2,
Mat& outImg,
const Scalar&matchColor=Scalar::all(-1),
const Scalar&singlePointColor=Scalar::all(-1),
constvector>& matchesMask=vector>(),
intflags=DrawMatchesFlags::DEFAULT )
除了第五个参数matches1to2和第九个参数matchesMask有细微差别以外,两个版本基本上相同。
第一个参数,const Mat&类型的img1,第一幅源图像。
第二个参数,const vector
得到的特征点,它是一个输出参数。
第三个参数,const Mat&类型的img2,第二幅源图像。
第四个参数,const vector
得到的特征点,它是一个输出参数。
第五个参数,matches1to2,第一幅图像到第二幅图像的匹配点,即表示每一个图1中
的特征点都在图2中有一一对应的点
第六个参数,Mat&类型的outImg,输出图像,其内容取决于第五个参数标识符flags
第七个参数,const Scalar&类型的matchColor,匹配的输出颜色,即线和关键点的颜色。
它有默认值Scalar::all(-1),表示颜色是随机生成的。
第八个参数,const Scalar&类型的singlePointColor,单一特征点的颜色,它也有表示
随机生成颜色的默认值Scalar::all(-1)。
第九个参数,matchesMask,确定哪些匹配是会绘制出来的掩膜,如果掩膜为空
表示所有匹配都进行绘制。
第十个参数,int类型的flags,特征绘制的标识符,有默认值DrawMatchesFlags::DEFAULT。
可以在如下这个DrawMatchesFlags结构体中选取值。
struct DrawMatchesFlags
{
enum
{
DEFAULT = 0, // Output image matrix will be created (Mat::create),
// i.e. existing memory ofoutput image may be reused.
// Two source images,matches, and single keypoints
// will be drawn.
// For each keypoint, only the center pointwill be
// drawn (without a circlearound the keypoint with the
// keypoint size andorientation).
DRAW_OVER_OUTIMG = 1, // Output image matrix will not be
// created (usingMat::create). Matches will be drawn
// on existing contentof output image.
NOT_DRAW_SINGLE_POINTS = 2, // Single keypoints will not be drawn.
DRAW_RICH_KEYPOINTS = 4 // For each keypoint, the circle around
// keypoint withkeypoint size and orientation will
// be drawn.
};
};
在示例程序中,利用了SURF特征的特征描述办法,其操作封装在类SurfFeatureDetector中,
利用类内的detect函数可以检测出SURF特征的关键点,保存在vector容器中。
第二步4利用SurfDescriptorExtractor类进行特征向量的相关计算。将之前的vector变量变成
向量矩阵形式保存在Mat中。最后强行匹配两幅图像的特征向量,利用了类
BruteForceMatcher中的函数match。
程序核心思想:
(1)使用DescriptorExtractor接口来寻找关键点对应的特征向量。
(2)使用SurfDescriptorExtractor以及它的函数compute来完成特定的计算。
(3)使用BruteForceMatcher来匹配特征向量。
(4)使用函数drawMatches来绘制检测到匹配点。
//-------------------------------- SURF特征匹配 -------------------------------------------//
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main(int argc,char** argv)
{
// 1.载入图片
Mat srcImage1 = imread("D:\\photo\\bird.jpg");
Mat srcImage2 = imread("D:\\photo\\bird2.jpg");
if( !srcImage1.data || !srcImage2.data)
{
cout << "图片读取错误!" << endl;
}
// 2.使用SURF算子检测关键点
int minHessian = 700;
SurfFeatureDetector detector( minHessian); // 定义一个特征检测类对象detector
vector keyPoints1,keyPoints2; // vector模板类,存放任意类型的动态数组
// 3.调用detect函数检测出SURF特征关键点,保存在vector容器中
detector.detect( srcImage1, keyPoints1);
detector.detect( srcImage2, keyPoints2);
// 4.计算描述符(特征向量)
SurfDescriptorExtractor extractor;
Mat descriptors1, descriptors2;
extractor.compute( srcImage1, keyPoints1,descriptors1);
extractor.compute( srcImage2, keyPoints2,descriptors2);
// 5. 使用BruteForce进行匹配
// 实例化一个匹配器
BruteForceMatcher> matcher;
vector matches;
// 匹配两幅图中的描述子(descriptors)
matcher.match( descriptors1,descriptors2,matches);
// 6. 绘制从两个图像中匹配出关键点
Mat imgMatches;
drawMatches( srcImage1,keyPoints1,srcImage2,keyPoints2,matches,imgMatches);
// 7. 显示效果图
namedWindow("匹配图",0);
imshow("匹配图",imgMatches);
waitKey(0);
return 0;
}