Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
37 changes: 32 additions & 5 deletions components/nn/include/maix_nn_yolo26_depth.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,16 @@ namespace maix::nn
* @param fit image resize fit mode if input image not equal to model' input size,
* will auto resize to model's input size then detect, and recover to image input size.
* Default Fit.FIT_CONTAIN, see image.Fit.
* @param cal_a calibration scale factor a, the depth is converted by d_real = exp(a * log(d) + b).
* Default 1.0 (no calibration). Set both cal_a/cal_b to fit your camera/scene.
* @param cal_b calibration offset b, see cal_a. Default 0.0 (no calibration).
* @throw If error occurred, will raise err::Exception, you can find reason in log, mostly caused by args error or hardware error.
* @return result, a tensor.Tensor object. If in dual_buff mode, value can be None(in Python) or nullptr(in C++) when not ready. In C++, you need to delete it after use.
* @return result, a tensor.Tensor object with calibrated depth in meters. If in dual_buff mode, value can be None(in Python) or nullptr(in C++) when not ready. In C++, you need to delete it after use.
* @maixpy maix.nn.YOLO26Depth.get_depth
* @maixcdk maix.nn.YOLO26Depth.get_depth
*/
tensor::Tensor *get_depth(image::Image &img, image::Fit fit = image::FIT_CONTAIN)
tensor::Tensor *get_depth(image::Image &img, image::Fit fit = image::FIT_CONTAIN,
float cal_a = 1.0f, float cal_b = 0.0f)
{
if (_model == nullptr) return nullptr;
std::unique_ptr<tensor::Tensors> outputs(_model->forward_image(img, {}, {}, fit, false, false));
Expand All @@ -205,6 +210,7 @@ namespace maix::nn
_output_h = info.height;
_output_w = info.width;
tensor::Tensor *result = new tensor::Tensor(t->shape(), t->dtype(), t->data(), true);
_apply_calibration((float *)result->data(), info.width * info.height, cal_a, cal_b);
return result;
}

Expand All @@ -217,14 +223,19 @@ namespace maix::nn
* @param cmap Color map used convert grayscale distance estimation image to RGB image.
* Diiferent cmap will influence finally image.
* Default image.CMap.JET (near red/yellow, far blue).
* @param cal_a calibration scale factor a, the depth is converted by d_real = exp(a * log(d) + b).
* Default 1.0 (no calibration). Set both cal_a/cal_b to fit your camera/scene.
* @param cal_b calibration offset b, see cal_a. Default 0.0 (no calibration).
* @throw If error occurred, will raise err::Exception, you can find reason in log, mostly caused by args error or hardware error.
* @return result, a image::Image object. If in dual_buff mode, value can be None(in Python) or nullptr(in C++) when not ready. In C++, you need to delete it after use.
* @return result, a image::Image object with calibrated depth heatmap. If in dual_buff mode, value can be None(in Python) or nullptr(in C++) when not ready. In C++, you need to delete it after use.
* @maixpy maix.nn.YOLO26Depth.get_depth_image
* @maixcdk maix.nn.YOLO26Depth.get_depth_image
*/
image::Image *get_depth_image(image::Image &img, image::Fit fit = image::FIT_CONTAIN,
image::CMap cmap = image::CMap::JET)
image::CMap cmap = image::CMap::JET,
float cal_a = 1.0f, float cal_b = 0.0f)
{
std::unique_ptr<tensor::Tensor> depth(get_depth(img, fit));
std::unique_ptr<tensor::Tensor> depth(get_depth(img, fit, cal_a, cal_b));
if (!depth) return nullptr;
return depth_to_image(*depth, img.width(), img.height(), fit, cmap);
}
Expand Down Expand Up @@ -392,6 +403,22 @@ namespace maix::nn
return transform;
}

/**
* Apply log-affine depth calibration in place: d_real = exp(a * log(d) + b).
* With default a=1, b=0 the data is unchanged.
*/
void _apply_calibration(float *data, int count, float cal_a, float cal_b)
{
if (cal_a == 1.0f && cal_b == 0.0f)
return;
for (int i = 0; i < count; i++)
{
float d = data[i];
if (std::isfinite(d) && d > 0)
data[i] = expf(cal_a * logf(d) + cal_b);
}
}

/**
* Convert depth map to RGB888 heatmap: disparity(1/depth) + min-max normalize + cmap lookup.
* Return a new image::Image, caller should delete it.
Expand Down
25 changes: 17 additions & 8 deletions examples/nn_yolo26_depth/main/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
* YOLO26-depth example
*
* Usage:
* - 单张图片推理并保存热力图:
* ./nn_yolo26_depth mud_model_path image_path [output_path]
* 例: ./nn_yolo26_depth /tmp/yolo26n-depth.mud /tmp/bus.jpg /tmp/bus_heatmap.jpg
* - 实时摄像头推理并显示热力图:
* ./nn_yolo26_depth mud_model_path
* - 单张图片推理并保存热力图(可选传 cal_a cal_b 校准):
* ./nn_yolo26_depth mud_model_path image_path [output_path] [cal_a cal_b]
* 例: ./nn_yolo26_depth /tmp/yolo26n-depth.mud /tmp/bus.jpg /tmp/bus_heatmap.jpg 0.85 0.40
* - 实时摄像头推理并显示热力图(可选传 cal_a cal_b):
* ./nn_yolo26_depth mud_model_path [cal_a cal_b]
* 点击画面可添加/取消距离探针,最多保留 5 个,距离随画面实时更新。
*
* 依赖(设备上需存在):
Expand Down Expand Up @@ -166,6 +166,15 @@ int _main(int argc, char *argv[])
const char *model_path = argv[1];
bool dual_buff = false;
image::CMap cmap = image::CMap::JET;
// 可选深度校准参数(默认不校准): d_real = exp(cal_a * log(d_raw) + cal_b)
float cal_a = 1.0f;
float cal_b = 0.0f;
if (argc >= 6)
{
cal_a = std::stof(argv[4]);
cal_b = std::stof(argv[5]);
log::info("depth calibration enabled: cal_a=%f cal_b=%f", cal_a, cal_b);
}

log::info("model path: %s", model_path);
nn::YOLO26Depth model(model_path, dual_buff);
Expand All @@ -189,8 +198,8 @@ int _main(int argc, char *argv[])
img->width(), img->height(), model.input_width(), model.input_height());
}

// 推理得到深度热力图(自动 resize 回原图尺寸)
maix::image::Image *heatmap = model.get_depth_image(*img, image::FIT_CONTAIN, cmap);
// 推理得到深度热力图(自动 resize 回原图尺寸; 可传 cal_a/cal_b 校准)
maix::image::Image *heatmap = model.get_depth_image(*img, image::FIT_CONTAIN, cmap, cal_a, cal_b);
err::check_null_raise(heatmap, "get_depth_image failed");

// 保存热力图
Expand Down Expand Up @@ -246,7 +255,7 @@ int _main(int argc, char *argv[])
was_pressed = pressed;
}

std::unique_ptr<tensor::Tensor> depth(model.get_depth(*img, image::FIT_CONTAIN));
std::unique_ptr<tensor::Tensor> depth(model.get_depth(*img, image::FIT_CONTAIN, cal_a, cal_b));
if (!depth)
continue;

Expand Down
Loading