1 // This is gel/vifa/vifa_group_pgram.cxx
2 #include <iostream>
3 #include <cmath>
4 #include "vifa_group_pgram.h"
5 //:
6 // \file
7 
8 #ifdef _MSC_VER
9 #  include "vcl_msvc_warnings.h"
10 #endif
11 #include "vnl/vnl_math.h"
12 #include "vgl/vgl_point_2d.h"
13 #include "vgl/vgl_vector_2d.h"
14 #include "vgl/vgl_clip.h"
15 
16 #ifndef DEGTORAD
17 #define DEGTORAD  (vnl_math::pi_over_180)
18 #endif
19 
20 
21 vifa_group_pgram::
vifa_group_pgram(imp_line_list & lg,const vifa_group_pgram_params & old_params,double angle_range)22 vifa_group_pgram(imp_line_list&                  lg,
23                  const vifa_group_pgram_params&  old_params,
24                  double                          angle_range)
25   : vifa_group_pgram_params(old_params)
26 {
27   angle_range_ = angle_range;
28   th_dim_ = (int)std::ceil(angle_range_/angle_increment());
29   th_dim_++; //Include both 0 and angle_range_
30   bb_ = new vifa_bbox;
31 
32   for (int i = 0; i < th_dim_; i++)
33   {
34     auto*  illp = new imp_line_list;
35     curves_.push_back(illp);
36   }
37   this->Index(lg);
38 }
39 
40 //------------------------------------------------------------
41 //: Destructor
42 vifa_group_pgram::
~vifa_group_pgram()43 ~vifa_group_pgram()
44 {
45   for (auto illp : curves_)
46   {
47     for (auto & ili : *illp)
48     {
49       ili = nullptr;
50     }
51 
52     delete illp;
53   }
54 }
55 
56 //-----------------------------------------------------
57 //: Add an ImplicitLine to the index
58 void vifa_group_pgram::
Index(const imp_line_sptr & il)59 Index(const imp_line_sptr& il)
60 {
61   int  ang_bin = this->AngleLoc(il);
62   curves_[ang_bin]->push_back(il);
63 
64   this->touch();
65 }
66 
67 //------------------------------------------------------------
68 //: Add a set of ImplicitLines to the angular index
69 void vifa_group_pgram::
Index(imp_line_list & lg)70 Index(imp_line_list& lg)
71 {
72   for (auto & ili : lg)
73     Index(ili);
74 }
75 
76 //------------------------------------------------------------
77 //: Clear all lines from the index
78 void vifa_group_pgram::
Clear()79 Clear()
80 {
81   for (auto & curve : curves_)
82   {
83     imp_line_list*  illp = curve;
84     for (auto & ili : *illp)
85       ili = nullptr;
86 
87     delete illp;
88     curve = new imp_line_list;
89   }
90 
91   this->touch();
92 }
93 
94 //-----------------------------------------------------------------
95 //: Compute a histogram of parallel line coverage
96 //
GetCoverageHist()97 vifa_histogram_sptr vifa_group_pgram::GetCoverageHist() {
98   vifa_histogram_sptr  h = new vifa_histogram(th_dim_, 0.0f, float(angle_range_));
99 
100   float*  cnts = h->GetCounts();
101   for (int i = 0; i < th_dim_; i++)
102     cnts[i] = float(this->LineCoverage(i));
103   return h;
104 }
105 
106 //--------------------------------------------------------------
107 //: Get a populated line coverage corresponding to a given angle bin
108 //
109 vifa_line_cover_sptr vifa_group_pgram::
GetLineCover(int angle_bin)110 GetLineCover(int  angle_bin)
111 {
112   imp_line_sptr  il = this->LineAtAngle(angle_bin);
113 
114   // Get all the lines which are within +- angular resolution of angle_bin.
115   imp_line_list  lg;
116   this->CollectAdjacentLines(angle_bin, lg);
117 
118   if (lg.empty()) {
119     return nullptr;
120   }
121 
122   // Construct a bounding box from the ROI and clip
123   // the line defined by angle_bin
124   double  bx;
125   double  by;
126   double  ex;
127   double  ey;
128   this->CheckUpdateBoundingBox();
129   if (!vgl_clip_line_to_box(il->a(), il->b(), il->c(),
130                             bb_->min_x(), bb_->min_y(),
131                             bb_->max_x(), bb_->max_y(),
132                             bx, by, ex, ey))
133   {
134     std::cerr << "In vifa_group_pgram::GetLineCover(): No intersection found\n";
135     return nullptr;
136   }
137 
138   // Here we set the clipping bounds.
139   vgl_point_2d<double>  b(bx, by);
140   vgl_point_2d<double>  e(ex, ey);
141   il->set_points(b, e);
142 
143   // The line is cut into 1 pixel bins
144   int len = int(il->length());
145   if (!len)
146   {
147     return nullptr;
148   }
149 
150   vifa_line_cover_sptr  cov = new vifa_line_cover(il, len);
151   for (auto & ili : lg)
152   {
153     cov->InsertLine(ili);
154   }
155 
156   return cov;
157 }
158 
159 //--------------------------------------------------------------
160 //: Compute parallel line overlap on a line at the same orientation with midpoint at the center of the region of the line group.
161 //
162 double vifa_group_pgram::
LineCoverage(int angle_bin)163 LineCoverage(int  angle_bin)
164 {
165   vifa_line_cover_sptr  lc = this->GetLineCover(angle_bin);
166   return lc ? lc->GetCoverage() : 0.0;
167 }
168 
169 //--------------------------------------------------------------
170 //: Collect implicit line(s) from the angle array at orientations +- the given bin orientation.
171 //  Wrap around the end of the array so that 0 degrees and 180 degrees are considered parallel.
172 void vifa_group_pgram::
CollectAdjacentLines(int angle_bin,imp_line_list & lg)173 CollectAdjacentLines(int             angle_bin,
174                      imp_line_list&  lg)
175 {
176   if (angle_bin >= 0 && angle_bin < th_dim_)
177   {
178     if (angle_bin == 0)
179     {
180       // Case I - At the beginning of the array
181       lg.insert(lg.end(), curves_[th_dim_ - 1]->begin(),
182                 curves_[th_dim_ - 1]->end());
183       lg.insert(lg.end(), curves_[0]->begin(),
184                 curves_[0]->end());
185       lg.insert(lg.end(), curves_[1]->begin(),
186                 curves_[1]->end());
187     }
188     else if (angle_bin == th_dim_ - 1)
189     {
190       // Case II - At the end of the array
191       lg.insert(lg.end(), curves_[th_dim_ - 2]->begin(),
192                 curves_[th_dim_ - 2]->end());
193       lg.insert(lg.end(), curves_[th_dim_ - 1]->begin(),
194                 curves_[th_dim_ - 1]->end());
195       lg.insert(lg.end(), curves_[0]->begin(),
196                 curves_[0]->end());
197     }
198     else
199     {
200       // Case III - not near ends of the array
201       lg.insert(lg.end(), curves_[angle_bin - 1]->begin(),
202                 curves_[angle_bin - 1]->end());
203       lg.insert(lg.end(), curves_[angle_bin]->begin(),
204                 curves_[angle_bin]->end());
205       lg.insert(lg.end(), curves_[angle_bin + 1]->begin(),
206                 curves_[angle_bin + 1]->end());
207     }
208   }
209   else
210     std::cerr << "vifa_group_pgram::CollectAdjacentLines(): bad angle_bin\n";
211 }
212 
213 //------------------------------------------------------------------
214 //: Get Total length of parallel lines adjacent and including a bin
215 //
216 double vifa_group_pgram::
GetAdjacentPerimeter(int bin)217 GetAdjacentPerimeter(int  bin)
218 {
219   imp_line_list  lg;
220   this->CollectAdjacentLines(bin, lg);
221 
222   double  sum = 0;
223   for (auto & ili : lg)
224   {
225     vgl_vector_2d<double>  v = ili->point2() - ili->point1();
226     sum += v.length();
227   }
228 
229   return sum;
230 }
231 
232 //------------------------------------------------------
233 //: Compute the total length of parallel lines normalized by the total edge perimeter
norm_parallel_line_length()234 double vifa_group_pgram::norm_parallel_line_length() {
235   this->ComputeDominantDirs();
236   if (dominant_dirs_.empty()) {
237     // No basis
238     return 0.0;
239   }
240 
241   double max_cover = 0.0;
242   int    max_dir = 0;
243   auto  iit = dominant_dirs_.begin();
244   for (; iit != dominant_dirs_.end(); iit++)
245   {
246     int            dir = (*iit);
247     vifa_line_cover_sptr  lc = this->GetLineCover(dir);
248     if (!(lc.ptr()))
249     {
250 //      std::cout << "vgg::norm_parallel_line_length(): "
251 //               << "No line cover found for dir " << dir << std::endl;
252 
253       // Is this right?  What about other dominant directions?
254       // return 0.0;
255       continue;
256     }
257 
258     double  cov = lc->GetCoverage();
259 
260     if (cov > max_cover)
261     {
262       max_cover = cov;
263       max_dir = dir;
264     }
265   }
266 
267   double  per = this->GetAdjacentPerimeter(max_dir);
268 
269 //  std::cout << "vgg::norm_parallel_line_length(): per = " << per
270 //           << ", tmp1_ = " << tmp1_ << std::endl;
271 
272   return 1.5 * per / tmp1_;
273 }
274 
275 //---------------------------------------------------------
276 //: Find the angle bin corresponding to an implicit_line
277 int vifa_group_pgram::
AngleLoc(const imp_line_sptr & il)278 AngleLoc(const imp_line_sptr&  il)
279 {
280   // Compute angle index
281   double  angle = std::fmod(il->slope_degrees(), 180.0);
282   if (angle < 0.0)
283     angle += 180.0;
284 
285   if (angle > angle_range_)
286   {
287     std::cerr << "In vifa_group_pgram::AngleLoc(): angle " << angle
288              << " was outside the angle range " << angle_range_ << std::endl;
289     return 0;
290   }
291 
292   return (int)(std::floor(angle / angle_increment()));
293 }
294 
295 //--------------------------------------------------------------
296 //: Define a line passing through the center of the Hough ROI and at an angle corresponding to the angle bin
297 imp_line_sptr vifa_group_pgram::
LineAtAngle(int angle_bin)298 LineAtAngle(int  angle_bin)
299 {
300   // Get the new line's direction unit vector
301   double          ang_rad = DEGTORAD * angle_bin * angle_increment();
302   vgl_vector_2d<double>  d(std::cos(ang_rad), std::sin(ang_rad));
303 
304   // Get the new line's midpoint (bounding box centroid)
305   this->CheckUpdateBoundingBox();
306   vgl_point_2d<double>  m = bb_->centroid();
307 
308   // Return a new line
309   imp_line_sptr      il = new imp_line(d, m);
310   return il;
311 }
312 
313 //------------------------------------------------------
314 //: Compute the bounding box of the current index
315 //
ComputeBoundingBox()316 void vifa_group_pgram::ComputeBoundingBox() {
317   // Reset the bounding box
318   bb_->empty();
319 
320   for (auto illp : curves_)
321   {
322     for (auto & ili : *illp)
323     {
324       const imp_line_sptr&  il = ili;
325 
326       bb_->add(il->point1());
327       bb_->add(il->point2());
328     }
329   }
330 
331   // Update the bounding box timestamp
332   bb_->touch();
333 }
334 
335 //---------------------------------------------------------
336 //: Compute the dominant directions using the coverage histogram.
337 //  A dominant direction is a local maximum in the coverage distribution.
338 //
ComputeDominantDirs()339 void vifa_group_pgram::ComputeDominantDirs() {
340   dominant_dirs_.clear();
341 
342   // Get the coverage histogram and do find local maxima.
343   vifa_histogram_sptr  h = this->GetCoverageHist();
344   vifa_histogram_sptr  h_sup = h->NonMaximumSupress(max_suppress_radius(), true);
345   float*               cnts = h_sup->GetCounts();
346   int                  max_idx = h_sup->GetRes();
347 
348   // Rebuild the dominant direction array
349   for (int i = 0; i < max_idx; i++)
350     if (cnts[i] > 0)
351       dominant_dirs_.push_back(i);
352 
353 //  std::cout << "vgg::ComputeDominantDirs(): max_idx = " << max_idx << ", "
354 //           << dominant_dirs_.size() << " dominant directions found\n";
355 }
356