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