Skip to content

Commit af47cfb

Browse files
committed
Add tests
1 parent eec0e96 commit af47cfb

1 file changed

Lines changed: 136 additions & 0 deletions

File tree

tests/src/ondevice_tests/image_align_node_test.cpp

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,66 @@ using namespace std::chrono;
1111
using namespace std::chrono_literals;
1212

1313
namespace {
14+
constexpr auto kRuntimeCalibrationFrameTimeout = 180;
15+
constexpr auto kRuntimeCalibrationShiftPx = 40.0f;
16+
constexpr auto kRuntimeCalibrationVerificationFrames = 20;
17+
18+
bool approxEqual(float a, float b, float absTol = 1e-4f, float relTol = 1e-4f) {
19+
return std::abs(a - b) <= (absTol + relTol * std::max(std::abs(a), std::abs(b)));
20+
}
21+
22+
bool sameIntrinsics(const std::array<std::array<float, 3>, 3>& lhs, const std::array<std::array<float, 3>, 3>& rhs) {
23+
for(size_t i = 0; i < lhs.size(); ++i) {
24+
for(size_t j = 0; j < lhs[i].size(); ++j) {
25+
if(!approxEqual(lhs[i][j], rhs[i][j])) {
26+
return false;
27+
}
28+
}
29+
}
30+
return true;
31+
}
32+
33+
std::shared_ptr<dai::ImgFrame> waitForFrameAlignedTo(
34+
const std::shared_ptr<dai::MessageQueue>& queue, const dai::ImgTransformation& transformation, size_t maxFrames = kRuntimeCalibrationFrameTimeout) {
35+
for(size_t i = 0; i < maxFrames; ++i) {
36+
auto frame = queue->get<dai::ImgFrame>();
37+
if(frame != nullptr && frame->transformation.isAlignedTo(transformation)) {
38+
return frame;
39+
}
40+
}
41+
return nullptr;
42+
}
43+
44+
std::shared_ptr<dai::ImgFrame> waitForFrameWithChangedTransform(
45+
const std::shared_ptr<dai::MessageQueue>& queue, const dai::ImgTransformation& baseline, size_t maxFrames = kRuntimeCalibrationFrameTimeout) {
46+
for(size_t i = 0; i < maxFrames; ++i) {
47+
auto frame = queue->get<dai::ImgFrame>();
48+
if(frame != nullptr && !frame->transformation.isAlignedTo(baseline)) {
49+
return frame;
50+
}
51+
}
52+
return nullptr;
53+
}
54+
55+
std::shared_ptr<dai::ImgFrame> waitForNextFrame(const std::shared_ptr<dai::MessageQueue>& queue, size_t maxFrames = kRuntimeCalibrationFrameTimeout) {
56+
for(size_t i = 0; i < maxFrames; ++i) {
57+
auto frame = queue->get<dai::ImgFrame>();
58+
if(frame != nullptr) {
59+
return frame;
60+
}
61+
}
62+
return nullptr;
63+
}
64+
65+
dai::CalibrationHandler makeShiftedCalibration(const dai::CalibrationHandler& base, std::tuple<int, int> outputSize, float shiftPx) {
66+
dai::CalibrationHandler updated = base;
67+
auto intrinsics = updated.getCameraIntrinsics(dai::CameraBoardSocket::CAM_A, outputSize);
68+
intrinsics[0][2] += shiftPx;
69+
intrinsics[1][2] += shiftPx * 0.5f;
70+
updated.setCameraIntrinsics(dai::CameraBoardSocket::CAM_A, intrinsics, outputSize);
71+
return updated;
72+
}
73+
1474
void runImageAlignTest(bool useDepth, bool runOnHost, dai::ImgResizeMode resizeMode) {
1575
dai::Pipeline p;
1676
auto rgbCam = p.create<dai::node::Camera>()->build(dai::CameraBoardSocket::CAM_A);
@@ -57,6 +117,74 @@ void runImageAlignTest(bool useDepth, bool runOnHost, dai::ImgResizeMode resizeM
57117
}
58118
p.stop();
59119
}
120+
121+
void runImageAlignRuntimeCalibrationTest(bool useDepth) {
122+
constexpr auto rgbSize = std::pair<int, int>{1280, 640};
123+
124+
dai::Pipeline p;
125+
auto rgbCam = p.create<dai::node::Camera>()->build(dai::CameraBoardSocket::CAM_A);
126+
auto leftCam = p.create<dai::node::Camera>()->build(dai::CameraBoardSocket::CAM_B);
127+
auto rightCam = p.create<dai::node::Camera>()->build(dai::CameraBoardSocket::CAM_C);
128+
std::shared_ptr<dai::node::StereoDepth> stereo;
129+
auto align = p.create<dai::node::ImageAlign>();
130+
131+
auto* rgbOut = rgbCam->requestOutput(rgbSize, std::nullopt, dai::ImgResizeMode::CROP, std::nullopt, true);
132+
auto* leftOut = leftCam->requestOutput({1280, 800}, std::nullopt);
133+
auto* rightOut = rightCam->requestOutput({1280, 800}, std::nullopt);
134+
135+
if(useDepth) {
136+
stereo = p.create<dai::node::StereoDepth>();
137+
leftOut->link(stereo->left);
138+
rightOut->link(stereo->right);
139+
stereo->depth.link(align->input);
140+
} else {
141+
leftOut->link(align->input);
142+
rightOut->createOutputQueue(); // Keep both mono streams active on platforms that require stereo pair streaming.
143+
align->initialConfig->staticDepthPlane = 0x5AB1;
144+
}
145+
rgbOut->link(align->inputAlignTo);
146+
147+
auto alignedQueue = align->outputAligned.createOutputQueue();
148+
auto alignToQueue = rgbOut->createOutputQueue();
149+
auto device = p.getDefaultDevice();
150+
p.start();
151+
152+
auto baselineAlignTo = alignToQueue->get<dai::ImgFrame>();
153+
REQUIRE(baselineAlignTo != nullptr);
154+
155+
auto baselineAligned = waitForFrameAlignedTo(alignedQueue, baselineAlignTo->transformation);
156+
REQUIRE(baselineAligned != nullptr);
157+
REQUIRE(baselineAligned->getInstanceNum() == baselineAlignTo->getInstanceNum());
158+
159+
auto originalCalibration = device->getCalibration();
160+
auto shiftedCalibration = makeShiftedCalibration(originalCalibration, rgbSize, kRuntimeCalibrationShiftPx);
161+
device->setCalibration(shiftedCalibration);
162+
163+
auto shiftedAlignTo = waitForFrameWithChangedTransform(alignToQueue, baselineAlignTo->transformation, kRuntimeCalibrationVerificationFrames);
164+
if(shiftedAlignTo == nullptr) {
165+
shiftedAlignTo = waitForNextFrame(alignToQueue);
166+
}
167+
REQUIRE(shiftedAlignTo != nullptr);
168+
169+
auto shiftedAligned = waitForFrameAlignedTo(alignedQueue, shiftedAlignTo->transformation);
170+
REQUIRE(shiftedAligned != nullptr);
171+
172+
device->setCalibration(originalCalibration);
173+
174+
auto revertedAlignTo = waitForFrameAlignedTo(alignToQueue, baselineAlignTo->transformation, kRuntimeCalibrationVerificationFrames);
175+
if(revertedAlignTo == nullptr) {
176+
revertedAlignTo = waitForNextFrame(alignToQueue);
177+
}
178+
REQUIRE(revertedAlignTo != nullptr);
179+
180+
for(size_t i = 0; i < kRuntimeCalibrationVerificationFrames; ++i) {
181+
auto revertedAligned = alignedQueue->get<dai::ImgFrame>();
182+
REQUIRE(revertedAligned != nullptr);
183+
REQUIRE(revertedAligned->transformation.isAlignedTo(revertedAlignTo->transformation));
184+
}
185+
186+
p.stop();
187+
}
60188
} // namespace
61189

62190
TEST_CASE("Test ImageAlign node image to image alignment") {
@@ -90,3 +218,11 @@ TEST_CASE("Test ImageAlign node depth to image alignment on host") {
90218
runImageAlignTest(useDepth, runOnHost, resizeMode);
91219
}
92220
}
221+
222+
TEST_CASE("Test ImageAlign node updates aligned transform after runtime calibration change") {
223+
runImageAlignRuntimeCalibrationTest(true);
224+
}
225+
226+
TEST_CASE("Test ImageAlign node resets image-to-image alignment after runtime calibration change") {
227+
runImageAlignRuntimeCalibrationTest(false);
228+
}

0 commit comments

Comments
 (0)