Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/esp/bindings/SceneBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,14 @@ void initSceneBindings(py::module& m) {
.def_property_readonly("semantic_index_map",
&SemanticScene::getSemanticIndexMap)
.def("semantic_index_to_object_index",
&SemanticScene::semanticIndexToObjectIndex);
&SemanticScene::semanticIndexToObjectIndex)
.def("get_regions_for_point", &SemanticScene::getRegionsForPoint,
"Compute all SemanticRegions which contain the point and return a "
"list of indices for the regions in this SemanticScene.")
.def("get_regions_for_points", &SemanticScene::getRegionsForPoints,
"Compute SemanticRegion containment for a set of points. Return a "
"sorted list of tuple pairs with each containing region index and "
"the percentage of points contained by that region.");

// ==== ObjectControls ====
py::class_<ObjectControls, ObjectControls::ptr>(m, "ObjectControls")
Expand Down
33 changes: 33 additions & 0 deletions src/esp/scene/SemanticScene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -591,5 +591,38 @@ std::vector<uint32_t> SemanticScene::buildSemanticOBBs(
return unMappedObjectIDXs;
} // SemanticScene::buildSemanticOBBs

std::vector<int> SemanticScene::getRegionsForPoint(
const Mn::Vector3& point) const {
std::vector<int> containingRegions;
for (int rix = 0; rix < regions_.size(); ++rix) {
if (regions_[rix]->contains(point)) {
containingRegions.push_back(rix);
}
}
return containingRegions;
}

std::vector<std::pair<int, float>> SemanticScene::getRegionsForPoints(
const std::vector<Mn::Vector3>& points) const {
std::vector<std::pair<int, float>> containingRegionWeights;
for (int rix = 0; rix < regions_.size(); ++rix) {
float containmentCount = 0;
for (const auto& point : points) {
if (regions_[rix]->contains(point)) {
containmentCount += 1;
}
}
if (containmentCount > 0) {
containingRegionWeights.emplace_back(
std::pair<int, float>(rix, containmentCount / points.size()));
}
}
std::sort(containingRegionWeights.begin(), containingRegionWeights.end(),
[](const std::pair<int, float>& a, std::pair<int, float>& b) {
return a.second > b.second;
});
return containingRegionWeights;
}

} // namespace scene
} // namespace esp
18 changes: 18 additions & 0 deletions src/esp/scene/SemanticScene.h
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,24 @@ class SemanticScene {
*/
float CCFractionToUseForBBox() const { return ccLargestVolToUseForBBox_; }

/**
* @brief Compute all SemanticRegions which contain the point and return a
* list of indices for regions in this SemanticScene.
* @param point The query point.
* @return A list of indices for regions which contain the point.
*/
std::vector<int> getRegionsForPoint(const Mn::Vector3& point) const;

/**
* @brief Compute SemanticRegion containment for a set of points.
* @param points A set of points to test for semantic containment.
* @return std::vector<std::pair<int, float>> A sorted list of tuples
* containing region index and percentage of input points contained in that
* region.
*/
std::vector<std::pair<int, float>> getRegionsForPoints(
const std::vector<Mn::Vector3>& points) const;

protected:
/**
* @brief Verify a requested file exists.
Expand Down
30 changes: 29 additions & 1 deletion tests/test_semantic_scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# LICENSE file in the root directory of this source tree.

from os import path as osp
from typing import List

import magnum as mn
import pytest
Expand Down Expand Up @@ -50,7 +51,7 @@ def test_semantic_regions():
assert len(regions) == 2

# Build a list of points within the region
hit_test_points = 6 * [None]
hit_test_points: List[mn.Vector3] = 6 * [None]
hit_test_points[0] = mn.Vector3(-5.1, 0.0, 0.01)
hit_test_points[1] = mn.Vector3(-5.1, 1.0, 0.01)
hit_test_points[2] = mn.Vector3(-5.1, -1.0, 0.01)
Expand Down Expand Up @@ -92,6 +93,18 @@ def test_semantic_regions():
assert not region.contains(miss_test_points[4])
assert not region.contains(miss_test_points[5])

# check batch containment routines
for point in hit_test_points:
assert 0 in semantic_scene.get_regions_for_point(point)
for point in miss_test_points:
assert 0 not in semantic_scene.get_regions_for_point(point)

regions_weights = semantic_scene.get_regions_for_points(
hit_test_points + miss_test_points
)
assert regions_weights[0][0] == 0
assert regions_weights[0][1] >= 0.49 # half or more points contained

# positive X region
region = regions[1]
assert region.id == "test_region_positiveX"
Expand All @@ -117,6 +130,21 @@ def test_semantic_regions():
assert not region.contains(-1 * miss_test_points[4])
assert not region.contains(-1 * miss_test_points[5])

# mix the bathroom and kitchen points
mixed_points: List[mn.Vector3] = list(hit_test_points[1:]) + [
(-1 * p) for p in hit_test_points
] # add one less to create imbalance
regions_weights = semantic_scene.get_regions_for_points(mixed_points)
print(f"regions_weights = {regions_weights}")
assert regions_weights[0][0] == 1 # bathroom with more points comes first
assert (
regions_weights[0][1] >= 0.51
) # more than half points contained in bathroom
assert regions_weights[1][0] == 0 # kitchen with fewer points is second
assert (
abs(regions_weights[1][1] - (1.0 - regions_weights[0][1])) < 0.001
) # kitchen should have the remainder of total percent


@pytest.mark.parametrize("scene", _test_scenes)
def test_semantic_scene(scene, make_cfg_settings):
Expand Down