Matching GetAlignedMatching(size_t size) {
    Matching match;
    for (size_t i = 0; i < size; i++) {
        match.push_back(DMatch(i, i, 0));
    }
    return match;
}
Example #2
0
// Draws matches of keypints from two images on output image.
void ICLASS_API DrawMatches(
	TMat* img1, TCVectorKeyPoint* keypoints1,
	TMat* img2, TCVectorKeyPoint* keypoints2,
	TCVectorDMatch* matches1to2, TMat** outImg)
{
	vector<KeyPoint> k1, k2;	
	for (size_t i = 0; i < keypoints1->size(); i++)
	{
		TKeyPoint K = *keypoints1->at(i);
		k1.push_back(KeyPoint(K.x, K.y, K.size, K.angle, K.response, K.octave, K.class_id));
	}
	for (size_t i = 0; i < keypoints2->size(); i++)
	{
		TKeyPoint K = *keypoints2->at(i);
		k2.push_back(KeyPoint(K.x, K.y, K.size, K.angle, K.response, K.octave, K.class_id));
	}
	
	vector<DMatch> m1to2;
	for (size_t i = 0; i < matches1to2->size(); i++)
	{
		TDMatch K = *matches1to2->at(i);
		m1to2.push_back(DMatch(K.queryIdx, K.trainIdx, K.imgIdx, K.distance));
	}
	
	Mat oImg;
	drawMatches(*img1->Mat(), k1, *img2->Mat(), k2, m1to2, oImg);

	*outImg = new TMat(oImg);
};
Example #3
0
    vector<DMatch> points(
        vector<pair<int, int> > edge_matches,
        Mat &desc1, Mat &desc2,
        vector<vector<int> > &edges1, vector<vector<int> > &edges2,
        double th, double sigma = 0.5
    ) {
        vector<DMatch> matches;
        set<pair<int, int> > S;
        for (size_t i = 0; i < edge_matches.size(); i++) {
            int base_edge_idx = edge_matches[i].first;
            int ref_edge_idx  = edge_matches[i].second;
            vector<Mat> des_1(3), des_2(3);
            for (int k = 0; k < 3; k++) {
                des_1[k] = desc1.row(edges1[base_edge_idx][k]);
                des_2[k] = desc2.row(edges2[ref_edge_idx][k]);
            }

            vector<int> best_match(3);
            vector<double> best_sim(3, -1E30);
            for (int j = 0; j < 3; j++) {
                for (int k = 0; k < 3; k++) {
                    double _sim = exp(-sum(abs(des_1[j] - des_2[k]))[0] / sigma);

                    if (_sim > best_sim[j]) {
                        best_sim[j] = _sim;
                        best_match[j] = k;
                    }
                }
            }

            for (int j = 0; j < 3; j++) {
                int k = best_match[j];

                int qI = edges1[base_edge_idx][j];
                int tI = edges2[ref_edge_idx][k];

                if (!S.count(make_pair(qI, tI)) && best_sim[j] > th) {
                    double _dist = -sigma * log(best_sim[j]);
                    matches.push_back(DMatch(qI, tI, _dist));
                    S.insert(make_pair(qI, tI));
                }
            }
        }
        return matches;
    }
