图像的特征匹配在实际应用中有很多用途,特征匹配,顾名思义,就是要先提取特征点,然后 计算 特征向量,第三步就是匹配了,计算哪两个向量最近。sift的原理有些麻烦,opencv都做好了接口,拿来主义,直接跑一下测试
1 sift 特征匹配
void FeatureDialog::on_pbGoodSIFTMatch_clicked()
{
if(fileName.isEmpty() || fileName2.isEmpty())
QMessageBox::warning(this,QString("Warning"),QString("Select the file to match,please"),QMessageBox::Ok);
cv::Mat src1,src2;
src1 = cv::imread(fileName.toStdString(),cv::IMREAD_COLOR);
src2 = cv::imread(fileName2.toStdString(),cv::IMREAD_COLOR);
//convert to gray
cv::cvtColor(src1,src1,cv::COLOR_BGR2GRAY);
cv::cvtColor(src2,src2,cv::COLOR_BGR2GRAY);
cv::Ptr<cv::xfeatures2d::SiftFeatureDetector> siftDetector = cv::xfeatures2d::SiftFeatureDetector::create();
std::vector<cv::KeyPoint> keypoints1,keypoints2;
//
siftDetector->detect(src1,keypoints1);
siftDetector->detect(src2,keypoints2);
cv::Ptr<cv::xfeatures2d::SiftDescriptorExtractor> siftDescriptor = cv::xfeatures2d::SiftDescriptorExtractor::create();
cv::Mat imgdescriptor1,imgdescriptor2;
siftDescriptor->compute(src1,keypoints1,imgdescriptor1);
siftDescriptor->compute(src2,keypoints2,imgdescriptor2);
cv::FlannBasedMatcher matcher;
std::vector<std::vector<cv::DMatch> > matchPoints;
std::vector<cv::DMatch> goodMatchPoints;
#if 0 //两种方法都可以
std::vector<cv::Mat> train_desc(1,imgdescriptor1); //初始化长度为1,内容是imgdescriptor1
matcher.add(train_desc);
matcher.train();
matcher.knnMatch(imgdescriptor2,matchPoints,2);
#else
matcher.knnMatch(imgdescriptor2,imgdescriptor1,matchPoints,2);//从imgdescriptor1 中找与imgdescriptor2 i个点匹配的2个匹配放到matchpoints中
#endif
std::cout << "total match points:" << matchPoints.size() << std::endl;
//为了排除因为图像遮挡和环境混乱而产生误匹配的关键点,sift的作者提出了比较最邻近距离的sift匹配:取一幅图像中的一个sift关键点,并找出其与另一幅图像中欧式距离最近
//的前两个关键点,在这两个关键点中,如果最近的距离除以次近距离得到的ratio少于阈值T,则接受这一对匹配点。降低T匹配点数会减少,更准确,反之亦然。
//一般取T在0.4~0.6 ,小于0.4,匹配的点比较少,大于0.6误匹配的点多一些
for(int i=0;i < matchPoints.size();i++){
if(matchPoints[i][0].distance < 0.6*matchPoints[i][1].distance){
goodMatchPoints.push_back(matchPoints[i][0]);
}
}
cv::Mat first_match;
cv::drawMatches(src2,keypoints2,src1,keypoints1,goodMatchPoints,first_match);//goodMatchPoints是src2到src1,因为生成的matchPoints是src2 到src1
cv::namedWindow("goodsift match",cv::WINDOW_NORMAL);
cv::imshow("goodsift match",first_match);
cv::imwrite("goodsift_match.jpg",first_match);
cv::waitKey();
return;
}
2.surf特征
surf特征是sift特征的改进版。具有尺度不变性。
代码上和上述的sift一样,只需要把sift换成surf即可,但是速度确是sift的10倍,精度比sift稍差。
void FeatureDialog::on_pbGoodSURFMatch_clicked()
{
if(fileName.isEmpty() || fileName2.isEmpty())
QMessageBox::warning(this,QString("Warning"),QString("Select the file to match,please"),QMessageBox::Ok);
cv::Mat src1,src2;
src1 = cv::imread(fileName.toStdString(),cv::IMREAD_COLOR);
src2 = cv::imread(fileName2.toStdString(),cv::IMREAD_COLOR);
//convert to gray
cv::cvtColor(src1,src1,cv::COLOR_BGR2GRAY);
cv::cvtColor(src2,src2,cv::COLOR_BGR2GRAY);
cv::Ptr<cv::xfeatures2d::SurfFeatureDetector> surfDetector = cv::xfeatures2d::SurfFeatureDetector::create();
std::vector<cv::KeyPoint> keypoints1,keypoints2;
//
surfDetector->detect(src1,keypoints1);
surfDetector->detect(src2,keypoints2);
cv::Ptr<cv::xfeatures2d::SurfDescriptorExtractor> surfDescriptor = cv::xfeatures2d::SurfDescriptorExtractor::create();
cv::Mat imgdescriptor1,imgdescriptor2;
surfDescriptor->compute(src1,keypoints1,imgdescriptor1);
surfDescriptor->compute(src2,keypoints2,imgdescriptor2);
cv::FlannBasedMatcher matcher;
std::vector<std::vector<cv::DMatch> > matchPoints;
std::vector<cv::DMatch> goodMatchPoints;
#if 0 //两种方法都可以
std::vector<cv::Mat> train_desc(1,imgdescriptor1); //初始化长度为1,内容是imgdescriptor1
matcher.add(train_desc);
matcher.train();
matcher.knnMatch(imgdescriptor2,matchPoints,2);
#else
matcher.knnMatch(imgdescriptor2,imgdescriptor1,matchPoints,2);//从imgdescriptor1 中找与imgdescriptor2 i个点匹配的2个匹配放到matchpoints中
#endif
std::cout << "total match points:" << matchPoints.size() << std::endl;
//为了排除因为图像遮挡和环境混乱而产生误匹配的关键点,sift的作者提出了比较最邻近距离的sift匹配:取一幅图像中的一个sift关键点,并找出其与另一幅图像中欧式距离最近
//的前两个关键点,在这两个关键点中,如果最近的距离除以次近距离得到的ratio少于阈值T,则接受这一对匹配点。降低T匹配点数会减少,更准确,反之亦然。
//一般取T在0.4~0.6 ,小于0.4,匹配的点比较少,大于0.6误匹配的点多一些
for(int i=0;i < matchPoints.size();i++){
if(matchPoints[i][0].distance < 0.6*matchPoints[i][1].distance){
goodMatchPoints.push_back(matchPoints[i][0]);
}
}
cv::Mat first_match;
cv::drawMatches(src2,keypoints2,src1,keypoints1,goodMatchPoints,first_match);//goodMatchPoints是src2到src1,因为生成的matchPoints是src2 到src1
cv::namedWindow("goodsurf match",cv::WINDOW_NORMAL);
cv::imshow("goodsurf match",first_match);
cv::imwrite("goodsurf_match.jpg",first_match);
cv::waitKey();
return;
}
我们看一下效果还是不错的
3.orb
orb是Oriented Brief的简称,特点是速度快,比sift快100倍,比surf快10倍,orb是brief的改进算法,那么brief的缺点如下:
- 不具备旋转不变性
- 对噪声敏感
- 不具备尺度不变性
ORB的改进在于解决了上述的1和2,也就是说对噪声不敏感,具有旋转不变性,但是值得注意的是上面的第三个,ORB不具有尺度不变性。
opencv的测试代码
void FeatureDialog::on_pbORB_clicked()
{
if(fileName.isEmpty() || fileName2.isEmpty())
QMessageBox::warning(this,QString("Warning"),QString("Select the file to match,please"),QMessageBox::Ok);
cv::Mat src1,src2;
src1 = cv::imread(fileName.toStdString(),cv::IMREAD_COLOR);
src2 = cv::imread(fileName2.toStdString(),cv::IMREAD_COLOR);
//convert to gray
cv::cvtColor(src1,src1,cv::COLOR_BGR2GRAY);
cv::cvtColor(src2,src2,cv::COLOR_BGR2GRAY);
cv::Ptr<cv::ORB> orb = cv::ORB::create();
//cv::ORB orb;
std::vector<cv::KeyPoint> keypoints1,keypoints2;
orb->detect(src1,keypoints1);
orb->detect(src2,keypoints2);
cv::Mat imgdescriptor1,imgdescriptor2;
orb->compute(src1,keypoints1,imgdescriptor1);
orb->compute(src2,keypoints2,imgdescriptor2);
cv::flann::Index flannIndex(imgdescriptor1,cv::flann::LshIndexParams(12,20,2),cvflann::FLANN_DIST_HAMMING);
std::vector<cv::DMatch> goodMatchPoints;
cv::Mat matchIndex(imgdescriptor2.rows,2 , CV_32SC1);
cv::Mat matchDistance(imgdescriptor2.rows, 2, CV_32FC1);
flannIndex.knnSearch(imgdescriptor1,matchIndex,matchDistance,2,cv::flann::SearchParams());
for(int i=0;i<matchDistance.rows;i++){
if(matchDistance.at<float>(i,0) < 0.6 * matchDistance.at<float>(i,1)){
cv::DMatch dmatches(i,matchIndex.at<int>(i,0),matchDistance.at<float>(i,0));
goodMatchPoints.push_back(dmatches);
}
}
cv::Mat first_match;
cv::drawMatches(src2,keypoints2,src1,keypoints1,goodMatchPoints,first_match);//goodMatchPoints是src2到src1,因为生成的matchPoints是src2 到src1
cv::namedWindow("orb match",cv::WINDOW_NORMAL);
cv::imshow("orb match",first_match);
cv::imwrite("orb_match.jpg",first_match);
cv::waitKey();
return;
}