JeVois  1.0
JeVois Smart Embedded Machine Vision Toolkit
VideoMapping.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 
19 #include <jevois/Debug/Log.H>
20 #include <jevois/Util/Utils.H>
21 
22 #include <fstream>
23 #include <sstream>
24 #include <algorithm>
25 #include <cmath>
26 
27 #define PERROR(x) LERROR("In file " << JEVOIS_ENGINE_CONFIG_FILE << ':' << linenum << ": " << x)
28 
29 namespace
30 {
31  unsigned int parseFormat(std::string const & str)
32  {
33  if (str == "BAYER") return V4L2_PIX_FMT_SRGGB8;
34  else if (str == "YUYV") return V4L2_PIX_FMT_YUYV;
35  else if (str == "GREY" || str == "GRAY") return V4L2_PIX_FMT_GREY;
36  else if (str == "MJPG") return V4L2_PIX_FMT_MJPEG;
37  else if (str == "RGB565") return V4L2_PIX_FMT_RGB565;
38  else if (str == "BGR24") return V4L2_PIX_FMT_BGR24;
39  else if (str == "NONE") return 0;
40  else throw std::runtime_error("Invalid pixel format " + str);
41  }
42 }
43 
44 // ####################################################################################################
45 std::string jevois::VideoMapping::sopath() const
46 {
47  return JEVOIS_MODULE_PATH "/" + vendor + '/' + modulename + '/' + modulename + ".so";
48 }
49 
50 // ####################################################################################################
51 unsigned int jevois::VideoMapping::osize() const
52 { return jevois::v4l2ImageSize(ofmt, ow, oh); }
53 
54 // ####################################################################################################
55 unsigned int jevois::VideoMapping::csize() const
56 { return jevois::v4l2ImageSize(cfmt, cw, ch); }
57 
58 // ####################################################################################################
59 float jevois::VideoMapping::uvcToFps(unsigned int interval)
60 {
61  // Let's round it off to the nearest 1/100Hz:
62  return float(1000000000U / interval) * 0.01F;
63 }
64 
65 // ####################################################################################################
66 unsigned int jevois::VideoMapping::fpsToUvc(float fps)
67 {
68  return (unsigned int)(10000000.0F / fps + 0.499F);
69 }
70 
71 // ####################################################################################################
72 float jevois::VideoMapping::v4l2ToFps(struct v4l2_fract const & interval)
73 {
74  // Let's round it off to the nearest 1/100Hz:
75  return float(interval.denominator * 100U / interval.numerator) * 0.01F;
76 }
77 
78 // ####################################################################################################
79 struct v4l2_fract jevois::VideoMapping::fpsToV4l2(float fps)
80 {
81  return { 100U, (unsigned int)(fps * 100.0F) };
82 }
83 
84 // ####################################################################################################
85 std::string jevois::VideoMapping::ostr() const
86 {
87  std::ostringstream ss;
88  ss << jevois::fccstr(ofmt) << ' ' << ow << 'x' << oh << " @ " << ofps << "fps";
89  return ss.str();
90 }
91 
92 // ####################################################################################################
93 std::string jevois::VideoMapping::cstr() const
94 {
95  std::ostringstream ss;
96  ss << jevois::fccstr(cfmt) << ' ' << cw << 'x' << ch << " @ " << cfps << "fps";
97  return ss.str();
98 }
99 
100 // ####################################################################################################
101 std::string jevois::VideoMapping::str() const
102 {
103  std::ostringstream ss;
104 
105  ss << "OUT: " << this->ostr() << " CAM: " << this->cstr();
106  //ss << " (uvc " << uvcformat << '/' << uvcframe << '/' << jevois::VideoMapping::fpsToUvc(ofps) << ')';
107  ss << " MOD: " << this->vendor << ':' << this->modulename;
108  //ss << ' ' << this->sopath();
109  return ss.str();
110 }
111 
112 // ####################################################################################################
113 std::ostream & jevois::operator<<(std::ostream & out, jevois::VideoMapping const & m)
114 {
115  out << jevois::fccstr(m.ofmt) << ' ' << m.ow << ' ' << m.oh << ' ' << m.ofps << ' '
116  << jevois::fccstr(m.cfmt) << ' ' << m.cw << ' ' << m.ch << ' ' << m.cfps << ' '
117  << m.vendor << ' ' << m.modulename;
118  return out;
119 }
120 
121 // ####################################################################################################
122 std::istream & jevois::operator>>(std::istream & in, jevois::VideoMapping & m)
123 {
124  std::string of, cf;
125  in >> of >> m.ow >> m.oh >> m.ofps >> cf >> m.cw >> m.ch >> m.cfps >> m.vendor >> m.modulename;
126 
127  m.ofmt = parseFormat(of);
128  m.cfmt = parseFormat(cf);
129 
130  return in;
131 }
132 
133 // ####################################################################################################
134 std::vector<jevois::VideoMapping> jevois::loadVideoMappings(size_t & defidx)
135 {
136  std::ifstream ifs(JEVOIS_ENGINE_CONFIG_FILE);
137  if (ifs.is_open() == false) LFATAL("Could not open [" << JEVOIS_ENGINE_CONFIG_FILE << ']');
138  return jevois::videoMappingsFromStream(ifs, defidx);
139 }
140 
141 // ####################################################################################################
142 std::vector<jevois::VideoMapping> jevois::videoMappingsFromStream(std::istream & is, size_t & defidx)
143 {
144  size_t linenum = 1;
145  std::vector<jevois::VideoMapping> mappings;
146  jevois::VideoMapping defmapping = { };
147 
148  for (std::string line; std::getline(is, line); ++linenum)
149  {
150  std::vector<std::string> tok = jevois::split(line);
151  if (tok.empty()) continue; // skip blank lines
152  if (tok.size() == 1 && tok[0].empty()) continue; // skip blank lines
153  if (tok[0][0] == '#') continue; // skip comments
154  if (tok.size() < 10) { PERROR("Found " << tok.size() << " tokens instead of >= 10 -- SKIPPING"); continue; }
155 
157  try
158  {
159  m.ofmt = parseFormat(tok[0]);
160  m.ow = std::stoi(tok[1]);
161  m.oh = std::stoi(tok[2]);
162  m.ofps = std::stof(tok[3]);
163 
164  m.cfmt = parseFormat(tok[4]);
165  m.cw = std::stoi(tok[5]);
166  m.ch = std::stoi(tok[6]);
167  m.cfps = std::stof(tok[7]);
168  }
169  catch (std::exception const & e) { PERROR("Skipping entry because of parsing error: " << e.what()); }
170  catch (...) { PERROR("Skipping entry because of parsing errors"); }
171 
172  m.vendor = tok[8];
173  m.modulename = tok[9];
174 
175  std::string const sopath = m.sopath();
176  std::ifstream testifs(sopath);
177  if (testifs.is_open() == false) { PERROR("Could not open module " << sopath << " -- SKIPPING"); continue; }
178 
179  // Handle optional star for default mapping. We tolerate several and pick the first one:
180  if (tok.size() > 10)
181  {
182  if (tok[10] == "*")
183  {
184  if (defmapping.cfmt == 0) defmapping = m;
185  if (tok.size() > 11 && tok[11][0] != '#') PERROR("Extra garbage after 11th token ignored");
186  }
187  else if (tok[10][0] != '#') PERROR("Extra garbage after 10th token ignored");
188  }
189 
190  mappings.push_back(m);
191  }
192 
193  // Sort the array:
194  std::sort(mappings.begin(), mappings.end(),
195  [=](jevois::VideoMapping const & a, jevois::VideoMapping const & b)
196  {
197  // Return true if a should be ordered before b:
198  if (a.ofmt < b.ofmt) return true;
199  if (a.ofmt == b.ofmt) {
200  if (a.ow > b.ow) return true;
201  if (a.ow == b.ow) {
202  if (a.oh > b.oh) return true;
203  if (a.oh == b.oh) {
204  if (a.ofps > b.ofps) return true;
205  if (std::abs(a.ofps - b.ofps) < 0.01F) {
206  // The two output modes are identical. Warn unless the output format is NONE. We will adjust the
207  // framerates later to distinguish the offenders:
208  if (a.ofmt != 0)
209  PERROR("WARNING: Two modes have identical output format: " <<
210  jevois::fccstr(a.ofmt) << ' ' << a.ow << 'x' << a.oh << " @ " << a.ofps << "fps");
211 
212  // All right, all USB stuff being equal, just sort according to the camera format:
213  if (a.cfmt < b.cfmt) return true;
214  if (a.cfmt == b.cfmt) {
215  if (a.cw > b.cw) return true;
216  if (a.cw == b.cw) {
217  if (a.ch > b.ch) return true;
218  if (a.ch == b.ch) {
219  if (a.cfps > b.cfps) return true;
220  // it's ok to have duplicates here since either those are NONE USB modes that are selected
221  // manually, or we will adjust below
222  }
223  }
224  }
225  }
226  }
227  }
228  }
229  return false;
230  });
231 
232  // If we had duplicate output formats, adjust framerates slightly: In the sorting above, we ordered by decreasing ofps
233  // (all else being equal). Here we are going to decrease ofps on the second mapping when we hit a match. We need to
234  // beware that this should propagate down to subsequent matching mappings while preserving the ordering:
235  for (size_t i = 1; i < mappings.size(); ++i)
236  {
237  jevois::VideoMapping const & a = mappings[i-1];
238  jevois::VideoMapping & b = mappings[i];
239 
240  if (b.ofmt != 0 && a.ofmt == b.ofmt && a.ow == b.ow && a.oh == b.oh)
241  {
242  if (std::abs(a.ofps - b.ofps) < 0.01F) b.ofps -= 1.0F; // equal fps, decrease b.ofps by 1fps
243  else if (b.ofps > a.ofps) b.ofps = a.ofps - 1.0F; // got out of order because of a previous decrease
244  }
245  }
246 
247  // We need at least one mapping to work, and we need at least one with UVC output too keep hosts happy:
248  if (mappings.empty() || mappings.back().ofmt == 0)
249  {
250  PERROR("No valid video mapping with UVC output found -- INSERTING A DEFAULT ONE");
252  m.ofmt = V4L2_PIX_FMT_YUYV; m.ow = 640; m.oh = 480; m.ofps = 30.0F;
253  m.cfmt = V4L2_PIX_FMT_YUYV; m.cw = 640; m.ch = 480; m.cfps = 30.0F;
254  m.vendor = "JeVois"; m.modulename = "PassThrough";
255 
256  // We are guaranteed that this will not create a duplicate output mapping:
257  mappings.push_back(m);
258  }
259 
260  // Find back our default mapping index in the sorted array:
261  if (defmapping.cfmt == 0)
262  {
263  LERROR("No default video mapping provided, using first one with UVC output");
264  for (size_t i = 0; i < mappings.size(); ++i) if (mappings[i].ofmt) { defidx = i; break; }
265  }
266  else
267  {
268  // Default was set, find its index after sorting:
269  for (size_t i = 0; i < mappings.size(); ++i)
270  if (mappings[i].ofmt == defmapping.ofmt && mappings[i].ow == defmapping.ow &&
271  mappings[i].oh == defmapping.oh && mappings[i].ofps == defmapping.ofps)
272  { defidx = i; break; }
273  }
274 
275  // Now that everything is sorted, compute our UVC format and frame indices, those are 1-based, and frame is reset each
276  // time format changes. Note that all the intervals will be passed as a list to the USB host for a given format and
277  // frame combination, so all the mappings that have identical pixel format and frame size here receive the same
278  // uvcformat and uvcframe numbers. Note that we skip over all the NONE ofmt modes here:
279  unsigned int ofmt = ~0U, ow = ~0U, oh = ~0U, iformat = 0, iframe = 0;
280  for (jevois::VideoMapping & m : mappings)
281  {
282  if (m.ofmt == 0) { m.uvcformat = 0; m.uvcframe = 0; LDEBUG(m.str()); continue; }
283  if (m.ofmt != ofmt) { ofmt = m.ofmt; ow = ~0U; oh = ~0U; ++iformat; iframe = 0; } // Switch to the next format
284  if (m.ow != ow || m.oh != oh) { ow = m.ow; oh = m.oh; ++iframe; } // Switch to the next frame size
285  m.uvcformat = iformat; m.uvcframe = iframe;
286  LDEBUG(m.str());
287  }
288 
289  return mappings;
290 }
291 
292 // ####################################################################################################
293 bool jevois::VideoMapping::match(unsigned int oformat, unsigned int owidth, unsigned int oheight,
294  float oframespersec) const
295 {
296  if (ofmt == oformat && ow == owidth && oh == oheight && (std::abs(ofps - oframespersec) < 0.1F)) return true;
297  return false;
298 }
299 
#define LDEBUG(msg)
Convenience macro for users to print out console or syslog messages, DEBUG level. ...
Definition: Log.H:148
float ofps
output frame rate in frames/sec
Definition: VideoMapping.H:46
unsigned int uvcformat
USB-UVC format number (1-based)
Definition: VideoMapping.H:53
unsigned int osize() const
Return the size in bytes of an output image.
Definition: VideoMapping.C:51
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:65
static struct v4l2_fract fpsToV4l2(float fps)
Convert from fps to V4L2 interval.
Definition: VideoMapping.C:79
unsigned int ofmt
output pixel format, or 0 for no output over USB
Definition: VideoMapping.H:43
std::string cstr() const
Convenience function to print out FCC WxH @ fps, for the input (camera) format.
Definition: VideoMapping.C:93
std::string modulename
Name of the Module that will process this mapping.
Definition: VideoMapping.H:58
#define LERROR(msg)
Convenience macro for users to print out console or syslog messages, ERROR level. ...
Definition: Log.H:186
unsigned int ch
camera height
Definition: VideoMapping.H:50
static float uvcToFps(unsigned int interval)
Convert from USB/UVC interval to fps.
Definition: VideoMapping.C:59
std::string ostr() const
Convenience function to print out FCC WxH @ fps, for the output (UVC) format.
Definition: VideoMapping.C:85
Simple struct to hold video mapping definitions for the processing Engine.
Definition: VideoMapping.H:41
std::string sopath() const
Return the full absolute path and file name of the module's .so file.
Definition: VideoMapping.C:45
std::string fccstr(unsigned int fcc)
Convert a V4L2 four-cc code (V4L2_PIX_FMT_...) to a 4-char string.
Definition: Utils.C:32
unsigned int uvcframe
USB UVC frame number (1-based)
Definition: VideoMapping.H:54
unsigned int oh
output height
Definition: VideoMapping.H:45
static float v4l2ToFps(struct v4l2_fract const &interval)
Convert from V4L2 interval to fps.
Definition: VideoMapping.C:72
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level. ...
Definition: Log.H:205
std::string str() const
Convenience function to print out the whole mapping in a human-friendly way.
Definition: VideoMapping.C:101
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:61
unsigned int cfmt
camera pixel format
Definition: VideoMapping.H:48
unsigned int cw
camera width
Definition: VideoMapping.H:49
#define PERROR(x)
Definition: VideoMapping.C:27
bool match(unsigned int oformat, unsigned int owidth, unsigned int oheight, float oframespersec) const
Return true if this VideoMapping's output format is a match to the given output parameters.
Definition: VideoMapping.C:293
static unsigned int fpsToUvc(float fps)
Convert from fps to USB/UVC interval.
Definition: VideoMapping.C:66
unsigned int ow
output width
Definition: VideoMapping.H:44
std::string vendor
Module creator name, used as a directory to organize the modules.
Definition: VideoMapping.H:56
unsigned int csize() const
Return the size in bytes of a camera image.
Definition: VideoMapping.C:55
float cfps
camera frame rate in frames/sec
Definition: VideoMapping.H:51