天天看點

OpenCV3.4.11實作SIFT特征點提取和比對

直接上代碼吧

#include <iostream>
#include "opencv2/opencv.hpp"
#include"opencv2/core/core.hpp"
#include <opencv2/features2d/features2d.hpp>

bool siftPointsDetect(std::string imgName1, std::string imgName2, int pointNum)
{
	cv::Mat img1, img2;
	img1 = cv::imread(imgName1);
	img2 = cv::imread(imgName2);
	if (img1.empty() || img2.empty()) {
		std::cout << "error: cannot reading image: " << std::endl;
		return false;
	}
	// sift特征提取
	std::vector<cv::KeyPoint> keyPoint1, keyPoint2;
	cv::Ptr<cv::Feature2D> feature = cv::SIFT::create(pointNum);  // 提取pointNum個特征點
	
	feature->detect(img1, keyPoint1);
	feature->detect(img2, keyPoint2);
	std::cout << "detect feature ok\n";

	cv::Mat descor1, descor2;
	feature->compute(img1, keyPoint1, descor1);
	feature->compute(img2, keyPoint2, descor2);
	std::cout << "generate  descor ok\n";
	
	//繪制特征點(關鍵點)
	cv::Mat feature_pic1, feature_pic2;
	cv::drawKeypoints(img1, keyPoint1, feature_pic1, cv::Scalar(0, 255, 0), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
	cv::drawKeypoints(img2, keyPoint1, feature_pic2, cv::Scalar(0, 255, 0), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
	//顯示結果
	cv::imwrite("feature1.jpg", feature_pic1);
	cv::imwrite("feature2.jpg", feature_pic2);

	cv::FlannBasedMatcher matcher;  //執行個體化FLANN比對器
	std::vector<cv::DMatch>matches;   //定義比對結果變量
	matcher.match(descor1, descor2, matches);  //實作描述符之間的比對
	std::cout << "original match numbers: " << matches.size() << std::endl;

	cv::Mat oriMatchRes;
	cv::drawMatches(img1, keyPoint1, img2, keyPoint2, matches, oriMatchRes,
		cv::Scalar(0, 255, 0), cv::Scalar::all(-1));
	cv::imwrite("oriMatchResult.jpg", oriMatchRes);

	double sum = 0;
	double max_dist = 0;
	double min_dist = 100;
	for (int i = 0; i<matches.size(); i++)
	{
		double dist = matches[i].distance;
		if (dist < min_dist)
			min_dist = dist;
		if (dist > max_dist)
			max_dist = dist;
	}
	std::cout << "max distance: " << max_dist << std::endl;
	std::cout << "min distance: " << min_dist << std::endl;

	//篩選出較好的比對點  
	std::vector<cv::DMatch> goodMatches;
	double dThreshold = 0.5;    //比對的門檻值,越大比對的點數越多
	for (int i = 0; i<matches.size(); i++) {
		if (matches[i].distance < dThreshold * max_dist) {
			goodMatches.push_back(matches[i]);
		}
	}

	//RANSAC 消除誤比對特征點 主要分為三個部分:
	//1)根據matches将特征點對齊,将坐标轉換為float類型
	//2)使用求基礎矩陣方法findFundamentalMat,得到RansacStatus
	//3)根據RansacStatus來将誤比對的點也即RansacStatus[i]=0的點删除

	//根據matches将特征點對齊,将坐标轉換為float類型
	std::vector<cv::KeyPoint> R_keypoint01, R_keypoint02;
	for (int i = 0; i<goodMatches.size(); i++) {
		R_keypoint01.push_back(keyPoint1[goodMatches[i].queryIdx]);
		R_keypoint02.push_back(keyPoint2[goodMatches[i].trainIdx]);
		// 這兩句話的了解:R_keypoint1是要存儲img01中能與img02比對的特征點,
		// matches中存儲了這些比對點對的img01和img02的索引值
	}

	//坐标轉換
	std::vector<cv::Point2f> p01, p02;
	for (int i = 0; i<goodMatches.size(); i++) {
		p01.push_back(R_keypoint01[i].pt);
		p02.push_back(R_keypoint02[i].pt);
	}

	//計算基礎矩陣并剔除誤比對點
	std::vector<uchar> RansacStatus;
	cv::Mat Fundamental = findHomography(p01, p02, RansacStatus, CV_RANSAC);
	cv::Mat dst;
	warpPerspective(img1, dst, Fundamental, cv::Size(img1.cols, img1.rows));
	cv::imwrite("epipolarImage.jpg", dst); // 核線影像

	// 剔除誤比對的點對
	std::vector<cv::KeyPoint> RR_keypoint01, RR_keypoint02;
	// 重新定義RR_keypoint 和RR_matches來存儲新的關鍵點和比對矩陣
	std::vector<cv::DMatch> RR_matches;           
	int index = 0;
	for (int i = 0; i<goodMatches.size(); i++) {
		if (RansacStatus[i] != 0) {
			RR_keypoint01.push_back(R_keypoint01[i]);
			RR_keypoint02.push_back(R_keypoint02[i]);
			goodMatches[i].queryIdx = index;
			goodMatches[i].trainIdx = index;
			RR_matches.push_back(goodMatches[i]);
			index++;
		}
	}
	std::cout << "refine match pairs: " << RR_matches.size() << std::endl;
	// 畫出消除誤比對後的圖
	cv::Mat img_RR_matches;
	cv::drawMatches(img1, RR_keypoint01, img2, RR_keypoint02, RR_matches, img_RR_matches, 
		cv::Scalar(0, 255, 0), cv::Scalar::all(-1));
	cv::imwrite("refineMatchResult.jpg", img_RR_matches);

	return true;
}
           

main 函數如下:

int main(int argc, char *argv[])
{
	if (argc < 3) {
		std::cout << "error: little parameters\n";
		return -1;
	}

	bool bOk = false;
	std::string path = argv[1];
	std::string imgName1 = path + argv[2];
	std::string imgName2 = path + argv[3];

	int detectPointNum = atoi(argv[4]);
	if (detectPointNum < 5)
		detectPointNum = 5000; // 預設提取5000個特征點
	std::cout << "per image detect feature point number: " << detectPointNum << std::endl;
	bOk = siftPointsDetect(imgName1, imgName2, detectPointNum);
	return 0;
}
           

希望能幫到你啊