licornea_tools
image_correspondence.cc
Go to the documentation of this file.
1 #include "image_correspondence.h"
2 #include "../../lib/string.h"
3 #include "../../lib/assert.h"
4 #include <fstream>
5 #include <iostream>
6 #include <vector>
7 #include <set>
8 
9 namespace tlz {
10 
11 namespace {
12  int binary_cors_magic_ = 0x2D1111C0;
13 }
14 
17 
18  if(has(j_feat, "reference_view"))
19  feat.reference_view = decode_view_index(j_feat["reference_view"]);
20 
21  const json& j_pts = j_feat["points"];
22  for(auto it = j_pts.begin(); it != j_pts.end(); ++it) {
23  view_index idx = decode_view_index(it.key());
24  feat.points[idx] = decode_feature_point(it.value());
25  }
26 
27  return feat;
28 }
29 
30 
32  json j_feat = json::object();
33  if(feat.reference_view)
34  j_feat["reference_view"] = encode_view_index(feat.reference_view);
35 
36  json j_points = json::object();
37  for(const auto& kv : feat.points) {
38  const view_index& idx = kv.first;
39  const feature_point& pt = kv.second;
40  j_points[encode_view_index(idx)] = encode_feature_point(pt);
41  }
42  j_feat["points"] = j_points;
43  return j_feat;
44 }
45 
46 
49  if(has(j_cors, "dataset_group")) cors.dataset_group = j_cors["dataset_group"];
50  const json& j_features = j_cors["features"];
51  for(auto it = j_features.begin(); it != j_features.end(); ++it) {
52  std::string feature_name = it.key();
53  const json& j_feature = it.value();
54  cors.features[feature_name] = decode_image_correspondence_feature(j_feature);
55  }
56  return cors;
57 }
58 
59 
61  json j_cors = json::object();
62  if(! cors.dataset_group.empty()) j_cors["dataset_group"] = cors.dataset_group;
63  json j_features = json::object();
64  for(const auto& kv : cors.features) {
65  const std::string& feature_name = kv.first;
66  const image_correspondence_feature& feature = kv.second;
67  j_features[feature_name] = encode_image_correspondence_feature(feature);
68  }
69  j_cors["features"] = j_features;
70  return j_cors;
71 }
72 
73 
74 void export_binary_image_correspondences(const image_correspondences& cors, const std::string& filename) {
75  std::ofstream str(filename, std::ios_base::binary);
76 
77  auto write = [&str](const auto& val) {
78  const auto* ptr = reinterpret_cast<const std::ostream::char_type*>(&val);
79  str.write(ptr, sizeof(val));
80  };
81  auto write_buffer = [&str](const void* buf, std::size_t sz) {
82  str.write(static_cast<const std::ostream::char_type*>(buf), sz);
83  };
84 
85  auto uint8 = [&write](std::uint8_t val) { write(val); };
86  auto int32 = [&write](std::int32_t val) { write(val); };
87  auto float64 = [&write](double val) { write(val); };
88 
89  auto short_string = [&](const std::string& str) {
90  Assert(str.length() <= 255);
91  uint8(str.length());
92  write_buffer(str.data(), str.length());
93  };
94  auto view_idx = [&](const view_index& idx) {
95  int32(idx.x);
96  int32(idx.y);
97  };
98 
99  int32(binary_cors_magic_);
100  short_string(cors.dataset_group);
101  int32(cors.features.size());
102  for(const auto& kv : cors.features) {
103  const std::string& feature_name = kv.first;
104  const image_correspondence_feature& feature = kv.second;
105  short_string(feature_name);
106  view_idx(feature.reference_view);
107  int32(feature.points.size());
108  for(const auto& kv2 : feature.points) {
109  const view_index& idx = kv2.first;
110  const feature_point& fpoint = kv2.second;
111  view_idx(idx);
112  float64(fpoint.position[0]);
113  float64(fpoint.position[1]);
114  float64(fpoint.depth);
115  float64(fpoint.weight);
116  }
117  }
118 }
119 
120 
122  std::ifstream str(filename, std::ios_base::binary);
123 
124  auto read = [&str](auto& val) {
125  auto* ptr = reinterpret_cast<std::ostream::char_type*>(&val);
126  str.read(ptr, sizeof(val));
127  };
128  auto read_buffer = [&str](void* buf, std::size_t sz) {
129  str.read(static_cast<std::ostream::char_type*>(buf), sz);
130  };
131 
132  auto uint8 = [&read]() { std::uint8_t val; read(val); return val; };
133  auto int32 = [&read]() { std::int32_t val; read(val); return val; };
134  auto float64 = [&read]() { double val; read(val); return val; };
135 
136  auto short_string = [&]() {
137  char string[255];
138  std::size_t sz = uint8();
139  read_buffer(&string, sz);
140  return std::string(string, sz);
141  };
142  auto view_idx = [&]() {
143  int x = int32();
144  int y = int32();
145  return view_index(x, y);
146  };
147 
149  int magic = int32();
150  if(magic != binary_cors_magic_) throw std::runtime_error("binary cors file does not have magic number");
151  cors.dataset_group = short_string();
152  std::size_t features_count = int32();
153  for(int i = 0; i < features_count; ++i) {
154  std::string feature_name = short_string();
155  image_correspondence_feature& feature = cors.features[feature_name];
156  feature.reference_view = view_idx();
157 
158  std::size_t points_count = int32();
159  for(int j = 0; j < points_count; ++j) {
160  view_index idx = view_idx();
161  feature_point& fpoint = feature.points[idx];
162  fpoint.position[0] = float64();
163  fpoint.position[1] = float64();
164  fpoint.depth = float64();
165  fpoint.weight = float64();
166  }
167  }
168 
169  return cors;
170 }
171 
172 
173 void export_image_corresponcences(const image_correspondences& cors, const std::string& filename) {
174  if(file_name_extension(filename) == "json") {
175  std::cout << "exporting image correspondences to JSON" << std::endl;
177  } else if(file_name_extension(filename) == "bin") {
178  std::cout << "exporting image correspondences to binary" << std::endl;
179  export_binary_image_correspondences(cors, filename);
180  } else {
181  throw std::runtime_error("unknown filename extension for image correspondences (need .json or .bin)");
182  }
183 }
184 
186  if(file_name_extension(filename) == "json") return decode_image_correspondences(import_json_file(filename));
187  else if(file_name_extension(filename) == "bin") return import_binary_image_correspondences(filename);
188  else throw std::runtime_error("unknown filename extension for image correspondences (need .json or .bin)");
189 }
190 
191 
192 std::set<view_index> get_reference_views_set(const image_correspondences& cors) {
193  std::set<view_index> reference_views_set;
194  for(const auto& kv : cors.features)
195  reference_views_set.insert(kv.second.reference_view);
196  return reference_views_set;
197 }
198 
199 
200 std::vector<view_index> get_reference_views(const image_correspondences& cors) {
201  std::set<view_index> reference_views_set = get_reference_views_set(cors);
202  return std::vector<view_index>(reference_views_set.begin(), reference_views_set.end());
203 }
204 
205 
206 std::set<view_index> get_all_views_set(const image_correspondences& cors) {
207  std::set<view_index> views_set;
208  for(const auto& kv : cors.features)
209  for(const auto& kv2 : kv.second.points)
210  views_set.insert(kv2.first); // std::set: each view_index gets inserted only once
211  return views_set;
212 }
213 
214 
215 std::vector<view_index> get_all_views(const image_correspondences& cors) {
216  auto views_set = get_all_views_set(cors);
217  return std::vector<view_index>(views_set.begin(), views_set.end());
218 }
219 
220 
221 std::set<std::string> get_feature_names_set(const image_correspondences& cors) {
222  auto feature_names = get_feature_names(cors);
223  return std::set<std::string>(feature_names.begin(), feature_names.end());
224 }
225 
226 
227 std::vector<std::string> get_feature_names(const image_correspondences& cors) {
228  std::vector<std::string> feature_names;
229  for(const auto& kv : cors.features)
230  feature_names.push_back(kv.first);
231  return feature_names;
232 }
233 
234 
235 std::string short_feature_name(const std::string& full_feature_name) {
236  auto pos = full_feature_name.find_first_of('#');
237  if(pos == std::string::npos) return full_feature_name;
238  else return full_feature_name.substr(0, pos);
239 }
240 
241 
243  image_correspondences out_cors;
244  out_cors.dataset_group = cors.dataset_group;
245  for(const auto& kv : cors.features) {
246  const image_correspondence_feature& feature = kv.second;
247  if(feature.reference_view == reference_view)
248  out_cors.features[kv.first] = feature;
249  }
250  return out_cors;
251 }
252 
253 
255  if(intr.distortion.is_none()) return cors;
256 
257  std::vector<vec2> dist_points;
258  std::vector<vec2*> undist_points_ptrs;
259 
260  image_correspondences out_cors = cors;
261 
262  for(const auto& kv : cors.features) {
263  const std::string& feature_name = kv.first;
264  const image_correspondence_feature& feature = kv.second;
265  image_correspondence_feature& out_feature = out_cors.features.at(feature_name);
266  for(const auto& kv2 : feature.points) {
267  const view_index& idx = kv2.first;
268  const feature_point& pt = kv2.second;
269  feature_point& out_pt = out_feature.points.at(idx);
270  dist_points.push_back(pt.position);
271  undist_points_ptrs.push_back(&out_pt.position);
272  }
273  }
274 
275  std::vector<vec2> undist_points = undistort_points(intr, dist_points);
276 
277  for(std::ptrdiff_t i = 0; i < undist_points.size(); ++i)
278  *undist_points_ptrs[i] = undist_points[i];
279 
280  return out_cors;
281 }
282 
283 
284 cv::Mat_<cv::Vec3b> visualize_view_points(const image_correspondence_feature& feature, const cv::Mat_<cv::Vec3b>& back_img, const cv::Vec3b& col, int dot_radius, const border& bord) {
285  cv::Mat_<cv::Vec3b> img;
286  back_img.copyTo(img);
287 
288  bool is_2d = feature.points.begin()->first.is_2d();
289  if(! is_2d) {
290  // draw circle
291  vec2 center_point = feature.points.at(feature.reference_view).position;
292  cv::Point2f center_point_cv(bord.left + center_point[0], bord.top + center_point[1]);
293  cv_aa_circle(img, center_point_cv, 10, cv::Scalar(col), 2);
294 
295  // draw connecting line
296  std::vector<cv::Point> trail_points;
297  for(const auto& kv : feature.points) {
298  vec2 pt = kv.second.position;
299  pt[0] += bord.left; pt[1] += bord.top;
300  trail_points.emplace_back(pt[0], pt[1]);
301  }
302 
303  std::vector<std::vector<cv::Point>> polylines = { trail_points };
304  cv::polylines(img, polylines, false, cv::Scalar(col), 2);
305 
306  } else {
307  // draw dot for each point
308  for(const auto& kv : feature.points) {
309  vec2 pt = kv.second.position;
310  pt[0] += bord.left; pt[1] += bord.top;
311 
312  cv::Point2f pt_cv(pt[0], pt[1]);
313 
314  if(dot_radius <= 1) {
315  if(cv::Rect(cv::Point(), img.size()).contains(pt_cv)) img(pt_cv) = col;
316  } else {
317  cv_aa_circle(img, pt_cv, dot_radius, cv::Scalar(col), -1);
318  }
319  }
320  }
321 
322  return img;
323 }
324 
325 
326 cv::Mat_<cv::Vec3b> visualize_view_points_closeup(const image_correspondence_feature& feature, const cv::Mat_<cv::Vec3b>& img, const cv::Vec3b& col, const view_index& ref_idx, real dots_opacity, const border& bord) {
327  real y_min = +INFINITY, y_max = -INFINITY, x_min = +INFINITY, x_max = -INFINITY;
328  for(const auto& kv : feature.points) {
329  vec2 pos = kv.second.position;
330  if(pos[0] > x_max) x_max = pos[0];
331  if(pos[0] < x_min) x_min = pos[0];
332  if(pos[1] > y_max) y_max = pos[1];
333  if(pos[1] < y_min) y_min = pos[1];
334  }
335  real scale = std::min({ img.cols/(x_max-x_min), img.rows/(y_max-y_min) });
336 
337 
338  cv::Rect roi(x_min+bord.left, y_min+bord.top, x_max-x_min, y_max-y_min);
339 
340  cv::Mat_<cv::Vec3b> out_img;
341  cv::resize(cv::Mat(img, roi), out_img, cv::Size(0,0), scale, scale, cv::INTER_CUBIC);
342 
343  cv::Mat_<cv::Vec3b> out_img_with_dots;
344  out_img.copyTo(out_img_with_dots);
345 
346  for(const auto& kv : feature.points) {
347  const view_index& idx = kv.first;
348  cv::Point2f pt_cv = vec2_to_point2f(kv.second.position);
349  pt_cv.x -= x_min;
350  pt_cv.y -= y_min;
351  pt_cv.x *= scale;
352  pt_cv.y *= scale;
353 
354  if(ref_idx && idx == ref_idx) {
355  cv_aa_circle(out_img_with_dots, pt_cv, 5, cv::Scalar(cv::Vec3b(0,0,255)), -1);
356  cv_aa_circle(out_img, pt_cv, 5, cv::Scalar(cv::Vec3b(0,0,255)), -1);
357  }
358 
359  cv_aa_circle(out_img_with_dots, pt_cv, 2, cv::Scalar(col), -1);
360  }
361 
362  cv::addWeighted(out_img_with_dots, dots_opacity, out_img, 1.0-dots_opacity, 0.0, out_img);
363 
364  return out_img;
365 }
366 
367 
369  std::cout << "loading image correspondences" << std::endl;
371 }
372 
373 
374 
375 }
cv::Mat_< cv::Vec3b > visualize_view_points(const image_correspondence_feature &feature, const cv::Mat_< cv::Vec3b > &back_img, const cv::Vec3b &col, int dot_radius, const border &bord)
bool has(const json &j, const std::string &key)
Definition: json.h:24
std::string short_feature_name(const std::string &full_feature_name)
std::set< std::string > get_feature_names_set(const image_correspondences &cors)
std::set< view_index > get_all_views_set(const image_correspondences &cors)
cv::Mat_< cv::Vec3b > visualize_view_points_closeup(const image_correspondence_feature &feature, const cv::Mat_< cv::Vec3b > &img, const cv::Vec3b &col, const view_index &ref_idx, real dots_opacity, const border &bord)
std::string encode_view_index(view_index idx)
Definition: dataset.cc:275
std::vector< view_index > get_all_views(const image_correspondences &cors)
json encode_image_correspondence_feature(const image_correspondence_feature &feat)
std::string in_filename_arg()
Definition: args.cc:98
image_correspondences import_image_correspondences(const std::string &filename)
image_correspondences image_correspondences_with_reference(const image_correspondences &cors, const view_index &reference_view)
Set of features, each on set of views.
feature_point decode_feature_point(const json &j_pt)
Definition: feature_point.cc:5
int left
Definition: border.h:10
cv::Vec< real, 2 > vec2
Definition: common.h:22
image_correspondence_feature decode_image_correspondence_feature(const json &j_feat)
std::map< std::string, image_correspondence_feature > features
distortion_parameters distortion
Definition: intrinsics.h:30
json encode_feature_point(const feature_point &pt)
std::vector< view_index > get_reference_views(const relative_camera_positions &rcpos)
std::vector< std::string > get_feature_names(const image_correspondences &cors)
double real
Definition: common.h:16
image_correspondences decode_image_correspondences(const json &j_cors)
void export_json_file(const json &j, const std::string &filename, bool compact)
Definition: json.cc:9
void export_binary_image_correspondences(const image_correspondences &cors, const std::string &filename)
image_correspondences image_correspondences_arg()
int top
Definition: border.h:9
feature_points undistort(const feature_points &dist_fpoints, const intrinsics &intr)
std::set< view_index > get_reference_views_set(const image_correspondences &cors)
#define Assert
Definition: assert.h:40
Feature on set of views. Optionally one view is "reference".
std::vector< vec2 > undistort_points(const intrinsics &intr, const std::vector< vec2 > &distorted)
Definition: intrinsics.cc:45
view_index decode_view_index(const std::string &key)
Definition: dataset.cc:281
std::string file_name_extension(const std::string &filename)
Definition: string.cc:9
void export_image_corresponcences(const image_correspondences &cors, const std::string &filename)
image_correspondences import_binary_image_correspondences(const std::string &filename)
nlohmann::json json
Definition: json.h:11
json encode_image_correspondences(const image_correspondences &cors)
json import_json_file(const std::string &filename)
Definition: json.cc:24
std::map< view_index, feature_point > points
void cv_aa_circle(cv::Mat &mat, const cv::Point2f &center, float rad, cv::Scalar col, int thickness)
Definition: opencv.cc:5