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.
422 lines
14 KiB
422 lines
14 KiB
/* License: Apache 2.0. See LICENSE file in root directory. */
|
|
/* Copyright(c) 2019 Intel Corporation. All Rights Reserved. */
|
|
#pragma once
|
|
|
|
#include <librealsense2/rs.hpp>
|
|
|
|
#include "depth-metrics.h"
|
|
#include "model-views.h"
|
|
#include "viewer.h"
|
|
#include "ux-window.h"
|
|
#include "os.h"
|
|
|
|
#include <tuple>
|
|
#include <vector>
|
|
#include <thread>
|
|
#include <mutex>
|
|
|
|
namespace rs2
|
|
{
|
|
namespace depth_quality
|
|
{
|
|
class metrics_model;
|
|
|
|
struct sample
|
|
{
|
|
sample(std::vector<single_metric_data> samples, double timestamp, unsigned long long frame_number) :
|
|
samples(std::move(samples)), timestamp(timestamp), frame_number(frame_number) {}
|
|
|
|
std::vector<single_metric_data> samples;
|
|
|
|
double timestamp;
|
|
unsigned long long frame_number;
|
|
};
|
|
|
|
struct metric_definition
|
|
{
|
|
std::string name;
|
|
std::string units;
|
|
};
|
|
|
|
class metrics_recorder
|
|
{
|
|
public:
|
|
metrics_recorder(viewer_model& viewer_model) :
|
|
_recording(false), _viewer_model(viewer_model)
|
|
{}
|
|
|
|
void add_metric(const metric_definition& data)
|
|
{
|
|
std::lock_guard<std::mutex> lock(_m);
|
|
_metric_data.push_back(data);
|
|
}
|
|
|
|
void add_sample(rs2::frameset& frames, std::vector<single_metric_data> sample)
|
|
{
|
|
std::lock_guard<std::mutex> lock(_m);
|
|
if (_recording)
|
|
{
|
|
record_frames(frames);
|
|
|
|
if (sample.size())
|
|
_samples.push_back({ sample, _model_timer.get_elapsed_ms(), frames.get_frame_number() });
|
|
}
|
|
}
|
|
void start_record(metrics_model* metrics)
|
|
{
|
|
std::lock_guard<std::mutex> lock(_m);
|
|
_metrics = metrics;
|
|
if (auto ret = file_dialog_open(save_file, NULL, NULL, NULL))
|
|
{
|
|
_filename_base = ret;
|
|
_recording = true;
|
|
}
|
|
}
|
|
|
|
void stop_record(device_model* dev)
|
|
{
|
|
std::lock_guard<std::mutex> lock(_m);
|
|
_recording = false;
|
|
serialize_to_csv();
|
|
|
|
if (dev)
|
|
{
|
|
if (auto adv = dev->dev.as<rs400::advanced_mode>())
|
|
{
|
|
std::string filename = _filename_base + "_configuration.json";
|
|
std::ofstream out(filename);
|
|
try
|
|
{
|
|
out << adv.serialize_json();
|
|
}
|
|
catch (...)
|
|
{
|
|
_viewer_model.not_model->add_notification(
|
|
notification_data{ "Metrics Recording: JSON Serializaion has failed",
|
|
RS2_LOG_SEVERITY_WARN,
|
|
RS2_NOTIFICATION_CATEGORY_UNKNOWN_ERROR } );
|
|
}
|
|
}
|
|
}
|
|
_samples.clear();
|
|
_viewer_model.not_model->add_notification(
|
|
notification_data{ "Finished to record frames and matrics data ",
|
|
RS2_LOG_SEVERITY_INFO,
|
|
RS2_NOTIFICATION_CATEGORY_UNKNOWN_ERROR } );
|
|
}
|
|
|
|
bool is_recording()
|
|
{
|
|
return _recording;
|
|
}
|
|
|
|
private:
|
|
void serialize_to_csv() const;
|
|
void record_frames(const frameset & frame);
|
|
viewer_model& _viewer_model;
|
|
std::vector<metric_definition> _metric_data;
|
|
std::vector<sample> _samples;
|
|
rsutils::time::stopwatch _model_timer;
|
|
std::mutex _m;
|
|
bool _recording;
|
|
std::string _filename_base;
|
|
metrics_model* _metrics;
|
|
colorizer _colorize;
|
|
pointcloud _pc;
|
|
};
|
|
|
|
class metric_plot : public std::enable_shared_from_this<metric_plot>
|
|
{
|
|
public:
|
|
enum range
|
|
{
|
|
GREEN_RANGE,
|
|
YELLOW_RANGE,
|
|
RED_RANGE,
|
|
MAX_RANGE
|
|
};
|
|
|
|
std::shared_ptr<metric_plot> set(range r, float from, float to)
|
|
{
|
|
ranges[r].x = from;
|
|
ranges[r].y = to;
|
|
return shared_from_this();
|
|
}
|
|
|
|
range get_range(float val) const
|
|
{
|
|
for (int i = 0; i < MAX_RANGE; i++)
|
|
{
|
|
if (ranges[i].x < val && val <= ranges[i].y)
|
|
return (range)i;
|
|
}
|
|
return MAX_RANGE;
|
|
}
|
|
|
|
metric_plot(const std::string& name, float min, float max,
|
|
const std::string& units, const std::string& description,
|
|
const bool with_plane_fit)
|
|
: _idx(0), _first_idx(0),_vals(), _min(min), _max(max), _id("##" + name),
|
|
_label(name + " = "), _name(name),
|
|
_units(units), _description(description),
|
|
_enabled(true),
|
|
_requires_plane_fit(with_plane_fit),
|
|
_trending_up(std::chrono::milliseconds(700)),
|
|
_trending_down(std::chrono::milliseconds(700)),
|
|
_persistent_visibility(std::chrono::milliseconds(2000)) // The metric's status will be absorbed to make the UI persistent
|
|
{
|
|
for (int i = 0; i < MAX_RANGE; i++) ranges[i] = { 0.f, 0.f };
|
|
}
|
|
~metric_plot() {}
|
|
|
|
void add_value(float val)
|
|
{
|
|
std::lock_guard<std::mutex> lock(_m);
|
|
_vals[_idx] = val;
|
|
_timestamps[_idx] = _model_timer.get_elapsed_ms();
|
|
_idx = (_idx + 1) % SIZE;
|
|
if (_first_idx== _idx)
|
|
_first_idx = (_first_idx + 1) % SIZE;
|
|
}
|
|
|
|
void render(ux_window& win);
|
|
|
|
void visible(bool is_visible)
|
|
{
|
|
std::lock_guard<std::mutex> lock(_m);
|
|
_persistent_visibility.add_value(is_visible);
|
|
}
|
|
|
|
void enable(bool enable)
|
|
{
|
|
std::lock_guard<std::mutex> lock(_m);
|
|
if (enable != _enabled)
|
|
{
|
|
_persistent_visibility.reset();
|
|
_enabled = enable;
|
|
}
|
|
}
|
|
|
|
bool enabled() const { return _enabled; }
|
|
bool requires_plane_fit() const { return _requires_plane_fit; }
|
|
std::string get_name() { return _name; }
|
|
|
|
private:
|
|
bool has_trend(bool positive);
|
|
|
|
std::mutex _m;
|
|
const static size_t SIZE = 200;
|
|
size_t _idx, _first_idx;
|
|
std::array<float, SIZE> _vals;
|
|
std::array<double, SIZE> _timestamps;
|
|
float _min, _max;
|
|
std::string _id, _label, _units, _name, _description;
|
|
bool _enabled;
|
|
const bool _requires_plane_fit;
|
|
|
|
rsutils::time::stopwatch _model_timer;
|
|
temporal_event _trending_up;
|
|
temporal_event _trending_down;
|
|
temporal_event _persistent_visibility; // Control the metric visualization
|
|
|
|
float2 ranges[MAX_RANGE];
|
|
|
|
friend class metrics_model; // For CSV export
|
|
};
|
|
|
|
class metrics_model
|
|
{
|
|
public:
|
|
metrics_model(viewer_model& viewer_model);
|
|
~metrics_model();
|
|
|
|
void render(ux_window& win);
|
|
|
|
std::array<float3, 4> get_plane()
|
|
{
|
|
std::lock_guard<std::mutex> lock(_m);
|
|
return _latest_metrics.plane_corners;
|
|
}
|
|
|
|
void update_stream_attributes(const rs2_intrinsics &intrinsic, float scale_units, float baseline)
|
|
{
|
|
std::lock_guard<std::mutex> lock(_m);
|
|
_depth_intrinsic = intrinsic;
|
|
_depth_scale_units = scale_units;
|
|
_stereo_baseline_mm = baseline;
|
|
};
|
|
|
|
void update_roi_attributes(const region_of_interest& roi, float roi_percent)
|
|
{
|
|
std::lock_guard<std::mutex> lock(_m);
|
|
_roi = roi;
|
|
_roi_percentage = roi_percent;
|
|
}
|
|
|
|
region_of_interest get_roi()
|
|
{
|
|
std::lock_guard<std::mutex> lock(_m);
|
|
return _roi;
|
|
}
|
|
|
|
snapshot_metrics get_last_metrics()
|
|
{
|
|
std::lock_guard<std::mutex> lock(_m);
|
|
return _latest_metrics;
|
|
}
|
|
|
|
void begin_process_frame(rs2::frame f) { _frame_queue.enqueue(std::move(f)); }
|
|
|
|
void add_metric(std::shared_ptr<metric_plot> metric) { _plots.push_back(metric); }
|
|
|
|
callback_type callback;
|
|
|
|
void set_ground_truth(int gt)
|
|
{
|
|
std::lock_guard<std::mutex> lock(_m);
|
|
_ground_truth_mm = gt;
|
|
_use_gt = true;
|
|
}
|
|
|
|
void set_plane_fit(bool found)
|
|
{
|
|
std::lock_guard<std::mutex> lock(_m);
|
|
_plane_fit = found;
|
|
for (auto&& plot : _plots)
|
|
{
|
|
if (plot->enabled())
|
|
{
|
|
bool val = plot->requires_plane_fit() ? found : true;
|
|
plot->visible(val);
|
|
}
|
|
}
|
|
}
|
|
|
|
void disable_ground_truth()
|
|
{
|
|
std::lock_guard<std::mutex> lock(_m);
|
|
_use_gt = false;
|
|
_ground_truth_mm = 0;
|
|
}
|
|
std::tuple<int, bool> get_inputs() const
|
|
{
|
|
std::lock_guard<std::mutex> lock(_m);
|
|
return std::make_tuple(_ground_truth_mm, _plane_fit);
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
_plane_fit = false;
|
|
rs2::frame f;
|
|
while (_frame_queue.poll_for_frame(&f));
|
|
}
|
|
|
|
void update_device_data(const std::string& camera_info)
|
|
{
|
|
_camera_info = camera_info;
|
|
}
|
|
bool is_recording()
|
|
{
|
|
return _recorder.is_recording();
|
|
}
|
|
void start_record()
|
|
{
|
|
_recorder.start_record(this);
|
|
}
|
|
void stop_record(device_model* dev)
|
|
{
|
|
_recorder.stop_record(dev);
|
|
}
|
|
private:
|
|
metrics_model(const metrics_model&);
|
|
|
|
frame_queue _frame_queue;
|
|
std::thread _worker_thread;
|
|
|
|
rs2_intrinsics _depth_intrinsic;
|
|
float _depth_scale_units;
|
|
float _stereo_baseline_mm;
|
|
int _ground_truth_mm;
|
|
bool _use_gt;
|
|
bool _plane_fit;
|
|
region_of_interest _roi;
|
|
float _roi_percentage;
|
|
snapshot_metrics _latest_metrics;
|
|
bool _active;
|
|
std::vector<std::shared_ptr<metric_plot>> _plots;
|
|
metrics_recorder _recorder;
|
|
std::string _camera_info;
|
|
mutable std::mutex _m;
|
|
|
|
friend class metrics_recorder;
|
|
friend class tool_model;
|
|
};
|
|
|
|
using metric = std::shared_ptr<metric_plot>;
|
|
|
|
class tool_model
|
|
{
|
|
public:
|
|
tool_model(rs2::context& ctx);
|
|
|
|
bool start(ux_window& win);
|
|
|
|
void render(ux_window& win);
|
|
|
|
void update_configuration();
|
|
|
|
void reset(ux_window& win);
|
|
|
|
bool draw_instructions(ux_window& win, const rect& viewer_rect, bool& distance, bool& orientation);
|
|
|
|
void draw_guides(ux_window& win, const rect& viewer_rect, bool distance_guide, bool orientation_guide);
|
|
|
|
std::shared_ptr<metric_plot> make_metric(
|
|
const std::string& name, float min, float max, bool plane_fit,
|
|
const std::string& units,
|
|
const std::string& description);
|
|
|
|
void on_frame(callback_type callback) { _metrics_model.callback = callback; }
|
|
|
|
float get_depth_scale() const { return _metrics_model._depth_scale_units; }
|
|
rs2::device get_active_device(void) const;
|
|
|
|
private:
|
|
|
|
std::string capture_description();
|
|
|
|
rs2::context& _ctx;
|
|
pipeline _pipe;
|
|
std::shared_ptr<device_model> _device_model;
|
|
viewer_model _viewer_model;
|
|
rs2::points _last_points;
|
|
texture_buffer* _last_texture;
|
|
std::shared_ptr<subdevice_model> _depth_sensor_model;
|
|
metrics_model _metrics_model;
|
|
std::string _error_message;
|
|
bool _first_frame = true;
|
|
rsutils::time::periodic_timer _update_readonly_options_timer;
|
|
bool _device_in_use = false;
|
|
|
|
float _roi_percent = 0.4f;
|
|
int _roi_combo_index = 2;
|
|
temporal_event _roi_located;
|
|
|
|
temporal_event _too_far;
|
|
temporal_event _too_close;
|
|
temporal_event _skew_left;
|
|
temporal_event _skew_right;
|
|
temporal_event _skew_up;
|
|
temporal_event _skew_down;
|
|
temporal_event _angle_alert;
|
|
std::map<int, temporal_event> _depth_scale_events;
|
|
|
|
float _min_dist, _max_dist, _max_angle;
|
|
std::mutex _mutex;
|
|
|
|
bool _use_ground_truth = false;
|
|
int _ground_truth = 0;
|
|
};
|
|
}
|
|
}
|