//static void matchDescriptors( const Mat& queryDescriptors, const vector<Mat>& trainDescriptors,
                       //vector<DMatch>& matches, FlannBasedMatcher& descriptorMatcher )
static void matchDescriptors( const Mat& queryDescriptors, const vector<Mat>& trainDescriptors,
                       vector<DMatch>& matches, FlannBasedMatcher& descriptorMatcher, const vector<Mat>& trainImages, const vector<string>& trainImagesNames )

{
    cout << "< Set train descriptors collection in the matcher and match query descriptors to them..." << endl;

    descriptorMatcher.add( trainDescriptors );
    descriptorMatcher.train();

    descriptorMatcher.match( queryDescriptors, matches );

    CV_Assert( queryDescriptors.rows == (int)matches.size() || matches.empty() );

    cout << "Number of matches: " << matches.size() << endl;
    cout << ">" << endl;

    for( int i = 0; i < trainDescriptors.size(); i++){

        std::vector< std::vector< DMatch> > matches2;

        std::vector< DMatch > good_matches;

        descriptorMatcher.knnMatch( queryDescriptors, trainDescriptors[i], matches2, 2);
        CV_Assert( queryDescriptors.rows == (int)matches2.size() || matches2.empty() );

        for (int j = 0; j < matches2.size(); ++j){
            const float ratio = 0.8; // As in Lowe's paper; can be tuned
            if (matches2[j][0].distance < ratio * matches2[j][1].distance){
                good_matches.push_back(matches2[j][0]);
            }

        }

        cout << "currentMatchSize : " << good_matches.size() << endl;

    }

    
}
/**
 * @function main
 */
