forked from IntelRealSense/librealsense
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rs-depth-quality.cpp
189 lines (164 loc) · 7.86 KB
/
rs-depth-quality.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// 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;
}