Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@ if(DEPTHAI_DYNAMIC_CALIBRATION_SUPPORT)
endif()

set(TARGET_OPENCV_SOURCES
src/opencv/ColorizeDepthFrame.cpp
src/opencv/ImgFrame.cpp
src/pipeline/node/host/Display.cpp
src/pipeline/node/host/HostCamera.cpp
Expand Down
24 changes: 24 additions & 0 deletions bindings/python/src/pipeline/datatype/ImgFrameBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// depthai
#include "depthai/common/ImgTransformations.hpp"
#include "depthai/pipeline/datatype/ImgFrame.hpp"
#include "depthai/utility/ColorizeDepthFrame.hpp"
#include "ndarray_converter.h"
// pybind
#include <pybind11/cast.h>
Expand Down Expand Up @@ -324,4 +325,27 @@ void bind_imgframe(pybind11::module& m, void* pCallstack) {
// add aliases dai.ImgFrame.Type and dai.ImgFrame.Specs
// m.attr("ImgFrame").attr("Type") = m.attr("RawImgFrame").attr("Type");
// m.attr("ImgFrame").attr("Specs") = m.attr("RawImgFrame").attr("Specs");

#ifdef DEPTHAI_HAVE_OPENCV_SUPPORT
m.def(
"colorizeDepthFrame",
[](py::object frame, float minDepth, float maxDepth, int colormap, bool useLog) -> py::object {
if(py::isinstance<ImgFrame>(frame)) {
auto& img = frame.cast<ImgFrame&>();
return py::cast(dai::utility::colorizeDepthFrame(img, minDepth, maxDepth, static_cast<cv::ColormapTypes>(colormap), useLog));
}
if(py::isinstance<py::array>(frame)) {
auto mat = frame.cast<cv::Mat>();
return py::cast(dai::utility::colorizeDepthFrame(mat, minDepth, maxDepth, static_cast<cv::ColormapTypes>(colormap), useLog));
}
throw std::invalid_argument("colorizeDepthFrame expects an ImgFrame or a numpy array");
},
py::arg("frame"),
py::arg("minDepth") = 500.0f,
py::arg("maxDepth") = 12000.0f,
py::arg("colormap") = static_cast<int>(cv::COLORMAP_JET),
py::arg("useLog") = true,
"Colorize a single-channel depth frame. Depth values (including minDepth and maxDepth) are usually in millimeters. "
"If maxDepth <= minDepth (e.g. 0,0) the 3rd/95th percentile range is auto-computed from finite positive pixels.");
#endif
}
21 changes: 1 addition & 20 deletions examples/cpp/AutoCalibration/auto_calibration_example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,6 @@

#include "depthai/depthai.hpp"

// Visualization helper
void showDepth(const cv::Mat& depthFrame, const std::string& windowName = "Depth", int minDistance = 500, int maxDistance = 5000) {
if(maxDistance <= minDistance) return;

cv::Mat clipped = depthFrame.clone();
clipped.setTo(minDistance, depthFrame < minDistance);
clipped.setTo(maxDistance, depthFrame > maxDistance);

cv::Mat displayFrame;
double scale = 255.0 / (maxDistance - minDistance);
double offset = -minDistance * scale;
clipped.convertTo(displayFrame, CV_8UC1, scale, offset);

cv::Mat colorMap;
cv::applyColorMap(displayFrame, colorMap, cv::COLORMAP_TURBO);

cv::imshow(windowName, colorMap);
}

std::tuple<double, double, double> rotationMatrixToEulerAngles(const cv::Matx33d& rotationMatrix) {
constexpr double kPi = 3.14159265358979323846;
const double sy = std::sqrt(rotationMatrix(0, 0) * rotationMatrix(0, 0) + rotationMatrix(1, 0) * rotationMatrix(1, 0));
Expand Down Expand Up @@ -148,7 +129,7 @@ int main() {
}

auto depth = stereoOut->get<dai::ImgFrame>();
showDepth(depth->getCvFrame(), "Depth", 500, 5000);
cv::imshow("Depth", dai::utility::colorizeDepthFrame(*depth, 500.0f, 12000.0f, cv::COLORMAP_TURBO, true).getCvFrame());

if(cv::waitKey(1) == 'q') break;
}
Expand Down
58 changes: 1 addition & 57 deletions examples/cpp/Depth/depth_rgb_align.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,62 +66,6 @@ class FPSCounter {
std::deque<std::chrono::steady_clock::time_point> frameTimes;
};

cv::Mat colorizeDepth(const cv::Mat& frameDepth) {
if(frameDepth.empty() || frameDepth.channels() != 1) {
return cv::Mat::zeros(frameDepth.size(), CV_8UC3);
}

cv::Mat depth32f;
frameDepth.convertTo(depth32f, CV_32F);

const cv::Mat nonZeroMask = depth32f != 0.0f;
const int nz = cv::countNonZero(nonZeroMask);
if(nz == 0) {
return cv::Mat::zeros(frameDepth.size(), CV_8UC3);
}

std::vector<float> values;
values.reserve(static_cast<size_t>(nz));
for(int r = 0; r < depth32f.rows; ++r) {
const float* d = depth32f.ptr<float>(r);
const uchar* m = nonZeroMask.ptr<uchar>(r);
for(int c = 0; c < depth32f.cols; ++c) {
if(m[c]) {
values.push_back(d[c]);
}
}
}

std::sort(values.begin(), values.end());
auto pct = [&](double p) {
const size_t idx = static_cast<size_t>(std::round((p / 100.0) * (values.size() - 1)));
return values[idx];
};

const float minDepth = pct(3.0);
const float maxDepth = pct(95.0);

cv::Mat logDepth;
depth32f.copyTo(logDepth);
logDepth.setTo(minDepth, ~nonZeroMask);
cv::log(logDepth, logDepth);

const float logMinDepth = std::log(minDepth);
const float logMaxDepth = std::log(maxDepth);

cv::min(logDepth, logMaxDepth, logDepth);
cv::max(logDepth, logMinDepth, logDepth);
logDepth = (logDepth - logMinDepth) * (255.0f / (logMaxDepth - logMinDepth));

cv::Mat depth8U;
logDepth.convertTo(depth8U, CV_8U);

cv::Mat depthFrameColor;
cv::applyColorMap(depth8U, depthFrameColor, cv::COLORMAP_JET);
depthFrameColor.setTo(cv::Scalar::all(0), ~nonZeroMask);
return depthFrameColor;
}

float rgbWeight = 0.4f;
float depthWeight = 0.6f;

Expand Down Expand Up @@ -190,7 +134,7 @@ int main() {

if(frameDepth != nullptr) {
cv::Mat cvFrame = frameRgb->getCvFrame();
cv::Mat alignedDepthColorized = colorizeDepth(frameDepth->getFrame());
cv::Mat alignedDepthColorized = dai::utility::colorizeDepthFrame(*frameDepth, 500.0f, 12000.0f, cv::COLORMAP_JET, true).getCvFrame();
cv::imshow("Depth aligned", alignedDepthColorized);

if(cvFrame.channels() == 1) {
Expand Down
58 changes: 1 addition & 57 deletions examples/cpp/Depth/unified_depth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,62 +30,6 @@

namespace {

cv::Mat colorizeDepth(const cv::Mat& frameDepth) {
if(frameDepth.empty() || frameDepth.channels() != 1) {
return cv::Mat::zeros(frameDepth.size(), CV_8UC3);
}

cv::Mat depth32f;
frameDepth.convertTo(depth32f, CV_32F);

const cv::Mat nonZeroMask = depth32f != 0.0f;
const int nz = cv::countNonZero(nonZeroMask);
if(nz == 0) {
return cv::Mat::zeros(frameDepth.size(), CV_8UC3);
}

std::vector<float> values;
values.reserve(static_cast<size_t>(nz));
for(int r = 0; r < depth32f.rows; ++r) {
const float* d = depth32f.ptr<float>(r);
const uchar* m = nonZeroMask.ptr<uchar>(r);
for(int c = 0; c < depth32f.cols; ++c) {
if(m[c]) {
values.push_back(d[c]);
}
}
}

std::sort(values.begin(), values.end());
auto pct = [&](double p) {
const size_t idx = static_cast<size_t>(std::round((p / 100.0) * (values.size() - 1)));
return values[idx];
};

const float minDepth = pct(3.0);
const float maxDepth = pct(95.0);

cv::Mat logDepth;
depth32f.copyTo(logDepth);
logDepth.setTo(minDepth, ~nonZeroMask);
cv::log(logDepth, logDepth);

const float logMinDepth = std::log(minDepth);
const float logMaxDepth = std::log(maxDepth);

cv::min(logDepth, logMaxDepth, logDepth);
cv::max(logDepth, logMinDepth, logDepth);
logDepth = (logDepth - logMinDepth) * (255.0f / (logMaxDepth - logMinDepth));

cv::Mat depth8U;
logDepth.convertTo(depth8U, CV_8U);

cv::Mat depthFrameColor;
cv::applyColorMap(depth8U, depthFrameColor, cv::COLORMAP_JET);
depthFrameColor.setTo(cv::Scalar::all(0), ~nonZeroMask);
return depthFrameColor;
}

cv::Mat colorizeConfidence(const cv::Mat& frame) {
if(frame.empty() || frame.channels() != 1) {
return cv::Mat::zeros(frame.size(), CV_8UC3);
Expand Down Expand Up @@ -351,7 +295,7 @@ int main(int argc, char** argv) {
auto confidenceFrame = confidenceQueue->get<dai::ImgFrame>();

if(depthFrame != nullptr) {
cv::imshow("depth", colorizeDepth(depthFrame->getFrame()));
cv::imshow("depth", dai::utility::colorizeDepthFrame(*depthFrame, 500.0f, 12000.0f, cv::COLORMAP_JET, true).getCvFrame());
}
if(confidenceFrame != nullptr) {
cv::imshow("confidence", colorizeConfidence(confidenceFrame->getFrame()));
Expand Down
73 changes: 3 additions & 70 deletions examples/cpp/DetectionNetwork/detection_network_remap.cpp
Original file line number Diff line number Diff line change
@@ -1,84 +1,17 @@
#include <algorithm> // Required for std::sort and std::unique
#include <cmath> // Required for std::log, std::isnan, std::isinf
#include <csignal>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <string>
#include <vector>

#include "depthai/depthai.hpp"
#include "xtensor/containers/xadapt.hpp"
#include "xtensor/core/xmath.hpp"

std::atomic<bool> quitEvent(false);

void signalHandler(int) {
quitEvent = true;
}

cv::Mat colorizeDepth(cv::Mat frameDepth) {
cv::Mat invalidMask = frameDepth == 0;
cv::Mat depthFrameColor;

try {
cv::Mat frameDepthFloat;
frameDepth.convertTo(frameDepthFloat, CV_32F);
xt::xtensor<float, 2> depth =
xt::adapt((float*)frameDepthFloat.data, {static_cast<size_t>(frameDepthFloat.rows), static_cast<size_t>(frameDepthFloat.cols)});

// Get valid depth values (non-zero)
std::vector<float> validDepth;
validDepth.reserve(depth.size());
std::copy_if(depth.begin(), depth.end(), std::back_inserter(validDepth), [](float x) { return x != 0; });

if(validDepth.size() == 0) {
return cv::Mat::zeros(frameDepth.rows, frameDepth.cols, CV_8UC3);
}

// Calculate percentiles
std::sort(validDepth.begin(), validDepth.end());
float minDepth = validDepth[static_cast<size_t>(validDepth.size() * 0.03)];
float maxDepth = validDepth[static_cast<size_t>(validDepth.size() * 0.95)];

// Take log of depth values
auto logDepth = xt::eval(xt::log(depth));
float logMinDepth = std::log(minDepth);
float logMaxDepth = std::log(maxDepth);

// Replace invalid values with logMinDepth using a naive implementation
auto logDepthData = logDepth.data();
auto depthData = depth.data();
const size_t size = depth.size();
for(size_t i = 0; i < size; i++) {
if(std::isnan(logDepthData[i]) || std::isinf(logDepthData[i]) || depthData[i] == 0.0f) {
logDepthData[i] = logMinDepth;
}
}

// Clip values
logDepth = xt::clip(logDepth, logMinDepth, logMaxDepth);

// Normalize to 0-255 range
auto normalizedDepth = (logDepth - logMinDepth) / (logMaxDepth - logMinDepth) * 255.0f;

// Convert to CV_8UC1
cv::Mat depthMat(frameDepth.rows, frameDepth.cols, CV_8UC1);
std::transform(normalizedDepth.begin(), normalizedDepth.end(), depthMat.data, [](float x) { return static_cast<uchar>(x); });

// Apply colormap
cv::applyColorMap(depthMat, depthFrameColor, cv::COLORMAP_JET);

// Set invalid pixels to black
depthFrameColor.setTo(cv::Scalar(0, 0, 0), invalidMask);

} catch(const std::exception& e) {
std::cerr << "Error in colorizeDepth: " << e.what() << std::endl;
return cv::Mat::zeros(frameDepth.rows, frameDepth.cols, CV_8UC3);
}

return depthFrameColor;
}

// Helper function to display frames with detections
void displayFrame(const std::string& name,
std::shared_ptr<dai::ImgFrame> frame,
Expand All @@ -88,7 +21,7 @@ void displayFrame(const std::string& name,
cv::Mat cvFrame;

if(frame->getType() == dai::ImgFrame::Type::RAW16) {
cvFrame = colorizeDepth(frame->getFrame());
cvFrame = dai::utility::colorizeDepthFrame(*frame, 500.0f, 12000.0f, cv::COLORMAP_JET, true).getCvFrame();
} else {
cvFrame = frame->getCvFrame();
}
Expand Down Expand Up @@ -167,7 +100,7 @@ int main() {

auto qRgb = detectionNetwork->passthrough.createOutputQueue();
auto qDet = detectionNetwork->out.createOutputQueue();
auto qDepth = stereo->disparity.createOutputQueue();
auto qDepth = stereo->depth.createOutputQueue();

pipeline.start();

Expand All @@ -194,4 +127,4 @@ int main() {
}

return 0;
}
}
14 changes: 7 additions & 7 deletions examples/cpp/DynamicCalibration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ This folder contains minimal, end-to-end **C++** examples that use **`dai::node:
**Flow:**
1. Create mono cameras → request **full-res NV12** (unrectified) → link to:
- `DynamicCalibration.left/right`
- `StereoDepth.left/right` (for live disparity view)
- `StereoDepth.left/right` (for live depth view)
2. Start the pipeline, give AE a moment to settle.
3. **Start calibration** by sending `DynamicCalibrationControl::Commands::StartCalibration{}`.
4. In the loop:
- Show `left`, `right`, and `disparity`.
- Show `left`, `right`, and `depth`.
- Poll `coverageOutput` for progress.
- Poll `calibrationOutput` for a result.
5. When a result arrives:
Expand Down Expand Up @@ -110,10 +110,10 @@ This folder contains minimal, end-to-end **C++** examples that use **`dai::node:
**File:** `calibration_integration.cpp`

**What it does:**
Runs one loop that periodically refreshes coverage, executes calibration, and applies a new calibration automatically when the returned metrics indicate drift — while showing `left`, `right`, and a colorized `disparity` preview.
Runs one loop that periodically refreshes coverage, executes calibration, and applies a new calibration automatically when the returned metrics indicate drift — while showing `left`, `right`, and a colorized `depth` preview.

**Flow:**
1. Create mono cameras → request **full-res NV12** (unrectified) → link to `dai::node::DynamicCalibration` and `dai::node::StereoDepth` for live disparity. Read the device’s current calibration as the baseline.
1. Create mono cameras → request **full-res NV12** (unrectified) → link to `dai::node::DynamicCalibration` and `dai::node::StereoDepth` for live depth. Read the device’s current calibration as the baseline.
2. On a fixed interval (e.g., ~3 seconds), send on the control queue:
- `DynamicCalibrationControl::Commands::LoadImage{}` to compute coverage on the current frames, and
- `DynamicCalibrationControl::Commands::Calibrate{true}` to compute a new candidate calibration and return metrics on `calibrationOutput`.
Expand All @@ -125,7 +125,7 @@ Runs one loop that periodically refreshes coverage, executes calibration, and ap
5. Exit on `q` keypress or window close.

**Notes & defaults:**
- Disparity preview can be auto-scaled to the observed maximum; zero disparity can be rendered black for clarity.
- Depth preview uses the shared colorization helper with a 500–12000 mm range and logarithmic scaling.
- The 0.05 px Sampson threshold is a simple heuristic — tune to your tolerance and noise profile.

**Example console output:**
Expand All @@ -146,7 +146,7 @@ Mono CAM_B ──▶ [Camera] ── NV12 (full-res) ──▶ DynamicCalibratio

Mono CAM_C ──▶ [Camera] ── NV12 (full-res) ──▶ DynamicCalibration.right
└───────────▶ StereoDepth.right ──▶ disparity
└───────────▶ StereoDepth.right ──▶ depth
```

---
Expand Down Expand Up @@ -230,7 +230,7 @@ If you previously read fields from `CalibrationQuality::qualityData`, read the s
- **No quality data returned**
Ensure the target is sharp, well-lit, and covers diverse parts of the image. Increase lighting or steady the rig.

- **Disparity looks worse after apply**
- **Depth preview looks worse after apply**
Collect more diverse samples (tilt/translate the target), or try a performance mode tuned for robustness. Clean lenses; verify focus.

- **Nothing happens after StartCalibration**
Expand Down
Loading