OpenCV学习之仿射变换 & SURF特征点描述合辑
放射变换相关函数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 <iostream> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> 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<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.56),static_cast<float>(srcImage.rows * 0.23)); dstTriangle[2] = Point2f(static_cast<float>(srcImage.cols * 0.15),static_cast<float>(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<KeyPoint>& keypoints, Mat& outImage,
constScalar& color=Scalar::all(-1), int flags=DrawMatchesFlags::DEFAULT)
第一个参数,const Mat&类型的src,输入图像。
第二个参数,const vector<KeyPoint>&类型的keypoints,根据源图像得到的特征点,它是一个
输出参数。
第三个参数,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 <iostream> #include <opencv2/core/core.hpp> #include <opencv2/features2d/features2d.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/nonfree/nonfree.hpp> 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<KeyPoint> 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<KeyPoint>& keypoints1, const Mat& img2,
constvector<KeyPoint>& keypoints2, constvector<DMatch>& matches1to2,
Mat& outImage, const Scalar&matchColor=Scalar::all(-1), const Scalar&singlePointColor=Scalar::all(-1),
const vector<char>&matchesMask=vector<char>(), intflags=DrawMatchesFlags::DEFAULT);
C++: void drawMatches(const Mat& img1, constvector<KeyPoint>& keypoints1, const Mat& img2, constvector<KeyPoint>& keypoints2, const vector<vector<DMatch>>&matches1to2, Mat& outImg, const Scalar&matchColor=Scalar::all(-1), const Scalar&singlePointColor=Scalar::all(-1), constvector<vector<char>>& matchesMask=vector<vector<char>>(), intflags=DrawMatchesFlags::DEFAULT )除了第五个参数matches1to2和第九个参数matchesMask有细微差别以外,两个版本基本上相同。
第一个参数,const Mat&类型的img1,第一幅源图像。
第二个参数,const vector<KeyPoint>&类型的keypoints1,根据第一幅图像
得到的特征点,它是一个输出参数。
第三个参数,const Mat&类型的img2,第二幅源图像。
第四个参数,const vector<KeyPoint>&类型的keypoints2,根据第二幅源图像
得到的特征点,它是一个输出参数。
第五个参数,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 <iostream> #include <opencv2/core/core.hpp> #include <opencv2/features2d/features2d.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/nonfree/nonfree.hpp> #include <opencv2/legacy/legacy.hpp> 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<KeyPoint> 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<L2<float>> matcher; vector<DMatch> 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; }
- 【OpenCV入门教程之十八】OpenCV仿射变换 & SURF特征点描述合辑
- 【OpenCV入门教程之十八】OpenCV仿射变换 & SURF特征点描述合辑
- OpenCV之十八 OpenCV仿射变换 & SURF特征点描述合辑
- OpenCV 仿射变换 & SURF特征点描述合辑
- 【OpenCV入门教程之十八】OpenCV仿射变换 & SURF特征点描述合辑
- 【OpenCV入门教程之十八】OpenCV仿射变换 & SURF特征点描述合辑
- 【OpenCV入门教程之十八】OpenCV仿射变换 & SURF特征点描述合辑
- OpenCV仿射变换 & SURF特征点描述合辑
- OpenCV仿射变换 & SURF特征点描述合辑
- 【OpenCV入门教程之十八】OpenCV仿射变换 & SURF特征点描述合辑
- 我的OpenCV学习笔记(20):提取元素的轮廓及形状描述子
- OpenCV学习六:findContours、drawContours、仿射变换、RotatedRect区域矫正及获取
- OpenCV学习26--仿射变换
- opencv document 仿射变换学习
- OpenCV学习笔记(二)——特征提取与描述
- OpenCV学习笔记(29)KAZE 算法原理与源码分析(三)特征检测与描述
- 学习【OpenCV入门教程之十四】OpenCV霍夫变换:霍夫线变换,霍夫圆变换合辑---笔记
- opencv学习——仿射变换
- 学习OpenCV范例(十六)——重映射和仿射变换
- OpenCV入门学习(二)Affine Transformations(仿射变换)