JeVois  1.16
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
GUIhelper.C
Go to the documentation of this file.
1 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2020 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 #ifdef JEVOIS_PRO
19 
20 // for demo
21 #include <math.h>
22 #include <imgui.h>
23 #define IMGUI_DEFINE_MATH_OPERATORS // Access to math operators
24 #include <imgui_internal.h>
25 #include <algorithm> // for tweak demo
26 #include <jevois/GPU/GUIhelper.H>
27 #include <jevois/GPU/GUIconsole.H>
28 #include <jevois/GPU/GPUimage.H>
29 #include <jevois/Core/Module.H>
30 #include <jevois/Debug/SysInfo.H>
31 #include <imgui.h>
32 #include <imgui_internal.h>
33 #include <glm/gtc/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale, glm::perspective
34 #include <glm/gtx/euler_angles.hpp>
35 #include <fstream>
36 #include <set>
37 
38 #include <dirent.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 
42 // ##############################################################################################################
43 jevois::GUIhelper::GUIhelper(std::string const & instance, bool conslock) :
44  jevois::Component(instance), pixel_perfect_z(0.0f), itsIdle(true), itsConsLock(conslock), itsBackend()
45 {
46  // We defer OpenGL init to startFrame() so that all OpenGL code is in the same thread.
47 
48  itsWindowTitle = "JeVois-Pro v" + std::string(JEVOIS_VERSION_STRING);
49 
50  // Create some config files:
51  itsCfgItems.emplace_back(CfgItem { JEVOIS_ROOT_PATH "/config/videomappings.cfg",
52  "JeVois videomappings.cfg",
53  "",
55 
56  itsCfgItems.emplace_back(CfgItem { JEVOIS_ROOT_PATH "/config/initscript.cfg",
57  "JeVois initscript.cfg",
58  "",
60 
61  itsCfgItems.emplace_back(CfgItem { "params.cfg",
62  "Module's params.cfg",
63  "",
65 
66  itsCfgItems.emplace_back(CfgItem { "script.cfg",
67  "Module's script.cfg",
68  "",
70 
71  itsCfgItems.emplace_back(CfgItem { JEVOIS_ROOT_PATH "/share/dnn/models.yml",
72  "JeVois models.yml DNN Zoo",
73  "",
75 
77 
78  // Configure the config-file text editor:
79  auto cfglang = TextEditor::LanguageDefinition::JeVoisCfg();
80  itsCfgEditor.SetLanguageDefinition(cfglang);
81 
82  // Configure the code editor:
83  auto codelang = TextEditor::LanguageDefinition::CPlusPlus();
84  itsCodeEditor.SetLanguageDefinition(codelang);
85 }
86 
87 // ##############################################################################################################
89 {
90  // In case a DNN get was in progress, wait for it while letting user know:
91  JEVOIS_WAIT_GET_FUTURE(itsDnnGetFut);
92 }
93 
94 // ##############################################################################################################
95 void jevois::GUIhelper::reset(bool modulechanged)
96 {
97  itsEndFrameCalled = true;
98  itsImages.clear();
99  itsImages2.clear();
100  itsLastDrawnImage = nullptr;
101  itsLastDrawnTextLine = -1;
102  itsIdle = true;
103  itsIcon.clear();
104  itsModName.clear();
105  itsModDesc.clear();
106  itsModAuth.clear();
107  itsModLang.clear();
108  itsModDoc.clear();
109  itsRefreshCfgList = true;
110 
111  if (modulechanged)
112  {
113  itsCodeFileName.clear();
114  std::lock_guard<std::mutex> _(itsErrorMtx);
115  itsErrors.clear();
116  }
117 
118  // Get the actual window/screen size:
119  unsigned short w, h;
120  itsBackend.getWindowSize(w, h);
121 
122  if (w == 0) LFATAL("Need to call startFrame() at least once first");
123 
124  float const fov_y = 45.0f;
125 
126  proj = glm::perspective(glm::radians(fov_y), float(w) / float(h), 1.0f, h * 2.0f);
127  const_cast<float &>(pixel_perfect_z) = -float(h) / (2.0 * tan(fov_y * M_PI / 360.0));
128 #ifdef JEVOIS_PLATFORM
129  // On platform, we need to translate a bit to avoid aliasing issues, which are problematic with our YUYV shader:
130  proj = glm::translate(proj, glm::vec3(0.375f, 0.375f, 0.0f));
131 #endif
132  view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, pixel_perfect_z));
133 
134  // Set the style (on first call):
135  style::set(style::get());
136 }
137 
138 // ##############################################################################################################
139 bool jevois::GUIhelper::startFrame(unsigned short & w, unsigned short & h)
140 {
141  if (itsEndFrameCalled == false) LFATAL("You need to call endFrame() at the end of your process() function");
142 
143  // Get current window size, will be 0x0 if not initialized yet:
144  itsBackend.getWindowSize(w, h);
145 
146  if (w == 0)
147  {
148  // Need to init the display:
149  auto const siz = winsize::get(); winsize::freeze();
150  auto const fs = fullscreen::get(); fullscreen::freeze();
151  LINFO("OpenGL init " << siz.width << 'x' << siz.height << (fs ? " fullscreen" : ""));
152  itsBackend.init(siz.width, siz.height, fs, scale::get(), itsConsLock);
153  rounding::set(int(rounding::get() * scale::get() + 0.499F));
154  reset();
155  }
156 
157  // Poll events:
158  bool shouldclose = false; auto const now = std::chrono::steady_clock::now();
159  if (itsBackend.pollEvents(shouldclose)) itsLastEventTime = now;
160 
161  if (shouldclose && allowquit::get())
162  {
163  LINFO("Closing down on user request...");
164  engine()->quit();
165  }
166 
167  // Start the frame on the backend:
168  itsBackend.newFrame();
169 
170  // Check if we have been idle:
171  float const hs = hidesecs::get();
172  if (hs)
173  {
174  std::chrono::duration<float> elapsed = now - itsLastEventTime;
175  itsIdle = (elapsed.count() >= hs);
176  }
177  else itsIdle = false;
178 
179  itsEndFrameCalled = false;
180 
181  return itsIdle;
182 }
183 
184 // ##############################################################################################################
185 void jevois::GUIhelper::onParamChange(jevois::gui::scale const & param, float const & newval)
186 {
187  float oldval = param.get();
188  if (newval == oldval) return;
189 
190  ImGui::GetStyle().ScaleAllSizes(newval / oldval);
191  ImGui::GetIO().FontGlobalScale = newval;
192  ImGui::GetStyle().MouseCursorScale = 2.0f; // do not scale the cursor, otherwise it disappears...
193 
194  // Also scale the window corner rounding, make it no more than 24:
195  if (oldval) rounding::set(std::min(24, int(rounding::get() * newval / oldval + 0.499F)));
196 }
197 
198 // ##############################################################################################################
199 void jevois::GUIhelper::onParamChange(jevois::gui::style const & JEVOIS_UNUSED_PARAM(param),
200  jevois::gui::GuiStyle const & newval)
201 {
202  switch (newval)
203  {
204  case jevois::gui::GuiStyle::Dark:
205  ImGui::StyleColorsDark();
206  itsCfgEditor.SetPalette(TextEditor::GetDarkPalette());
207  itsCodeEditor.SetPalette(TextEditor::GetDarkPalette());
208  break;
209 
210  case jevois::gui::GuiStyle::Light:
211  ImGui::StyleColorsLight();
212  ImGui::GetStyle().Colors[ImGuiCol_WindowBg] = ImVec4(0.94f, 0.94f, 0.94f, 0.92f); // a bit transparent
213  itsCfgEditor.SetPalette(TextEditor::GetLightPalette());
214  itsCodeEditor.SetPalette(TextEditor::GetLightPalette());
215  break;
216 
217  case jevois::gui::GuiStyle::Classic:
218  ImGui::StyleColorsClassic();
219  itsCfgEditor.SetPalette(TextEditor::GetRetroBluePalette());
220  itsCodeEditor.SetPalette(TextEditor::GetRetroBluePalette());
221  break;
222  }
223 }
224 
225 // ##############################################################################################################
226 void jevois::GUIhelper::onParamChange(jevois::gui::rounding const & JEVOIS_UNUSED_PARAM(param), int const & newval)
227 {
228  auto & s = ImGui::GetStyle();
229  s.WindowRounding = newval;
230  s.ChildRounding = newval;
231  s.FrameRounding = newval;
232  s.PopupRounding = newval;
233  s.ScrollbarRounding = newval;
234  s.GrabRounding = newval;
235 }
236 
237 // ##############################################################################################################
239 { return (itsEndFrameCalled == false); }
240 
241 // ##############################################################################################################
242 void jevois::GUIhelper::drawImage(char const * name, jevois::RawImage const & img, int & x, int & y,
243  unsigned short & w, unsigned short & h, bool noalias, bool isoverlay)
244 {
245  // We will use one image per v4l2 buffer:
246  std::string const imgname = name + std::to_string(img.bufindex);
247 
248  // Get the image by name (will create a default-constructed one the first time a new name is used):
249  auto & im = itsImages[imgname];
250 
251  // Set the new pixel data:
252  im.set(img);
253 
254  // Draw it:
255  im.draw(x, y, w, h, noalias, proj * view);
256 
257  // Remember which image was drawn last, used by i2d():
258  if (isoverlay == false)
259  {
260  itsLastDrawnImage = & im;
261  itsUsingScaledImage = false;
262  itsLastDrawnTextLine = -1;
263  }
264 }
265 
266 // ##############################################################################################################
267 void jevois::GUIhelper::drawImage(char const * name, cv::Mat const & img, bool rgb, int & x, int & y,
268  unsigned short & w, unsigned short & h, bool noalias, bool isoverlay)
269 {
270  // Get the image by name (will create a default-constructed one the first time a new name is used):
271  auto & im = itsImages[name];
272 
273  // Set the new pixel data:
274  im.set(img, rgb);
275 
276  // Draw it:
277  im.draw(x, y, w, h, noalias, proj * view);
278 
279  // Remember which image was drawn last, used by i2d():
280  if (isoverlay == false)
281  {
282  itsLastDrawnImage = & im;
283  itsUsingScaledImage = false;
284  itsLastDrawnTextLine = -1;
285  }
286 }
287 
288 // ##############################################################################################################
289 void jevois::GUIhelper::drawInputFrame(char const * name, jevois::InputFrame const & frame, int & x, int & y,
290  unsigned short & w, unsigned short & h, bool noalias, bool casync)
291 {
292  // We will use one image per v4l2 buffer:
293  jevois::RawImage const img = frame.get(casync);
294  std::string const imgname = name + std::to_string(img.bufindex);
295 
296  // Get the image by name (will create a default-constructed one the first time a new name is used):
297  auto & im = itsImages[imgname];
298 
299  // Set the new pixel data using DMABUF acceleration. This will boil down to a no-op unless image size or format has
300  // changed (including the first time we set it):
301  im.set(frame, itsBackend.getDisplay());
302 
303  // Draw it:
304  im.draw(x, y, w, h, noalias, proj * view);
305 
306  // Remember which image was drawn last, used by i2d():
307  itsLastDrawnImage = & im;
308  itsUsingScaledImage = frame.hasScaledImage();
309  itsLastDrawnTextLine = -1;
310  if (itsUsingScaledImage)
311  {
312  jevois::RawImage img2 = frame.get2(casync);
313  itsScaledImageFacX = float(img.width) / float(img2.width);
314  itsScaledImageFacY = float(img.height) / float(img2.height);
315  }
316 }
317 
318 // ##############################################################################################################
319 void jevois::GUIhelper::drawInputFrame2(char const * name, jevois::InputFrame const & frame, int & x, int & y,
320  unsigned short & w, unsigned short & h, bool noalias, bool casync)
321 {
322  // We will use one image per v4l2 buffer:
323  jevois::RawImage const img = frame.get2(casync);
324  std::string const imgname = name + std::to_string(img.bufindex);
325 
326  // Get the image by name (will create a default-constructed one the first time a new name is used):
327  auto & im = itsImages2[imgname];
328 
329  // Set the new pixel data using DMABUF acceleration. This will boil down to a no-op unless image size or format has
330  // changed (including the first time we set it):
331  im.set2(frame, itsBackend.getDisplay());
332 
333  // Draw it:
334  im.draw(x, y, w, h, noalias, proj * view);
335 
336  // Remember which image was drawn last, used by i2d():
337  itsLastDrawnImage = & im;
338  itsUsingScaledImage = false;
339  itsLastDrawnTextLine = -1;
340 }
341 
342 // ##############################################################################################################
343 ImVec2 jevois::GUIhelper::i2d(ImVec2 p, char const * name)
344 {
345  // Find the image:
346  GPUimage * img;
347 
348  if (name == nullptr)
349  {
350  if (itsLastDrawnImage == nullptr) throw std::range_error("You need to call drawImage() or drawInputFrame() first");
351  img = itsLastDrawnImage;
352  if (itsUsingScaledImage) { p.x *= itsScaledImageFacX; p.y *= itsScaledImageFacY; }
353  }
354  else
355  {
356  std::string nstr = name;
357  auto itr = itsImages.find(nstr);
358  if (itr == itsImages.end())
359  {
360  // Add a zero in case we are dealing with a camera frame (drawImage() and drawInputFrame() add suffixes, but all
361  // image buffers are the same size):
362  itr = itsImages.find(nstr + '0');
363  if (itr == itsImages.end()) throw std::range_error("No previously drawn image with name [" + nstr + "] found");
364  }
365  img = & itr->second;
366  }
367 
368  // Delegate:
369  return img->i2d(p);
370 }
371 
372 // ##############################################################################################################
373 ImVec2 jevois::GUIhelper::i2d(float x, float y, char const * name)
374 { return i2d(ImVec2(x, y), name); }
375 
376 // ##############################################################################################################
377 ImVec2 jevois::GUIhelper::i2ds(ImVec2 p, char const * name)
378 {
379  // Find the image:
380  GPUimage * img;
381 
382  if (name == nullptr)
383  {
384  if (itsLastDrawnImage == nullptr) throw std::range_error("You need to call drawImage() or drawInputFrame() first");
385  img = itsLastDrawnImage;
386  if (itsUsingScaledImage) { p.x *= itsScaledImageFacX; p.y *= itsScaledImageFacY; }
387  }
388  else
389  {
390  std::string nstr = name;
391  auto itr = itsImages.find(nstr);
392  if (itr == itsImages.end())
393  {
394  // Add a zero in case we are dealing with a camera frame (drawImage() and drawInputFrame() add suffixes, but all
395  // image buffers are the same size):
396  itr = itsImages.find(nstr + '0');
397  if (itr == itsImages.end()) throw std::range_error("No previously drawn image with name [" + nstr + "] found");
398  }
399  img = & itr->second;
400  }
401 
402  // Delegate:
403  return img->i2ds(p);
404 }
405 
406 // ##############################################################################################################
407 ImVec2 jevois::GUIhelper::i2ds(float x, float y, char const * name)
408 { return i2ds(ImVec2(x, y), name); }
409 
410 // ##############################################################################################################
411 void jevois::GUIhelper::drawLine(float x1, float y1, float x2, float y2, ImU32 col)
412 {
413  ImGui::GetBackgroundDrawList()->AddLine(i2d(x1, y1), i2d(x2, y2), col, linethick::get());
414 }
415 
416 // ##############################################################################################################
417 void jevois::GUIhelper::drawRect(float x1, float y1, float x2, float y2, ImU32 col, bool filled)
418 {
419  auto dlb = ImGui::GetBackgroundDrawList();
420  ImVec2 const tl = i2d(x1, y1);
421  ImVec2 const br = i2d(x2, y2);
422 
423  if (filled) dlb->AddRectFilled(tl, br, applyFillAlpha(col));
424 
425  dlb->AddRect(tl, br, col, 0.0F, ImDrawCornerFlags_All, linethick::get());
426 }
427 
428 // ##############################################################################################################
429 void jevois::GUIhelper::drawPolyInternal(ImVec2 const * pts, size_t npts, ImU32 col, bool filled)
430 {
431  auto dlb = ImGui::GetBackgroundDrawList();
432  float const thick = linethick::get();
433 
434  if (filled) dlb->AddConvexPolyFilled(pts, npts, applyFillAlpha(col));
435 
436  if (npts)
437  {
438  for (size_t i = 0; i < npts - 1; ++i) dlb->AddLine(pts[i], pts[i + 1], col, thick);
439  dlb->AddLine(pts[npts - 1], pts[0], col, thick);
440  }
441 }
442 
443 // ##############################################################################################################
444 void jevois::GUIhelper::drawPoly(std::vector<cv::Point> const & pts, ImU32 col, bool filled)
445 {
446  size_t const npts = pts.size();
447 
448  ImVec2 iv[npts]; int i = 0;
449  for (auto const & p : pts) iv[i++] = i2d(p.x, p.y);
450 
451  drawPolyInternal(iv, npts, col, filled);
452 }
453 
454 // ##############################################################################################################
455 void jevois::GUIhelper::drawPoly(std::vector<cv::Point2f> const & pts, ImU32 col, bool filled)
456 {
457  size_t const npts = pts.size();
458 
459  ImVec2 iv[npts]; int i = 0;
460  for (auto const & p : pts) iv[i++] = i2d(p.x, p.y);
461 
462  drawPolyInternal(iv, npts, col, filled);
463 }
464 
465 // ##############################################################################################################
466 void jevois::GUIhelper::drawCircle(float x, float y, float r, ImU32 col, bool filled)
467 {
468  auto dlb = ImGui::GetBackgroundDrawList();
469 
470  ImVec2 const center = i2d(x, y);
471  float const rad = i2ds(r, 0).x;
472 
473  if (filled) dlb->AddCircleFilled(center, rad, applyFillAlpha(col), 0);
474 
475  dlb->AddCircle(center, rad, col, 0, linethick::get());
476 }
477 
478 // ##############################################################################################################
479 void jevois::GUIhelper::drawText(float x, float y, char const * txt, ImU32 col)
480 {
481  ImGui::GetBackgroundDrawList()->AddText(i2d(x, y), col, txt);
482 }
483 
484 // ##############################################################################################################
485 void jevois::GUIhelper::drawText(float x, float y, std::string const & txt, ImU32 col)
486 {
487  drawText(x, y, txt.c_str(), col);
488 }
489 
490 // ##############################################################################################################
491 ImU32 jevois::GUIhelper::applyFillAlpha(ImU32 col) const
492 {
493  unsigned char alpha = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
494  alpha = (unsigned char)(fillalpha::get() * alpha);
495  return (col & ~IM_COL32_A_MASK) | (alpha << IM_COL32_A_SHIFT);
496 }
497 
498 // ##############################################################################################################
499 ImVec2 jevois::GUIhelper::iline(int line, char const * name)
500 {
501  if (line == -1) line = ++itsLastDrawnTextLine; else itsLastDrawnTextLine = line;
502  ImVec2 p = i2d(0, 0, name);
503  p.x += 5.0F;
504  p.y += 5.0F + (ImGui::GetFontSize() + 5.0F) * line;
505  return p;
506 }
507 
508 // ##############################################################################################################
509 void jevois::GUIhelper::itext(char const * txt, ImU32 const & col, int line)
510 {
511  ImU32 const c = (col == IM_COL32_BLACK_TRANS) ? ImU32(overlaycolor::get()) : col;
512  ImGui::GetBackgroundDrawList()->AddText(iline(line), c, txt);
513 }
514 
515 // ##############################################################################################################
516 void jevois::GUIhelper::itext(std::string const & txt, ImU32 const & col, int line)
517 {
518  itext(txt.c_str(), col, line);
519 }
520 
521 // ##############################################################################################################
522 void jevois::GUIhelper::iinfo(jevois::InputFrame const & inframe, std::string const & fpscpu,
523  unsigned short winw, unsigned short winh)
524 {
525  unsigned short ww, wh;
526  if (winw == 0 || winh == 0) itsBackend.getWindowSize(ww, wh); else { ww = winw; wh = winh; }
527 
528  jevois::RawImage const & inimg = inframe.get();
529  std::string cam2str;
530  if (inframe.hasScaledImage())
531  {
532  jevois::RawImage const & inimg2 = inframe.get2();
533  cam2str += jevois::sformat(" + %s:%dx%d", jevois::fccstr(inimg2.fmt).c_str(), inimg2.width, inimg2.height);
534  }
535  std::string const msg = jevois::sformat("%s, Camera: %s:%dx%d%s, Display: RGBA:%dx%d", fpscpu.c_str(),
536  jevois::fccstr(inimg.fmt).c_str(), inimg.width, inimg.height,
537  cam2str.c_str(), ww, wh);
538 
539  ImGui::GetBackgroundDrawList()->AddText(ImVec2(10, wh-10-ImGui::GetFontSize()), overlaycolor::get(), msg.c_str());
540 }
541 
542 // ##############################################################################################################
543 void jevois::GUIhelper::releaseImage(char const * name)
544 {
545  auto itr = itsImages.find(name);
546  if (itr != itsImages.end()) itsImages.erase(itr);
547 }
548 
549 // ##############################################################################################################
550 void jevois::GUIhelper::releaseImage2(char const * name)
551 {
552  auto itr = itsImages2.find(name);
553  if (itr != itsImages2.end()) itsImages2.erase(itr);
554 }
555 
556 // ##############################################################################################################
558 {
559  // Decide whether to show mouse cursor based on idle state:
560  ImGui::GetIO().MouseDrawCursor = ! itsIdle;
561 
562  // Draw our JeVois GUI on top of everything:
563  if (itsIdle == false) drawJeVoisGUI();
564 
565  // Render everything and swap buffers:
566  itsBackend.render();
567 
568  itsEndFrameCalled = true;
569 }
570 
571 // ##############################################################################################################
573 {
574  if (ImGui::Begin(itsWindowTitle.c_str(), nullptr /* no closing */))//, ImGuiWindowFlags_MenuBar))
575  {
576  //drawMenuBar();
577  drawModuleSelect();
578 
579  //ImGui::Text("Camera + Process + Display: %.2f fps", ImGui::GetIO().Framerate);
580  ImGui::Separator();
581 
582  if (ImGui::BeginTabBar("##tabs", ImGuiTabBarFlags_None))
583  {
584 
585  if (ImGui::BeginTabItem("Info"))
586  {
587  itsRefreshCfgList = true;
588  drawInfo();
589  ImGui::EndTabItem();
590  }
591 
592  if (ImGui::BeginTabItem("Parameters"))
593  {
594  itsRefreshCfgList = true;
595  drawParameters();
596  ImGui::EndTabItem();
597  }
598 
599  if (ImGui::BeginTabItem("Console"))
600  {
601  itsRefreshCfgList = true;
602  drawConsole();
603  ImGui::EndTabItem();
604  }
605 
606  if (ImGui::BeginTabItem("Camera"))
607  {
608  itsRefreshCfgList = true;
609  drawCamCtrls();
610  ImGui::EndTabItem();
611  }
612 
613  if (ImGui::BeginTabItem("Config"))
614  {
615  drawCfgEditor();
616  ImGui::EndTabItem();
617  }
618 
619  if (ImGui::BeginTabItem("Code"))
620  {
621  itsRefreshCfgList = true;
622  drawCodeEditor();
623  ImGui::EndTabItem();
624  }
625 
626  if (ImGui::BeginTabItem("System"))
627  {
628  itsRefreshCfgList = true;
629  drawSystem();
630  ImGui::EndTabItem();
631  }
632 
633  if (ImGui::BeginTabItem("Tweaks"))
634  {
635  itsRefreshCfgList = true;
636  drawTweaks();
637  ImGui::EndTabItem();
638  }
639  ImGui::EndTabBar();
640  }
641  ImGui::End();
642  }
643  else ImGui::End(); // do not draw anything if window is collapsed
644 
645  // Show style editor window, imgui demo, etc if user requested it:
646  if (itsShowStyleEditor)
647  {
648  ImGui::Begin("GUI Style Editor", &itsShowStyleEditor);
649  ImGui::ShowStyleEditor();
650  ImGui::End();
651  }
652  if (itsShowAppMetrics) ImGui::ShowMetricsWindow(&itsShowAppMetrics);
653  if (itsShowImGuiDemo) ImGui::ShowDemoWindow(&itsShowImGuiDemo);
654 
655  // Draw an error popup, if any exception was received through reportError():
656  drawErrorPopup();
657 }
658 
659 // ##############################################################################################################
661 {
662  if (ImGui::BeginMenuBar())
663  {
664  if (ImGui::BeginMenu("File"))
665  {
666  if (ImGui::MenuItem("Quit")) engine()->quit();
667 
668  //ShowExampleMenuFile();
669  ImGui::EndMenu();
670  }
671  /*
672  if (ImGui::BeginMenu("Machine Vision Module"))
673  {
674  auto e = engine();
675 
676  // Start with the JeVois-Pro mappings:
677  size_t idx = 0;
678  e->foreachVideoMapping([&idx,&e](jevois::VideoMapping const & m) {
679  if (m.ofmt == JEVOISPRO_FMT_GUI && ImGui::MenuItem(m.str().c_str()))
680  e->requestSetFormat(idx++); else ++idx;
681  });
682  ImGui::Separator();
683 
684  // Then the compatibility JeVois modules:
685  idx = 0;
686  e->foreachVideoMapping([&idx,&e](jevois::VideoMapping const & m) {
687  if (m.ofmt != 0 && m.ofmt != JEVOISPRO_FMT_GUI && ImGui::MenuItem(m.str().c_str()))
688  e->requestSetFormat(idx++); else ++idx;
689  });
690 
691  ImGui::Separator();
692  // Finally the no-USB modules: FIXME - those currently kill the gui since no startFrame() is emitted
693  idx = 0;
694  e->foreachVideoMapping([&idx,&e](jevois::VideoMapping const & m) {
695  if (m.ofmt == 0 && ImGui::MenuItem(m.str().c_str()))
696  e->requestSetFormat(idx++); else ++idx;
697  });
698 
699  ImGui::EndMenu();
700  }
701  */
702  if (ImGui::BeginMenu("Tools"))
703  {
704  ImGui::MenuItem("ImGui Style Editor", NULL, &itsShowStyleEditor);
705  ImGui::MenuItem("ImGui Metrics/Debugger", NULL, &itsShowAppMetrics);
706  ImGui::MenuItem("ImGui Demo/Doc", NULL, &itsShowImGuiDemo);
707 
708  ImGui::EndMenu();
709  }
710 
711  ImGui::EndMenuBar();
712  }
713 }
714 
715 // ##############################################################################################################
717 {
718  static std::map<std::string, size_t> mods;
719  auto e = engine();
720  static std::string currstr;
721 
722  // Refresh the list of modules if needed:
723  ImGui::AlignTextToFramePadding();
724  ImGui::Text("Module:");
725  ImGui::SameLine();
726  ImGui::SetNextItemWidth(6 * ImGui::GetFontSize() + 5);
727  if (ImGui::Combo("##typemachinevisionmodule", &itsVideoMappingListType, "Pro/GUI\0Legacy\0Headless\0\0")
728  || currstr.empty() || itsRefreshVideoMappings)
729  {
730  // Recompute mods for the selected type:
731  itsRefreshVideoMappings = false;
732  mods.clear();
733 
734  switch (itsVideoMappingListType)
735  {
736  case 0:
737  {
738  // JeVois-Pro GUI mappings:
739  size_t idx = 0;
740  e->foreachVideoMapping([&idx](jevois::VideoMapping const & m) {
741  if (m.ofmt == JEVOISPRO_FMT_GUI) mods[m.menustr()] = idx;
742  ++idx;
743  });
744  }
745  break;
746 
747  case 1:
748  {
749  // Legacy JeVois mappings:
750  size_t idx = 0;
751  e->foreachVideoMapping([&idx](jevois::VideoMapping const & m) {
752  if (m.ofmt != 0 && m.ofmt != JEVOISPRO_FMT_GUI) mods[m.menustr()] = idx;
753  ++idx;
754  });
755  }
756  break;
757 
758  case 2:
759  {
760  // Headless modules
761  size_t idx = 0;
762  e->foreachVideoMapping([&idx](jevois::VideoMapping const & m) {
763  if (m.ofmt == 0) mods[m.menustr()] = idx;
764  ++idx;
765  });
766  }
767  break;
768 
769  default: LFATAL("Internal error, itsVideoMappingListType=" << itsVideoMappingListType);
770  }
771 
772  // Refresh our display string:
773  currstr = e->getCurrentVideoMapping().menustr().c_str();
774  }
775 
776  // Draw the module selector and allow selection:
777  ImGui::SameLine();
778 
779  if (ImGui::BeginCombo("##machinevisionmodule", currstr.c_str()))
780  {
781  for (auto const & m : mods)
782  {
783  bool is_selected = false;
784  if (ImGui::Selectable(m.first.c_str(), is_selected))
785  {
786  e->requestSetFormat(m.second);
787  currstr.clear();
788  }
789 
790  }
791  ImGui::EndCombo();
792  }
793 }
794 
795 // ##############################################################################################################
797 {
798  std::shared_ptr<jevois::Module> m = engine()->module();
799  if (m)
800  {
801  // Get the icon and display it:
802  if (itsIcon.loaded() == false)
803  try { itsIcon.load(m->absolutePath("icon.png")); }
804  catch (...)
805  {
807 
808  // Load a default C++ or Python icon:
809  try
810  {
811  if (engine()->getCurrentVideoMapping().ispython) itsIcon.load(JEVOIS_SHARE_PATH "/icons/py.png");
812  else itsIcon.load(JEVOIS_SHARE_PATH "/icons/cpp.png");
813  }
814  catch (...)
815  {
816  // Defaults icons not found, just use a blank:
817  cv::Mat blank(32, 32, CV_8UC4, 0);
818  itsIcon.load(blank);
819  }
820  }
821 
822  if (itsIcon.loaded())
823  {
824  int const siz = ImGui::CalcTextSize(" ").x;
825  itsIcon.draw(ImGui::GetCursorScreenPos(), ImVec2(siz, siz));
826  }
827 
828  // Get the html doc if we have not yet parsed it:
829  if (itsModName.empty())
830  {
831  std::string fname = m->absolutePath("modinfo.html");
832  std::ifstream ifs(fname);
833  if (ifs.is_open() == false)
834  itsModAuth = ("Cannot read file: " + fname).c_str();
835  else
836  {
837  int state = 0;
838  for (std::string s; std::getline(ifs, s); )
839  switch (state)
840  {
841  case 0: // Looking for module display name
842  {
843  std::string const str = jevois::extractString(s, "<td class=modinfoname>", "</td>");
844  if (str.empty() == false) { itsModName = str; ++state; }
845  break;
846  }
847 
848  case 1: // Looking for short synopsis
849  {
850  std::string const str = jevois::extractString(s, "<td class=modinfosynopsis>", "</td>");
851  if (str.empty() == false) { itsModDesc = str; ++state; }
852  break;
853  }
854 
855  case 2: // Looking for author info
856  {
857  std::string const str = jevois::extractString(s, "<table class=modinfoauth width=100%>", "</table>");
858  if (str.empty() == false)
859  {
860  itsModAuth = jevois::join(jevois::split(str, "<[^<]*>"), " ").substr(2); // remove HTML tags
861  ++state;
862  }
863  break;
864  }
865 
866  case 3: // Looking for language info
867  {
868  std::string const str = jevois::extractString(s, "<table class=moduledata>", "</table>");
869  if (str.empty() == false)
870  {
871  itsModLang = jevois::join(jevois::split(str, "<[^<]*>"), " "); // remove HTML tags
872  auto tok = jevois::split(itsModLang, "&nbsp;");
873  if (tok.size() >= 3)
874  {
875  if (jevois::stringStartsWith(tok[2], "C++")) itsModLang = "Language: C++";
876  else itsModLang = "Language: Python";
877  }
878  else itsModLang = "Language: Unknown";
879  ++state;
880  }
881  break;
882  }
883 
884  case 4: // Looking for main doc start
885  {
886  std::string const str = jevois::extractString(s, "<td class=modinfodesc>", "");
887  if (str.empty() == false)
888  {
889  std::string str2 = jevois::extractString(str, "<div class=\"textblock\">", "");
890  str2 = jevois::join(jevois::split(str2, "<[^<]*>"), " ");
891  size_t idx = str2.find_first_not_of(" "); if (idx != str2.npos) str2 = str2.substr(idx);
892  itsModDoc.emplace_back(str2);
893  ++state;
894  }
895  break;
896  }
897 
898  case 5: // Extracting main doc until its end marker is encountered
899  {
900  if (s == "</div></td></tr>") ++state;
901  else
902  {
903  std::string ss = jevois::join(jevois::split(s, "<[^<]*>"), " ");
904  std::string prefix;
905 
906  if (s.find("/h1>") != s.npos || s.find("/h2>") != s.npos || s.find("/h3>") != s.npos) prefix = "* ";
907  if (s.find("<li>") != s.npos) prefix = "- ";
908  ss = prefix + ss;
909 
910  // Finally fix multiple spaces:
911  // https://stackoverflow.com/questions/8362094/replace-multiple-spaces-with-one-space-in-a-string
912  std::string::iterator new_end =
913  std::unique(ss.begin(), ss.end(), [](char lhs, char rhs) { return (lhs == rhs) && (lhs == ' '); });
914  ss.erase(new_end, ss.end());
915  size_t idx = ss.find_first_not_of(" "); if (idx != ss.npos) ss = ss.substr(idx);
916  itsModDoc.push_back(ss);
917  }
918  }
919 
920  default: break;
921  }
922  ifs.close();
923  }
924  }
925 
926  // Display the doc:
927  ImGui::TextUnformatted((" " + itsModName).c_str());
928  ImGui::TextUnformatted((" " + itsModDesc).c_str());
929  ImGui::TextUnformatted((" " + itsModAuth + " " + itsModLang).c_str());
930  ImGui::TextUnformatted(" ");
931 
932  int wrap = ImGui::GetCursorPos().x + ImGui::GetWindowSize().x - ImGui::GetFontSize() * 2.0f;
933  if (wrap < 200) wrap = 200;
934  ImGui::PushTextWrapPos(wrap);
935 
936  bool show = true;
937  for (std::string const & s : itsModDoc)
938  {
939  // Create collapsible header and get its collapsed status:
940  if (jevois::stringStartsWith(s, "* "))
941  show = ImGui::CollapsingHeader(s.c_str() + 2, ImGuiTreeNodeFlags_DefaultOpen);
942  else if (show)
943  {
944  // If header not collapsed, show data:
945  if (jevois::stringStartsWith(s, "- ")) ImGui::BulletText("%s", s.c_str() + 2);
946  else ImGui::TextUnformatted(s.c_str());
947  }
948  }
949  ImGui::PopTextWrapPos();
950  }
951  else
952  ImGui::TextUnformatted("No JeVois Module currently loaded.");
953 }
954 
955 // ##############################################################################################################
956 void jevois::GUIhelper::setparstr(std::string const & descriptor, std::string const & val)
957 {
958  try { engine()->setParamStringUnique(descriptor, val); }
959  catch (...) { reportError(jevois::warnAndIgnoreException()); }
960  // drawGUI() will call drawErrorPopup() which will show the popup
961 }
962 
963 // ##############################################################################################################
965 {
966  static bool show_frozen = true; static bool show_system = false;
967 
968  toggleButton("Show Frozen Parameters", &show_frozen);
969  ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::CalcTextSize("Show System Parameters").x - 30.0f);
970  toggleButton("Show System Parameters", &show_system);
971 
972  jevois::Engine * e = engine();
973  jevois::Component * c; if (show_system) c = e; else c = e->module().get();
974 
975  // Stop here if we want to show module params but we have no module:
976  if (c == nullptr) { ImGui::TextUnformatted("No module loaded."); return; }
977 
978  // Record any ambiguous parameter names, so we can prefix them by component name:
979  std::set<std::string> pnames, ambig;
980 
981  // Get all the parameter summaries:
982  std::map<std::string /* categ */, std::vector<jevois::ParameterSummary>> psm;
983  c->foreachParam([this, &psm, &pnames, &ambig](std::string const &, jevois::ParameterBase * p) {
984  jevois::ParameterSummary psum = p->summary();
985  if (pnames.insert(psum.name).second == false) ambig.insert(psum.name);
986  psm[psum.category].push_back(std::move(psum)); } );
987 
988  // Stop here if no params to display:
989  if (psm.empty()) { ImGui::Text("This module has no parameters."); return; }
990 
991  // Create a collapsing header for each parameter category:
992  int widgetnum = 0; // we need a unique ID for each widget
993  float maxlen = 0.0f;
994  for (auto const & pp : psm)
995  {
996  // Do not even show a header if all params under it are frozen and we do not want to show frozen params:
997  if (show_frozen == false)
998  {
999  bool all_frozen = true;
1000  for (auto const & ps : pp.second) if (ps.frozen == false) { all_frozen = false; break; }
1001  if (all_frozen) continue;
1002  }
1003 
1004  // Show a header for this category:
1005  if (ImGui::CollapsingHeader(pp.first.c_str()))
1006  {
1007  ImGui::Columns(3, "parameters");
1008 
1009  // Create a widget for each param:
1010  for (auto const & ps : pp.second)
1011  {
1012  // Skip if frozen and we do not want to show frozen params:
1013  if (ps.frozen && show_frozen == false) continue;
1014 
1015  // We need a unique ID for each ImGui widget, and we will use no visible widget name:
1016  static char wname[16]; snprintf(wname, 16, "##p%d", widgetnum);
1017  bool reset = true; // will set to false if we do not want a reset button
1018 
1019  // Start with parameter name and a tooltip with its descriptor:
1020  ImGui::AlignTextToFramePadding();
1021  std::string nam = ps.name;
1022  if (ambig.contains(nam))
1023  {
1024  // Here we are only going to disambiguate by the owning component name:
1025  auto tok = jevois::split(ps.descriptor, ":");
1026  if (tok.size() >= 2) nam = tok[tok.size()-2] + ':' + nam;
1027  }
1028 
1029  ImGui::TextUnformatted(nam.c_str());
1030  if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", ps.descriptor.c_str());
1031  maxlen = std::max(maxlen, ImGui::CalcTextSize(nam.c_str()).x);
1032 
1033  // Add a tooltip:
1034  ImGui::NextColumn();
1035  ImGui::AlignTextToFramePadding();
1036  std::string const & vtype = ps.valuetype;
1037  if (jevois::stringStartsWith(ps.validvalues, "None:"))
1038  helpMarker(ps.description.c_str(), ("Parameter type: " + vtype).c_str());
1039  else
1040  helpMarker(ps.description.c_str(), ("Parameter type: " + vtype).c_str(),
1041  ("Allowed values: " + ps.validvalues).c_str());
1042 
1043  // Now a widget for the parameter:
1044  ImGui::NextColumn();
1045  bool const is_uchar = (vtype == "unsigned char");
1046  bool const is_int = (vtype == "short" || vtype == "int" || vtype == "long int" || vtype == "long long int");
1047  bool const is_uint = (is_uchar || vtype == "unsigned short" || vtype == "unsigned int" ||
1048  vtype == "unsigned long int" || vtype == "unsigned long long int" || vtype == "size_t");
1049  bool const is_real = (vtype == "float" || vtype == "double" || vtype == "long double");
1050 
1051  // Grey out the item if it is disabled:
1052  if (ps.frozen)
1053  {
1054  ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
1055  ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
1056  }
1057 
1058  // ----------------------------------------------------------------------
1059  if (jevois::stringStartsWith(ps.validvalues, "List:["))
1060  {
1061  std::string vals = ps.validvalues.substr(6, ps.validvalues.size() - 7);
1062  auto vv = jevois::split(vals, "\\|");
1063 
1064  if (vv.empty() == false)
1065  {
1066  // Find the index of the current value:
1067  int index = 0; for (auto const & v : vv) if (v == ps.value) break; else ++index;
1068  if (ImGui::Combo(wname, &index,
1069  [](void * vec, int idx, const char ** out_text)
1070  {
1071  auto & ve = *static_cast<std::vector<std::string>*>(vec);
1072  if (idx < 0 || idx >= static_cast<int>(ve.size())) return false;
1073  *out_text = ve.at(idx).c_str();
1074  return true;
1075  },
1076  static_cast<void *>(&vv), vv.size()))
1077  setparstr(ps.descriptor, vv[index]);
1078  }
1079  }
1080  // ----------------------------------------------------------------------
1081  else if (is_uchar || jevois::stringStartsWith(ps.validvalues, "Range:["))
1082  {
1083  if (is_uchar)
1084  {
1085  // For unsigned char, use a slider:
1086  unsigned long val; jevois::paramStringToVal(ps.value, val);
1087  std::string rng = ps.validvalues.substr(7, ps.validvalues.size() - 8);
1088 
1089  // We may or not have a range spec, if not, use 0..255:
1090  if (rng.empty())
1091  {
1092  long mi = 0, ma = 255;
1093  if (ImGui::SliderScalar(wname, ImGuiDataType_U64, &val, &mi, &ma))
1094  setparstr(ps.descriptor, std::to_string(val));
1095  }
1096  else
1097  {
1099  if (ImGui::SliderScalar(wname, ImGuiDataType_U64, &val, &r.min(), &r.max()))
1100  setparstr(ps.descriptor, std::to_string(val));
1101  }
1102  }
1103  else if (is_uint)
1104  {
1105  // For uint that has a range specified, use a slider:
1106  std::string rng = ps.validvalues.substr(7, ps.validvalues.size() - 8);
1108  unsigned long val; jevois::paramStringToVal(ps.value, val);
1109  if (ImGui::SliderScalar(wname, ImGuiDataType_U64, &val, &r.min(), &r.max()))
1110  setparstr(ps.descriptor, std::to_string(val));
1111  }
1112  else if (is_int)
1113  {
1114  // For int that has a range specified, use a slider:
1115  std::string rng = ps.validvalues.substr(7, ps.validvalues.size() - 8);
1117  long val; jevois::paramStringToVal(ps.value, val);
1118  if (ImGui::SliderScalar(wname, ImGuiDataType_S64, &val, &r.min(), &r.max()))
1119  setparstr(ps.descriptor, std::to_string(val));
1120  }
1121  else if (is_real)
1122  {
1123  // For real with a range specified, use a slider:
1124  std::string rng = ps.validvalues.substr(7, ps.validvalues.size() - 8);
1126  double val; jevois::paramStringToVal(ps.value, val);
1127  if (ImGui::SliderScalar(wname, ImGuiDataType_Double, &val, &r.min(), &r.max()))
1128  setparstr(ps.descriptor, std::to_string(val));
1129  }
1130  else
1131  {
1132  // For more complex types, just allow free typing, parameter will do the checking:
1133  char buf[256]; strncpy(buf, ps.value.c_str(), sizeof(buf)-1);
1134  if (ImGui::InputText(wname, buf, sizeof(buf), ImGuiInputTextFlags_EnterReturnsTrue))
1135  setparstr(ps.descriptor, buf);
1136  }
1137  }
1138  // ----------------------------------------------------------------------
1139  else if (vtype == "jevois::Range<unsigned char>")
1140  {
1141  // For a range parameter, use a double drag:
1143  int mi = val.min(), ma = val.max();
1144  if (ImGui::DragIntRange2(wname, &mi, &ma, 0, 0, 255, "Min: %d", "Max: %d"))
1145  setparval(ps.descriptor, jevois::Range<unsigned char>(mi, ma));
1146  }
1147  // ----------------------------------------------------------------------
1148  else if (vtype == "bool")
1149  {
1150  bool val; jevois::paramStringToVal(ps.value, val);
1151  if (ImGui::Checkbox(wname, &val)) setparval(ps.descriptor, val);
1152  }
1153  // ----------------------------------------------------------------------
1154  else if (vtype == "ImColor")
1155  {
1156  ImColor val; jevois::paramStringToVal(ps.value, val);
1157  if (ImGui::ColorEdit4(wname, (float *)&val, ImGuiColorEditFlags_AlphaPreview)) setparval(ps.descriptor, val);
1158  }
1159  // ----------------------------------------------------------------------
1160  else
1161  {
1162  // User will type in some value, parameter will check it:
1163  char buf[256]; strncpy(buf, ps.value.c_str(), sizeof(buf)-1);
1164  if (ImGui::InputText(wname, buf, sizeof(buf), ImGuiInputTextFlags_EnterReturnsTrue))
1165  setparstr(ps.descriptor, buf);
1166  }
1167 
1168  // Possibly add a reset button:
1169  if (reset)
1170  {
1171  static char rname[18]; snprintf(rname, 18, "Reset##%d", widgetnum);
1172  ImGui::SameLine();
1173  if (ImGui::Button(rname)) setparstr(ps.descriptor, ps.defaultvalue);
1174  }
1175 
1176  // Restore any grey out:
1177  if (ps.frozen)
1178  {
1179  ImGui::PopItemFlag();
1180  ImGui::PopStyleVar();
1181  }
1182 
1183  // Ready for next row:
1184  ImGui::NextColumn(); ++widgetnum;
1185  }
1186  ImGui::SetColumnWidth(0, maxlen + 30.0f);
1187  ImGui::SetColumnWidth(1, ImGui::CalcTextSize("(?)").x + 30.0f);
1188  ImGui::SetColumnWidth(2, 1000);
1189 
1190  // Back to single column before the next param categ:
1191  ImGui::Columns(1);
1192  }
1193  }
1194 }
1195 
1196 // ##############################################################################################################
1198 {
1199  jevois::Engine * e = engine();
1200 
1201  // Start with toggle buttons for serlog and serout:
1202  bool slusb = false, slhard = false, schanged = false;
1203  auto sl = e->getParamValUnique<jevois::engine::SerPort>("engine:serlog");
1204  switch (sl)
1205  {
1206  case jevois::engine::SerPort::None: slusb = false; slhard = false; break;
1207  case jevois::engine::SerPort::All: slusb = true; slhard = true; break;
1208  case jevois::engine::SerPort::Hard: slusb = false; slhard = true; break;
1209  case jevois::engine::SerPort::USB: slusb = true; slhard = false; break;
1210  }
1211  ImGui::AlignTextToFramePadding();
1212  ImGui::Text("Log messages:");
1213  ImGui::SameLine(); if (toggleButton("USB##serlogu", &slusb)) schanged = true;
1214  ImGui::SameLine(); if (toggleButton("Hard##serlogh", &slhard)) schanged = true;
1215  ImGui::SameLine(); toggleButton("Cons##serlogc", &itsSerLogEnabled);
1216  ImGui::SameLine(0, 50);
1217 
1218  bool sousb = false, sohard = false;
1219  auto so = e->getParamValUnique<jevois::engine::SerPort>("engine:serout");
1220  switch (so)
1221  {
1222  case jevois::engine::SerPort::None: sousb = false; sohard = false; break;
1223  case jevois::engine::SerPort::All: sousb = true; sohard = true; break;
1224  case jevois::engine::SerPort::Hard: sousb = false; sohard = true; break;
1225  case jevois::engine::SerPort::USB: sousb = true; sohard = false; break;
1226  }
1227  ImGui::Text("Module output:");
1228  ImGui::SameLine(); if (toggleButton("USB##seroutu", &sousb)) schanged = true;
1229  ImGui::SameLine(); if (toggleButton("Hard##serouth", &sohard)) schanged = true;
1230  ImGui::SameLine(); toggleButton("Cons##seroutc", &itsSerOutEnabled);
1231 
1232  if (schanged)
1233  {
1234  if (slusb)
1235  {
1236  if (slhard) e->setParamValUnique("engine:serlog", jevois::engine::SerPort::All);
1237  else e->setParamValUnique("engine:serlog", jevois::engine::SerPort::USB);
1238  }
1239  else
1240  {
1241  if (slhard) e->setParamValUnique("engine:serlog", jevois::engine::SerPort::Hard);
1242  else e->setParamValUnique("engine:serlog", jevois::engine::SerPort::None);
1243  }
1244 
1245  if (sousb)
1246  {
1247  if (sohard) e->setParamValUnique("engine:serout", jevois::engine::SerPort::All);
1248  else e->setParamValUnique("engine:serout", jevois::engine::SerPort::USB);
1249  }
1250  else
1251  {
1252  if (sohard) e->setParamValUnique("engine:serout", jevois::engine::SerPort::Hard);
1253  else e->setParamValUnique("engine:serout", jevois::engine::SerPort::None);
1254  }
1255  }
1256 
1257  // Now a combo for serstyle:
1258  auto m = dynamic_cast<jevois::StdModule *>(e->module().get());
1259  if (m)
1260  {
1261  auto sp = m->getParamValUnique<jevois::module::SerStyle>("serstyle");
1262  int idx = 0; for (auto const & v : jevois::module::SerStyle_Values) if (v == sp) break; else ++idx;
1263 
1264  ImGui::SameLine();
1265  if (ImGui::Combo("serstyle", &idx,
1266  [](void * vec, int idx, const char ** out_text)
1267  {
1268  auto & ve = *static_cast<std::vector<std::string>*>(vec);
1269  if (idx < 0 || idx >= static_cast<int>(ve.size())) return false;
1270  *out_text = ve.at(idx).c_str();
1271  return true;
1272  },
1273  const_cast<void *>(static_cast<void const *>(&jevois::module::SerStyle_Strings)),
1274  jevois::module::SerStyle_Strings.size()))
1275  try { e->setParamValUnique("serstyle", jevois::module::SerStyle_Values[idx]); }
1276  catch (...) { jevois::warnAndIgnoreException(); }
1277  }
1278  ImGui::Separator();
1279 
1280  // Now a console:
1281  auto c = e->getComponent<jevois::GUIconsole>("guiconsole");
1282  if (c) c->draw();
1283 }
1284 
1285 // ##############################################################################################################
1287 { return itsSerLogEnabled; }
1288 
1289 // ##############################################################################################################
1291 { return itsSerOutEnabled; }
1292 
1293 // ##############################################################################################################
1295 {
1296  engine()->drawCameraGUI();
1297 }
1298 
1299 // ##############################################################################################################
1300 int jevois::GUIhelper::modal(std::string const & title, char const * text, bool & dont_ask_me_next_time,
1301  char const * b1txt, char const * b2txt)
1302 {
1303  int ret = 0;
1304 
1305  // Open the modal if needed, and remember it:
1306  if (itsOpenModals.find(title) == itsOpenModals.end())
1307  {
1308  ImGui::OpenPopup(title.c_str());
1309  itsOpenModals.insert(title);
1310  }
1311 
1312  // Display the modal and get any button clicks:
1313  if (ImGui::BeginPopupModal(title.c_str(), NULL, ImGuiWindowFlags_AlwaysAutoResize))
1314  {
1315  ImGui::TextUnformatted(text);
1316  ImGui::Separator();
1317  ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
1318  ImGui::Checkbox("Don't ask me next time", &dont_ask_me_next_time);
1319  ImGui::PopStyleVar();
1320 
1321  if (ImGui::Button(b1txt, ImVec2(120, 0))) ret = 1;
1322  ImGui::SetItemDefaultFocus();
1323  ImGui::SameLine();
1324  if (ImGui::Button(b2txt, ImVec2(120, 0))) ret = 2;
1325  ImGui::EndPopup();
1326  }
1327 
1328  // Close and remove from our set of open modals if a button was clicked:
1329  if (ret)
1330  {
1331  ImGui::CloseCurrentPopup();
1332  itsOpenModals.erase(title);
1333  }
1334 
1335  return ret;
1336 }
1337 
1338 // ##############################################################################################################
1339 void jevois::GUIhelper::loadEditorFile(TextEditor & e, std::string const & fn, std::string const & failtxt, bool failro)
1340 {
1341  LINFO("Loading " << fn << " ...");
1342 
1343  std::ifstream t(fn);
1344  if (t.good())
1345  {
1346  std::string str((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
1347  e.SetText(str);
1348  e.SetReadOnly(false);
1349  }
1350  else
1351  {
1352  e.SetText(failtxt);
1353  e.SetReadOnly(failro);
1354  }
1355 }
1356 
1357 // ##############################################################################################################
1358 void jevois::GUIhelper::saveEditorFile(TextEditor & e, std::string const & fn)
1359 {
1360  LINFO("Saving " << fn << " ...");
1361  std::ofstream os(fn);
1362  if (os.is_open() == false) { reportError("Cannot write " + fn); return; }
1363 
1364  std::string const txt = e.GetText();
1365  os << txt;
1366 
1367  // Mark as un-edited:
1368  e.SetEdited(false);
1369 }
1370 
1371 // ##############################################################################################################
1373 {
1374  if (ImGui::BeginMenuBar())
1375  {
1376  if (ImGui::BeginMenu("Edit"))
1377  {
1378  bool ro = e.IsReadOnly();
1379 
1380  ImGui::Separator();
1381  if (ImGui::MenuItem("Read-only mode", nullptr, &ro)) e.SetReadOnly(ro);
1382  ImGui::Separator();
1383 
1384  if (ImGui::MenuItem("Undo", "ALT-Backspace", nullptr, !ro && e.CanUndo())) e.Undo();
1385  if (ImGui::MenuItem("Redo", "Ctrl-Y", nullptr, !ro && e.CanRedo())) e.Redo();
1386 
1387  ImGui::Separator();
1388 
1389  if (ImGui::MenuItem("Copy", "Ctrl-C", nullptr, e.HasSelection())) e.Copy();
1390  if (ImGui::MenuItem("Cut", "Ctrl-X", nullptr, !ro && e.HasSelection())) e.Cut();
1391  if (ImGui::MenuItem("Delete", "Del", nullptr, !ro && e.HasSelection())) e.Delete();
1392  if (ImGui::MenuItem("Paste", "Ctrl-V", nullptr, !ro && ImGui::GetClipboardText()!=nullptr)) e.Paste();
1393 
1394  ImGui::Separator();
1395 
1396  if (ImGui::MenuItem("Select all", nullptr, nullptr))
1397  e.SetSelection(TextEditor::Coordinates(), TextEditor::Coordinates(e.GetTotalLines(), 0));
1398 
1399  ImGui::EndMenu();
1400  }
1401  ImGui::EndMenuBar();
1402  }
1403 }
1404 
1405 // ##############################################################################################################
1406 void jevois::GUIhelper::drawEditor(TextEditor & e)
1407 {
1408  auto cpos = e.GetCursorPosition();
1409 
1410  ImGui::Text("%6d/%-6d %6d lines | %s | %s | %s", cpos.mLine + 1, cpos.mColumn + 1, e.GetTotalLines(),
1411  e.IsOverwrite() ? "Ovr" : "Ins",
1412  e.IsEdited() ? "*" : " ",
1413  e.GetLanguageDefinition().mName.c_str());
1414 
1415  e.Render("JeVois-Pro Configuration Editor");
1416 }
1417 
1418 // ##############################################################################################################
1420 {
1421  // Refresh our list of files if needed:
1422  if (itsRefreshCfgList)
1423  {
1424  // Remove all dynamic files:
1425  while (itsCfgItems.size() > itsNumFixedCfgItems) itsCfgItems.pop_back();
1426 
1427  // Rescan the custom dnn directory:
1428  DIR * dirp = opendir(JEVOIS_CUSTOM_DNN_PATH);
1429  if (dirp)
1430  {
1431  struct dirent * dent;
1432  while ((dent = readdir(dirp)) != nullptr)
1433  {
1434  std::string const node = dent->d_name;
1435  size_t const nl = node.length();
1436  if ((nl > 4 && node.substr(nl-4) == ".yml") || (nl > 5 && node.substr(nl-5) == ".yaml"))
1437  itsCfgItems.emplace_back(CfgItem { jevois::absolutePath(JEVOIS_CUSTOM_DNN_PATH, node),
1438  "Custom DNN " + node,
1439  "",
1440  CfgSaveAction::Reload });
1441  }
1442  closedir(dirp);
1443  }
1444  itsRefreshCfgList = false;
1445 
1446  // If the currently open file is not in our list anymore, load file 0:
1447  if (itsCfgCurrentItem >= int(itsNumFixedCfgItems)) { itsCfgNewItem = 0; itsCfgWantLoad = true;}
1448  }
1449 
1450  // Create combo entries for imgui:
1451  char const * items[itsCfgItems.size()];
1452  for (int i = 0; CfgItem const & c : itsCfgItems) items[i++] = c.displayname.c_str();
1453 
1454  // Check if the user is trying to select a different file:
1455  if (ImGui::Combo("##cfgeditorcombo", &itsCfgNewItem, items, itsCfgItems.size())) itsCfgWantLoad = true;
1456 
1457  // Want to load a new file? check if we need to save the current one first:
1458  if (itsCfgWantLoad && itsCfgWantAction == false)
1459  {
1460  if (itsCfgEditor.IsEdited())
1461  {
1462  static bool discard_edits_dont_ask = false;
1463  int ret = modal("Discard edits?", "File was edited. Discard all edits? This cannot be undone.",
1464  discard_edits_dont_ask, "Discard", "Save");
1465  switch (ret)
1466  {
1467  case 0: break; // Need to wait
1468  case 1: itsCfgWantAction = false; itsCfgOkToLoad = true; break; // Discard selected
1469  case 2: saveEditorFile(itsCfgEditor, itsCfgFileName); itsCfgWantAction = true; break; // save selected
1470  default: break;
1471  }
1472  }
1473  else
1474  {
1475  itsCfgWantLoad = false;
1476  itsCfgOkToLoad = true;
1477  }
1478  }
1479 
1480  // Need to execute an action after a save?
1481  if (itsCfgWantAction)
1482  {
1483  switch (itsCfgCurrentAction)
1484  {
1485  case CfgSaveAction::None:
1486  itsCfgWantAction = false;
1487  break;
1488 
1489  case CfgSaveAction::Reload:
1490  {
1491  static bool reload_dont_ask = false;
1492  int ret = modal("Reload Module?", "Reload Machine Vision Module for changes to take effect?",
1493  reload_dont_ask, "Reload", "Later");
1494  switch (ret)
1495  {
1496  case 0: break; // Need to wait
1497  case 1: // Reload selected
1498  engine()->requestSetFormat(-1);
1499  itsCfgWantAction = false;
1500  itsCfgOkToLoad = itsCfgWantLoad;
1501  break;
1502 
1503  case 2: // Later selected: we don't want action anymore
1504  itsCfgWantAction = false;
1505  itsCfgOkToLoad = itsCfgWantLoad;
1506  break;
1507 
1508  default: break;
1509  }
1510  }
1511  break;
1512 
1513  case CfgSaveAction::Reboot:
1514  {
1515  static bool reboot_dont_ask = false;
1516  int ret = modal("Restart?", "Restart JeVois-Pro for changes to take effect?",
1517  reboot_dont_ask, "Restart", "Later");
1518  switch (ret)
1519  {
1520  case 0: break; // Need to wait
1521 
1522  case 1: // Reboot selected
1523  engine()->reboot();
1524  itsCfgWantAction = false;
1525  break;
1526 
1527  case 2: // Later selected: we don't want action anymore
1528  itsCfgWantAction = false;
1529  break;
1530 
1531  default: break;
1532  }
1533  }
1534  break;
1535 
1536  case CfgSaveAction::RefreshMappings:
1537  {
1538  engine()->reloadVideoMappings();
1539  itsRefreshVideoMappings = true;
1540  itsCfgWantAction = false;
1541  }
1542  break;
1543  }
1544  }
1545 
1546  // Ready to load a new file?
1547  if (itsCfgOkToLoad)
1548  {
1549  itsCfgOkToLoad = false;
1550  itsCfgCurrentItem = itsCfgNewItem;
1551  itsCfgCurrentAction = itsCfgItems[itsCfgCurrentItem].action;
1552 
1553  // If path is relative, make it within the module's path (if any):
1554  if (items[itsCfgCurrentItem][0] != '/')
1555  {
1556  auto m = engine()->module();
1557  if (m) itsCfgFileName = m->absolutePath(itsCfgItems[itsCfgCurrentItem].filename);
1558  else itsCfgFileName = itsCfgItems[itsCfgCurrentItem].filename;
1559  }
1560  else itsCfgFileName = itsCfgItems[itsCfgCurrentItem].filename;
1561 
1562  // Load the file:
1563  loadEditorFile(itsCfgEditor, itsCfgFileName, "", false);
1564  }
1565 
1566  // Draw a save button:
1567  ImGui::SameLine();
1568  ImGui::TextUnformatted(" "); ImGui::SameLine();
1569  if (ImGui::Button("Save")) { saveEditorFile(itsCfgEditor, itsCfgFileName); itsCfgWantAction = true; }
1570  ImGui::Separator();
1571 
1572  // Add a menu for editor actions:
1573  drawEditorMenu(itsCfgEditor);
1574 
1575  // Render the editor in a child window so it can scroll correctly:
1576  drawEditor(itsCfgEditor);
1577 }
1578 
1579 // ##############################################################################################################
1581 {
1582  // If we do not have a module, do nothing:
1583  std::shared_ptr<jevois::Module> m = engine()->module();
1584 
1585  if (!m)
1586  {
1587  itsCodeEditor.SetText("No machine vision module loaded.");
1588  itsCodeEditor.SetReadOnly(true);
1589  return;
1590  }
1591 
1592  // Load the code if needed:
1593  static bool ispython = false;
1594  if (itsCodeFileName.empty())
1595  {
1596  // Get module type:
1597  VideoMapping const & vm = engine()->getCurrentVideoMapping();
1598  itsCodeFileName = vm.srcpath();
1599  ispython = vm.ispython;
1600 
1601  // Set the language:
1602  if (ispython)
1603  {
1604  auto codelang = TextEditor::LanguageDefinition::Python();
1605  itsCodeEditor.SetLanguageDefinition(codelang);
1606  }
1607  else
1608  {
1609  auto codelang = TextEditor::LanguageDefinition::CPlusPlus();
1610  itsCodeEditor.SetLanguageDefinition(codelang);
1611  }
1612 
1613  // Load the file:
1614  loadEditorFile(itsCodeEditor, itsCodeFileName, "Failed to load " + itsCodeFileName, true);
1615  }
1616 
1617  if (ispython)
1618  {
1619  // Draw a save button:
1620  if (ImGui::Button("Save and Restart Module"))
1621  {
1622  saveEditorFile(itsCodeEditor, itsCodeFileName);
1623  engine()->requestSetFormat(-1);
1624  }
1625  ImGui::Separator();
1626  }
1627  else itsCodeEditor.SetReadOnly(true);
1628 
1629  // Add a menu for editor actions:
1630  drawEditorMenu(itsCodeEditor);
1631 
1632  // Render the editor in a child window so it can scroll correctly:
1633  drawEditor(itsCodeEditor);
1634 }
1635 
1636 // ##############################################################################################################
1637 void jevois::GUIhelper::newModEntry(char const * wname, std::string & str, char const * desc,
1638  char const * hint, char const * hlp)
1639 {
1640  static char buf[256];
1641  ImGui::AlignTextToFramePadding();
1642  ImGui::TextUnformatted(desc);
1643  ImGui::NextColumn();
1644  helpMarker(desc, hlp);
1645  ImGui::NextColumn();
1646  strncpy(buf, str.c_str(), sizeof(buf)-1);
1647  if (ImGui::InputTextWithHint(wname, hint, buf, sizeof(buf))) str = buf;
1648  ImGui::NextColumn();
1649 }
1650 
1651 // ##############################################################################################################
1653 {
1654  float const fontw = ImGui::GetFontSize();
1655 
1656  static int refresh = 1;
1657  static std::string cpu, mem, ver;
1658  static size_t npu, tpu, vpu; static int fan;
1659  if (--refresh == 0)
1660  {
1661  refresh = 60;
1662  cpu = jevois::getSysInfoCPU();
1663  mem = jevois::getSysInfoMem();
1664  ver = jevois::getSysInfoVersion();
1668  fan = jevois::getFanSpeed();
1669  }
1670  ImGui::Text("JeVois-Pro v%s -- %s", JEVOIS_VERSION_STRING, ver.c_str());
1671  ImGui::Text(cpu.c_str());
1672  ImGui::Text(mem.c_str());
1673  ImGui::Text("NPU: %d, TPU: %d, VPU: %d. Fan: %d%%", npu, tpu, vpu, fan);
1674  ImGui::Separator();
1675 
1676  // #################### Create new module:
1677  drawNewModuleForm();
1678  ImGui::Separator();
1679 
1680  // #################### ping:
1681  static std::string pingstr;
1682  static int showping = 0;
1683  if (ImGui::Button("Ping jevois.usc.edu"))
1684  {
1685  std::string ret = jevois::system("/usr/bin/ping -c 1 -w 2 jevois.usc.edu");
1686  std::vector<std::string> rvec = jevois::split(ret, "\n");
1687  if (rvec.size() < 2) reportError("Unable to ping jevois.usc.edu");
1688  else { pingstr = rvec[1]; showping = 60; }
1689  }
1690  if (showping)
1691  {
1692  ImGui::SameLine();
1693  ImGui::Text(pingstr.c_str());
1694  --showping;
1695  }
1696 
1697  ImGui::Separator();
1698 
1699  // #################### dnnget:
1700  static std::string zip;
1701  static std::string donestr;
1702  static int state = 0;
1703 
1704  ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue;
1705  if (state) flags |= ImGuiInputTextFlags_ReadOnly;
1706  ImGui::SetNextItemWidth(fontw * 6.0F);
1707  char buf[5] = { };
1708  if (ImGui::InputText("Load Custom DNN", buf, sizeof(buf), flags)) state = 1;
1709  ImGui::SameLine();
1710 
1711  switch (state)
1712  {
1713  case 0:
1714  ImGui::Text(" ");
1715  break;
1716 
1717  case 1:
1718  {
1719  ImGui::Text("-- Downloading...");
1720  zip = std::string(buf) + ".zip";
1721  itsDnnGetFut =
1723  {
1724  return jevois::system("/usr/bin/curl " JEVOIS_CUSTOM_DNN_URL "/" + zip + " -o "
1725  JEVOIS_CUSTOM_DNN_PATH "/" + zip, true);
1726  });
1727  state = 2;
1728  }
1729  break;
1730 
1731  case 2:
1732  {
1733  if (itsDnnGetFut.valid() == false) { reportError("Unknown error while loading custom DNN"); state = 0; break; }
1734  ImGui::Text("-- Downloading...");
1735  if (itsDnnGetFut.wait_for(std::chrono::microseconds(100)) == std::future_status::ready)
1736  {
1737  itsDnnGetFut.get();
1738 
1739  // Check that the file exists:
1740  std::ifstream ifs(JEVOIS_CUSTOM_DNN_PATH "/" + zip);
1741  if (ifs.is_open() == false)
1742  {
1743  reportError("Failed to download. Check network connectivity and available disk space.");
1744  state = 0;
1745  break;
1746  }
1747  itsDnnGetFut =
1749  {
1750  return jevois::system("/usr/bin/unzip -o " JEVOIS_CUSTOM_DNN_PATH "/" + zip +
1751  " -d " JEVOIS_CUSTOM_DNN_PATH, true);
1752  });
1753  state = 3;
1754  }
1755  }
1756  break;
1757 
1758  case 3:
1759  {
1760  if (itsDnnGetFut.valid() == false) { reportError("Unknown error while unpacking custom DNN"); state = 0; break; }
1761  ImGui::Text("-- Installing...");
1762  if (itsDnnGetFut.wait_for(std::chrono::microseconds(100)) == std::future_status::ready)
1763  {
1764  std::string ret = itsDnnGetFut.get();
1765  std::vector<std::string> rvec = jevois::split(ret, "\n");
1766  if (rvec.size() > 2 && jevois::stringStartsWith(rvec[1], " End-of-central-directory signature not found"))
1767  donestr = "-- Invalid file, check DNN download key.";
1768  else
1769  donestr = "-- Done. Reload model zoo to take effect.";
1770  jevois::system("/bin/rm " JEVOIS_CUSTOM_DNN_PATH "/" + zip, true);
1771  state = 4;
1772  }
1773  }
1774  break;
1775 
1776  case 200:
1777  ImGui::Text(" ");
1778  state = 0;
1779  break;
1780 
1781  default:
1782  ImGui::Text(donestr.c_str());
1783  ++state;
1784  }
1785  ImGui::Separator();
1786 
1787 #ifdef JEVOIS_PLATFORM
1788  // #################### boot mode:
1789  static int bootmode = 0;
1790  ImGui::AlignTextToFramePadding();
1791  ImGui::Text("On boot, start:");
1792  ImGui::SameLine();
1793  if (ImGui::Combo("##onboot", &bootmode, "(no change)\0JeVois-Pro\0Ubuntu Console\0Ubuntu Graphical\0\0"))
1794  {
1795  switch (bootmode)
1796  {
1797  case 0: break;
1798  case 2: jevois::system("systemctl --no-reload set-default multi-user.target"); break;
1799  case 3: jevois::system("systemctl --no-reload set-default graphical.target"); break;
1800  default: jevois::system("systemctl --no-reload set-default jevoispro.target"); break;
1801  }
1802  jevois::system("sync");
1803  }
1804  ImGui::Separator();
1805 #endif
1806 }
1807 
1808 // ##############################################################################################################
1809 namespace
1810 {
1811  unsigned int get_v4l2_fmt(int idx)
1812  {
1813  switch (idx)
1814  {
1815  case 0: return V4L2_PIX_FMT_YUYV;
1816  case 1: return V4L2_PIX_FMT_RGB24;
1817  case 2: return V4L2_PIX_FMT_RGB32;
1818  case 3: return V4L2_PIX_FMT_GREY;
1819  case 4: return V4L2_PIX_FMT_SBGGR16;
1820  default: return 0;
1821  }
1822  }
1823 }
1824 
1825 // ##############################################################################################################
1827 {
1828  float const fontw = ImGui::GetFontSize();
1829 
1830  ImGui::PushStyleColor(ImGuiCol_PopupBg, 0xf0e0ffe0);
1831 
1832  if (ImGui::Button("Create new machine vision module...")) ImGui::OpenPopup("Create new machine vision module");
1833 
1834  ImVec2 const center = ImGui::GetMainViewport()->GetCenter();
1835  ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
1836  ImGui::SetNextWindowContentSize(ImVec2(940, 730));
1837 
1838  if (ImGui::BeginPopupModal("Create new machine vision module", NULL, ImGuiWindowFlags_AlwaysAutoResize))
1839  {
1840  try
1841  {
1842  static std::string name, vendor, synopsis, author, email, website, license;
1843 
1844  ImGui::Text("Fill out the details below:");
1845  ImGui::Separator();
1846 
1847  ImGui::Columns(3, "new module");
1848 
1849  newModEntry("##NewModname", name, "Module Name", "MyModule",
1850  "Required. Must start with an uppercase letter. "
1851  "Will be a folder name under /jevoispro/modules/VendorName");
1852 
1853  newModEntry("##NewModvendor", vendor, "Vendor Name", "MyVendor",
1854  "Required. Must start with an uppercase letter. "
1855  "Will be a folder name under /jevoispro/modules/");
1856 
1857  newModEntry("##NewModsynopsis", synopsis, "Synopsis", "Detect Object of type X",
1858  "Optional. Brief description of what the module does.");
1859 
1860  newModEntry("##NewModauthor", author, "Author Name", "John Smith", "Optional");
1861 
1862  newModEntry("##NewModemail", email, "Author Email", "you@yourcompany.com", "Optional");
1863 
1864  newModEntry("##NewModwebsite", website, "Author Website", "http://yourcompany.com", "Optional");
1865 
1866  newModEntry("##NewModlicense", license, "License", "GPL v3", "Optional");
1867 
1868  // Language:
1869  static int language = 0;
1870  ImGui::AlignTextToFramePadding();
1871  ImGui::TextUnformatted("Module Language");
1872  ImGui::NextColumn();
1873  helpMarker("Module Language", "Machine language to use for your module.");
1874  ImGui::NextColumn();
1875  ImGui::Combo("##NewModlanguage", &language, "Python\0\0"); // FIXME
1876  //ImGui::Combo("##NewModlanguage", &language, "Python\0C++\0\0");
1877  ImGui::NextColumn();
1878 
1879  // Template:
1880  static int templ = 0;
1881  ImGui::AlignTextToFramePadding();
1882  ImGui::TextUnformatted("Module Template");
1883  ImGui::NextColumn();
1884  helpMarker("Module Template", "Type of placeholder code that will be provided to get your module started.");
1885  ImGui::NextColumn();
1886  ImGui::Combo("##NewModtemplate", &templ, "Pro/GUI\0Legacy\0Headless\0\0");
1887  ImGui::NextColumn();
1888 
1889  // Output video mapping:
1890  int oflags = ImGuiInputTextFlags_None;
1891  if (templ != 1)
1892  {
1893  oflags |= ImGuiInputTextFlags_ReadOnly;
1894  ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
1895  ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
1896  }
1897 
1898  static int ofmt = 0;
1899  ImGui::AlignTextToFramePadding();
1900  ImGui::TextUnformatted("Module Output");
1901  ImGui::NextColumn();
1902  helpMarker("Module Output", "Output video format for legacy module.");
1903  ImGui::NextColumn();
1904  ImGui::SetNextItemWidth(fontw * 5.0F);
1905  ImGui::Combo("##NewModofmt", &ofmt, "YUYV\0RGB\0RGBA\0GREY\0BAYER\0\0");
1906  static int ow = 320, oh = 240; static float ofps = 30.0F;
1907  ImGui::SameLine();
1908  ImGui::SetNextItemWidth(fontw * 4.0F);
1909  ImGui::InputInt("##NewModow", &ow, 0, 0, ImGuiInputTextFlags_CharsDecimal | oflags);
1910  ImGui::SameLine();
1911  ImGui::Text("x");
1912  ImGui::SameLine();
1913  ImGui::SetNextItemWidth(fontw * 4.0F);
1914  ImGui::InputInt("##NewModoh", &oh, 0, 0, ImGuiInputTextFlags_CharsDecimal | oflags);
1915  ImGui::SameLine();
1916  ImGui::Text("@");
1917  ImGui::SameLine();
1918  ImGui::SetNextItemWidth(fontw * 4.0F);
1919  ImGui::InputFloat("##NewModofps", &ofps, 0.0F, 0.0F, "%.1f", ImGuiInputTextFlags_CharsDecimal | oflags);
1920  ImGui::SameLine();
1921  ImGui::Text("fps");
1922  ImGui::NextColumn();
1923 
1924  if (templ != 1)
1925  {
1926  ImGui::PopItemFlag();
1927  ImGui::PopStyleVar();
1928  }
1929 
1930  // Camera mode:
1931  static int cmode = 0;
1932  ImGui::AlignTextToFramePadding();
1933  ImGui::TextUnformatted("Camera Mode");
1934  ImGui::NextColumn();
1935  helpMarker("Camera Mode", "Camera sensor configuration for your module.");
1936  ImGui::NextColumn();
1937  ImGui::SetNextItemWidth(fontw * 18.0F);
1938  ImGui::Combo("##NewModcmode", &cmode, "Dual-resolution (Crop+Scale)\0Single-resolution Crop\0"
1939  "Single-resolution Scale\0\0");
1940  ImGui::NextColumn();
1941 
1942  // Camera WDR mode:
1943  static int wdrmode = 0;
1944  ImGui::AlignTextToFramePadding();
1945  ImGui::TextUnformatted("Camera WDR");
1946  ImGui::NextColumn();
1947  helpMarker("Camera WDR", "Camera sensor wide-dynamic-range (WDR) setting for your module. Linear is for no WDR, "
1948  "DOL is for digital overlap (merging short and long exposure frames).");
1949  ImGui::NextColumn();
1950  ImGui::Combo("##NewModwdrmode", &wdrmode, "Linear\0\0"); // FIXME
1951  //ImGui::Combo("##NewModwdrmode", &wdrmode, "Linear\0DOL\0\0");
1952  ImGui::NextColumn();
1953 
1954  // Camera video mapping:
1955  static int cfmt = 0;
1956  ImGui::AlignTextToFramePadding();
1957  ImGui::TextUnformatted("Camera Format");
1958  ImGui::NextColumn();
1959  helpMarker("Camera Format", "Camera video format to use for input to module and for GUI display.");
1960  ImGui::NextColumn();
1961  ImGui::SetNextItemWidth(fontw * 5.0F);
1962  ImGui::Combo("##NewModcfmt", &cfmt, "YUYV\0RGB\0RGBA\0GREY\0BAYER\0\0");
1963  static int cw = 1920, ch = 1080; static float cfps = 30.0F;
1964  ImGui::SameLine();
1965  ImGui::SetNextItemWidth(fontw * 4.0F);
1966  ImGui::InputInt("##NewModcw", &cw, 0, 0, ImGuiInputTextFlags_CharsDecimal);
1967  ImGui::SameLine();
1968  ImGui::Text("x");
1969  ImGui::SameLine();
1970  ImGui::SetNextItemWidth(fontw * 4.0F);
1971  ImGui::InputInt("##NewModch", &ch, 0, 0, ImGuiInputTextFlags_CharsDecimal);
1972  ImGui::SameLine();
1973  ImGui::Text("@");
1974  ImGui::SameLine();
1975  ImGui::SetNextItemWidth(fontw * 4.0F);
1976  ImGui::InputFloat("##NewModcfps", &cfps, 0.0F, 0.0F, "%.1f", ImGuiInputTextFlags_CharsDecimal);
1977  ImGui::SameLine();
1978  ImGui::Text("fps");
1979  ImGui::NextColumn();
1980 
1981  // Camera second frame video mapping:
1982  int c2flags = ImGuiInputTextFlags_None;
1983  if (cmode != 0)
1984  {
1985  oflags |= ImGuiInputTextFlags_ReadOnly;
1986  ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
1987  ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
1988  }
1989 
1990  static int c2fmt = 1;
1991  ImGui::AlignTextToFramePadding();
1992  ImGui::TextUnformatted("Camera Format 2");
1993  ImGui::NextColumn();
1994  helpMarker("Camera Format 2", "Camera video format for the second stream (for processing).");
1995  ImGui::NextColumn();
1996  ImGui::SetNextItemWidth(fontw * 5.0F);
1997  ImGui::Combo("##NewModc2fmt", &c2fmt, "YUYV\0RGB\0RGBA\0GREY\0BAYER\0\0");
1998  static int c2w = 512, c2h = 288;
1999  ImGui::SameLine();
2000  ImGui::SetNextItemWidth(fontw * 4.0F);
2001  ImGui::InputInt("##NewModc2w", &c2w, 0, 0, ImGuiInputTextFlags_CharsDecimal | c2flags);
2002  ImGui::SameLine();
2003  ImGui::Text("x");
2004  ImGui::SameLine();
2005  ImGui::SetNextItemWidth(fontw * 4.0F);
2006  ImGui::InputInt("##NewModc2h", &c2h, 0, 0, ImGuiInputTextFlags_CharsDecimal | c2flags);
2007  ImGui::NextColumn();
2008 
2009  if (cmode != 0)
2010  {
2011  ImGui::PopItemFlag();
2012  ImGui::PopStyleVar();
2013  }
2014 
2015  // Adjust columns:
2016  ImGui::SetColumnWidth(0, fontw * 10.0F);
2017  ImGui::SetColumnWidth(1, ImGui::CalcTextSize("(?)").x + 30.0F);
2018  //ImGui::SetColumnWidth(2, 800.0F);
2019 
2020  ImGui::Columns(1);
2021 
2022 
2023  ImGui::Separator();
2024  ImGui::Separator();
2025 
2026  ImVec2 const button_size(ImGui::GetFontSize() * 7.0f, 0.0f);
2027  if (ImGui::Button("Cancel", button_size)) { ImGui::CloseCurrentPopup(); ImGui::EndPopup(); return; }
2028  ImGui::SameLine(0, 530);
2029 
2030  if (ImGui::Button("Create", button_size))
2031  {
2032  // Validate inputs:
2033  if (name.empty()) LFATAL("New Module cannot have an empty name.");
2034  if (name[0]<'A' || name[0]>'Z') LFATAL("New Module name must start with an uppercase letter.");
2035  if (vendor.empty()) LFATAL("New Module cannot have empty vendor name.");
2036  LINFO("New Module data valid...");
2037 
2038  // Let's do it:
2039  mkdir(jevois::sformat("/%s/%s", JEVOIS_MODULE_PATH, vendor.c_str()).c_str(), 0777);
2040  std::string const dir = jevois::sformat("/%s/%s/%s", JEVOIS_MODULE_PATH, vendor.c_str(), name.c_str());
2041  if (mkdir(dir.c_str(), 0777) == -1)
2042  LFATAL("Error creating directory [" << dir << "] for new module. Maybe that module name already exists "
2043  "or not running as root?");
2044  LINFO("Created new Module directory: " << dir);
2045 
2046  // Create a new video mapping to add to videomappimgs.cfg:
2047  jevois::VideoMapping m { };
2048  switch (templ)
2049  {
2050  case 0: m.ofmt = JEVOISPRO_FMT_GUI; break;
2051  case 1: m.ofmt = get_v4l2_fmt(ofmt); m.ow = ow; m.oh = oh; m.ofps = ofps; break;
2052  case 2: m.ofmt = 0; break;
2053  default: break;
2054  }
2055 
2056  m.cfmt = get_v4l2_fmt(cfmt);
2057  m.cw = cw; m.ch = ch; m.cfps = cfps;
2058 
2059  m.vendor = vendor;
2060  m.modulename = name;
2061 
2062  switch (wdrmode)
2063  {
2064  case 0: m.wdr = jevois::WDRtype::Linear; break;
2065  case 1: m.wdr = jevois::WDRtype::DOL; break;
2066  default: break;
2067  }
2068 
2069  switch (cmode)
2070  {
2071  case 0: m.crop = jevois::CropType::CropScale;
2072  m.c2fmt = get_v4l2_fmt(c2fmt);
2073  m.c2w = c2w; m.c2h = c2h;
2074  break;
2075 
2076  case 1: m.crop = jevois::CropType::Crop; break;
2077  case 2: m.crop = jevois::CropType::Scale; break;
2078  default: break;
2079  }
2080 
2081  m.ispython = (language == 0);
2082 
2083  // Copy the desired code template and cook it:
2084  std::string code;
2085  switch (language)
2086  {
2087  case 0:
2088  {
2089  std::ifstream f(JEVOIS_SHARE_PATH "/templates/PyModule.py");
2090  if (f.is_open() == false) LFATAL("Cannot read " JEVOIS_SHARE_PATH "/templates/PyModule.py");
2091  std::string const str((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
2092  code = str;
2093  }
2094  break;
2095 
2096  case 1:
2097  {
2098  LFATAL("C++ template fixme");
2099  }
2100  break;
2101 
2102  default: break;
2103  }
2104 
2105  jevois::replaceStringAll(code, "__MODULE__", name);
2106  jevois::replaceStringAll(code, "__VENDOR__", vendor);
2107  jevois::replaceStringAll(code, "__SYNOPSIS__", synopsis);
2108  jevois::replaceStringAll(code, "__AUTHOR__", author);
2109  jevois::replaceStringAll(code, "__EMAIL__", email);
2110  jevois::replaceStringAll(code, "__WEBSITE__", website);
2111  jevois::replaceStringAll(code, "__LICENSE__", license);
2112  std::ostringstream oss; oss << m;
2113  jevois::replaceStringAll(code, "__VIDEOMAPPING__", oss.str());
2114 
2115  // Write the code:
2116  std::ofstream ofs(m.sopath());
2117  if (ofs.is_open() == false) LFATAL("Cannot write " << m.sopath() << " -- check that you are running as root.");
2118  ofs << code << std::endl;
2119  LINFO("Wrote code template to: " << m.sopath());
2120 
2121  // Add the video mapping:
2122  jevois::system(JEVOIS "-add-videomapping " + oss.str());
2123  LINFO("Added videomapping: " << oss.str());
2124 
2125  engine()->reloadVideoMappings();
2126  size_t idx = 0; size_t foundidx = 12345678;
2127  engine()->foreachVideoMapping([&](VideoMapping const & mm) { if (m.isSameAs(mm)) foundidx = idx; ++idx; });
2128  if (foundidx != 12345678) engine()->requestSetFormat(foundidx);
2129  itsRefreshVideoMappings = true; // Force a refresh of our list of video mappings
2130  itsRefreshCfgList = true; // Force a refresh of videomappings.cfg in the config editor
2131  itsVideoMappingListType = templ; // Switch to the mapping list that contains our new module
2132 
2133  // Clear a few things before the next module:
2134  name.clear(); vendor.clear(); synopsis.clear();
2135 
2136  ImGui::CloseCurrentPopup();
2137  }
2138  }
2139  catch (...) { reportAndIgnoreException(); }
2140 
2141  // Make sure we always end the popup, even if we had an exception:
2142  ImGui::EndPopup();
2143  }
2144 
2145  ImGui::PopStyleColor();
2146 }
2147 
2148 // ##############################################################################################################
2149 // imgui opengl demo in 1024 bytes:
2150 using V=ImVec2;using F=float;int h;struct E{V p;F z,w;bool operator<(E&o){return z<o.z;}};E G[999];
2151 #define L(i,x,y,z)for(F i=x;i<y;i+=z)
2152 #define Q(y)sin((y+t)*.03)*(1-sin(t*3)*cos(y/99+t))*9
2153 #define H(p,w)L(k,0,5,1)d->AddCircleFilled(p+V(1,-1)*w*k/8,w*(1-k/5),k<4?0xff000000+k*0x554400:-1);
2154 #define J(b)L(i,0,h,1){E&o=G[int(i)];if(b*o.z>0)H(o.p,(o.z*.3+4)*o.w)}
2155 #define O(b)i=t+1.6+j/99;if(b*sin(i)>0)H(c+v*j+u*(cos(i)*40+Q(j)),30)
2156 void FX(ImDrawList*d,V a,V b,V,ImVec4,F t){F i=sin(t)-.7;V u(cos(i),sin(i)),v(-sin(i),cos(i)),c=(a+b)/2;F l=300;
2157 F w=0;L(z,4,20,1){w+=z;L(yy,-l,l,z*2){F y=yy+fmod(t*z*10,z*2);L(i,-1,2,2)d->AddCircle(c+v*y+u*i*(w+sin((y+t)/25)*w/30),z,0xff000000+0x110e00*int(z*z*z/384),12,z/2);}}
2158 h=0;L(y,-l,l,15)L(b,0,16,1){i=t+b*.2+y/99;G[h++]={c+v*y+u*(cos(i)*60+Q(y)),sinf(i),(b<1||b>14)?2.f:1.5f};}std::sort(G,G+h);
2159  F j=(-2+fmod(t*3,5))*99;J(-1)O(-1)a=c+v*-l;L(y,-l,l,15){b=c+v*y+u*((15-rand())&31)*.1;L(k,0,9,1)d->AddLine(a-v,b,k<8?0x11222200*k:-1,(9-k)*4);a=b;}O(1)J(1)}
2160 
2161 // ##############################################################################################################
2163 {
2164  if (ImGui::Button("Open Style Editor")) itsShowStyleEditor = true;
2165  ImGui::SameLine();
2166  if (ImGui::Button("Open App Metrics")) itsShowAppMetrics = true;
2167  ImGui::SameLine();
2168  if (ImGui::Button("Open ImGui Demo")) itsShowImGuiDemo = true;
2169  ImGui::Separator();
2170 
2171 
2172  float camz = pixel_perfect_z;
2173  float yaw = 0.0f;
2174  float pitch = 0.0f;
2175  float roll = 0.0f;
2176  static float fudgex = 0.375f;
2177  static float fudgey = 0.375f;
2178  static float fudgez = 0.0f;
2179  int winw = 1920, winh = 1080;
2180 
2181  ImGui::SliderFloat("OpenGL Camera z", &camz, -2.0f * winh, -1.0f);
2182  ImGui::SliderAngle("OpenGL yaw", &yaw, -179.0f, 180.0f);
2183  ImGui::SliderAngle("OpenGL pitch", &pitch, -179.0f, 180.0f);
2184  ImGui::SliderAngle("OpenGL roll", &roll, -179.0f, 180.0f);
2185 
2186  ImGui::SliderFloat("fudge x", &fudgex, -1.0f, 1.0f);
2187  ImGui::SliderFloat("fudge y", &fudgey, -1.0f, 1.0f);
2188  ImGui::SliderFloat("fudge z", &fudgez, -1.0f, 1.0f);
2189 
2190  // mess with the projection matrix:
2191  proj = glm::perspective(glm::radians(45.0f), float(winw) / float(winh), 1.0f, winh * 2.0f);
2192  proj = glm::translate(proj, glm::vec3(fudgex, fudgey, fudgez));
2193 
2194  // update our view matrix
2195  view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, camz));
2196  view *= glm::yawPitchRoll(yaw, pitch, roll);
2197 
2198  // Note: the ortho projection of ImGui is like: glm::ortho(0.0f, 1920.0f, 1080.0f, 0.0f);
2199  /*
2200  glm::mat4 gogo = glm::ortho(0.0f, 1920.0f, 1080.0f, 0.0f);
2201  gogo = glm::translate(gogo, glm::vec3(0.125f, 0.125f, 0.0f));
2202 
2203  for (int i = 0; i < 4; ++i)
2204  for (int j = 0; j < 4; ++j)
2205  printf("gogo[%d][%d] = %f\n", i, j, gogo[i][j]);
2206  */
2207 
2208  //static bool demo = false;
2209  //if (ImGui::Checkbox("OpenGL Demo", &demo))
2210  //{
2211  // from https://github.com/ocornut/imgui/issues/3606
2212  ImGuiIO& io = ImGui::GetIO();
2213  ImGui::Begin("FX", NULL, ImGuiWindowFlags_AlwaysAutoResize);
2214  ImVec2 size(320.0f, 180.0f);
2215  ImGui::InvisibleButton("canvas", size);
2216  ImVec2 p0 = ImGui::GetItemRectMin();
2217  ImVec2 p1 = ImGui::GetItemRectMax();
2218  ImDrawList* draw_list = ImGui::GetWindowDrawList();
2219  draw_list->PushClipRect(p0, p1);
2220 
2221  ImVec4 mouse_data;
2222  mouse_data.x = (io.MousePos.x - p0.x) / size.x;
2223  mouse_data.y = (io.MousePos.y - p0.y) / size.y;
2224  mouse_data.z = io.MouseDownDuration[0];
2225  mouse_data.w = io.MouseDownDuration[1];
2226 
2227  FX(draw_list, p0, p1, size, mouse_data, (float)ImGui::GetTime());
2228  draw_list->PopClipRect();
2229  ImGui::End();
2230  //}
2231 
2232 }
2233 
2234 // ##############################################################################################################
2235 void jevois::GUIhelper::reportError(std::string const & err)
2236 {
2237  auto now = std::chrono::steady_clock::now();
2238 
2239  std::lock_guard<std::mutex> _(itsErrorMtx);
2240 
2241  // Did we already report this error? If so, just update the last received time:
2242  for (auto & e : itsErrors) if (e.err == err) { e.lasttime = now; return; }
2243 
2244  // It's a new error, push a new entry into our list:
2245  ErrorData d { err, now, now };
2246  itsErrors.emplace(itsErrors.end(), std::move(d));
2247 
2248  // drawErrorPopup() will clear old entries
2249 }
2250 
2251 // ##############################################################################################################
2252 void jevois::GUIhelper::reportAndIgnoreException(std::string const & prefix)
2253 {
2254  if (prefix.empty())
2255  {
2256  try { throw; }
2257  catch (std::exception const & e) { reportError(e.what()); }
2258  catch (...) { reportError("Unknown error"); }
2259  }
2260  else
2261  {
2262  try { throw; }
2263  catch (std::exception const & e) { reportError(prefix + ": " + e.what()); }
2264  catch (...) { reportError(prefix + ": Unknown error"); }
2265  }
2266 }
2267 
2268 // ##############################################################################################################
2269 void jevois::GUIhelper::reportAndRethrowException(std::string const & prefix)
2270 {
2271  reportAndIgnoreException(prefix);
2272  throw;
2273 }
2274 
2275 // ##############################################################################################################
2277 {
2278  std::lock_guard<std::mutex> _(itsErrorMtx);
2279  if (itsErrors.empty()) return;
2280 
2281  ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize |
2282  ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNav;
2283 
2284  ImGui::SetNextWindowPos(ImVec2(10.0f, 10.0f), ImGuiCond_Always);
2285  ImGui::PushStyleColor(ImGuiCol_WindowBg, 0xc0e0e0ff);
2286 
2287  static bool show = true;
2288 
2289  if (ImGui::Begin("Error detected!", &show, window_flags))
2290  {
2291  ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
2292  ImGui::Text("Error detected!");
2293 
2294  auto itr = itsErrors.begin();
2295  while (itr != itsErrors.end())
2296  {
2297  // Clear the error after a while, unless mouse cursor is on it:
2298  std::chrono::duration<float> d = std::chrono::steady_clock::now() - itr->lasttime;
2299  std::chrono::duration<float> d2 = std::chrono::steady_clock::now() - itr->firsttime;
2300  if (d.count() >= 1.0f && d2.count() >= 10.0f && ImGui::IsWindowHovered() == false)
2301  itr = itsErrors.erase(itr);
2302  else
2303  {
2304  // Show this error:
2305  ImGui::Separator();
2306  ImGui::TextUnformatted(itr->err.c_str());
2307  ++itr;
2308  }
2309  }
2310  ImGui::PopTextWrapPos();
2311 
2312  ImGui::End();
2313  }
2314  ImGui::PopStyleColor();
2315 }
2316 
2317 // ##############################################################################################################
2318 void jevois::GUIhelper::helpMarker(char const * msg, char const * msg2, char const * msg3)
2319 {
2320  //ImGui::TextDisabled("(?)");
2321  ImGui::Text("(?)");
2322  if (ImGui::IsItemHovered())
2323  {
2324  ImGui::BeginTooltip();
2325  ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
2326  ImGui::TextUnformatted(msg);
2327  if (msg2) { ImGui::Separator(); ImGui::TextUnformatted(msg2); }
2328  if (msg3) { ImGui::Separator(); ImGui::TextUnformatted(msg3); }
2329  ImGui::PopTextWrapPos();
2330  ImGui::EndTooltip();
2331  }
2332 }
2333 
2334 // ##############################################################################################################
2335 bool jevois::GUIhelper::toggleButton(char const * name, bool * val)
2336 {
2337  bool changed = false;
2338  if (*val)
2339  {
2340  ImGui::PushID(name);
2341  ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(4.0f/7.0f, 1.0f, 1.0f));
2342  ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(4.0f/7.0f, 1.0f, 1.0f));
2343  ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(4.0f/7.0f, 0.5f, 0.5f));
2344  if (ImGui::Button(name)) { *val = false; changed = true; }
2345  ImGui::PopStyleColor(3);
2346  ImGui::PopID();
2347  }
2348  else if (ImGui::Button(name)) { *val = true; changed = true; }
2349 
2350  return changed;
2351 }
2352 
2353 // ##############################################################################################################
2355 {
2356  unsigned short winw, winh;
2357  startFrame(winw, winh);
2358 
2359  if (itsHeadless.loaded() == false)
2360  try { itsHeadless.load(JEVOIS_SHARE_PATH "/icons/headless.png"); }
2361  catch (...)
2362  {
2364  cv::Mat blank(winh, winw, CV_8UC4, 0);
2365  itsHeadless.load(blank);
2366  }
2367 
2368  if (itsHeadless.loaded()) itsHeadless.draw(ImVec2(0, 0), ImVec2(winw, winh), ImGui::GetBackgroundDrawList());
2369 
2370  endFrame();
2371 }
2372 
2373 #endif // JEVOIS_PRO
jevois::GUIhelper::ErrorData
Definition: GUIhelper.H:333
E
Definition: GUIhelper.C:2150
jevois::module::SerStyle
SerStyle
Definition: Module.H:203
jevois::GUIhelper::CfgSaveAction::Reload
@ Reload
JEVOIS_WAIT_GET_FUTURE
#define JEVOIS_WAIT_GET_FUTURE(f)
Wait for a future to become ready for 5 seconds, get(), warn and ignore exception,...
Definition: Log.H:312
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::GUIhelper::drawImage
void drawImage(char const *name, RawImage const &img, int &x, int &y, unsigned short &w, unsigned short &h, bool noalias=false, bool isoverlay=false)
Draw a RawImage, copying pixel data to an OpenGL texture.
Definition: GUIhelper.C:242
jevois::getFanSpeed
int getFanSpeed()
Get fan speed in percent, only meaningful on JeVois-Pro Platform, all others return 0.
Definition: SysInfo.C:138
jevois::imu::get
Data collection mode RAW means that the latest available raw data is returned each time get() is called
jevois::GUIhelper::drawInputFrame2
void drawInputFrame2(char const *name, InputFrame const &frame, int &x, int &y, unsigned short &w, unsigned short &h, bool noalias=false, bool casync=false)
Draw the second (scaled) input video frame from the camera using zero-copy.
Definition: GUIhelper.C:319
jevois::GUIhelper::drawNewModuleForm
void drawNewModuleForm()
Definition: GUIhelper.C:1826
jevois::paramStringToVal
void paramStringToVal(std::string const &valstring, T &result)
Machine-readable conversion from string to T, for use in jevois::Parameter.
jevois::GUIhelper::reportAndIgnoreException
void reportAndIgnoreException(std::string const &prefix="")
Report current exception in a modal dialog, then ignore it.
Definition: GUIhelper.C:2252
jevois::GUIhelper::CfgSaveAction::RefreshMappings
@ RefreshMappings
jevois::Range
A generic range class.
Definition: Range.H:80
jevois::GUIhelper::drawMenuBar
void drawMenuBar()
Definition: GUIhelper.C:660
jevois::GUIhelper::drawCircle
void drawCircle(float x, float y, float r, ImU32 col=IM_COL32(128, 255, 128, 255), bool filled=true)
Draw circle over an image.
Definition: GUIhelper.C:466
Module.H
jevois::GUIhelper::startFrame
bool startFrame(unsigned short &w, unsigned short &h)
Start a new rendering frame.
Definition: GUIhelper.C:139
GPUimage.H
jevois::GUIhelper::itext
void itext(char const *txt, ImU32 const &col=IM_COL32_BLACK_TRANS, int line=-1)
Draw some overlay text on top of an image.
Definition: GUIhelper.C:509
jevois::GUIhelper::onParamChange
void onParamChange(gui::scale const &param, float const &newval) override
jevois::Range::min
const T & min() const
Return the minimum value.
jevois::GUIhelper::drawInfo
void drawInfo()
Definition: GUIhelper.C:796
jevois::sformat
std::string sformat(char const *fmt,...) __attribute__((format(__printf__
Create a string using printf style arguments.
Definition: Utils.C:401
jevois::GUIhelper::CfgItem
Definition: GUIhelper.H:406
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::GUIhelper::setparstr
void setparstr(std::string const &descriptor, std::string const &val)
Definition: GUIhelper.C:956
jevois::GPUimage
Class to hold a GPUtexture, GPUprogram, and other data associated with rendering an image in OpenGL.
Definition: GPUimage.H:38
jevois::getSysInfoMem
std::string getSysInfoMem()
Get memory info.
Definition: SysInfo.C:66
jevois::GUIhelper::headlessDisplay
void headlessDisplay()
Show a message that we are running headless.
Definition: GUIhelper.C:2354
jevois::GUIhelper::drawPolyInternal
void drawPolyInternal(ImVec2 const *pts, size_t npts, ImU32 col, bool filled)
Definition: GUIhelper.C:429
jevois::GUIhelper::endFrame
void endFrame()
Finish current frame and render it.
Definition: GUIhelper.C:557
jevois::Component
A component of a model hierarchy.
Definition: Component.H:180
jevois::GUIhelper::reportAndRethrowException
void reportAndRethrowException(std::string const &prefix="")
Report current exception in a modal dialog, then re-throw it.
Definition: GUIhelper.C:2269
jevois::GUIhelper::itsCfgEditor
TextEditor itsCfgEditor
Definition: GUIhelper.H:397
jevois::GUIhelper::releaseImage
void releaseImage(char const *name)
Release an image.
Definition: GUIhelper.C:543
jevois::GUIhelper::drawTweaks
void drawTweaks()
Definition: GUIhelper.C:2162
jevois::GUIhelper::newModEntry
void newModEntry(char const *wname, std::string &str, char const *desc, char const *hint, char const *hlp)
Definition: GUIhelper.C:1637
jevois::GUIhelper::itsNumFixedCfgItems
size_t itsNumFixedCfgItems
Definition: GUIhelper.H:413
jevois::RawImage
A raw image as coming from a V4L2 Camera and/or being sent out to a USB Gadget.
Definition: RawImage.H:110
jevois::GUIhelper::itsCfgItems
std::vector< CfgItem > itsCfgItems
Definition: GUIhelper.H:412
jevois::GUIhelper::CfgSaveAction::Reboot
@ Reboot
jevois::GUIhelper::helpMarker
void helpMarker(char const *msg, char const *msg2=nullptr, char const *msg3=nullptr)
Display a (?) label and show tooltip when it is hovered.
Definition: GUIhelper.C:2318
jevois::GUIhelper::loadEditorFile
void loadEditorFile(TextEditor &e, std::string const &fn, std::string const &failtxt, bool failro)
Definition: GUIhelper.C:1339
jevois::Component::foreachParam
void foreachParam(std::function< void(std::string const &compname, ParameterBase *p)> func, std::string const &cname="")
Run a function on every param we hold.
Definition: Component.C:569
jevois::GUIhelper::i2d
ImVec2 i2d(ImVec2 p, char const *name=nullptr)
Convert coordinates of a point from within a rendered image to on-screen.
Definition: GUIhelper.C:343
jevois::ParameterBase
Base class for Parameter.
Definition: Parameter.H:121
jevois::GUIhelper::drawErrorPopup
void drawErrorPopup()
Definition: GUIhelper.C:2276
jevois::GUIhelper::drawModuleSelect
void drawModuleSelect()
Definition: GUIhelper.C:716
o
#define o
Definition: Font10x20.C:6
jevois::GUIhelper::drawEditorMenu
void drawEditorMenu(TextEditor &e)
Definition: GUIhelper.C:1372
jevois::RawImage::bufindex
size_t bufindex
The index of the data buffer in the kernel driver.
Definition: RawImage.H:150
jevois::GUIhelper::drawParameters
void drawParameters()
Definition: GUIhelper.C:964
jevois::ParameterSummary::category
std::string category
Category of the parameter, as a string.
Definition: Parameter.H:108
jevois::GUIhelper::drawRect
void drawRect(float x1, float y1, float x2, float y2, ImU32 col=IM_COL32(128, 255, 128, 255), bool filled=true)
Draw rectangular box over an image.
Definition: GUIhelper.C:417
jevois::async_little
std::future< std::invoke_result_t< std::decay_t< Function >, std::decay_t< Args >... > > async_little(Function &&f, Args &&... args)
Async execution using a thread pool.
jevois::GUIhelper::drawCamCtrls
void drawCamCtrls()
Definition: GUIhelper.C:1294
jevois::InputFrame::get
const RawImage & get(bool casync=false) const
Get the next captured camera image.
Definition: InputFrame.C:50
jevois::getSysInfoVersion
std::string getSysInfoVersion()
Get O.S. version info.
Definition: SysInfo.C:74
jevois::RawImage::width
unsigned int width
Image width in pixels.
Definition: RawImage.H:145
Q
#define Q(y)
Definition: GUIhelper.C:2152
jevois::GUIhelper::drawLine
void drawLine(float x1, float y1, float x2, float y2, ImU32 col=IM_COL32(128, 255, 128, 255))
Draw line over an image.
Definition: GUIhelper.C:411
jevois::GUIhelper::seroutEnabled
bool seroutEnabled() const
Tell whether user enabled serout messages to GUI console.
Definition: GUIhelper.C:1290
jevois::GUIhelper::reportError
void reportError(std::string const &err)
Report an error in an overlay window.
Definition: GUIhelper.C:2235
jevois
Definition: Concepts.dox:1
jevois::getNumInstalledNPUs
size_t getNumInstalledNPUs()
Get the number of JeVois-Pro NPUs present on this system.
Definition: SysInfo.C:127
F
float F
Definition: GUIhelper.C:2150
jevois::getSysInfoCPU
std::string getSysInfoCPU()
Get CPU info: frequency, thermal, load.
Definition: SysInfo.C:24
jevois::GUIhelper::drawPoly
void drawPoly(std::vector< cv::Point > const &pts, ImU32 col=IM_COL32(128, 255, 128, 255), bool filled=true)
Draw polygon over an image.
Definition: GUIhelper.C:444
jevois::Component::getParamValUnique
T getParamValUnique(std::string const &paramdescriptor) const
Get a parameter value, simple version assuming only one parameter match.
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::InputFrame::hasScaledImage
bool hasScaledImage() const
Check whether a second input image scaled by the JeVoisPro Platform ISP is available.
Definition: InputFrame.C:61
jevois::GUIhelper::drawCodeEditor
void drawCodeEditor()
Definition: GUIhelper.C:1580
GUIconsole.H
J
#define J(b)
Definition: GUIhelper.C:2154
SysInfo.H
jevois::GUIhelper::saveEditorFile
void saveEditorFile(TextEditor &e, std::string const &fn)
Definition: GUIhelper.C:1358
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::GUIhelper::toggleButton
bool toggleButton(char const *name, bool *val)
Helper to draw a toggle button.
Definition: GUIhelper.C:2335
jevois::GUIhelper::reset
void reset(bool modulechanged=true)
Reset to default state, typically called on Module or video format change.
Definition: GUIhelper.C:95
jevois::GUIhelper::frameStarted
bool frameStarted() const
Helper to indicate that startFrame() was called, and thus endFrame() should be called.
Definition: GUIhelper.C:238
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::getNumInstalledTPUs
size_t getNumInstalledTPUs()
Get the number of Coral TPUs present on this system.
Definition: SysInfo.C:87
jevois::Component::setParamValUnique
void setParamValUnique(std::string const &paramdescriptor, T const &val)
Set a parameter value, simple version assuming only one parameter match.
jevois::GUIhelper::drawEditor
void drawEditor(TextEditor &e)
Definition: GUIhelper.C:1406
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::GUIhelper::drawJeVoisGUI
void drawJeVoisGUI()
Definition: GUIhelper.C:572
jevois::GPUimage::i2ds
ImVec2 i2ds(ImVec2 const &p)
Convert a 2D size from within a rendered image to on-screen.
Definition: GPUimage.C:382
jevois::InputFrame::get2
const RawImage & get2(bool casync=false) const
Get the next captured camera image, ISP-scaled second frame.
Definition: InputFrame.C:67
jevois::Engine
JeVois processing engine - gets images from camera sensor, processes them, and sends results over USB...
Definition: Engine.H:381
jevois::GUIhelper::serlogEnabled
bool serlogEnabled() const
Tell whether user enabled serlog messages to GUI console.
Definition: GUIhelper.C:1286
jevois::Manager::getComponent
std::shared_ptr< Comp > getComponent(std::string const &instanceName) const
Get a top-level component by instance name.
jevois::Crop
() Scale() Crop(CropScale)) struct VideoMapping
Simple struct to hold video mapping definitions for the processing Engine.
Definition: VideoMapping.H:43
jevois::GUIhelper::releaseImage2
void releaseImage2(char const *name)
Release an image, second video stream.
Definition: GUIhelper.C:550
jevois::warnAndIgnoreException
std::string warnAndIgnoreException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
Definition: Log.C:224
jevois::GUIhelper::drawSystem
void drawSystem()
Definition: GUIhelper.C:1652
L
#define L(i, x, y, z)
Definition: GUIhelper.C:2151
LFATAL
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
Definition: Log.H:217
jevois::GUIhelper::drawText
void drawText(float x, float y, char const *txt, ImU32 col=IM_COL32(128, 255, 128, 255))
Draw text over an image.
Definition: GUIhelper.C:479
jevois::GUIhelper::GUIhelper
GUIhelper(std::string const &instance, bool conslock=false)
Constructor.
Definition: GUIhelper.C:43
jevois::GUIconsole
Simple console with coloring and completion.
Definition: GUIconsole.H:31
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::ParameterSummary
ParameterSummary provides a summary about a parameter.
Definition: Parameter.H:83
jevois::GUIhelper::drawInputFrame
void drawInputFrame(char const *name, InputFrame const &frame, int &x, int &y, unsigned short &w, unsigned short &h, bool noalias=false, bool casync=false)
Draw the input video frame from the camera using zero-copy.
Definition: GUIhelper.C:289
jevois::GUIhelper::drawConsole
void drawConsole()
Definition: GUIhelper.C:1197
jevois::module::SerStyle_Values
Style for standardized serial messages as defined in SerStyle_Values
Definition: Module.H:205
jevois::getNumInstalledVPUs
size_t getNumInstalledVPUs()
Get the number of Myriad-X VPUs present on this system.
Definition: SysInfo.C:112
jevois::RawImage::height
unsigned int height
Image height in pixels.
Definition: RawImage.H:146
jevois::GUIhelper::iinfo
void iinfo(jevois::InputFrame const &inframe, std::string const &fpscpu, unsigned short winw=0, unsigned short winh=0)
Display processing and video info at bottom of screen.
Definition: GUIhelper.C:522
jevois::GUIhelper::itsWindowTitle
std::string itsWindowTitle
Definition: GUIhelper.H:352
jevois::to_string
std::string to_string(T const &val)
Convert from type to string.
jevois::InputFrame
Exception-safe wrapper around a raw camera input frame.
Definition: InputFrame.H:50
jevois::ParameterBase::summary
virtual const ParameterSummary summary() const =0
Get summary info about this parameter.
jevois::GUIhelper::i2ds
ImVec2 i2ds(ImVec2 p, char const *name=nullptr)
Convert a 2D size from within a rendered image to on-screen.
Definition: GUIhelper.C:377
jevois::RawImage::fmt
unsigned int fmt
Pixel format as a V4L2_PIX_FMT_XXX.
Definition: RawImage.H:147
jevois::GUIconsole::draw
void draw()
Render into ImGui.
Definition: GUIconsole.C:84
V
ImVec2 V
Definition: GUIhelper.C:2150
jevois::GPUimage::i2d
ImVec2 i2d(ImVec2 const &p)
Convert coordinates of a point from within a rendered image to on-screen.
Definition: GPUimage.C:375
jevois::GUIhelper::modal
int modal(std::string const &title, char const *text, bool &dont_ask_me_next_time, char const *b1txt="Ok", char const *b2txt="Cancel")
Helper to draw a modal with 2 choices.
Definition: GUIhelper.C:1300
jevois::GUIhelper::iline
ImVec2 iline(int line=-1, char const *name=nullptr)
Get coordinates of the start of a given line of text to be drawn as overlay on top of an image.
Definition: GUIhelper.C:499
jevois::ParameterSummary::name
std::string name
Plain name of the parameter.
Definition: Parameter.H:90
h
int h
Definition: GUIhelper.C:2150
jevois::Engine::module
std::shared_ptr< Module > module() const
Get a pointer to our current module (may be null)
Definition: Engine.C:1217
jevois::GUIhelper::applyFillAlpha
ImU32 applyFillAlpha(ImU32 col) const
Definition: GUIhelper.C:491
jevois::StdModule
Base class for a module that supports standardized serial messages.
Definition: Module.H:238
jevois::Range::max
const T & max() const
Return the maximum value.
O
#define O(b)
Definition: GUIhelper.C:2155
jevois::GUIhelper::drawCfgEditor
void drawCfgEditor()
Definition: GUIhelper.C:1419
G
E G[999]
Definition: GUIhelper.C:2150
LINFO
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
Definition: Log.H:181
jevois::Component::absolutePath
std::string absolutePath(std::string const &path="")
If given path is relative (not starting with /), prepend the Component path to it.
Definition: Component.C:515
jevois::GUIhelper::~GUIhelper
virtual ~GUIhelper()
Destructor.
Definition: GUIhelper.C:88
GUIhelper.H
FX
void FX(ImDrawList *d, V a, V b, V, ImVec4, F t)
Definition: GUIhelper.C:2156
jevois::GUIhelper::itsCodeEditor
TextEditor itsCodeEditor
Definition: GUIhelper.H:423