main.cpp
Go to the documentation of this file.
1 #include <GL/gl3w.h>
2 #include <GLFW/glfw3.h>
3 
4 #include <iostream>
5 #include <atomic>
6 #include <chrono>
7 
8 #include <unistd.h>
9 
10 #include "quori_face/Window.hpp"
12 #include "quori_face/Shader.hpp"
13 #include "quori_face/Program.hpp"
14 #include "quori_face/Texture.hpp"
16 #include "quori_face/transform.hpp"
17 #include "quori_face/gl.hpp"
18 
19 #include "geometry.hpp"
20 #include "shader/frag.hpp"
21 #include "shader/vertex.hpp"
22 
23 #include <opencv2/imgcodecs.hpp>
24 #include <opencv2/imgproc.hpp>
25 #include <opencv2/photo.hpp>
26 
29 #include <cv_bridge/cv_bridge.h>
30 
31 #include <unordered_map>
32 #include <ros/ros.h>
33 #include <ros/package.h>
34 
35 #include <boost/optional.hpp>
36 #include <fstream>
37 
38 #include <dynamic_reconfigure/server.h>
39 #include <quori_face/CalibrationConfig.h>
40 
41 #include <yaml-cpp/yaml.h>
42 
43 #include "trace.hpp"
44 #include "param.hpp"
45 
46 using namespace quori_face;
47 using namespace quori_face_node;
48 
49 namespace
50 {
51  sensor_msgs::ImageConstPtr latest_image;
52 
53  void imageCallback(const sensor_msgs::ImageConstPtr &msg)
54  {
55  latest_image = msg;
56  }
57 
58  std::atomic<bool> static_params_updated(false);
60 
62 
63  SphericalCoordinate min_coord = {
64  .theta = -M_PI * 0.45,
65  .psi = -M_PI * 0.5
66  };
67 
68  SphericalCoordinate max_coord = {
69  .theta = M_PI * 0.45,
70  .psi = M_PI * 0.5
71  };
72 
73  void reconfigureCallback(CalibrationConfig &calibration, uint32_t level)
74  {
75  static bool debounce = true;
76  if (debounce)
77  {
78  debounce = false;
79  return;
80  }
81 
82  static_params.delta.x = calibration.dx;
83  static_params.delta.y = calibration.dy;
84 
85  center.theta = calibration.center_theta;
86  center.psi = calibration.center_psi;
87 
88  min_coord.theta = center.theta + calibration.min_theta;
89  min_coord.psi = center.psi + calibration.min_psi;
90 
91  max_coord.theta = center.theta + calibration.max_theta;
92  max_coord.psi = center.psi + calibration.max_psi;
93 
94  static_params_updated = true;
95  }
96 
97  const Vector2<std::uint32_t> LOOKUP_TABLE_RESOLUTION = {
98  // Theta resolution
99  .x = 2048,
100  // Psi resolution
101  .y = 2048
102  };
103 
104  const Vector2<std::uint32_t> IMAGE_RESOLUTION = {
105  .x = 1280,
106  .y = 720
107  };
108 
109  const std::unordered_map<std::string, std::uint32_t> ENCODING_GL_MAPPINGS {
112  };
113 
114  const std::unordered_map<std::string, std::uint32_t> ENCODING_CV_MAPPINGS {
117  };
118 
119  const std::unordered_map<std::string, boost::optional<cv::ColorConversionCodes>> ENCODING_CV_CVT_MAPPINGS {
120  { sensor_msgs::image_encodings::RGB8, cv::COLOR_RGB2BGR },
121  { sensor_msgs::image_encodings::BGR8, boost::none }
122  };
123 
124  std::size_t encodingSize(const std::string &encoding)
125  {
126  const std::size_t bitDepth = sensor_msgs::image_encodings::bitDepth(latest_image->encoding);
127  const std::size_t numChannels = sensor_msgs::image_encodings::numChannels(latest_image->encoding);
128  return bitDepth / 8 * numChannels;
129  }
130 
131  void mapImage(std::uint8_t *const data, const cv::Mat &image)
132  {
133  const std::size_t elementSize = image.elemSize();
134  for (std::size_t row = 0; row < image.rows; ++row)
135  {
136  memcpy(data + image.cols * row * elementSize, image.ptr(row), image.cols * elementSize);
137  }
138  }
139 }
140 
141 
142 
143 int main(int argc, char *argv[])
144 {
145  using namespace std;
146  using namespace cv;
147 
148  ros::init(argc, argv, "quori_face");
149 
150  ros::NodeHandle nh;
151  ros::NodeHandle pnh("~");
152 
153 
154 
155  boost::recursive_mutex reconfigure_mutex;
156  dynamic_reconfigure::Server<quori_face::CalibrationConfig> reconfigure_server(reconfigure_mutex);
157  reconfigure_server.setCallback(boost::bind(&reconfigureCallback, _1, _2));
158 
159  const auto lookup_table_resolution = param(pnh, "lookup_table_resolution", LOOKUP_TABLE_RESOLUTION);
160  const auto image_resolution = param(pnh, "image_resolution", IMAGE_RESOLUTION);
161  center = param(pnh, "center", center);
162 
163  min_coord = center + param(pnh, "min", min_coord);
164  max_coord = center + param(pnh, "max", max_coord);
165 
166  const bool update_params = param(pnh, "update_params", false);
167 
168  const std::string override_image_encoding = param(pnh, "override_image_encoding", std::string());
169 
170  static_params = param(pnh, "transform", static_params);
171 
172 
173 
174  CalibrationConfig config;
175  config.dx = static_params.delta.x;
176  config.dy = static_params.delta.y;
177  config.center_theta = center.theta;
178  config.center_psi = center.psi;
179  config.min_theta = min_coord.theta - center.theta;
180  config.min_psi = min_coord.psi - center.psi;
181  config.max_theta = max_coord.theta - center.theta;
182  config.max_psi = max_coord.psi - center.psi;
183  reconfigure_server.updateConfig(config);
184 
185  const auto generate_lookup_table = [&]() {
186  const auto start_time = std::chrono::system_clock::now();
187  cout << "Updating lookup table (this may take several seconds)...";
188  cout.flush();
189 
190  GLfloat *const ret = generateLookupTable(static_params, min_coord, max_coord, lookup_table_resolution);
191  const auto end_time = std::chrono::system_clock::now();
192  cout << " done (took " << std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count() << "ms)" << endl;
193  return ret;
194  };
195  GLfloat *lookup_table = generate_lookup_table();
196 
197 
199  const auto image_sub = it.subscribe("image", 1, &imageCallback);
200  const auto window = Window::open(static_params.screen_size.x, static_params.screen_size.y, "Quori Face", Monitor::getPrimaryMonitor());
202 
203  window->bind();
204 
205  WindowManager::ref().attach(window);
206  const auto program = Program::link({
209  });
210 
211  // Generate geometry for displaying the transformed image
212 
213  GLuint vao;
215 
216  GLuint vbo;
218 
219  GLuint ebo;
221 
223 
226 
229 
232 
233  QUORI_FACE_NODE_TRACE(glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (void *)(3 * sizeof(GLfloat))));
235 
236  // Create textures
237 
238  size_t image_size = image_resolution.x * image_resolution.y * 3;
239  uint8_t *const image_texture_data = new uint8_t[image_size];
240  memset(image_texture_data, 0, image_size);
241  Texture::Ptr image_texture = Texture::create(image_resolution.y, image_resolution.x, GL_BGR, image_texture_data);
242  delete[] image_texture_data;
243  const auto lookup_table_texture = Texture::create(static_params.screen_size.y, static_params.screen_size.x, lookup_table);
244 
245  program->use();
246  QUORI_FACE_NODE_TRACE(glUniform1i(program->getUniformLocation("lookup_table"), 0));
247  QUORI_FACE_NODE_TRACE(glUniform1i(program->getUniformLocation("image"), 1));
248  GLint i_resolution[] = {
249  static_cast<GLint>(static_params.screen_size.x),
250  static_cast<GLint>(static_params.screen_size.y),
251  0
252  };
253 
254  QUORI_FACE_NODE_TRACE(glUniform3iv(program->getUniformLocation("i_resolution"), 3, i_resolution));
258 
259  std::size_t image_pbo_index = 0;
260  std::size_t lookup_table_pbo_index = 0;
261  PixelBufferObject image_pbo(image_resolution.x * image_resolution.y * sizeof(uint8_t) * 3);
262  PixelBufferObject lookup_table_pbo(lookup_table_resolution.x * lookup_table_resolution.y * sizeof(float) * 3);
263 
264  const auto copy_current_image_pbo = [&]()
265  {
266  image_texture->bind();
267  image_pbo.bind(image_pbo_index);
268  QUORI_FACE_NODE_TRACE(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image_resolution.x, image_resolution.y, GL_BGR, GL_UNSIGNED_BYTE, 0));
269  checkGlError();
270  image_pbo.unbind();
271  };
272 
273  const auto write_next_image_pbo = [&](const cv::Mat &image)
274  {
275  image_pbo.bind(image_pbo_index + 1);
276  uint8_t *const data = reinterpret_cast<uint8_t *>(QUORI_FACE_NODE_TRACE(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE)));
277  checkGlError();
278  if (data)
279  {
280  mapImage(data, image);
282  checkGlError();
283  }
284 
285  image_pbo.unbind();
286  };
287 
288  sensor_msgs::ImageConstPtr prev_image;
289 
290  while (!window->isClosed() && ros::ok())
291  {
292  ros::spinOnce();
293 
294  if (!latest_image) continue;
295  if (latest_image == prev_image) continue;
296 
297  const auto &encoding = override_image_encoding.empty() ? latest_image->encoding : override_image_encoding;
298 
299  const auto cv_it = ENCODING_CV_MAPPINGS.find(encoding);
300  if (cv_it == ENCODING_CV_MAPPINGS.cend())
301  {
302  cerr << "Unsupported image encoding (no CV encoding) \"" << encoding << "\"" << endl;
303  continue;
304  }
305 
306 
307  const cv::Mat image(
308  cv::Size(latest_image->width, latest_image->height),
309  cv_it->second,
310  // We pinky promise not to modify the constant image data
311  const_cast<uint8_t *>(latest_image->data.data()),
312  latest_image->step
313  );
314 
315  const auto cvt_it = ENCODING_CV_CVT_MAPPINGS.find(encoding);
316  if (cvt_it == ENCODING_CV_CVT_MAPPINGS.cend())
317  {
318  cerr << "Unsupported image encoding (no conversion) \"" << encoding << "\"" << endl;
319  continue;
320  }
321 
322  cv::Mat final_image;
323  if (!cvt_it->second)
324  {
325  final_image = image;
326  }
327  else
328  {
329  cv::cvtColor(image, final_image, cvt_it->second.get());
330  }
331 
332  if (final_image.rows != image_resolution.y || final_image.cols != image_resolution.x)
333  {
334  cv::Mat resized;
335  cv::resize(final_image, resized, cv::Size(image_resolution.x, image_resolution.y));
336  final_image = resized;
337  }
338 
339  prev_image = latest_image;
340 
341  if (static_params_updated)
342  {
343  static_params_updated = false;
344  GLfloat *const next_lookup_table = generate_lookup_table();
345 
347  checkGlError();
348 
349  lookup_table_texture->bind();
350  QUORI_FACE_NODE_TRACE(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, static_params.screen_size.x, static_params.screen_size.y, GL_RGB, GL_FLOAT, next_lookup_table));
351  delete[] lookup_table;
352  lookup_table = next_lookup_table;
353  checkGlError();
354 
356  checkGlError();
357  }
358 
359 
360  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
362 
364 
365  copy_current_image_pbo();
366  write_next_image_pbo(final_image);
367 
368  program->use();
369 
371  lookup_table_texture->bind();
372 
374  image_texture->bind();
375 
379 
380  window->swapBuffers();
381  ++image_pbo_index;
382 
383 
385  }
386 
390 
391  delete[] lookup_table;
392 
393  // Update params
394 
395  if (update_params)
396  {
397  const std::string path = ros::package::getPath("quori_face");
398  if (path.empty())
399  {
400  ROS_ERROR("Failed to update parameter file. quori_face package not found.");
401  return EXIT_FAILURE;
402  }
403 
404  std::ofstream out(path + "/config/params.yaml");
405 
406  if (!out.is_open())
407  {
408  ROS_ERROR("Failed to update parameter file. Couldn't open parameter file for writing.");
409  return EXIT_FAILURE;
410  }
411 
412  YAML::Node delta;
413  delta["x"] = static_params.delta.x;
414  delta["y"] = static_params.delta.y;
415 
416  YAML::Node screen_size;
417  screen_size["x"] = static_params.screen_size.x;
418  screen_size["y"] = static_params.screen_size.y;
419 
420  YAML::Node transform;
421  transform["R"] = static_params.R;
422  transform["r_m"] = static_params.r_m;
423  transform["r_o"] = static_params.r_o;
424  transform["h"] = static_params.h;
425  transform["L"] = static_params.L;
426  transform["epsilon"] = static_params.epsilon;
427  transform["delta"] = delta;
428  transform["screen_size"] = screen_size;
429 
430  YAML::Node lookup_table_resolution_node;
431  lookup_table_resolution_node["x"] = lookup_table_resolution.x;
432  lookup_table_resolution_node["y"] = lookup_table_resolution.y;
433 
434  YAML::Node image_resolution_node;
435  image_resolution_node["x"] = image_resolution.x;
436  image_resolution_node["y"] = image_resolution.y;
437 
438  YAML::Node min_node;
439  min_node["theta"] = (min_coord.theta - center.theta);
440  min_node["psi"] = (min_coord.psi - center.psi);
441 
442  YAML::Node max_node;
443  max_node["theta"] = (max_coord.theta - center.theta);
444  max_node["psi"] = (max_coord.psi - center.psi);
445 
446  YAML::Node center_node;
447  center_node["theta"] = center.theta;
448  center_node["psi"] = center.psi;
449 
450  YAML::Node root;
451  root["transform"] = transform;
452  root["lookup_table_resolution"] = lookup_table_resolution_node;
453  root["image_resolution"] = image_resolution_node;
454  root["max"] = max_node;
455  root["min"] = min_node;
456  root["center"] = center_node;
457 
458 
459  YAML::Emitter emitter;
460  emitter << root;
461 
462  out << emitter.c_str();
463  out.close();
464  }
465 
466  return EXIT_SUCCESS;
467 }
GL_ARRAY_BUFFER
#define GL_ARRAY_BUFFER
Definition: glcorearb.h:606
quori_face::shader::FRAGMENT
const std::string FRAGMENT
glEnable
#define glEnable
Definition: gl3w.h:875
GL_TEXTURE1
#define GL_TEXTURE1
Definition: glcorearb.h:465
quori_face::Vector2< std::uint32_t >
ros::package::getPath
ROSLIB_DECL std::string getPath(const std::string &package_name)
image_encodings.h
image_transport::ImageTransport
quori_face::WindowManager::ref
static WindowManager & ref()
Definition: WindowManager.cpp:95
quori_face::TransformStaticParameters::DEFAULT
static const TransformStaticParameters DEFAULT
Definition: transform.hpp:57
GL_FLOAT
#define GL_FLOAT
Definition: glcorearb.h:192
GL_FALSE
#define GL_FALSE
Definition: glcorearb.h:75
ros::init
ROSCPP_DECL void init(const M_string &remappings, const std::string &name, uint32_t options=0)
GL_TEXTURE_2D
#define GL_TEXTURE_2D
Definition: glcorearb.h:179
QUORI_FACE_NODE_TRACE
#define QUORI_FACE_NODE_TRACE(stmt)
Definition: nodes/quori_face_node/trace.hpp:16
ros.h
glVertexAttribPointer
#define glVertexAttribPointer
Definition: gl3w.h:1377
GLuint
unsigned int GLuint
Definition: glcorearb.h:69
ros::spinOnce
ROSCPP_DECL void spinOnce()
string
GLsizei const GLchar *const * string
Definition: glcorearb.h:790
quori_face::shader::VERTEX
const std::string VERTEX
quori_face::Vector2::x
T x
Definition: Vector2.hpp:37
glClear
#define glClear
Definition: gl3w.h:771
sensor_msgs::image_encodings::RGB8
const std::string RGB8
glTexSubImage2D
#define glTexSubImage2D
Definition: gl3w.h:1209
param.hpp
ros::ok
ROSCPP_DECL bool ok()
Window.hpp
image
GLeglImageOES image
Definition: glcorearb.h:4008
GL_UNSIGNED_BYTE
#define GL_UNSIGNED_BYTE
Definition: glcorearb.h:187
param
GLenum GLfloat param
Definition: glcorearb.h:252
trace.hpp
glEnableVertexAttribArray
#define glEnableVertexAttribArray
Definition: gl3w.h:877
quori_face::generateLookupTable
float * generateLookupTable(const TransformStaticParameters &static_params, const SphericalCoordinate &min, const SphericalCoordinate &max, const Vector2< std::uint32_t > &size)
Definition: transform.cpp:198
glBindTexture
#define glBindTexture
Definition: gl3w.h:747
quori_face::Shader::compile
static Ptr compile(const Type type, const std::uint8_t *const buffer, const std::size_t length)
Definition: Shader.cpp:29
quori_face
Definition: Cache.hpp:9
glDrawElements
#define glDrawElements
Definition: gl3w.h:862
Shader.hpp
glUniform3iv
#define glUniform3iv
Definition: gl3w.h:1253
Texture.hpp
quori_face::SphericalCoordinate::theta
double theta
Definition: transform.hpp:76
quori_face::GEOMETRY_VERTICES
const std::array< float, 20 > GEOMETRY_VERTICES
Definition: geometry.cpp:3
image_transport::ImageTransport::subscribe
Subscriber subscribe(const std::string &base_topic, uint32_t queue_size, const boost::function< void(const sensor_msgs::ImageConstPtr &)> &callback, const ros::VoidPtr &tracked_object=ros::VoidPtr(), const TransportHints &transport_hints=TransportHints())
gl3w.h
quori_face::Program::link
static Ptr link(const std::initializer_list< Shader::Ptr > &shaders)
Definition: Program.cpp:19
glActiveTexture
#define glActiveTexture
Definition: gl3w.h:726
glClearColor
#define glClearColor
Definition: gl3w.h:778
quori_face::SphericalCoordinate::CENTER
static const SphericalCoordinate CENTER
Definition: transform.hpp:86
quori_face::Window::open
static Ptr open(Args &&...args)
Definition: Window.hpp:72
GL_ELEMENT_ARRAY_BUFFER
#define GL_ELEMENT_ARRAY_BUFFER
Definition: glcorearb.h:607
Program.hpp
GL_COLOR_BUFFER_BIT
#define GL_COLOR_BUFFER_BIT
Definition: glcorearb.h:74
quori_face::Shader::Type::Vertex
@ Vertex
frag.hpp
sensor_msgs::image_encodings::numChannels
static int numChannels(const std::string &encoding)
quori_face::Texture::create
static Ptr create(std::size_t rows, std::size_t cols, float *const data)
Definition: Texture.cpp:14
GL_BGR
#define GL_BGR
Definition: glcorearb.h:436
quori_face::SphericalCoordinate
Definition: transform.hpp:68
imageCallback
void imageCallback(const sensor_msgs::ImageConstPtr &msg)
GL_UNSIGNED_INT
#define GL_UNSIGNED_INT
Definition: glcorearb.h:191
geometry.hpp
WindowManager.hpp
package.h
level
GLint level
Definition: glcorearb.h:256
sensor_msgs::image_encodings::bitDepth
static int bitDepth(const std::string &encoding)
image_transport.h
path
GLsizei const GLchar *const * path
Definition: glcorearb.h:3572
quori_face::checkGlError
void checkGlError()
Throws a std::runtime_error if the OpenGL error flag is set.
Definition: gl.cpp:47
glDeleteVertexArrays
#define glDeleteVertexArrays
Definition: gl3w.h:842
quori_face::PixelBufferObject
Definition: PixelBufferObject.hpp:13
glBindVertexArray
#define glBindVertexArray
Definition: gl3w.h:751
transform.hpp
quori_face::Texture::Ptr
std::shared_ptr< Texture > Ptr
Definition: Texture.hpp:17
GL_TEXTURE0
#define GL_TEXTURE0
Definition: glcorearb.h:464
quori_face::PixelBufferObject::bind
void bind(const std::size_t i)
Definition: PixelBufferObject.cpp:24
GL_PIXEL_UNPACK_BUFFER
#define GL_PIXEL_UNPACK_BUFFER
Definition: glcorearb.h:949
gl.hpp
glDeleteBuffers
#define glDeleteBuffers
Definition: gl3w.h:831
PixelBufferObject.hpp
GL_STATIC_DRAW
#define GL_STATIC_DRAW
Definition: glcorearb.h:620
std
cv_bridge.h
GLfloat
khronos_float_t GLfloat
Definition: glcorearb.h:64
quori_face::Shader::Type::Fragment
@ Fragment
glMapBuffer
#define glMapBuffer
Definition: gl3w.h:1063
ROS_ERROR
#define ROS_ERROR(...)
GL_READ_WRITE
#define GL_READ_WRITE
Definition: glcorearb.h:613
quori_face::WindowManager::attach
void attach(const Window::Ptr &window)
Definition: WindowManager.cpp:37
quori_face_node
Definition: param.hpp:11
data
GLboolean * data
Definition: glcorearb.h:279
quori_face::SphericalCoordinate::psi
double psi
Definition: transform.hpp:81
glBufferData
#define glBufferData
Definition: gl3w.h:765
program
GLbitfield GLuint program
Definition: glcorearb.h:1886
quori_face::Window::pollEvents
static void pollEvents()
Definition: Window.cpp:62
GL_TRIANGLES
#define GL_TRIANGLES
Definition: glcorearb.h:81
sensor_msgs::image_encodings::BGR8
const std::string BGR8
glUnmapBuffer
#define glUnmapBuffer
Definition: gl3w.h:1284
glBindBuffer
#define glBindBuffer
Definition: gl3w.h:733
glGenVertexArrays
#define glGenVertexArrays
Definition: gl3w.h:905
f
GLdouble f
Definition: glcorearb.h:291
quori_face::Monitor::getPrimaryMonitor
static Monitor::Ptr getPrimaryMonitor()
Definition: Monitor.cpp:23
quori_face::TransformStaticParameters
Definition: transform.hpp:15
glUniform1i
#define glUniform1i
Definition: gl3w.h:1236
quori_face::transform
Vector2< double > transform(const TransformStaticParameters &static_params, const SphericalCoordinate &coord)
Definition: transform.cpp:171
quori_face::PixelBufferObject::unbind
void unbind()
Definition: PixelBufferObject.cpp:29
glGenBuffers
#define glGenBuffers
Definition: gl3w.h:897
vertex.hpp
ros::NodeHandle
main
int main(int argc, char *argv[])
Definition: main.cpp:143
GLint
int GLint
Definition: glcorearb.h:65
GL_RGB
#define GL_RGB
Definition: glcorearb.h:221
quori_face::GEOMETRY_INDICES
const std::array< std::uint32_t, 6 > GEOMETRY_INDICES
Definition: geometry.cpp:10


quori_face
Author(s):
autogenerated on Wed Mar 2 2022 00:53:20