ViSP  2.8.0
vpKeyPointSurf.cpp
1 /****************************************************************************
2  *
3  * $Id: vpKeyPointSurf.cpp 4182 2013-03-27 13:20:58Z fspindle $
4  *
5  * This file is part of the ViSP software.
6  * Copyright (C) 2005 - 2013 by INRIA. All rights reserved.
7  *
8  * This software is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * ("GPL") version 2 as published by the Free Software Foundation.
11  * See the file LICENSE.txt at the root directory of this source
12  * distribution for additional information about the GNU GPL.
13  *
14  * For using ViSP with software that can not be combined with the GNU
15  * GPL, please contact INRIA about acquiring a ViSP Professional
16  * Edition License.
17  *
18  * See http://www.irisa.fr/lagadic/visp/visp.html for more information.
19  *
20  * This software was developed at:
21  * INRIA Rennes - Bretagne Atlantique
22  * Campus Universitaire de Beaulieu
23  * 35042 Rennes Cedex
24  * France
25  * http://www.irisa.fr/lagadic
26  *
27  * If you have questions regarding the use of this file, please contact
28  * INRIA at visp@inria.fr
29  *
30  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
31  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
32  *
33  *
34  * Description:
35  * Key point Surf.
36  *
37  * Authors:
38  * Nicolas Melchior
39  *
40  *****************************************************************************/
41 
42 
43 #include <visp/vpKeyPointSurf.h>
44 
45 #if defined (VISP_HAVE_OPENCV_NONFREE)
46 #if VISP_HAVE_OPENCV_VERSION >= 0x010100 // Require opencv >= 1.1.0
47 
48 #include <visp/vpImageConvert.h>
49 #include <visp/vpImageTools.h>
50 #include <visp/vpDisplay.h>
51 #include <visp/vpDebug.h>
52 
53 #include <ctype.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <vector>
57 #include <list>
58 
59 // Compare two surf descriptors.
60 double compareSURFDescriptors( const float* d1, const float* d2,
61  double best, int length )
62 {
63  double total_cost = 0;
64  int i;
65  assert( length % 4 == 0 );
66  for( i = 0; i < length; i += 4 )
67  {
68  double t0 = d1[i] - d2[i];
69  double t1 = d1[i+1] - d2[i+1];
70  double t2 = d1[i+2] - d2[i+2];
71  double t3 = d1[i+3] - d2[i+3];
72  total_cost += t0*t0 + t1*t1 + t2*t2 + t3*t3;
73  if( total_cost > best )
74  break;
75  }
76  return total_cost;
77 }
78 
79 
80 //Find for a point candidate the most similar point in the reference point list.
81 int naiveNearestNeighbor( const float *vec, int laplacian,
82  const CvSeq *model_keypoints,
83  const CvSeq *model_descriptors )
84 {
85  int length = (int)(model_descriptors->elem_size/(int)sizeof(float));
86  int i, neighbor = -1;
87  double d, dist1 = 1e6, dist2 = 1e6;
88  CvSeqReader reader, kreader;
89  cvStartReadSeq( model_keypoints, &kreader, 0 );
90  cvStartReadSeq( model_descriptors, &reader, 0 );
91 
92  for( i = 0; i < model_descriptors->total; i++ )
93  {
94  const CvSURFPoint* kp = (const CvSURFPoint*)kreader.ptr;
95  const float* mvec = (const float*)reader.ptr;
96  CV_NEXT_SEQ_ELEM( kreader.seq->elem_size, kreader );
97  CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
98  if( laplacian != kp->laplacian )
99  continue;
100  d = compareSURFDescriptors( vec, mvec, dist2, length );
101  if( d < dist1 )
102  {
103  dist2 = dist1;
104  dist1 = d;
105  neighbor = i;
106  }
107  else if ( d < dist2 )
108  dist2 = d;
109  }
110  if ( dist1 < 0.6*dist2 )
111  return neighbor;
112  return -1;
113 }
114 
115 
116 //Find for a point candidate the most similar point in the reference point list.
117 int naiveNearestNeighbor( const float *vec,
118  const CvSeq *ref_keypoints,
119  const CvSeq *ref_descriptors )
120 {
121  int length = (int)(ref_descriptors->elem_size/(int)sizeof(float));
122  int i, neighbor = -1;
123  double d, dist1 = 1e6, dist2 = 1e6;
124  CvSeqReader reader, kreader;
125  cvStartReadSeq( ref_keypoints, &kreader, 0 );
126  cvStartReadSeq( ref_descriptors, &reader, 0 );
127 
128  for( i = 0; i < ref_descriptors->total; i++ )
129  {
130  const float* mvec = (const float*)reader.ptr;
131  CV_NEXT_SEQ_ELEM( kreader.seq->elem_size, kreader );
132  CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
133  d = compareSURFDescriptors( vec, mvec, dist2, length );
134  if( d < dist1 )
135  {
136  dist2 = dist1;
137  dist1 = d;
138  neighbor = i;
139  }
140  else if ( d < dist2 )
141  dist2 = d;
142  }
143  if ( dist1 < 0.6*dist2 )
144  return neighbor;
145  return -1;
146 }
147 
148 
149 
150 //Find all the matched points
151 void findPairs( const CvSeq* objectKeypoints,
152  const CvSeq* objectDescriptors,
153  const CvSeq* imageKeypoints,
154  const CvSeq* imageDescriptors,
155  std::vector<int>& ptpairs )
156 {
157  int i;
158  CvSeqReader reader, kreader;
159  cvStartReadSeq( objectKeypoints, &kreader );
160  cvStartReadSeq( objectDescriptors, &reader );
161  ptpairs.clear();
162 
163  for( i = 0; i < objectDescriptors->total; i++ )
164  {
165  const CvSURFPoint* kp = (const CvSURFPoint*)kreader.ptr;
166  const float* descriptor = (const float*)reader.ptr;
167  CV_NEXT_SEQ_ELEM( kreader.seq->elem_size, kreader );
168  CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
169  int nearest_neighbor = naiveNearestNeighbor( descriptor,
170  kp->laplacian,
171  imageKeypoints,
172  imageDescriptors );
173  if( nearest_neighbor >= 0 )
174  {
175  ptpairs.push_back(i);
176  ptpairs.push_back(nearest_neighbor);
177  }
178  }
179 }
180 
181 
182 
191 {
192  descriptorType = extendedDescriptor;
193  hessianThreshold = 500;
194  init();
195 
196  image_keypoints = NULL;
197  image_descriptors = NULL;
198  ref_keypoints = NULL;
199  ref_descriptors = NULL;
200 
201  storage_cur = NULL;
202 }
203 
207 void vpKeyPointSurf::init()
208 {
209 #if (VISP_HAVE_OPENCV_VERSION >= 0x020400) // Require opencv >= 2.4.0
210  cv::initModule_nonfree();
211 #endif
212 
213  storage = cvCreateMemStorage(0);
214  params = cvSURFParams(hessianThreshold, descriptorType);
215 }
216 
222 {
223  cvReleaseMemStorage(&storage);
224  if(storage_cur!=NULL){
225  cvReleaseMemStorage(&storage_cur);
226  }
227 }
228 
239 {
240  IplImage* model = NULL;
241 
242  if((I.getWidth() % 8) == 0){
243  int height = (int)I.getHeight();
244  int width = (int)I.getWidth();
245  CvSize size = cvSize(width, height);
246  model = cvCreateImageHeader(size, IPL_DEPTH_8U, 1);
247  model->imageData = (char*)I.bitmap;
248  }else{
249  vpImageConvert::convert(I,model);
250  }
251 
252  cvExtractSURF( model, 0, &ref_keypoints, &ref_descriptors, storage, params );
253 
254  const unsigned int nbPoints = (unsigned int)ref_keypoints->total;
255 
256  referenceImagePointsList.resize(nbPoints);
257 
258  for(unsigned int i = 0; i < nbPoints; i++ )
259  {
260  CvSURFPoint* r1 = (CvSURFPoint*)cvGetSeqElem(ref_keypoints, (int)i);
261 
262  referenceImagePointsList[i].set_i(r1->pt.y);
263  referenceImagePointsList[i].set_j(r1->pt.x);
264  }
265 
266  if((I.getWidth() % 8) == 0){
267  model->imageData = NULL;
268  cvReleaseImageHeader(&model);
269  }else{
270  cvReleaseImage(&model);
271  }
272 
273  _reference_computed = true;
274  return nbPoints;
275 }
276 
277 
296  const vpImagePoint &iP,
297  const unsigned int height, const unsigned int width)
298 {
299  if((iP.get_i()+height) >= I.getHeight()
300  || (iP.get_j()+width) >= I.getWidth())
301  {
302  vpTRACE("Bad size for the subimage");
304  "Bad size for the subimage"));
305  }
306 
307  vpImage<unsigned char> subImage;
308 
310  (unsigned int)iP.get_i(),
311  (unsigned int)iP.get_j(),
312  height, width, subImage);
313 
314  unsigned int nbRefPoint = this->buildReference(subImage);
315 
316  for(unsigned int k = 0; k < nbRefPoint; k++)
317  {
318  (referenceImagePointsList[k]).set_i((referenceImagePointsList[k]).get_i()
319  + iP.get_i());
320  (referenceImagePointsList[k]).set_j((referenceImagePointsList[k]).get_j()
321  + iP.get_j());
322  }
323  return(nbRefPoint);
324 }
325 
326 
341  const vpRect& rectangle)
342 {
343  vpImagePoint iP;
344  iP.set_i(rectangle.getTop());
345  iP.set_j(rectangle.getLeft());
346  return (this->buildReference(I, iP,
347  (unsigned int)rectangle.getHeight(),
348  (unsigned int)rectangle.getWidth()));
349 }
350 
351 
363 {
364  IplImage* currentImage = NULL;
365 
366  if((I.getWidth() % 8) == 0){
367  int height = (int)I.getHeight();
368  int width = (int)I.getWidth();
369  CvSize size = cvSize(width, height);
370  currentImage = cvCreateImageHeader(size, IPL_DEPTH_8U, 1);
371  currentImage->imageData = (char*)I.bitmap;
372  }else{
373  vpImageConvert::convert(I,currentImage);
374  }
375 
376  /* we release the memory storage for the current points (it has to be kept
377  allocated for the get descriptor points, ...) */
378  if(storage_cur != NULL){
379  cvReleaseMemStorage(&storage_cur);
380  storage_cur = NULL;
381  }
382  storage_cur = cvCreateMemStorage(0);
383 
384  cvExtractSURF( currentImage, 0, &image_keypoints, &image_descriptors,
385  storage_cur, params );
386 
387  CvSeqReader reader, kreader;
388  cvStartReadSeq( ref_keypoints, &kreader );
389  cvStartReadSeq( ref_descriptors, &reader );
390 
391 
392  std::list<int> indexImagePair;
393  std::list<int> indexReferencePair;
394 
395 
396  unsigned int nbrPair = 0;
397 
398  for(int i = 0; i < ref_descriptors->total; i++ )
399  {
400  const CvSURFPoint* kp = (const CvSURFPoint*)kreader.ptr;
401  const float* descriptor = (const float*)reader.ptr;
402  CV_NEXT_SEQ_ELEM( kreader.seq->elem_size, kreader );
403  CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
404  int nearest_neighbor = naiveNearestNeighbor( descriptor,
405  kp->laplacian,
406  image_keypoints,
407  image_descriptors );
408  if( nearest_neighbor >= 0 )
409  {
410  indexReferencePair.push_back(i);
411  indexImagePair.push_back(nearest_neighbor);
412  nbrPair++;
413  }
414  }
415 
416  std::list<int>::const_iterator indexImagePairIter = indexImagePair.begin();
417  std::list<int>::const_iterator indexReferencePairIter = indexReferencePair.begin();
418 
419  matchedReferencePoints.resize(0);
420 
421  if (nbrPair == 0)
422  return (0);
423 
424  currentImagePointsList.resize(nbrPair);
425  matchedReferencePoints.resize(nbrPair);
426 
427  for (unsigned int i = 0; i < nbrPair; i++)
428  {
429  int index = *indexImagePairIter;
430 
431  CvSURFPoint* r1 = (CvSURFPoint*)cvGetSeqElem(image_keypoints, index);
432 
433  currentImagePointsList[i].set_i(r1->pt.y);
434  currentImagePointsList[i].set_j(r1->pt.x);
435 
436  matchedReferencePoints[i] = (unsigned int)*indexReferencePairIter;
437 
438 
439  ++indexImagePairIter;
440  ++indexReferencePairIter;
441  }
442 
443  if((I.getWidth() % 8) == 0){
444  currentImage->imageData = NULL;
445  cvReleaseImageHeader(&currentImage);
446  }else{
447  cvReleaseImage(&currentImage);
448  }
449 
450  return nbrPair;
451 }
452 
453 
473  const vpImagePoint &iP,
474  const unsigned int height, const unsigned int width)
475 {
476  if((iP.get_i()+height) >= I.getHeight()
477  || (iP.get_j()+width) >= I.getWidth())
478  {
479  vpTRACE("Bad size for the subimage");
481  "Bad size for the subimage"));
482  }
483 
484  vpImage<unsigned char> subImage;
485 
487  (unsigned int)iP.get_i(),
488  (unsigned int)iP.get_j(),
489  height, width, subImage);
490 
491  unsigned int nbMatchedPoint = this->matchPoint(subImage);
492 
493  for(unsigned int k = 0; k < nbMatchedPoint; k++)
494  {
495  (currentImagePointsList[k]).set_i((currentImagePointsList[k]).get_i()
496  + iP.get_i());
497  (currentImagePointsList[k]).set_j((currentImagePointsList[k]).get_j()
498  + iP.get_j());
499  }
500 
501  return(nbMatchedPoint);
502 }
503 
504 
520  const vpRect& rectangle)
521 {
522  vpImagePoint iP;
523  iP.set_i(rectangle.getTop());
524  iP.set_j(rectangle.getLeft());
525  return (this->matchPoint(I, iP,
526  (unsigned int)rectangle.getHeight(),
527  (unsigned int)rectangle.getWidth()));
528 }
529 
530 
550  const vpImage<unsigned char> &Icurrent, unsigned int size)
551 {
552 // matchedPointsCurrentImageList.front();
553 // matchedPointsReferenceImageList.front();
554 
555 // if (matchedPointsCurrentImageList.nbElements()
556 // != matchedPointsReferenceImageList.nbElements())
557 // {
558 // vpTRACE("Numbers of points mismatch");
559 // throw(vpException(vpException::fatalError,"Numbers of points mismatch"));
560 // }
561 
562  for (unsigned int i = 0; i < matchedReferencePoints.size(); i++)
563  {
566 // matchedPointsReferenceImageList.next();
567 // matchedPointsCurrentImageList.next();
568  }
569 }
570 
571 
584 void vpKeyPointSurf::display(const vpImage<unsigned char> &Icurrent, unsigned int size, const vpColor &color)
585 {
586 // matchedPointsCurrentImageList.front();
587 //
588 // vpImagePoint ipCur;
589 //
590  for (unsigned int i = 0; i < matchedReferencePoints.size(); i++)
591  {
592  vpDisplay::displayCross (Icurrent, currentImagePointsList[i], size, color);
593  }
594 }
595 
596 #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS
597 
616 vp_deprecated vpList<int*>* vpKeyPointSurf::matchPoint(vpList<float*> descriptorList, vpList<int> laplacianList)
617 {
618  vpList<int*>* pairPoints = new vpList<int*>;
619 
620  if(descriptorList.nb != laplacianList.nb){
621  vpTRACE("Error, the two lists have different number of element");
622  return pairPoints;
623  }
624 
625  CvSeqReader reader;
626  cvStartReadSeq( ref_descriptors, &reader );
627 
628  descriptorList.front();
629  pairPoints->front();
630  laplacianList.front();
631  int indexList = 0;
632  while(!descriptorList.outside()){
633  float* descriptor = descriptorList.value();
634 
635  int nearest_neighbor = naiveNearestNeighbor( descriptor, laplacianList.value(), ref_keypoints, ref_descriptors);
636 
637  if(nearest_neighbor >= 0){
638  int* tab;
639  tab = new int[2];
640  tab[0] = nearest_neighbor;
641  tab[1] = indexList;
642  pairPoints->addRight(tab);
643  }
644  indexList++;
645  descriptorList.next();
646  laplacianList.next();
647  }
648 
649  return pairPoints;
650 }
651 #endif
652 
653 std::list<int*>* vpKeyPointSurf::matchPoint(std::list<float*> descriptorList, std::list<int> laplacianList)
654 {
655  std::list<int*>* pairPoints = new std::list<int*>;
656 
657  if(descriptorList.size() != laplacianList.size()){
658  vpTRACE("Error, the two lists have different number of element");
659  return pairPoints;
660  }
661 
662  CvSeqReader reader;
663  cvStartReadSeq( ref_descriptors, &reader );
664 
665  std::list<float*>::const_iterator descriptorListIter = descriptorList.begin();
666  std::list<int>::const_iterator laplacianListIter = laplacianList.begin();
667  descriptorList.front();
668  int indexList = 0;
669  while(descriptorListIter != descriptorList.end()){
670  float* descriptor = *descriptorListIter;
671 
672  int nearest_neighbor = naiveNearestNeighbor( descriptor, *laplacianListIter, ref_keypoints, ref_descriptors);
673 
674  if(nearest_neighbor >= 0){
675  int* tab;
676  tab = new int[2];
677  tab[0] = nearest_neighbor;
678  tab[1] = indexList;
679  pairPoints->push_back(tab);
680  }
681  indexList++;
682  ++descriptorListIter;
683  ++laplacianListIter;
684  }
685 
686  return pairPoints;
687 }
688 
695 {
696  if (index >= static_cast<int>(referenceImagePointsList.size()) || index < 0){
697  vpTRACE("Index of the reference point out of range");
698  throw(vpException(vpException::fatalError,"Index of the reference point out of range"));
699  }
700 
701  float* descriptor = NULL;
702 
703  CvSeqReader reader;
704  cvStartReadSeq( ref_descriptors, &reader );
705 
706  for(int j = 0; j < ref_descriptors->total; j++ ){
707  if(j== index){
708  descriptor = (float*)reader.ptr;
709  break;
710  }
711  CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
712  }
713 
714  return descriptor;
715 }
716 
723 {
724  if (index >= static_cast<int>(referenceImagePointsList.size()) || index < 0){
725  vpTRACE("Index of the reference point out of range");
726  throw(vpException(vpException::fatalError,"Index of the reference point out of range"));
727  }
728 
729  CvSeqReader reader;
730  cvStartReadSeq( ref_keypoints, &reader );
731 
732  int laplacian = 0;/* normally only -1, 0, +1 are possible */
733 
734  for(int j = 0; j < ref_keypoints->total; j++ ){
735  if(j== index){
736  const CvSURFPoint* kp = (const CvSURFPoint*)reader.ptr;
737  laplacian = kp->laplacian;
738  break;
739  }
740  CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
741  }
742 
743  return laplacian;
744 }
745 
753 void vpKeyPointSurf::getDescriptorParamReferencePoint (const int index, int& size, float& dir)
754 {
755  if (index >= static_cast<int>(referenceImagePointsList.size()) || index < 0){
756  vpTRACE("Index of the reference point out of range");
757  throw(vpException(vpException::fatalError,"Index of the reference point out of range"));
758  }
759 
760  CvSeqReader reader;
761  cvStartReadSeq( ref_keypoints, &reader );
762 
763  for(int j = 0; j < ref_keypoints->total; j++ ){
764  if(j== index){
765  const CvSURFPoint* kp = (const CvSURFPoint*)reader.ptr;
766  size = kp->size;
767  dir = kp->dir;
768  break;
769  }
770  CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
771  }
772 }
773 
774 
775 #endif
776 #endif
777 
void set_j(const double j)
Definition: vpImagePoint.h:156
class that defines what is a Keypoint. This class provides all the basic elements to implement classe...
double getTop() const
Definition: vpRect.h:169
void getDescriptorParamReferencePoint(const int index, int &size, float &dir)
double get_i() const
Definition: vpImagePoint.h:181
bool outside(void) const
Test if the current element is outside the list (on the virtual element)
Definition: vpList.h:431
unsigned int getWidth() const
Definition: vpImage.h:159
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
#define vpTRACE
Definition: vpDebug.h:401
Provide simple list management.
Definition: vpList.h:112
Type * bitmap
points toward the bitmap
Definition: vpImage.h:120
Class to define colors available for display functionnalities.
Definition: vpColor.h:125
int getLaplacianReferencePoint(const int index)
void set_i(const double i)
Definition: vpImagePoint.h:145
error that can be emited by ViSP classes.
Definition: vpException.h:75
unsigned int matchPoint(const vpImage< unsigned char > &I)
double getHeight() const
Definition: vpRect.h:150
void display(const vpImage< unsigned char > &Iref, const vpImage< unsigned char > &Icurrent, unsigned int size=3)
static const vpColor green
Definition: vpColor.h:170
double get_j() const
Definition: vpImagePoint.h:192
void next(void)
position the current element on the next one
Definition: vpList.h:275
static const vpColor red
Definition: vpColor.h:167
double getWidth() const
Definition: vpRect.h:188
unsigned int nb
Definition: vpList.h:118
void front(void)
Position the current element on the first element of the list.
Definition: vpList.h:386
float * getDescriptorReferencePoint(const int index)
type & value(void)
return the value of the current element
Definition: vpList.h:303
bool _reference_computed
flag to indicate if the reference has been built.
virtual void displayCross(const vpImagePoint &ip, unsigned int size, const vpColor &color, unsigned int thickness=1)=0
void addRight(const type &el)
add a new element in the list, at the right of the current one
Definition: vpList.h:480
std::vector< vpImagePoint > referenceImagePointsList
unsigned int buildReference(const vpImage< unsigned char > &I)
static void createSubImage(const vpImage< Type > &I, unsigned int i_sub, unsigned int j_sub, unsigned int nrow_sub, unsigned int ncol_sub, vpImage< Type > &S)
Definition: vpImageTools.h:133
std::vector< unsigned int > matchedReferencePoints
unsigned int getHeight() const
Definition: vpImage.h:150
Defines a rectangle in the plane.
Definition: vpRect.h:82
std::vector< vpImagePoint > currentImagePointsList
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:92
double getLeft() const
Definition: vpRect.h:156
virtual ~vpKeyPointSurf()