bool remove_vertices_and_faces()

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;
}