1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html
4 
5 // This code is also subject to the license terms in the LICENSE_WillowGarage.md file found in this module's directory
6 
7 #include "precomp.hpp"
8 
9 namespace cv
10 {
11 namespace linemod
12 {
13 
14 // struct Feature
15 
16 /**
17  * \brief Get the label [0,8) of the single bit set in quantized.
18  */
getLabel(int quantized)19 static inline int getLabel(int quantized)
20 {
21   switch (quantized)
22   {
23     case 1:   return 0;
24     case 2:   return 1;
25     case 4:   return 2;
26     case 8:   return 3;
27     case 16:  return 4;
28     case 32:  return 5;
29     case 64:  return 6;
30     case 128: return 7;
31     default:
32       CV_Error(Error::StsBadArg, "Invalid value of quantized parameter");
33   }
34 }
35 
read(const FileNode & fn)36 void Feature::read(const FileNode& fn)
37 {
38   FileNodeIterator fni = fn.begin();
39   fni >> x >> y >> label;
40 }
41 
write(FileStorage & fs) const42 void Feature::write(FileStorage& fs) const
43 {
44   fs << "[:" << x << y << label << "]";
45 }
46 
47 // struct Template
48 
49 /**
50  * \brief Crop a set of overlapping templates from different modalities.
51  *
52  * \param[in,out] templates Set of templates representing the same object view.
53  *
54  * \return The bounding box of all the templates in original image coordinates.
55  */
cropTemplates(std::vector<Template> & templates)56 static Rect cropTemplates(std::vector<Template>& templates)
57 {
58   int min_x = std::numeric_limits<int>::max();
59   int min_y = std::numeric_limits<int>::max();
60   int max_x = std::numeric_limits<int>::min();
61   int max_y = std::numeric_limits<int>::min();
62 
63   // First pass: find min/max feature x,y over all pyramid levels and modalities
64   for (int i = 0; i < (int)templates.size(); ++i)
65   {
66     Template& templ = templates[i];
67 
68     for (int j = 0; j < (int)templ.features.size(); ++j)
69     {
70       int x = templ.features[j].x << templ.pyramid_level;
71       int y = templ.features[j].y << templ.pyramid_level;
72       min_x = std::min(min_x, x);
73       min_y = std::min(min_y, y);
74       max_x = std::max(max_x, x);
75       max_y = std::max(max_y, y);
76     }
77   }
78 
79   /// @todo Why require even min_x, min_y?
80   if (min_x % 2 == 1) --min_x;
81   if (min_y % 2 == 1) --min_y;
82 
83   // Second pass: set width/height and shift all feature positions
84   for (int i = 0; i < (int)templates.size(); ++i)
85   {
86     Template& templ = templates[i];
87     templ.width = (max_x - min_x) >> templ.pyramid_level;
88     templ.height = (max_y - min_y) >> templ.pyramid_level;
89     int offset_x = min_x >> templ.pyramid_level;
90     int offset_y = min_y >> templ.pyramid_level;
91 
92     for (int j = 0; j < (int)templ.features.size(); ++j)
93     {
94       templ.features[j].x -= offset_x;
95       templ.features[j].y -= offset_y;
96     }
97   }
98 
99   return Rect(min_x, min_y, max_x - min_x, max_y - min_y);
100 }
101 
read(const FileNode & fn)102 void Template::read(const FileNode& fn)
103 {
104   width = fn["width"];
105   height = fn["height"];
106   pyramid_level = fn["pyramid_level"];
107 
108   FileNode features_fn = fn["features"];
109   features.resize(features_fn.size());
110   FileNodeIterator it = features_fn.begin(), it_end = features_fn.end();
111   for (int i = 0; it != it_end; ++it, ++i)
112   {
113     features[i].read(*it);
114   }
115 }
116 
write(FileStorage & fs) const117 void Template::write(FileStorage& fs) const
118 {
119   fs << "width" << width;
120   fs << "height" << height;
121   fs << "pyramid_level" << pyramid_level;
122 
123   fs << "features" << "[";
124   for (int i = 0; i < (int)features.size(); ++i)
125   {
126     features[i].write(fs);
127   }
128   fs << "]"; // features
129 }
130 
131 /****************************************************************************************\
132 *                             Modality interfaces                                        *
133 \****************************************************************************************/
134 
selectScatteredFeatures(const std::vector<Candidate> & candidates,std::vector<Feature> & features,size_t num_features,float distance)135 void QuantizedPyramid::selectScatteredFeatures(const std::vector<Candidate>& candidates,
136                                                std::vector<Feature>& features,
137                                                size_t num_features, float distance)
138 {
139   features.clear();
140   float distance_sq = distance * distance;
141   int i = 0;
142   while (features.size() < num_features)
143   {
144     Candidate c = candidates[i];
145 
146     // Add if sufficient distance away from any previously chosen feature
147     bool keep = true;
148     for (int j = 0; (j < (int)features.size()) && keep; ++j)
149     {
150       Feature f = features[j];
151       keep = (c.f.x - f.x)*(c.f.x - f.x) + (c.f.y - f.y)*(c.f.y - f.y) >= distance_sq;
152     }
153     if (keep)
154       features.push_back(c.f);
155 
156     if (++i == (int)candidates.size())
157     {
158       // Start back at beginning, and relax required distance
159       i = 0;
160       distance -= 1.0f;
161       distance_sq = distance * distance;
162     }
163   }
164 }
165 
create(const String & modality_type)166 Ptr<Modality> Modality::create(const String& modality_type)
167 {
168   if (modality_type == "ColorGradient")
169     return makePtr<ColorGradient>();
170   else if (modality_type == "DepthNormal")
171     return makePtr<DepthNormal>();
172   else
173     return Ptr<Modality>();
174 }
175 
create(const FileNode & fn)176 Ptr<Modality> Modality::create(const FileNode& fn)
177 {
178   String type = fn["type"];
179   Ptr<Modality> modality = create(type);
180   modality->read(fn);
181   return modality;
182 }
183 
colormap(const Mat & quantized,Mat & dst)184 void colormap(const Mat& quantized, Mat& dst)
185 {
186   std::vector<Vec3b> lut(8);
187   lut[0] = Vec3b(  0,   0, 255);
188   lut[1] = Vec3b(  0, 170, 255);
189   lut[2] = Vec3b(  0, 255, 170);
190   lut[3] = Vec3b(  0, 255,   0);
191   lut[4] = Vec3b(170, 255,   0);
192   lut[5] = Vec3b(255, 170,   0);
193   lut[6] = Vec3b(255,   0,   0);
194   lut[7] = Vec3b(255,   0, 170);
195 
196   dst = Mat::zeros(quantized.size(), CV_8UC3);
197   for (int r = 0; r < dst.rows; ++r)
198   {
199     const uchar* quant_r = quantized.ptr(r);
200     Vec3b* dst_r = dst.ptr<Vec3b>(r);
201     for (int c = 0; c < dst.cols; ++c)
202     {
203       uchar q = quant_r[c];
204       if (q)
205         dst_r[c] = lut[getLabel(q)];
206     }
207   }
208 }
209 
drawFeatures(InputOutputArray img,const std::vector<Template> & templates,const Point2i & tl,int size)210 void drawFeatures(InputOutputArray img, const std::vector<Template>& templates, const Point2i& tl, int size)
211 {
212 #ifdef HAVE_OPENCV_IMGPROC
213     static Scalar colors[] = {{0, 0, 255}, {0, 255, 0}};
214     static int markers[] = {MARKER_SQUARE, MARKER_DIAMOND};
215 
216     int modality = 0;
217     for(const Template& t : templates)
218     {
219         if(t.pyramid_level != 0) continue;
220 
221         for(const Feature& f : t.features)
222         {
223             drawMarker(img, tl + Point(f.x, f.y), colors[int(modality != 0)], markers[int(modality != 0)], size);
224         }
225 
226         modality++;
227     }
228 #else
229     CV_Assert(false, "functionality needs imgproc module");
230 #endif
231 }
232 
233 /****************************************************************************************\
234 *                             Color gradient modality                                    *
235 \****************************************************************************************/
236 
237 // Forward declaration
238 void hysteresisGradient(Mat& magnitude, Mat& angle,
239                         Mat& ap_tmp, float threshold);
240 
241 /**
242  * \brief Compute quantized orientation image from color image.
243  *
244  * Implements section 2.2 "Computing the Gradient Orientations."
245  *
246  * \param[in]  src       The source 8-bit, 3-channel image.
247  * \param[out] magnitude Destination floating-point array of squared magnitudes.
248  * \param[out] angle     Destination 8-bit array of orientations. Each bit
249  *                       represents one bin of the orientation space.
250  * \param      threshold Magnitude threshold. Keep only gradients whose norms are
251  *                       larger than this.
252  */
quantizedOrientations(const Mat & src,Mat & magnitude,Mat & angle,float threshold)253 static void quantizedOrientations(const Mat& src, Mat& magnitude,
254                            Mat& angle, float threshold)
255 {
256   magnitude.create(src.size(), CV_32F);
257 
258   // Allocate temporary buffers
259   Size size = src.size();
260   Mat sobel_3dx; // per-channel horizontal derivative
261   Mat sobel_3dy; // per-channel vertical derivative
262   Mat sobel_dx(size, CV_32F);      // maximum horizontal derivative
263   Mat sobel_dy(size, CV_32F);      // maximum vertical derivative
264   Mat sobel_ag;  // final gradient orientation (unquantized)
265   Mat smoothed;
266 
267   // Compute horizontal and vertical image derivatives on all color channels separately
268   static const int KERNEL_SIZE = 7;
269   // For some reason cvSmooth/cv::GaussianBlur, cvSobel/cv::Sobel have different defaults for border handling...
270   GaussianBlur(src, smoothed, Size(KERNEL_SIZE, KERNEL_SIZE), 0, 0, BORDER_REPLICATE);
271   Sobel(smoothed, sobel_3dx, CV_16S, 1, 0, 3, 1.0, 0.0, BORDER_REPLICATE);
272   Sobel(smoothed, sobel_3dy, CV_16S, 0, 1, 3, 1.0, 0.0, BORDER_REPLICATE);
273 
274   short * ptrx  = (short *)sobel_3dx.data;
275   short * ptry  = (short *)sobel_3dy.data;
276   float * ptr0x = (float *)sobel_dx.data;
277   float * ptr0y = (float *)sobel_dy.data;
278   float * ptrmg = (float *)magnitude.data;
279 
280   const int length1 = static_cast<int>(sobel_3dx.step1());
281   const int length2 = static_cast<int>(sobel_3dy.step1());
282   const int length3 = static_cast<int>(sobel_dx.step1());
283   const int length4 = static_cast<int>(sobel_dy.step1());
284   const int length5 = static_cast<int>(magnitude.step1());
285   const int length0 = sobel_3dy.cols * 3;
286 
287   for (int r = 0; r < sobel_3dy.rows; ++r)
288   {
289     int ind = 0;
290 
291     for (int i = 0; i < length0; i += 3)
292     {
293       // Use the gradient orientation of the channel whose magnitude is largest
294       int mag1 = ptrx[i+0] * ptrx[i + 0] + ptry[i + 0] * ptry[i + 0];
295       int mag2 = ptrx[i+1] * ptrx[i + 1] + ptry[i + 1] * ptry[i + 1];
296       int mag3 = ptrx[i+2] * ptrx[i + 2] + ptry[i + 2] * ptry[i + 2];
297 
298       if (mag1 >= mag2 && mag1 >= mag3)
299       {
300         ptr0x[ind] = ptrx[i];
301         ptr0y[ind] = ptry[i];
302         ptrmg[ind] = (float)mag1;
303       }
304       else if (mag2 >= mag1 && mag2 >= mag3)
305       {
306         ptr0x[ind] = ptrx[i + 1];
307         ptr0y[ind] = ptry[i + 1];
308         ptrmg[ind] = (float)mag2;
309       }
310       else
311       {
312         ptr0x[ind] = ptrx[i + 2];
313         ptr0y[ind] = ptry[i + 2];
314         ptrmg[ind] = (float)mag3;
315       }
316       ++ind;
317     }
318     ptrx += length1;
319     ptry += length2;
320     ptr0x += length3;
321     ptr0y += length4;
322     ptrmg += length5;
323   }
324 
325   // Calculate the final gradient orientations
326   phase(sobel_dx, sobel_dy, sobel_ag, true);
327   hysteresisGradient(magnitude, angle, sobel_ag, threshold * threshold);
328 }
329 
hysteresisGradient(Mat & magnitude,Mat & quantized_angle,Mat & angle,float threshold)330 void hysteresisGradient(Mat& magnitude, Mat& quantized_angle,
331                         Mat& angle, float threshold)
332 {
333   // Quantize 360 degree range of orientations into 16 buckets
334   // Note that [0, 11.25), [348.75, 360) both get mapped in the end to label 0,
335   // for stability of horizontal and vertical features.
336   Mat_<unsigned char> quantized_unfiltered;
337   angle.convertTo(quantized_unfiltered, CV_8U, 16.0 / 360.0);
338 
339   // Zero out top and bottom rows
340   /// @todo is this necessary, or even correct?
341   memset(quantized_unfiltered.ptr(), 0, quantized_unfiltered.cols);
342   memset(quantized_unfiltered.ptr(quantized_unfiltered.rows - 1), 0, quantized_unfiltered.cols);
343   // Zero out first and last columns
344   for (int r = 0; r < quantized_unfiltered.rows; ++r)
345   {
346     quantized_unfiltered(r, 0) = 0;
347     quantized_unfiltered(r, quantized_unfiltered.cols - 1) = 0;
348   }
349 
350   // Mask 16 buckets into 8 quantized orientations
351   for (int r = 1; r < angle.rows - 1; ++r)
352   {
353     uchar* quant_r = quantized_unfiltered.ptr<uchar>(r);
354     for (int c = 1; c < angle.cols - 1; ++c)
355     {
356       quant_r[c] &= 7;
357     }
358   }
359 
360   // Filter the raw quantized image. Only accept pixels where the magnitude is above some
361   // threshold, and there is local agreement on the quantization.
362   quantized_angle = Mat::zeros(angle.size(), CV_8U);
363   for (int r = 1; r < angle.rows - 1; ++r)
364   {
365     float* mag_r = magnitude.ptr<float>(r);
366 
367     for (int c = 1; c < angle.cols - 1; ++c)
368     {
369       if (mag_r[c] > threshold)
370       {
371   // Compute histogram of quantized bins in 3x3 patch around pixel
372         int histogram[8] = {0, 0, 0, 0, 0, 0, 0, 0};
373 
374         uchar* patch3x3_row = &quantized_unfiltered(r-1, c-1);
375         histogram[patch3x3_row[0]]++;
376         histogram[patch3x3_row[1]]++;
377         histogram[patch3x3_row[2]]++;
378 
379   patch3x3_row += quantized_unfiltered.step1();
380         histogram[patch3x3_row[0]]++;
381         histogram[patch3x3_row[1]]++;
382         histogram[patch3x3_row[2]]++;
383 
384   patch3x3_row += quantized_unfiltered.step1();
385         histogram[patch3x3_row[0]]++;
386         histogram[patch3x3_row[1]]++;
387         histogram[patch3x3_row[2]]++;
388 
389   // Find bin with the most votes from the patch
390         int max_votes = 0;
391         int index = -1;
392         for (int i = 0; i < 8; ++i)
393         {
394           if (max_votes < histogram[i])
395           {
396             index = i;
397             max_votes = histogram[i];
398           }
399         }
400 
401   // Only accept the quantization if majority of pixels in the patch agree
402   static const int NEIGHBOR_THRESHOLD = 5;
403         if (max_votes >= NEIGHBOR_THRESHOLD)
404           quantized_angle.at<uchar>(r, c) = uchar(1 << index);
405       }
406     }
407   }
408 }
409 
410 class ColorGradientPyramid : public QuantizedPyramid
411 {
412 public:
413   ColorGradientPyramid(const Mat& src, const Mat& mask,
414                        float weak_threshold, size_t num_features,
415                        float strong_threshold);
416 
417   virtual void quantize(Mat& dst) const CV_OVERRIDE;
418 
419   virtual bool extractTemplate(Template& templ) const CV_OVERRIDE;
420 
421   virtual void pyrDown() CV_OVERRIDE;
422 
423 protected:
424   /// Recalculate angle and magnitude images
425   void update();
426 
427   Mat src;
428   Mat mask;
429 
430   int pyramid_level;
431   Mat angle;
432   Mat magnitude;
433 
434   float weak_threshold;
435   size_t num_features;
436   float strong_threshold;
437 };
438 
ColorGradientPyramid(const Mat & _src,const Mat & _mask,float _weak_threshold,size_t _num_features,float _strong_threshold)439 ColorGradientPyramid::ColorGradientPyramid(const Mat& _src, const Mat& _mask,
440                                            float _weak_threshold, size_t _num_features,
441                                            float _strong_threshold)
442   : src(_src),
443     mask(_mask),
444     pyramid_level(0),
445     weak_threshold(_weak_threshold),
446     num_features(_num_features),
447     strong_threshold(_strong_threshold)
448 {
449   update();
450 }
451 
update()452 void ColorGradientPyramid::update()
453 {
454   quantizedOrientations(src, magnitude, angle, weak_threshold);
455 }
456 
pyrDown()457 void ColorGradientPyramid::pyrDown()
458 {
459   // Some parameters need to be adjusted
460   num_features /= 2; /// @todo Why not 4?
461   ++pyramid_level;
462 
463   // Downsample the current inputs
464   Size size(src.cols / 2, src.rows / 2);
465   Mat next_src;
466   cv::pyrDown(src, next_src, size);
467   src = next_src;
468   if (!mask.empty())
469   {
470     Mat next_mask;
471     resize(mask, next_mask, size, 0.0, 0.0, INTER_NEAREST);
472     mask = next_mask;
473   }
474 
475   update();
476 }
477 
quantize(Mat & dst) const478 void ColorGradientPyramid::quantize(Mat& dst) const
479 {
480   dst = Mat::zeros(angle.size(), CV_8U);
481   angle.copyTo(dst, mask);
482 }
483 
extractTemplate(Template & templ) const484 bool ColorGradientPyramid::extractTemplate(Template& templ) const
485 {
486   // Want features on the border to distinguish from background
487   Mat local_mask;
488   if (!mask.empty())
489   {
490     erode(mask, local_mask, Mat(), Point(-1,-1), 1, BORDER_REPLICATE);
491     subtract(mask, local_mask, local_mask);
492   }
493 
494   // Create sorted list of all pixels with magnitude greater than a threshold
495   std::vector<Candidate> candidates;
496   bool no_mask = local_mask.empty();
497   float threshold_sq = strong_threshold*strong_threshold;
498   for (int r = 0; r < magnitude.rows; ++r)
499   {
500     const uchar* angle_r = angle.ptr<uchar>(r);
501     const float* magnitude_r = magnitude.ptr<float>(r);
502     const uchar* mask_r = no_mask ? NULL : local_mask.ptr<uchar>(r);
503 
504     for (int c = 0; c < magnitude.cols; ++c)
505     {
506       if (no_mask || mask_r[c])
507       {
508         uchar quantized = angle_r[c];
509         if (quantized > 0)
510         {
511           float score = magnitude_r[c];
512           if (score > threshold_sq)
513           {
514             candidates.push_back(Candidate(c, r, getLabel(quantized), score));
515           }
516         }
517       }
518     }
519   }
520   // We require a certain number of features
521   if (candidates.size() < num_features)
522     return false;
523   // NOTE: Stable sort to agree with old code, which used std::list::sort()
524   std::stable_sort(candidates.begin(), candidates.end());
525 
526   // Use heuristic based on surplus of candidates in narrow outline for initial distance threshold
527   float distance = static_cast<float>(candidates.size() / num_features + 1);
528   selectScatteredFeatures(candidates, templ.features, num_features, distance);
529 
530   // Size determined externally, needs to match templates for other modalities
531   templ.width = -1;
532   templ.height = -1;
533   templ.pyramid_level = pyramid_level;
534 
535   return true;
536 }
537 
ColorGradient()538 ColorGradient::ColorGradient()
539   : weak_threshold(10.0f),
540     num_features(63),
541     strong_threshold(55.0f)
542 {
543 }
544 
ColorGradient(float _weak_threshold,size_t _num_features,float _strong_threshold)545 ColorGradient::ColorGradient(float _weak_threshold, size_t _num_features, float _strong_threshold)
546   : weak_threshold(_weak_threshold),
547     num_features(_num_features),
548     strong_threshold(_strong_threshold)
549 {
550 }
551 
create(float weak_threshold,size_t num_features,float strong_threshold)552 Ptr<ColorGradient> ColorGradient::create(float weak_threshold, size_t num_features, float strong_threshold)
553 {
554     return makePtr<ColorGradient>(weak_threshold, num_features, strong_threshold);
555 }
556 
557 static const char CG_NAME[] = "ColorGradient";
558 
name() const559 String ColorGradient::name() const
560 {
561   return CG_NAME;
562 }
563 
processImpl(const Mat & src,const Mat & mask) const564 Ptr<QuantizedPyramid> ColorGradient::processImpl(const Mat& src,
565                                                      const Mat& mask) const
566 {
567   return makePtr<ColorGradientPyramid>(src, mask, weak_threshold, num_features, strong_threshold);
568 }
569 
read(const FileNode & fn)570 void ColorGradient::read(const FileNode& fn)
571 {
572   String type = fn["type"];
573   CV_Assert(type == CG_NAME);
574 
575   weak_threshold = fn["weak_threshold"];
576   num_features = int(fn["num_features"]);
577   strong_threshold = fn["strong_threshold"];
578 }
579 
write(FileStorage & fs) const580 void ColorGradient::write(FileStorage& fs) const
581 {
582   fs << "type" << CG_NAME;
583   fs << "weak_threshold" << weak_threshold;
584   fs << "num_features" << int(num_features);
585   fs << "strong_threshold" << strong_threshold;
586 }
587 
588 /****************************************************************************************\
589 *                               Depth normal modality                                    *
590 \****************************************************************************************/
591 
592 // Contains GRANULARITY and NORMAL_LUT
593 #include "normal_lut.i"
594 
accumBilateral(long delta,long i,long j,long * A,long * b,int threshold)595 static void accumBilateral(long delta, long i, long j, long * A, long * b, int threshold)
596 {
597   long f = std::abs(delta) < threshold ? 1 : 0;
598 
599   const long fi = f * i;
600   const long fj = f * j;
601 
602   A[0] += fi * i;
603   A[1] += fi * j;
604   A[3] += fj * j;
605   b[0]  += fi * delta;
606   b[1]  += fj * delta;
607 }
608 
609 /**
610  * \brief Compute quantized normal image from depth image.
611  *
612  * Implements section 2.6 "Extension to Dense Depth Sensors."
613  *
614  * \param[in]  src  The source 16-bit depth image (in mm).
615  * \param[out] dst  The destination 8-bit image. Each bit represents one bin of
616  *                  the view cone.
617  * \param distance_threshold   Ignore pixels beyond this distance.
618  * \param difference_threshold When computing normals, ignore contributions of pixels whose
619  *                             depth difference with the central pixel is above this threshold.
620  *
621  * \todo Should also need camera model, or at least focal lengths? Replace distance_threshold with mask?
622  */
quantizedNormals(const Mat & src,Mat & dst,int distance_threshold,int difference_threshold)623 static void quantizedNormals(const Mat& src, Mat& dst, int distance_threshold,
624                       int difference_threshold)
625 {
626   dst = Mat::zeros(src.size(), CV_8U);
627 
628   const unsigned short * lp_depth   = src.ptr<ushort>();
629   unsigned char  * lp_normals = dst.ptr<uchar>();
630 
631   const int l_W = src.cols;
632   const int l_H = src.rows;
633 
634   const int l_r = 5; // used to be 7
635   const int l_offset0 = -l_r - l_r * l_W;
636   const int l_offset1 =    0 - l_r * l_W;
637   const int l_offset2 = +l_r - l_r * l_W;
638   const int l_offset3 = -l_r;
639   const int l_offset4 = +l_r;
640   const int l_offset5 = -l_r + l_r * l_W;
641   const int l_offset6 =    0 + l_r * l_W;
642   const int l_offset7 = +l_r + l_r * l_W;
643 
644   const int l_offsetx = GRANULARITY / 2;
645   const int l_offsety = GRANULARITY / 2;
646 
647   for (int l_y = l_r; l_y < l_H - l_r - 1; ++l_y)
648   {
649     const unsigned short * lp_line = lp_depth + (l_y * l_W + l_r);
650     unsigned char * lp_norm = lp_normals + (l_y * l_W + l_r);
651 
652     for (int l_x = l_r; l_x < l_W - l_r - 1; ++l_x)
653     {
654       long l_d = lp_line[0];
655 
656       if (l_d < distance_threshold)
657       {
658         // accum
659         long l_A[4]; l_A[0] = l_A[1] = l_A[2] = l_A[3] = 0;
660         long l_b[2]; l_b[0] = l_b[1] = 0;
661         accumBilateral(lp_line[l_offset0] - l_d, -l_r, -l_r, l_A, l_b, difference_threshold);
662         accumBilateral(lp_line[l_offset1] - l_d,    0, -l_r, l_A, l_b, difference_threshold);
663         accumBilateral(lp_line[l_offset2] - l_d, +l_r, -l_r, l_A, l_b, difference_threshold);
664         accumBilateral(lp_line[l_offset3] - l_d, -l_r,    0, l_A, l_b, difference_threshold);
665         accumBilateral(lp_line[l_offset4] - l_d, +l_r,    0, l_A, l_b, difference_threshold);
666         accumBilateral(lp_line[l_offset5] - l_d, -l_r, +l_r, l_A, l_b, difference_threshold);
667         accumBilateral(lp_line[l_offset6] - l_d,    0, +l_r, l_A, l_b, difference_threshold);
668         accumBilateral(lp_line[l_offset7] - l_d, +l_r, +l_r, l_A, l_b, difference_threshold);
669 
670         // solve
671         long l_det =  l_A[0] * l_A[3] - l_A[1] * l_A[1];
672         long l_ddx =  l_A[3] * l_b[0] - l_A[1] * l_b[1];
673         long l_ddy = -l_A[1] * l_b[0] + l_A[0] * l_b[1];
674 
675         /// @todo Magic number 1150 is focal length? This is something like
676         /// f in SXGA mode, but in VGA is more like 530.
677         float l_nx = static_cast<float>(1150 * l_ddx);
678         float l_ny = static_cast<float>(1150 * l_ddy);
679         float l_nz = static_cast<float>(-l_det * l_d);
680 
681         float l_sqrt = sqrtf(l_nx * l_nx + l_ny * l_ny + l_nz * l_nz);
682 
683         if (l_sqrt > 0)
684         {
685           float l_norminv = 1.0f / (l_sqrt);
686 
687           l_nx *= l_norminv;
688           l_ny *= l_norminv;
689           l_nz *= l_norminv;
690 
691           //*lp_norm = fabs(l_nz)*255;
692 
693           int l_val1 = static_cast<int>(l_nx * l_offsetx + l_offsetx);
694           int l_val2 = static_cast<int>(l_ny * l_offsety + l_offsety);
695           int l_val3 = static_cast<int>(l_nz * GRANULARITY + GRANULARITY);
696 
697           *lp_norm = NORMAL_LUT[l_val3][l_val2][l_val1];
698         }
699         else
700         {
701           *lp_norm = 0; // Discard shadows from depth sensor
702         }
703       }
704       else
705       {
706         *lp_norm = 0; //out of depth
707       }
708       ++lp_line;
709       ++lp_norm;
710     }
711   }
712   medianBlur(dst, dst, 5);
713 }
714 
715 class DepthNormalPyramid : public QuantizedPyramid
716 {
717 public:
718   DepthNormalPyramid(const Mat& src, const Mat& mask,
719                      int distance_threshold, int difference_threshold, size_t num_features,
720                      int extract_threshold);
721 
722   virtual void quantize(Mat& dst) const CV_OVERRIDE;
723 
724   virtual bool extractTemplate(Template& templ) const CV_OVERRIDE;
725 
726   virtual void pyrDown() CV_OVERRIDE;
727 
728 protected:
729   Mat mask;
730 
731   int pyramid_level;
732   Mat normal;
733 
734   size_t num_features;
735   int extract_threshold;
736 };
737 
DepthNormalPyramid(const Mat & src,const Mat & _mask,int distance_threshold,int difference_threshold,size_t _num_features,int _extract_threshold)738 DepthNormalPyramid::DepthNormalPyramid(const Mat& src, const Mat& _mask,
739                                        int distance_threshold, int difference_threshold, size_t _num_features,
740                                        int _extract_threshold)
741   : mask(_mask),
742     pyramid_level(0),
743     num_features(_num_features),
744     extract_threshold(_extract_threshold)
745 {
746   quantizedNormals(src, normal, distance_threshold, difference_threshold);
747 }
748 
pyrDown()749 void DepthNormalPyramid::pyrDown()
750 {
751   // Some parameters need to be adjusted
752   num_features /= 2; /// @todo Why not 4?
753   extract_threshold /= 2;
754   ++pyramid_level;
755 
756   // In this case, NN-downsample the quantized image
757   Mat next_normal;
758   Size size(normal.cols / 2, normal.rows / 2);
759   resize(normal, next_normal, size, 0.0, 0.0, INTER_NEAREST);
760   normal = next_normal;
761   if (!mask.empty())
762   {
763     Mat next_mask;
764     resize(mask, next_mask, size, 0.0, 0.0, INTER_NEAREST);
765     mask = next_mask;
766   }
767 }
768 
quantize(Mat & dst) const769 void DepthNormalPyramid::quantize(Mat& dst) const
770 {
771   dst = Mat::zeros(normal.size(), CV_8U);
772   normal.copyTo(dst, mask);
773 }
774 
extractTemplate(Template & templ) const775 bool DepthNormalPyramid::extractTemplate(Template& templ) const
776 {
777   // Features right on the object border are unreliable
778   Mat local_mask;
779   if (!mask.empty())
780   {
781     erode(mask, local_mask, Mat(), Point(-1,-1), 2, BORDER_REPLICATE);
782   }
783 
784   // Compute distance transform for each individual quantized orientation
785   Mat temp = Mat::zeros(normal.size(), CV_8U);
786   Mat distances[8];
787   for (int i = 0; i < 8; ++i)
788   {
789     temp.setTo(1 << i, local_mask);
790     bitwise_and(temp, normal, temp);
791     // temp is now non-zero at pixels in the mask with quantized orientation i
792     distanceTransform(temp, distances[i], DIST_C, 3);
793   }
794 
795   // Count how many features taken for each label
796   int label_counts[8] = {0, 0, 0, 0, 0, 0, 0, 0};
797 
798   // Create sorted list of candidate features
799   std::vector<Candidate> candidates;
800   bool no_mask = local_mask.empty();
801   for (int r = 0; r < normal.rows; ++r)
802   {
803     const uchar* normal_r = normal.ptr<uchar>(r);
804     const uchar* mask_r = no_mask ? NULL : local_mask.ptr<uchar>(r);
805 
806     for (int c = 0; c < normal.cols; ++c)
807     {
808       if (no_mask || mask_r[c])
809       {
810         uchar quantized = normal_r[c];
811 
812         if (quantized != 0 && quantized != 255) // background and shadow
813         {
814           int label = getLabel(quantized);
815 
816           // Accept if distance to a pixel belonging to a different label is greater than
817           // some threshold. IOW, ideal feature is in the center of a large homogeneous
818           // region.
819           float score = distances[label].at<float>(r, c);
820           if (score >= extract_threshold)
821           {
822             candidates.push_back( Candidate(c, r, label, score) );
823             ++label_counts[label];
824           }
825         }
826       }
827     }
828   }
829   // We require a certain number of features
830   if (candidates.size() < num_features)
831     return false;
832 
833   // Prefer large distances, but also want to collect features over all 8 labels.
834   // So penalize labels with lots of candidates.
835   for (size_t i = 0; i < candidates.size(); ++i)
836   {
837     Candidate& c = candidates[i];
838     c.score /= (float)label_counts[c.f.label];
839   }
840   std::stable_sort(candidates.begin(), candidates.end());
841 
842   // Use heuristic based on object area for initial distance threshold
843   float area = no_mask ? (float)normal.total() : (float)countNonZero(local_mask);
844   float distance = sqrtf(area) / sqrtf((float)num_features) + 1.5f;
845   selectScatteredFeatures(candidates, templ.features, num_features, distance);
846 
847   // Size determined externally, needs to match templates for other modalities
848   templ.width = -1;
849   templ.height = -1;
850   templ.pyramid_level = pyramid_level;
851 
852   return true;
853 }
854 
DepthNormal()855 DepthNormal::DepthNormal()
856   : distance_threshold(2000),
857     difference_threshold(50),
858     num_features(63),
859     extract_threshold(2)
860 {
861 }
862 
DepthNormal(int _distance_threshold,int _difference_threshold,size_t _num_features,int _extract_threshold)863 DepthNormal::DepthNormal(int _distance_threshold, int _difference_threshold, size_t _num_features,
864                          int _extract_threshold)
865   : distance_threshold(_distance_threshold),
866     difference_threshold(_difference_threshold),
867     num_features(_num_features),
868     extract_threshold(_extract_threshold)
869 {
870 }
871 
create(int distance_threshold,int difference_threshold,size_t num_features,int extract_threshold)872 Ptr<DepthNormal> DepthNormal::create(int distance_threshold, int difference_threshold, size_t num_features,
873                                      int extract_threshold)
874 {
875     return makePtr<DepthNormal>(distance_threshold, difference_threshold, num_features, extract_threshold);
876 }
877 
878 static const char DN_NAME[] = "DepthNormal";
879 
name() const880 String DepthNormal::name() const
881 {
882   return DN_NAME;
883 }
884 
processImpl(const Mat & src,const Mat & mask) const885 Ptr<QuantizedPyramid> DepthNormal::processImpl(const Mat& src,
886                                                    const Mat& mask) const
887 {
888   return makePtr<DepthNormalPyramid>(src, mask, distance_threshold, difference_threshold,
889                                      num_features, extract_threshold);
890 }
891 
read(const FileNode & fn)892 void DepthNormal::read(const FileNode& fn)
893 {
894   String type = fn["type"];
895   CV_Assert(type == DN_NAME);
896 
897   distance_threshold = fn["distance_threshold"];
898   difference_threshold = fn["difference_threshold"];
899   num_features = int(fn["num_features"]);
900   extract_threshold = fn["extract_threshold"];
901 }
902 
write(FileStorage & fs) const903 void DepthNormal::write(FileStorage& fs) const
904 {
905   fs << "type" << DN_NAME;
906   fs << "distance_threshold" << distance_threshold;
907   fs << "difference_threshold" << difference_threshold;
908   fs << "num_features" << int(num_features);
909   fs << "extract_threshold" << extract_threshold;
910 }
911 
912 /****************************************************************************************\
913 *                                 Response maps                                          *
914 \****************************************************************************************/
915 
orUnaligned8u(const uchar * src,const int src_stride,uchar * dst,const int dst_stride,const int width,const int height)916 static void orUnaligned8u(const uchar * src, const int src_stride,
917                    uchar * dst, const int dst_stride,
918                    const int width, const int height)
919 {
920 #if CV_SSE2
921   volatile bool haveSSE2 = checkHardwareSupport(CPU_SSE2);
922 #if CV_SSE3
923   volatile bool haveSSE3 = checkHardwareSupport(CPU_SSE3);
924 #endif
925   bool src_aligned = reinterpret_cast<unsigned long long>(src) % 16 == 0;
926 #endif
927 
928   for (int r = 0; r < height; ++r)
929   {
930     int c = 0;
931 
932 #if CV_SSE2
933     // Use aligned loads if possible
934     if (haveSSE2 && src_aligned)
935     {
936       for ( ; c < width - 15; c += 16)
937       {
938         const __m128i* src_ptr = reinterpret_cast<const __m128i*>(src + c);
939         __m128i* dst_ptr = reinterpret_cast<__m128i*>(dst + c);
940         *dst_ptr = _mm_or_si128(*dst_ptr, *src_ptr);
941       }
942     }
943 #if CV_SSE3
944     // Use LDDQU for fast unaligned load
945     else if (haveSSE3)
946     {
947       for ( ; c < width - 15; c += 16)
948       {
949         __m128i val = _mm_lddqu_si128(reinterpret_cast<const __m128i*>(src + c));
950         __m128i* dst_ptr = reinterpret_cast<__m128i*>(dst + c);
951         *dst_ptr = _mm_or_si128(*dst_ptr, val);
952       }
953     }
954 #endif
955     // Fall back to MOVDQU
956     else if (haveSSE2)
957     {
958       for ( ; c < width - 15; c += 16)
959       {
960         __m128i val = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + c));
961         __m128i* dst_ptr = reinterpret_cast<__m128i*>(dst + c);
962         *dst_ptr = _mm_or_si128(*dst_ptr, val);
963       }
964     }
965 #endif
966     for ( ; c < width; ++c)
967       dst[c] |= src[c];
968 
969     // Advance to next row
970     src += src_stride;
971     dst += dst_stride;
972   }
973 }
974 
975 /**
976  * \brief Spread binary labels in a quantized image.
977  *
978  * Implements section 2.3 "Spreading the Orientations."
979  *
980  * \param[in]  src The source 8-bit quantized image.
981  * \param[out] dst Destination 8-bit spread image.
982  * \param      T   Sampling step. Spread labels T/2 pixels in each direction.
983  */
spread(const Mat & src,Mat & dst,int T)984 static void spread(const Mat& src, Mat& dst, int T)
985 {
986   // Allocate and zero-initialize spread (OR'ed) image
987   dst = Mat::zeros(src.size(), CV_8U);
988 
989   // Fill in spread gradient image (section 2.3)
990   for (int r = 0; r < T; ++r)
991   {
992     int height = src.rows - r;
993     for (int c = 0; c < T; ++c)
994     {
995       orUnaligned8u(&src.at<unsigned char>(r, c), static_cast<int>(src.step1()), dst.ptr(),
996                     static_cast<int>(dst.step1()), src.cols - c, height);
997     }
998   }
999 }
1000 
1001 // Auto-generated by create_similarity_lut.py
1002 CV_DECL_ALIGNED(16) static const unsigned char SIMILARITY_LUT[256] = {0, 4, 3, 4, 2, 4, 3, 4, 1, 4, 3, 4, 2, 4, 3, 4, 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 4, 4, 3, 3, 4, 4, 2, 3, 4, 4, 3, 3, 4, 4, 0, 1, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 3, 3, 4, 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 0, 2, 1, 2, 0, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 0, 3, 2, 3, 1, 3, 2, 3, 0, 3, 2, 3, 1, 3, 2, 3, 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 0, 4, 3, 4, 2, 4, 3, 4, 1, 4, 3, 4, 2, 4, 3, 4, 0, 1, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 3, 4, 4, 3, 3, 4, 4, 2, 3, 4, 4, 3, 3, 4, 4, 0, 2, 1, 2, 0, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 0, 2, 3, 3, 4, 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 0, 3, 2, 3, 1, 3, 2, 3, 0, 3, 2, 3, 1, 3, 2, 3, 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4};
1003 
1004 /**
1005  * \brief Precompute response maps for a spread quantized image.
1006  *
1007  * Implements section 2.4 "Precomputing Response Maps."
1008  *
1009  * \param[in]  src           The source 8-bit spread quantized image.
1010  * \param[out] response_maps Vector of 8 response maps, one for each bit label.
1011  */
computeResponseMaps(const Mat & src,std::vector<Mat> & response_maps)1012 static void computeResponseMaps(const Mat& src, std::vector<Mat>& response_maps)
1013 {
1014   CV_Assert((src.rows * src.cols) % 16 == 0);
1015 
1016   // Allocate response maps
1017   response_maps.resize(8);
1018   for (int i = 0; i < 8; ++i)
1019     response_maps[i].create(src.size(), CV_8U);
1020 
1021   Mat lsb4(src.size(), CV_8U);
1022   Mat msb4(src.size(), CV_8U);
1023 
1024   for (int r = 0; r < src.rows; ++r)
1025   {
1026     const uchar* src_r = src.ptr(r);
1027     uchar* lsb4_r = lsb4.ptr(r);
1028     uchar* msb4_r = msb4.ptr(r);
1029 
1030     for (int c = 0; c < src.cols; ++c)
1031     {
1032       // Least significant 4 bits of spread image pixel
1033       lsb4_r[c] = src_r[c] & 15;
1034       // Most significant 4 bits, right-shifted to be in [0, 16)
1035       msb4_r[c] = (src_r[c] & 240) >> 4;
1036     }
1037   }
1038 
1039 #if CV_SSSE3
1040   volatile bool haveSSSE3 = checkHardwareSupport(CV_CPU_SSSE3);
1041   if (haveSSSE3)
1042   {
1043     const __m128i* lut = reinterpret_cast<const __m128i*>(SIMILARITY_LUT);
1044     for (int ori = 0; ori < 8; ++ori)
1045     {
1046       __m128i* map_data = response_maps[ori].ptr<__m128i>();
1047       __m128i* lsb4_data = lsb4.ptr<__m128i>();
1048       __m128i* msb4_data = msb4.ptr<__m128i>();
1049 
1050       // Precompute the 2D response map S_i (section 2.4)
1051       for (int i = 0; i < (src.rows * src.cols) / 16; ++i)
1052       {
1053         // Using SSE shuffle for table lookup on 4 orientations at a time
1054         // The most/least significant 4 bits are used as the LUT index
1055         __m128i res1 = _mm_shuffle_epi8(lut[2*ori + 0], lsb4_data[i]);
1056         __m128i res2 = _mm_shuffle_epi8(lut[2*ori + 1], msb4_data[i]);
1057 
1058         // Combine the results into a single similarity score
1059         map_data[i] = _mm_max_epu8(res1, res2);
1060       }
1061     }
1062   }
1063   else
1064 #endif
1065   {
1066     // For each of the 8 quantized orientations...
1067     for (int ori = 0; ori < 8; ++ori)
1068     {
1069       uchar* map_data = response_maps[ori].ptr<uchar>();
1070       uchar* lsb4_data = lsb4.ptr<uchar>();
1071       uchar* msb4_data = msb4.ptr<uchar>();
1072       const uchar* lut_low = SIMILARITY_LUT + 32*ori;
1073       const uchar* lut_hi = lut_low + 16;
1074 
1075       for (int i = 0; i < src.rows * src.cols; ++i)
1076       {
1077         map_data[i] = std::max(lut_low[ lsb4_data[i] ], lut_hi[ msb4_data[i] ]);
1078       }
1079     }
1080   }
1081 }
1082 
1083 /**
1084  * \brief Convert a response map to fast linearized ordering.
1085  *
1086  * Implements section 2.5 "Linearizing the Memory for Parallelization."
1087  *
1088  * \param[in]  response_map The 2D response map, an 8-bit image.
1089  * \param[out] linearized   The response map in linearized order. It has T*T rows,
1090  *                          each of which is a linear memory of length (W/T)*(H/T).
1091  * \param      T            Sampling step.
1092  */
linearize(const Mat & response_map,Mat & linearized,int T)1093 static void linearize(const Mat& response_map, Mat& linearized, int T)
1094 {
1095   CV_Assert(response_map.rows % T == 0);
1096   CV_Assert(response_map.cols % T == 0);
1097 
1098   // linearized has T^2 rows, where each row is a linear memory
1099   int mem_width = response_map.cols / T;
1100   int mem_height = response_map.rows / T;
1101   linearized.create(T*T, mem_width * mem_height, CV_8U);
1102 
1103   // Outer two for loops iterate over top-left T^2 starting pixels
1104   int index = 0;
1105   for (int r_start = 0; r_start < T; ++r_start)
1106   {
1107     for (int c_start = 0; c_start < T; ++c_start)
1108     {
1109       uchar* memory = linearized.ptr(index);
1110       ++index;
1111 
1112       // Inner two loops copy every T-th pixel into the linear memory
1113       for (int r = r_start; r < response_map.rows; r += T)
1114       {
1115         const uchar* response_data = response_map.ptr(r);
1116         for (int c = c_start; c < response_map.cols; c += T)
1117           *memory++ = response_data[c];
1118       }
1119     }
1120   }
1121 }
1122 
1123 /****************************************************************************************\
1124 *                               Linearized similarities                                  *
1125 \****************************************************************************************/
1126 
accessLinearMemory(const std::vector<Mat> & linear_memories,const Feature & f,int T,int W)1127 static const unsigned char* accessLinearMemory(const std::vector<Mat>& linear_memories,
1128           const Feature& f, int T, int W)
1129 {
1130   // Retrieve the TxT grid of linear memories associated with the feature label
1131   const Mat& memory_grid = linear_memories[f.label];
1132   CV_DbgAssert(memory_grid.rows == T*T);
1133   CV_DbgAssert(f.x >= 0);
1134   CV_DbgAssert(f.y >= 0);
1135   // The LM we want is at (x%T, y%T) in the TxT grid (stored as the rows of memory_grid)
1136   int grid_x = f.x % T;
1137   int grid_y = f.y % T;
1138   int grid_index = grid_y * T + grid_x;
1139   CV_DbgAssert(grid_index >= 0);
1140   CV_DbgAssert(grid_index < memory_grid.rows);
1141   const unsigned char* memory = memory_grid.ptr(grid_index);
1142   // Within the LM, the feature is at (x/T, y/T). W is the "width" of the LM, the
1143   // input image width decimated by T.
1144   int lm_x = f.x / T;
1145   int lm_y = f.y / T;
1146   int lm_index = lm_y * W + lm_x;
1147   CV_DbgAssert(lm_index >= 0);
1148   CV_DbgAssert(lm_index < memory_grid.cols);
1149   return memory + lm_index;
1150 }
1151 
1152 /**
1153  * \brief Compute similarity measure for a given template at each sampled image location.
1154  *
1155  * Uses linear memories to compute the similarity measure as described in Fig. 7.
1156  *
1157  * \param[in]  linear_memories Vector of 8 linear memories, one for each label.
1158  * \param[in]  templ           Template to match against.
1159  * \param[out] dst             Destination 8-bit similarity image of size (W/T, H/T).
1160  * \param      size            Size (W, H) of the original input image.
1161  * \param      T               Sampling step.
1162  */
similarity(const std::vector<Mat> & linear_memories,const Template & templ,Mat & dst,Size size,int T)1163 static void similarity(const std::vector<Mat>& linear_memories, const Template& templ,
1164                 Mat& dst, Size size, int T)
1165 {
1166   // 63 features or less is a special case because the max similarity per-feature is 4.
1167   // 255/4 = 63, so up to that many we can add up similarities in 8 bits without worrying
1168   // about overflow. Therefore here we use _mm_add_epi8 as the workhorse, whereas a more
1169   // general function would use _mm_add_epi16.
1170   CV_Assert(templ.features.size() <= 63);
1171   /// @todo Handle more than 255/MAX_RESPONSE features!!
1172 
1173   // Decimate input image size by factor of T
1174   int W = size.width / T;
1175   int H = size.height / T;
1176 
1177   // Feature dimensions, decimated by factor T and rounded up
1178   int wf = (templ.width - 1) / T + 1;
1179   int hf = (templ.height - 1) / T + 1;
1180 
1181   // Span is the range over which we can shift the template around the input image
1182   int span_x = W - wf;
1183   int span_y = H - hf;
1184 
1185   // Compute number of contiguous (in memory) pixels to check when sliding feature over
1186   // image. This allows template to wrap around left/right border incorrectly, so any
1187   // wrapped template matches must be filtered out!
1188   int template_positions = span_y * W + span_x + 1; // why add 1?
1189   //int template_positions = (span_y - 1) * W + span_x; // More correct?
1190 
1191   /// @todo In old code, dst is buffer of size m_U. Could make it something like
1192   /// (span_x)x(span_y) instead?
1193   dst = Mat::zeros(H, W, CV_8U);
1194   uchar* dst_ptr = dst.ptr<uchar>();
1195 
1196 #if CV_SSE2
1197   volatile bool haveSSE2 = checkHardwareSupport(CV_CPU_SSE2);
1198 #if CV_SSE3
1199   volatile bool haveSSE3 = checkHardwareSupport(CV_CPU_SSE3);
1200 #endif
1201 #endif
1202 
1203   // Compute the similarity measure for this template by accumulating the contribution of
1204   // each feature
1205   for (int i = 0; i < (int)templ.features.size(); ++i)
1206   {
1207     // Add the linear memory at the appropriate offset computed from the location of
1208     // the feature in the template
1209     Feature f = templ.features[i];
1210     // Discard feature if out of bounds
1211     /// @todo Shouldn't actually see x or y < 0 here?
1212     if (f.x < 0 || f.x >= size.width || f.y < 0 || f.y >= size.height)
1213       continue;
1214     const uchar* lm_ptr = accessLinearMemory(linear_memories, f, T, W);
1215 
1216     // Now we do an aligned/unaligned add of dst_ptr and lm_ptr with template_positions elements
1217     int j = 0;
1218     // Process responses 16 at a time if vectorization possible
1219 #if CV_SSE2
1220 #if CV_SSE3
1221     if (haveSSE3)
1222     {
1223       // LDDQU may be more efficient than MOVDQU for unaligned load of next 16 responses
1224       for ( ; j < template_positions - 15; j += 16)
1225       {
1226         __m128i responses = _mm_lddqu_si128(reinterpret_cast<const __m128i*>(lm_ptr + j));
1227         __m128i* dst_ptr_sse = reinterpret_cast<__m128i*>(dst_ptr + j);
1228         *dst_ptr_sse = _mm_add_epi8(*dst_ptr_sse, responses);
1229       }
1230     }
1231     else
1232 #endif
1233     if (haveSSE2)
1234     {
1235       // Fall back to MOVDQU
1236       for ( ; j < template_positions - 15; j += 16)
1237       {
1238         __m128i responses = _mm_loadu_si128(reinterpret_cast<const __m128i*>(lm_ptr + j));
1239         __m128i* dst_ptr_sse = reinterpret_cast<__m128i*>(dst_ptr + j);
1240         *dst_ptr_sse = _mm_add_epi8(*dst_ptr_sse, responses);
1241       }
1242     }
1243 #endif
1244     for ( ; j < template_positions; ++j)
1245       dst_ptr[j] = uchar(dst_ptr[j] + lm_ptr[j]);
1246   }
1247 }
1248 
1249 /**
1250  * \brief Compute similarity measure for a given template in a local region.
1251  *
1252  * \param[in]  linear_memories Vector of 8 linear memories, one for each label.
1253  * \param[in]  templ           Template to match against.
1254  * \param[out] dst             Destination 8-bit similarity image, 16x16.
1255  * \param      size            Size (W, H) of the original input image.
1256  * \param      T               Sampling step.
1257  * \param      center          Center of the local region.
1258  */
similarityLocal(const std::vector<Mat> & linear_memories,const Template & templ,Mat & dst,Size size,int T,Point center)1259 static void similarityLocal(const std::vector<Mat>& linear_memories, const Template& templ,
1260                      Mat& dst, Size size, int T, Point center)
1261 {
1262   // Similar to whole-image similarity() above. This version takes a position 'center'
1263   // and computes the energy in the 16x16 patch centered on it.
1264   CV_Assert(templ.features.size() <= 63);
1265 
1266   // Compute the similarity map in a 16x16 patch around center
1267   int W = size.width / T;
1268   dst = Mat::zeros(16, 16, CV_8U);
1269 
1270   // Offset each feature point by the requested center. Further adjust to (-8,-8) from the
1271   // center to get the top-left corner of the 16x16 patch.
1272   // NOTE: We make the offsets multiples of T to agree with results of the original code.
1273   int offset_x = (center.x / T - 8) * T;
1274   int offset_y = (center.y / T - 8) * T;
1275 
1276 #if CV_SSE2
1277   volatile bool haveSSE2 = checkHardwareSupport(CV_CPU_SSE2);
1278 #if CV_SSE3
1279   volatile bool haveSSE3 = checkHardwareSupport(CV_CPU_SSE3);
1280 #endif
1281   __m128i* dst_ptr_sse = dst.ptr<__m128i>();
1282 #endif
1283 
1284   for (int i = 0; i < (int)templ.features.size(); ++i)
1285   {
1286     Feature f = templ.features[i];
1287     f.x += offset_x;
1288     f.y += offset_y;
1289     // Discard feature if out of bounds, possibly due to applying the offset
1290     if (f.x < 0 || f.y < 0 || f.x >= size.width || f.y >= size.height)
1291       continue;
1292 
1293     const uchar* lm_ptr = accessLinearMemory(linear_memories, f, T, W);
1294 
1295     // Process whole row at a time if vectorization possible
1296 #if CV_SSE2
1297 #if CV_SSE3
1298     if (haveSSE3)
1299     {
1300       // LDDQU may be more efficient than MOVDQU for unaligned load of 16 responses from current row
1301       for (int row = 0; row < 16; ++row)
1302       {
1303         __m128i aligned = _mm_lddqu_si128(reinterpret_cast<const __m128i*>(lm_ptr));
1304         dst_ptr_sse[row] = _mm_add_epi8(dst_ptr_sse[row], aligned);
1305         lm_ptr += W; // Step to next row
1306       }
1307     }
1308     else
1309 #endif
1310     if (haveSSE2)
1311     {
1312       // Fall back to MOVDQU
1313       for (int row = 0; row < 16; ++row)
1314       {
1315         __m128i aligned = _mm_loadu_si128(reinterpret_cast<const __m128i*>(lm_ptr));
1316         dst_ptr_sse[row] = _mm_add_epi8(dst_ptr_sse[row], aligned);
1317         lm_ptr += W; // Step to next row
1318       }
1319     }
1320     else
1321 #endif
1322     {
1323       uchar* dst_ptr = dst.ptr<uchar>();
1324       for (int row = 0; row < 16; ++row)
1325       {
1326         for (int col = 0; col < 16; ++col)
1327           dst_ptr[col] = uchar(dst_ptr[col] + lm_ptr[col]);
1328         dst_ptr += 16;
1329         lm_ptr += W;
1330       }
1331     }
1332   }
1333 }
1334 
addUnaligned8u16u(const uchar * src1,const uchar * src2,ushort * res,int length)1335 static void addUnaligned8u16u(const uchar * src1, const uchar * src2, ushort * res, int length)
1336 {
1337   const uchar * end = src1 + length;
1338 
1339   while (src1 != end)
1340   {
1341     *res = *src1 + *src2;
1342 
1343     ++src1;
1344     ++src2;
1345     ++res;
1346   }
1347 }
1348 
1349 /**
1350  * \brief Accumulate one or more 8-bit similarity images.
1351  *
1352  * \param[in]  similarities Source 8-bit similarity images.
1353  * \param[out] dst          Destination 16-bit similarity image.
1354  */
addSimilarities(const std::vector<Mat> & similarities,Mat & dst)1355 static void addSimilarities(const std::vector<Mat>& similarities, Mat& dst)
1356 {
1357   if (similarities.size() == 1)
1358   {
1359     similarities[0].convertTo(dst, CV_16U);
1360   }
1361   else
1362   {
1363     // NOTE: add() seems to be rather slow in the 8U + 8U -> 16U case
1364     dst.create(similarities[0].size(), CV_16U);
1365     addUnaligned8u16u(similarities[0].ptr(), similarities[1].ptr(), dst.ptr<ushort>(), static_cast<int>(dst.total()));
1366 
1367     /// @todo Optimize 16u + 8u -> 16u when more than 2 modalities
1368     for (size_t i = 2; i < similarities.size(); ++i)
1369       add(dst, similarities[i], dst, noArray(), CV_16U);
1370   }
1371 }
1372 
1373 /****************************************************************************************\
1374 *                               High-level Detector API                                  *
1375 \****************************************************************************************/
1376 
Detector()1377 Detector::Detector()
1378 {
1379 }
1380 
Detector(const std::vector<Ptr<Modality>> & _modalities,const std::vector<int> & T_pyramid)1381 Detector::Detector(const std::vector< Ptr<Modality> >& _modalities,
1382                    const std::vector<int>& T_pyramid)
1383   : modalities(_modalities),
1384     pyramid_levels(static_cast<int>(T_pyramid.size())),
1385     T_at_level(T_pyramid)
1386 {
1387 }
1388 
match(const std::vector<Mat> & sources,float threshold,std::vector<Match> & matches,const std::vector<String> & class_ids,OutputArrayOfArrays quantized_images,const std::vector<Mat> & masks) const1389 void Detector::match(const std::vector<Mat>& sources, float threshold, std::vector<Match>& matches,
1390                      const std::vector<String>& class_ids, OutputArrayOfArrays quantized_images,
1391                      const std::vector<Mat>& masks) const
1392 {
1393   matches.clear();
1394   if (quantized_images.needed())
1395     quantized_images.create(1, static_cast<int>(pyramid_levels * modalities.size()), CV_8U);
1396 
1397   CV_Assert(sources.size() == modalities.size());
1398   // Initialize each modality with our sources
1399   std::vector< Ptr<QuantizedPyramid> > quantizers;
1400   for (int i = 0; i < (int)modalities.size(); ++i){
1401     Mat mask, source;
1402     source = sources[i];
1403     if(!masks.empty()){
1404       CV_Assert(masks.size() == modalities.size());
1405       mask = masks[i];
1406     }
1407     CV_Assert(mask.empty() || mask.size() == source.size());
1408     quantizers.push_back(modalities[i]->process(source, mask));
1409   }
1410   // pyramid level -> modality -> quantization
1411   LinearMemoryPyramid lm_pyramid(pyramid_levels,
1412                                  std::vector<LinearMemories>(modalities.size(), LinearMemories(8)));
1413 
1414   // For each pyramid level, precompute linear memories for each modality
1415   std::vector<Size> sizes;
1416   for (int l = 0; l < pyramid_levels; ++l)
1417   {
1418     int T = T_at_level[l];
1419     std::vector<LinearMemories>& lm_level = lm_pyramid[l];
1420 
1421     if (l > 0)
1422     {
1423       for (int i = 0; i < (int)quantizers.size(); ++i)
1424         quantizers[i]->pyrDown();
1425     }
1426 
1427     Mat quantized, spread_quantized;
1428     std::vector<Mat> response_maps;
1429     for (int i = 0; i < (int)quantizers.size(); ++i)
1430     {
1431       quantizers[i]->quantize(quantized);
1432       spread(quantized, spread_quantized, T);
1433       computeResponseMaps(spread_quantized, response_maps);
1434 
1435       LinearMemories& memories = lm_level[i];
1436       for (int j = 0; j < 8; ++j)
1437         linearize(response_maps[j], memories[j], T);
1438 
1439       if (quantized_images.needed()) //use copyTo here to side step reference semantics.
1440         quantized.copyTo(quantized_images.getMatRef(static_cast<int>(l*quantizers.size() + i)));
1441     }
1442 
1443     sizes.push_back(quantized.size());
1444   }
1445 
1446   if (class_ids.empty())
1447   {
1448     // Match all templates
1449     TemplatesMap::const_iterator it = class_templates.begin(), itend = class_templates.end();
1450     for ( ; it != itend; ++it)
1451       matchClass(lm_pyramid, sizes, threshold, matches, it->first, it->second);
1452   }
1453   else
1454   {
1455     // Match only templates for the requested class IDs
1456     for (int i = 0; i < (int)class_ids.size(); ++i)
1457     {
1458       TemplatesMap::const_iterator it = class_templates.find(class_ids[i]);
1459       if (it != class_templates.end())
1460         matchClass(lm_pyramid, sizes, threshold, matches, it->first, it->second);
1461     }
1462   }
1463 
1464   // Sort matches by similarity, and prune any duplicates introduced by pyramid refinement
1465   std::sort(matches.begin(), matches.end());
1466   std::vector<Match>::iterator new_end = std::unique(matches.begin(), matches.end());
1467   matches.erase(new_end, matches.end());
1468 }
1469 
1470 // Used to filter out weak matches
1471 struct MatchPredicate
1472 {
MatchPredicatecv::linemod::MatchPredicate1473   MatchPredicate(float _threshold) : threshold(_threshold) {}
operator ()cv::linemod::MatchPredicate1474   bool operator() (const Match& m) { return m.similarity < threshold; }
1475   float threshold;
1476 };
1477 
matchClass(const LinearMemoryPyramid & lm_pyramid,const std::vector<Size> & sizes,float threshold,std::vector<Match> & matches,const String & class_id,const std::vector<TemplatePyramid> & template_pyramids) const1478 void Detector::matchClass(const LinearMemoryPyramid& lm_pyramid,
1479                           const std::vector<Size>& sizes,
1480                           float threshold, std::vector<Match>& matches,
1481                           const String& class_id,
1482                           const std::vector<TemplatePyramid>& template_pyramids) const
1483 {
1484   // For each template...
1485   for (size_t template_id = 0; template_id < template_pyramids.size(); ++template_id)
1486   {
1487     const TemplatePyramid& tp = template_pyramids[template_id];
1488 
1489     // First match over the whole image at the lowest pyramid level
1490     /// @todo Factor this out into separate function
1491     const std::vector<LinearMemories>& lowest_lm = lm_pyramid.back();
1492 
1493     // Compute similarity maps for each modality at lowest pyramid level
1494     std::vector<Mat> similarities(modalities.size());
1495     int lowest_start = static_cast<int>(tp.size() - modalities.size());
1496     int lowest_T = T_at_level.back();
1497     int num_features = 0;
1498     for (int i = 0; i < (int)modalities.size(); ++i)
1499     {
1500       const Template& templ = tp[lowest_start + i];
1501       num_features += static_cast<int>(templ.features.size());
1502       similarity(lowest_lm[i], templ, similarities[i], sizes.back(), lowest_T);
1503     }
1504 
1505     // Combine into overall similarity
1506     /// @todo Support weighting the modalities
1507     Mat total_similarity;
1508     addSimilarities(similarities, total_similarity);
1509 
1510     // Convert user-friendly percentage to raw similarity threshold. The percentage
1511     // threshold scales from half the max response (what you would expect from applying
1512     // the template to a completely random image) to the max response.
1513     // NOTE: This assumes max per-feature response is 4, so we scale between [2*nf, 4*nf].
1514     int raw_threshold = static_cast<int>(2*num_features + (threshold / 100.f) * (2*num_features) + 0.5f);
1515 
1516     // Find initial matches
1517     std::vector<Match> candidates;
1518     for (int r = 0; r < total_similarity.rows; ++r)
1519     {
1520       ushort* row = total_similarity.ptr<ushort>(r);
1521       for (int c = 0; c < total_similarity.cols; ++c)
1522       {
1523         int raw_score = row[c];
1524         if (raw_score > raw_threshold)
1525         {
1526           int offset = lowest_T / 2 + (lowest_T % 2 - 1);
1527           int x = c * lowest_T + offset;
1528           int y = r * lowest_T + offset;
1529           float score =(raw_score * 100.f) / (4 * num_features) + 0.5f;
1530           candidates.push_back(Match(x, y, score, class_id, static_cast<int>(template_id)));
1531         }
1532       }
1533     }
1534 
1535     // Locally refine each match by marching up the pyramid
1536     for (int l = pyramid_levels - 2; l >= 0; --l)
1537     {
1538       const std::vector<LinearMemories>& lms = lm_pyramid[l];
1539       int T = T_at_level[l];
1540       int start = static_cast<int>(l * modalities.size());
1541       Size size = sizes[l];
1542       int border = 8 * T;
1543       int offset = T / 2 + (T % 2 - 1);
1544       int max_x = size.width - tp[start].width - border;
1545       int max_y = size.height - tp[start].height - border;
1546 
1547       std::vector<Mat> similarities2(modalities.size());
1548       Mat total_similarity2;
1549       for (int m = 0; m < (int)candidates.size(); ++m)
1550       {
1551         Match& match2 = candidates[m];
1552         int x = match2.x * 2 + 1; /// @todo Support other pyramid distance
1553         int y = match2.y * 2 + 1;
1554 
1555         // Require 8 (reduced) row/cols to the up/left
1556         x = std::max(x, border);
1557         y = std::max(y, border);
1558 
1559         // Require 8 (reduced) row/cols to the down/left, plus the template size
1560         x = std::min(x, max_x);
1561         y = std::min(y, max_y);
1562 
1563         // Compute local similarity maps for each modality
1564         int numFeatures = 0;
1565         for (int i = 0; i < (int)modalities.size(); ++i)
1566         {
1567           const Template& templ = tp[start + i];
1568           numFeatures += static_cast<int>(templ.features.size());
1569           similarityLocal(lms[i], templ, similarities2[i], size, T, Point(x, y));
1570         }
1571         addSimilarities(similarities2, total_similarity2);
1572 
1573         // Find best local adjustment
1574         int best_score = 0;
1575         int best_r = -1, best_c = -1;
1576         for (int r = 0; r < total_similarity2.rows; ++r)
1577         {
1578           ushort* row = total_similarity2.ptr<ushort>(r);
1579           for (int c = 0; c < total_similarity2.cols; ++c)
1580           {
1581             int score = row[c];
1582             if (score > best_score)
1583             {
1584               best_score = score;
1585               best_r = r;
1586               best_c = c;
1587             }
1588           }
1589         }
1590         // Update current match
1591         match2.x = (x / T - 8 + best_c) * T + offset;
1592         match2.y = (y / T - 8 + best_r) * T + offset;
1593         match2.similarity = (best_score * 100.f) / (4 * numFeatures);
1594       }
1595 
1596       // Filter out any matches that drop below the similarity threshold
1597       std::vector<Match>::iterator new_end = std::remove_if(candidates.begin(), candidates.end(),
1598                                                             MatchPredicate(threshold));
1599       candidates.erase(new_end, candidates.end());
1600     }
1601 
1602     matches.insert(matches.end(), candidates.begin(), candidates.end());
1603   }
1604 }
1605 
addTemplate(const std::vector<Mat> & sources,const String & class_id,const Mat & object_mask,Rect * bounding_box)1606 int Detector::addTemplate(const std::vector<Mat>& sources, const String& class_id,
1607                           const Mat& object_mask, Rect* bounding_box)
1608 {
1609   int num_modalities = static_cast<int>(modalities.size());
1610   std::vector<TemplatePyramid>& template_pyramids = class_templates[class_id];
1611   int template_id = static_cast<int>(template_pyramids.size());
1612 
1613   TemplatePyramid tp;
1614   tp.resize(num_modalities * pyramid_levels);
1615 
1616   // For each modality...
1617   for (int i = 0; i < num_modalities; ++i)
1618   {
1619     // Extract a template at each pyramid level
1620     Ptr<QuantizedPyramid> qp = modalities[i]->process(sources[i], object_mask);
1621     for (int l = 0; l < pyramid_levels; ++l)
1622     {
1623       /// @todo Could do mask subsampling here instead of in pyrDown()
1624       if (l > 0)
1625         qp->pyrDown();
1626 
1627       bool success = qp->extractTemplate(tp[l*num_modalities + i]);
1628       if (!success)
1629         return -1;
1630     }
1631   }
1632 
1633   Rect bb = cropTemplates(tp);
1634   if (bounding_box)
1635     *bounding_box = bb;
1636 
1637   /// @todo Can probably avoid a copy of tp here with swap
1638   template_pyramids.push_back(tp);
1639   return template_id;
1640 }
1641 
addSyntheticTemplate(const std::vector<Template> & templates,const String & class_id)1642 int Detector::addSyntheticTemplate(const std::vector<Template>& templates, const String& class_id)
1643 {
1644   std::vector<TemplatePyramid>& template_pyramids = class_templates[class_id];
1645   int template_id = static_cast<int>(template_pyramids.size());
1646   template_pyramids.push_back(templates);
1647   return template_id;
1648 }
1649 
getTemplates(const String & class_id,int template_id) const1650 const std::vector<Template>& Detector::getTemplates(const String& class_id, int template_id) const
1651 {
1652   TemplatesMap::const_iterator i = class_templates.find(class_id);
1653   CV_Assert(i != class_templates.end());
1654   CV_Assert(i->second.size() > size_t(template_id));
1655   return i->second[template_id];
1656 }
1657 
numTemplates() const1658 int Detector::numTemplates() const
1659 {
1660   int ret = 0;
1661   TemplatesMap::const_iterator i = class_templates.begin(), iend = class_templates.end();
1662   for ( ; i != iend; ++i)
1663     ret += static_cast<int>(i->second.size());
1664   return ret;
1665 }
1666 
numTemplates(const String & class_id) const1667 int Detector::numTemplates(const String& class_id) const
1668 {
1669   TemplatesMap::const_iterator i = class_templates.find(class_id);
1670   if (i == class_templates.end())
1671     return 0;
1672   return static_cast<int>(i->second.size());
1673 }
1674 
classIds() const1675 std::vector<String> Detector::classIds() const
1676 {
1677   std::vector<String> ids;
1678   TemplatesMap::const_iterator i = class_templates.begin(), iend = class_templates.end();
1679   for ( ; i != iend; ++i)
1680   {
1681     ids.push_back(i->first);
1682   }
1683 
1684   return ids;
1685 }
1686 
read(const FileNode & fn)1687 void Detector::read(const FileNode& fn)
1688 {
1689   class_templates.clear();
1690   pyramid_levels = fn["pyramid_levels"];
1691   fn["T"] >> T_at_level;
1692 
1693   modalities.clear();
1694   FileNode modalities_fn = fn["modalities"];
1695   FileNodeIterator it = modalities_fn.begin(), it_end = modalities_fn.end();
1696   for ( ; it != it_end; ++it)
1697   {
1698     modalities.push_back(Modality::create(*it));
1699   }
1700 }
1701 
write(FileStorage & fs) const1702 void Detector::write(FileStorage& fs) const
1703 {
1704   fs << "pyramid_levels" << pyramid_levels;
1705   fs << "T" << T_at_level;
1706 
1707   fs << "modalities" << "[";
1708   for (int i = 0; i < (int)modalities.size(); ++i)
1709   {
1710     fs << "{";
1711     modalities[i]->write(fs);
1712     fs << "}";
1713   }
1714   fs << "]"; // modalities
1715 }
1716 
readClass(const FileNode & fn,const String & class_id_override)1717   String Detector::readClass(const FileNode& fn, const String &class_id_override)
1718   {
1719   // Verify compatible with Detector settings
1720   FileNode mod_fn = fn["modalities"];
1721   CV_Assert(mod_fn.size() == modalities.size());
1722   FileNodeIterator mod_it = mod_fn.begin(), mod_it_end = mod_fn.end();
1723   int i = 0;
1724   for ( ; mod_it != mod_it_end; ++mod_it, ++i)
1725     CV_Assert(modalities[i]->name() == (String)(*mod_it));
1726   CV_Assert((int)fn["pyramid_levels"] == pyramid_levels);
1727 
1728   // Detector should not already have this class
1729     String class_id;
1730     if (class_id_override.empty())
1731     {
1732       String class_id_tmp = fn["class_id"];
1733       CV_Assert(class_templates.find(class_id_tmp) == class_templates.end());
1734       class_id = class_id_tmp;
1735     }
1736     else
1737     {
1738       class_id = class_id_override;
1739     }
1740 
1741   TemplatesMap::value_type v(class_id, std::vector<TemplatePyramid>());
1742   std::vector<TemplatePyramid>& tps = v.second;
1743   int expected_id = 0;
1744 
1745   FileNode tps_fn = fn["template_pyramids"];
1746   tps.resize(tps_fn.size());
1747   FileNodeIterator tps_it = tps_fn.begin(), tps_it_end = tps_fn.end();
1748   for ( ; tps_it != tps_it_end; ++tps_it, ++expected_id)
1749   {
1750     int template_id = (*tps_it)["template_id"];
1751     CV_Assert(template_id == expected_id);
1752     FileNode templates_fn = (*tps_it)["templates"];
1753     tps[template_id].resize(templates_fn.size());
1754 
1755     FileNodeIterator templ_it = templates_fn.begin(), templ_it_end = templates_fn.end();
1756     int idx = 0;
1757     for ( ; templ_it != templ_it_end; ++templ_it)
1758     {
1759       tps[template_id][idx++].read(*templ_it);
1760     }
1761   }
1762 
1763   class_templates.insert(v);
1764   return class_id;
1765 }
1766 
writeClass(const String & class_id,FileStorage & fs) const1767 void Detector::writeClass(const String& class_id, FileStorage& fs) const
1768 {
1769   TemplatesMap::const_iterator it = class_templates.find(class_id);
1770   CV_Assert(it != class_templates.end());
1771   const std::vector<TemplatePyramid>& tps = it->second;
1772 
1773   fs << "class_id" << it->first;
1774   fs << "modalities" << "[:";
1775   for (size_t i = 0; i < modalities.size(); ++i)
1776     fs << modalities[i]->name();
1777   fs << "]"; // modalities
1778   fs << "pyramid_levels" << pyramid_levels;
1779   fs << "template_pyramids" << "[";
1780   for (size_t i = 0; i < tps.size(); ++i)
1781   {
1782     const TemplatePyramid& tp = tps[i];
1783     fs << "{";
1784     fs << "template_id" << int(i); //TODO is this cast correct? won't be good if rolls over...
1785     fs << "templates" << "[";
1786     for (size_t j = 0; j < tp.size(); ++j)
1787     {
1788       fs << "{";
1789       tp[j].write(fs);
1790       fs << "}"; // current template
1791     }
1792     fs << "]"; // templates
1793     fs << "}"; // current pyramid
1794   }
1795   fs << "]"; // pyramids
1796 }
1797 
readClasses(const std::vector<String> & class_ids,const String & format)1798 void Detector::readClasses(const std::vector<String>& class_ids,
1799                            const String& format)
1800 {
1801   for (size_t i = 0; i < class_ids.size(); ++i)
1802   {
1803     const String& class_id = class_ids[i];
1804     String filename = cv::format(format.c_str(), class_id.c_str());
1805     FileStorage fs(filename, FileStorage::READ);
1806     readClass(fs.root());
1807   }
1808 }
1809 
writeClasses(const String & format) const1810 void Detector::writeClasses(const String& format) const
1811 {
1812   TemplatesMap::const_iterator it = class_templates.begin(), it_end = class_templates.end();
1813   for ( ; it != it_end; ++it)
1814   {
1815     const String& class_id = it->first;
1816     String filename = cv::format(format.c_str(), class_id.c_str());
1817     FileStorage fs(filename, FileStorage::WRITE);
1818     writeClass(class_id, fs);
1819   }
1820 }
1821 
1822 static const int T_DEFAULTS[] = {5, 8};
1823 
getDefaultLINE()1824 Ptr<Detector> getDefaultLINE()
1825 {
1826   std::vector< Ptr<Modality> > modalities;
1827   modalities.push_back(makePtr<ColorGradient>());
1828   return makePtr<Detector>(modalities, std::vector<int>(T_DEFAULTS, T_DEFAULTS + 2));
1829 }
1830 
getDefaultLINEMOD()1831 Ptr<Detector> getDefaultLINEMOD()
1832 {
1833   std::vector< Ptr<Modality> > modalities;
1834   modalities.push_back(makePtr<ColorGradient>());
1835   modalities.push_back(makePtr<DepthNormal>());
1836   return makePtr<Detector>(modalities, std::vector<int>(T_DEFAULTS, T_DEFAULTS + 2));
1837 }
1838 
1839 } // namespace linemod
1840 } // namespace cv
1841