zoukankan      html  css  js  c++  java
  • OpenCV 学习笔记(0)两幅图像标定配准

    参考教程  

    依赖opencv扩展库,使用sifi匹配

    保存配准信息  

     "./config/calibratedPara.yaml" 
    #include <iostream>
    #include<opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    #include <opencv2/opencv.hpp> 
    #include<opencv2/xfeatures2d.hpp>
    #include<opencv2/core/core.hpp>
    
    
    #define PATH_XMAL           "./config/calibratedPara.yaml" 
    #define IMG_WIDTH 			2592//2592
    #define IMG_HEIGHT  		1944//2048
    
    using namespace cv;
    using namespace std;
    using namespace cv::xfeatures2d;//只有加上这句命名空间,SiftFeatureDetector and SiftFeatureExtractor才可以使用
    
    
    
    								/******************************************************
    								*name		:Rect CalcCorners(const Mat& H, const Mat& src)
    								*function	:通过H计算图片角点位置,返回左上和宽高
    								*time		:2019-4-28
    								********************************************************/
    Rect CalcCorners(const Mat& H, const Mat& src)
    {
    	double v1[3];
    	Mat _V1 = Mat(3, 1, CV_64FC1, v1);
    	//左上角(0,0,1)
    	Mat _V2 = (Mat_<double>(3, 1) << 0, 0, 1);
    	_V1 = H * _V2;
    	Point _left_top;
    	_left_top.x = v1[0] / v1[2];
    	_left_top.y = v1[1] / v1[2];
    	//左下角(0,src.rows,1)
    	_V2 = (Mat_<double>(3, 1) << 0, src.rows, 1);
    	_V1 = H * _V2;
    	Point _left_bottom;
    	_left_bottom.x = v1[0] / v1[2];
    	_left_bottom.y = v1[1] / v1[2];
    	//右上角(src.cols,0,1)
    	_V2 = (Mat_<double>(3, 1) << src.cols, 0, 1);
    	_V1 = H * _V2;
    	Point _right_top;
    	_right_top.x = v1[0] / v1[2];
    	_right_top.y = v1[1] / v1[2];
    	//右下角(src.cols,src.rows,1)
    	_V2 = (Mat_<double>(3, 1) << src.cols, src.rows, 1);
    	_V1 = H * _V2;
    	Point _right_bottom;
    	_right_bottom.x = v1[0] / v1[2];
    	_right_bottom.y = v1[1] / v1[2];
    	int _x1 = (int)max(_left_bottom.x, _left_top.x);
    	int _y1 = (int)max(_left_top.y, _right_top.y);
    	int _x2 = (int)min(_right_top.x, _right_bottom.x);
    	int _y2 = (int)min(_left_bottom.y, _right_bottom.y);
    
    	cout << "point is " << _x1 << " " << _y1 << " " << _x2 << " " << _y2 << endl;
    	if (_x2 > IMG_WIDTH) _x2 = IMG_WIDTH - 1;
    	if (_y2 > IMG_HEIGHT) _y2 = IMG_HEIGHT - 1;
    	if (_x1 < 0) _x1 = 0;
    	if (_y1 < 0) _y1 = 0;
    
    	cout << "point is " << _x1 << " " << _y1 << " " << _x2 << " " << _y2 << endl;
    
    	return Rect(Point(_x1, _y1), Point(_x2, _y2));	//表示左上点和右下点
    }
    
    
    Rect inscrRect;
    cv::Mat warpedPic;
    cv::Mat Homography;
    Mat compicCalibrate;
    
    int main()
    {
    	//Create SIFT class pointer
    	Ptr<Feature2D> f2d = xfeatures2d::SIFT::create();
    	//SiftFeatureDetector siftDetector;
    	//Loading images
    	Mat img_1 = imread("1.bmp");
    	Mat img_2 = imread("2.bmp");
    	if (!img_1.data || !img_2.data)
    	{
    		cout << "Reading picture error!" << endl;
    		return false;
    	}
    	//Detect the keypoints
    	double t0 = getTickCount();//当前
    	vector<KeyPoint> keypoints_1, keypoints_2;
    	f2d->detect(img_1, keypoints_1);
    	f2d->detect(img_2, keypoints_2);
    	cout << "The keypoints number of img1 is:" << keypoints_1.size() << endl;
    	cout << "The keypoints number of img2 is:" << keypoints_2.size() << endl;
    	//Calculate descriptors (feature vectors)
    	Mat descriptors_1, descriptors_2;
    	f2d->compute(img_1, keypoints_1, descriptors_1);
    	f2d->compute(img_2, keypoints_2, descriptors_2);
    	double freq = getTickFrequency();
    	double tt = ((double)getTickCount() - t0) / freq;
    	cout << "Extract SIFT Time:" << tt << "ms" << endl;
    	//画关键点
    	Mat img_keypoints_1, img_keypoints_2;
    	drawKeypoints(img_1, keypoints_1, img_keypoints_1, Scalar::all(-1), 0);
    	drawKeypoints(img_2, keypoints_2, img_keypoints_2, Scalar::all(-1), 0);
    	//imshow("img_keypoints_1",img_keypoints_1);
    	//imshow("img_keypoints_2",img_keypoints_2);
    
    	//Matching descriptor vector using BFMatcher
    	BFMatcher matcher;
    	vector<DMatch> matches;
    	matcher.match(descriptors_1, descriptors_2, matches);
    	cout << "The number of match:" << matches.size() << endl;
    	//绘制匹配出的关键点
    	Mat img_matches;
    	drawMatches(img_1, keypoints_1, img_2, keypoints_2, matches, img_matches);
    	//imshow("Match image",img_matches);
    	//计算匹配结果中距离最大和距离最小值
    	double min_dist = matches[0].distance, max_dist = matches[0].distance;
    	for (int m = 0; m < matches.size(); m++)
    	{
    		if (matches[m].distance<min_dist)
    		{
    			min_dist = matches[m].distance;
    		}
    		if (matches[m].distance>max_dist)
    		{
    			max_dist = matches[m].distance;
    		}
    	}
    	cout << "min dist=" << min_dist << endl;
    	cout << "max dist=" << max_dist << endl;
    	//筛选出较好的匹配点
    	vector<DMatch> goodMatches;
    	for (int m = 0; m < matches.size(); m++)
    	{
    		if (matches[m].distance < 0.6*max_dist)
    		{
    			goodMatches.push_back(matches[m]);
    		}
    	}
    	cout << "The number of good matches:" << goodMatches.size() << endl;
    	//画出匹配结果
    	Mat img_out;
    	//红色连接的是匹配的特征点数,绿色连接的是未匹配的特征点数
    	//matchColor – Color of matches (lines and connected keypoints). If matchColor==Scalar::all(-1) , the color is generated randomly.
    	//singlePointColor – Color of single keypoints(circles), which means that keypoints do not have the matches.If singlePointColor == Scalar::all(-1), the color is generated randomly.
    	//CV_RGB(0, 255, 0)存储顺序为R-G-B,表示绿色
    	drawMatches(img_1, keypoints_1, img_2, keypoints_2, goodMatches, img_out, Scalar::all(-1), CV_RGB(0, 0, 255), Mat(), 2);
    	namedWindow("good Matches", 0);
    	imshow("good Matches", img_out);
    	//RANSAC匹配过程
    	vector<DMatch> m_Matches;
    	m_Matches = goodMatches;
    	int ptCount = goodMatches.size();
    	if (ptCount < 100)
    	{
    		cout << "Don't find enough match points" << endl;
    		return 0;
    	}
    
    	//坐标转换为float类型
    	vector <KeyPoint> RAN_KP1, RAN_KP2;
    	//size_t是标准C库中定义的,应为unsigned int,在64位系统中为long unsigned int,在C++中为了适应不同的平台,增加可移植性。
    	for (size_t i = 0; i < m_Matches.size(); i++)
    	{
    		RAN_KP1.push_back(keypoints_1[goodMatches[i].queryIdx]);
    		RAN_KP2.push_back(keypoints_2[goodMatches[i].trainIdx]);
    		//RAN_KP1是要存储img01中能与img02匹配的点
    		//goodMatches存储了这些匹配点对的img01和img02的索引值
    	}
    	//坐标变换
    	vector <Point2f> p01, p02;
    	for (size_t i = 0; i < m_Matches.size(); i++)
    	{
    		p01.push_back(RAN_KP1[i].pt);
    		p02.push_back(RAN_KP2[i].pt);
    	}
    	/*vector <Point2f> img1_corners(4);
    	img1_corners[0] = Point(0,0);
    	img1_corners[1] = Point(img_1.cols,0);
    	img1_corners[2] = Point(img_1.cols, img_1.rows);
    	img1_corners[3] = Point(0, img_1.rows);
    	vector <Point2f> img2_corners(4);*/
    	////求转换矩阵
    	//Mat m_homography;
    	//vector<uchar> m;
    	//m_homography = findHomography(p01, p02, RANSAC);//寻找匹配图像
    	//求基础矩阵 Fundamental,3*3的基础矩阵
    	vector<uchar> RansacStatus;
    	Mat Fundamental = findFundamentalMat(p01, p02, RansacStatus, FM_RANSAC);
    	//重新定义关键点RR_KP和RR_matches来存储新的关键点和基础矩阵,通过RansacStatus来删除误匹配点
    	vector <KeyPoint> RR_KP1, RR_KP2;
    	vector <DMatch> RR_matches;
    	int index = 0;
    	for (size_t i = 0; i < m_Matches.size(); i++)
    	{
    		if (RansacStatus[i] != 0)
    		{
    			RR_KP1.push_back(RAN_KP1[i]);
    			RR_KP2.push_back(RAN_KP2[i]);
    			m_Matches[i].queryIdx = index;
    			m_Matches[i].trainIdx = index;
    			RR_matches.push_back(m_Matches[i]);
    			index++;
    		}
    	}
    	cout << "RANSAC后匹配点数" << RR_matches.size() << endl;
    	Mat img_RR_matches;
    	drawMatches(img_1, RR_KP1, img_2, RR_KP2, RR_matches, img_RR_matches);
    	namedWindow("After RANSAC", 0);
    	imshow("After RANSAC", img_RR_matches);
    	//等待任意按键按下
    	waitKey(1);
    
    
    
    
    	vector<cv::Point2f> Pic1Point, Pic2Point;
    	for (int i = 0; i < RR_matches.size(); i++)
    	{
    		Pic1Point.push_back(RR_KP1[RR_matches[i].queryIdx].pt);
    		Pic2Point.push_back(RR_KP2[RR_matches[i].trainIdx].pt);
    	}
    
    	Homography = cv::findHomography(Pic1Point, Pic2Point, CV_RANSAC); //计算将p2投影到p1上的单映性矩阵
    
    	FileStorage fs(PATH_XMAL, FileStorage::WRITE); //单应矩阵保存
    	fs << "Homography" << Homography;
    
    	warpPerspective(img_1, warpedPic, Homography, cv::Size(img_2.cols, img_2.rows));//第一路图像根据参数Homography变换映射到warpedPic图
    	inscrRect = CalcCorners(Homography, img_1);// 第一路图像根据参数Homography计算本土映射区域的起始点和宽高
    	fs << "inscrRect" << inscrRect;//保存在xml
    	fs.release();
    
    	Rect cutRoi(inscrRect.x, inscrRect.y, inscrRect.width, inscrRect.height);// 定义一个抠图区域
    	Mat Pic1Roi = warpedPic(cutRoi).clone();//第一张变换图扣出对应区域 
    
    	compicCalibrate.create(inscrRect.height, inscrRect.width * 2, CV_8UC3);
    
    //	Mat Pic1Roi = warpedPic(inscrRect);
    	Mat Pic2Roi = img_2(inscrRect);
    	Pic1Roi.copyTo(compicCalibrate(Rect(0, 0, Pic1Roi.cols, Pic1Roi.rows)));
    	Pic2Roi.copyTo(compicCalibrate(Rect(Pic1Roi.cols, 0, Pic2Roi.cols, Pic2Roi.rows)));
    	namedWindow("martch", 0);
    	imshow("martch", compicCalibrate);
    	waitKey(0);
    }
    

      

  • 相关阅读:
    mybatis中的配置文件的约束
    win10下PHP开发环境搭建
    装饰器的理解
    在iis上添加woff字体文件读取
    转发:使用sql命令查询视图中所有引用的基础表
    转:C4项目中验证用户登录一个特性就搞定
    转载:NSobject官方介绍
    thinkphp生命周期
    array_intersect_assoc — 带索引检查计算数组的交集
    array_flip — 交换数组中的键和值
  • 原文地址:https://www.cnblogs.com/kekeoutlook/p/11115134.html
Copyright © 2011-2022 走看看