1 // This is brl/bseg/sdet/sdet_nonmax_suppression.cxx
2 #include <iostream>
3 #include <cstdlib>
4 #include "sdet_nonmax_suppression.h"
5 //:
6 // \file
7 #ifdef _MSC_VER
8 #  include "vcl_msvc_warnings.h"
9 #endif
10 #include <vsol/vsol_point_2d.h>
11 #include <vsol/vsol_line_2d.h>
12 #include "vgl/vgl_point_2d.h"
13 #include "vnl/vnl_matrix.h"
14 #include <vnl/algo/vnl_svd.h>
15 #include "vgl/vgl_homg_point_2d.h"
16 #include <vgl/algo/vgl_homg_operators_2d.h>
17 #include <cassert>
18 
19 //---------------------------------------------------------------
20 // Constructors
21 //
22 //----------------------------------------------------------------
23 
24 //: Constructor from a parameter block, and gradients along x and y directions given as arrays
25 //
sdet_nonmax_suppression(sdet_nonmax_suppression_params & nsp,vbl_array_2d<double> & grad_x,vbl_array_2d<double> & grad_y)26 sdet_nonmax_suppression::sdet_nonmax_suppression(sdet_nonmax_suppression_params& nsp,
27                                                  vbl_array_2d<double> &grad_x,
28                                                  vbl_array_2d<double> &grad_y)
29   : sdet_nonmax_suppression_params(nsp)
30 {
31   width_ = grad_x.rows();
32   height_ = grad_x.cols();
33   grad_x_.resize(width_, height_);
34   grad_y_.resize(width_, height_);
35   grad_mag_.resize(width_, height_);
36   grad_x_ = grad_x;
37   grad_y_ = grad_y;
38 
39   for (int j = 0; j < height_; j++)
40   {
41     for (int i = 0; i < width_; i++)
42     {
43       double val = std::sqrt(std::pow(grad_x_(i,j),2.0) + std::pow(grad_y_(i,j),2.0));
44       grad_mag_(i,j) = val;
45       if (val > max_grad_mag_)
46         max_grad_mag_ = val;
47     }
48   }
49   points_valid_ = false;
50   parabola_fit_type_ = nsp.pfit_type_;
51 }
52 
53 //: Constructor from a parameter block, gradient magnitudes given as an array and directions given as component arrays
54 //
sdet_nonmax_suppression(sdet_nonmax_suppression_params & nsp,vbl_array_2d<double> & dir_x,vbl_array_2d<double> & dir_y,vbl_array_2d<double> & grad_mag)55 sdet_nonmax_suppression::sdet_nonmax_suppression(sdet_nonmax_suppression_params& nsp,
56                                                  vbl_array_2d<double> &dir_x,
57                                                  vbl_array_2d<double> &dir_y,
58                                                  vbl_array_2d<double> &grad_mag)
59   : sdet_nonmax_suppression_params(nsp)
60 {
61   width_ = grad_mag.rows();
62   height_ = grad_mag.cols();
63   grad_x_.resize(width_, height_);
64   grad_y_.resize(width_, height_);
65   grad_mag_.resize(width_, height_);
66   grad_x_ = dir_x;
67   grad_y_ = dir_y;
68   grad_mag_ = grad_mag;
69 
70   for (int j = 0; j < height_; j++)
71   {
72     for (int i = 0; i < width_; i++)
73     {
74       double val = grad_mag_(i,j);
75       if (val > max_grad_mag_)
76         max_grad_mag_ = val;
77     }
78   }
79   points_valid_ = false;
80   parabola_fit_type_ = nsp.pfit_type_;
81 }
82 
83 //: Constructor from a parameter block, gradient magnitudes given as an array and the search directions
84 //
sdet_nonmax_suppression(sdet_nonmax_suppression_params & nsp,vbl_array_2d<double> & grad_mag,vbl_array_2d<vgl_vector_2d<double>> & directions)85 sdet_nonmax_suppression::sdet_nonmax_suppression(sdet_nonmax_suppression_params& nsp,
86                                                  vbl_array_2d<double> &grad_mag,
87                                                  vbl_array_2d<vgl_vector_2d <double> > &directions)
88   : sdet_nonmax_suppression_params(nsp)
89 {
90   width_ = grad_mag.rows();
91   height_ = grad_mag.cols();
92   grad_x_.resize(width_, height_);
93   grad_y_.resize(width_, height_);
94   grad_mag_.resize(width_, height_);
95   grad_mag_ = grad_mag;
96   for (int j = 0; j < height_; j++)
97   {
98     for (int i = 0; i < width_; i++)
99     {
100       vgl_vector_2d<double> direction = directions(i,j);
101       normalize(direction);
102       grad_x_(i,j) = grad_mag_(i,j) * direction.x();
103       grad_y_(i,j) = grad_mag_(i,j) * direction.y();
104       double val = grad_mag_(i,j);
105       if (val > max_grad_mag_)
106         max_grad_mag_ = val;
107     }
108   }
109   points_valid_ = false;
110   parabola_fit_type_ = nsp.pfit_type_;
111 }
112 
113 //: Constructor from a parameter block, and gradients along x and y directions given as images
114 //
sdet_nonmax_suppression(sdet_nonmax_suppression_params & nsp,vil_image_view<double> & grad_x,vil_image_view<double> & grad_y)115 sdet_nonmax_suppression::sdet_nonmax_suppression(sdet_nonmax_suppression_params& nsp,
116                                                  vil_image_view<double> &grad_x,
117                                                  vil_image_view<double> &grad_y)
118   : sdet_nonmax_suppression_params(nsp)
119 {
120   width_ = grad_x.ni();
121   height_ = grad_x.nj();
122   grad_x_.resize(width_, height_);
123   grad_y_.resize(width_, height_);
124   grad_mag_.resize(width_, height_);
125 
126   for (int j = 0; j < height_; j++)
127   {
128     for (int i = 0; i < width_; i++)
129     {
130       double val_x = grad_x(i,j);
131       double val_y = grad_y(i,j);
132       grad_x_(i,j) = val_x;
133       grad_y_(i,j) = val_y;
134       double val = std::sqrt(std::pow(val_x,2.0) + std::pow(val_y,2.0));
135       grad_mag_(i,j) = val;
136       if (val > max_grad_mag_)
137         max_grad_mag_ = val;
138     }
139   }
140   points_valid_ = false;
141   parabola_fit_type_ = nsp.pfit_type_;
142 }
143 
144 //: Constructor from a parameter block, gradient magnitudes given as an image and directions given as component image
145 //
sdet_nonmax_suppression(sdet_nonmax_suppression_params & nsp,vil_image_view<double> & dir_x,vil_image_view<double> & dir_y,vil_image_view<double> & grad_mag)146 sdet_nonmax_suppression::sdet_nonmax_suppression(sdet_nonmax_suppression_params& nsp,
147                                                  vil_image_view<double> &dir_x,
148                                                  vil_image_view<double> &dir_y,
149                                                  vil_image_view<double> &grad_mag)
150   : sdet_nonmax_suppression_params(nsp)
151 {
152   width_ = grad_mag.ni();
153   height_ = grad_mag.nj();
154   grad_x_.resize(width_, height_);
155   grad_y_.resize(width_, height_);
156   grad_mag_.resize(width_, height_);
157 
158   for (int j = 0; j < height_; j++)
159   {
160     for (int i = 0; i < width_; i++)
161     {
162       grad_x_(i,j) = dir_x(i,j);
163       grad_y_(i,j) = dir_y(i,j);
164       double val = grad_mag(i,j);
165       grad_mag_(i,j) = val;
166       if (val > max_grad_mag_)
167         max_grad_mag_ = val;
168     }
169   }
170   points_valid_ = false;
171   parabola_fit_type_ = nsp.pfit_type_;
172 }
173 
174 //: Constructor from a parameter block, gradient magnitudes given as an image and the search directions
175 //
sdet_nonmax_suppression(sdet_nonmax_suppression_params & nsp,vil_image_view<double> & grad_mag,vbl_array_2d<vgl_vector_2d<double>> & directions)176 sdet_nonmax_suppression::sdet_nonmax_suppression(sdet_nonmax_suppression_params& nsp,
177                                                  vil_image_view<double> &grad_mag,
178                                                  vbl_array_2d<vgl_vector_2d <double> > &directions)
179   : sdet_nonmax_suppression_params(nsp)
180 {
181   width_ = grad_mag.ni();
182   height_ = grad_mag.nj();
183   grad_x_.resize(width_, height_);
184   grad_y_.resize(width_, height_);
185   grad_mag_.resize(width_, height_);
186   for (int j = 0; j < height_; j++)
187   {
188     for (int i = 0; i < width_; i++)
189     {
190       double val = grad_mag(i,j);
191       grad_mag_(i,j) = val;
192       vgl_vector_2d<double> direction = directions(i,j);
193       normalize(direction);
194       grad_x_(i,j) = val * direction.x();
195       grad_y_(i,j) = val * direction.y();
196       if (val > max_grad_mag_)
197         max_grad_mag_ = val;
198     }
199   }
200   points_valid_ = false;
201   parabola_fit_type_ = nsp.pfit_type_;
202 }
203 
204 //:Default Destructor
205 sdet_nonmax_suppression::~sdet_nonmax_suppression()
206 = default;
207 
208 //-------------------------------------------------------------------------
209 //: Apply the algorithm
210 //
apply()211 void sdet_nonmax_suppression::apply()
212 {
213   if (points_valid_)
214     return;
215   points_.clear();
216 
217   // run non-maximum suppression at every point
218 
219   for (int y = 1; y < height_-1; y++)
220   {
221     for (int x = 1; x < width_-1; x++)
222     {
223       //if (grad_mag_(x,y) > max_grad_mag_ * thresh_ / 100.0)
224       if (grad_mag_(x,y) > thresh_)
225       {
226         double gx = grad_x_(x,y);
227         double gy = grad_y_(x,y);
228         vgl_vector_2d<double> direction(gx,gy);
229         normalize(direction);
230         if (std::abs(gx) > 10e-6 && std::abs(gy) > 10e-6)
231         {
232           int face_num = intersected_face_number(gx, gy);
233           assert(face_num != -1);
234           double s = intersection_parameter(gx, gy, face_num);
235           assert(s != -1000);
236           double f[3];
237           f_values(x, y, gx, gy, s, face_num, f);
238           double s_list[3];
239           s_list[0] = -s;
240           s_list[1] = 0.0;
241           s_list[2] = s;
242           if (f[1] > f[0] && f[1] > f[2])
243           {
244             double s_star = (parabola_fit_type_ == PFIT_3_POINTS)
245                           ? subpixel_s(s_list, f)
246                           : subpixel_s(x, y, direction);
247             if (-1.5 < s_star && s_star < 1.5)
248             {
249               vgl_point_2d<double> subpix(x + s_star * direction.x(), y + s_star * direction.y());
250               vsol_point_2d_sptr p = new vsol_point_2d(subpix.x(), subpix.y());
251               vsol_point_2d_sptr line_start = new vsol_point_2d(subpix.x()-direction.y()*0.5, subpix.y()+direction.x()*0.5);
252               vsol_point_2d_sptr line_end = new vsol_point_2d(subpix.x()+direction.y()*0.5, subpix.y()-direction.x()*0.5);
253               vsol_line_2d_sptr l = new vsol_line_2d(line_start, line_end);
254               points_.push_back(p);
255               lines_.push_back(l);
256               directions_.push_back(direction);
257             }
258           }
259         }
260       }
261     }
262   }
263   points_valid_ = true;
264 }
265 
intersected_face_number(double gx,double gy)266 int sdet_nonmax_suppression::intersected_face_number(double gx, double gy)
267 {
268   if (gx >= 0 && gy >= 0)
269   {
270     if (gx >= gy)
271       return 1;
272     else
273       return 2;
274   }
275   else if (gx < 0 && gy >= 0)
276   {
277     if (std::abs(gx) < gy)
278       return 3;
279     else
280       return 4;
281   }
282   else if (gx < 0 && gy < 0)
283   {
284     if (std::abs(gx) >= std::abs(gy))
285       return 5;
286     else
287       return 6;
288   }
289   else if (gx >= 0 && gy < 0)
290   {
291     if (gx < std::abs(gy))
292       return 7;
293     else
294       return 8;
295   }
296   return -1;
297 }
298 
intersection_parameter(double gx,double gy,int face_num)299 double sdet_nonmax_suppression::intersection_parameter(double gx, double gy, int face_num)
300 {
301   vgl_vector_2d<double> direction(gx,gy);
302   normalize(direction);
303   if (face_num == 1 || face_num == 8)
304     return 1.0/direction.x();
305   else if (face_num == 2 || face_num == 3)
306     return 1.0/direction.y();
307   else if (face_num == 4 || face_num == 5)
308     return -1.0/direction.x();
309   else if (face_num == 6 || face_num == 7)
310     return -1.0/direction.y();
311   return -1000.0;
312 }
313 
f_values(int x,int y,double gx,double gy,double s,int face_num,double * f)314 void sdet_nonmax_suppression::f_values(int x, int y, double gx, double gy, double s, int face_num, double *f)
315 {
316   vgl_vector_2d<double> direction(gx,gy);
317   normalize(direction);
318 
319   int corners[4];
320   get_relative_corner_coordinates(face_num, corners);
321 
322   vgl_vector_2d<double> intersection_point = s * direction;
323   vgl_vector_2d<double> corner1(corners[0], corners[1]); //have to convert to double for subtraction
324   vgl_vector_2d<double> corner2(corners[2], corners[3]); //have to convert to double for subtraction
325   double distance1 = length(intersection_point - corner1);
326   double distance2 = length(intersection_point - corner2);
327   double value1 = grad_mag_(x+corners[0], y+corners[1]);
328   double value2 = grad_mag_(x+corners[2], y+corners[3]);
329   double f_plus = value1 * distance2 + value2 * distance1;
330 
331   intersection_point = -s * direction;
332   corner1.set(-corners[0], -corners[1]); //have to convert to double for subtraction
333   corner2.set(-corners[2], -corners[3]); //have to convert to double for subtraction
334   distance1 = length(intersection_point - corner1);
335   distance2 = length(intersection_point - corner2);
336   value1 = grad_mag_(x-corners[0], y-corners[1]);
337   value2 = grad_mag_(x-corners[2], y-corners[3]);
338   double f_minus = value1 * distance2 + value2 * distance1;
339 
340   f[0] = f_minus;
341   f[1] = grad_mag_(x,y);
342   f[2] = f_plus;
343 }
344 
get_relative_corner_coordinates(int face_num,int * corners)345 void sdet_nonmax_suppression::get_relative_corner_coordinates(int face_num, int *corners)
346 {
347   switch (face_num)
348   {
349    case 1:
350      corners[0] = 1;
351      corners[1] = 0;
352      corners[2] = 1;
353      corners[3] = 1;
354      break;
355    case 2:
356      corners[0] = 1;
357      corners[1] = 1;
358      corners[2] = 0;
359      corners[3] = 1;
360      break;
361    case 3:
362      corners[0] = 0;
363      corners[1] = 1;
364      corners[2] = -1;
365      corners[3] = 1;
366     break;
367    case 4:
368      corners[0] = -1;
369      corners[1] = 1;
370      corners[2] = -1;
371      corners[3] = 0;
372     break;
373    case 5:
374      corners[0] = -1;
375      corners[1] = 0;
376      corners[2] = -1;
377      corners[3] = -1;
378     break;
379    case 6:
380      corners[0] = -1;
381      corners[1] = -1;
382      corners[2] = 0;
383      corners[3] = -1;
384     break;
385    case 7:
386      corners[0] = 0;
387      corners[1] = -1;
388      corners[2] = 1;
389      corners[3] = -1;
390     break;
391    case 8:
392      corners[0] = 1;
393      corners[1] = -1;
394      corners[2] = 1;
395      corners[3] = 0;
396     break;
397    default:
398      corners[0] = 0;
399      corners[1] = 0;
400      corners[2] = 0;
401      corners[3] = 0;
402   }
403 }
404 
subpixel_s(const double * s,const double * f)405 double sdet_nonmax_suppression::subpixel_s(const double *s, const double *f)
406 {
407   double A = f[2] / ((s[2]-s[0])*(s[2]-s[1]));
408   double B = f[1] / ((s[1]-s[0])*(s[1]-s[2]));
409   double C = f[0] / ((s[0]-s[1])*(s[0]-s[2]));
410   double s_star = ((A+B)*s[0] + (A+C)*s[1] + (B+C)*s[2]) / (2*(A+B+C));
411   return s_star;
412 }
413 
subpixel_s(int x,int y,vgl_vector_2d<double> direction)414 double sdet_nonmax_suppression::subpixel_s(int x, int y, vgl_vector_2d<double> direction)
415 {
416   double d;
417   double s;
418   double f;
419   vgl_homg_point_2d<double> p1(0.0, 0.0);
420   vgl_homg_point_2d<double> p2(direction.x(), direction.y());
421   vgl_homg_line_2d<double> line1(p1,p2);
422   //construct the matrices
423   vnl_matrix<double> A(9, 3);
424   vnl_matrix<double> B(9, 1);
425   vnl_matrix<double> P(3, 1);
426   int index = 0;
427   for (int j = -1; j <= 1; j++)
428   {
429     for (int i = -1; i <= 1; i++)
430     {
431       find_distance_s_and_f_for_point(i, j, line1, d, s, direction);
432       f = grad_mag_(x+i,y+j);
433       A(index, 0) = std::pow(s,2.0);
434       A(index, 1) = s;
435       A(index, 2) = 1.0;
436       B(index, 0) = f;
437       index++;
438     }
439   }
440 #if 0
441   vnl_matrix<double> A_trans = A.transpose();
442   P = (vnl_matrix_inverse<double>(A_trans*A) * A_trans) * B;
443 #else
444   vnl_svd<double> svd(A);
445   P = svd.solve(B);
446 #endif
447   double s_star = -P(1,0)/(2*P(0,0));
448   return s_star;
449 }
450 
find_distance_s_and_f_for_point(int x,int y,vgl_homg_line_2d<double> line,double & d,double & s,vgl_vector_2d<double> direction)451 void sdet_nonmax_suppression::find_distance_s_and_f_for_point(int x, int y, vgl_homg_line_2d<double> line,
452                                                               double &d, double &s, vgl_vector_2d<double> direction)
453 {
454   vgl_homg_point_2d<double> point(x,y);
455   vgl_homg_line_2d<double> perp_line = vgl_homg_operators_2d<double>::perp_line_through_point(line, point);
456   vgl_homg_point_2d<double> intersection_point_homg = vgl_homg_operators_2d<double>::intersection(line, perp_line);
457   vgl_point_2d<double> intersection_point(intersection_point_homg);
458   vgl_vector_2d<double> d_helper(x-intersection_point.x(), y-intersection_point.y());
459   d = length(d_helper);
460   s = intersection_point.x() / direction.x();
461 }
462 
463 //----------------------------------------------------------
464 //: Clear internal storage
465 //
clear()466 void sdet_nonmax_suppression::clear()
467 {
468   points_.clear();
469   points_valid_ = false;
470 }
471