int main(int, char **argv)
{



    image1 = imread(argv[1], 1);
    image2 = imread(argv[2], 1);
    rows = image1.rows;
    cols = image1.cols;


    namedWindow("image1", WINDOW_AUTOSIZE);
    imshow("image1", image1);
    namedWindow("image2", WINDOW_AUTOSIZE);
    imshow("image2", image2);

    Mat image1_gray;
    Mat image2_gray;


    /// Converts an image from one color space to another.
    cvtColor(image1, image1_gray, COLOR_BGR2GRAY);
    cvtColor(image2, image2_gray, COLOR_BGR2GRAY);

    /// Detector parameters
    int blockSize = 2;
    int apertureSize = 3;
    double k = 0.04;

    /// Detecting corners
    /*
       void ocl::cornerHarris(const oclMat& src, oclMat& dst, int blockSize, int ksize, double k, int bordertype=cv::BORDER_DEFAULT)

       src – Source image. Only CV_8UC1 and CV_32FC1 images are supported now.
       dst – Destination image containing cornerness values. It has the same size as src and CV_32FC1 type.
       blockSize – Neighborhood size
       ksize – Aperture parameter for the Sobel operator
       k – Harris detector free parameter
       bordertype – Pixel extrapolation method. Only BORDER_REFLECT101, BORDER_REFLECT, BORDER_CONSTANT and BORDER_REPLICATE are supported now.
     */

    Mat image1dst;
    Mat image2dst;

    image1dst = Mat::zeros(image1.size(), CV_32FC1);
    image2dst = Mat::zeros(image2.size(), CV_32FC1);


    cornerHarris(image1_gray, image1dst, blockSize, apertureSize, k, BORDER_DEFAULT);
    cornerHarris(image2_gray, image2dst, blockSize, apertureSize, k, BORDER_DEFAULT);

    int threshHarris = 100;

    /// Normalizing
    /*
       void normalize(InputArray src, OutputArray dst, double alpha=1, double beta=0, int norm_type=NORM_L2, int dtype=-1, InputArray mask=noArray() )
       src – input array.
       dst – output array of the same size as src .
       alpha – norm value to normalize to or the lower range boundary in case of the range normalization.
       beta – upper range boundary in case of the range normalization; it is not used for the norm normalization.
       normType – normalization type (see the details below).
       dtype – when negative, the output array has the same type as src; otherwise, it has the same number of channels as src and the depth =CV_MAT_DEPTH(dtype).
       mask – optional operation mask.
     */

    Mat image1dst_norm;
    Mat image2dst_norm;

    normalize(image1dst, image1dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat());
    normalize(image2dst, image2dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat());

    /*
       On each element of the input array, the function convertScaleAbs performs three operations sequentially: scaling, taking an absolute value, conversion to an unsigned 8-bit type:

     */

    Mat image1dst_norm_scaled;
    Mat image2dst_norm_scaled;

    convertScaleAbs(image1dst_norm, image1dst_norm_scaled);
    convertScaleAbs(image2dst_norm, image2dst_norm_scaled);

    KeyPoint kp;

    for (int j = 0; j < image1dst_norm.rows; j++) {
	for (int i = 0; i < image1dst_norm.cols; i++) {
	    if ((int) image1dst_norm.at < float >(j, i) > threshHarris) {

		kp.pt.x = (float) j;
		kp.pt.y = (float) i;
		//necessaire je ne sais pas pk
		kp.size = 100.0;
		keypoints1.push_back(kp);

	    }
	}
    }

    for (int j = 0; j < image2dst_norm.rows; j++) {
	for (int i = 0; i < image2dst_norm.cols; i++) {
	    if ((int) image2dst_norm.at < float >(j, i) > threshHarris) {


		kp.pt.x = (float) j;
		kp.pt.y = (float) i;
		//necessaire je ne sais pas pk
		kp.size = 100.0;
		keypoints2.push_back(kp);

	    }
	}
    }


    BriefDescriptorExtractor briefDesc(64);

    Mat descriptors1, descriptors2;
    briefDesc.compute(image1, keypoints1, descriptors1);
    briefDesc.compute(image2, keypoints2, descriptors2);

    //Ptr<DescriptorMatcher> matcher =  new FlannBasedMatcher(DescriptorMatcher::create("Flann"));
    FlannBasedMatcher matcher;

    Mat descriptorAuxKp1;
    Mat descriptorAuxKp2;


    vector < int >associateIdx;

    for (int i = 0; i < descriptors1.rows; i++) {
	//on copie la ligne i du descripteur, qui correspond aux différentes valeurs données par le descripteur pour le Keypoints[i]
	descriptors1.row(i).copyTo(descriptorAuxKp1);

//ici on va mettre que les valeurs du descripteur des keypoints de l'image 2 que l'on veut comparer aux keypoints de l'image1 en cours de traitement
	descriptorAuxKp2.create(0, 0, CV_8UC1);


	//associateIdx va servir à faire la transition entre les indices renvoyés par matches et ceux des Keypoints
	associateIdx.erase(associateIdx.begin(), associateIdx.end());


	for (int j = 0; j < descriptors2.rows; j++) {

	    float p1x = keypoints1[i].pt.x;
	    float p1y = keypoints1[i].pt.y;
	    float p2x = keypoints2[j].pt.x;
	    float p2y = keypoints2[j].pt.y;

	    float distance = sqrt(pow((p1x - p2x), 2) + pow((p1y - p2y), 2));

	    //parmis les valeurs dans descriptors2 on ne va garder que ceux dont les keypoints associés sont à une distance définie du keypoints en cours, en l'occurence le ieme ici.
	    if (distance < 10) {

		descriptorAuxKp2.push_back(descriptors2.row(j));
		associateIdx.push_back(j);

	    }


	}
	//ici on ne matche qu'un keypoints de l'image1 avec tous les keypoints gardés de l'image 2
        matcher.add(descriptorAuxKp1);
        matcher.train();

	matcher.match(descriptorAuxKp2, matches);

	//on remet à la bonne valeur les attributs de matches
	for (int idxMatch = 0; idxMatch < matches.size(); idxMatch++) {
	    //on a comparer le keypoints i
	    matches[idxMatch].queryIdx = i;
	    //avec le keypoints2 j
	    matches[idxMatch].trainIdx = associateIdx[matches[idxMatch].trainIdx];
	}

	//on concatene les matches trouvés pour les points précedents avec les nouveaux
	matchesWithDist.insert(matchesWithDist.end(), matches.begin(), matches.end());


    }



//ici on trie les matchesWithDist par distance des valeurs des descripteurs et non par distance euclidienne
    nth_element(matchesWithDist.begin(), matchesWithDist.begin() + 24, matchesWithDist.end());
    // initial position
    // position of the sorted element
    // end position

    Mat imageMatches;
    Mat matchesMask;
    drawMatches(image1, keypoints1,	// 1st image and its keypoints
		image2, keypoints2,	// 2nd image and its keypoints
		matchesWithDist,	// the matches
		imageMatches,	// the image produced
		Scalar::all(-1),	// color of the lines
		Scalar(255, 255, 255)	//color of the keypoints
	);


    namedWindow(matches_window, CV_WINDOW_AUTOSIZE);
    imshow(matches_window, imageMatches);
    imwrite("resultat.png", imageMatches);



    /// Create a window and a trackbar
    namedWindow(transparency_window, WINDOW_AUTOSIZE);
    createTrackbar("Threshold: ", transparency_window, &thresh, max_thresh, interface);








    interface(0, 0);

    waitKey(0);
    return (0);
}
Exemple #3
0
int main(int argc, char** argv) {
  gval_debug_init();

  // get options
  int showhelp = 0;
  unsigned int n_cluster = 0;

  int opt;
  while ((opt = getopt(argc, argv, "k:h")) != -1) {
    switch (opt) {
      case 'h':
        showhelp = 1;
        break;
      case 'k':
        n_cluster = atoi(optarg);
        break;
      default:
        showhelp = 1;
        break;
    }
  }

  if (showhelp || n_cluster <= 0 || argc - optind < 2) {
    fprintf(stderr, "Usage: %s -k n_cluster input_dir output\n", argv[0]);
    return EXIT_SUCCESS;
  }

  path p(argv[optind]);
  if (!exists(p) || !is_directory(p)) {
    fprintf(stderr, "%s is not a directory\n", argv[optind]);
    return EXIT_FAILURE;
  }

  BOWKMeansTrainer bow(n_cluster);

  directory_iterator dir_end;
  for (directory_iterator i(p); i != dir_end; i++) {
    if (i->path().extension() == DESCRIPTORS_EXT) {
      FILE* in = fopen(i->path().c_str(), "r");
      assert(in);
      int counter = 0;
      int nempty = 0;
      Mat* desc = (Mat*) gval_read_cvmat(in);
      while (desc != NULL) {
        counter++;
        if (!desc->empty()) {
          nempty++;
          bow.add(desc->clone());
        }
        gval_free_cvmat(desc);
        desc = (Mat*) gval_read_cvmat(in);
      }
      fclose(in);
      fprintf(stderr, "Read from file %s (%d/%d)\n",
          i->path().c_str(), nempty, counter);
    }
  }

  fprintf(stderr, "Clustering (%d descriptors, %d clusters)...",
      bow.descripotorsCount(), n_cluster);
  Mat voc = bow.cluster();
  fprintf(stderr, " Done\n");

  fprintf(stderr, "Counting document frequency...");
  int* dfcounter = (int*) calloc(n_cluster, sizeof(int));
  vector<Mat> all = bow.getDescriptors();
  FlannBasedMatcher matcher;
  matcher.add(vector<Mat>(1, voc));
  for (vector<Mat>::const_iterator it = all.begin();
      it != all.end(); it++) {
    vector<int> ct(n_cluster, 0);
    vector<DMatch> matches;
    matcher.match(*it, matches);
    for (vector<DMatch>::const_iterator jt = matches.begin();
        jt != matches.end(); jt++) {
      assert(jt->trainIdx >= 0 && jt->trainIdx < n_cluster);
      ct[jt->trainIdx] = 1;
    }

    for (int j = 0; j < n_cluster; j++) {
      dfcounter[j] += ct[j];
    }
  }
  int dtotal = all.size();
  fprintf(stderr, " Done\n");

  FILE* out = fopen(argv[optind + 1], "w");
  assert(out);
  gval_write_cvmat(&voc, out);
  fwrite(dfcounter, sizeof(int), n_cluster, out);
  fwrite(&dtotal, sizeof(int), 1, out);

  // debug
  fprintf(stderr, "total:%d\n", dtotal);
  for (int j = 0; j < n_cluster; j++) {
    fprintf(stderr, "%d:%d\n", j, dfcounter[j]);
  }

  fclose(out);
  fprintf(stderr, "Written to %s\n", argv[optind + 1]);

  free(dfcounter);

  return EXIT_SUCCESS;
}
Exemple #4
0
int main( int argc, char* argv[])
{
	// jmena souboru pro zpracovani
	string imageName1;
	string imageName2;


	// zpracovani parametru prikazove radky
	for( int i = 1; i < argc; i++){
		if( string(argv[ i]) == "-i1" && i + 1 < argc){
			imageName1 = argv[ ++i];
		} else if( string(argv[ i]) == "-i2" && i + 1 < argc){
			imageName2 = argv[ ++i];
		} else if( string(argv[ i]) == "-h"){
			cout << "Use: " << argv[0] << "  -i1 imageName1 -i2 imageName2" << endl;
			cout << "Merges two images into one. The images have to share some common area and have to be taken from one location." << endl;
			return 0;
		} else {
			cerr << "Error: Unrecognized command line parameter \"" << argv[ i] << "\" use -h to get more information." << endl;
		}
	}

	// kontrola zadani parametru
	if( imageName1.empty() || imageName2.empty()){
		cerr << "Error: Some mandatory command line options were not specified. Use -h for more information." << endl;
		return -1;
	}


	// nacteni sedotonovych obrazku 
	Mat img1 = imread( imageName1, 0);
	Mat img2 = imread( imageName2, 0);

	if( img1.data == NULL || img2.data == NULL){
		cerr << "Error: Failed to read input image files." << endl;
		return -1;
	}

	// SURF detektor lokalnich oblasti
	SurfFeatureDetector detector;

	// samotna detekce lokalnich priznaku
	vector< KeyPoint> keyPoints1, keyPoints2;
	detector.detect( img1, keyPoints1);
	detector.detect( img2, keyPoints2);
	cout << keyPoints1.size() << " " << keyPoints2.size();

	// extraktor SURF descriptoru
	SurfDescriptorExtractor descriptorExtractor;

	// samonty vypocet SURF descriptoru
	Mat descriptors1, descriptors2;
	descriptorExtractor.compute( img1, keyPoints1, descriptors1);
	descriptorExtractor.compute( img2, keyPoints2, descriptors2);

	// tento vektor je pouze pro ucely funkce hledajici korespondence
	vector< Mat> descriptorVector2;
	descriptorVector2.push_back( descriptors2);

	// objekt, ktery dokaze snad pomerne efektivne vyhledavat podebne vektory v prostorech s vysokym poctem dimenzi
	FlannBasedMatcher matcher;
	// Pridani deskriptoru, mezi kterymi budeme pozdeji hledat nejblizsi sousedy
	matcher.add( descriptorVector2);
	// Vytvoreni vyhledavaci struktury nad vlozenymi descriptory
	matcher.train();

	// nalezeni nejpodobnejsich descriptoru (z obrazku 2) pro descriptors1 (oblasti z obrazku 1)
	vector<cv::DMatch > matches;
	matcher.match( descriptors1, matches);

	// serazeni korespondenci od nejlepsi (ma nejmensi vzajemnou vzdalenost v prostoru descriptoru)
	sort( matches.begin(), matches.end(), compareDMatch);
	// pouzijeme jen 200 nejlepsich korespondenci
	matches.resize( min( 200, (int) matches.size()));

	// pripraveni korespondujicich dvojic
	Mat img1Pos( matches.size(), 1, CV_32FC2);
	Mat img2Pos( matches.size(), 1, CV_32FC2);

	// naplneni matic pozicemi
	for( int i = 0; i < (int)matches.size(); i++){
		img1Pos.at< Vec2f>( i)[0] = keyPoints1[ matches[ i].queryIdx].pt.x;
		img1Pos.at< Vec2f>( i)[1] = keyPoints1[ matches[ i].queryIdx].pt.y;
		img2Pos.at< Vec2f>( i)[0] = keyPoints2[ matches[ i].trainIdx].pt.x;
		img2Pos.at< Vec2f>( i)[1] = keyPoints2[ matches[ i].trainIdx].pt.y;
	}

	// Doplnte vypocet 3x3 matice homografie s vyuzitim algoritmu RANSAC. Pouzijte jdenu funkci knihovny OpenCV.
	/** FILL DONE **/
	Mat homography = findHomography( img1Pos, img2Pos, CV_RANSAC );


	// vystupni buffer pro vykresleni spojenych obrazku
	Mat outputBuffer( 1024, 1280, CV_8UC1);

	// Vysledny spojeny obraz budeme chtit vykreslit do outputBuffer tak, aby se dotykal okraju, ale nepresahoval je.
	// "Prilepime" obrazek 2 k prvnimu. Tuto "slepeninu" je potreba zvetsit a posunout, aby byla na pozadovane pozici.
	// K tomuto potrebujeme zjistit maximalni a minimalni souradnice vykreslenych obrazu. U obrazu 1 je to jednoduche, minima a maxima se 
	// ziskaji primo z rozmeru obrazu. U obrazku 2 musime pomoci drive ziskane homografie promitnout do prostoru obrazku 1 jeho rohove body.

	float minX = 0;
	float minY = 0;
	float maxX = (float) img1.cols;
	float maxY = (float) img1.rows;

	// rohy obrazku 2
	vector< Vec3d> corners;
	corners.push_back( Vec3d( 0, 0, 1));
	corners.push_back( Vec3d( img2.cols, 0, 1));
	corners.push_back( Vec3d( img2.cols, img2.rows, 1));
	corners.push_back( Vec3d( 0, img2.rows, 1));

	// promitnuti rohu obrazku 2 do prosotoru obrazku 1 a upraveni minim a maxim
	for( int i = 0; i < (int)corners.size();i ++){

		// Doplnte transformaci Mat( corners[ i]) do prostoru obrazku 1 pomoci homography.
		// Dejte si pozor odkud kam homography je. Podle toho pouzijte homography, nebo homography.inv().
		/**FILL ALMOST DONE**/
		Mat projResult = homography.inv() * Mat( corners[ i]);// * homography;

		minX = std::min( minX, (float) (projResult.at<double>( 0) / projResult.at<double>( 2)));
		maxX = std::max( maxX, (float) (projResult.at<double>( 0) / projResult.at<double>( 2)));
		minY = std::min( minY, (float) (projResult.at<double>( 1) / projResult.at<double>( 2)));
		maxY = std::max( maxY, (float) (projResult.at<double>( 1) / projResult.at<double>( 2)));
	}




	// Posuneme a zvetseme/zmenseme vysledny spojeny obrazek tak, by vysledny byl co nejvetsi, ale aby byl uvnitr vystupniho bufferu.

	// Zmena velikosti musi byt takova, aby se nam vysledek vesel na vysku i na sirku
	double scaleFactor = min( outputBuffer.cols / ( maxX - minX), outputBuffer.rows / ( maxY - minY));

	// Doplnte pripraveni matice, ktera zmeni velikost (scaleMatrix) o scaleFactor a druhe (translateMatrix), ktera posune vysledek o -minX a -minY. 
	// Po tomto bude obrazek ve vystupnim bufferu.
	Mat scaleMatrix = Mat::eye( 3, 3, CV_64F);
	Mat translateMatrix = Mat::eye( 3, 3, CV_64F);
	/**FILL DONE**/
    scaleMatrix.at<double>(0,0) = scaleFactor;
    scaleMatrix.at<double>(1,1) = scaleFactor;

    translateMatrix.at<double>(0,2) = -(double)minX; 
    translateMatrix.at<double>(1,2) = -(double)minY;
   
    cout << endl << minX << " " << minY << endl << translateMatrix << endl << endl;
    
	Mat centerMatrix = scaleMatrix * translateMatrix;


	// Transformace obrazku 1 
	warpPerspective( img1, outputBuffer, centerMatrix, outputBuffer.size(), 1, BORDER_TRANSPARENT);

	// Transformace obrazku 2 
	warpPerspective( img2, outputBuffer, centerMatrix * homography.inv(), outputBuffer.size(), 1, BORDER_TRANSPARENT);

	cout << "normMatrix" << endl;
	cout << centerMatrix << endl << endl;

	cout << "normMatrix" << endl;
	cout << homography << endl << endl;

#if VISUAL_OUTPUT
	imshow( "IMG1", img1);
	imshow( "IMG2", img2);
	imshow( "MERGED", outputBuffer);
	waitKey();
#endif
}
//--------------------------------------【main( )函数】-----------------------------------------
//          描述:控制台应用程序的入口函数,我们的程序从这里开始执行
//-----------------------------------------------------------------------------------------------
int main( ) 
{
	//【0】改变console字体颜色
	system("color 6F"); 

	void ShowHelpText();

	//【1】载入图像、显示并转化为灰度图
	Mat trainImage = imread("1.jpg"), trainImage_gray;
	imshow("原始图",trainImage);
	cvtColor(trainImage, trainImage_gray, CV_BGR2GRAY);

	//【2】检测Surf关键点、提取训练图像描述符
	vector<KeyPoint> train_keyPoint;
	Mat trainDescriptor;
	SurfFeatureDetector featureDetector(80);
	featureDetector.detect(trainImage_gray, train_keyPoint);
	SurfDescriptorExtractor featureExtractor;
	featureExtractor.compute(trainImage_gray, train_keyPoint, trainDescriptor);

	//【3】创建基于FLANN的描述符匹配对象
	FlannBasedMatcher matcher;
	vector<Mat> train_desc_collection(1, trainDescriptor);
	matcher.add(train_desc_collection);
	matcher.train();

	//【4】创建视频对象、定义帧率
	VideoCapture cap(0);
	unsigned int frameCount = 0;//帧数

	//【5】不断循环,直到q键被按下
	while(char(waitKey(1)) != 'q')
	{
		//<1>参数设置
		int64 time0 = getTickCount();
		Mat testImage, testImage_gray;
		cap >> testImage;//采集视频到testImage中
		if(testImage.empty())
			continue;

		//<2>转化图像到灰度
		cvtColor(testImage, testImage_gray, CV_BGR2GRAY);

		//<3>检测S关键点、提取测试图像描述符
		vector<KeyPoint> test_keyPoint;
		Mat testDescriptor;
		featureDetector.detect(testImage_gray, test_keyPoint);
		featureExtractor.compute(testImage_gray, test_keyPoint, testDescriptor);

		//<4>匹配训练和测试描述符
		vector<vector<DMatch> > matches;
		matcher.knnMatch(testDescriptor, matches, 2);

		// <5>根据劳氏算法(Lowe's algorithm),得到优秀的匹配点
		vector<DMatch> goodMatches;
		for(unsigned int i = 0; i < matches.size(); i++)
		{
			if(matches[i][0].distance < 0.6 * matches[i][1].distance)
				goodMatches.push_back(matches[i][0]);
		}

		//<6>绘制匹配点并显示窗口
		Mat dstImage;
		drawMatches(testImage, test_keyPoint, trainImage, train_keyPoint, goodMatches, dstImage);
		imshow("匹配窗口", dstImage);

		//<7>输出帧率信息
		cout << "当前帧率为:" << getTickFrequency() / (getTickCount() - time0) << endl;
	}

	return 0;
}
Exemple #6
0
// La funcion devuelve la cantidad de objetos encontrados en la imagen o, en caso de error, -1
int Matcher::featureMatch(const std::string& pathProduct,cv::Mat &ref) {
  using namespace cv;
  using namespace std;

  const bool GRISES = false;
  const float FACTOR_CLUSTERING = 0.01;  //matches cuya distancia sean menores a este valor por el tamaño de la imagen seran agrupados
  const int CALIDAD_FEATURES = 800;  // mientras mas alto, menos features tomara pero seran de mejor calidad
  const int MINIMA_DISTANCIA_MATCHES = 30;  // mientras mas chica mas parecidos deben ser los descriptors del match
  const int MULTIPLICADOR_DISTANCE_MATCHES = 3;

  if (ref.empty())
    return -1;
  Mat imagenCamara;
  if (!GRISES)
    imagenCamara = ref;
  else
    cv::cvtColor(ref, imagenCamara, CV_BGR2GRAY);
  if (imagenCamara.empty())
    return -1;

  vector<Mat> imgsObjeto;
  utils::Directory dir(pathProduct);
  std::vector<std::string> fileNames = dir.getFileNames();
  std::vector<std::string>::iterator it = fileNames.begin();
  while (it != fileNames.end()) {
    cv::Mat imagen = cv::imread((*it));
    if (!imagen.empty()) {
      if (GRISES)
        cv::cvtColor(imagen, imagen, CV_BGR2GRAY);
      imgsObjeto.push_back(imagen);
    }
    it++;
  }

  if (imgsObjeto.size() < 1)
    return -1;

  Ptr<Feature2D> detector;
  detector = xfeatures2d::SIFT::create(CALIDAD_FEATURES);

  vector<vector<KeyPoint> > keypoints_object;
  vector<KeyPoint> keypoints_scene;

  detector->detect(imgsObjeto, keypoints_object);
  detector->detect(imagenCamara, keypoints_scene);

  vector<Mat> descriptors_object;
  Mat descriptors_scene;
  detector->compute(imgsObjeto, keypoints_object, descriptors_object);
  detector->compute(imagenCamara, keypoints_scene, descriptors_scene);

  FlannBasedMatcher matcher;
  //BFMatcher matcher(NORM_L2);
  std::vector<DMatch> matches;

  matcher.add(descriptors_object);

  matcher.match(descriptors_scene, matches);

  double maxDist = 0;
  double minDist = MINIMA_DISTANCIA_MATCHES;

  for (unsigned int i = 0; i < matches.size(); i++) {
    double dist = matches[i].distance;
    if (dist < minDist)
      minDist = dist;
    if (dist > maxDist)
      maxDist = dist;
  }

  std::vector<DMatch> buenosMatches;

  for (unsigned int i = 0; i < matches.size(); i++) {
    if (matches[i].distance < MULTIPLICADOR_DISTANCE_MATCHES * minDist) {
      buenosMatches.push_back(matches[i]);
    }
  }

  int total = buenosMatches.size();

  // no cuento los matches que estan cerca uno de otros ya que pertenecen al mismo objeto :
  int tamanioImgCamara = (imagenCamara.rows + imagenCamara.cols) / 2;

  std::vector<bool> validos(total, true);

  for (unsigned int i = 0; i < buenosMatches.size(); i++) {
    if (validos[i]) {
      Point2f a = keypoints_scene[buenosMatches[i].queryIdx].pt;

      for (unsigned int j = 0; j < i; j++) {
        if (validos[j]) {
          Point2f b = keypoints_scene[buenosMatches[j].queryIdx].pt;
          double dist = cv::norm(a - b);
          if ( (float) dist < (float) tamanioImgCamara * FACTOR_CLUSTERING) {
            total--;
            validos[j] = false;
          }
        }
      }

    }
  }
  return total;
}