You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

190 lines
7.9 KiB

// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2015 Intel Corporation. All Rights Reserved.
#include <numeric>
#include <librealsense2/rs.hpp>
#include "depth-quality-model.h"
int main(int argc, const char * argv[]) try
{
rs2::context ctx;
rs2::ux_window window("Depth Quality Tool", ctx);
rs2::depth_quality::tool_model model(ctx);
using namespace rs2::depth_quality;
// ===============================
// Metrics Definitions
// ===============================
metric fill = model.make_metric(
"Fill-Rate", 0, 100, false, "%",
"Fill Rate.\n"
"Percentage of pixels with valid depth\n"
"values out of all pixels within the ROI\n");
metric z_accuracy = model.make_metric(
"Z Accuracy", -10, 10, true, "%",
"Z-Accuracy given Ground Truth (GT)\n"
" as percentage of the range.\n"
"Algorithm:\n"
"1. Transpose Z values from the Fitted to the GT plane\n"
"2. Calculate depth errors:\n"
" err= signed(Transposed Z - GT).\n"
"3. Retrieve the median of the depth errors:\n"
"4. Interpret results:\n"
" - Positive value indicates that the Plane Fit\n"
"is further than the Ground Truth (overshot)\n"
" - Negative value indicates the Plane Fit\n"
"is in front of Ground Truth (undershot)\n");
metric plane_fit_rms_error = model.make_metric(
"Plane Fit RMS Error", 0.f, 5.f, true, "%",
"Plane Fit RMS Error .\n"
"This metric provides RMS of Z-Error (Spatial Noise)\n"
"and is calculated as follows:\n"
"Zi - depth range of i-th pixel (mm)\n"
"Zpi -depth of Zi projection onto plane fit (mm)\n"
" n \n"
"RMS = SQRT((SUM(Zi-Zpi)^2)/n)\n"
" i=1 ");
metric sub_pixel_rms_error = model.make_metric(
"Subpixel RMS Error", 0.f, 1.f, true, "pixel",
"Subpixel RMS Error .\n"
"This metric provides the subpixel accuracy\n"
"and is calculated as follows:\n"
"Zi - depth range of i-th pixel (mm)\n"
"Zpi -depth of Zi projection onto plane fit (mm)\n"
"BL - optical baseline (mm)\n"
"FL - focal length, as a multiple of pixel width\n"
"Di = BL*FL/Zi; Dpi = Bl*FL/Zpi\n"
" n \n"
"RMS = SQRT((SUM(Di-Dpi)^2)/n)\n"
" i=1 ");
// ===============================
// Metrics Calculation
// ===============================
model.on_frame([&](
const std::vector<rs2::float3>& points,
const rs2::plane p,
const rs2::region_of_interest roi,
const float baseline_mm,
const float focal_length_pixels,
const int ground_truth_mm,
const bool plane_fit,
const float plane_fit_to_ground_truth_mm,
const float distance_mm,
bool record,
std::vector<single_metric_data>& samples)
{
float TO_METERS = model.get_depth_scale();
static const float TO_MM = 1000.f;
static const float TO_PERCENT = 100.f;
// Calculate fill rate relative to the ROI
auto fill_rate = points.size() / float((roi.max_x - roi.min_x)*(roi.max_y - roi.min_y)) * TO_PERCENT;
fill->add_value(fill_rate);
if(record) samples.push_back({fill->get_name(), fill_rate });
if (!plane_fit) return;
const float bf_factor = baseline_mm * focal_length_pixels * TO_METERS; // also convert point units from mm to meter
std::vector<rs2::float3> points_set = points;
std::vector<float> distances;
std::vector<float> disparities;
std::vector<float> gt_errors;
// Reserve memory for the data
distances.reserve(points.size());
disparities.reserve(points.size());
if (ground_truth_mm) gt_errors.reserve(points.size());
// Remove outliers [below 0.5% and above 99.5%)
std::sort(points_set.begin(), points_set.end(), [](const rs2::float3& a, const rs2::float3& b) { return a.z < b.z; });
size_t outliers = points_set.size() / 200;
points_set.erase(points_set.begin(), points_set.begin() + outliers); // crop min 0.5% of the dataset
points_set.resize(points_set.size() - outliers); // crop max 0.5% of the dataset
// Convert Z values into Depth values by aligning the Fitted plane with the Ground Truth (GT) plane
// Calculate distance and disparity of Z values to the fitted plane.
// Use the rotated plane fit to calculate GT errors
for (auto point : points_set)
{
// Find distance from point to the reconstructed plane
auto dist2plane = p.a*point.x + p.b*point.y + p.c*point.z + p.d;
// Project the point to plane in 3D and find distance to the intersection point
rs2::float3 plane_intersect = { float(point.x - dist2plane*p.a),
float(point.y - dist2plane*p.b),
float(point.z - dist2plane*p.c) };
// Store distance, disparity and gt- error
distances.push_back(dist2plane * TO_MM);
disparities.push_back(bf_factor / point.length() - bf_factor / plane_intersect.length());
// The negative dist2plane represents a point closer to the camera than the fitted plane
if (ground_truth_mm) gt_errors.push_back(plane_fit_to_ground_truth_mm + (dist2plane * TO_MM));
}
// Show Z accuracy metric only when Ground Truth is available
z_accuracy->enable(ground_truth_mm > 0);
if (ground_truth_mm)
{
std::sort(begin(gt_errors), end(gt_errors));
auto gt_median = gt_errors[gt_errors.size() / 2];
auto accuracy = TO_PERCENT * (gt_median / ground_truth_mm);
z_accuracy->add_value(accuracy);
if (record) samples.push_back({ z_accuracy->get_name(), accuracy });
}
// Calculate Sub-pixel RMS for Stereo-based Depth sensors
double total_sq_disparity_diff = 0;
for (auto disparity : disparities)
{
total_sq_disparity_diff += disparity*disparity;
}
auto rms_subpixel_val = static_cast<float>(std::sqrt(total_sq_disparity_diff / disparities.size()));
sub_pixel_rms_error->add_value(rms_subpixel_val);
if (record) samples.push_back({ sub_pixel_rms_error->get_name(), rms_subpixel_val });
// Calculate Plane Fit RMS (Spatial Noise) mm
double plane_fit_err_sqr_sum = std::inner_product(distances.begin(), distances.end(), distances.begin(), 0.);
auto rms_error_val = static_cast<float>(std::sqrt(plane_fit_err_sqr_sum / distances.size()));
auto rms_error_val_per = TO_PERCENT * (rms_error_val / distance_mm);
plane_fit_rms_error->add_value(rms_error_val_per);
if (record)
{
samples.push_back({ plane_fit_rms_error->get_name(), rms_error_val_per });
samples.push_back({ plane_fit_rms_error->get_name() + " mm", rms_error_val });
}
});
// ===============================
// Rendering Loop
// ===============================
window.on_load = [&]()
{
return model.start(window);
};
while(window)
{
model.render(window);
}
return EXIT_SUCCESS;
}
catch (const rs2::error& e)
{
std::cerr << "RealSense error calling " << e.get_failed_function() << "(" << e.get_failed_args() << "):\n " << e.what() << std::endl;
return EXIT_FAILURE;
}
catch (const std::exception& e)
{
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}