Main MRPT website > C++ reference for MRPT 1.9.9
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-2018, 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::vision;
19 using namespace mrpt::img;
20 using namespace mrpt::system;
21 using namespace mrpt::img;
22 using namespace std;
23 
24 /************************************************************************************************
25 * extractFeaturesORB
26 **
27 ************************************************************************************************/
29  const mrpt::img::CImage& inImg, CFeatureList& feats,
30  const unsigned int init_ID, 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 =
68  nDesiredFeatures == 0 ? 1000 : 3 * nDesiredFeatures;
69  Ptr<cv::ORB> orb = cv::ORB::create(
70  n_feats_2_extract, options.ORBOptions.scale_factor,
71  options.ORBOptions.n_levels);
72  orb->detectAndCompute(
73  cvImg, Mat(), cv_feats, cv_descs, use_precomputed_feats);
74 #endif
75 
76  const size_t n_feats = cv_feats.size();
77 
78  // if we had input features, just convert cv_feats to CFeatures and return
79  const unsigned int patch_size_2 = options.patchSize / 2;
80  unsigned int f_id = init_ID;
81  if (use_precomputed_feats)
82  {
83  for (size_t k = 0; k < n_feats; ++k)
84  {
85  feats[k]->descriptors.ORB.resize(cv_descs.cols);
86  for (int m = 0; m < cv_descs.cols; ++m)
87  feats[k]->descriptors.ORB[m] =
88  cv_descs.at<uchar>(k, m); // to do: memcopy
89 
90  /*
91  feats[k].response = cv_feats[k].response;
92  feats[k].scale = cv_feats[k].size;
93  feats[k].angle = cv_feats[k].orientation;
94  feats[k].ID = f_id++;
95  */
96  feats[k]->type = featORB;
97 
98  if (options.ORBOptions.extract_patch && options.patchSize > 0)
99  {
100  inImg.extract_patch(
101  feats[k]->patch, round(feats[k]->x) - patch_size_2,
102  round(feats[k]->y) - patch_size_2, options.patchSize,
103  options.patchSize);
104  }
105  }
106  return;
107  }
108 
109  // 1) Sort the fearues by "response": It's ~100 times faster to sort a list
110  // of
111  // indices "sorted_indices" than sorting directly the actual list of
112  // features "cv_feats"
113  std::vector<size_t> sorted_indices(n_feats);
114  for (size_t i = 0; i < n_feats; i++) sorted_indices[i] = i;
115  std::sort(
116  sorted_indices.begin(), sorted_indices.end(),
118 
119  // 2) Filter by "min-distance" (in options.ORBOptions.min_distance)
120  // 3) Convert to MRPT CFeatureList format.
121  // Steps 2 & 3 are done together in the while() below.
122  // The "min-distance" filter is done by means of a 2D binary matrix where
123  // each cell is marked when one
124  // feature falls within it. This is not exactly the same than a pure
125  // "min-distance" but is pretty close
126  // and for large numbers of features is much faster than brute force search
127  // of kd-trees.
128  // (An intermediate approach would be the creation of a mask image updated
129  // for each accepted feature, etc.)
130 
131  const bool do_filter_min_dist = options.ORBOptions.min_distance > 1;
132  const unsigned int occupied_grid_cell_size =
133  options.ORBOptions.min_distance / 2.0;
134  const float occupied_grid_cell_size_inv = 1.0f / occupied_grid_cell_size;
135 
136  unsigned int grid_lx =
137  !do_filter_min_dist
138  ? 1
139  : (unsigned int)(1 + inImg.getWidth() * occupied_grid_cell_size_inv);
140  unsigned int grid_ly =
141  !do_filter_min_dist
142  ? 1
143  : (unsigned int)(1 + inImg.getHeight() * occupied_grid_cell_size_inv);
144 
145  mrpt::math::CMatrixBool occupied_sections(
146  grid_lx, grid_ly); // See the comments above for an explanation.
147  occupied_sections.fillAll(false);
148 
149  const size_t n_max_feats = nDesiredFeatures > 0
150  ? std::min(size_t(nDesiredFeatures), n_feats)
151  : n_feats;
152 
153  if (!options.addNewFeatures) feats.clear();
154  // feats.reserve( feats.size() + n_max_feats );
155 
156  const size_t imgH = inImg.getHeight();
157  const size_t imgW = inImg.getWidth();
158  size_t k = 0;
159  size_t c_feats = 0;
160  while (c_feats < n_max_feats && k < n_feats)
161  {
162  const size_t idx = sorted_indices[k++];
163  const KeyPoint& kp = cv_feats[idx];
164  if (options.ORBOptions.extract_patch && options.patchSize > 0)
165  {
166  // check image boundaries for extracting the patch
167  const int xBorderInf = (int)floor(kp.pt.x - patch_size_2);
168  const int xBorderSup = (int)floor(kp.pt.x + patch_size_2);
169  const int yBorderInf = (int)floor(kp.pt.y - patch_size_2);
170  const int yBorderSup = (int)floor(kp.pt.y + patch_size_2);
171 
172  if (!(xBorderSup < (int)imgW && xBorderInf > 0 &&
173  yBorderSup < (int)imgH && yBorderInf > 0))
174  continue; // nope, skip.
175  }
176 
177  if (do_filter_min_dist)
178  {
179  // Check the min-distance:
180  const size_t section_idx_x =
181  size_t(kp.pt.x * occupied_grid_cell_size_inv);
182  const size_t section_idx_y =
183  size_t(kp.pt.y * occupied_grid_cell_size_inv);
184 
185  if (occupied_sections(section_idx_x, section_idx_y))
186  continue; // Already occupied! skip.
187 
188  // Mark section as occupied
189  occupied_sections.set_unsafe(section_idx_x, section_idx_y, true);
190  if (section_idx_x > 0)
191  occupied_sections.set_unsafe(
192  section_idx_x - 1, section_idx_y, true);
193  if (section_idx_y > 0)
194  occupied_sections.set_unsafe(
195  section_idx_x, section_idx_y - 1, true);
196  if (section_idx_x < grid_lx - 1)
197  occupied_sections.set_unsafe(
198  section_idx_x + 1, section_idx_y, true);
199  if (section_idx_y < grid_ly - 1)
200  occupied_sections.set_unsafe(
201  section_idx_x, section_idx_y + 1, true);
202  }
203 
204  // All tests passed: add new feature:
205  CFeature::Ptr ft = mrpt::make_aligned_shared<CFeature>();
206  ft->type = featORB;
207  ft->ID = f_id++;
208  ft->x = kp.pt.x;
209  ft->y = kp.pt.y;
210  ft->response = kp.response;
211  ft->orientation = kp.angle;
212  ft->scale = kp.octave;
213  ft->patchSize = 0;
214 
215  // descriptor
216  ft->descriptors.ORB.resize(cv_descs.cols);
217  for (int m = 0; m < cv_descs.cols; ++m)
218  ft->descriptors.ORB[m] = cv_descs.at<uchar>(idx, m);
219 
220  if (options.ORBOptions.extract_patch && options.patchSize > 0)
221  {
222  ft->patchSize = options.patchSize; // The size of the feature patch
223 
224  inImg.extract_patch(
225  ft->patch, round(ft->x) - patch_size_2,
226  round(ft->y) - patch_size_2, options.patchSize,
227  options.patchSize); // Image patch surronding the feature
228  }
229 
230  feats.push_back(ft);
231  c_feats++;
232  }
233 #endif
234 #endif
235  MRPT_END
236 }
237 
239  const CImage& in_img, CFeatureList& in_features) const
240 {
241 #if MRPT_HAS_OPENCV
242 #if MRPT_OPENCV_VERSION_NUM < 0x240
243  THROW_EXCEPTION("This function requires OpenCV > 2.4.0");
244 #else
245  using namespace cv;
246 
247  const size_t n_feats = in_features.size();
248  const CImage inImg_gray(in_img, FAST_REF_OR_CONVERT_TO_GRAY);
249 
250  // convert from CFeatureList to vector<KeyPoint>
251  vector<KeyPoint> cv_feats(n_feats);
252  for (size_t k = 0; k < n_feats; ++k)
253  {
254  KeyPoint& kp = cv_feats[k];
255  kp.pt.x = in_features[k]->x;
256  kp.pt.y = in_features[k]->y;
257  kp.angle = in_features[k]->orientation;
258  kp.size = in_features[k]->scale;
259  } // end-for
260 
261  Mat cvImg(cv::cvarrToMat(inImg_gray.getAs<IplImage>()));
262  Mat cv_descs;
263 
264 #if MRPT_OPENCV_VERSION_NUM < 0x300
265  Ptr<Feature2D> orb = Algorithm::create<Feature2D>("Feature2D.ORB");
266  orb->operator()(
267  cvImg, Mat(), cv_feats, cv_descs, true /* use_precomputed_feats */);
268 #else
269  Ptr<cv::ORB> orb = cv::ORB::create(
270  n_feats, options.ORBOptions.scale_factor, options.ORBOptions.n_levels);
271  orb->detectAndCompute(
272  cvImg, Mat(), cv_feats, cv_descs, true /* use_precomputed_feats */);
273 #endif
274 
275  // add descriptor to CFeatureList
276  for (size_t k = 0; k < n_feats; ++k)
277  {
278  in_features[k]->descriptors.ORB.resize(cv_descs.cols);
279  for (int i = 0; i < cv_descs.cols; ++i)
280  in_features[k]->descriptors.ORB[i] = cv_descs.at<uchar>(k, i);
281 
282  } // end-for
283 #endif
284 #endif
285 
286 } // end-internal_computeORBImageDescriptors
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: img/CImage.h:599
#define MRPT_START
Definition: exceptions.h:262
#define min(a, b)
Declares a matrix of booleans (non serializable).
void internal_computeORBDescriptors(const mrpt::img::CImage &in_img, CFeatureList &in_features) const
Compute the ORB descriptor of the provided features into the input image.
This namespace provides a OS-independent interface to many useful functions: filenames manipulation...
Definition: math_frwds.h:25
#define THROW_EXCEPTION(msg)
Definition: exceptions.h:41
size_t size() const
Definition: CFeature.h:386
size_t getHeight() const override
Returns the height of the image in pixels.
Definition: CImage.cpp:892
STL namespace.
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.
size_t getWidth() const override
Returns the width of the image in pixels.
Definition: CImage.cpp:864
void resize(size_t N)
Definition: CFeature.h:392
Classes for computer vision, detectors, features, etc.
Definition: CCamModel.h:20
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:304
void extractFeaturesORB(const mrpt::img::CImage &img, CFeatureList &feats, const unsigned int init_ID=0, const unsigned int nDesiredFeatures=0, const TImageROI &ROI=TImageROI()) const
Extract features from the image based on the ORB method.
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
#define MRPT_END
Definition: exceptions.h:266
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:1359
GLenum GLint GLint y
Definition: glext.h:3538
ORB detector and descriptor, OpenCV&#39;s implementation ("ORB: an efficient alternative to SIFT o...
GLenum GLint x
Definition: glext.h:3538
void push_back(const CFeature::Ptr &f)
Definition: CFeature.h:398
A class for storing images as grayscale or RGB bitmaps.
Definition: img/CImage.h:130
void fillAll(const T &val)
#define MRPT_UNUSED_PARAM(a)
Determines whether this is an X86 or AMD64 platform.
Definition: common.h:186
int round(const T value)
Returns the closer integer (int) to x.
Definition: round.h:23



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: ad3a9d8ae Tue May 1 23:10:22 2018 -0700 at lun oct 28 00:14:14 CET 2019