JeVois  1.5
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;
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(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(ifs, defidx, checkso);
141 }
142 
143 // ####################################################################################################
144 std::vector<jevois::VideoMapping> jevois::videoMappingsFromStream(std::istream & is, size_t & defidx, bool checkso)
145 {
146  size_t linenum = 1;
147  std::vector<jevois::VideoMapping> mappings;
148  jevois::VideoMapping defmapping = { };
149 
150  for (std::string line; std::getline(is, line); ++linenum)
151  {
152  std::vector<std::string> tok = jevois::split(line);
153  if (tok.empty()) continue; // skip blank lines
154  if (tok.size() == 1 && tok[0].empty()) continue; // skip blank lines
155  if (tok[0][0] == '#') continue; // skip comments
156  if (tok.size() < 10) { PERROR("Found " << tok.size() << " tokens instead of >= 10 -- SKIPPING"); continue; }
157 
159  try
160  {
161  m.ofmt = jevois::strfcc(tok[0]);
162  m.ow = std::stoi(tok[1]);
163  m.oh = std::stoi(tok[2]);
164  m.ofps = std::stof(tok[3]);
165 
166  m.cfmt = jevois::strfcc(tok[4]);
167  m.cw = std::stoi(tok[5]);
168  m.ch = std::stoi(tok[6]);
169  m.cfps = std::stof(tok[7]);
170  }
171  catch (std::exception const & e) { PERROR("Skipping entry because of parsing error: " << e.what()); }
172  catch (...) { PERROR("Skipping entry because of parsing errors"); }
173 
174  m.vendor = tok[8];
175  m.modulename = tok[9];
176 
177  // Determine C++ vs python, silently skip this module if none of those and checkso was given:
178  try { m.setModuleType(); }
179  catch (...)
180  {
181  if (checkso)
182  { PERROR("No .so|.py found for " << m.vendor << '/' << m.modulename << " -- SKIPPING."); continue; }
183  }
184 
185  // Handle optional star for default mapping. We tolerate several and pick the first one:
186  if (tok.size() > 10)
187  {
188  if (tok[10] == "*")
189  {
190  if (defmapping.cfmt == 0) defmapping = m;
191  if (tok.size() > 11 && tok[11][0] != '#') PERROR("Extra garbage after 11th token ignored");
192  }
193  else if (tok[10][0] != '#') PERROR("Extra garbage after 10th token ignored");
194  }
195 
196  mappings.push_back(m);
197  }
198 
199  // Sort the array:
200  std::sort(mappings.begin(), mappings.end(),
201  [=](jevois::VideoMapping const & a, jevois::VideoMapping const & b)
202  {
203  // Return true if a should be ordered before b:
204  if (a.ofmt < b.ofmt) return true;
205  if (a.ofmt == b.ofmt) {
206  if (a.ow > b.ow) return true;
207  if (a.ow == b.ow) {
208  if (a.oh > b.oh) return true;
209  if (a.oh == b.oh) {
210  if (a.ofps > b.ofps) return true;
211  if (std::abs(a.ofps - b.ofps) < 0.01F) {
212  // The two output modes are identical. Warn unless the output format is NONE. We will adjust the
213  // framerates later to distinguish the offenders:
214  if (a.ofmt != 0)
215  PERROR("WARNING: Two modes have identical output format: " <<
216  jevois::fccstr(a.ofmt) << ' ' << a.ow << 'x' << a.oh << " @ " << a.ofps << "fps");
217 
218  // All right, all USB stuff being equal, just sort according to the camera format:
219  if (a.cfmt < b.cfmt) return true;
220  if (a.cfmt == b.cfmt) {
221  if (a.cw > b.cw) return true;
222  if (a.cw == b.cw) {
223  if (a.ch > b.ch) return true;
224  if (a.ch == b.ch) {
225  if (a.cfps > b.cfps) return true;
226  // it's ok to have duplicates here since either those are NONE USB modes that are selected
227  // manually, or we will adjust below
228  }
229  }
230  }
231  }
232  }
233  }
234  }
235  return false;
236  });
237 
238  // We need at least one mapping to work, and we need at least one with UVC output too keep hosts happy:
239  if (mappings.empty() || mappings.back().ofmt == 0)
240  {
241  PERROR("No valid video mapping with UVC output found -- INSERTING A DEFAULT ONE");
243  m.ofmt = V4L2_PIX_FMT_YUYV; m.ow = 640; m.oh = 480; m.ofps = 30.0F;
244  m.cfmt = V4L2_PIX_FMT_YUYV; m.cw = 640; m.ch = 480; m.cfps = 30.0F;
245  m.vendor = "JeVois"; m.modulename = "PassThrough"; m.ispython = false;
246 
247  // We are guaranteed that this will not create a duplicate output mapping since either mappings was empty or it had
248  // no USB-out modes:
249  mappings.push_back(m);
250  }
251 
252  // If we had duplicate output formats, discard full exact duplicates (including same module), and otherwise adjust
253  // framerates slightly: In the sorting above, we ordered by decreasing ofps (all else being equal). Here we are going
254  // to decrease ofps on the second mapping when we hit a match. We need to beware that this should propagate down to
255  // subsequent matching mappings while preserving the ordering:
256  auto a = mappings.begin(), b = a + 1;
257  while (b != mappings.end())
258  {
259  // Discard exact duplicates, adjust frame rates for matching specs but different modules:
260  if (a->isSameAs(*b)) { b = mappings.erase(b); continue; }
261  else if (b->ofmt != 0 && a->ofmt == b->ofmt && a->ow == b->ow && a->oh == b->oh)
262  {
263  if (std::abs(a->ofps - b->ofps) < 0.01F) b->ofps -= 1.0F; // equal fps, decrease b.ofps by 1fps
264  else if (b->ofps > a->ofps) b->ofps = a->ofps - 1.0F; // got out of order because of a previous decrease
265  }
266  a = b; ++b;
267  }
268 
269  // Find back our default mapping index in the sorted array:
270  if (defmapping.cfmt == 0)
271  {
272  LERROR("No default video mapping provided, using first one with UVC output");
273  for (size_t i = 0; i < mappings.size(); ++i) if (mappings[i].ofmt) { defidx = i; break; }
274  }
275  else
276  {
277  // Default was set, find its index after sorting:
278  for (size_t i = 0; i < mappings.size(); ++i)
279  if (mappings[i].ofmt == defmapping.ofmt && mappings[i].ow == defmapping.ow &&
280  mappings[i].oh == defmapping.oh && mappings[i].ofps == defmapping.ofps)
281  { defidx = i; break; }
282  }
283 
284  // Now that everything is sorted, compute our UVC format and frame indices, those are 1-based, and frame is reset each
285  // time format changes. Note that all the intervals will be passed as a list to the USB host for a given format and
286  // frame combination, so all the mappings that have identical pixel format and frame size here receive the same
287  // uvcformat and uvcframe numbers. Note that we skip over all the NONE ofmt modes here:
288  unsigned int ofmt = ~0U, ow = ~0U, oh = ~0U, iformat = 0, iframe = 0;
289  for (jevois::VideoMapping & m : mappings)
290  {
291  if (m.ofmt == 0) { m.uvcformat = 0; m.uvcframe = 0; LDEBUG(m.str()); continue; }
292  if (m.ofmt != ofmt) { ofmt = m.ofmt; ow = ~0U; oh = ~0U; ++iformat; iframe = 0; } // Switch to the next format
293  if (m.ow != ow || m.oh != oh) { ow = m.ow; oh = m.oh; ++iframe; } // Switch to the next frame size
294  m.uvcformat = iformat; m.uvcframe = iframe;
295  LDEBUG(m.str());
296  }
297 
298  return mappings;
299 }
300 
301 // ####################################################################################################
302 bool jevois::VideoMapping::match(unsigned int oformat, unsigned int owidth, unsigned int oheight,
303  float oframespersec) const
304 {
305  if (ofmt == oformat && ow == owidth && oh == oheight && (std::abs(ofps - oframespersec) < 0.1F)) return true;
306  return false;
307 }
308 
309 // ####################################################################################################
311 {
312  // First assume that it is a C++ compiled module and check for the .so file:
313  ispython = false;
314  std::string sopa = sopath();
315  std::ifstream testifs(sopa);
316  if (testifs.is_open() == false)
317  {
318  // Could not find the .so, maybe it is a python module:
319  ispython = true; sopa = sopath();
320  std::ifstream testifs2(sopa);
321  if (testifs2.is_open() == false) throw std::runtime_error("Could not open module file " + sopa + "|.so");
322  }
323 }
#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:46
unsigned int uvcformat
USB-UVC format number (1-based)
Definition: VideoMapping.H:53
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:43
std::string modulename
Name of the Module that will process this mapping.
Definition: VideoMapping.H:58
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:50
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:41
std::string fccstr(unsigned int fcc)
Convert a V4L2 four-cc code (V4L2_PIX_FMT_...) to a 4-char string.
Definition: Utils.C:37
unsigned int uvcframe
USB UVC frame number (1-based)
Definition: VideoMapping.H:54
unsigned int oh
output height
Definition: VideoMapping.H:45
void setModuleType()
Determine whether module is C++ or python and set ispython flag accordingly.
Definition: VideoMapping.C:310
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:79
unsigned int cfmt
camera pixel format
Definition: VideoMapping.H:48
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:302
unsigned int osize() const
Return the size in bytes of an output image.
Definition: VideoMapping.C:37
unsigned int cw
camera width
Definition: VideoMapping.H:49
#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:60
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:44
std::string vendor
Module creator name, used as a directory to organize the modules.
Definition: VideoMapping.H:56
unsigned int strfcc(std::string const &str)
Convert a JeVois video format string to V4L2 four-cc code (V4L2_PIX_FMT_...)
Definition: Utils.C:50
float cfps
camera frame rate in frames/sec
Definition: VideoMapping.H:51
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:113
std::string cstr() const
Convenience function to print out FCC WxH @ fps, for the input (camera) format.
Definition: VideoMapping.C:79