in code/cpp/tools/generate_oriented_bounding_box/main.cpp [18:225]
int main (int argc, const char** argv) {
//
// parse input arguments
//
args::ArgumentParser parser("generate_oriented_bounding_box", "");
args::HelpFlag help (parser, "__DUMMY__", "Display this help menu", {'h', "help"});
args::ValueFlag<std::string> points_file_arg (parser, "POINTS_FILE", "points_file", {"points_file"}, args::Options::Required);
args::ValueFlag<double> n_epsilon_arg (parser, "N_EPSILON", "n_epsilon", {"n_epsilon"}, args::Options::Required);
args::ValueFlag<int> n_point_samples_arg (parser, "N_POINT_SAMPLES", "n_point_samples", {"n_point_samples"}, args::Options::Required);
args::ValueFlag<int> n_grid_size_arg (parser, "N_GRID_SIZE", "n_grid_size", {"n_grid_size"}, args::Options::Required);
args::ValueFlag<int> n_diam_opt_loops_arg (parser, "N_DIAM_OPT_LOOPS", "n_diam_opt_loops", {"n_diam_opt_loops"}, args::Options::Required);
args::ValueFlag<int> n_grid_search_opt_loops_arg (parser, "N_GRID_SEARCH_OPT_LOOPS", "n_grid_search_opt_loops", {"n_grid_search_opt_loops"}, args::Options::Required);
args::ValueFlag<std::string> output_bounding_box_center_file_arg (parser, "OUTPUT_BOUNDING_BOX_CENTER_FILE", "output_bounding_box_center_file", {"output_bounding_box_center_file"}, args::Options::Required);
args::ValueFlag<std::string> output_bounding_box_extent_file_arg (parser, "OUTPUT_BOUNDING_BOX_EXTENT_FILE", "output_bounding_box_extent_file", {"output_bounding_box_extent_file"}, args::Options::Required);
args::ValueFlag<std::string> output_bounding_box_orientation_file_arg (parser, "OUTPUT_BOUNDING_BOX_ORIENTATION_FILE", "output_bounding_box_orientation_file", {"output_bounding_box_orientation_file"}, args::Options::Required);
args::Flag silent_arg (parser, "__DUMMY__", "silent", {"silent"});
try {
parser.ParseCLI(argc, argv);
} catch (args::Completion e) {
std::cout << parser;
return 1;
} catch (args::Help e) {
std::cout << parser;
return 1;
} catch (args::ParseError e) {
std::cout << parser;
std::cout << std::endl << std::endl << std::endl << e.what() << std::endl << std::endl << std::endl;
return 1;
} catch (args::RequiredError e) {
std::cout << parser;
std::cout << std::endl << std::endl << std::endl << e.what() << std::endl << std::endl << std::endl;
return 1;
}
auto points_file = args::get(points_file_arg);
auto n_epsilon = args::get(n_epsilon_arg);
auto n_point_samples = args::get(n_point_samples_arg);
auto n_grid_size = args::get(n_grid_size_arg);
auto n_diam_opt_loops = args::get(n_diam_opt_loops_arg);
auto n_grid_search_opt_loops = args::get(n_grid_search_opt_loops_arg);
auto output_bounding_box_center_file = args::get(output_bounding_box_center_file_arg);
auto output_bounding_box_extent_file = args::get(output_bounding_box_extent_file_arg);
auto output_bounding_box_orientation_file = args::get(output_bounding_box_orientation_file_arg);
auto silent = args::get(silent_arg);
arma::mat points;
if (!silent) {
std::cout << "[HYPERSIM: GENERATE_ORIENTED_BOUNDING_BOXES] Begin..." << std::endl;
}
//
// load input data
//
points.load(points_file, arma::hdf5_binary_trans);
assert(points.n_cols == 2 || points.n_cols == 3);
//
// compute bounding box
//
arma::vec bounding_box_center_world, bounding_box_extent_world;
arma::mat bounding_box_R_world_from_obj;
if (points.n_cols == 2) {
// Note that ApproxMVBB assumes that each point is a column, which is different to our convention in Hypersim, so we transpose here.
ApproxMVBB::Matrix2Dyn points_amvbb(2, points.n_rows);
for (int i = 0; i < points.n_rows; i++) {
points_amvbb.col(i) << points(i,0), points(i,1);
}
// see https://github.com/gabyx/ApproxMVBB for details
ApproxMVBB::MinAreaRectangle amvbb_min_area_rectangle(points_amvbb);
amvbb_min_area_rectangle.compute();
auto rect = amvbb_min_area_rectangle.getMinRectangle();
auto rect_center = rect.m_p + 0.5*rect.m_u*rect.m_uL + 0.5*rect.m_v*rect.m_vL;
arma::vec bounding_box_extent_obj_({rect.m_uL, rect.m_vL});
arma::mat bounding_box_R_world_from_obj_({
{rect.m_u(0), rect.m_v(0)},
{rect.m_u(1), rect.m_v(1)}});
bounding_box_center_world = {rect_center(0), rect_center(1)};
// Our convention is as follows: We set the x-axis to be the longest axis. We set the positive direction
// of the x-axis to be the direction that points roughly towards the center of mass of the points. We set
// the y-axis to be the positive x-axis rotated by positive 90 degrees.
arma::vec points_mean_world = arma::mean(points, 0);
arma::vec points_bbcenter_to_mean = points_mean_world - bounding_box_center_world;
arma::uvec sorted_axes = sort_index(bounding_box_extent_obj_, "descend");
arma::vec axis_0 = bounding_box_R_world_from_obj_.col(sorted_axes(0));
auto axis_0_length = bounding_box_extent_obj_(sorted_axes(0));
if (arma::dot(axis_0, points_bbcenter_to_mean) < 0) {
axis_0 = -axis_0;
}
arma::mat R({
{0, -1},
{1, 0}});
arma::vec axis_1 = R*axis_0; // positive rotation by 90 degrees
auto axis_1_length = bounding_box_extent_obj_(sorted_axes(1));
bounding_box_extent_world = {axis_0_length, axis_1_length};
bounding_box_R_world_from_obj = {
{axis_0(0), axis_1(0)},
{axis_0(1), axis_1(1)}};
}
if (points.n_cols == 3) {
// Note that ApproxMVBB assumes that each point is a column, which is different to our convention in Hypersim, so we transpose here.
ApproxMVBB::Matrix3Dyn points_amvbb(3, points.n_rows);
for (int i = 0; i < points.n_rows; i++) {
points_amvbb.col(i) << points(i,0), points(i,1), points(i,2);
}
// suppress overly verbose output
auto std_cout_rdbuf = std::cout.rdbuf();
std::ostringstream std_ostringstream;
if (silent) {
std::cout.rdbuf(std_ostringstream.rdbuf());
}
// see https://github.com/gabyx/ApproxMVBB for details
ApproxMVBB::OOBB oobb = ApproxMVBB::approximateMVBB(points_amvbb, n_epsilon, n_point_samples, n_grid_size, n_diam_opt_loops, n_grid_search_opt_loops);
if (silent) {
std::cout.rdbuf(std_cout_rdbuf);
}
// make all points inside the OOBB, see https://github.com/gabyx/ApproxMVBB for details
ApproxMVBB::Matrix33 A_KI = oobb.m_q_KI.matrix().transpose();
for(int i = 0; i < points_amvbb.cols(); i++) {
oobb.unite(A_KI*points_amvbb.col(i));
}
arma::vec bounding_box_center_obj_({oobb.center()(0), oobb.center()(1), oobb.center()(2)});
arma::vec bounding_box_extent_obj_({oobb.extent()(0), oobb.extent()(1), oobb.extent()(2)});
arma::mat bounding_box_R_world_from_obj_({
{oobb.m_q_KI.matrix()(0,0), oobb.m_q_KI.matrix()(0,1), oobb.m_q_KI.matrix()(0,2)},
{oobb.m_q_KI.matrix()(1,0), oobb.m_q_KI.matrix()(1,1), oobb.m_q_KI.matrix()(1,2)},
{oobb.m_q_KI.matrix()(2,0), oobb.m_q_KI.matrix()(2,1), oobb.m_q_KI.matrix()(2,2)}});
bounding_box_center_world = bounding_box_R_world_from_obj_*bounding_box_center_obj_;
// Our convention is as follows: We set the x-axis to be the longest axis. We set the positive direction
// of the x-axis to be the direction that points roughly towards the center of mass of the points. We set
// the y-axis to be the 2nd longest axis, and we set the positive direction of the y-axis to be the
// direction that points roughly towards the center of mass of the points. We set the positive z-axis to
// be x cross y.
arma::vec points_mean_world = arma::mean(points, 0);
arma::vec points_bbcenter_to_mean = points_mean_world - bounding_box_center_world;
arma::uvec sorted_axes = sort_index(bounding_box_extent_obj_, "descend");
arma::vec axis_0 = bounding_box_R_world_from_obj_.col(sorted_axes(0));
auto axis_0_length = bounding_box_extent_obj_(sorted_axes(0));
if (arma::dot(axis_0, points_bbcenter_to_mean) < 0) {
axis_0 = -axis_0;
}
arma::vec axis_1 = bounding_box_R_world_from_obj_.col(sorted_axes(1));
auto axis_1_length = bounding_box_extent_obj_(sorted_axes(1));
if (arma::dot(axis_1, points_bbcenter_to_mean) < 0) {
axis_1 = -axis_1;
}
arma::vec axis_2 = arma::cross(axis_0, axis_1);
auto axis_2_length = bounding_box_extent_obj_(sorted_axes(2));
bounding_box_extent_world = {axis_0_length, axis_1_length, axis_2_length};
bounding_box_R_world_from_obj = {
{axis_0(0), axis_1(0), axis_2(0)},
{axis_0(1), axis_1(1), axis_2(1)},
{axis_0(2), axis_1(2), axis_2(2)}};
}
//
// save bounding box
//
if (!silent) {
std::cout << "[HYPERSIM: GENERATE_ORIENTED_BOUNDING_BOXES] Saving bounding box results..." << std::endl;
}
bounding_box_center_world.save(output_bounding_box_center_file, arma::hdf5_binary_trans);
bounding_box_extent_world.save(output_bounding_box_extent_file, arma::hdf5_binary_trans);
bounding_box_R_world_from_obj.save(output_bounding_box_orientation_file, arma::hdf5_binary_trans);
if (!silent) {
std::cout << "[HYPERSIM: GENERATE_ORIENTED_BOUNDING_BOXES] Finished." << std::endl;
}
return 0;
}