@@ -2496,6 +2496,189 @@ class op_normal_estimate : public voxel_operation {
2496
2496
}
2497
2497
};
2498
2498
2499
+
2500
+ class op_dimensionality_estimate : public voxel_operation {
2501
+ public:
2502
+ const std::vector<argument_spec>& arg_names () const {
2503
+ static std::vector<argument_spec> nm_ = { { true , " input" , " voxels" }, { false , " max_depth" , " integer" }, { false , " max_depth_2" , " integer" }, {false , " distance_2" , " real" }, {false , " neighbourhood_size" , " integer" }, {false , " reposition" , " integer" }};
2504
+ return nm_;
2505
+ }
2506
+ symbol_value invoke (const scope_map& scope) const {
2507
+ static normal_and_curvature_t fmt;
2508
+ normal_and_curvature<int16_t > v;
2509
+
2510
+ auto voxels = (regular_voxel_storage*)scope.get_value <abstract_voxel_storage*>(" input" );
2511
+ auto result = voxels->empty_copy_as (&fmt);
2512
+
2513
+ boost::optional<int > max_depth, max_depth_2, neighbourhood_size;
2514
+ boost::optional<double > distance_2;
2515
+
2516
+ try {
2517
+ max_depth = scope.get_value <int >(" max_depth" );
2518
+ } catch (scope_map::not_in_scope&) {}
2519
+ try {
2520
+ max_depth_2 = scope.get_value <int >(" max_depth_2" );
2521
+ } catch (scope_map::not_in_scope&) {}
2522
+ try {
2523
+ neighbourhood_size = scope.get_value <int >(" neighbourhood_size" );
2524
+ } catch (scope_map::not_in_scope&) {}
2525
+ try {
2526
+ distance_2 = scope.get_value <double >(" distance_2" );
2527
+ } catch (scope_map::not_in_scope&) {}
2528
+
2529
+ if (!max_depth && !neighbourhood_size) {
2530
+ throw std::runtime_error (" Either max_depth or neighbourhood_size needs to be provided" );
2531
+ }
2532
+
2533
+ int reposition = scope.get_value_or <int >(" reposition" , 0 ) ? 2 : 1 ;
2534
+
2535
+ std::vector<float > coords;
2536
+
2537
+ if (neighbourhood_size) {
2538
+ coords.reserve (*neighbourhood_size);
2539
+ } else if (max_depth) {
2540
+ auto box_dim = 1 + (*max_depth) * 2 ;
2541
+ coords.reserve (box_dim * box_dim * box_dim);
2542
+ }
2543
+
2544
+ for (auto it = voxels->begin (); it != voxels->end (); ++it) {
2545
+ visitor<26 > vis;
2546
+
2547
+ auto seed = *it;
2548
+
2549
+ for (int iter = 0 ; iter < reposition; ++iter) {
2550
+
2551
+ int previous_coord_count = -1 ;
2552
+ int max_depth_guess = ((int )std::ceil (std::pow (26 , 1 . / 3 ) - 1 )) / 2 ;
2553
+
2554
+ while (true ) {
2555
+ if (neighbourhood_size) {
2556
+ vis.max_depth = max_depth_guess;
2557
+ } else {
2558
+ vis.max_depth = iter == 1 && max_depth_2 ? max_depth_2 : max_depth;
2559
+ }
2560
+
2561
+ coords.clear ();
2562
+
2563
+ vis ([&coords, neighbourhood_size](const tagged_index& pos) {
2564
+ if (pos.which == tagged_index::VOXEL) {
2565
+ if (!neighbourhood_size || (coords.size () / 3 < *neighbourhood_size)) {
2566
+ coords.push_back (pos.pos .get (0 ));
2567
+ coords.push_back (pos.pos .get (1 ));
2568
+ coords.push_back (pos.pos .get (2 ));
2569
+ }
2570
+ } else {
2571
+ throw std::runtime_error (" Unexpected" );
2572
+ }
2573
+ }, voxels, seed);
2574
+
2575
+ if (neighbourhood_size) {
2576
+ if (coords.size () / 3 == *neighbourhood_size) {
2577
+ break ;
2578
+ }
2579
+ if (previous_coord_count == (int )coords.size ()) {
2580
+ break ;
2581
+ }
2582
+ if (max_depth) {
2583
+ if (*max_depth == max_depth_guess) {
2584
+ break ;
2585
+ }
2586
+ }
2587
+ previous_coord_count = (int )coords.size ();
2588
+ } else if (max_depth) {
2589
+ break ;
2590
+ }
2591
+
2592
+ ++max_depth_guess;
2593
+ }
2594
+
2595
+ auto points = Eigen::Map<Eigen::Matrix<float , 3 , Eigen::Dynamic>>(coords.data (), 3 , coords.size () / 3 ).transpose ();
2596
+
2597
+ if (reposition == 2 && iter == 0 ) {
2598
+ #if 1
2599
+ auto mean = points.colwise ().mean ();
2600
+ #elif 0
2601
+ // median
2602
+ Eigen::RowVector3f mean;
2603
+ size_t i = 0 ;
2604
+ for (const auto & c : points.colwise ()) {
2605
+ std::vector<double > r;
2606
+ r.reserve (c.size ());
2607
+ for (auto & v : c) {
2608
+ r.push_back (v);
2609
+ }
2610
+ std::sort (r.begin (), r.end ());
2611
+ auto v = r.size () % 2 == 0
2612
+ ? (r[(r.size () - 2 ) / 2 ] + r[(r.size () - 2 ) / 2 + 1 ]) / 2 .
2613
+ : r[r.size () / 2 ];
2614
+ mean (i++) = v;
2615
+ }
2616
+ #else
2617
+ // bounding box center
2618
+ auto mi = points.colwise().minCoeff();
2619
+ auto ma = points.colwise().maxCoeff();
2620
+ auto mean = (mi + ma) / 2.;
2621
+ #endif
2622
+
2623
+ // Find point in neighborhood closest to median
2624
+ auto minl = std::numeric_limits<double >::infinity ();
2625
+ Eigen::RowVectorXf p;
2626
+ for (const auto & r : points.rowwise ()) {
2627
+ auto l = (r - mean).norm ();
2628
+ if (l < minl) {
2629
+ minl = l;
2630
+ p = r;
2631
+ }
2632
+ }
2633
+
2634
+ // Assign to seed to reposition neighborhood around found median
2635
+ seed.get <0 >() = (size_t )p.data ()[0 ];
2636
+ seed.get <1 >() = (size_t )p.data ()[1 ];
2637
+ seed.get <2 >() = (size_t )p.data ()[2 ];
2638
+
2639
+ // Continue within outer loop to restart traversal from new seed
2640
+ continue ;
2641
+ }
2642
+
2643
+ Eigen::MatrixXf centered = points.rowwise () - points.colwise ().mean ();
2644
+
2645
+ if (distance_2) {
2646
+ auto norms = centered.rowwise ().norm ().array ();
2647
+ auto mask = norms < (*distance_2);
2648
+ Eigen::MatrixXf filtered (mask.count (), centered.cols ());
2649
+ size_t idx = 0 ;
2650
+ for (size_t i = 0 ; i < mask.size (); ++i) {
2651
+ if (mask (i)) {
2652
+ filtered.row (idx++) = centered.row (i);
2653
+ }
2654
+ }
2655
+ centered = filtered;
2656
+ }
2657
+
2658
+ Eigen::MatrixXf cov = centered.adjoint () * centered;
2659
+ Eigen::SelfAdjointEigenSolver<Eigen::MatrixXf> eig (cov);
2660
+
2661
+ std::array<double , 3 > norm_eigv = {
2662
+ eig.eigenvalues ().data ()[0 ] / eig.eigenvalues ().norm (),
2663
+ eig.eigenvalues ().data ()[1 ] / eig.eigenvalues ().norm (),
2664
+ eig.eigenvalues ().data ()[2 ] / eig.eigenvalues ().norm ()
2665
+ };
2666
+
2667
+ v.nxyz_curv = {
2668
+ (int16_t )(norm_eigv[0 ] * (float )(std::numeric_limits<int16_t >::max () - 1 )),
2669
+ (int16_t )(norm_eigv[1 ] * (float )(std::numeric_limits<int16_t >::max () - 1 )),
2670
+ (int16_t )(norm_eigv[2 ] * (float )(std::numeric_limits<int16_t >::max () - 1 )),
2671
+ 0
2672
+ };
2673
+
2674
+ result->Set (*it, &v);
2675
+ }
2676
+ }
2677
+
2678
+ return result;
2679
+ }
2680
+ };
2681
+
2499
2682
namespace {
2500
2683
class check_curvature_and_normal_deviation {
2501
2684
private:
0 commit comments