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