Skip to content

Commit 37433d2

Browse files
authored
Merge pull request #1504 from luxonis/fix_improve_spatial_calculations
Fix: Improve calculation of spatial coordinates from depth
2 parents 4808a7b + c831d57 commit 37433d2

File tree

4 files changed

+188
-2
lines changed

4 files changed

+188
-2
lines changed

cmake/Depthai/DepthaiDeviceRVC4Config.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
set(DEPTHAI_DEVICE_RVC4_MATURITY "snapshot")
44

55
# "version if applicable"
6-
set(DEPTHAI_DEVICE_RVC4_VERSION "0.0.1+cd0fc231d8c860acf11ab424109d7e42500626ae")
6+
set(DEPTHAI_DEVICE_RVC4_VERSION "0.0.1+d5316ad7b60416d0a88190f4b3c2dde0bbb50e5a")

cmake/Depthai/DepthaiDeviceSideConfig.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
set(DEPTHAI_DEVICE_SIDE_MATURITY "snapshot")
33

44
# "full commit hash of device side binary"
5-
set(DEPTHAI_DEVICE_SIDE_COMMIT "0ada9b9800478cf2c7056cea5c2bab2c8f6a2cbd")
5+
set(DEPTHAI_DEVICE_SIDE_COMMIT "f81edb9c2328ee1f80a6f80c3fade0af0076a3ff")
66

77
# "version if applicable"
88
set(DEPTHAI_DEVICE_SIDE_VERSION "")

tests/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,9 @@ dai_add_test(neural_network_test src/ondevice_tests/neural_network_test.cpp)
341341
target_compile_definitions(neural_network_test PRIVATE BLOB_PATH="${mobilenet_blob}")
342342
dai_set_test_labels(neural_network_test ondevice rvc2_all ci)
343343

