// License: Apache 2.0. See LICENSE file in root directory. // Copyright(c) 2015 Intel Corporation. All Rights Reserved. // The utility shall be used to collect and analyze Realsense Cameras performance. // Therefore no UI is provided, and the data is gathered and serialized in csv-compatible format for offline analysis. // The utility is configured via command-line parameters and requires user-provided configuration file to run // The following lines provide examples of the configurations available /* //Defines the order at which the params (comma-separated) should appear in configuration file //#rs2_stream, width, height, fps, rs2_format, stream_index (optional, put zero or leave empty if you're not sure) //# Examples (note that all lines starting with non-alpha character (#@!...) will be skipped: #D435/415 DEPTH,640,480,30,Z16,0 INFRARED,640,480,30,Y8,1 INFRARED,640,480,30,Y8,2 COLOR,640,480,30,RGB8,0 ##D435i-specific ACCEL,1,1,63,MOTION_XYZ32F GYRO,1,1,200,MOTION_XYZ32F #T265 #FISHEYE,848,800,30,Y8,1 #FISHEYE,848,800,30,Y8,2 #ACCEL,1,1,62,MOTION_XYZ32F,0 #GYRO,1,1,200,MOTION_XYZ32F,0 #POSE,0,0,262,6DOF,0 */ #include #include "tclap/CmdLine.h" #include #include #include using namespace std; using namespace TCLAP; namespace rs_data_collect { const uint64_t DEF_FRAMES_NUMBER = 100; const std::string DEF_OUTPUT_FILE_NAME("frames_data.csv"); // Split string into token, trim unreadable characters inline std::vector tokenize(std::string line, char separator) { std::vector tokens; // Remove trailing characters line.erase(line.find_last_not_of("\t\n\v\r ") + 1); stringstream ss(line); while (ss.good()) { string substr; getline(ss, substr, separator); tokens.push_back(substr); } return tokens; } struct stringify { std::ostringstream ss; template stringify & operator << (const T & val) { ss << val; return *this; } operator std::string() const { return ss.str(); } }; template inline bool val_in_range(const T& val, const std::initializer_list& list) { for (const auto& i : list) { if (val == i) { return true; } } return false; } inline int parse_number(char const *s, int base = 0) { char c; stringstream ss(s); int i; ss >> i; if (ss.fail() || ss.get(c)) { throw runtime_error(string(string("Invalid numeric input - ") + s + string("\n"))); } return i; } inline std::string to_lower(const std::string& s) { auto copy = s; std::transform(copy.begin(), copy.end(), copy.begin(), ::tolower); return copy; } inline rs2_format parse_format(const string str) { for (int i = RS2_FORMAT_ANY; i < RS2_FORMAT_COUNT; i++) { if (to_lower(rs2_format_to_string((rs2_format)i)) == str) { return (rs2_format)i; } } throw runtime_error((string("Invalid format - ") + str + string("\n")).c_str()); } inline rs2_stream parse_stream_type(const string str) { for (int i = RS2_STREAM_ANY; i < RS2_STREAM_COUNT; i++) { if (to_lower(rs2_stream_to_string((rs2_stream)i)) == str) { return static_cast(i); } } throw runtime_error((string("Invalid stream type - ") + str + string("\n")).c_str()); } inline int parse_fps(const string str) { return parse_number(str.c_str()); } inline std::string get_profile_description(const rs2::stream_profile& profile) { std::stringstream ss; ss << profile.stream_name() << "," << profile.stream_type() << "," << profile.format() << "," << profile.fps(); if (auto vp = profile.as()) ss << "," << vp.width() << "," << vp.height(); ss << std::endl; return ss.str().c_str(); } struct stream_request { rs2_stream _stream_type; rs2_format _stream_format; int _width; int _height; int _fps; int _stream_idx; }; inline std::ostream& operator<<(std::ostream& os, const stream_request& req) { return os << "Type: " << req._stream_type << ", Idx: " << req._stream_idx << ", " << req._stream_format << ", [" << req._width << "x" << req._height << "], " << req._fps << "fps" << std::endl; } enum config_params { e_stream_type, e_res_width, e_res_height, e_fps, e_format, e_stream_index }; enum application_stop : uint8_t { stop_on_frame_num, stop_on_timeout, stop_on_user_frame_num, stop_on_any }; class data_collector { public: data_collector(std::shared_ptr dev, ValueArg& timeout, ValueArg& max_frames); ~data_collector(){}; data_collector(const data_collector&); void parse_and_configure(ValueArg& config_file); void save_data_to_file(const string& out_filename); void collect_frame_attributes(rs2::frame f, std::chrono::time_point start_time); bool collecting(std::chrono::time_point start_time); const std::vector& selected_sensors() const { return active_sensors; }; struct frame_record { frame_record(unsigned long long frame_number, double frame_ts, double host_ts, rs2_timestamp_domain domain, rs2_stream stream_type,int stream_index, double _p1=0., double _p2=0., double _p3=0., double _p4=0., double _p5=0., double _p6=0., double _p7=0.): _frame_number(frame_number), _ts(frame_ts), _arrival_time(host_ts), _domain(domain), _stream_type(stream_type), _stream_idx(stream_index), _params({_p1,_p2,_p3,_p4,_p5,_p6,_p7}) {}; std::string to_string() const { std::stringstream ss; ss << std::endl << rs2_stream_to_string(_stream_type) << "," << _stream_idx << "," << _frame_number << "," << std::fixed << std::setprecision(3) << _ts << "," << _arrival_time; // IMU and Pose frame hold the sample data in addition to the frame's header attributes size_t specific_attributes = 0; if (val_in_range(_stream_type,{RS2_STREAM_GYRO,RS2_STREAM_ACCEL})) specific_attributes = 3; if (val_in_range(_stream_type,{RS2_STREAM_POSE})) specific_attributes = 7; for (auto i=0; i _params; // |The parameters are optional and sensor specific }; private: std::shared_ptr _dev; std::map, std::vector> data_collection; std::vector requests_to_go, user_requests; std::vector active_sensors; std::vector selected_stream_profiles; uint64_t _max_frames; int64_t _time_out_sec; application_stop _stop_cond; bool parse_configuration(const std::string& line, const std::vector& tokens, rs2_stream& type, int& width, int& height, rs2_format& format, int& fps, int& index); // Assign the user configuration to the selected device bool configure_sensors(); }; }