Skip to content

Commit 52c54ed

Browse files
committed
op_dimensionality_estimate
1 parent 97b9f95 commit 52c54ed

File tree

4 files changed

+191
-3
lines changed

4 files changed

+191
-3
lines changed

3rdparty/eigen

Submodule eigen updated from 0fd6b4f to 3147391

voxec.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ voxel_operation_map::map_t& voxel_operation_map::map() {
6565
m.insert(std::make_pair(std::string("repeat_slice"), &instantiate<op_repeat_slice>));
6666
m.insert(std::make_pair(std::string("component_foreach"), &instantiate<op_component_foreach>));
6767
m.insert(std::make_pair(std::string("normal_estimate"), &instantiate<op_normal_estimate>));
68+
m.insert(std::make_pair(std::string("dimensionality_estimate"), &instantiate<op_dimensionality_estimate>));
6869
m.insert(std::make_pair(std::string("segment"), &instantiate<op_segment>));
6970
m.insert(std::make_pair(std::string("keep_neighbours"), &instantiate<op_keep_neighbours>));
7071
m.insert(std::make_pair(std::string("free"), &instantiate<op_free>));

voxec.h

+183
Original file line numberDiff line numberDiff line change
@@ -2496,6 +2496,189 @@ class op_normal_estimate : public voxel_operation {
24962496
}
24972497
};
24982498

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+
24992682
namespace {
25002683
class check_curvature_and_normal_deviation {
25012684
private:

wrap/wrapper.i

+6-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
PyObject* get(long i, long j, long k) const {
4848
auto acvs_voxels = dynamic_cast<abstract_chunked_voxel_storage const*>($self);
4949
if (!acvs_voxels) {
50-
throw std::runtime_error("Unsupported");
50+
throw std::runtime_error("Unsupported storage");
5151
}
5252
auto ijk_long = (make_vec<long>(i, j, k) - (acvs_voxels->grid_offset() * acvs_voxels->chunk_size()).as<long>());
5353
auto ijk = ijk_long.as<size_t>();
@@ -60,6 +60,10 @@
6060
}
6161
if ($self->value_bits() == 1) {
6262
return PyBool_FromLong(self->Get(ijk));
63+
} else if ($self->value_bits() == 8) {
64+
uint8_t v;
65+
$self->Get(ijk, &v);
66+
return PyLong_FromLong(v);
6367
} else if ($self->value_bits() == 32) {
6468
uint32_t v;
6569
$self->Get(ijk, &v);
@@ -78,7 +82,7 @@
7882
return tup;
7983
}
8084
} else {
81-
throw std::runtime_error("Unsupported");
85+
throw std::runtime_error("Unsupported data type, size: " + std::to_string($self->value_bits()));
8286
}
8387
}
8488
PyObject* get(double x, double y, double z) const {

0 commit comments

Comments
 (0)