ColorImage.cc
Go to the documentation of this file.
1 
37 #ifdef WIN32
38 #ifndef WIN32_LEAN_AND_MEAN
39 #define WIN32_LEAN_AND_MEAN 1
40 #endif
41 
42 #include <windows.h>
43 #include <winsock2.h>
44 #else
45 #include <unistd.h>
46 #include <arpa/inet.h> // htons
47 #endif
48 
49 #include <fstream>
50 #include <iomanip>
51 #include <iostream>
52 #include <memory>
53 #include <signal.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <string>
58 #include <cstdint>
59 #include <array>
60 
62 
65 
66 using namespace crl::multisense;
67 
68 namespace { // anonymous
69 
70 volatile bool doneG = false;
71 
72 void usage(const char* programNameP)
73 {
74  std::cerr << "USAGE: " << programNameP << " [<options>]" << std::endl;
75  std::cerr << "Where <options> are:" << std::endl;
76  std::cerr << "\t-a <current_address> : CURRENT IPV4 address (default=10.66.171.21)" << std::endl;
77  std::cerr << "\t-m <mtu> : CURRENT MTU (default=7200)" << std::endl;
78  std::cerr << "\t-s <color_source> : LEFT,RIGHT,AUX (default=aux)" << std::endl;
79 
80  exit(1);
81 }
82 
83 #ifdef WIN32
84 BOOL WINAPI signalHandler(DWORD dwCtrlType)
85 {
86  CRL_UNUSED(dwCtrlType);
87  doneG = true;
88  return TRUE;
89 }
90 #else
91 void signalHandler(int sig)
92 {
93  (void)sig;
94  doneG = true;
95 }
96 #endif
97 
98 //
99 // Wrapper around a Channel pointer to make cleanup easier
100 
101 class ChannelWrapper
102 {
103  public:
104 
105  ChannelWrapper(const std::string& ipAddress) :
106  channelPtr_(Channel::Create(ipAddress))
107  {
108  }
109 
110  ~ChannelWrapper()
111  {
112  if (channelPtr_) {
113  Channel::Destroy(channelPtr_);
114  }
115  }
116 
117  Channel* ptr() noexcept
118  {
119  return channelPtr_;
120  }
121 
122  private:
123 
124  ChannelWrapper(const ChannelWrapper&) = delete;
125  ChannelWrapper operator=(const ChannelWrapper&) = delete;
126 
127  Channel* channelPtr_ = nullptr;
128 };
129 
130 //
131 // Wrapper to preserve image data outside of the image callback
132 
133 class ImageBufferWrapper
134 {
135  public:
136  ImageBufferWrapper(crl::multisense::Channel* driver,
137  const crl::multisense::image::Header& data) :
138  driver_(driver),
139  callbackBuffer_(driver->reserveCallbackBuffer()),
140  data_(data)
141  {
142  }
143 
144  ~ImageBufferWrapper()
145  {
146  if (driver_) {
147  driver_->releaseCallbackBuffer(callbackBuffer_);
148  }
149  }
150 
151  const image::Header& data() const noexcept
152  {
153  return data_;
154  }
155 
156  private:
157 
158  ImageBufferWrapper(const ImageBufferWrapper&) = delete;
159  ImageBufferWrapper operator=(const ImageBufferWrapper&) = delete;
160 
161  crl::multisense::Channel* driver_ = nullptr;
162  void* callbackBuffer_;
163  const image::Header data_;
164 
165 };
166 
167 struct UserData
168 {
169  Channel* driver = nullptr;
170  std::shared_ptr<const ImageBufferWrapper> chroma = nullptr;
171  std::shared_ptr<const ImageBufferWrapper> luma = nullptr;
174  std::pair<DataSource, DataSource> colorSource;
175 };
176 
177 template<typename T>
178 constexpr std::array<uint8_t, 3> ycbcrToBgr(const crl::multisense::image::Header& luma,
179  const crl::multisense::image::Header& chroma,
180  const size_t u,
181  const size_t v)
182 {
183  const uint8_t* lumaP = reinterpret_cast<const uint8_t*>(luma.imageDataP);
184  const uint8_t* chromaP = reinterpret_cast<const uint8_t*>(chroma.imageDataP);
185 
186  const size_t luma_offset = (v * luma.width) + u;
187  const size_t chroma_offset = 2 * (((v / 2) * (luma.width / 2)) + (u / 2));
188 
189  const float px_y = static_cast<float>(lumaP[luma_offset]);
190  const float px_cb = static_cast<float>(chromaP[chroma_offset + 0]) - 128.0f;
191  const float px_cr = static_cast<float>(chromaP[chroma_offset + 1]) - 128.0f;
192 
193  float px_r = px_y + 1.13983f * px_cr;
194  float px_g = px_y - 0.39465f * px_cb - 0.58060f * px_cr;
195  float px_b = px_y + 2.03211f * px_cb;
196 
197  if (px_r < 0.0f) px_r = 0.0f;
198  else if (px_r > 255.0f) px_r = 255.0f;
199  if (px_g < 0.0f) px_g = 0.0f;
200  else if (px_g > 255.0f) px_g = 255.0f;
201  if (px_b < 0.0f) px_b = 0.0f;
202  else if (px_b > 255.0f) px_b = 255.0f;
203 
204  return { {static_cast<uint8_t>(px_r), static_cast<uint8_t>(px_g), static_cast<uint8_t>(px_b)} };
205 }
206 
207 void ycbcrToBgr(const crl::multisense::image::Header& luma,
208  const crl::multisense::image::Header& chroma,
209  uint8_t* output)
210 {
211  if (luma.bitsPerPixel != 8 || chroma.bitsPerPixel != 16)
212  {
213  throw std::runtime_error("Only 8-bit luma and 16-bit chroma images are supported by the \
214  ycbcrToBgr conversion function");
215  }
216 
217  const size_t rgb_stride = luma.width * 3;
218 
219  for (uint32_t y = 0; y < luma.height; ++y)
220  {
221  const size_t row_offset = y * rgb_stride;
222 
223  for (uint32_t x = 0; x < luma.width; ++x)
224  {
225  memcpy(output + row_offset + (3 * x), ycbcrToBgr<uint8_t>(luma, chroma, x, y).data(), 3);
226  }
227  }
228 }
229 
230 bool savePpm(const std::string& fileName,
231  uint32_t width,
232  uint32_t height,
233  const void* dataP)
234 {
235  std::ofstream outputStream(fileName.c_str(), std::ios::out | std::ios::binary);
236 
237  if (false == outputStream.good()) {
238  std::cerr << "Failed to open \"" << fileName << "\"" << std::endl;
239  return false;
240  }
241 
242  const uint32_t imageSize = height * width * 3;
243 
244 
245  outputStream << "P6\n"
246  << width << " " << height << "\n"
247  << 0xFF << "\n";
248 
249  outputStream.write(reinterpret_cast<const char*>(dataP), imageSize);
250 
251  outputStream.close();
252  return true;
253 }
254 
255 bool saveColor(const std::string& fileName,
256  std::shared_ptr<const ImageBufferWrapper> leftRect,
257  std::shared_ptr<const ImageBufferWrapper> leftChromaRect)
258 {
259  std::vector<uint8_t> output(leftRect->data().width * leftRect->data().height * 3);
260  ycbcrToBgr(leftRect->data(), leftChromaRect->data(), output.data());
261  // something like this
262  savePpm(fileName, leftRect->data().width, leftRect->data().height, output.data());
263  return true;
264 }
265 
266 void imageCallback(const image::Header& header,
267  void* userDataP)
268 {
269  UserData* userData = reinterpret_cast<UserData*>(userDataP);
270 
271 
272  if (!userData->driver) {
273  std::cerr << "Invalid MultiSense channel" << std::endl;
274  return;
275  }
276 
277  if (header.source == userData->colorSource.first)
278  {
279  userData->chroma = std::make_shared<ImageBufferWrapper>(userData->driver, header);
280  if (userData->luma && userData->luma->data().frameId == header.frameId)
281  {
282  // matching frameID's, pass through to create image
283  }
284  else
285  {
286  return;
287  }
288  }
289  if (header.source == userData->colorSource.second)
290  {
291  userData->luma = std::make_shared<ImageBufferWrapper>(userData->driver, header);
292  if (userData->chroma && userData->chroma->data().frameId == header.frameId)
293  {
294  // matching frameID's, pass through to create image
295  }
296  else
297  {
298  return;
299  }
300  }
301 
302  if (userData->luma != nullptr && userData->chroma != nullptr)
303  {
304  saveColor(std::to_string(header.frameId) + ".ppm", userData->luma,
305  userData->chroma);
306  }
307 }
308 
309 std::pair<DataSource, DataSource> colorSourceFromArg(const std::string &srcStr)
310 {
311  if (srcStr == "aux")
312  {
314  }
315  else if (srcStr == "left")
316  {
318  }
319  else if (srcStr == "right")
320  {
322  }
323  else
324  {
325  throw std::runtime_error("Invalid color source given");
326  }
327 }
328 
329 } // anonymous
330 
331 int main(int argc,
332  char** argvPP)
333 {
334  std::string currentAddress = "10.66.171.21";
335  int32_t mtu = 7200;
336  std::pair<DataSource, DataSource> userSource{ Source_Chroma_Rectified_Aux, Source_Luma_Rectified_Aux };
337 
338 #if WIN32
339  SetConsoleCtrlHandler(signalHandler, TRUE);
340 #else
341  signal(SIGINT, signalHandler);
342 #endif
343 
344  //
345  // Parse args
346 
347  int c;
348 
349  while (-1 != (c = getopt(argc, argvPP, "a:m:s:")))
350  switch (c) {
351  case 'a': currentAddress = std::string(optarg); break;
352  case 'm': mtu = atoi(optarg); break;
353  case 's': userSource = colorSourceFromArg(optarg); break;
354  default: usage(*argvPP); break;
355  }
356 
357  Status status;
358 
359  //
360  // Initialize communications.
361 
362  auto channelP = std::make_unique<ChannelWrapper>(currentAddress);
363  if (nullptr == channelP || nullptr == channelP->ptr()) {
364  std::cerr << "Failed to establish communications with \"" << currentAddress << "\"" << std::endl;
365  return EXIT_FAILURE;
366  }
367 
368  //
369  // Change MTU
370 
371  status = channelP->ptr()->setMtu(mtu);
372  if (Status_Ok != status) {
373  std::cerr << "Failed to set MTU to " << mtu << ": " << Channel::statusString(status) << std::endl;
374  return EXIT_FAILURE;
375  }
376 
377  //
378  // Query calibration
379 
380  image::Calibration calibration;
381  status = channelP->ptr()->getImageCalibration(calibration);
382  if (Status_Ok != status) {
383  std::cerr << "Failed to query calibraiton: " << Channel::statusString(status) << std::endl;
384  return EXIT_FAILURE;
385  }
386 
387  //
388  // Query device info
389 
391  status = channelP->ptr()->getDeviceInfo(deviceInfo);
392  if (Status_Ok != status) {
393  std::cerr << "Failed to query device info: " << Channel::statusString(status) << std::endl;
394  return EXIT_FAILURE;
395  }
396 
397  //
398  // Change framerate and resolution to 1/4 resolution
399 
400  image::Config cfg;
401 
402  status = channelP->ptr()->getImageConfig(cfg);
403  if (Status_Ok != status) {
404  std::cerr << "Failed to get image config: " << Channel::statusString(status) << std::endl;
405  return EXIT_FAILURE;
406  }
407  else {
408  cfg.setFps(10.0);
409  cfg.setAutoWhiteBalance(true);
410  cfg.setAutoExposure(true);
411  cfg.setResolution(deviceInfo.imagerWidth / 2, deviceInfo.imagerHeight / 2);
412 
413  status = channelP->ptr()->setImageConfig(cfg);
414  if (Status_Ok != status) {
415  std::cerr << "Failed to configure sensor: " << Channel::statusString(status) << std::endl;
416  return EXIT_FAILURE;
417  }
418  }
419 
420  //
421  // Setup user data to store camera state for pointcloud reprojection
422 
423  UserData userData{ channelP->ptr(), nullptr, nullptr, calibration, deviceInfo, userSource };
424 
425  //
426  // Add callbacks
427 
428  channelP->ptr()->addIsolatedCallback(imageCallback, userSource.first | userSource.second, &userData);
429 
430  //
431  // Start streaming
432 
433  status = channelP->ptr()->startStreams(userSource.first | userSource.second);
434  if (Status_Ok != status) {
435  std::cerr << "Failed to start streams: " << Channel::statusString(status) << std::endl;
436  return EXIT_FAILURE;
437  }
438 
439  while (!doneG) {
440  usleep(1000000);
441  }
442 
443  status = channelP->ptr()->stopStreams(Source_All);
444  if (Status_Ok != status) {
445  std::cerr << "Failed to stop streams: " << Channel::statusString(status) << std::endl;
446  return EXIT_FAILURE;
447  }
448 
449  return EXIT_SUCCESS;
450 }
int main(int argc, char **argvPP)
Definition: ColorImage.cc:331
static const char * statusString(Status status)
Definition: channel.cc:802
static CRL_CONSTEXPR DataSource Source_Chroma_Left
static CRL_CONSTEXPR DataSource Source_Luma_Left
void setResolution(uint32_t w, uint32_t h)
int getopt(int argc, char **argv, char *opts)
Definition: getopt.c:31
static CRL_CONSTEXPR DataSource Source_Luma_Rectified_Aux
static CRL_CONSTEXPR DataSource Source_Luma_Right
#define CRL_UNUSED(var)
Definition: Portability.hh:44
static CRL_CONSTEXPR Status Status_Ok
def usage(progname)
static CRL_CONSTEXPR DataSource Source_Chroma_Rectified_Aux
static void Destroy(Channel *instanceP)
Definition: channel.cc:789
char * optarg
Definition: getopt.c:29
static CRL_CONSTEXPR DataSource Source_Chroma_Right
static CRL_CONSTEXPR DataSource Source_All


multisense_lib
Author(s):
autogenerated on Sat Jun 24 2023 03:01:21