1 #ifndef vtol_extract_topology_hxx_
2 #define vtol_extract_topology_hxx_
3
4 #include <iostream>
5 #include <iosfwd>
6 #include "vtol_extract_topology.h"
7 //:
8 // \file
9
10 #ifdef _MSC_VER
11 # include <vcl_msvc_warnings.h>
12 #endif
13 #include <cassert>
14
15 #include <vgl/vgl_point_2d.h>
16 #include <vgl/vgl_vector_2d.h>
17 #include <vgl/algo/vgl_line_2d_regression.h>
18
19 #include <vtol/vtol_vertex_2d.h>
20 #include <vtol/vtol_edge_2d.h>
21 #include <vtol/vtol_edge_2d_sptr.h>
22 #include <vtol/vtol_intensity_face.h>
23
24 #include <vsol/vsol_curve_2d_sptr.h>
25
26 #include <vdgl/vdgl_edgel.h>
27 #include <vdgl/vdgl_edgel_chain.h>
28 #include <vdgl/vdgl_interpolator_linear.h>
29 #include <vdgl/vdgl_digital_curve.h>
30
31 #ifndef NDEBUG
32 # define DBG( x ) x;
33 #else
34 # define DBG( x ) /*debugging removed*/ do {} while (false)
35 #endif
36
37 // =============================================================================
38 // EXTRACT TOPOLOGY
39 // =============================================================================
40
41 typedef vtol_extract_topology_vertex_node vertex_node;
42
43 // ---------------------------------------------------------------------------
44 // static variables and constants
45 constexpr unsigned vtol_extract_topology_vertex_node::null_index;
46 constexpr unsigned vtol_extract_topology_vertex_node::done_index;
47
48
49 // ---------------------------------------------------------------------------
50 // constructor
51
52
53 template< typename T >
54 vtol_extract_topology<T>::
vtol_extract_topology(label_image_type const & in_image,vtol_extract_topology_params const & in_params)55 vtol_extract_topology( label_image_type const& in_image,
56 vtol_extract_topology_params const& in_params )
57 : label_img_( in_image ),
58 params_( in_params )
59 {
60 compute_label_range();
61 construct_topology();
62 }
63
64
65 // ---------------------------------------------------------------------------
66 // vertices
67
68 template< typename T >
69 std::vector< vtol_vertex_2d_sptr >
70 vtol_extract_topology<T>::
vertices() const71 vertices() const
72 {
73 typedef std::vector< vertex_node >::const_iterator vertex_iterator_t;
74
75 std::vector< vtol_vertex_2d_sptr > verts;
76
77 for ( vertex_iterator_t i = node_list_.begin();
78 i != node_list_.end(); ++i ) {
79 verts.push_back( i->vertex );
80 }
81
82 return verts;
83 }
84
85
86 // ---------------------------------------------------------------------------
87 // faces
88
89 template< typename T >
90 std::vector< vtol_intensity_face_sptr >
91 vtol_extract_topology<T>::
faces(data_image_type const & data_img) const92 faces( data_image_type const& data_img ) const
93 {
94 region_collection region_list;
95 collect_regions( region_list );
96
97 // Generate faces for each label. A given label may generate more
98 // than one face based on containment, etc.
99 //
100 std::vector< vtol_intensity_face_sptr > faces;
101 for (auto & i : region_list) {
102 if ( ! i.empty() ) {
103 compute_faces( i, faces, &data_img );
104 }
105 }
106
107 return faces;
108 }
109
110
111 template< typename T >
112 std::vector< vtol_intensity_face_sptr >
113 vtol_extract_topology<T>::
faces() const114 faces( ) const
115 {
116 region_collection region_list;
117 collect_regions( region_list );
118
119 // Generate faces for each label. A given label may generate more
120 // than one face based on containment, etc.
121 //
122 std::vector< vtol_intensity_face_sptr > faces;
123 for (auto & i : region_list) {
124 if ( ! i.empty() ) {
125 compute_faces( i, faces, nullptr );
126 }
127 }
128
129 return faces;
130 }
131
132
133 // ---------------------------------------------------------------------------
134 // init
135
136 template< typename T >
137 void
138 vtol_extract_topology<T>::
compute_label_range()139 compute_label_range()
140 {
141 assert( label_img_.ni() >= 1 && label_img_.nj() >= 1 );
142
143 // Determine the label ranges
144 //
145 min_label_ = label_img_(0,0);
146 max_label_ = min_label_;
147 for ( unsigned j = 0; j < label_img_.nj(); ++j ) {
148 for ( unsigned i = 0; i < label_img_.ni(); ++i ) {
149 if ( min_label_ > label_img_(i,j) )
150 min_label_ = label_img_(i,j);
151 if ( max_label_ < label_img_(i,j) )
152 max_label_ = label_img_(i,j);
153 }
154 }
155 }
156
157 // ---------------------------------------------------------------------------
158 // label
159
160 template< typename LABEL_TYPE >
161 typename vtol_extract_topology< LABEL_TYPE >::LabelPoint
162 vtol_extract_topology< LABEL_TYPE >::
label(unsigned i,unsigned j) const163 label( unsigned i, unsigned j ) const
164 {
165 if ( i < label_img_.ni() && j < label_img_.nj() ) {
166 return LabelPoint(label_img_( i, j ), true);
167 }
168 else {
169 return LabelPoint(0, false);
170 }
171 }
172
173
174 // ---------------------------------------------------------------------------
175 // is junction vertex
176
177 template< typename T >
178 bool
179 vtol_extract_topology<T>::
is_junction_vertex(unsigned i,unsigned j) const180 is_junction_vertex( unsigned i, unsigned j ) const
181 {
182 // A junction must have at least three incident boundary edges
183
184 unsigned edge_count = 0;
185 for ( unsigned dir = 0; dir < 4; ++dir ) {
186 if ( is_edge( i, j, dir ) ) {
187 ++edge_count;
188 }
189 }
190
191 return edge_count >= 3;
192 }
193
194
195 // ---------------------------------------------------------------------------
196 // is boundary vertex
197
198 template< typename LABEL_TYPE >
199 bool
200 vtol_extract_topology< LABEL_TYPE >::
is_boundary_vertex(unsigned i,unsigned j) const201 is_boundary_vertex( unsigned i, unsigned j ) const
202 {
203 // A non-boundary (interior) point is surrounded by pixels of the
204 // same value.
205
206 LabelPoint pixel1( label( i , j ) );
207 LabelPoint pixel2( label( i , j-1 ) );
208 LabelPoint pixel3( label( i-1, j ) );
209 LabelPoint pixel4( label( i-1, j-1 ) );
210
211 return pixel1 != pixel2 || pixel2 != pixel3 || pixel3 != pixel4;
212 }
213
214
215 // ---------------------------------------------------------------------------
216 // is edge
217
218 template< typename LABEL_TYPE >
219 bool
220 vtol_extract_topology< LABEL_TYPE >::
is_edge(unsigned i,unsigned j,unsigned dir) const221 is_edge( unsigned i, unsigned j, unsigned dir ) const
222 {
223 LabelPoint left, right;
224
225 edge_labels( i, j, dir, left, right );
226
227 return left != right;
228 }
229
230
231 // ---------------------------------------------------------------------------
232 // edge labels
233
234 template< typename LABEL_TYPE >
235 void
236 vtol_extract_topology< LABEL_TYPE >::
edge_labels(unsigned i,unsigned j,unsigned dir,LabelPoint & left,LabelPoint & right) const237 edge_labels( unsigned i, unsigned j, unsigned dir,
238 LabelPoint& left, LabelPoint& right ) const
239 {
240 assert( dir <= 3 );
241
242 // These are the offsets to get the "left" pixel position for each
243 // direction given the vertex location (i,j). The vertices occur at
244 // the corners of the pixels, so that vertex (0,0) is at the
245 // top-left corner of pixel (0,0).
246 //
247 // The offsets for the "right" pixel in direction d is the offset
248 // for the left pixel in direction (d+1).
249 //
250 static const int offsets[4][2] =
251 { { 0, -1 }, { 0, 0 }, { -1, 0 }, { -1, -1 } };
252
253 unsigned dir2 = (dir+1) % 4;
254
255 left = label( i+offsets[dir ][0], j+offsets[dir ][1] );
256 right = label( i+offsets[dir2][0], j+offsets[dir2][1] );
257 }
258
259
260 // ---------------------------------------------------------------------------
261 // vertex index
262
263 template< typename T >
264 unsigned
265 vtol_extract_topology<T>::
vertex_index(unsigned i,unsigned j) const266 vertex_index( unsigned i, unsigned j ) const
267 {
268 assert( i < index_img_.ni() &&
269 j < index_img_.nj() );
270
271 return index_img_( i, j );
272 }
273
274
275 // ---------------------------------------------------------------------------
276 // set vertex index
277
278 template< typename T >
279 void
280 vtol_extract_topology<T>::
set_vertex_index(unsigned i,unsigned j,unsigned index)281 set_vertex_index( unsigned i, unsigned j, unsigned index )
282 {
283 assert( i < index_img_.ni() &&
284 j < index_img_.nj() );
285
286 index_img_( i, j ) = index;
287 }
288
289
290 // ---------------------------------------------------------------------------
291 // node
292
293 template< typename T >
294 vtol_extract_topology_vertex_node&
295 vtol_extract_topology<T>::
node(unsigned index)296 node( unsigned index )
297 {
298 assert( index < node_list_.size() );
299
300 return node_list_[index];
301 }
302
303
304 template< typename T >
305 vtol_extract_topology_vertex_node const&
306 vtol_extract_topology<T>::
node(unsigned index) const307 node( unsigned index ) const
308 {
309 assert( index < node_list_.size() );
310
311 return node_list_[index];
312 }
313
314
315 // ---------------------------------------------------------------------------
316 // move
317
318 template< typename T >
319 void
320 vtol_extract_topology<T>::
move(unsigned dir,unsigned & i,unsigned & j)321 move( unsigned dir, unsigned& i, unsigned& j )
322 {
323 assert( dir < 4 );
324
325 switch ( dir )
326 {
327 case 0: // right
328 ++i;
329 break;
330 case 1: // down
331 ++j;
332 break;
333 case 2: // left
334 --i;
335 break;
336 case 3: // up
337 --j;
338 break;
339 default: break; // never reached
340 }
341 }
342
343
344 // ---------------------------------------------------------------------------
345 // set mark
346
347 template< typename T >
348 void
349 vtol_extract_topology<T>::
set_mark(unsigned & marker,unsigned dir) const350 set_mark( unsigned& marker, unsigned dir ) const
351 {
352 marker |= ( 1 << dir );
353 }
354
355
356 // ---------------------------------------------------------------------------
357 // is marked
358
359 template< typename T >
360 bool
361 vtol_extract_topology<T>::
is_marked(unsigned marker,unsigned dir) const362 is_marked( unsigned marker, unsigned dir ) const
363 {
364 return ( marker & ( 1 << dir ) ) != 0;
365 }
366
367
368 // ---------------------------------------------------------------------------
369 // trace edge chain
370
371 template< typename T >
372 void
373 vtol_extract_topology<T>::
trace_edge_chain(unsigned i,unsigned j,unsigned dir)374 trace_edge_chain( unsigned i, unsigned j, unsigned dir )
375 {
376 // Quick exit if there is nothing to trace in this direction.
377 //
378 if ( ! is_edge( i, j, dir ) )
379 return;
380
381
382 // When constructing the spatial and topological objects from the
383 // "pixel corner" vertices, we add the (-.5,-.5) offset required to
384 // bring everything into pixel coordinates.
385
386 unsigned start_index = vertex_index( i, j );
387 unsigned start_dir = dir;
388
389 // The chain of "pixel corners" from one vertex to the other.
390 //
391 vdgl_edgel_chain_sptr chain = new vdgl_edgel_chain();
392
393 // Start vertex
394 chain->add_edgel( vdgl_edgel( i-0.5, j-0.5, -1, dir*90 ) );
395 move( dir, i, j );
396
397 // If we have a complete cycle (i.e. start==end), we have to step
398 // back one because vtol doesn't like edges with the same start and
399 // end. When we step back, we need to keep track of the direction
400 // from which we came to that point. We'll only ever need to step
401 // back once, so it is sufficient to keep the current direction
402 // (direction of the last step) in dir) and the previous direction
403 // in prev_dir.
404 unsigned int prev_dir = (unsigned int)(-1);
405
406 while ( vertex_index( i, j ) == vertex_node::null_index )
407 {
408 set_vertex_index( i, j, vertex_node::done_index );
409
410 chain->add_edgel( vdgl_edgel( i-0.5, j-0.5, -1, dir*90 ) );
411
412 // find and move in the outgoing direction. There should be
413 // exactly one, since this is not a vertex.
414 //
415 DBG( unsigned count = 0 );
416 prev_dir = dir;
417 dir = (dir+3) % 4; // same as dir = dir - 1
418 while ( ! is_edge( i, j, dir ) ) {
419 dir = (dir+1) % 4;
420 DBG( ++count );
421 DBG( assert( count < 3 ) );
422 }
423
424 move( dir, i, j );
425
426 // The same non-junction vertex should not appear on multiple
427 // traces. (The "reverse trace" is will be done below by just
428 // reversing the start and end.)
429 //
430 assert( vertex_index( i, j ) != vertex_node::done_index );
431 }
432
433 unsigned end_index = vertex_index( i, j );
434
435 if ( end_index == start_index ) {
436 // Construct a new vertex at the just-before-last point to avoid
437 // having a chain with identical end-points. Move backwards one
438 // unit and create a vertex.
439 move( (dir+2)%4, i, j );
440 set_vertex_index( i, j, node_list_.size() );
441 node_list_.emplace_back(i, j);
442 end_index = vertex_index( i, j );
443 dir = prev_dir; // the direction we came from to the new end point.
444 // The new end point is already in the edgel chain; no need to add again.
445 }
446 else {
447 // Add the end vertex
448 chain->add_edgel( vdgl_edgel( i-0.5, j-0.5 ) );
449 }
450
451 chain = smooth_chain( chain, params_.num_for_smooth );
452
453 vertex_node& start_node = node( start_index );
454 vertex_node& end_node = node( end_index );
455
456 vdgl_interpolator_sptr interp = new vdgl_interpolator_linear( chain );
457 vsol_curve_2d_sptr curve = new vdgl_digital_curve( interp );
458 vtol_edge_2d_sptr edge = new vtol_edge_2d( start_node.vertex,
459 end_node.vertex,
460 curve );
461
462 edgel_chain_sptr chain2 = new edgel_chain;
463 chain2->chain = chain;
464 chain2->edge = edge;
465
466 // Turn around, because the that is the direction we would begin in
467 // from the end node for a reverse trace.
468 //
469 dir = (dir+2) % 4;
470
471 start_node.link[ start_dir ] = end_index;
472 start_node.back_dir[ start_dir ] = dir;
473 start_node.edgel_chain[ start_dir ] = chain2;
474
475 end_node.link[ dir ] = start_index;
476 end_node.back_dir[ dir ] = start_dir;
477 end_node.edgel_chain[ dir ] = chain2;
478 }
479
480
481 // ---------------------------------------------------------------------------
482 // construct topology
483
484 template< typename T >
485 void
486 vtol_extract_topology<T>::
construct_topology()487 construct_topology( )
488 {
489 // Construct the list of vertex nodes from the junctions and
490 // initialize the vertex index array.
491 //
492 index_img_.set_size( label_img_.ni()+1, label_img_.nj()+1 );
493
494 node_list_.clear();
495 index_img_.fill( vertex_node::null_index );
496
497 for ( unsigned j = 0; j <= label_img_.nj(); ++j ) {
498 for ( unsigned i = 0; i <= label_img_.ni(); ++i ) {
499 if ( is_junction_vertex( i, j ) ) {
500 set_vertex_index( i, j, node_list_.size() );
501 node_list_.emplace_back(i, j);
502 }
503 }
504 }
505
506 // Construct the edge graph by following each edge from
507 // each vertex.
508 //
509 for ( unsigned index = 0; index < node_list_.size(); ++index ) {
510 for ( unsigned dir = 0; dir < 4; ++dir ) {
511 if ( node(index).link[dir] == vertex_node::null_index ) {
512 trace_edge_chain( node(index).i,
513 node(index).j,
514 dir );
515 }
516 }
517 }
518
519 // Look for untouched boundary vertices, and make them vertices
520 // too. Tracing from one of these must lead back to itself. The
521 // topology classes don't like edges that are themselves
522 // cycles. They expect that each edge has two distinct
523 // vertices. Instead of creating two vertices at the same point, we
524 // create two vertices adjacent to each other. Thus, each of these
525 // faces (created later) will be bounded by two edges, one
526 // potentially long edge, and one edge with length one pixel.
527 //
528 for ( unsigned j = 0; j <= label_img_.nj(); ++j ) {
529 for ( unsigned i = 0; i <= label_img_.ni(); ++i ) {
530 if ( vertex_index( i, j ) == vertex_node::null_index &&
531 is_boundary_vertex( i, j ) )
532 {
533 // Find the two outgoing directions
534 //
535 unsigned dir = 0;
536 while ( dir < 4 && ! is_edge( i, j, dir ) ) {
537 ++dir;
538 }
539 assert( dir < 4 );
540 unsigned dir2 = dir+1;
541 while ( dir2 < 4 && ! is_edge( i, j, dir2 ) ) {
542 ++dir2;
543 }
544 assert( dir2 < 4 );
545
546 // Create a vertex at this point
547 //
548 set_vertex_index( i, j, node_list_.size() );
549 node_list_.emplace_back(i, j);
550
551 // Create a vertex at a neighbour
552 //
553 unsigned i2 = i, j2 = j;
554 move( dir, i2, j2 );
555 assert( vertex_index( i2, j2 ) == vertex_node::null_index &&
556 is_boundary_vertex( i2, j2 ) );
557 set_vertex_index( i2, j2, node_list_.size() );
558 node_list_.emplace_back(i2, j2);
559
560 // Trace from here, going both ways
561 //
562 trace_edge_chain( i, j, dir );
563 trace_edge_chain( i, j, dir2 );
564 }
565 }
566 }
567
568 #ifndef NDEBUG
569 // Verify the integrity of the vertex graph
570 bool good = true;
571 for ( unsigned index = 0; index < node_list_.size(); ++index ) {
572 for ( unsigned dir = 0; dir < 4; ++dir ) {
573 if ( node(index).link[dir] != vertex_node::null_index ) {
574 unsigned nbr = node(index).link[dir];
575 unsigned back_dir = node(index).back_dir[dir];
576 if ( node(nbr).link[back_dir] != index ) {
577 std::cerr << "Bad back link on vertex " << index << " ("<<node(index).i
578 << ',' << node(index).j << " in dir " << dir << '\n'
579 << " link " << dir << " = " << node(index).link[dir] << ";\n"
580 << " back_dir " << dir << " = " << node(index).back_dir[dir] << '\n';
581 }
582 }
583 }
584 assert( good );
585 }
586 #endif
587 }
588
589
590 // ---------------------------------------------------------------------------
591 // trace face boundary
592
593 template< typename LABEL_TYPE >
594 bool
595 vtol_extract_topology< LABEL_TYPE >::
trace_face_boundary(std::vector<unsigned> & markers,unsigned index,unsigned dir,region_type & chain_list,LabelPoint & region_label) const596 trace_face_boundary( std::vector<unsigned>& markers,
597 unsigned index,
598 unsigned dir,
599 region_type& chain_list,
600 LabelPoint& region_label ) const
601 {
602 unsigned start_index = index;
603 LabelPoint start_left;
604 edge_labels( node(index).i, node(index).j, dir,
605 start_left, region_label );
606
607 #ifdef DEBUG
608 std::cout << "start left, region label: " << (int) start_left << ' '
609 << (int)region_label << " ; i,j: " << node(index).i << ' '
610 << node(index).j << " ; index,dir " << index << ' '
611 << dir << " ; label(i,j) = "
612 << (int)label_img_(node(index).i, node(index).j) << std::endl;
613 if ( region_label + 1 == min_label_ ) {
614 std::cout << "exiting" << std::endl;
615 return false;
616 }
617 #endif
618 if (!region_label.valid) {
619 return false;
620 }
621
622 // Find an interior pixel of this face based on the first edge we
623 // encounter. The vertex (i,j) corresponds to pixel-coordinate-space
624 // (i-0.5,j-0.5). Use this and the direction to get a point on the
625 // right of the first edge. The delta_* store the appropriate
626 // offsets for each direction.
627 //
628 static const int delta_i[4] = { 0, -1, -1, 0 };
629 static const int delta_j[4] = { 0, 0, -1, -1 };
630 chain_list.i = node(index).i + delta_i[dir];
631 chain_list.j = node(index).j + delta_j[dir];
632
633 #ifdef DEBUG
634 std::cout << "index " << index << " dir " << dir << " node " << node(index).i
635 << " delta " << delta_i[dir] << std::endl;
636 #endif
637 assert( chain_list.i < label_img_.ni() );
638 assert( chain_list.j < label_img_.nj() );
639
640 do {
641 // Mark the current direction of the current node as travelled,
642 // and go to the next node and find the outgoing direction there.
643
644 assert( ! is_marked( markers[index], dir ) );
645 set_mark( markers[index], dir );
646 chain_list.push_back( node(index).edgel_chain[dir] );
647
648 unsigned back_dir = node(index).back_dir[ dir ];
649 index = node(index).link[ dir ];
650 assert( index != vertex_node::null_index );
651
652 // Look from right to left, so that we are more conservative at
653 // corner touches. That is, we will take
654 //
655 // +----+
656 // | |
657 // +----+----+
658 // | |
659 // +----+
660 //
661 // as two regions instead of one figure-8 region.
662 //
663 // We keep the object on the right, which actually corresponds to
664 // the standard "keep the object on the left" in a right-handed
665 // coordinate system.
666 //
667 unsigned i = node(index).i;
668 unsigned j = node(index).j;
669 LabelPoint left, right;
670 dir = back_dir;
671 DBG( unsigned old_dir = dir % 4 );
672 do {
673 dir = (dir+3) % 4; // same as dir = dir - 1
674
675 // Make sure we haven't done a full cycle.
676 DBG( assert( dir != old_dir ) );
677
678 edge_labels( i, j, dir, left, right );
679 } while ( left == right || right != region_label );
680
681 } while ( index != start_index );
682
683 return true;
684 }
685
686
687 // ---------------------------------------------------------------------------
688 // collect regions
689
690 template< typename LABEL_TYPE >
691 void
692 vtol_extract_topology< LABEL_TYPE >::
collect_regions(region_collection & region_list) const693 collect_regions( region_collection& region_list ) const
694 {
695 // Use put marks about which nodes (vertices) and directions have
696 // been processed.
697 //
698 std::vector< unsigned > markers( node_list_.size(), 0 );
699
700 region_list.resize( max_label_ - min_label_ + 1 );
701
702 // Process each vertex, generating the boundary chains
703 //
704 for ( unsigned i = 0; i < node_list_.size(); ++i ) {
705 for ( unsigned dir = 0; dir < 4; ++dir ) {
706 if ( ! is_marked( markers[i], dir ) &&
707 node(i).link[dir] != vertex_node::null_index ) {
708 region_type_sptr chain = new region_type;
709 LabelPoint label;
710 if ( trace_face_boundary( markers, i, dir, *chain, label ) ) {
711 assert( (label.valid) && (label.label >= min_label_) );
712 region_list[ label.label - min_label_ ].push_back( chain );
713 }
714 }
715 }
716 }
717
718 #ifndef NDEBUG
719 // After we extract all the chains, we must have gone through every
720 // vertex in every available direction except for the
721 // counter-clockwise chain at the boundary (which is the border of
722 // the "outside" region).
723 //
724 for ( unsigned i = 0; i < node_list_.size(); ++i ) {
725 for ( unsigned dir = 0; dir < 4; ++dir ) {
726 assert( node(i).link[dir] == vertex_node::null_index ||
727 ( dir==0 && node(i).j==label_img_.nj() ) ||
728 ( dir==1 && node(i).i==0 ) ||
729 ( dir==2 && node(i).j==0 ) ||
730 ( dir==3 && node(i).i==label_img_.ni() ) ||
731 is_marked( markers[i], dir ) );
732 }
733 }
734 #endif
735 }
736
737
738 // ---------------------------------------------------------------------------
739 // compute faces
740
741 template< typename T >
742 void
743 vtol_extract_topology<T>::
compute_faces(std::vector<region_type_sptr> const & chains,std::vector<vtol_intensity_face_sptr> & faces,data_image_type const * data_img) const744 compute_faces( std::vector< region_type_sptr > const& chains,
745 std::vector< vtol_intensity_face_sptr >& faces,
746 data_image_type const* data_img ) const
747 {
748 assert( chains.size() > 0 );
749
750 // Determine the containment tree by repeatedly adding the chains
751 // into a tree. The adding process makes sure that the chain falls
752 // into the appropriate place in the containment hierarchy.
753
754 chain_tree_node universe( nullptr );
755 for (const auto & chain : chains) {
756 universe.add( chain );
757 }
758
759 // If we have a data image, use it to add digital region information
760 // to the face
761 //
762 if ( data_img ) {
763 finder_type* finder = new finder_type( label_img_, vil_region_finder_4_conn );
764 add_faces( faces, finder, data_img, &universe );
765 delete finder;
766 }
767 else {
768 add_faces( faces, nullptr, nullptr, &universe );
769 }
770 }
771
772 // ---------------------------------------------------------------------------
773 // add faces
774
775 template <typename T>
776 void
777 vtol_extract_topology<T>::
add_faces(std::vector<vtol_intensity_face_sptr> & faces,typename vtol_extract_topology<T>::finder_type * find,data_image_type const * img,typename vtol_extract_topology<T>::chain_tree_node * node,bool even_level) const778 add_faces( std::vector<vtol_intensity_face_sptr>& faces,
779 typename vtol_extract_topology<T>::finder_type* find,
780 data_image_type const* img,
781 typename vtol_extract_topology<T>::chain_tree_node* node,
782 bool even_level ) const
783 {
784 if ( even_level ) {
785 faces.push_back( node->make_face( find, img ) );
786 }
787 for ( unsigned i = 0; i < node->children.size(); ++i ) {
788 add_faces( faces, find, img, node->children[i], !even_level );
789 }
790 }
791
792 // ---------------------------------------------------------------------------
793 // contains
794
795
796 template <typename T>
797 bool
798 vtol_extract_topology<T>::
contains(region_type_sptr a,region_type_sptr b)799 contains( region_type_sptr a, region_type_sptr b )
800 {
801 assert( b );
802 if ( !a ) {
803 return true;
804 }
805 else {
806 // Odd number of crossings => inside.
807 //
808 unsigned num_crossings = 0;
809 for ( unsigned i = 0; i < a->size(); ++i ) {
810 num_crossings += num_crosses_x_pos_ray( b->i, b->j, *(*a)[i] );
811 }
812 return ( num_crossings % 2 ) == 1;
813 }
814 }
815
816
817 // ---------------------------------------------------------------------------
818 // num crosses x pos ray
819
820 template <typename T>
821 unsigned
822 vtol_extract_topology<T>::
num_crosses_x_pos_ray(double x,double y,vdgl_edgel_chain const & chain)823 num_crosses_x_pos_ray( double x, double y, vdgl_edgel_chain const& chain )
824 {
825 if ( chain.size() < 2 )
826 return 0;
827
828 // The edges are vertical or horizontal line segments. Leverage that
829 // in doing the intersection check. Assume that the ray will *not*
830 // cross on a vertex.
831 //
832 unsigned count = 0;
833 for ( unsigned int i = 0; i+1 < chain.size(); ++i ) {
834 vdgl_edgel const* e0 = &chain[i];
835 vdgl_edgel const* e1 = &chain[i+1];
836 assert( e0->y() != y && e1->y() != y );
837 if ( ( e0->y() < y && y < e1->y() ) ||
838 ( e1->y() < y && y < e0->y() ) ) {
839 assert( e0->x() == e1->x() );
840 assert( e0->x() != x );
841 if ( e0->x() > x ) {
842 ++count;
843 }
844 }
845 }
846
847 return count;
848 }
849
850
851 // ---------------------------------------------------------------------------
852 // smooth chain
853
854 template <typename T>
855 vdgl_edgel_chain_sptr
856 vtol_extract_topology<T>::
smooth_chain(vdgl_edgel_chain_sptr chain,unsigned int num_pts) const857 smooth_chain( vdgl_edgel_chain_sptr chain,
858 unsigned int num_pts ) const
859 {
860 // Can't smooth over more points than we have
861 if ( num_pts > chain->size() ) {
862 num_pts = chain->size();
863 }
864
865 // Need at least two points to fit a line
866 if ( num_pts < 2)
867 return chain;
868
869 vdgl_edgel_chain_sptr new_chain = new vdgl_edgel_chain;
870
871 vgl_line_2d_regression<double> reg;
872
873 // These store the indices of the edgel points used to estimate the
874 // line. The points in [fit_start,fit_end) are used.
875 //
876 unsigned fit_start;
877 unsigned fit_end;
878
879 // This is the index of the edgel point we are smoothing.
880 //
881 unsigned curr_ind = 0;
882
883 vgl_point_2d<double> pt1, pt2;
884 vgl_vector_2d<double> dir;
885 double slope;
886
887 // Add the first few points to get the first line. We'll use this
888 // line as the smoothing estimate for the first few edgel pixels. We
889 // don't add the first point, because we will constrain the line to
890 // pass through it. This will make sure that the vertices don't move
891 // because of the smoothing.
892 //
893 for ( fit_end = 1; fit_end < num_pts; ++fit_end ) {
894 reg.increment_partial_sums( chain->edgel(fit_end).x(),
895 chain->edgel(fit_end).y() );
896 }
897 assert( reg.get_n_pts() + 1 == num_pts );
898 if ( !reg.fit_constrained( chain->edgel(0).x(), chain->edgel(0).y() ) ) {
899 std::cerr << "line fit failed at start\n";
900 }
901
902 // Project the first half of the points used in estimating the line
903 // onto the line to get the smoothed positions.
904 //
905 reg.get_line().get_two_points( pt1, pt2 );
906 dir = reg.get_line().direction();
907 slope = reg.get_line().slope_degrees();
908 for ( ; curr_ind < (fit_end+1)/2; ++curr_ind ) {
909 pt2.set( chain->edgel(curr_ind).x(), chain->edgel(curr_ind).y() );
910 pt2 = pt1 + dot_product( pt2-pt1, dir ) * dir;
911 new_chain->add_edgel( vdgl_edgel( pt2.x(), pt2.y(), -1, slope ) );
912 }
913
914 // We have all the points from [1,fit_end_] in the regression object.
915 //
916 fit_start = 1;
917
918 while ( fit_end + 1 < chain->size() ) {
919 // Add one more point to get num_pts points
920 //
921 reg.increment_partial_sums( chain->edgel(fit_end).x(),
922 chain->edgel(fit_end).y() );
923 ++fit_end;
924
925 assert( reg.get_n_pts() == num_pts );
926
927 // Estimate a new line
928 //
929 if ( !reg.fit() ) {
930 std::cerr << "line fit failed at " << fit_start << '-' << fit_end << '\n';
931 }
932
933 // Project the current point onto the line to get the smoothed position
934 //
935 reg.get_line().get_two_points( pt1, pt2 );
936 dir = reg.get_line().direction();
937 slope = reg.get_line().slope_degrees();
938 pt2.set( chain->edgel(curr_ind).x(), chain->edgel(curr_ind).y() );
939 pt2 = pt1 + dot_product( pt2-pt1, dir ) * dir;
940 new_chain->add_edgel( vdgl_edgel( pt2.x(), pt2.y(), -1, slope ) );
941 ++curr_ind;
942
943 // Remove the start point in preparation for the next line segment
944 //
945 reg.decrement_partial_sums( chain->edgel(fit_start).x(),
946 chain->edgel(fit_start).y() );
947 ++fit_start;
948 }
949
950 assert( reg.get_n_pts() + 1 == num_pts );
951
952 // The special case when we are using all the points to fit a line,
953 // the end point is already in the regression object when it
954 // normally wouldn't be. We have to replace it with the starting
955 // point (which is not in the regression object) so we can do a
956 // constrained fit.
957 //
958 if ( num_pts == chain->size() ) {
959 assert( fit_end == chain->size() );
960 assert( fit_start == 1 );
961 --fit_end;
962 reg.decrement_partial_sums( chain->edgel(fit_end).x(),
963 chain->edgel(fit_end).y() );
964 reg.increment_partial_sums( chain->edgel(0).x(),
965 chain->edgel(0).y() );
966 }
967
968 // We have num_pts-1 points in the regression object. We do a
969 // constrained fit to make sure we interpolate the end vertex, and
970 // use this line for the final few edgel points.
971 //
972 if ( !reg.fit_constrained( chain->edgel(fit_end).x(),
973 chain->edgel(fit_end).y() ) ) {
974 std::cerr << "line fit failed at end\n";
975 }
976
977 dir = reg.get_line().direction();
978 reg.get_line().get_two_points( pt1, pt2 );
979 slope = reg.get_line().slope_degrees();
980 for ( ; curr_ind <= fit_end; ++curr_ind ) {
981 pt2.set( chain->edgel(curr_ind).x(), chain->edgel(curr_ind).y() );
982 pt2 = pt1 + dot_product( pt2-pt1, dir )*dir;
983 new_chain->add_edgel( vdgl_edgel( pt2.x(), pt2.y(), -1, slope ) );
984 }
985
986 return new_chain;
987 }
988
989 #endif // vtol_extract_topology_hxx_
990