Skip to content

Commit

Permalink
Region Query API (#2317)
Browse files Browse the repository at this point in the history
* Functions to query for SemanticRegions containing a point or points with pytest
  • Loading branch information
aclegg3 authored Feb 6, 2024
1 parent ad8b289 commit 9f7c84e
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 2 deletions.
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

0 comments on commit 9f7c84e

Please sign in to comment.