unit-tests-post-processing.h
Go to the documentation of this file.
1 // License: Apache 2.0. See LICENSE file in root directory.
2 // Copyright(c) 2018 Intel Corporation. All Rights Reserved.
3 
4 // Definitions and utility functions to load and parse prerecorded frames data
5 // utilized in post-processing filters validation
6 // The file is intended to be used by both librealsense and 3rd party tools
7 #include <vector>
8 #include <iostream>
9 #include <fstream>
10 #include <string>
11 #include <map>
12 #include <numeric>
13 
15 {
17  bool spatial_filter = false;
18  float spatial_alpha = 0.f;
21  bool temporal_filter = false;
22  float temporal_alpha = 0.f;
25  bool holes_filter = false;
28  float depth_units = 0.001f;
29  float stereo_baseline_mm = 0.f;
30  float focal_length = 0.f;
35 
37 
38  std::vector<std::string> input_frame_names;
39  std::vector<std::string> output_frame_names;
40 
41  std::vector<std::vector<uint8_t>> _input_frames; // stores the actul pixel values for the frames sequence
42  std::vector<std::vector<uint8_t>> _output_frames;
43 
44  void reset()
45  {
46  name.clear();
47  spatial_filter = temporal_filter = holes_filter = false;
48  spatial_alpha = temporal_alpha = 0;
49  spatial_iterations = temporal_persistence = holes_filling_mode = spatial_delta = temporal_delta = 0;
50  input_res_x = input_res_y = output_res_x = output_res_y = 0;
51  downsample_scale = 1;
52  _input_frames.clear();
53  _output_frames.clear();
54  }
55 };
56 
57 
58 std::vector<uint8_t> load_from_binary(const std::string& str)
59 {
60  std::ifstream file(str.c_str(), std::ios::binary);
61 
62  // Determine the file length
63  file.seekg(0, std::ios_base::end);
64  std::size_t size = file.tellg();
65  file.seekg(0, std::ios_base::beg);
66 
67  // Create a vector to store the data
68  std::vector<uint8_t> v(size);
69 
70  // Load the data
71  file.read((char*)&v[0], size);
72  // Close the file
73  file.close();
74 
75  return v;
76 }
77 
79 {
97 };
98 
99 // The table establishes the mapping of attributes name to be found in the input files
100 // generated by the reference viewer (realsense2ex_test.cpp)
101 const std::map<metadata_attrib, std::string> metadata_attributes = {
102  { res_x, "Resolution_x" },
103  { res_y, "Resolution_y" },
104  { focal_length, "Focal Length" },
105  { depth_units, "Depth Units" },
106  { stereo_baseline,"Stereo Baseline" },
107  { downscale, "Scale" },
108  { spat_filter, "Spatial Filter Params:" },
109  { spat_alpha, "SpatialAlpha" },
110  { spat_delta, "SpatialDelta" },
111  { spat_iter, "SpatialIterations" },
112  { temp_filter, "Temporal Filter Params:" },
113  { temp_alpha, "TemporalAlpha" },
114  { temp_delta, "TemporalDelta" },
115  { temp_persist, "TemporalPersistency" },
116  { holes_filter, "Holes Filling Mode:" },
117  { holes_fill, "HolesFilling" },
118  { frames_sequence_size, "Frames sequence length" }
119 };
120 
121 // Parse frame's metadata files and partially fill the configuration struct
123 {
124  // First, fill all key/value pair into and appropriate container
125  std::map<std::string, std::string> dict;
126 
127  std::ifstream data(str.c_str());
129  int invalid_line = 0;
130 
132  while (true)
133  {
134  if (!std::getline(data, key, ','))
135  invalid_line++;
136 
137  std::getline(data, value, '\n');
138  std::stringstream ss(value); // trim EOL discrepancies
139  ss >> value;
140  dict[key] = value;
141 
142  if (invalid_line > 1) // Two or more non-kvp lines designate eof. Note this when creating the attributes
143  break;
144  }
145 
146  // Then parse the data according to the predefined set of attribute names
148 
149  // Note that the function does not differentiate between input and output frames metadata csv.
150  // The user must assign the parameters appropriately
151  cfg.input_res_x = dict.count(metadata_attributes.at(res_x)) ? std::stoi(dict.at(metadata_attributes.at(res_x))) : 0;
152  cfg.input_res_y = dict.count(metadata_attributes.at(res_y)) ? std::stoi(dict.at(metadata_attributes.at(res_y))) : 0;
154  cfg.depth_units = dict.count(metadata_attributes.at(depth_units)) ? std::stof(dict.at(metadata_attributes.at(depth_units))) : 0.f;
155  cfg.focal_length = dict.count(metadata_attributes.at(focal_length)) ? std::stof(dict.at(metadata_attributes.at(focal_length))) : 0.f;
156 
157  cfg.downsample_scale = dict.count(metadata_attributes.at(downscale)) ? std::stoi(dict.at(metadata_attributes.at(downscale))) : 1;
158  cfg.spatial_filter = dict.count(metadata_attributes.at(spat_filter)) > 0;
159  cfg.spatial_alpha = dict.count(metadata_attributes.at(spat_alpha)) ? std::stof(dict.at(metadata_attributes.at(spat_alpha))) : 0.f;
160  cfg.spatial_delta = dict.count(metadata_attributes.at(spat_delta)) ? std::stoi(dict.at(metadata_attributes.at(spat_delta))) : 0;
162  cfg.temporal_filter = dict.count(metadata_attributes.at(temp_filter)) > 0;
163  cfg.temporal_alpha = dict.count(metadata_attributes.at(temp_alpha)) ? std::stof(dict.at(metadata_attributes.at(temp_alpha))) : 0.f;
164  cfg.temporal_delta = dict.count(metadata_attributes.at(temp_delta)) ? std::stoi(dict.at(metadata_attributes.at(temp_delta))) : 0;
166 
167  cfg.holes_filter = dict.count(metadata_attributes.at(holes_filter)) > 0;
169 
170  // Parse the name of the files sequence
173  // Populate the input_frame_names member with the file names
174  for (size_t i = 0; i < cfg.frames_sequence_size; i++)
175  cfg.input_frame_names.emplace_back(dict.at(std::to_string(i)) + ".raw");
176  return cfg;
177 }
178 
179 inline bool load_test_configuration(const std::string test_name, ppf_test_config& test_config)
180 {
182  std::string base_name = folder_name + test_name;
183  base_name += ".0"; // Frame sequence will always be zero-indexed
184  static const std::map<std::string, std::string> test_file_names = {
185  { "Input_pixels", ".Input.raw" },
186  { "Input_metadata", ".Input.csv" },
187  { "Output_pixels", ".Output.raw"},
188  { "Output_metadata", ".Output.csv"}
189  };
190 
191  std::vector<bool> fe;
192  // Verify that all the required test files are present
193  for (auto& filename : test_file_names)
194  {
195  CAPTURE(base_name);
196  CAPTURE(filename.second);
197  fe.push_back(file_exists(base_name + filename.second));
198  if (!fe.back())
199  {
200  WARN("A required test file is not present: " << base_name + filename.second << " .Test will be skipped");
201  }
202  }
203 
204  if (std::find(fe.begin(), fe.end(), false) != fe.end())
205  return false;
206 
207  // Prepare the configuration data set
208  test_config.reset();
209 
210  test_config.name = test_name;
211  ppf_test_config input_meta_params = attrib_from_csv(base_name + test_file_names.at("Input_metadata"));
212  ppf_test_config output_meta_params = attrib_from_csv(base_name + test_file_names.at("Output_metadata"));
213  test_config.frames_sequence_size = input_meta_params.frames_sequence_size;
214 
215  if (test_config.frames_sequence_size > 50)
216  {
217  WARN("The input sequence is too long - " << test_config.frames_sequence_size << " frames. Performance may be affected");
218  }
219  test_config.input_frame_names = input_meta_params.input_frame_names;
220  test_config.output_frame_names = output_meta_params.input_frame_names;
221  // Prefetch all frames data. Assumes that the frames sequences less than a hundred frames
222  for (size_t i = 0; i < test_config.frames_sequence_size; i++)
223  {
224  test_config._input_frames.push_back(std::move(load_from_binary(folder_name + test_config.input_frame_names[i])));
225  test_config._output_frames.push_back(std::move(load_from_binary(folder_name + test_config.output_frame_names[i])));
226  }
227 
228  test_config.input_res_x = input_meta_params.input_res_x;
229  test_config.input_res_y = input_meta_params.input_res_y;
230  test_config.output_res_x = output_meta_params.input_res_x;
231  test_config.output_res_y = output_meta_params.input_res_y;
232  test_config.depth_units = input_meta_params.depth_units;
233  test_config.focal_length = input_meta_params.focal_length;
234  test_config.stereo_baseline_mm = input_meta_params.stereo_baseline_mm;
235  test_config.downsample_scale = output_meta_params.downsample_scale;
236  test_config.spatial_filter = output_meta_params.spatial_filter;
237  test_config.spatial_alpha = output_meta_params.spatial_alpha;
238  test_config.spatial_delta = output_meta_params.spatial_delta;
239  test_config.spatial_iterations = output_meta_params.spatial_iterations;
240  test_config.holes_filter = output_meta_params.holes_filter;
241  test_config.holes_filling_mode = output_meta_params.holes_filling_mode;
242  test_config.temporal_filter = output_meta_params.temporal_filter;
243  test_config.temporal_alpha = output_meta_params.temporal_alpha;
244  test_config.temporal_delta = output_meta_params.temporal_delta;
245  test_config.temporal_persistence = output_meta_params.temporal_persistence;
246 
247  // Perform sanity tests on the input data
248  CAPTURE(test_config.name);
249  CAPTURE(test_config.input_res_x);
250  CAPTURE(test_config.input_res_y);
251 
252  for (auto i = 0; i < test_config.frames_sequence_size; i++)
253  {
254  CAPTURE(test_config._input_frames[i].size());
255  CAPTURE(test_config._output_frames[i].size());
256  }
257 
258  CAPTURE(test_config.output_res_x);
259  CAPTURE(test_config.output_res_y);
260  CAPTURE(test_config.spatial_filter);
261  CAPTURE(test_config.spatial_alpha);
262  CAPTURE(test_config.spatial_delta);
263  CAPTURE(test_config.spatial_iterations);
264  CAPTURE(test_config.temporal_filter);
265  CAPTURE(test_config.temporal_alpha);
266  CAPTURE(test_config.temporal_delta);
267  CAPTURE(test_config.temporal_persistence);
268  CAPTURE(test_config.holes_filter);
269  CAPTURE(test_config.holes_filling_mode);
270  CAPTURE(test_config.downsample_scale);
271 
272  // Input data sanity
273  // The resulted frame dimension will be dividible by 4;
274  auto _padded_width = uint16_t(test_config.input_res_x / test_config.downsample_scale) + 3;
275  _padded_width /= 4;
276  _padded_width *= 4;
277 
278  auto _padded_height = uint16_t(test_config.input_res_y / test_config.downsample_scale) + 3;
279  _padded_height /= 4;
280  _padded_height *= 4;
281  REQUIRE(test_config.output_res_x == _padded_width);
282  REQUIRE(test_config.output_res_y == _padded_height);
283  REQUIRE(test_config.input_res_x > 0);
284  REQUIRE(test_config.input_res_y > 0);
285  REQUIRE(test_config.output_res_x > 0);
286  REQUIRE(test_config.output_res_y > 0);
287  REQUIRE(std::fabs(test_config.stereo_baseline_mm) > 0.f);
288  REQUIRE(test_config.depth_units > 0);
289  REQUIRE(test_config.focal_length > 0);
290  REQUIRE(test_config.frames_sequence_size > 0);
291  for (auto i = 0; i < test_config.frames_sequence_size; i++)
292  {
293  REQUIRE((test_config.input_res_x * test_config.input_res_y * 2) == test_config._input_frames[i].size()); // Assuming uint16_t type
294  REQUIRE((test_config.output_res_x * test_config.output_res_y * 2) == test_config._output_frames[i].size());
295  }
296 
297  // The following tests use assumption about the filters intrinsic
298  // Note that the specific parameter threshold are verified as of April 2018.
299  if (test_config.spatial_filter)
300  {
301  REQUIRE(test_config.spatial_alpha >= 0.25f);
302  REQUIRE(test_config.spatial_alpha <= 1.f);
303  REQUIRE(test_config.spatial_delta >= 1);
304  REQUIRE(test_config.spatial_delta <= 50);
305  REQUIRE(test_config.spatial_iterations >= 1);
306  REQUIRE(test_config.spatial_iterations <= 5);
307  }
308  if (test_config.temporal_filter)
309  {
310  REQUIRE(test_config.temporal_alpha >= 0.f);
311  REQUIRE(test_config.temporal_alpha <= 1.f);
312  REQUIRE(test_config.temporal_delta >= 1);
313  REQUIRE(test_config.temporal_delta <= 100);
314  REQUIRE(test_config.temporal_persistence >= 0);
315  // Reference code treats values [1-8] as valid persistence modes.
316  // Zero or val>8 mean no persistance
317  REQUIRE(test_config.temporal_persistence <= 8);
318  }
319 
320  if (test_config.holes_filter)
321  {
322  REQUIRE(test_config.holes_filling_mode >= 0);
323  REQUIRE(test_config.holes_filling_mode <= 2);
324  }
325 
326  return true;
327 }
328 
329 template <typename T>
330 inline bool profile_diffs(const std::string& plot_name, std::vector<T>& distances, const float max_allowed_std, const float outlier, size_t frame_idx)
331 {
332  static_assert((std::is_arithmetic<T>::value), "Profiling is defined for built-in arithmetic types");
333 
334  std::ofstream output_file(plot_name);
335  for (const auto &val : distances) output_file << val << "\n";
336  output_file.close();
337 
338  REQUIRE(!distances.empty());
339 
340  float mean = std::accumulate(distances.begin(),
341  distances.end(), 0.0f) / distances.size();
342 
343  auto pixels = distances.size();
344  long long first_non_identical_index = -1;
345  auto first_difference = 0.f;
346  auto non_identical_count = distances.size() - std::count(distances.begin(), distances.end(), static_cast<T>(0));
347  auto first_non_identical_iter = std::find_if(distances.begin(), distances.end(), [](const float& val) { return val != 0; });
348  if (first_non_identical_iter != distances.end())
349  {
350  first_non_identical_index = first_non_identical_iter - distances.begin();
351  first_difference = *first_non_identical_iter;
352  }
353  auto max = std::max_element(distances.begin(), distances.end());
354  auto max_val_index = max - distances.begin();
355  auto max_val = (max != distances.end()) ? *max : 0;
356  float e = 0;
357  float inverse = 1.f / distances.size();
358  for (auto elem : distances)
359  {
360  e += pow(elem - mean, 2);
361  }
362 
363  float standard_deviation = static_cast<float>(sqrt(inverse * e));
364 
365  if (max_val != 0)
366  WARN("Frame" << frame_idx
367  << ": Non-identical pixels = " << non_identical_count
368  << " \nFirst non-identical diff = " << first_difference << " at index " << first_non_identical_index
369  << "\nmax_diff=" << max_val << ", index=" << max_val_index);
370 
371  CAPTURE(pixels);
372  CAPTURE(mean);
373  CAPTURE(max_val);
374  CAPTURE(max_val_index);
375  CAPTURE(non_identical_count);
376  CAPTURE(first_non_identical_index);
377  CAPTURE(first_difference);
378  CAPTURE(outlier);
379  CAPTURE(standard_deviation);
380  CAPTURE(max_allowed_std);
381  CAPTURE(frame_idx);
382 
383  CHECK( standard_deviation <= max_allowed_std );
384  CHECK( fabs(max_val) <= outlier );
385 
386  return ((fabs(max_val) <= outlier) && (standard_deviation <= max_allowed_std));
387 }
float stof(const std::string &value)
GLuint GLuint end
GLuint const GLchar * name
GLfloat value
bool load_test_configuration(const std::string test_name, ppf_test_config &test_config)
std::string get_folder_path(special_folder f)
Definition: os.cpp:247
unsigned short uint16_t
Definition: stdint.h:79
GLsizei const GLchar *const * string
unsigned char uint8_t
Definition: stdint.h:78
e
Definition: rmse.py:177
std::vector< std::vector< uint8_t > > _output_frames
ppf_test_config attrib_from_csv(const std::string &str)
GLuint GLfloat * val
GLuint64 key
Definition: glext.h:8966
std::vector< std::string > output_frame_names
GLsizeiptr size
#define WARN(msg)
Definition: catch.hpp:17431
#define CAPTURE(...)
Definition: catch.hpp:17432
REQUIRE(n_callbacks==1)
unsigned int uint32_t
Definition: stdint.h:80
def find(dir, mask)
Definition: file.py:25
GLint GLint GLsizei GLint GLenum GLenum const void * pixels
std::vector< std::string > input_frame_names
const GLuint GLenum const void * binary
Definition: glext.h:1882
GLint GLsizei count
bool profile_diffs(const std::string &plot_name, std::vector< T > &distances, const float max_allowed_std, const float outlier, size_t frame_idx)
typename::boost::move_detail::remove_reference< T >::type && move(T &&t) BOOST_NOEXCEPT
metadata_attrib
int stoi(const std::string &value)
const std::map< metadata_attrib, std::string > metadata_attributes
int i
std::vector< uint8_t > load_from_binary(const std::string &str)
bool file_exists(const std::string &filename)
GLboolean * data
GLdouble v
#define CHECK(condition)
Definition: parser.hpp:150
std::vector< std::vector< uint8_t > > _input_frames
std::string to_string(T value)


librealsense2
Author(s): Sergey Dorodnicov , Doron Hirshberg , Mark Horn , Reagan Lopez , Itay Carpis
autogenerated on Mon May 3 2021 02:50:13