void DisplayCorrespondence::onNewImage() {
	int wrongmatch_counter=0;
	int goodmatch_counter=0;

	Mat image = in_img.read();
	std::string path = in_path.read();
	std::vector<int> image_params = in_image_params.read();
	std::vector<std::vector<int> > MatchedSourceForTile =in_MatchedSourceForTile.read();
	std::vector<int> PositionOfPatchesInImages =in_PositionOfPatchesInImages.read();
	std::vector<std::vector<int> > MatchedPatchInMatcher =in_MatchedPatchInMatcher.read();
	std::vector<std::vector<double> > DistanceMap = in_DistanceMap.read();
	std::vector<std::string> all_file_paths = in_all_file_paths.read();
	int files_number = all_file_paths.size();
	std::vector<double> QueryMatchMap = in_match_map.read();
	double match_quality = in_match_quality.read();

	patches_cols = image_params[0];
	patches_rows = image_params[1];
	int patchsize = image_params[2];

	int bd_cols = image_params[3];
	int bd_rows = image_params[4];
	int bd_patch_size = image_params[5];

	int queue_size = image_params[6];
	int BestMatchingImage = image_params[7];

	int image_cols = image_params[8];
	int image_rows = image_params[9];
	
	double threshold = (double)image_params[10]/10000;

	std::vector<std::vector<int> > BDMatchMap;
	BDMatchMap.resize(bd_cols * bd_rows);
	//for (int q = 0; q < bd_cols * bd_rows; q++) {
	//	BDMatchMap[q] = -1;
	//}

	int query_col;
	int query_row;
	int fn;

	std::cout << "qs:" << queue_size << "  thre:"<<threshold<<" p:"<<image_params[10]<<std::endl;
	/*=======================================================================*/
	/*************************************************************************/
	/*		create corresspondence map for DB image							 */
	/*************************************************************************/

	if (mode == 3) {
		for (int k = 0; k < patches_rows * patches_cols; k++) {
			int flag = 0;
			int zzz;
			//check if there is match of current patch to the best image
			for (zzz = 0; zzz < queue_size; zzz++) {
				if (DistanceMap[k][zzz]>threshold || DistanceMap[k][zzz]<0){
					//std::cout<<"k:"<<k<<" zzz:"<<zzz<<" dmap"<<DistanceMap[k][zzz]<<"  th:"<<threshold<<std::endl;
					//break;
				}
				if (MatchedSourceForTile[k][zzz] == BestMatchingImage) {
					flag = 1;
					break;
				}
			}
			if (flag) {
				//where in the image is the patch (all patches are numbered linearly in 1D. Like in progressive scan in TV from left to right and to next line)
				fn = PositionOfPatchesInImages[MatchedPatchInMatcher[k][zzz]];
				BDMatchMap[fn].push_back(k);
			}
		}
	}
	
	if (mode==4){
	//!!!!!!!!!!!!!!!!!!!!!!!
	Mat matchedim = cv::imread(path, -1);
	//!!!!!!!!!!!!!!!!!!
		double nx, ny, mx, my;
		
		double angle_rad=M_PI*angle/180;
		
		//scalefactor=1.0;
		int basex=bd_patch_size*bd_cols;
		int basey=bd_patch_size*bd_rows;
		//int imgx=patchsize*patches_cols;
		//int imgy=patchsize*patches_rows;
		int imgx=image_cols;
		int imgy=image_rows;
				
		//circle(matchedim, Point(basex/2.0,basey/2.0), 20, Scalar(0,255,0),-1);
		//circle(image, Point(imgx/2.0,imgy/2.0), 20, Scalar(0,255,0),-1);
		//float px=-100, py=-100;
		
		//float px2=px*scalefactor;
		//float py2=px*scalefactor;
		
		//float qx=px2*cos(angle_rad)-py2*sin(angle_rad);
		//float qy=px2*sin(angle_rad)+py2*cos(angle_rad);
		
		
		
		//std::cout<<qx<<" "<<qy<<std::endl;
		
		//circle(matchedim, Point(basex/2.0+px,basey/2.0+py), 10, Scalar(255,0,0),-1);
		//circle(image, Point(basex/2.0+qx,basey/2.0+qy), 10, Scalar(255,0,0),-1);
				
		for (int k = 0; k < patches_rows * patches_cols; k++) {
					
					
			int flag = 0;
			int zzz;
			//check if there is match of current patch to the best image
			for (zzz = 0; zzz < queue_size; zzz++) {
				if (DistanceMap[k][zzz]>threshold || DistanceMap[k][zzz]<0.0){
					//std::cout<<"k:"<<k<<" zzz:"<<zzz<<" dmap"<<DistanceMap[k][zzz]<<"  th:"<<threshold<<std::endl;
					break;
				}
				if (MatchedSourceForTile[k][zzz] == BestMatchingImage) {
					flag = 1;
					break;
				}
			}
			if (flag) {
			
				fn = PositionOfPatchesInImages[MatchedPatchInMatcher[k][zzz]];
			
				
		
				//float p1x=base_keypoints[matches[i].queryIdx].pt.x-basex/2;
				//float p1y=base_keypoints[matches[i].queryIdx].pt.y-basey/2;
				//float p2x=keypoints[matches[i].trainIdx].pt.x-imgx/2;
				//float p2y=keypoints[matches[i].trainIdx].pt.y-imgy/2;
				
				//cout<<"loop i2: "<<i<<std::endl;
				//float bigx=p1x*(cos(angle_rad)-sin(angle_rad));
				//float bigy=p1y*(sin(angle_rad)+cos(angle_rad));
				
				//float bigx=p1x*cos(angle_rad)-p1y*sin(angle_rad);
				//float bigy=p1x*sin(angle_rad)+p1y*cos(angle_rad);
				
				//cout<<"loop i3: "<<i<<std::endl;
				
				//if (sqrt((bigx-p2x)*(bigx-p2x)+(bigy-p2y)*(bigy-p2y))<=threshold){
					
			
				nx = bd_patch_size * (fn % bd_cols) + bd_patch_size * 0.5;
				ny = bd_patch_size * (fn / bd_cols) + bd_patch_size * 0.5;
				
				//nx*=scalefactor;
				//ny*=scalefactor;
				
				//which tile is matched
				query_col = k % patches_cols;
				query_row = k / patches_cols;

				//location of the center
				mx = patchsize * (query_col) + 0.5 * patchsize;
				my = patchsize * (query_row) + 0.5 * patchsize;
				
				
				float p1x=nx-basex/2.0;
				float p1y=ny-basey/2.0;
				float p2x=mx-imgx/2.0;
				float p2y=my-imgy/2.0;
				
				p1x*=scalefactor;
				p1y*=scalefactor;
				
				//circle(image, Point(mx,my), 10, Scalar(0,0,255), -1);
				//circle(matchedim, Point(nx,ny), 10, Scalar(0,0,255),-1);
				
				
				
				
				float bigx=p1x*cos(angle_rad)-p1y*sin(angle_rad);
				float bigy=p1x*sin(angle_rad)+p1y*cos(angle_rad);	//<<" basex"<<basex<<" basey"<<basey<<" imgx"<<imgx<<" imgy"<<imgy
				//std::cout<<"scale:"<<scalefactor<<std::endl;
				
				if (sqrt((bigx-p2x)*(bigx-p2x)+(bigy-p2y)*(bigy-p2y))<=(sqrt(2)*bd_patch_size*scalefactor/2+3)){
					//where in the image is the patch (all patches are numbered linearly in 1D. Like in progressive scan in TV from left to right and to next line)
					
					//std::cout<<"p1x:"<<p1x<<" p1y:"<<p1y<<" p2x:"<<p2x<<" p2y:"<<p2y<<" bx:"<<bigx<<" by:"<<bigy<<" scale:"<<scalefactor<<std::endl;
				//std::cout<<sqrt((bigx-p2x)*(bigx-p2x)+(bigy-p2y)*(bigy-p2y))<<std::endl;
					
					BDMatchMap[fn].push_back(k);
					goodmatch_counter++;
				}
				else{
					wrongmatch_counter++;
				}
			}
	
		}
		wrongmatches.push_back(wrongmatch_counter);
		goodmatches.push_back(goodmatch_counter);
		similarity.push_back(match_quality);
	}
	/*=======================================================================*/
	/*************************************************************************/
	/*				simple correspondence drawing							 */
	/*************************************************************************/
	
	if (mode == 0) {
		float nx, ny, mx, my;
		Mat outimg;
		//int query_col;
		//int query_row;


		std::vector<cv::KeyPoint> im1kp;
		std::vector<cv::KeyPoint> im2kp;
		std::vector<cv::DMatch> immatches;

		int tempc = 0;

		int cant = 0, cant2 = 0;
		//read query image
		Mat matchedim = cv::imread(path, -1);
		double color;

		for (int k = 0; k < patches_rows * patches_cols; k++) {
			int flag = 0;
			int zzz;
			//check if there is match of current patch to the best image
			for (zzz = 0; zzz < queue_size; zzz++) {
				if (MatchedSourceForTile[k][zzz] == BestMatchingImage) {
					flag = 1;
					//cant++;
					break;
				}
			}
			if (flag) {

				//where in the image is the patch (all patches are numbered linearly in 1D. Like in progressive scan in TV from left to right and to next line)
				fn = PositionOfPatchesInImages[MatchedPatchInMatcher[k][zzz]];
				//BDMatchMap[fn] = k;

				//error?
				if (fn < 0) {
					return;
				}

				//location of the center tile in the image#include <time.h>
				nx = bd_patch_size * (fn % bd_cols) + bd_patch_size * 0.5;
				ny = bd_patch_size * (fn / bd_cols) + bd_patch_size * 0.5;

				//which tile is matched
				query_col = k % patches_cols;
				query_row = k / patches_cols;

				//location of the center
				mx = patchsize * (query_col) + 0.5 * patchsize;
				my = patchsize * (query_row) + 0.5 * patchsize;

				//Is it not a bad match?
				if (DistanceMap[k][0] < 0 || DistanceMap[k][0] > 0.3) {
					cant2++;
					continue;
				}

				//choose colour
				//color = (1 - 10 * DistanceMap[k][0] * DistanceMap[k][0]) * 255.0;
				int cb = 0, cg = 0, cr = 255;
				int qLineW = 2, bdLineW = 2;
				//cb=rand() % 255;
				//cg=rand() % 255;
				//cr=rand() % 255;

				//draw patches in query img
				rectangle(image, Point(mx - 0.5 * patchsize, my - 0.5
						* patchsize), Point(mx + 0.5 * patchsize, my + 0.5
						* patchsize), Scalar(cb, cg, cr), qLineW);

				//draw patches in BD img
				rectangle(matchedim, Point(nx - 0.5 * bd_patch_size, ny - 0.5
						* bd_patch_size), Point(nx + 0.5 * bd_patch_size, ny
						+ 0.5 * bd_patch_size), Scalar(cb, cg, cr), bdLineW);

				//each tile center as a KP
				im1kp.push_back(KeyPoint(Point2f(nx, ny), 5.0));
				im2kp.push_back(KeyPoint(Point2f(mx, my), 5.0));

				std::ostringstream q;
				q.precision(2);
				q << QueryMatchMap[k];

				//putText(image, q.str(), cvPoint(mx-0.25*patchsize,my-0.125*patchsize), FONT_HERSHEY_SIMPLEX, 0.5, cvScalar(0,0,0), 1.5, CV_AA);

				//and now set correspondence
				immatches.push_back(DMatch(tempc, tempc, 0.0));
				tempc++;
			}
		}
		drawMatches(image, im2kp, matchedim, im1kp, immatches, outimg,
				Scalar::all(-1));
		out_image.write(outimg);
	}

	/*=======================================================================*/
	/*************************************************************************/
	/*				corresponding tiles marked with the same color			 */
	/*************************************************************************/
	if (mode == 3 || mode==4) {
		std::cout << "mode:" << mode << " s" << BDMatchMap.size() << std::endl;
		//!!!!!!!!!!!!!!!!!!!
		//!!!!!!!!!!!!!!!!
		//int counter=0;
		Mat matchedim = cv::imread(path, -1);
		float nx, ny, mx, my;
		Mat outimg;
		std::vector<cv::KeyPoint> im1kp;
		std::vector<cv::KeyPoint> im2kp;
		std::vector<cv::DMatch> immatches;

		int k;
		for (int m = 0; m < BDMatchMap.size(); m++) {
			int cb = 0, cg = 0, cr = 255;
			int qLineW = -1, bdLineW = -1;
			cb = rand() % 255;
			cg = rand() % 255;
			cr = rand() % 255;
			if (BDMatchMap[m].size() > 0) {
				for (int n = 0; n < BDMatchMap[m].size(); n++) {
				
				
					k = BDMatchMap[m][n];
					fn = n;

					query_col = k % patches_cols;
					query_row = k / patches_cols;

					//std::cout<<query_col<<" "<<query_row<<" "<<k<<std::endl;

					//location of the center
					mx = patchsize * (query_col) + 0.5 * patchsize;
					my = patchsize * (query_row) + 0.5 * patchsize;

					//Is it not a bad match?
					if (DistanceMap[k][0] < 0.0 || DistanceMap[k][0] > threshold) {
						continue;
					}
					//counter++;
					//draw patches in query img
					rectangle(image, Point(mx - 0.5 * patchsize, my - 0.5
							* patchsize), Point(mx + 0.5 * patchsize, my + 0.5
							* patchsize), Scalar(cb, cg, cr), qLineW);
							
					std::ostringstream q;
					q.precision(2);
					q << QueryMatchMap[k];
					putText(image, q.str(), cvPoint(mx-0.25*patchsize,my-0.125*patchsize), FONT_HERSHEY_SIMPLEX, 0.3, cvScalar(0,0,0), 1.5, CV_AA);
							
				}

				nx = bd_patch_size * (m % bd_cols) + bd_patch_size * 0.5;
				ny = bd_patch_size * (m / bd_cols) + bd_patch_size * 0.5;

				//draw patches in BD img
				rectangle(matchedim, Point(nx - 0.5 * bd_patch_size, ny - 0.5
						* bd_patch_size), Point(nx + 0.5 * bd_patch_size, ny
						+ 0.5 * bd_patch_size), Scalar(cb, cg, cr), bdLineW);
			}
		}
		drawMatches(image, im2kp, matchedim, im1kp, immatches, outimg,
				Scalar::all(-1));

		out_image.write(outimg);
		//goodmatches_q.push_back(counter);
	}

	/*=======================================================================*/
	/*************************************************************************/
	/*			experimental mode to reconstruct image from matched patches	 */
	/*						  possible only when patches have the same size  */
	/*************************************************************************/
	//
	if ((mode == 1 && patchsize == bd_patch_size) || mode == 2) {

		int nx, ny, mx, my;
		//int fn;
		//int query_col;
		//int query_row;

		int ax, bx, ay, by;
		int fx;
		int r, g, b;
		Mat outimage(image_rows, image_cols, CV_8UC3,
				Scalar(0.0, 0.0, 0.0, 1.0));

		int counter = 0;
		int zzz = 0, flag = 0;

		//scan through all the files
		for (int l = 0; l < files_number; l++) {

			Mat matchedim = cv::imread(all_file_paths[l], -1);
			unsigned char *input = (unsigned char*) (matchedim.data);

			for (int k = 0; k < patches_rows * patches_cols; k++) {
				//matches only to the best image
				if (mode == 1) {
					flag = 0;
					//find if an image has some corresponging match on the list
					for (zzz = 0; zzz < queue_size; zzz++) {
						if (MatchedSourceForTile[k][zzz] == BestMatchingImage) {
							flag = 1;
							break;
						}
					}
				}
				//matches to all img in the DB
				if (flag || mode == 2) {

					query_col = k % patches_cols;
					query_row = k / patches_cols;
					mx = patchsize * (query_col);
					my = patchsize * (query_row);

					fn
							= PositionOfPatchesInImages[MatchedPatchInMatcher[k][zzz]];

					nx = bd_patch_size * (fn % bd_cols);
					ny = bd_patch_size * (fn / bd_cols);

					//if the tile is matched to the current image l
					if (MatchedSourceForTile[k][zzz] == l) {

						//then copy image pixel-by-pixel
						for (int ax = 0; ax < patchsize; ax++) {
							for (int ay = 0; ay < patchsize; ay++) {
								//outimage[mx+ax][mx+ay]=matchedim[nx+ax][nx+ay];
								//if ((ax+nx)>512||(ay+ny))
								b = matchedim.data[matchedim.step[0]
										* (ay + ny) + matchedim.step[1] * (ax
										+ nx)];
								g = matchedim.data[matchedim.step[0]
										* (ay + ny) + matchedim.step[1] * (ax
										+ nx) + 1];
								r = matchedim.data[matchedim.step[0]
										* (ay + ny) + matchedim.step[1] * (ax
										+ nx) + 2];

								outimage.data[outimage.step[0] * (my + ay)
										+ outimage.step[1] * (mx + ax) + 0] = b;
								outimage.data[outimage.step[0] * (my + ay)
										+ outimage.step[1] * (mx + ax) + 1] = g;
								outimage.data[outimage.step[0] * (my + ay)
										+ outimage.step[1] * (mx + ax) + 2] = r;

							}
						}
					}
				}
			}
		}

		std::cout << "done" << std::endl;
		out_image.write(outimage);
	}
	/*************************************************************************/

	std::cout << "raise" << std::endl;
	matched->raise();
}
Example #5
0
void TestCube::setMatches(int num, vector<DMatch> &matches) {
  for (int i = 0; i < num; i++) {
    matches.push_back(DMatch(i, i, 0));
  }
}
int test_multitypestorage()
{
	// init some variables:
	Mat a(10,10,CV_32F, Scalar(0.3));
	Mat b(20,20,CV_32F, Scalar(1.));
	Mat c(10,10,CV_32FC3, Scalar(120.));

	double v(9.);
	double w(19.);

	vector<KeyPoint> kp, kp1;
	kp.push_back(KeyPoint(1.0,-1.0,20));
	kp.push_back(KeyPoint(2.0,21.0,10));

	kp1.push_back(KeyPoint(11.0,-1.0,20));
	kp1.push_back(KeyPoint(2.0,1.0,10));


	vector<vector<DMatch> > m;
	vector<DMatch> _m, _n; 
	_m.push_back(DMatch(1,2,10.)); 
	_m.push_back(DMatch(3,4,4.)); 
	_n.push_back(DMatch(6,2,1.)); 
	_n.push_back(DMatch(5,4,41.)); 
	m.push_back(_m);
	m.push_back(_n);
		

	
	// 0-test:
	//cerr << "test_compare(1,2) = " << test_compare(1,2) << " = 0 (false) " << endl;
	//cerr << "test_compare(1.,1.) = " << test_compare(1.,1.) << " = 1 (true) " << endl;
	//cerr << "test_compare(a,a) = " << test_compare(a,a) << " = 1 " << endl;
	//cerr << "test_compare(a,b) = " << test_compare(a,b) << " = 0 "<< endl;
	//cerr << "test_compare(c,c) = " << test_compare(c,c) << " = 1 "<< endl;
	//cerr << "test_compare(a,c) = " << test_compare(a,c) << " = 0 "<< endl;
	//cerr << "test_compare(v,v) = " << test_compare(v,v) << " = 1 "<< endl;
	//cerr << "test_compare(v,w) = " << test_compare(v,w) << " = 0 "<< endl;
	//cerr << "test_compare(kp,kp) = " << test_compare<KeyPoint>(kp,kp) << " = 1 "<< endl;
	//cerr << "test_compare(kp,kp1) = " << test_compare<KeyPoint>(kp,kp1) << " = 0 "<< endl;
	//cerr << "test_compare(m,m) = " << test_compare<vector<DMatch> >(m,m) << " = 1 "<< endl;
	//cerr << "test_compare(_m,_n) = " << test_compare<DMatch>(_m,_n) << " = 0 "<< endl;


	// init MTStorage:
	MultiTypeStorage storage;	

	// add elements:
	storage.setElement<Mat>("a", a);
	storage.setElement<Mat>("b", b);
	storage.setElement<Mat>("c", c);
	storage.setElement<double>("v", v);
	storage.setElement<double>("w", w);
	storage.setElement<vector<KeyPoint> >("kp", kp);
	storage.setElement<vector<vector<DMatch> > >("m", m);


	// get elements:
	Mat A;
	if (storage.getElement<Mat>("a") != NULL) 
		A = *(storage.getElement<Mat>("a"));
	else{
		cerr << "a is not found !" << endl;
		return -1;
	}

	Mat B; 
	if (storage.getElement<Mat>("b")!=NULL)
		B = *(storage.getElement<Mat>("b"));
	else{
		cerr << "b is not found !" << endl;
		return -1;
	}

	Mat C = *(storage.getElement<Mat>("c"));

	double V = *(storage.getElement<double>("v"));
	double W = *(storage.getElement<double>("w"));
	vector<KeyPoint> KP = *(storage.getElement<vector<KeyPoint> >("kp"));
	vector<vector<DMatch> > M = *(storage.getElement<vector<vector<DMatch> > >("m"));

	// compare:
	int s1 = test_compare(A,a) + test_compare(B,b) + test_compare(C,c) + test_compare(V,v) + test_compare(W,w) + test_compare(KP,kp) + test_compare(M,m);
	if (s1 == 7)
		cerr << "test OK" << endl;
	else
		cerr << "test failed" << endl;


}
Example #7
0
Mat Tracker::process(const Mat frame, Stats& stats)
{
    TickMeter tm;
    vector<KeyPoint> kp;
    Mat desc;

    tm.start();
    detector->detectAndCompute(frame, noArray(), kp, desc);
    stats.keypoints = (int)kp.size();

    vector< vector<DMatch> > matches;
    vector<KeyPoint> matched1, matched2;
    matcher->knnMatch(first_desc, desc, matches, 2);
    for(unsigned i = 0; i < matches.size(); i++) {
        if(matches[i][0].distance < nn_match_ratio * matches[i][1].distance) {
            matched1.push_back(first_kp[matches[i][0].queryIdx]);
            matched2.push_back(      kp[matches[i][0].trainIdx]);
        }
    }
    stats.matches = (int)matched1.size();

    Mat inlier_mask, homography;
    vector<KeyPoint> inliers1, inliers2;
    vector<DMatch> inlier_matches;
    if(matched1.size() >= 4) {
        homography = findHomography(Points(matched1), Points(matched2),
                                    RANSAC, ransac_thresh, inlier_mask);
    }
    tm.stop();
    stats.fps = 1. / tm.getTimeSec();

    if(matched1.size() < 4 || homography.empty()) {
        Mat res;
        hconcat(first_frame, frame, res);
        stats.inliers = 0;
        stats.ratio = 0;
        return res;
    }
    for(unsigned i = 0; i < matched1.size(); i++) {
        if(inlier_mask.at<uchar>(i)) {
            int new_i = static_cast<int>(inliers1.size());
            inliers1.push_back(matched1[i]);
            inliers2.push_back(matched2[i]);
            inlier_matches.push_back(DMatch(new_i, new_i, 0));
        }
    }
    stats.inliers = (int)inliers1.size();
    stats.ratio = stats.inliers * 1.0 / stats.matches;

    vector<Point2f> new_bb;
    perspectiveTransform(object_bb, new_bb, homography);
    Mat frame_with_bb = frame.clone();
    if(stats.inliers >= bb_min_inliers) {
        drawBoundingBox(frame_with_bb, new_bb);
    }
    Mat res;
    drawMatches(first_frame, inliers1, frame_with_bb, inliers2,
                inlier_matches, res,
                Scalar(255, 0, 0), Scalar(255, 0, 0));
    return res;
}