in code/cpp/tools/scene_annotation_tool/main.cpp [878:1288]
bool remove_vertices_and_faces(bool prefer_remove_small_vertices, bool prefer_remove_distant_vertices, bool remove_orphaned_vertices) {
assert(g_vertices_orig.n_cols == 3);
assert(g_faces_vi_orig.n_cols == 3);
assert(g_faces_vi_orig.n_rows == g_faces_oi_orig.n_elem);
assert(arma::all(arma::vectorise(g_faces_vi_orig) >= 0));
assert(arma::all(arma::vectorise(g_faces_vi_orig) < g_vertices_orig.n_rows));
assert(arma::all(g_faces_oi_orig >= 0));
assert(arma::all(g_faces_oi_orig < g_mesh_objects.size()));
int num_vertices, num_faces;
arma::mat vertices = g_vertices_orig;
arma::umat faces_vi = arma::conv_to<arma::umat>::from(g_faces_vi_orig);
arma::ivec faces_oi = g_faces_oi_orig;
auto max_num_faces = g_max_num_faces;
auto max_num_vertices = g_max_num_vertices;
auto face_score_area_half_life = g_face_score_area_half_life;
auto face_score_distance_half_life = g_face_score_distance_half_life;
arma::vec camera_position = { g_viewer.core().camera_eye(0), g_viewer.core().camera_eye(1), g_viewer.core().camera_eye(2) };
num_vertices = vertices.n_rows;
num_faces = faces_vi.n_rows;
if (num_vertices <= max_num_vertices && num_faces <= max_num_faces) {
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Vertices and faces are within user-specified budgets, so there is no need to remove mesh data..." << std::endl;
return false;
}
// Compute face areas.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Computing face areas and distances..." << std::endl;
arma::mat faces_v0 = vertices.rows(faces_vi.col(0));
arma::mat faces_v1 = vertices.rows(faces_vi.col(1));
arma::mat faces_v2 = vertices.rows(faces_vi.col(2));
arma::mat face_edges_01 = faces_v1 - faces_v0;
arma::mat face_edges_02 = faces_v2 - faces_v0;
arma::vec face_areas = arma::ones<arma::vec>(num_faces) * std::numeric_limits<float>::infinity();
for (int fi = 0; fi < num_faces; fi++) {
face_areas(fi) = 0.5*arma::norm(arma::cross(face_edges_01.row(fi), face_edges_02.row(fi)));
}
arma::vec face_distances = arma::ones<arma::vec>(num_faces) * std::numeric_limits<float>::infinity();
arma::vec vertex_distances = arma::ones<arma::vec>(3) * std::numeric_limits<float>::infinity();
for (int fi = 0; fi < num_faces; fi++) {
vertex_distances(0) = arma::norm(camera_position - faces_v0.row(fi));
vertex_distances(1) = arma::norm(camera_position - faces_v1.row(fi));
vertex_distances(2) = arma::norm(camera_position - faces_v2.row(fi));
face_distances(fi) = arma::min(vertex_distances);
}
arma::vec face_scores_area = arma::ones<arma::vec>(num_faces);
arma::vec face_scores_distance = arma::ones<arma::vec>(num_faces);
// Compute area score as an exponentially decaying function of face area.
if (prefer_remove_small_vertices) {
face_scores_area = arma::exp2(-face_areas/face_score_area_half_life);
}
// Compute distance score as an exponentially growing function of distance.
if (prefer_remove_distant_vertices) {
face_scores_distance = arma::exp2(face_distances/face_score_distance_half_life);
}
// Compute final face score as the product of the area score and the distance score.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Computing face scores..." << std::endl;
arma::vec face_scores = face_scores_area % face_scores_distance; // % operator is element-wise multiplication
//
// Remove vertices (selected)
//
if (num_vertices > max_num_vertices) {
auto num_vertices_to_remove = num_vertices - max_num_vertices;
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Removing " << num_vertices_to_remove << " vertices..." << std::endl;
// Construct a list of faces that refer to each vertex. Iterate over the faces,
// and for each vertex of each face, add the face to a per-vertex list.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Constructing list of faces that refer to each vertex..." << std::endl;
std::vector<std::vector<int>> vertices_fi_;
for (int vi = 0; vi < num_vertices; vi++) {
vertices_fi_.push_back({});
}
for (int fi = 0; fi < num_faces; fi++) {
arma::uvec vi = faces_vi.row(fi).t();
arma::uvec vi_unique = arma::unique(vi);
for (int vii = 0; vii < vi_unique.n_elem; vii++) {
vertices_fi_.at(vi_unique(vii)).push_back(fi);
}
}
// Convert to list of arma::uvec for more convenient indexing.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Converting to list of arma::uvec for more convenient indexing..." << std::endl;
std::vector<arma::uvec> vertices_fi;
for (int vi = 0; vi < num_vertices; vi++) {
auto fi_ = vertices_fi_.at(vi);
arma::uvec fi = arma::ones<arma::uvec>(fi_.size());
for (int fii = 0; fii < fi_.size(); fii++) {
fi(fii) = fi_.at(fii);
}
vertices_fi.push_back(fi);
}
// Compute vertex scores. For each vertex, compute the vertex score as the minimum face
// score among faces that refer to the vertex. This approach favors removing vertices that
// only belong to faces with high scores, because a single face with a low score will give
// the vertex a low score.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Computing vertex scores..." << std::endl;
arma::vec vertex_scores = arma::zeros<arma::vec>(num_vertices);
for (int vi = 0; vi < num_vertices; vi++) {
arma::uvec fi = vertices_fi.at(vi);
assert(arma::all(fi == arma::sort(fi)));
assert(arma::all(fi == arma::unique(fi)));
auto num_faces_vi = fi.n_elem;
if (num_faces_vi != 0) {
vertex_scores(vi) = arma::min(face_scores.elem(fi));
}
}
// Select vertices to remove.
arma::vec selection_scores = vertex_scores;
auto num_elements_to_select = num_vertices_to_remove;
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Selecting elements to remove..." << std::endl;
arma::arma_rng::set_seed(0);
std::vector<int> selected_elements_;
for (int i = 0; i < g_max_num_iters; i++) {
auto num_elements_to_select_curr = num_elements_to_select - selected_elements_.size();
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Remaining elements to select: " << num_elements_to_select_curr << std::endl;
// Since we are choosing without replacement, we need to re-normalize during each iteration.
arma::vec selection_scores_normalized_cumsum = arma::cumsum(selection_scores) / arma::sum(selection_scores);
arma::vec sorted_uniform_vals = arma::sort(arma::randu<arma::vec>(num_elements_to_select_curr), "ascend");
arma::uvec selected_elements_curr = arma::zeros<arma::uvec>(num_elements_to_select_curr);
int ci = 0;
for (int ui = 0; ui < num_elements_to_select_curr; ui++) {
while (selection_scores_normalized_cumsum(ci) <= sorted_uniform_vals(ui)) {
ci++;
}
selected_elements_curr(ui) = ci;
}
arma::uvec selected_elements_curr_unique = arma::unique(selected_elements_curr);
for (int si = 0; si < selected_elements_curr_unique.n_elem; si++) {
selected_elements_.push_back(selected_elements_curr_unique(si));
}
// Since we are choosing without replacement, we need to modify the vertex scores to not repeatedly select the same vertex.
selection_scores.elem(selected_elements_curr_unique).zeros();
if (selected_elements_.size() == num_elements_to_select) {
break;
}
}
if (selected_elements_.size() < num_elements_to_select) {
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Scores are too sharply peaked. Couldn't select enough elements in the maximum number of iterations. Giving up......" << std::endl;
return false;
}
assert(selected_elements_.size() == num_elements_to_select);
// Convert to arma::uvec for more convenient indexing.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Converting selected elements to arma::uvec for more convenient indexing..." << std::endl;
arma::uvec selected_elements = arma::zeros<arma::uvec>(selected_elements_.size());
for (int si = 0; si < selected_elements_.size(); si++) {
selected_elements(si) = selected_elements_.at(si);
}
std::vector<int> vertices_to_remove_selected_ = selected_elements_;
arma::uvec vertices_to_remove_selected = selected_elements;
// Mark all orphaned and selected vertices for removal.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Marking all selected vertices for removal..." << std::endl;
arma::uvec vertices_to_keep_mask = arma::ones<arma::uvec>(num_vertices);
vertices_to_keep_mask.elem(vertices_to_remove_selected).zeros();
arma::uvec vertices_to_keep = arma::find(vertices_to_keep_mask == 1);
arma::uvec vertices_to_remove_mask = vertices_to_keep_mask == 0;
arma::uvec vertices_to_remove = arma::find(vertices_to_remove_mask == 1);
// Mark all faces for removal that refer to a vertex marked for removal.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Marking all faces for removal that refer to a vertex marked for removal..." << std::endl;
arma::uvec faces_to_keep_mask = arma::ones<arma::uvec>(num_faces);
for (auto vi : vertices_to_remove_selected_) {
faces_to_keep_mask.elem(vertices_fi.at(vi)).zeros();
}
arma::uvec faces_to_keep = arma::find(faces_to_keep_mask == 1);
arma::uvec faces_to_remove_mask = faces_to_keep_mask == 0;
arma::uvec faces_to_remove = arma::find(faces_to_remove_mask == 1);
// Remove vertices and compact the result into a new densely packed array.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Removing " << vertices_to_remove.n_elem << " vertices and compacting the result into a new densely packed array..." << std::endl;
arma::mat vertices_compact = vertices.rows(vertices_to_keep);
// Remove faces and compact the results into a new densely packed array.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Removing " << faces_to_remove.n_elem << " faces and compacting the results into new densely packed arrays..." << std::endl;
arma::umat faces_vi_compact = faces_vi.rows(faces_to_keep);
arma::ivec faces_oi_compact = faces_oi.elem(faces_to_keep);
arma::vec face_scores_compact = face_scores.elem(faces_to_keep);
// Adjust faces to account for the newly compacted vertex array.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Adjusting faces to account for the newly compacted vertex array..." << std::endl;
arma::uvec vertices_to_remove_mask_cumsum = arma::cumsum(vertices_to_remove_mask);
arma::umat faces_vi_compact_adjusted = arma::reshape(arma::vectorise(faces_vi_compact) - vertices_to_remove_mask_cumsum.elem(arma::vectorise(faces_vi_compact)), faces_vi_compact.n_rows, 3);
// Set output data
vertices = vertices_compact;
faces_vi = faces_vi_compact_adjusted;
faces_oi = faces_oi_compact;
face_scores = face_scores_compact;
num_vertices = vertices.n_rows;
num_faces = faces_vi.n_rows;
}
//
// Remove faces (selected)
//
if (num_faces > max_num_faces) {
auto num_faces_to_remove = num_faces - max_num_faces;
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Removing " << num_faces_to_remove << " faces..." << std::endl;
// Select faces to remove.
arma::vec selection_scores = face_scores;
auto num_elements_to_select = num_faces_to_remove;
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Selecting elements to remove..." << std::endl;
arma::arma_rng::set_seed(0);
std::vector<int> selected_elements_;
for (int i = 0; i < g_max_num_iters; i++) {
auto num_elements_to_select_curr = num_elements_to_select - selected_elements_.size();
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Remaining elements to select: " << num_elements_to_select_curr << std::endl;
// Since we are choosing without replacement, we need to re-normalize during each iteration.
arma::vec selection_scores_normalized_cumsum = arma::cumsum(selection_scores) / arma::sum(selection_scores);
arma::vec sorted_uniform_vals = arma::sort(arma::randu<arma::vec>(num_elements_to_select_curr), "ascend");
arma::uvec selected_elements_curr = arma::zeros<arma::uvec>(num_elements_to_select_curr);
int ci = 0;
for (int ui = 0; ui < num_elements_to_select_curr; ui++) {
while (selection_scores_normalized_cumsum(ci) <= sorted_uniform_vals(ui)) {
ci++;
}
selected_elements_curr(ui) = ci;
}
arma::uvec selected_elements_curr_unique = arma::unique(selected_elements_curr);
for (int si = 0; si < selected_elements_curr_unique.n_elem; si++) {
selected_elements_.push_back(selected_elements_curr_unique(si));
}
// Since we are choosing without replacement, we need to modify the vertex scores to not repeatedly select the same vertex.
selection_scores.elem(selected_elements_curr_unique).zeros();
if (selected_elements_.size() == num_elements_to_select) {
break;
}
}
if (selected_elements_.size() < num_elements_to_select) {
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Scores are too sharply peaked. Couldn't select enough elements in the maximum number of iterations. Giving up..." << std::endl;
return false;
}
assert(selected_elements_.size() == num_elements_to_select);
// Convert to arma::uvec for more convenient indexing.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Converting selected elements to arma::uvec for more convenient indexing..." << std::endl;
arma::uvec selected_elements = arma::zeros<arma::uvec>(selected_elements_.size());
for (int si = 0; si < selected_elements_.size(); si++) {
selected_elements(si) = selected_elements_.at(si);
}
std::vector<int> faces_to_remove_selected_ = selected_elements_;
arma::uvec faces_to_remove_selected = selected_elements;
// Mark all selected faces for removal.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Marking selected faces for removal..." << std::endl;
arma::uvec faces_to_keep_mask = arma::ones<arma::uvec>(num_faces);
faces_to_keep_mask.elem(faces_to_remove_selected).zeros();
arma::uvec faces_to_keep = arma::find(faces_to_keep_mask == 1);
arma::uvec faces_to_remove_mask = faces_to_keep_mask == 0;
arma::uvec faces_to_remove = arma::find(faces_to_remove_mask == 1);
// Remove faces and compact the results into a new densely packed array.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Removing " << faces_to_remove.n_elem << " faces and compacting the results into new densely packed arrays..." << std::endl;
arma::umat faces_vi_compact = faces_vi.rows(faces_to_keep);
arma::ivec faces_oi_compact = faces_oi.elem(faces_to_keep);
arma::vec face_scores_compact = face_scores.elem(faces_to_keep);
// Set output data
faces_vi = faces_vi_compact;
faces_oi = faces_oi_compact;
face_scores = face_scores_compact;
num_faces = faces_vi.n_rows;
}
//
// Remove vertices (orphaned)
//
if (remove_orphaned_vertices) {
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Removing orphaned vertices..." << std::endl;
// Construct a list of faces that refer to each vertex. Iterate over the faces,
// and for each vertex of each face, add the face to a per-vertex list.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Constructing list of faces that refer to each vertex..." << std::endl;
std::vector<std::vector<int>> vertices_fi_;
for (int vi = 0; vi < num_vertices; vi++) {
vertices_fi_.push_back({});
}
for (int fi = 0; fi < num_faces; fi++) {
arma::uvec vi = faces_vi.row(fi).t();
arma::uvec vi_unique = arma::unique(vi);
for (int vii = 0; vii < vi_unique.n_elem; vii++) {
vertices_fi_.at(vi_unique(vii)).push_back(fi);
}
}
// Convert to list of arma::uvec for more convenient indexing.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Converting to list of arma::uvec for more convenient indexing..." << std::endl;
std::vector<arma::uvec> vertices_fi;
for (int vi = 0; vi < num_vertices; vi++) {
auto fi_ = vertices_fi_.at(vi);
arma::uvec fi = arma::ones<arma::uvec>(fi_.size());
for (int fii = 0; fii < fi_.size(); fii++) {
fi(fii) = fi_.at(fii);
}
vertices_fi.push_back(fi);
}
// Construct a list of orphaned vertices. An "orphaned" vertex, i.e., a vertex that
// is not belong to any face, can be removed without further consideration.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Constructing list of orphaned vertices..." << std::endl;
std::vector<int> vertices_to_remove_orphaned_;
for (int vi = 0; vi < num_vertices; vi++) {
arma::uvec fi = vertices_fi.at(vi);
assert(arma::all(fi == arma::sort(fi)));
assert(arma::all(fi == arma::unique(fi)));
auto num_faces_vi = fi.n_elem;
if (num_faces_vi == 0) {
vertices_to_remove_orphaned_.push_back(vi);
}
}
// Convert to arma::uvec for more convenient indexing.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Converting orphaned vertices to arma::uvec for more convenient indexing..." << std::endl;
arma::uvec vertices_to_remove_orphaned = arma::zeros<arma::uvec>(vertices_to_remove_orphaned_.size());
for (int i = 0; i < vertices_to_remove_orphaned_.size(); i++) {
vertices_to_remove_orphaned(i) = vertices_to_remove_orphaned_.at(i);
}
// Mark all orphaned vertices for removal.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Marking all orphaned vertices for removal..." << std::endl;
arma::uvec vertices_to_keep_mask = arma::ones<arma::uvec>(num_vertices);
vertices_to_keep_mask.elem(vertices_to_remove_orphaned).zeros();
arma::uvec vertices_to_keep = arma::find(vertices_to_keep_mask == 1);
arma::uvec vertices_to_remove_mask = vertices_to_keep_mask == 0;
arma::uvec vertices_to_remove = arma::find(vertices_to_remove_mask == 1);
// Remove vertices and compact the result into a new densely packed array.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Removing " << vertices_to_remove.n_elem << " vertices and compacting the result into a new densely packed array..." << std::endl;
arma::mat vertices_compact = vertices.rows(vertices_to_keep);
// Adjust faces to account for the newly compacted vertex array.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Adjusting faces to account for the newly compacted vertex array..." << std::endl;
arma::uvec vertices_to_remove_mask_cumsum = arma::cumsum(vertices_to_remove_mask);
arma::umat faces_vi_adjusted = arma::reshape(arma::vectorise(faces_vi) - vertices_to_remove_mask_cumsum.elem(arma::vectorise(faces_vi)), faces_vi.n_rows, 3);
// Set output data
vertices = vertices_compact;
faces_vi = faces_vi_adjusted;
num_vertices = vertices.n_rows;
}
// Convert to final output format.
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Converting to final output format..." << std::endl;
g_vertices_curr = vertices;
g_faces_vi_curr = arma::conv_to<arma::imat>::from(faces_vi);
g_faces_oi_curr = arma::conv_to<arma::ivec>::from(faces_oi);
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Updating viewer state..." << std::endl;
initialize_embree_state();
initialize_derived_segmentation_state(g_segmentation_state_prev);
initialize_derived_segmentation_state(g_segmentation_state_curr);
update_derived_segmentation_state();
clear_viewer_mesh();
set_viewer_mesh();
set_viewer_mesh_colors();
std::cout << "[HYPERSIM: SCENE_ANNOTATION_TOOL] Finished." << std::endl;
return true;
}