JeVois  1.16
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Utils.C
Go to the documentation of this file.
1 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2016 by Laurent Itti, the University of Southern
4 // California (USC), and iLab at USC. See http://iLab.usc.edu and http://jevois.org for information about this project.
5 //
6 // This file is part of the JeVois Smart Embedded Machine Vision Toolkit. This program is free software; you can
7 // redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
8 // Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
10 // License for more details. You should have received a copy of the GNU General Public License along with this program;
11 // if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12 //
13 // Contact information: Laurent Itti - 3641 Watt Way, HNB-07A - Los Angeles, CA 90089-2520 - USA.
14 // Tel: +1 213 740 3527 - itti@pollux.usc.edu - http://iLab.usc.edu - http://jevois.org
15 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16 /*! \file */
17 
18 #include <jevois/Util/Utils.H>
19 #include <jevois/Debug/Log.H>
20 
21 #include <linux/videodev2.h>
22 #include <string>
23 #include <vector>
24 #include <regex>
25 
26 #include <string.h> // for strncmp
27 #include <fstream>
28 #include <cstdarg> // for va_start, etc
29 #include <cstdio>
30 #include <iostream>
31 #include <memory>
32 #include <stdexcept>
33 #include <array>
34 #include <cctype> // for std::isspace()
35 
36 #include <opencv2/core/hal/interface.h> // for CV_MAT_DEPTH_MASK
37 
38 // For ""s std::string literals:
39 using namespace std::literals;
40 using namespace std::string_literals;
41 using namespace std::literals::string_literals;
42 
43 // ####################################################################################################
44 std::string jevois::fccstr(unsigned int fcc)
45 {
46  if (fcc == 0) return "NONE"; // for no video over USB output
47 
48  std::string ret(" ");
49  ret[0] = static_cast<char>(fcc & 0xff);
50  ret[1] = static_cast<char>((fcc >> 8) & 0xff);
51  ret[2] = static_cast<char>((fcc >> 16) & 0xff);
52  ret[3] = static_cast<char>((fcc >> 24) & 0xff);
53  return ret;
54 }
55 
56 // ####################################################################################################
57 std::string jevois::cvtypestr(unsigned int cvtype)
58 {
59  // From: https://gist.github.com/rummanwaqar/cdaddd5a175f617c0b107d353fd33695
60  std::string r;
61 
62  uchar depth = cvtype & CV_MAT_DEPTH_MASK;
63  uchar chans = 1 + (cvtype >> CV_CN_SHIFT);
64 
65  switch (depth)
66  {
67  case CV_8U: r = "8U"; break;
68  case CV_8S: r = "8S"; break;
69  case CV_16U: r = "16U"; break;
70  case CV_16S: r = "16S"; break;
71  case CV_16F: r = "16F"; break;
72  case CV_32S: r = "32S"; break;
73  case CV_32F: r = "32F"; break;
74  case CV_64F: r = "64F"; break;
75  default: r = "User"; break;
76  }
77 
78  if (chans > 1)
79  {
80  r += 'C';
81  r += (chans + '0');
82  }
83 
84  return r;
85 }
86 
87 // ####################################################################################################
88 unsigned int jevois::cvBytesPerPix(unsigned int cvtype)
89 {
90  uchar depth = cvtype & CV_MAT_DEPTH_MASK;
91  uchar chans = 1 + (cvtype >> CV_CN_SHIFT);
92  int r = 0;
93  switch (depth)
94  {
95  case CV_8U: r = 1; break;
96  case CV_8S: r = 1; break;
97  case CV_16U: r = 2; break;
98  case CV_16S: r = 2; break;
99  case CV_16F: r = 2; break;
100  case CV_32S: r = 4; break;
101  case CV_32F: r = 4; break;
102  case CV_64F: r = 4; break;
103  default: LFATAL("Unsupported OpenCV type " << cvtype);
104  }
105 
106  return r * chans;
107 }
108 
109 // ####################################################################################################
110 unsigned int jevois::strfcc(std::string const & str)
111 {
112  if (str == "BAYER") return V4L2_PIX_FMT_SRGGB8;
113  else if (str == "YUYV") return V4L2_PIX_FMT_YUYV;
114  else if (str == "GREY" || str == "GRAY") return V4L2_PIX_FMT_GREY;
115  else if (str == "MJPG") return V4L2_PIX_FMT_MJPEG;
116  else if (str == "RGB565") return V4L2_PIX_FMT_RGB565;
117  else if (str == "BGR24") return V4L2_PIX_FMT_BGR24;
118  else if (str == "RGB3") return V4L2_PIX_FMT_RGB24;
119  else if (str == "BGR3") return V4L2_PIX_FMT_BGR24;
120 
121  // Additional modes supported by JeVois-Pro ISP. We do not necessarily support all but we can name them:
122  else if (str == "RGB24") return V4L2_PIX_FMT_RGB24;
123  else if (str == "RGB32") return V4L2_PIX_FMT_RGB32;
124  else if (str == "UYVY") return V4L2_PIX_FMT_UYVY;
125  else if (str == "BAYER16") return V4L2_PIX_FMT_SBGGR16;
126  else if (str == "NV12") return V4L2_PIX_FMT_NV12;
127  else if (str == "YUV444") return V4L2_PIX_FMT_YUV444;
128  else if (str == "META") return ISP_V4L2_PIX_FMT_META;
129 
130  else if (str == "JVUI") return JEVOISPRO_FMT_GUI;
131 
132  else if (str == "NONE") return 0;
133  else throw std::runtime_error("Invalid pixel format " + str);
134 }
135 
136 // ####################################################################################################
137 unsigned int jevois::v4l2BytesPerPix(unsigned int fcc)
138 {
139  switch (fcc)
140  {
141  case V4L2_PIX_FMT_YUYV: return 2U;
142  case V4L2_PIX_FMT_GREY: return 1U;
143  case V4L2_PIX_FMT_SRGGB8: return 1U;
144  case V4L2_PIX_FMT_RGB565: return 2U;
145  case V4L2_PIX_FMT_MJPEG: return 2U; // at most??
146  case V4L2_PIX_FMT_BGR24: return 3U;
147  case V4L2_PIX_FMT_RGB24: return 3U;
148  case V4L2_PIX_FMT_RGB32: return 4U;
149  case V4L2_PIX_FMT_UYVY: return 2U;
150  case V4L2_PIX_FMT_SBGGR16: return 2U;
151  case V4L2_PIX_FMT_NV12: return 2U; // actually, it has 4:2:0 sampling so 1.5 bytes/pix
152  case V4L2_PIX_FMT_YUV444: return 3U;
153  case ISP_V4L2_PIX_FMT_META: return 1U;
154  case JEVOISPRO_FMT_GUI: return 4U; // RGBA
155  case 0: return 0U; // for NONE output to USB mode
156  default: LFATAL("Unsupported pixel format " << jevois::fccstr(fcc));
157  }
158 }
159 
160 // ####################################################################################################
161 unsigned int jevois::v4l2ImageSize(unsigned int fcc, unsigned int width, unsigned int height)
162 { return width * height * jevois::v4l2BytesPerPix(fcc); }
163 
164 // ####################################################################################################
165 unsigned int jevois::blackColor(unsigned int fcc)
166 {
167  switch (fcc)
168  {
169  case V4L2_PIX_FMT_YUYV: return 0x8000;
170  case V4L2_PIX_FMT_GREY: return 0;
171  case V4L2_PIX_FMT_SRGGB8: return 0;
172  case V4L2_PIX_FMT_RGB565: return 0;
173  case V4L2_PIX_FMT_MJPEG: return 0;
174  case V4L2_PIX_FMT_BGR24: return 0;
175  case V4L2_PIX_FMT_RGB24: return 0;
176  case V4L2_PIX_FMT_RGB32: return 0;
177  case V4L2_PIX_FMT_UYVY: return 0x8000;
178  case V4L2_PIX_FMT_SBGGR16: return 0;
179  case V4L2_PIX_FMT_NV12: return 0;
180  case V4L2_PIX_FMT_YUV444: return 0x008080;
181  case JEVOISPRO_FMT_GUI: return 0;
182  default: LFATAL("Unsupported pixel format " << jevois::fccstr(fcc));
183  }
184 }
185 
186 // ####################################################################################################
187 unsigned int jevois::whiteColor(unsigned int fcc)
188 {
189  switch (fcc)
190  {
191  case V4L2_PIX_FMT_YUYV: return 0x80ff;
192  case V4L2_PIX_FMT_GREY: return 0xff;
193  case V4L2_PIX_FMT_SRGGB8: return 0xff;
194  case V4L2_PIX_FMT_RGB565: return 0xffff;
195  case V4L2_PIX_FMT_MJPEG: return 0xff;
196  case V4L2_PIX_FMT_BGR24: return 0xffffff;
197  case V4L2_PIX_FMT_RGB24: return 0xffffff;
198  case V4L2_PIX_FMT_RGB32: return 0xffffffff;
199  case V4L2_PIX_FMT_UYVY: return 0xff80;
200  case V4L2_PIX_FMT_SBGGR16: return 0xffff;
201  case V4L2_PIX_FMT_NV12: return 0xffff; // probably wrong
202  case V4L2_PIX_FMT_YUV444: return 0xff8080;
203  case JEVOISPRO_FMT_GUI: return 0xffffffff;
204  default: LFATAL("Unsupported pixel format " << jevois::fccstr(fcc));
205  }
206 }
207 
208 // ####################################################################################################
209 void jevois::applyLetterBox(unsigned int & imw, unsigned int & imh,
210  unsigned int const winw, unsigned int const winh, bool noalias)
211 {
212  if (imw == 0 || imh == 0 || winw == 0 || winh == 0) LFATAL("Cannot handle zero width or height");
213 
214  if (noalias)
215  {
216  // We want an integer scaling factor to avoid aliasing:
217  double const facw = double(winw) / imw, fach = double(winh) / imh;
218  double const minfac = std::min(facw, fach);
219  if (minfac >= 1.0)
220  {
221  // Image is smaller than window: scale image up by integer part of fac:
222  unsigned int const ifac = minfac;
223  imw *= ifac; imh *= ifac;
224  }
225  else
226  {
227  // Image is larger than window: scale image down by integer part of 1/fac:
228  double const maxfac = std::max(facw, fach);
229  unsigned int const ifac = 1.0 / maxfac;
230  imw /= ifac; imh /= ifac;
231  }
232  }
233  else
234  {
235  // Slightly different algo here to make sure we exactly hit the window width or height with no rounding errors:
236  double const ia = double(imw) / double (imh);
237  double const wa = double(winw) / double (winh);
238 
239  if (ia >= wa)
240  {
241  // Image aspect ratio is wider than window aspect ratio. Thus, resize image width to window width, then resize
242  // image height accordingly using the image aspect ratio:
243  imw = winw;
244  imh = (unsigned int)(imw / ia + 0.4999999);
245  }
246  else
247  {
248  // Image aspect ratio is taller than window aspect ratio. Thus, resize image height to window height, then resize
249  // image width accordingly using the image aspect ratio:
250  imh = winh;
251  imw = (unsigned int)(imh * ia + 0.4999999);
252  }
253  }
254 }
255 
256 // ####################################################################################################
257 std::vector<std::string> jevois::split(std::string const & input, std::string const & regex)
258 {
259  // This code is from: http://stackoverflow.com/questions/9435385/split-a-string-using-c11
260  // passing -1 as the submatch index parameter performs splitting
261  std::regex re(regex);
262  std::sregex_token_iterator first{input.begin(), input.end(), re, -1}, last;
263  return { first, last };
264 }
265 
266 // ####################################################################################################
267 std::string jevois::join(std::vector<std::string> const & strings, std::string const & delimiter)
268 {
269  if (strings.empty()) return "";
270  if (strings.size() == 1) return strings[0];
271 
272  std::string ret; size_t const szm1 = strings.size() - 1;
273 
274  for (size_t i = 0; i < szm1; ++i) ret += strings[i] + delimiter;
275  ret += strings[szm1];
276 
277  return ret;
278 }
279 
280 // ####################################################################################################
281 bool jevois::stringStartsWith(std::string const & str, std::string const & prefix)
282 {
283  return (strncmp(str.c_str(), prefix.c_str(), prefix.length()) == 0);
284 }
285 
286 // ####################################################################################################
287 std::string jevois::replaceWhitespace(std::string const & str, char rep)
288 {
289  std::string ret = str;
290  for (char & c : ret) if (std::isspace(c)) c = rep;
291  return ret;
292 }
293 
294 // ####################################################################################################
295 std::string jevois::strip(std::string const & str)
296 {
297  int idx = str.length() - 1;
298  while (idx >= 0 && std::isspace(str[idx])) --idx;
299  return str.substr(0, idx + 1);
300 }
301 
302 // ####################################################################################################
303 std::string jevois::extractString(std::string const & str, std::string const & startsep, std::string const & endsep)
304 {
305  size_t idx = str.find(startsep);
306  if (idx == std::string::npos) return std::string();
307  idx += startsep.size();
308 
309  if (endsep.empty()) return str.substr(idx);
310 
311  size_t idx2 = str.find(endsep, idx);
312  if (idx2 == std::string::npos) return std::string();
313  return str.substr(idx, idx2 - idx);
314 }
315 
316 // ####################################################################################################
317 // Code from https://stackoverflow.com/questions/3418231/replace-part-of-a-string-with-another-string
318 size_t jevois::replaceStringFirst(std::string & str, std::string const & from, std::string const & to)
319 {
320  if (from.empty()) return 0;
321 
322  size_t start_pos = str.find(from);
323  if (start_pos == std::string::npos) return 0;
324 
325  str.replace(start_pos, from.length(), to);
326  return 1;
327 }
328 
329 // ####################################################################################################
330 // Code from https://stackoverflow.com/questions/3418231/replace-part-of-a-string-with-another-string
331 size_t jevois::replaceStringAll(std::string & str, std::string const & from, std::string const & to)
332 {
333  if (from.empty()) return 0;
334 
335  size_t start_pos = 0, n = 0;
336  while((start_pos = str.find(from, start_pos)) != std::string::npos)
337  {
338  str.replace(start_pos, from.length(), to);
339  start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
340  ++n;
341  }
342 
343  return n;
344 }
345 
346 // ####################################################################################################
347 std::string jevois::absolutePath(std::string const & root, std::string const & path)
348 {
349  // If path is empty, return root (be it empty of not):
350  if (path.empty()) return root;
351 
352  // no-op if the given path is already absolute:
353  if (path[0] == '/') return path;
354 
355  // no-op if root is empty:
356  if (root.empty()) return path;
357 
358  // We know root is not empty and path does not start with a / and is not empty; concatenate both:
359  return root + '/' + path;
360 }
361 
362 // ####################################################################################################
363 namespace
364 {
365  // This code is from NRT, and before that from the iLab C++ neuromorphic vision toolkit
366  std::string vsformat(char const * fmt, va_list ap)
367  {
368  // if we have a null pointer or an empty string, then just return an empty std::string
369  if (fmt == nullptr || fmt[0] == '\0') return std::string();
370 
371  int bufsize = 1024;
372  while (true)
373  {
374  char buf[bufsize];
375 
376  int const nchars = vsnprintf(buf, bufsize, fmt, ap);
377 
378  if (nchars < 0)
379  {
380  // Better leave this as LFATAL() rather than LERROR(), otherwise we have to return a bogus std::string (e.g. an
381  // empty string, or "none", or...), which might be dangerous if it is later used as a filename, for example.
382  LFATAL("vsnprintf failed for format '" << fmt << "' with bufsize = " << bufsize);
383  }
384  else if (nchars >= bufsize)
385  {
386  // buffer was too small, so let's double the bufsize and try again:
387  bufsize *= 2;
388  continue;
389  }
390  else
391  {
392  // OK, the vsnprintf() succeeded:
393  return std::string(&buf[0], nchars);
394  }
395  }
396  return std::string(); // can't happen, but placate the compiler
397  }
398 }
399 
400 // ####################################################################################################
401 std::string jevois::sformat(char const * fmt, ...)
402 {
403  va_list a;
404  va_start(a, fmt);
405  std::string result = vsformat(fmt, a);
406  va_end(a);
407  return result;
408 }
409 
410 // ####################################################################################################
412 {
413 #ifdef JEVOIS_PLATFORM_A33
414  std::ofstream ofs("/proc/sys/vm/drop_caches");
415  if (ofs.is_open()) ofs << "3" << std::endl;
416  else LERROR("Failed to flush cache -- ignored");
417 #endif
418 }
419 
420 // ####################################################################################################
421 // This code modified from here: https://stackoverflow.com/questions/478898/how-to-execute-a-command-and-get-
422 // output-of-command-within-c-using-posix
423 std::string jevois::system(std::string const & cmd, bool errtoo)
424 {
425  std::array<char, 128> buffer; std::string result;
426  std::shared_ptr<FILE> pip;
427  if (errtoo) pip.reset(popen((cmd + " 2>&1").c_str(), "r"), pclose);
428  else pip.reset(popen(cmd.c_str(), "r"), pclose);
429  if (!pip) LFATAL("popen() failed for command [" << cmd << ']');
430  while (!feof(pip.get())) if (fgets(buffer.data(), 128, pip.get()) != NULL) result += buffer.data();
431  return result;
432 }
433 
434 // ####################################################################################################
435 std::string jevois::secs2str(double secs)
436 {
437  if (secs < 1.0e-6) return jevois::sformat("%.2fns", secs * 1.0e9);
438  else if (secs < 1.0e-3) return jevois::sformat("%.2fus", secs * 1.0e6);
439  else if (secs < 1.0) return jevois::sformat("%.2fms", secs * 1.0e3);
440  else return jevois::sformat("%.2fs", secs);
441 }
442 
443 // ####################################################################################################
444 void jevois::secs2str(std::ostringstream & ss, double secs)
445 {
446  if (secs < 1.0e-6) ss << secs * 1.0e9 << "ns";
447  else if (secs < 1.0e-3) ss << secs * 1.0e6 << "us";
448  else if (secs < 1.0) ss << secs * 1.0e3 << "ms";
449  else ss << secs << 's';
450 }
451 
452 // ####################################################################################################
453 std::string jevois::getFileString(char const * fname, int skip)
454 {
455  std::ifstream ifs(fname);
456  if (ifs.is_open() == false) throw std::runtime_error("Cannot read file: "s + fname);
457  std::string str;
458  while (skip-- >= 0) std::getline(ifs, str);
459  ifs.close();
460 
461  return str;
462 }
JEVOISPRO_FMT_GUI
#define JEVOISPRO_FMT_GUI
JeVois-Pro zero-copy display of camera input frame (to be used as output mode in VideoMapping)
Definition: Utils.H:30
jevois::whiteColor
unsigned int whiteColor(unsigned int fcc)
Return a value that corresponds to white for the given video format.
Definition: Utils.C:187
jevois::sformat
std::string sformat(char const *fmt,...) __attribute__((format(__printf__
Create a string using printf style arguments.
Definition: Utils.C:401
jevois::split
std::vector< std::string > split(std::string const &input, std::string const &regex="\\s+")
Split string into vector of tokens using a regex to specify what to split on; default regex splits by...
Definition: Utils.C:257
jevois::cvBytesPerPix
unsigned int cvBytesPerPix(unsigned int cvtype)
Return the number of bytes per pixel for a given OpenCV pixel type.
Definition: Utils.C:88
jevois::flushcache
void flushcache()
Flush the caches, may sometimes be useful when running the camera in turbo mode.
Definition: Utils.C:411
jevois::cvtypestr
std::string cvtypestr(unsigned int cvtype)
Convert cv::Mat::type() code to to a string (e.g., CV_8UC1, CV_32SC3, etc)
Definition: Utils.C:57
jevois::replaceStringFirst
size_t replaceStringFirst(std::string &str, std::string const &from, std::string const &to)
Replace first instance of 'from' with 'to'.
Definition: Utils.C:318
ISP_V4L2_PIX_FMT_META
#define ISP_V4L2_PIX_FMT_META
Metadata V4L2 format used by Amlogic A311D camera ISP.
Definition: Utils.H:26
jevois::blackColor
unsigned int blackColor(unsigned int fcc)
Return a value that corresponds to black for the given video format.
Definition: Utils.C:165
LERROR
#define LERROR(msg)
Convenience macro for users to print out console or syslog messages, ERROR level.
Definition: Log.H:198
jevois::getFileString
std::string getFileString(char const *fname, int skip=0)
Read one line from a file and return it as a string.
Definition: Utils.C:453
jevois::replaceWhitespace
std::string replaceWhitespace(std::string const &str, char rep='_')
Replace white space characters in a string with underscore (default) or another characters.
Definition: Utils.C:287
Log.H
jevois::system
std::string system(std::string const &cmd, bool errtoo=true)
Execute a command and grab stdout output to a string.
Definition: Utils.C:423
jevois::v4l2BytesPerPix
unsigned int v4l2BytesPerPix(unsigned int fcc)
Return the number of bytes per pixel for a given V4L2_PIX_FMT_...
Definition: Utils.C:137
jevois::replaceStringAll
size_t replaceStringAll(std::string &str, std::string const &from, std::string const &to)
Replace all instances of 'from' with 'to'.
Definition: Utils.C:331
jevois::join
std::string join(std::vector< std::string > const &strings, std::string const &delimiter)
Concatenate a vector of tokens into a string.
Definition: Utils.C:267
jevois::extractString
std::string extractString(std::string const &str, std::string const &startsep, std::string const &endsep)
Extract a portion of a string between two delimiters.
Definition: Utils.C:303
jevois::fccstr
std::string fccstr(unsigned int fcc)
Convert a V4L2 four-cc code (V4L2_PIX_FMT_...) to a 4-char string.
Definition: Utils.C:44
jevois::applyLetterBox
void applyLetterBox(unsigned int &imw, unsigned int &imh, unsigned int const winw, unsigned int const winh, bool noalias)
Apply a letterbox resizing to fit an image into a window.
Definition: Utils.C:209
LFATAL
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
Definition: Log.H:217
jevois::absolutePath
std::string absolutePath(std::string const &root, std::string const &path)
Compute an absolute path from two paths.
Definition: Utils.C:347
jevois::stringStartsWith
bool stringStartsWith(std::string const &str, std::string const &prefix)
Return true if str starts with prefix (including if both strings are equal)
Definition: Utils.C:281
jevois::secs2str
std::string secs2str(double secs)
Report a duration given in seconds with variable units (ns, us, ms, or s), with precision of 2 decima...
Definition: Utils.C:435
Utils.H
jevois::v4l2ImageSize
unsigned int v4l2ImageSize(unsigned int fcc, unsigned int width, unsigned int height)
Return the image size in bytes for a given V4L2_PIX_FMT_..., width, height.
Definition: Utils.C:161
jevois::strfcc
unsigned int strfcc(std::string const &str)
Convert a JeVois video format string to V4L2 four-cc code (V4L2_PIX_FMT_...)
Definition: Utils.C:110
jevois::strip
std::string strip(std::string const &str)
Strip white space (including CR, LF, tabs, etc) from the end of a string.
Definition: Utils.C:295