344+
dai_add_test(spatial_location_calculator_test src/ondevice_tests/pipeline/node/spatial_location_calculator_test.cpp)
345+
dai_set_test_labels(spatial_location_calculator_test ondevice rvc2_all rvc4 ci)
346+
344347
# Color camera test
345348
dai_add_test(color_camera_node_test src/ondevice_tests/color_camera_node_test.cpp)
346349
dai_set_test_labels(color_camera_node_test ondevice rvc2_all ci)
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
#include <catch2/catch_all.hpp>
2+
3+
#include <array>
4+
#include <cstdint>
5+
#include <cstring>
6+
#include <vector>
7+
8+
#include "depthai/depthai.hpp"
9+
10+
using Catch::Approx;
11+
12+
TEST_CASE("SpatialLocationCalculatorConfig tracks ROI updates") {
13+
dai::SpatialLocationCalculatorConfig config;
14+
REQUIRE(config.getConfigData().empty());
15+
16+
dai::SpatialLocationCalculatorConfigData initial;
17+
initial.roi = dai::Rect(10.0F, 20.0F, 30.0F, 40.0F, false);
18+
initial.depthThresholds.lowerThreshold = 100;
19+
initial.depthThresholds.upperThreshold = 2000;
20+
initial.calculationAlgorithm = dai::SpatialLocationCalculatorAlgorithm::MODE;
21+
initial.stepSize = 1;
22+
config.addROI(initial);
23+
24+
auto rois = config.getConfigData();
25+
REQUIRE(rois.size() == 1);
26+
CHECK(rois.front().depthThresholds.lowerThreshold == initial.depthThresholds.lowerThreshold);
27+
CHECK(rois.front().depthThresholds.upperThreshold == initial.depthThresholds.upperThreshold);
28+
CHECK(rois.front().calculationAlgorithm == initial.calculationAlgorithm);
29+
CHECK(rois.front().stepSize == initial.stepSize);
30+
CHECK(rois.front().roi.x == Approx(initial.roi.x));
31+
CHECK(rois.front().roi.y == Approx(initial.roi.y));
32+
CHECK(rois.front().roi.width == Approx(initial.roi.width));
33+
CHECK(rois.front().roi.height == Approx(initial.roi.height));
34+
35+
dai::SpatialLocationCalculatorConfigData overrideA;
36+
overrideA.roi = dai::Rect(0.05F, 0.05F, 0.25F, 0.25F, true);
37+
overrideA.depthThresholds.lowerThreshold = 300;
38+
overrideA.depthThresholds.upperThreshold = 4500;
39+
overrideA.calculationAlgorithm = dai::SpatialLocationCalculatorAlgorithm::MAX;
40+
overrideA.stepSize = 6;
41+
42+
dai::SpatialLocationCalculatorConfigData overrideB;
43+
overrideB.roi = dai::Rect(0.3F, 0.4F, 0.2F, 0.15F, true);
44+
overrideB.depthThresholds.lowerThreshold = 200;
45+
overrideB.depthThresholds.upperThreshold = 5200;
46+
overrideB.calculationAlgorithm = dai::SpatialLocationCalculatorAlgorithm::MEDIAN;
47+
overrideB.stepSize = 3;
48+
49+
config.setROIs({overrideA, overrideB});
50+
rois = config.getConfigData();
51+
REQUIRE(rois.size() == 2);
52+
53+
CHECK(rois[0].depthThresholds.lowerThreshold == overrideA.depthThresholds.lowerThreshold);
54+
CHECK(rois[0].depthThresholds.upperThreshold == overrideA.depthThresholds.upperThreshold);
55+
CHECK(rois[0].calculationAlgorithm == overrideA.calculationAlgorithm);
56+
CHECK(rois[0].stepSize == overrideA.stepSize);
57+
CHECK(rois[0].roi.x == Approx(overrideA.roi.x));
58+
CHECK(rois[0].roi.y == Approx(overrideA.roi.y));
59+
CHECK(rois[0].roi.width == Approx(overrideA.roi.width));
60+
CHECK(rois[0].roi.height == Approx(overrideA.roi.height));
61+
62+
CHECK(rois[1].depthThresholds.lowerThreshold == overrideB.depthThresholds.lowerThreshold);
63+
CHECK(rois[1].depthThresholds.upperThreshold == overrideB.depthThresholds.upperThreshold);
64+
CHECK(rois[1].calculationAlgorithm == overrideB.calculationAlgorithm);
65+
CHECK(rois[1].stepSize == overrideB.stepSize);
66+
CHECK(rois[1].roi.x == Approx(overrideB.roi.x));
67+
CHECK(rois[1].roi.y == Approx(overrideB.roi.y));
68+
CHECK(rois[1].roi.width == Approx(overrideB.roi.width));
69+
CHECK(rois[1].roi.height == Approx(overrideB.roi.height));
70+
}
71+
72+
TEST_CASE("SpatialLocationCalculator synthetic depth data test") {
73+
constexpr unsigned width = 640;
74+
constexpr unsigned height = 480;
75+
76+
struct RoiSpec {
77+
dai::Rect roi;
78+
std::uint16_t depth;
79+
std::uint32_t widthPx;
80+
std::uint32_t heightPx;
81+
};
82+
83+
const std::vector<RoiSpec> roiSpecs = {
84+
{dai::Rect(100.0F, 120.0F, 40.0F, 60.0F, false), static_cast<std::uint16_t>(400), 40, 60},
85+
{dai::Rect(320.0F, 200.0F, 64.0F, 72.0F, false), static_cast<std::uint16_t>(600), 64, 72},
86+
{dai::Rect(420.0F, 80.0F, 96.0F, 96.0F, false), static_cast<std::uint16_t>(800), 96, 96},
87+
};
88+
89+
dai::Pipeline pipeline;
90+
auto spatial = pipeline.create<dai::node::SpatialLocationCalculator>();
91+
92+
std::vector<dai::SpatialLocationCalculatorConfigData> configData;
93+
configData.reserve(roiSpecs.size());
94+
for(const auto& spec : roiSpecs) {
95+
dai::SpatialLocationCalculatorConfigData cfg;
96+
cfg.roi = spec.roi;
97+
cfg.depthThresholds.lowerThreshold = 0;
98+
cfg.depthThresholds.upperThreshold = 10000;
99+
cfg.calculationAlgorithm = dai::SpatialLocationCalculatorAlgorithm::AVERAGE;
100+
cfg.stepSize = 1;
101+
configData.push_back(cfg);
102+
}
103+
spatial->initialConfig->setROIs(configData);
104+
105+
auto depthQueue = spatial->inputDepth.createInputQueue();
106+
auto outputQueue = spatial->out.createOutputQueue();
107+
auto passthroughQueue = spatial->passthroughDepth.createOutputQueue();
108+
109+
110+
std::vector<std::uint16_t> depthPixels(width * height, 1000);
111+
auto setRegionDepth = [&](const RoiSpec& spec) {
112+
const int x0 = static_cast<int>(spec.roi.x);
113+
const int y0 = static_cast<int>(spec.roi.y);
114+
for(int y = 0; y < static_cast<int>(spec.heightPx); ++y) {
115+
for(int x = 0; x < static_cast<int>(spec.widthPx); ++x) {
116+
depthPixels[(y0 + y) * width + (x0 + x)] = spec.depth;
117+
}
118+
}
119+
};
120+
for(const auto& spec : roiSpecs) {
121+
setRegionDepth(spec);
122+
}
123+
124+
// Prepare synthetic depth frame
125+
auto depthFrame = std::make_shared<dai::ImgFrame>();
126+
depthFrame->setType(dai::ImgFrame::Type::RAW16);
127+
depthFrame->setSize(width, height);
128+
depthFrame->setSourceSize(width, height);
129+
std::vector<std::uint8_t> rawDepth(depthPixels.size() * sizeof(std::uint16_t));
130+
std::memcpy(rawDepth.data(), depthPixels.data(), rawDepth.size());
131+
depthFrame->setData(rawDepth);
132+
133+
const std::array<std::array<float, 3>, 3> intrinsics = {{
134+
{{7.5F, 0.0F, 3.2F}},
135+
{{0.0F, 4.8F, 2.4F}},
136+
{{0.0F, 0.0F, 1.0F}},
137+
}};
138+
depthFrame->transformation.setSourceSize(width, height);
139+
depthFrame->transformation.setSize(width, height);
140+
depthFrame->transformation.setIntrinsicMatrix(intrinsics);
141+
REQUIRE(depthFrame->validateTransformations());
142+
143+
pipeline.start();
144+
depthQueue->send(depthFrame);
145+
146+
auto spatialData = outputQueue->get<dai::SpatialLocationCalculatorData>();
147+
REQUIRE(spatialData != nullptr);
148+
const auto results = spatialData->getSpatialLocations();
149+
REQUIRE(results.size() == roiSpecs.size());
150+
151+
auto passthroughFrame = passthroughQueue->get<dai::ImgFrame>();
152+
REQUIRE(passthroughFrame != nullptr);
153+
CHECK(passthroughFrame->getWidth() == width);
154+
CHECK(passthroughFrame->getHeight() == height);
155+
156+
const float fx = intrinsics[0][0];
157+
const float fy = intrinsics[1][1];
158+
const float cx = intrinsics[0][2];
159+
const float cy = intrinsics[1][2];
160+
161+
for(std::size_t idx = 0; idx < roiSpecs.size(); ++idx) {
162+
const auto& spec = roiSpecs[idx];
163+
const auto& spatialLoc = results.at(idx);
164+
165+
CHECK(spatialLoc.depthAverage == Approx(static_cast<float>(spec.depth)).margin(1.0F));
166+
CHECK(spatialLoc.depthMin == spec.depth);
167+
CHECK(spatialLoc.depthMax == spec.depth);
168+
CHECK(spatialLoc.depthAveragePixelCount == spec.widthPx * spec.heightPx);
169+
170+
const float centerX = spec.roi.x + spec.roi.width / 2.0F;
171+
const float centerY = spec.roi.y + spec.roi.height / 2.0F;
172+
const float expectedX = ((centerX - cx) / fx) * static_cast<float>(spec.depth);
173+
const float expectedY = ((centerY - cy) / fy) * static_cast<float>(spec.depth);
174+
const float expectedZ = static_cast<float>(spec.depth);
175+
176+
CHECK(spatialLoc.spatialCoordinates.x == Approx(expectedX).margin(5.0F));
177+
CHECK(spatialLoc.spatialCoordinates.y == Approx(expectedY).margin(5.0F));
178+
CHECK(spatialLoc.spatialCoordinates.z == Approx(expectedZ).margin(1.0F));
179+
}
180+
181+
pipeline.stop();
182+
pipeline.wait();
183+
}

0 commit comments

Comments
 (0)