Main MRPT website > C++ reference for MRPT 1.5.6
CFeatureExtraction_ORB.cpp
Go to the documentation of this file.
1 /* +---------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2017, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +---------------------------------------------------------------------------+ */
9 
10 #include "vision-precomp.h" // Precompiled headers
11 
13 
14 // Universal include for all versions of OpenCV
15 #include <mrpt/otherlibs/do_opencv_includes.h>
16 
17 using namespace mrpt;
18 using namespace mrpt::utils;
19 using namespace mrpt::vision;
20 using namespace mrpt::system;
21 using namespace std;
22 
23 /************************************************************************************************
24 * extractFeaturesORB *
25 ************************************************************************************************/
26 void CFeatureExtraction::extractFeaturesORB(
27  const mrpt::utils::CImage & inImg,
28  CFeatureList & feats,
29  const unsigned int init_ID,
30  const unsigned int nDesiredFeatures,
31  const TImageROI & ROI ) const
32 {
33  MRPT_UNUSED_PARAM(ROI);
35 
36 #if MRPT_HAS_OPENCV
37 # if MRPT_OPENCV_VERSION_NUM < 0x240
38  THROW_EXCEPTION("This function requires OpenCV > 2.4.0")
39 # else
40 
41  using namespace cv;
42 
43  vector<KeyPoint> cv_feats; // OpenCV keypoint output vector
44  Mat cv_descs; // OpenCV descriptor output
45 
46  const bool use_precomputed_feats = feats.size() > 0;
47 
48  if( use_precomputed_feats )
49  {
50  cv_feats.resize( feats.size() );
51  for( size_t k = 0; k < cv_feats.size(); ++k )
52  {
53  cv_feats[k].pt.x = feats[k]->x;
54  cv_feats[k].pt.y = feats[k]->y;
55  }
56  }
57 
58  // Make sure we operate on a gray-scale version of the image:
59  const CImage inImg_gray( inImg, FAST_REF_OR_CONVERT_TO_GRAY );
60  const Mat cvImg = cv::cvarrToMat( inImg_gray.getAs<IplImage>() );
61 
62  // The detector and descriptor
63 # if MRPT_OPENCV_VERSION_NUM < 0x300
64  Ptr<Feature2D> orb = Algorithm::create<Feature2D>("Feature2D.ORB");
65  orb->operator()( cvImg, Mat(), cv_feats, cv_descs, use_precomputed_feats );
66 #else
67  const size_t n_feats_2_extract = nDesiredFeatures == 0 ? 1000 : 3*nDesiredFeatures;
68  Ptr<cv::ORB> orb = cv::ORB::create( n_feats_2_extract, options.ORBOptions.scale_factor, options.ORBOptions.n_levels );
69  orb->detectAndCompute(cvImg, Mat(), cv_feats, cv_descs, use_precomputed_feats );
70 #endif
71 
72  const size_t n_feats = cv_feats.size();
73 
74  // if we had input features, just convert cv_feats to CFeatures and return
75  const unsigned int patch_size_2 = options.patchSize/2;
76  unsigned int f_id = init_ID;
77  if( use_precomputed_feats )
78  {
79  for( size_t k = 0; k < n_feats; ++k )
80  {
81  feats[k]->descriptors.ORB.resize( cv_descs.cols );
82  for( int m = 0; m < cv_descs.cols; ++m )
83  feats[k]->descriptors.ORB[m] = cv_descs.at<uchar>(k,m); // to do: memcopy
84 
85  /*
86  feats[k].response = cv_feats[k].response;
87  feats[k].scale = cv_feats[k].size;
88  feats[k].angle = cv_feats[k].orientation;
89  feats[k].ID = f_id++;
90  */
91  feats[k]->type = featORB;
92 
93  if( options.ORBOptions.extract_patch && options.patchSize > 0 )
94  {
95  inImg.extract_patch(
96  feats[k]->patch,
97  round( feats[k]->x ) - patch_size_2,
98  round( feats[k]->y ) - patch_size_2,
99  options.patchSize,
100  options.patchSize );
101  }
102  }
103  return;
104  }
105 
106  // 1) Sort the fearues by "response": It's ~100 times faster to sort a list of
107  // indices "sorted_indices" than sorting directly the actual list of features "cv_feats"
108  std::vector<size_t> sorted_indices(n_feats);
109  for (size_t i=0;i<n_feats;i++) sorted_indices[i]=i;
110  std::sort( sorted_indices.begin(), sorted_indices.end(), KeypointResponseSorter< vector<KeyPoint> >(cv_feats) );
111 
112  // 2) Filter by "min-distance" (in options.ORBOptions.min_distance)
113  // 3) Convert to MRPT CFeatureList format.
114  // Steps 2 & 3 are done together in the while() below.
115  // The "min-distance" filter is done by means of a 2D binary matrix where each cell is marked when one
116  // feature falls within it. This is not exactly the same than a pure "min-distance" but is pretty close
117  // and for large numbers of features is much faster than brute force search of kd-trees.
118  // (An intermediate approach would be the creation of a mask image updated for each accepted feature, etc.)
119 
120  const bool do_filter_min_dist = options.ORBOptions.min_distance > 1;
121  const unsigned int occupied_grid_cell_size = options.ORBOptions.min_distance/2.0;
122  const float occupied_grid_cell_size_inv = 1.0f/occupied_grid_cell_size;
123 
124  unsigned int grid_lx = !do_filter_min_dist ? 1 : (unsigned int)(1 + inImg.getWidth() * occupied_grid_cell_size_inv);
125  unsigned int grid_ly = !do_filter_min_dist ? 1 : (unsigned int)(1 + inImg.getHeight() * occupied_grid_cell_size_inv );
126 
127  mrpt::math::CMatrixBool occupied_sections(grid_lx,grid_ly); // See the comments above for an explanation.
128  occupied_sections.fillAll(false);
129 
130  const size_t n_max_feats = nDesiredFeatures > 0 ? std::min(size_t(nDesiredFeatures),n_feats) : n_feats;
131 
132  if( !options.addNewFeatures )
133  feats.clear();
134  // feats.reserve( feats.size() + n_max_feats );
135 
136  const size_t imgH = inImg.getHeight();
137  const size_t imgW = inImg.getWidth();
138  size_t k = 0;
139  size_t c_feats = 0;
140  while( c_feats < n_max_feats && k < n_feats )
141  {
142  const size_t idx = sorted_indices[k++];
143  const KeyPoint & kp = cv_feats[ idx ];
144  if( options.ORBOptions.extract_patch && options.patchSize > 0 )
145  {
146  // check image boundaries for extracting the patch
147  const int xBorderInf = (int)floor( kp.pt.x - patch_size_2 );
148  const int xBorderSup = (int)floor( kp.pt.x + patch_size_2 );
149  const int yBorderInf = (int)floor( kp.pt.y - patch_size_2 );
150  const int yBorderSup = (int)floor( kp.pt.y + patch_size_2 );
151 
152  if (!( xBorderSup < (int)imgW && xBorderInf > 0 && yBorderSup < (int)imgH && yBorderInf > 0 ))
153  continue; // nope, skip.
154  }
155 
156  if (do_filter_min_dist)
157  {
158  // Check the min-distance:
159  const size_t section_idx_x = size_t(kp.pt.x * occupied_grid_cell_size_inv);
160  const size_t section_idx_y = size_t(kp.pt.y * occupied_grid_cell_size_inv);
161 
162  if (occupied_sections(section_idx_x,section_idx_y))
163  continue; // Already occupied! skip.
164 
165  // Mark section as occupied
166  occupied_sections.set_unsafe(section_idx_x,section_idx_y, true);
167  if (section_idx_x>0) occupied_sections.set_unsafe(section_idx_x-1,section_idx_y, true);
168  if (section_idx_y>0) occupied_sections.set_unsafe(section_idx_x,section_idx_y-1, true);
169  if (section_idx_x<grid_lx-1) occupied_sections.set_unsafe(section_idx_x+1,section_idx_y, true);
170  if (section_idx_y<grid_ly-1) occupied_sections.set_unsafe(section_idx_x,section_idx_y+1, true);
171  }
172 
173  // All tests passed: add new feature:
174  CFeaturePtr ft = CFeature::Create();
175  ft->type = featORB;
176  ft->ID = f_id++;
177  ft->x = kp.pt.x;
178  ft->y = kp.pt.y;
179  ft->response = kp.response;
180  ft->orientation = kp.angle;
181  ft->scale = kp.octave;
182  ft->patchSize = 0;
183 
184  // descriptor
185  ft->descriptors.ORB.resize( cv_descs.cols );
186  for( int m = 0; m < cv_descs.cols; ++m )
187  ft->descriptors.ORB[m] = cv_descs.at<uchar>(idx,m);
188 
189  if( options.ORBOptions.extract_patch && options.patchSize > 0 )
190  {
191  ft->patchSize = options.patchSize; // The size of the feature patch
192 
193  inImg.extract_patch(
194  ft->patch,
195  round( ft->x ) - patch_size_2,
196  round( ft->y ) - patch_size_2,
197  options.patchSize,
198  options.patchSize ); // Image patch surronding the feature
199  }
200 
201  feats.push_back( ft );
202  c_feats++;
203  }
204 # endif
205 #endif
206  MRPT_END
207 }
208 
209 void CFeatureExtraction::internal_computeORBDescriptors(
210  const CImage & in_img,
211  CFeatureList & in_features) const
212 {
213 #if MRPT_HAS_OPENCV
214 # if MRPT_OPENCV_VERSION_NUM < 0x240
215  THROW_EXCEPTION("This function requires OpenCV > 2.4.0")
216 # else
217  using namespace cv;
218 
219  const size_t n_feats = in_features.size();
220  const CImage inImg_gray( in_img, FAST_REF_OR_CONVERT_TO_GRAY );
221 
222  // convert from CFeatureList to vector<KeyPoint>
223  vector<KeyPoint> cv_feats( n_feats );
224  for( size_t k = 0; k < n_feats; ++k )
225  {
226  KeyPoint & kp = cv_feats[k];
227  kp.pt.x = in_features[k]->x;
228  kp.pt.y = in_features[k]->y;
229  kp.angle = in_features[k]->orientation;
230  kp.size = in_features[k]->scale;
231  } // end-for
232 
233  Mat cvImg(cv::cvarrToMat(inImg_gray.getAs<IplImage>()));
234  Mat cv_descs;
235 
236 # if MRPT_OPENCV_VERSION_NUM < 0x300
237  Ptr<Feature2D> orb = Algorithm::create<Feature2D>("Feature2D.ORB");
238  orb->operator()( cvImg, Mat(), cv_feats, cv_descs, true /* use_precomputed_feats */ );
239 #else
240  Ptr<cv::ORB> orb = cv::ORB::create( n_feats, options.ORBOptions.scale_factor, options.ORBOptions.n_levels );
241  orb->detectAndCompute(cvImg, Mat(), cv_feats, cv_descs, true /* use_precomputed_feats */ );
242 #endif
243 
244  // add descriptor to CFeatureList
245  for( size_t k = 0; k < n_feats; ++k )
246  {
247  in_features[k]->descriptors.ORB.resize( cv_descs.cols );
248  for( int i = 0; i < cv_descs.cols; ++i )
249  in_features[k]->descriptors.ORB[i] = cv_descs.at<uchar>(k,i);
250 
251  } // end-for
252 # endif
253 #endif
254 
255 } // end-internal_computeORBImageDescriptors
256 
257 
Classes for serialization, sockets, ini-file manipulation, streams, list of properties-values, timewatch, extensions to STL.
Definition: zip.h:16
#define min(a, b)
Declares a matrix of booleans (non serializable).
This namespace provides a OS-independent interface to many useful functions: filenames manipulation...
Definition: math_frwds.h:29
A class for storing images as grayscale or RGB bitmaps.
Definition: CImage.h:101
size_t size() const
Definition: CFeature.h:280
#define THROW_EXCEPTION(msg)
STL namespace.
const T * getAs() const
Returns a pointer to a const T* containing the image - the idea is to call like "img.getAs<IplImage>()" so we can avoid here including OpenCV&#39;s headers.
Definition: CImage.h:517
A helper struct to sort keypoints by their response: It can be used with these types: ...
A structure for defining a ROI within an image.
void resize(size_t N)
Definition: CFeature.h:283
#define MRPT_END
#define MRPT_UNUSED_PARAM(a)
Can be used to avoid "not used parameters" warnings from the compiler.
Classes for computer vision, detectors, features, etc.
void push_back(const CFeaturePtr &f)
Definition: CFeature.h:285
void set_unsafe(size_t row, size_t col, const T &v)
Fast but unsafe method to write a value in the matrix.
A list of visual features, to be used as output by detectors, as input/output by trackers, etc.
Definition: CFeature.h:211
#define MRPT_START
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
void extract_patch(CImage &patch, const unsigned int col=0, const unsigned int row=0, const unsigned int width=1, const unsigned int height=1) const
Extract a patch from this image, saveing it into "patch" (its previous contents will be overwritten)...
Definition: CImage.cpp:1368
int round(const T value)
Returns the closer integer (int) to x.
Definition: round.h:26
GLenum GLint GLint y
Definition: glext.h:3516
ORB detector and descriptor, OpenCV&#39;s implementation ("ORB: an efficient alternative to SIFT or SURF"...
GLenum GLint x
Definition: glext.h:3516
size_t getWidth() const MRPT_OVERRIDE
Returns the width of the image in pixels.
Definition: CImage.cpp:855
size_t getHeight() const MRPT_OVERRIDE
Returns the height of the image in pixels.
Definition: CImage.cpp:884
void fillAll(const T &val)



Page generated by Doxygen 1.8.14 for MRPT 1.5.6 Git: 4c65e8431 Tue Apr 24 08:18:17 2018 +0200 at lun oct 28 01:35:26 CET 2019