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