1 // This is gel/vifa/vifa_int_face_attr_common.cxx
2 #include <iostream>
3 #include <cmath>
4 #include "vifa_int_face_attr_common.h"
5 //:
6 // \file
7 
8 #include <vdgl/vdgl_fit_lines.h>
9 #include <vtol/vtol_intensity_face.h>
10 #include "vgl/vgl_point_2d.h"
11 #include <vsol/vsol_line_2d.h>
12 #include <vtol/vtol_vertex_sptr.h>
13 #include <vifa/vifa_group_pgram.h>
14 #ifdef _MSC_VER
15 #  include "vcl_msvc_warnings.h"
16 #endif
17 
18 vifa_int_face_attr_common::
vifa_int_face_attr_common(vdgl_fit_lines_params * fitter_params,vifa_group_pgram_params * gpp_s,vifa_group_pgram_params * gpp_w,vifa_coll_lines_params * cpp,vifa_norm_params * np)19 vifa_int_face_attr_common(vdgl_fit_lines_params*    fitter_params,
20                           vifa_group_pgram_params*  gpp_s,
21                           vifa_group_pgram_params*  gpp_w,
22                           vifa_coll_lines_params*   cpp,
23                           vifa_norm_params*         np) :
24   vifa_int_face_attr_common_params(fitter_params, gpp_s, gpp_w, cpp, np)
25 {
26   this->init();
27 }
28 
29 vifa_int_face_attr_common::~vifa_int_face_attr_common() = default;
30 
31 
32 // ------------------------------------------------------------
33 // Data access & computation for non-attributes
34 //
35 
36 
37 edge_2d_list& vifa_int_face_attr_common::
GetFittedEdges()38 GetFittedEdges()
39 {
40   if (fitted_edges_.empty())
41     this->fit_lines();
42 
43   return fitted_edges_;
44 }
45 
46 coll_list& vifa_int_face_attr_common::
get_collinear_lines()47 get_collinear_lines()
48 {
49   if (!cpp_)
50     cpp_ = new vifa_coll_lines_params;
51 
52   if (collinear_lines_.empty())
53     this->find_collinear_lines();
54 
55   return collinear_lines_;
56 }
57 
58 double vifa_int_face_attr_common::
col_collapse()59 col_collapse()
60 {
61   double  collapsed_count = 0;
62   double  total_count = 0;
63 
64   for (auto & collinear_line : collinear_lines_)
65   {
66     total_count++;
67 
68     if (collinear_line->get_discard_flag())
69       collapsed_count++;
70   }
71 
72   if (total_count == 0)
73     return 0;
74 
75   return collapsed_count / total_count;
76 }
77 
78 double vifa_int_face_attr_common::
get_contrast_across_edge(const vtol_edge_sptr & e,double dflt_cont)79 get_contrast_across_edge(const vtol_edge_sptr&  e, double dflt_cont)
80 {
81   double    cont = dflt_cont;
82   face_list faces; e->faces(faces);
83 
84   // Expect only one or two intensity faces for 2-D case
85   if (faces.size() == 2)
86   {
87     vtol_intensity_face*  f1 = faces[0]->cast_to_intensity_face();
88     vtol_intensity_face*  f2 = faces[1]->cast_to_intensity_face();
89 
90     if (f1 && f2)
91       cont = std::fabs(f1->Io() - f2->Io());
92   }
93   return cont;
94 }
95 
96 vifa_coll_lines_sptr vifa_int_face_attr_common::
get_line_along_edge(vtol_edge * edge)97 get_line_along_edge(vtol_edge* edge)
98 {
99   vifa_coll_lines_sptr  ret(nullptr);
100   vtol_vertex_sptr    ev1 = edge->v1();
101   vtol_vertex_sptr    ev2 = edge->v2();
102 
103   for (auto & collinear_line : collinear_lines_)
104   {
105     edge_2d_list&  c_edges = collinear_line->get_contributors();
106 
107     for (auto & c_edge : c_edges)
108     {
109       vtol_vertex_sptr  v1 = c_edge->v1();
110       vtol_vertex_sptr  v2 = c_edge->v2();
111 
112       if (((*ev1 == *v1) && (*ev2 == *v2)) ||
113           ((*ev1 == *v2) && (*ev2 == *v1)))
114         ret = collinear_line;
115     }
116   }
117 
118   return ret;
119 }
120 
121 
122 // ------------------------------------------------------------
123 // Individual attribute computation
124 //
125 
126 
127 // Compute measure of projective parallelism, ratio of edges that have some
128 // projective overlap to total edge length.  Computes fitted_edges_ if needed.
129 float vifa_int_face_attr_common::
StrongParallelSal()130 StrongParallelSal()
131 {
132   if (para_sal_strong_ < 0)
133   {
134     if (!gpp_s_.ptr())
135     {
136       const float  angle_increment = 5.f;
137       gpp_s_ = new vifa_group_pgram_params(angle_increment);
138     }
139 
140     para_sal_strong_ = this->compute_parallel_sal(gpp_s_);
141   }
142 
143   return para_sal_strong_;
144 }
145 
146 float vifa_int_face_attr_common::
WeakParallelSal()147 WeakParallelSal()
148 {
149   if (para_sal_weak_ < 0)
150   {
151     if (!gpp_w_.ptr())
152     {
153       const float  angle_increment = 20.f;
154       gpp_w_ = new vifa_group_pgram_params(angle_increment);
155     }
156 
157     para_sal_weak_ = this->compute_parallel_sal(gpp_w_);
158   }
159 
160   return para_sal_weak_;
161 }
162 
163 
164 // ------------------------------------------------------------
165 // Protected methods
166 //
167 
168 
169 void vifa_int_face_attr_common::
init()170 init()
171 {
172   attributes_valid_    = false;
173   complexity_        = -1;
174   weighted_complexity_  = -1;
175   aspect_ratio_      = -1;
176   peri_length_      = -1;
177   weighted_peri_length_  = -1;
178   para_sal_weak_      = -1;
179   para_sal_strong_    = -1;
180 }
181 
182 // Fit edges of face_ with straight lines, but only if cached versions
183 // are not available.  Sets fitter_params_ with defaults if empty.
184 // Results are added to fitted_edges_.
185 void vifa_int_face_attr_common::
fit_lines()186 fit_lines()
187 {
188   edge_2d_list  edges_in_vect = this->GetEdges();
189 
190 #ifdef DEBUG
191     std::cout << "ifac::fit_lines(): " << edges_in_vect.size()
192              << " edges available\n";
193 #endif
194 
195     if (edges_in_vect.empty()) {
196       std::cerr << "vifa_int_face_attr_common::fit_lines: face_ is not set\n";
197       return;
198     }
199 
200   std::vector<vdgl_digital_curve_sptr>  curves_in;
201   for (auto & ei : edges_in_vect)
202   {
203     vsol_curve_2d_sptr c = ei->curve();
204     vdgl_digital_curve_sptr dc = c->cast_to_vdgl_digital_curve();
205     if (!dc)
206       continue;
207     curves_in.push_back(dc);
208   }
209 
210   if (!fitter_params_.ptr())
211   {
212     constexpr int fit_length = 6;
213     fitter_params_ = new vdgl_fit_lines_params(fit_length);
214   }
215 
216   // Call the line fitting routine (thanks Joe!)
217   vdgl_fit_lines  fitter(*(fitter_params_.ptr()));
218   fitter.set_curves(curves_in);
219   std::vector<vsol_line_2d_sptr>&  segs = fitter.get_line_segs();
220 
221 #ifdef DEBUG
222     std::cout << "ifac::fit_lines(): " << segs.size() << " segments from fitter\n";
223 #endif
224 
225   // Convert fitter output to edges & update statistics
226   auto  segi = segs.begin();
227   for (; segi != segs.end(); segi++)
228   {
229     vsol_line_2d_sptr  seg = *segi;
230     vtol_vertex_2d_sptr  v1 = new vtol_vertex_2d(*(seg->p0()));
231     vtol_vertex_2d_sptr  v2 = new vtol_vertex_2d(*(seg->p1()));
232     vtol_edge_2d_sptr  e = new vtol_edge_2d(v1, v2);
233     fitted_edges_.push_back(e);
234 
235     // Update statistics
236     fitted_edges_stats_.add_sample(seg->length());
237   }
238 }
239 
240 void vifa_int_face_attr_common::
find_collinear_lines()241 find_collinear_lines()
242 {
243   // sort fitted edges by length into f_edges
244   edge_2d_list  unsorted_edges = this->GetFittedEdges();
245   edge_2d_list  f_edges;
246   for (auto & unsorted_edge : unsorted_edges)
247   {
248     double        len = unsorted_edge->curve()->length();
249     auto  slot = f_edges.begin();
250     for (; slot != f_edges.end(); ++slot)
251       if ((*slot)->curve()->length() < len)
252       {
253         f_edges.insert(slot, unsorted_edge);
254         break;
255       }
256   }
257 
258   // build up a list of collinear line buckets
259   coll_list    unfiltered_lines;
260   coll_iterator  match;
261 
262 #ifdef DEBUG
263   std::cout << "Collineating: ";
264 #endif
265   for (auto & f_edge : f_edges)
266   {
267 #ifdef DEBUG
268     std::cout << '.';
269 #endif
270 
271     if (f_edge->curve()->length() == 0)
272       continue;
273 
274     bool  match_flag = find_collinear_match(f_edge,
275                                             unfiltered_lines,
276                                             cpp_->midpt_distance(),
277                                             match);
278 
279     if (!match_flag)
280     {
281       vifa_coll_lines_sptr  cl(new vifa_coll_lines(f_edge, cpp_->angle_tolerance()));
282 
283       unfiltered_lines.push_back(cl);
284     }
285     else
286       (*match)->add_and_update(f_edge);
287   }
288 
289 #ifdef DEBUG
290   std::cout << std::endl;
291 #endif
292 
293   // remove lines whose support is too low
294   for (auto & unfiltered_line : unfiltered_lines)
295   {
296     double  span = unfiltered_line->spanning_length();
297     double  support = unfiltered_line->support_length();
298 
299     if ((support/span) >= cpp_->discard_threshold())
300     {
301       collinear_lines_.push_back(unfiltered_line);
302       col_span_.add_sample(span);
303       col_support_.add_sample(support);
304       col_contrib_.add_sample(unfiltered_line->get_contributors().size());
305     }
306     else
307     {
308       // Unwind the unsupported line
309       edge_2d_list&  contrib = unfiltered_line->get_contributors();
310       for (auto & e : contrib)
311       {
312         vifa_coll_lines_sptr  cl(new vifa_coll_lines(e,
313                                                      cpp_->angle_tolerance(),
314                                                      cpp_->endpt_distance(),
315                                                      true));
316 
317         collinear_lines_.push_back(cl);
318 
319         double  span = cl->spanning_length();
320         double  support = cl->support_length();
321 
322         col_span_.add_sample(span);
323         col_support_.add_sample(support);
324         col_contrib_.add_sample(cl->get_contributors().size());
325       }
326     }
327   }
328 
329 #ifdef DEBUG
330   std::cout << unfiltered_lines.size() << " raw collinear lines; "
331            << collinear_lines_.size() << " lines above discard threshold "
332            << cpp_->discard_threshold_ << std::endl;
333 #endif
334 }
335 
336 bool vifa_int_face_attr_common::
find_collinear_match(const vtol_edge_2d_sptr & edge,coll_list & lines,double dist_threshold,coll_iterator & result)337 find_collinear_match(const vtol_edge_2d_sptr& edge,
338                      coll_list&        lines,
339                      double            dist_threshold,
340                      coll_iterator&    result)
341 {
342   double      min_dist = dist_threshold;
343   auto  match = lines.end();
344   for (auto c = lines.begin(); c != lines.end(); ++c)
345   {
346     double  dist = (*c)->get_measure(edge);
347     if ((dist < dist_threshold) && (dist < min_dist))
348     {
349       min_dist = dist;
350       match = c;
351     }
352   }
353 
354   if (match == lines.end())
355   {
356     return false;
357   }
358 
359   result = match;
360   return true;
361 }
362 
363 float vifa_int_face_attr_common::
compute_parallel_sal(const vifa_group_pgram_params_sptr & gpp)364 compute_parallel_sal(const vifa_group_pgram_params_sptr&  gpp)
365 {
366   float      sal = 0.f;
367   edge_2d_list  fedges = this->GetFittedEdges();
368 
369 #ifdef DEBUG
370   std::cout << "ifac::compute_parallel_sal(): " << fedges.size()
371            << " edges found\n";
372 #endif
373 
374   if (!fedges.empty()) {
375 #ifdef DEBUG
376     std::cout << (*fitter_params_);
377 #endif
378 
379     float    total_len = 0.f;
380     int      nlines = 0;
381     imp_line_list    lg_filtered;
382     for (auto & fedge : fedges)
383     {
384       vsol_curve_2d_sptr  cur = fedge->curve();
385       auto        len = float(cur->length());
386       total_len += len;
387 
388       if (len >= fitter_params_->min_fit_length_)
389       {
390         const vgl_point_2d<double>&  p1 = cur->p0()->get_p();
391         const vgl_point_2d<double>&  p2 = cur->p1()->get_p();
392         imp_line_sptr        filt_line  = new imp_line(p1, p2);
393 
394         lg_filtered.push_back(filt_line);
395         nlines++;
396       }
397     }
398 
399 #ifdef DEBUG
400     std::cout << "ifac::compute_parallel_sal(): " << nlines
401              << " lines after filtering, total_len = " << total_len << std::endl;
402 #endif
403 
404     // compute the score for the set of fitted lines
405     if (nlines > 2)
406     {
407       // Insert the lines into the parallellogram orientation index
408       vifa_group_pgram  gp(lg_filtered, *gpp);
409       gp.SetTemp1(total_len);
410 
411       // Get the resultant value
412       sal = float(gp.norm_parallel_line_length());
413     }
414   }
415 
416   return sal;
417 }
418