JeVois  1.23
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Loading...
Searching...
No Matches
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#define IMGUI_DEFINE_MATH_OPERATORS // Access to math operators
23#include <imgui.h>
24#include <imgui_internal.h>
25#include <algorithm> // for tweak demo
26
29#include <jevois/GPU/GPUimage.H>
30#include <jevois/Core/Module.H>
34#include <jevois/Util/Utils.H>
35#include <jevois/DNN/Utils.H>
37#include <glm/gtc/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale, glm::perspective
38#include <glm/gtx/euler_angles.hpp>
39#include <fstream>
40#include <set>
41
42// ##############################################################################################################
43jevois::GUIhelper::GUIhelper(std::string const & instance, bool conslock) :
44 jevois::Component(instance), itsConsLock(conslock), itsBackend()
45{
46 // We defer OpenGL init to startFrame() so that all OpenGL code is in the same thread.
47
48 // If auto-detecting screen size, get the framebuffer size here so we can use it later:
49 if (winsize::get().width == 0)
50 {
51 // Query the framebuffer if no window size was given:
52 int w = 1920, h = 1080; // defaults in case we fail to read from framebuffer
53 try
54 {
55 std::string const ws = jevois::getFileString("/sys/class/graphics/fb0/virtual_size");
56 std::vector<std::string> const tok = jevois::split(ws, "\\s*[,;x]\\s*");
57 if (tok.size() == 2) { w = std::stoi(tok[0]); h = std::stoi(tok[1]) / 2; } // Reported height is double...
58 LINFO("Detected framebuffer size: " << w << 'x' << h);
59 }
60 catch (...) { } // silently ignore any errors
61
62 winsize::set(cv::Size(w, h));
63 }
64
65 itsWindowTitle = "JeVois-Pro v" + std::string(JEVOIS_VERSION_STRING);
66
67 // Create some config files for the config editor:
68 std::vector<EditorItem> fixedcfg
69 {
70 { JEVOIS_ROOT_PATH "/config/videomappings.cfg", "JeVois videomappings.cfg", EditorSaveAction::RefreshMappings },
71 { JEVOIS_ROOT_PATH "/config/initscript.cfg", "JeVois initscript.cfg", EditorSaveAction::Reboot },
72 { "params.cfg", "Module's params.cfg", EditorSaveAction::Reload },
73 { "script.cfg", "Module's script.cfg", EditorSaveAction::Reload },
74 { JEVOIS_ROOT_PATH "/share/dnn/models.yml",
75 "JeVois models.yml DNN Zoo Root", EditorSaveAction::Reload },
76 { JEVOIS_ROOT_PATH "/share/dnn/opencv.yml",
77 "JeVois opencv.yml DNN Zoo for OpenCV models", EditorSaveAction::Reload },
78 { JEVOIS_ROOT_PATH "/share/dnn/npu.yml",
79 "JeVois npu.yml DNN Zoo for A311D NPU models", EditorSaveAction::Reload },
80 { JEVOIS_ROOT_PATH "/share/dnn/spu.yml",
81 "JeVois spu.yml DNN Zoo for Hailo SPU models", EditorSaveAction::Reload },
82 { JEVOIS_ROOT_PATH "/share/dnn/tpu.yml",
83 "JeVois tpu.yml DNN Zoo for Coral TPU models", EditorSaveAction::Reload },
84 { JEVOIS_ROOT_PATH "/share/dnn/vpu.yml",
85 "JeVois vpu.yml DNN Zoo for Myriad-X VPU models", EditorSaveAction::Reload },
86 { JEVOIS_ROOT_PATH "/share/dnn/ort.yml",
87 "JeVois ort.yml DNN Zoo for ONNX-Runtime CPU models", EditorSaveAction::Reload },
88 };
89
90 // Create the config editor:
91 itsCfgEditor.reset(new jevois::GUIeditor(this, "cfg", std::move(fixedcfg), JEVOIS_CUSTOM_DNN_PATH, "Custom DNN ",
92 { ".yaml", ".yml" }));
93
94 // Create some config files for the code editor:
95 std::vector<EditorItem> fixedcode
96 {
97 { "*", "Module's source code", EditorSaveAction::Reload },
98 // Gets added at runtime: { "#", "Module's CMakeLists.txt", EditorSaveAction::Compile },
99 };
100
101 // Create the code editor:
102 itsCodeEditor.reset(new jevois::GUIeditor(this, "code", std::move(fixedcode), JEVOIS_PYDNN_PATH, "PyDNN ",
103 { ".py", ".C", ".H", ".cpp", ".hpp", ".c", ".h", ".txt" }));
104}
105
106// ##############################################################################################################
108{
109 // In case a DNN get was in progress, wait for it while letting user know:
110 JEVOIS_WAIT_GET_FUTURE(itsDnnGetFut);
111}
112
113// ##############################################################################################################
114void jevois::GUIhelper::resetstate(bool modulechanged)
115{
116 itsEndFrameCalled = true;
117 itsImages.clear();
118 itsImages2.clear();
119 itsLastDrawnImage = nullptr;
120 itsLastDrawnTextLine = -1;
121 itsIdle = true;
122 itsIcon.clear();
123 itsModName.clear();
124 itsModDesc.clear();
125 itsModAuth.clear();
126 itsModLang.clear();
127 itsModDoc.clear();
128
129 if (modulechanged)
130 {
131 itsCfgEditor->refresh();
132 itsCodeEditor->refresh();
133 }
134
135 {
136 // Clear old error messages:
137 std::lock_guard<std::mutex> _(itsErrorMtx);
138 itsErrors.clear();
139 }
140
141 // Get the actual window/screen size:
142 unsigned short w, h;
143 itsBackend.getWindowSize(w, h);
144
145 if (w == 0) LFATAL("Need to call startFrame() at least once first");
146
147 float const fov_y = 45.0f;
148
149 proj = glm::perspective(glm::radians(fov_y), float(w) / float(h), 1.0f, h * 2.0f);
150 const_cast<float &>(pixel_perfect_z) = -float(h) / (2.0 * tan(fov_y * M_PI / 360.0));
151#ifdef JEVOIS_PLATFORM
152 // On platform, we need to translate a bit to avoid aliasing issues, which are problematic with our YUYV shader:
153 proj = glm::translate(proj, glm::vec3(0.375f, 0.375f, 0.0f));
154#endif
155 view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, pixel_perfect_z));
156
157 // Set the style (on first call):
158 style::set(style::get());
159
160 // Refresh whether we have a USB serial gadget loaded:
161 itsUSBserial = ! engine()->getParamStringUnique("engine:usbserialdev").empty();
162
163 // If the engine's current videomapping has a CMakeLists.txt, copy it to itsNewMapping to allow recompilation:
164 jevois::VideoMapping const & m = engine()->getCurrentVideoMapping();
165 if (std::filesystem::exists(m.cmakepath())) itsNewMapping = m;
166}
167
168// ##############################################################################################################
169bool jevois::GUIhelper::startFrame(unsigned short & w, unsigned short & h)
170{
171 if (itsEndFrameCalled == false) LFATAL("You need to call endFrame() at the end of your process() function");
172
173 // Get current window size, will be 0x0 if not initialized yet:
174 itsBackend.getWindowSize(w, h);
175
176 if (w == 0)
177 {
178 // Need to init the display:
179 cv::Size const siz = winsize::get();
180 bool const fs = fullscreen::get();
181 LINFO("OpenGL init " << siz.width << 'x' << siz.height << (fs ? " fullscreen" : ""));
182 itsBackend.init(siz.width, siz.height, fs, scale::get(), itsConsLock);
183 rounding::set(int(rounding::get() * scale::get() + 0.499F));
184
185 // Get the actual window size and update our param:
186 unsigned short winw, winh; itsBackend.getWindowSize(winw, winh); winsize::set(cv::Size(winw, winh));
187 winsize::freeze(true);
188 fullscreen::freeze(true);
189
190 // Reset the GUI:
191 resetstate();
192 }
193
194 // Poll events:
195 bool shouldclose = false; auto const now = std::chrono::steady_clock::now();
196 if (itsBackend.pollEvents(shouldclose)) itsLastEventTime = now;
197
198 if (shouldclose && allowquit::get())
199 {
200 LINFO("Closing down on user request...");
201 engine()->quit();
202 }
203
204 // Start the frame on the backend:
205 itsBackend.newFrame();
206
207 // Check if we have been idle:
208 if (itsIdleBlocked)
209 itsIdle = false;
210 else
211 {
212 float const hs = hidesecs::get();
213 if (hs)
214 {
215 std::chrono::duration<float> elapsed = now - itsLastEventTime;
216 itsIdle = (elapsed.count() >= hs);
217 }
218 else itsIdle = false;
219 }
220
221 itsEndFrameCalled = false;
222
223 return itsIdle;
224}
225
226// ##############################################################################################################
228{ return itsIdle; }
229
230// ##############################################################################################################
231void jevois::GUIhelper::onParamChange(jevois::gui::scale const & param, float const & newval)
232{
233 float oldval = param.get();
234 if (newval == oldval) return;
235
236 ImGui::GetStyle().ScaleAllSizes(newval / oldval);
237 ImGui::GetIO().FontGlobalScale = newval;
238 ImGui::GetStyle().MouseCursorScale = 2.0f; // do not scale the cursor, otherwise it disappears...
239
240 // Also scale the window corner rounding, make it no more than 24:
241 if (oldval) rounding::set(std::min(24, int(rounding::get() * newval / oldval + 0.499F)));
242}
243
244// ##############################################################################################################
245void jevois::GUIhelper::onParamChange(jevois::gui::style const &, jevois::gui::GuiStyle const & newval)
246{
247 switch (newval)
248 {
249 case jevois::gui::GuiStyle::Dark:
250 ImGui::StyleColorsDark();
251 itsCfgEditor->SetPalette(TextEditor::GetDarkPalette());
252 itsCodeEditor->SetPalette(TextEditor::GetDarkPalette());
253 break;
254
255 case jevois::gui::GuiStyle::Light:
256 ImGui::StyleColorsLight();
257 ImGui::GetStyle().Colors[ImGuiCol_WindowBg] = ImVec4(0.94f, 0.94f, 0.94f, 0.92f); // a bit transparent
258 itsCfgEditor->SetPalette(TextEditor::GetLightPalette());
259 itsCodeEditor->SetPalette(TextEditor::GetLightPalette());
260 break;
261
262 case jevois::gui::GuiStyle::Classic:
263 ImGui::StyleColorsClassic();
264 itsCfgEditor->SetPalette(TextEditor::GetRetroBluePalette());
265 itsCodeEditor->SetPalette(TextEditor::GetRetroBluePalette());
266 break;
267 }
268}
269
270// ##############################################################################################################
271void jevois::GUIhelper::onParamChange(jevois::gui::rounding const &, int const & newval)
272{
273 auto & s = ImGui::GetStyle();
274 s.WindowRounding = newval;
275 s.ChildRounding = newval;
276 s.FrameRounding = newval;
277 s.PopupRounding = newval;
278 s.ScrollbarRounding = newval;
279 s.GrabRounding = newval;
280}
281
282// ##############################################################################################################
284{ return (itsEndFrameCalled == false); }
285
286// ##############################################################################################################
287void jevois::GUIhelper::drawImage(char const * name, jevois::RawImage const & img, int & x, int & y,
288 unsigned short & w, unsigned short & h, bool noalias, bool isoverlay)
289{
290 // We will use one image per v4l2 buffer:
291 std::string const imgname = name + std::to_string(img.bufindex);
292
293 // Get the image by name (will create a default-constructed one the first time a new name is used):
294 auto & im = itsImages[imgname];
295
296 // Set the new pixel data:
297 im.set(img);
298
299 // Draw it:
300 im.draw(x, y, w, h, noalias, proj * view);
301
302 // Remember which image was drawn last, used by i2d():
303 if (isoverlay == false)
304 {
305 itsLastDrawnImage = & im;
306 itsUsingScaledImage = false;
307 itsLastDrawnTextLine = -1;
308 }
309}
310
311// ##############################################################################################################
312void jevois::GUIhelper::drawImage(char const * name, cv::Mat const & img, bool rgb, int & x, int & y,
313 unsigned short & w, unsigned short & h, bool noalias, bool isoverlay)
314{
315 // Get the image by name (will create a default-constructed one the first time a new name is used):
316 auto & im = itsImages[name];
317
318 // Set the new pixel data:
319 im.set(img, rgb);
320
321 // Draw it:
322 im.draw(x, y, w, h, noalias, proj * view);
323
324 // Remember which image was drawn last, used by i2d():
325 if (isoverlay == false)
326 {
327 itsLastDrawnImage = & im;
328 itsUsingScaledImage = false;
329 itsLastDrawnTextLine = -1;
330 }
331}
332
333// ##############################################################################################################
334void jevois::GUIhelper::drawInputFrame(char const * name, jevois::InputFrame const & frame, int & x, int & y,
335 unsigned short & w, unsigned short & h, bool noalias, bool casync)
336{
337 itsInputFrame = &frame; // May be used before endFrame() to get access to the image
338
339 // We will use one image per v4l2 buffer:
340 jevois::RawImage const img = frame.get(casync);
341 std::string const imgname = name + std::to_string(img.bufindex);
342
343 // Get the image by name (will create a default-constructed one the first time a new name is used):
344 auto & im = itsImages[imgname];
345
346 // Set the new pixel data using DMABUF acceleration. This will boil down to a no-op unless image size or format has
347 // changed (including the first time we set it):
348 im.set(frame, itsBackend.getDisplay());
349
350 // Draw it:
351 im.draw(x, y, w, h, noalias, proj * view);
352
353 // Remember which image was drawn last, used by i2d():
354 itsLastDrawnImage = & im;
355 itsUsingScaledImage = frame.hasScaledImage();
356 itsLastDrawnTextLine = -1;
357 if (itsUsingScaledImage)
358 {
359 jevois::RawImage img2 = frame.get2(casync);
360 itsScaledImageFacX = float(img.width) / float(img2.width);
361 itsScaledImageFacY = float(img.height) / float(img2.height);
362 }
363}
364
365// ##############################################################################################################
366void jevois::GUIhelper::drawInputFrame2(char const * name, jevois::InputFrame const & frame, int & x, int & y,
367 unsigned short & w, unsigned short & h, bool noalias, bool casync)
368{
369 // We will use one image per v4l2 buffer:
370 jevois::RawImage const img = frame.get2(casync);
371 std::string const imgname = name + std::to_string(img.bufindex);
372
373 // Get the image by name (will create a default-constructed one the first time a new name is used):
374 auto & im = itsImages2[imgname];
375
376 // Set the new pixel data using DMABUF acceleration. This will boil down to a no-op unless image size or format has
377 // changed (including the first time we set it):
378 im.set2(frame, itsBackend.getDisplay());
379
380 // Draw it:
381 im.draw(x, y, w, h, noalias, proj * view);
382
383 // Remember which image was drawn last, used by i2d():
384 itsLastDrawnImage = & im;
385 itsUsingScaledImage = false;
386 itsLastDrawnTextLine = -1;
387}
388
389// ##############################################################################################################
391{
392 if (itsInputFrame == nullptr)
393 LFATAL("Input frame not available -- only accessible between drawInputFrame() and endFrame()");
394 return itsInputFrame;
395}
396
397// ##############################################################################################################
398void jevois::GUIhelper::onParamChange(gui::twirl const &, float const & newval)
399{
400 // Compute alpha so that more twirl is also more faded (lower alpha). Here, we want to mask out the whole display,
401 // including all GUI drawings except the banner. Hence we will just draw a full-screen semi-transparent rectangle in
402 // endFrame(), using our computed alpha value:
403 itsGlobalAlpha = std::abs(1.0F - newval / 15.0F);
404
405 // Note: we only twirl the display images (itsImages), not the processing images (itsImages2):
406 for (auto & ip : itsImages) ip.second.twirl(newval);
407}
408
409// ##############################################################################################################
410ImVec2 jevois::GUIhelper::i2d(ImVec2 p, char const * name)
411{
412 // Find the image:
413 GPUimage * img;
414
415 if (name == nullptr)
416 {
417 if (itsLastDrawnImage == nullptr) throw std::range_error("You need to call drawImage() or drawInputFrame() first");
418 img = itsLastDrawnImage;
419 }
420 else
421 {
422 std::string nstr = name;
423 auto itr = itsImages.find(nstr);
424 if (itr == itsImages.end())
425 {
426 // Add a zero in case we are dealing with a camera frame (drawImage() and drawInputFrame() add suffixes, but all
427 // image buffers are the same size):
428 itr = itsImages.find(nstr + '0');
429 if (itr == itsImages.end()) throw std::range_error("No previously drawn image with name [" + nstr + "] found");
430 }
431 img = & itr->second;
432 }
433
434 // Adjust size if using scaled image:
435 if (itsUsingScaledImage) { p.x *= itsScaledImageFacX; p.y *= itsScaledImageFacY; }
436
437 // Delegate:
438 return img->i2d(p);
439}
440
441// ##############################################################################################################
442ImVec2 jevois::GUIhelper::i2d(float x, float y, char const * name)
443{ return i2d(ImVec2(x, y), name); }
444
445// ##############################################################################################################
446ImVec2 jevois::GUIhelper::i2ds(ImVec2 p, char const * name)
447{
448 // Find the image:
449 GPUimage * img;
450
451 if (name == nullptr)
452 {
453 if (itsLastDrawnImage == nullptr) throw std::range_error("You need to call drawImage() or drawInputFrame() first");
454 img = itsLastDrawnImage;
455 }
456 else
457 {
458 std::string nstr = name;
459 auto itr = itsImages.find(nstr);
460 if (itr == itsImages.end())
461 {
462 // Add a zero in case we are dealing with a camera frame (drawImage() and drawInputFrame() add suffixes, but all
463 // image buffers are the same size):
464 itr = itsImages.find(nstr + '0');
465 if (itr == itsImages.end()) throw std::range_error("No previously drawn image with name [" + nstr + "] found");
466 }
467 img = & itr->second;
468 }
469
470 // Adjust size if using scaled image:
471 if (itsUsingScaledImage) { p.x *= itsScaledImageFacX; p.y *= itsScaledImageFacY; }
472
473 // Delegate:
474 return img->i2ds(p);
475}
476
477// ##############################################################################################################
478ImVec2 jevois::GUIhelper::i2ds(float x, float y, char const * name)
479{ return i2ds(ImVec2(x, y), name); }
480
481// ##############################################################################################################
482void jevois::GUIhelper::drawLine(float x1, float y1, float x2, float y2, ImU32 col)
483{
484 ImGui::GetBackgroundDrawList()->AddLine(i2d(x1, y1), i2d(x2, y2), col, linethick::get());
485}
486
487// ##############################################################################################################
488void jevois::GUIhelper::drawRect(float x1, float y1, float x2, float y2, ImU32 col, bool filled)
489{
490 auto dlb = ImGui::GetBackgroundDrawList();
491 ImVec2 const tl = i2d(x1, y1);
492 ImVec2 const br = i2d(x2, y2);
493
494 if (filled) dlb->AddRectFilled(tl, br, applyFillAlpha(col));
495 ImDrawFlags constexpr flags = ImDrawFlags_None; // ImDrawFlags_RoundCornersAll
496 float constexpr rounding = 0.0F;
497
498 dlb->AddRect(tl, br, col, rounding, flags, linethick::get());
499}
500
501// ##############################################################################################################
502void jevois::GUIhelper::drawPolyInternal(ImVec2 const * pts, size_t npts, ImU32 col, bool filled)
503{
504 auto dlb = ImGui::GetBackgroundDrawList();
505 float const thick = linethick::get();
506
507 if (filled) dlb->AddConvexPolyFilled(pts, npts, applyFillAlpha(col));
508
509 if (npts > 1)
510 {
511 for (size_t i = 0; i < npts - 1; ++i)
512 {
513 ImVec2 const & p1 = pts[i];
514 ImVec2 const & p2 = pts[i + 1];
515
516 // Draw line if it is not collapsed to a single point:
517 if (p1.x != p2.x || p1.y != p2.y) dlb->AddLine(p1, p2, col, thick);
518 }
519 dlb->AddLine(pts[npts - 1], pts[0], col, thick); // close the polygon
520 }
521}
522
523// ##############################################################################################################
524void jevois::GUIhelper::drawPoly(std::vector<cv::Point> const & pts, ImU32 col, bool filled)
525{
526 size_t const npts = pts.size();
527 if (npts < 3) return;
528
529 ImVec2 iv[npts+1];
530 for (int i = 0; auto const & p : pts) iv[i++] = i2d(p.x, p.y);
531
532 drawPolyInternal(iv, npts, col, filled);
533}
534
535// ##############################################################################################################
536void jevois::GUIhelper::drawPoly(std::vector<cv::Point2f> const & pts, ImU32 col, bool filled)
537{
538 size_t const npts = pts.size();
539 if (npts < 3) return;
540
541 ImVec2 iv[npts];
542 for (int i = 0; auto const & p : pts) iv[i++] = i2d(p.x, p.y);
543
544 drawPolyInternal(iv, npts, col, filled);
545}
546
547// ##############################################################################################################
548void jevois::GUIhelper::drawPoly(cv::Mat const & pts, ImU32 col, bool filled)
549{
550 float const * ptr = pts.ptr<float>(0);
551
552 switch (pts.type())
553 {
554 case CV_32F:
555 {
556 if (pts.rows == 1 || pts.cols == 1)
557 {
558 // x,y,x,y,x,y... on one row or column of length 2N for N vertices:
559 int n = std::max(pts.rows, pts.cols);
560 if (n % 1) LFATAL("Incorrect input " << jevois::dnn::shapestr(pts) << ": odd number of coordinates");
561 if (std::min(pts.rows, pts.cols) < 3) return;
562 n /= 2; ImVec2 p[n]; for (int i = 0; i < n; ++i) { p[i] = i2d(ptr[0], ptr[1]); ptr += 2; }
563 drawPolyInternal(p, n, col, filled);
564 }
565 else if (pts.rows == 2)
566 {
567 // first row: x,x,x,x,x... second row: y,y,y,y,y... with N columns:
568 if (pts.cols < 3) return;
569 ImVec2 p[pts.cols]; for (int i = 0; i < pts.cols; ++i) { p[i] = i2d(ptr[0], ptr[pts.cols]); ++ptr; }
570 drawPolyInternal(p, pts.cols, col, filled);
571 }
572 else if (pts.cols == 2)
573 {
574 // x,y; x,y; x,y; ... for N rows of size 2 each:
575 if (pts.rows < 3) return;
576 ImVec2 p[pts.rows]; for (int i = 0; i < pts.rows; ++i) { p[i] = i2d(ptr[0], ptr[1]); ptr += 2; }
577 drawPolyInternal(p, pts.rows, col, filled);
578 }
579 else LFATAL("Incorrect input " << jevois::dnn::shapestr(pts) << ": 32F must be 1x2N, 2xN, 2Nx1, or Nx2");
580 }
581 break;
582
583 case CV_32FC2:
584 {
585 if (pts.rows == 1)
586 {
587 // (x,y),(x,y),... for N columns of pairs:
588 if (pts.cols < 3) return;
589 ImVec2 p[pts.cols]; for (int i = 0; i < pts.cols; ++i) { p[i] = i2d(ptr[0], ptr[1]); ptr += 2; }
590 drawPolyInternal(p, pts.cols, col, filled);
591 }
592 else if (pts.cols == 1)
593 {
594 // (x,y); (x,y); (x,y); ... for N rows of pairs
595 if (pts.rows < 3) return;
596 ImVec2 p[pts.rows]; for (int i = 0; i < pts.rows; ++i) { p[i] = i2d(ptr[0], ptr[1]); ptr += 2; }
597 drawPolyInternal(p, pts.rows, col, filled);
598 }
599 else LFATAL("Incorrect input " << jevois::dnn::shapestr(pts) << ": 32FC2 must be Nx1 or 1xN");
600 }
601 break;
602
603 default: LFATAL("Incorrect input " << jevois::dnn::shapestr(pts) << ": must be 32F or 32FC2");
604 }
605}
606
607// ##############################################################################################################
608void jevois::GUIhelper::drawCircle(float x, float y, float r, ImU32 col, bool filled)
609{
610 auto dlb = ImGui::GetBackgroundDrawList();
611
612 ImVec2 const center = i2d(x, y);
613 float const rad = i2ds(r, 0).x;
614
615 if (filled) dlb->AddCircleFilled(center, rad, applyFillAlpha(col), 0);
616
617 dlb->AddCircle(center, rad, col, 0, linethick::get());
618}
619
620// ##############################################################################################################
621void jevois::GUIhelper::drawEllipse(float x, float y, float rx, float ry, float rot, ImU32 col, bool filled)
622{
623 auto dlb = ImGui::GetBackgroundDrawList();
624
625 ImVec2 const center = i2d(x, y);
626 ImVec2 const rad = i2ds(rx, ry);
627
628 if (filled) dlb->AddEllipseFilled(center, rad, applyFillAlpha(col), rot, 0 /* auto num segments */);
629
630 dlb->AddEllipse(center, rad, col, rot, 0 /* auto num segments */, linethick::get());
631}
632
633// ##############################################################################################################
634void jevois::GUIhelper::drawText(float x, float y, char const * txt, ImU32 col)
635{
636 ImGui::GetBackgroundDrawList()->AddText(i2d(x, y), col, txt);
637}
638
639// ##############################################################################################################
640void jevois::GUIhelper::drawText(float x, float y, std::string const & txt, ImU32 col)
641{
642 drawText(x, y, txt.c_str(), col);
643}
644
645// ##############################################################################################################
647{
648 unsigned char alpha = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
649 alpha = (unsigned char)(fillalpha::get() * alpha);
650 return (col & ~IM_COL32_A_MASK) | (alpha << IM_COL32_A_SHIFT);
651}
652
653// ##############################################################################################################
654ImVec2 jevois::GUIhelper::iline(int line, char const * name)
655{
656 if (line == -1) line = ++itsLastDrawnTextLine; else itsLastDrawnTextLine = line;
657 ImVec2 p = i2d(0, 0, name);
658 p.x += 5.0F;
659 p.y += 5.0F + (ImGui::GetFontSize() + 5.0F) * line;
660 return p;
661}
662
663// ##############################################################################################################
664void jevois::GUIhelper::itext(char const * txt, ImU32 const & col, int line)
665{
666 ImU32 const c = (col == IM_COL32_BLACK_TRANS) ? ImU32(overlaycolor::get()) : col;
667 ImGui::GetBackgroundDrawList()->AddText(iline(line), c, txt);
668}
669
670// ##############################################################################################################
671void jevois::GUIhelper::itext(std::string const & txt, ImU32 const & col, int line)
672{
673 itext(txt.c_str(), col, line);
674}
675
676// ##############################################################################################################
677void jevois::GUIhelper::iinfo(jevois::InputFrame const & inframe, std::string const & fpscpu,
678 unsigned short winw, unsigned short winh)
679{
680 unsigned short ww, wh;
681 if (winw == 0 || winh == 0) itsBackend.getWindowSize(ww, wh); else { ww = winw; wh = winh; }
682
683 jevois::RawImage const & inimg = inframe.get();
684 std::string cam2str;
685 if (inframe.hasScaledImage())
686 {
687 jevois::RawImage const & inimg2 = inframe.get2();
688 cam2str += jevois::sformat(" + %s:%dx%d", jevois::fccstr(inimg2.fmt).c_str(), inimg2.width, inimg2.height);
689 }
690 std::string const msg = jevois::sformat("%s, Camera: %s:%dx%d%s, Display: RGBA:%dx%d", fpscpu.c_str(),
691 jevois::fccstr(inimg.fmt).c_str(), inimg.width, inimg.height,
692 cam2str.c_str(), ww, wh);
693
694 ImGui::GetBackgroundDrawList()->AddText(ImVec2(10, wh-10-ImGui::GetFontSize()), overlaycolor::get(), msg.c_str());
695}
696
697// ##############################################################################################################
698void jevois::GUIhelper::releaseImage(char const * name)
699{
700 auto itr = itsImages.find(name);
701 if (itr != itsImages.end()) itsImages.erase(itr);
702}
703
704// ##############################################################################################################
705void jevois::GUIhelper::releaseImage2(char const * name)
706{
707 auto itr = itsImages2.find(name);
708 if (itr != itsImages2.end()) itsImages2.erase(itr);
709}
710
711// ##############################################################################################################
712ImVec2 jevois::GUIhelper::d2i(ImVec2 p, char const * name)
713{
714 // Find the image:
715 GPUimage * img;
716 float fx = 1.0F, fy = 1.0F;
717
718 if (name == nullptr)
719 {
720 if (itsLastDrawnImage == nullptr) throw std::range_error("You need to call drawImage() or drawInputFrame() first");
721 img = itsLastDrawnImage;
722 if (itsUsingScaledImage) { fx = 1.0F / itsScaledImageFacX; fy = 1.0F / itsScaledImageFacY; }
723 }
724 else
725 {
726 std::string nstr = name;
727 auto itr = itsImages.find(nstr);
728 if (itr == itsImages.end())
729 {
730 // Add a zero in case we are dealing with a camera frame (drawImage() and drawInputFrame() add suffixes, but all
731 // image buffers are the same size):
732 itr = itsImages.find(nstr + '0');
733 if (itr == itsImages.end()) throw std::range_error("No previously drawn image with name [" + nstr + "] found");
734 }
735 img = & itr->second;
736 }
737
738 // Delegate:
739 ImVec2 ret = img->d2i(p);
740 ret.x *= fx; ret.y *= fy;
741 return ret;
742}
743
744// ##############################################################################################################
745ImVec2 jevois::GUIhelper::d2i(float x, float y, char const * name)
746{ return d2i(ImVec2(x, y), name); }
747
748// ##############################################################################################################
749ImVec2 jevois::GUIhelper::d2is(ImVec2 p, char const * name)
750{
751 // Find the image:
752 GPUimage * img;
753 float fx = 1.0F, fy = 1.0F;
754
755 if (name == nullptr)
756 {
757 if (itsLastDrawnImage == nullptr) throw std::range_error("You need to call drawImage() or drawInputFrame() first");
758 img = itsLastDrawnImage;
759 if (itsUsingScaledImage) { fx = 1.0F / itsScaledImageFacX; fy = 1.0F / itsScaledImageFacY; }
760 }
761 else
762 {
763 std::string nstr = name;
764 auto itr = itsImages.find(nstr);
765 if (itr == itsImages.end())
766 {
767 // Add a zero in case we are dealing with a camera frame (drawImage() and drawInputFrame() add suffixes, but all
768 // image buffers are the same size):
769 itr = itsImages.find(nstr + '0');
770 if (itr == itsImages.end()) throw std::range_error("No previously drawn image with name [" + nstr + "] found");
771 }
772 img = & itr->second;
773 }
774
775 // Delegate:
776 ImVec2 ret = img->d2is(p);
777 ret.x *= fx; ret.y *= fy;
778 return ret;
779}
780
781// ##############################################################################################################
782ImVec2 jevois::GUIhelper::d2is(float x, float y, char const * name)
783{ return d2is(ImVec2(x, y), name); }
784
785// ##############################################################################################################
787{
788 // Decide whether to show mouse cursor based on idle state:
789 ImGui::GetIO().MouseDrawCursor = ! itsIdle;
790
791 // Draw our JeVois GUI on top of everything:
792 if (itsIdle == false) drawJeVoisGUI();
793
794 // If compiling, draw compilation window:
795 if (itsCompileState != CompilationState::Idle) compileModule();
796
797 // Do we want to fade out the whole display?
798 if (itsGlobalAlpha != 1.0F)
799 {
800 if (itsGlobalAlpha < 0.0F || itsGlobalAlpha > 1.0F)
801 {
802 LERROR("Invalid global alpha " << itsGlobalAlpha << " -- RESET TO 1.0");
803 itsGlobalAlpha = 1.0F;
804 }
805 else
806 {
807 auto dlb = ImGui::GetBackgroundDrawList();
808 cv::Size const ws = winsize::get();
809 dlb->AddRectFilled(ImVec2(0, 0), ImVec2(ws.width, ws.height), ImColor(0.0F, 0.0F, 0.0F, 1.0F - itsGlobalAlpha));
810 }
811 }
812
813 // Do we have a demo banner to show?
814 if (itsBannerTitle.empty() == false)
815 {
816 ImGui::SetNextWindowPos(ImVec2(800, 500));
817 ImGui::SetNextWindowSize(ImVec2(1000, 400));
818 ImGui::PushStyleColor(ImGuiCol_WindowBg, 0xf0e0ffe0); // ABGR
819 ImGui::Begin("JeVois-Pro Demo Mode", nullptr, ImGuiWindowFlags_NoResize |
820 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse |
821 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings);
822 ImGui::SetWindowFontScale(1.5F);
823 ImGui::TextUnformatted(" ");
824 ImGui::TextUnformatted(itsBannerTitle.c_str());
825 ImGui::TextUnformatted(" ");
826 ImGui::SetWindowFontScale(1.0F);
827
828 int wrap = ImGui::GetWindowSize().x - ImGui::GetFontSize() * 2.0f;
829 ImGui::PushTextWrapPos(wrap);
830 ImGui::TextUnformatted(itsBannerMsg.c_str());
831 ImGui::PopTextWrapPos();
832
833 ImGui::SetCursorPosY(ImGui::GetWindowSize().y - ImGui::CalcTextSize("X").y * 2.3F);
834 ImGui::Separator();
835
836 if (ImGui::Button("Skip to Next Demo")) engine()->nextDemo();
837 ImGui::SetItemDefaultFocus();
838 ImGui::SameLine(600);
839 if (ImGui::Button("Abort Demo Mode")) engine()->abortDemo();
840 ImGui::End();
841 ImGui::PopStyleColor();
842 }
843
844 // Render everything and swap buffers:
845 itsBackend.render();
846
847 itsInputFrame = nullptr; // invalidated until next drawInputFrame()
848 itsEndFrameCalled = true;
849}
850
851// ##############################################################################################################
853{
854 // Set window size applied only on first use ever, otherwise from imgui.ini:
855 ImGui::SetNextWindowPos(ImVec2(920, 358), ImGuiCond_FirstUseEver);
856 ImGui::SetNextWindowSize(ImVec2(941, 639), ImGuiCond_FirstUseEver);
857
858 if (ImGui::Begin(itsWindowTitle.c_str(), nullptr /* no closing */))//, ImGuiWindowFlags_MenuBar))
859 {
860 //drawMenuBar();
861 drawModuleSelect();
862
863 //ImGui::Text("Camera + Process + Display: %.2f fps", ImGui::GetIO().Framerate);
864 ImGui::Separator();
865
866 if (ImGui::BeginTabBar("##tabs", ImGuiTabBarFlags_None))
867 {
868
869 if (ImGui::BeginTabItem("Info"))
870 {
871 drawInfo();
872 ImGui::EndTabItem();
873 }
874
875 if (ImGui::BeginTabItem("Parameters"))
876 {
877 drawParameters();
878 ImGui::EndTabItem();
879 }
880
881 if (ImGui::BeginTabItem("Console"))
882 {
883 drawConsole();
884 ImGui::EndTabItem();
885 }
886
887 if (ImGui::BeginTabItem("Camera"))
888 {
889 drawCamCtrls();
890 ImGui::EndTabItem();
891 }
892
893 if (ImGui::BeginTabItem("Config"))
894 {
895 itsCfgEditor->draw();
896 ImGui::EndTabItem();
897 }
898
899 if (ImGui::BeginTabItem("Code"))
900 {
901 itsCodeEditor->draw();
902 ImGui::EndTabItem();
903 }
904
905 if (ImGui::BeginTabItem("System"))
906 {
907 drawSystem();
908 ImGui::EndTabItem();
909 }
910
911 if (ImGui::BeginTabItem("Tweaks"))
912 {
913 drawTweaks();
914 ImGui::EndTabItem();
915 }
916 ImGui::EndTabBar();
917 }
918 ImGui::End();
919 }
920 else ImGui::End(); // do not draw anything if window is collapsed
921
922 // Show style editor window, imgui demo, etc if user requested it:
923 if (itsShowStyleEditor)
924 {
925 ImGui::Begin("GUI Style Editor", &itsShowStyleEditor);
926 ImGui::ShowStyleEditor();
927 ImGui::End();
928 }
929 if (itsShowAppMetrics) ImGui::ShowMetricsWindow(&itsShowAppMetrics);
930 if (itsShowImGuiDemo) ImGui::ShowDemoWindow(&itsShowImGuiDemo);
931
932 // Show Hardware serial monitor if desired:
933 if (itsShowHardSerialWin)
934 {
935 // Set window size applied only on first use ever, otherwise from imgui.ini:
936 ImGui::SetNextWindowSize(ImVec2(700, 500), ImGuiCond_FirstUseEver);
937
938 // Open or display window until user closes it:
939 ImGui::Begin("Hardware 4-pin Serial Monitor", &itsShowHardSerialWin);
940 try { engine()->getComponent<jevois::GUIserial>("serial")->draw(); }
941 catch (...)
942 {
943 ImGui::TextUnformatted("No Hardware serial port found!");
944 ImGui::Separator();
945 ImGui::TextUnformatted("Check engine:serialdev parameter, and");
946 ImGui::TextUnformatted("that engine:serialmonitors is true.");
947 }
948 ImGui::End();
949 }
950
951 // Show USB serial monitor if desired:
952 if (itsShowUsbSerialWin)
953 {
954 // Set window size applied only on first use ever, otherwise from imgui.ini:
955 ImGui::SetNextWindowSize(ImVec2(700, 500), ImGuiCond_FirstUseEver);
956
957 // Open or display window until user closes it:
958 ImGui::Begin("USB Serial Monitor", &itsShowUsbSerialWin);
959 try { engine()->getComponent<jevois::GUIserial>("usbserial")->draw(); }
960 catch (...)
961 {
962 ImGui::TextUnformatted("No USB serial port found!");
963 ImGui::Separator();
964 ImGui::TextUnformatted("Check engine:usbserialdev parameter, and");
965 ImGui::TextUnformatted("that engine:serialmonitors is true.");
966 }
967 ImGui::End();
968 }
969
970 // Draw an error popup, if any exception was received through reportError():
971 drawErrorPopup();
972}
973
974// ##############################################################################################################
976{
977 if (ImGui::BeginMenuBar())
978 {
979 if (ImGui::BeginMenu("File"))
980 {
981 if (ImGui::MenuItem("Quit")) engine()->quit();
982
983 //ShowExampleMenuFile();
984 ImGui::EndMenu();
985 }
986 /*
987 if (ImGui::BeginMenu("Machine Vision Module"))
988 {
989 auto e = engine();
990
991 // Start with the JeVois-Pro mappings:
992 size_t idx = 0;
993 e->foreachVideoMapping([&idx,&e](jevois::VideoMapping const & m) {
994 if (m.ofmt == JEVOISPRO_FMT_GUI && ImGui::MenuItem(m.str().c_str()))
995 e->requestSetFormat(idx++); else ++idx;
996 });
997 ImGui::Separator();
998
999 // Then the compatibility JeVois modules:
1000 idx = 0;
1001 e->foreachVideoMapping([&idx,&e](jevois::VideoMapping const & m) {
1002 if (m.ofmt != 0 && m.ofmt != JEVOISPRO_FMT_GUI && ImGui::MenuItem(m.str().c_str()))
1003 e->requestSetFormat(idx++); else ++idx;
1004 });
1005
1006 ImGui::Separator();
1007 // Finally the no-USB modules: FIXME - those currently kill the gui since no startFrame() is emitted
1008 idx = 0;
1009 e->foreachVideoMapping([&idx,&e](jevois::VideoMapping const & m) {
1010 if (m.ofmt == 0 && ImGui::MenuItem(m.str().c_str()))
1011 e->requestSetFormat(idx++); else ++idx;
1012 });
1013
1014 ImGui::EndMenu();
1015 }
1016 */
1017 if (ImGui::BeginMenu("Tools"))
1018 {
1019 ImGui::MenuItem("ImGui Style Editor", NULL, &itsShowStyleEditor);
1020 ImGui::MenuItem("ImGui Metrics/Debugger", NULL, &itsShowAppMetrics);
1021 ImGui::MenuItem("ImGui Demo/Doc", NULL, &itsShowImGuiDemo);
1022
1023 ImGui::EndMenu();
1024 }
1025
1026 ImGui::EndMenuBar();
1027 }
1028}
1029
1030// ##############################################################################################################
1032{
1033 static std::map<std::string, size_t> mods;
1034 auto e = engine();
1035 static std::string currstr;
1036
1037 // Refresh the list of modules if needed:
1038 ImGui::AlignTextToFramePadding();
1039 ImGui::TextUnformatted("Module:");
1040 ImGui::SameLine();
1041 ImGui::SetNextItemWidth(6 * ImGui::GetFontSize() + 5);
1042 if (ImGui::Combo("##typemachinevisionmodule", &itsVideoMappingListType, "Pro/GUI\0Legacy\0Headless\0\0")
1043 || currstr.empty() || itsRefreshVideoMappings)
1044 {
1045 // Recompute mods for the selected type:
1046 itsRefreshVideoMappings = false;
1047 mods.clear();
1048
1049 switch (itsVideoMappingListType)
1050 {
1051 case 0:
1052 {
1053 // JeVois-Pro GUI mappings:
1054 size_t idx = 0;
1055 e->foreachVideoMapping([&idx](jevois::VideoMapping const & m) {
1056 if (m.ofmt == JEVOISPRO_FMT_GUI) mods[m.menustr()] = idx;
1057 ++idx;
1058 });
1059 }
1060 break;
1061
1062 case 1:
1063 {
1064 // Legacy JeVois mappings:
1065 size_t idx = 0;
1066 e->foreachVideoMapping([&idx](jevois::VideoMapping const & m) {
1067 if (m.ofmt != 0 && m.ofmt != JEVOISPRO_FMT_GUI) mods[m.menustr()] = idx;
1068 ++idx;
1069 });
1070 }
1071 break;
1072
1073 case 2:
1074 {
1075 // Headless modules
1076 size_t idx = 0;
1077 e->foreachVideoMapping([&idx](jevois::VideoMapping const & m) {
1078 if (m.ofmt == 0) mods[m.menustr()] = idx;
1079 ++idx;
1080 });
1081 }
1082 break;
1083
1084 default: LFATAL("Internal error, itsVideoMappingListType=" << itsVideoMappingListType);
1085 }
1086
1087 // Refresh our display string:
1088 currstr = e->getCurrentVideoMapping().menustr().c_str();
1089 }
1090
1091 // Draw the module selector and allow selection:
1092 ImGui::SameLine();
1093
1094 if (ImGui::BeginCombo("##machinevisionmodule", currstr.c_str()))
1095 {
1096 for (auto const & m : mods)
1097 {
1098 bool is_selected = false;
1099 if (ImGui::Selectable(m.first.c_str(), is_selected))
1100 {
1101 e->requestSetFormat(m.second);
1102 currstr.clear();
1103 }
1104
1105 }
1106 ImGui::EndCombo();
1107 }
1108}
1109
1110// ##############################################################################################################
1112{
1113 std::shared_ptr<jevois::Module> m = engine()->module();
1114 if (m)
1115 {
1116 // Get the icon and display it:
1117 if (itsIcon.loaded() == false)
1118 try { itsIcon.load(m->absolutePath("icon.png")); }
1119 catch (...)
1120 {
1121 // Load a default C++ or Python icon:
1122 LERROR("This module has no icon -- USING DEFAULT");
1123 try
1124 {
1125 if (engine()->getCurrentVideoMapping().ispython) itsIcon.load(JEVOIS_SHARE_PATH "/icons/py.png");
1126 else itsIcon.load(JEVOIS_SHARE_PATH "/icons/cpp.png");
1127 }
1128 catch (...)
1129 {
1130 // Defaults icons not found, just use a blank:
1131 cv::Mat blank(32, 32, CV_8UC4, 0);
1132 itsIcon.load(blank);
1133 }
1134 }
1135
1136 if (itsIcon.loaded())
1137 {
1138 int const siz = ImGui::CalcTextSize(" ").x;
1139 itsIcon.draw(ImGui::GetCursorScreenPos(), ImVec2(siz, siz));
1140 }
1141
1142 // Get the html doc if we have not yet parsed it:
1143 if (itsModName.empty())
1144 {
1145 std::filesystem::path fname = m->absolutePath("modinfo.html");
1146 std::ifstream ifs(fname);
1147 if (ifs.is_open() == false)
1148 {
1149 // No modinfo file, maybe we can create one if we have the source:
1150 LINFO("Recomputing module's modinfo.html ...");
1151 jevois::VideoMapping const & vm = engine()->getCurrentVideoMapping();
1152 try
1153 {
1154 std::string const cmdout =
1155 jevois::system("cd " + m->absolutePath().string() + " && "
1156 "JEVOIS_SRC_ROOT=none jevoispro-modinfo " + vm.modulename, true);
1157
1158 if (! std::filesystem::exists(fname))
1159 throw std::runtime_error("Failed to create " + vm.modinfopath() + ": " + cmdout);
1160 }
1161 catch (...) { itsModName = vm.modulename; itsModAuth = "Cannot read file: " + fname.string(); }
1162 // If we did not throw, itsModName is still empty, but now modinfo.html exists. Will load it next time.
1163 }
1164 else
1165 {
1166 int state = 0;
1167 for (std::string s; std::getline(ifs, s); )
1168 switch (state)
1169 {
1170 case 0: // Looking for module display name
1171 {
1172 std::string const str = jevois::extractString(s, "<td class=modinfoname>", "</td>");
1173 if (str.empty() == false) { itsModName = str; ++state; }
1174 break;
1175 }
1176
1177 case 1: // Looking for short synopsis
1178 {
1179 std::string const str = jevois::extractString(s, "<td class=modinfosynopsis>", "</td>");
1180 if (str.empty() == false) { itsModDesc = str; ++state; }
1181 break;
1182 }
1183
1184 case 2: // Looking for author info
1185 {
1186 std::string const str = jevois::extractString(s, "<table class=modinfoauth width=100%>", "</table>");
1187 if (str.empty() == false)
1188 {
1189 itsModAuth = jevois::join(jevois::split(str, "<[^<]*>"), " ").substr(2); // remove HTML tags
1190 ++state;
1191 }
1192 break;
1193 }
1194
1195 case 3: // Looking for language info
1196 {
1197 std::string const str = jevois::extractString(s, "<table class=moduledata>", "</table>");
1198 if (str.empty() == false)
1199 {
1200 itsModLang = jevois::join(jevois::split(str, "<[^<]*>"), " "); // remove HTML tags
1201 auto tok = jevois::split(itsModLang, "&nbsp;");
1202 if (tok.size() >= 3)
1203 {
1204 if (jevois::stringStartsWith(tok[2], "C++")) itsModLang = "Language: C++";
1205 else itsModLang = "Language: Python";
1206 }
1207 else itsModLang = "Language: Unknown";
1208 ++state;
1209 }
1210 break;
1211 }
1212
1213 case 4: // Looking for main doc start
1214 {
1215 std::string const str = jevois::extractString(s, "<td class=modinfodesc>", "");
1216 if (str.empty() == false)
1217 {
1218 std::string str2 = jevois::extractString(str, "<div class=\"textblock\">", "");
1219 str2 = jevois::join(jevois::split(str2, "<[^<]*>"), " ");
1220 size_t idx = str2.find_first_not_of(" "); if (idx != str2.npos) str2 = str2.substr(idx);
1221 itsModDoc.emplace_back(str2);
1222 ++state;
1223 }
1224 break;
1225 }
1226
1227 case 5: // Extracting main doc until its end marker is encountered
1228 {
1229 if (s == "</div></td></tr>") ++state;
1230 else
1231 {
1232 std::string ss = jevois::join(jevois::split(s, "<[^<]*>"), " ");
1233 std::string prefix;
1234
1235 if (s.find("/h1>") != s.npos || s.find("/h2>") != s.npos || s.find("/h3>") != s.npos) prefix = "* ";
1236 if (s.find("<li>") != s.npos) prefix = "- ";
1237 ss = prefix + ss;
1238
1239 // Finally fix multiple spaces:
1240 // https://stackoverflow.com/questions/8362094/replace-multiple-spaces-with-one-space-in-a-string
1241 std::string::iterator new_end =
1242 std::unique(ss.begin(), ss.end(), [](char lhs, char rhs) { return (lhs == rhs) && (lhs == ' '); });
1243 ss.erase(new_end, ss.end());
1244 size_t idx = ss.find_first_not_of(" "); if (idx != ss.npos) ss = ss.substr(idx);
1245 itsModDoc.push_back(ss);
1246 }
1247 }
1248
1249 default: break;
1250 }
1251 ifs.close();
1252 }
1253 }
1254
1255 // Display the doc:
1256 ImGui::TextUnformatted((" " + itsModName).c_str());
1257 ImGui::TextUnformatted((" " + itsModDesc).c_str());
1258 ImGui::TextUnformatted((" " + itsModAuth + " " + itsModLang).c_str());
1259 ImGui::TextUnformatted(" ");
1260
1261 int wrap = ImGui::GetCursorPos().x + ImGui::GetWindowSize().x - ImGui::GetFontSize() * 2.0f;
1262 if (wrap < 200) wrap = 200;
1263 ImGui::PushTextWrapPos(wrap);
1264
1265 bool show = true;
1266 for (std::string const & s : itsModDoc)
1267 {
1268 // Create collapsible header and get its collapsed status:
1269 if (jevois::stringStartsWith(s, "* "))
1270 show = ImGui::CollapsingHeader(s.c_str() + 2, ImGuiTreeNodeFlags_DefaultOpen);
1271 else if (show)
1272 {
1273 // If header not collapsed, show data:
1274 if (jevois::stringStartsWith(s, "- ")) { ImGui::Bullet(); ImGui::TextUnformatted(s.c_str() + 2); }
1275 else ImGui::TextUnformatted(s.c_str());
1276 }
1277 }
1278 ImGui::PopTextWrapPos();
1279 }
1280 else
1281 ImGui::TextUnformatted("No JeVois Module currently loaded.");
1282}
1283
1284// ##############################################################################################################
1285void jevois::GUIhelper::setparstr(std::string const & descriptor, std::string const & val)
1286{
1287 try { engine()->setParamStringUnique(descriptor, val); }
1288 catch (...) { reportError(jevois::warnAndIgnoreException()); }
1289 // drawGUI() will call drawErrorPopup() which will show the popup
1290}
1291
1292// ##############################################################################################################
1294{
1295 static bool show_frozen = true; static bool show_system = false;
1296
1297 toggleButton("Show Frozen Parameters", &show_frozen);
1298 ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::CalcTextSize("Show System Parameters").x - 30.0f);
1299 toggleButton("Show System Parameters", &show_system);
1300
1301 jevois::Engine * e = engine();
1302 jevois::Component * c; if (show_system) c = e; else c = e->module().get();
1303
1304 // Stop here if we want to show module params but we have no module:
1305 if (c == nullptr) { ImGui::TextUnformatted("No module loaded."); return; }
1306
1307 // Record any ambiguous parameter names, so we can prefix them by component name:
1308 std::set<std::string> pnames, ambig;
1309
1310 // Get all the parameter summaries, except for hidden params:
1311 std::map<std::string /* categ */, std::vector<jevois::ParameterSummary>> psm;
1312 c->foreachParam([this, &psm, &pnames, &ambig](std::string const &, jevois::ParameterBase * p) {
1313 if (p->hidden()) return;
1315 if (pnames.insert(psum.name).second == false) ambig.insert(psum.name);
1316 psm[psum.category].emplace_back(psum); } );
1317
1318 // Stop here if no params to display:
1319 if (psm.empty()) { ImGui::TextUnformatted("This module has no parameters."); return; }
1320
1321 // Create a collapsing header for each parameter category:
1322 int widgetnum = 0; // we need a unique ID for each widget
1323 float maxlen = 0.0f;
1324 for (auto const & pp : psm)
1325 {
1326 // Do not even show a header if all params under it are frozen and we do not want to show frozen params:
1327 if (show_frozen == false)
1328 {
1329 bool all_frozen = true;
1330 for (auto const & ps : pp.second) if (ps.frozen == false) { all_frozen = false; break; }
1331 if (all_frozen) continue;
1332 }
1333
1334 // Show a header for this category:
1335 if (ImGui::CollapsingHeader(pp.first.c_str()))
1336 {
1337 ImGui::Columns(3, "parameters");
1338
1339 // Create a widget for each param:
1340 for (auto const & ps : pp.second)
1341 {
1342 // Skip if frozen and we do not want to show frozen params:
1343 if (ps.frozen && show_frozen == false) continue;
1344
1345 // We need a unique ID for each ImGui widget, and we will use no visible widget name:
1346 static char wname[16]; snprintf(wname, 16, "##p%d", widgetnum);
1347 bool rst = true; // will set to false if we do not want a reset button
1348
1349 // Start with parameter name and a tooltip with its descriptor:
1350 ImGui::AlignTextToFramePadding();
1351 std::string nam = ps.name;
1352 if (ambig.contains(nam))
1353 {
1354 // Here we are only going to disambiguate by the owning component name:
1355 auto tok = jevois::split(ps.descriptor, ":");
1356 if (tok.size() >= 2) nam = tok[tok.size()-2] + ':' + nam;
1357 }
1358
1359 ImGui::TextUnformatted(nam.c_str());
1360 if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", ps.descriptor.c_str());
1361 maxlen = std::max(maxlen, ImGui::CalcTextSize(nam.c_str()).x);
1362
1363 // Add a tooltip:
1364 ImGui::NextColumn();
1365 ImGui::AlignTextToFramePadding();
1366 std::string const & vtype = ps.valuetype;
1367 if (jevois::stringStartsWith(ps.validvalues, "None:"))
1368 helpMarker(ps.description.c_str(), ("Parameter type: " + vtype).c_str());
1369 else
1370 helpMarker(ps.description.c_str(), ("Parameter type: " + vtype).c_str(),
1371 ("Allowed values: " + ps.validvalues).c_str());
1372
1373 // Now a widget for the parameter:
1374 ImGui::NextColumn();
1375 bool const is_uchar = (vtype == "unsigned char");
1376 bool const is_int = (vtype == "short" || vtype == "int" || vtype == "long int" || vtype == "long long int");
1377 bool const is_uint = (is_uchar || vtype == "unsigned short" || vtype == "unsigned int" ||
1378 vtype == "unsigned long int" || vtype == "unsigned long long int" || vtype == "size_t");
1379 bool const is_real = (vtype == "float" || vtype == "double" || vtype == "long double");
1380
1381 // Grey out the item if it is disabled:
1382 int textflags = ImGuiInputTextFlags_EnterReturnsTrue;
1383 if (ps.frozen)
1384 {
1385 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
1386 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
1387 textflags |= ImGuiInputTextFlags_ReadOnly;
1388 }
1389
1390 // ----------------------------------------------------------------------
1391 if (jevois::stringStartsWith(ps.validvalues, "List:["))
1392 {
1393 std::string vals = ps.validvalues.substr(6, ps.validvalues.size() - 7);
1394 auto vv = jevois::split(vals, "\\|");
1395
1396 if (vv.empty() == false)
1397 {
1398 // Find the index of the current value:
1399 int index = 0; for (auto const & v : vv) if (v == ps.value) break; else ++index;
1400
1401 // Draw a combobox widget:
1402 if (combo(wname, vv, index)) setparstr(ps.descriptor, vv[index]);
1403 }
1404 }
1405 // ----------------------------------------------------------------------
1406 else if (is_uchar || jevois::stringStartsWith(ps.validvalues, "Range:["))
1407 {
1408 if (is_uchar)
1409 {
1410 // For unsigned char, use a slider:
1411 unsigned long val; jevois::paramStringToVal(ps.value, val);
1412 std::string rng = ps.validvalues.substr(7, ps.validvalues.size() - 8);
1413
1414 // We may or not have a range spec, if not, use 0..255:
1415 if (rng.empty())
1416 {
1417 long mi = 0, ma = 255;
1418 if (ImGui::SliderScalar(wname, ImGuiDataType_U64, &val, &mi, &ma))
1419 setparstr(ps.descriptor, std::to_string(val));
1420 }
1421 else
1422 {
1424 if (ImGui::SliderScalar(wname, ImGuiDataType_U64, &val, &r.min(), &r.max()))
1425 setparstr(ps.descriptor, std::to_string(val));
1426 }
1427 }
1428 else if (is_uint)
1429 {
1430 // For uint that has a range specified, use a slider:
1431 std::string rng = ps.validvalues.substr(7, ps.validvalues.size() - 8);
1433 unsigned long val; jevois::paramStringToVal(ps.value, val);
1434 if (ImGui::SliderScalar(wname, ImGuiDataType_U64, &val, &r.min(), &r.max()))
1435 setparstr(ps.descriptor, std::to_string(val));
1436 }
1437 else if (is_int)
1438 {
1439 // For int that has a range specified, use a slider:
1440 std::string rng = ps.validvalues.substr(7, ps.validvalues.size() - 8);
1442 long val; jevois::paramStringToVal(ps.value, val);
1443 if (ImGui::SliderScalar(wname, ImGuiDataType_S64, &val, &r.min(), &r.max()))
1444 setparstr(ps.descriptor, std::to_string(val));
1445 }
1446 else if (is_real)
1447 {
1448 // For real with a range specified, use a slider:
1449 std::string rng = ps.validvalues.substr(7, ps.validvalues.size() - 8);
1451 double val; jevois::paramStringToVal(ps.value, val);
1452 if (ImGui::SliderScalar(wname, ImGuiDataType_Double, &val, &r.min(), &r.max()))
1453 setparstr(ps.descriptor, std::to_string(val));
1454 }
1455 else
1456 {
1457 // For more complex types, just allow free typing, parameter will do the checking:
1458 char buf[256]; strncpy(buf, ps.value.c_str(), sizeof(buf)-1);
1459 if (ImGui::InputText(wname, buf, sizeof(buf), textflags))
1460 setparstr(ps.descriptor, buf);
1461 }
1462 }
1463 // ----------------------------------------------------------------------
1464 else if (vtype == "jevois::Range<unsigned char>")
1465 {
1466 // For a range parameter, use a double drag:
1468 int mi = val.min(), ma = val.max();
1469 if (ImGui::DragIntRange2(wname, &mi, &ma, 0, 0, 255, "Min: %d", "Max: %d"))
1470 setparval(ps.descriptor, jevois::Range<unsigned char>(mi, ma));
1471 }
1472 // ----------------------------------------------------------------------
1473 else if (vtype == "bool")
1474 {
1475 bool val; jevois::paramStringToVal(ps.value, val);
1476 if (ImGui::Checkbox(wname, &val)) setparval(ps.descriptor, val);
1477 }
1478 // ----------------------------------------------------------------------
1479 else if (vtype == "ImColor")
1480 {
1481 ImColor val; jevois::paramStringToVal(ps.value, val);
1482 if (ImGui::ColorEdit4(wname, (float *)&val, ImGuiColorEditFlags_AlphaPreview)) setparval(ps.descriptor, val);
1483 }
1484 // ----------------------------------------------------------------------
1485 else
1486 {
1487 // User will type in some value, parameter will check it:
1488 char buf[256]; strncpy(buf, ps.value.c_str(), sizeof(buf)-1);
1489 if (ImGui::InputText(wname, buf, sizeof(buf), textflags))
1490 setparstr(ps.descriptor, buf);
1491 }
1492
1493 // Possibly add a reset button:
1494 if (rst)
1495 {
1496 static char rname[24]; snprintf(rname, 24, "Reset##%d", widgetnum);
1497 ImGui::SameLine();
1498 if (ImGui::Button(rname)) setparstr(ps.descriptor, ps.defaultvalue);
1499 }
1500
1501 // Restore any grey out:
1502 if (ps.frozen)
1503 {
1504 ImGui::PopItemFlag();
1505 ImGui::PopStyleVar();
1506 }
1507
1508 // Ready for next row:
1509 ImGui::NextColumn(); ++widgetnum;
1510 }
1511
1512 // Back to single column before the next param categ:
1513 ImGui::EndColumns();
1514 }
1515 }
1516
1517 if (maxlen)
1518 {
1519 ImGui::Columns(3, "parameters");
1520 ImGui::SetColumnWidth(0, maxlen + ImGui::CalcTextSize("XX").x);
1521 ImGui::SetColumnWidth(1, ImGui::CalcTextSize(" (?) ").x);
1522 ImGui::SetColumnWidth(2, 2000);
1523 ImGui::EndColumns();
1524 }
1525}
1526
1527// ##############################################################################################################
1529{
1530 jevois::Engine * e = engine();
1531
1532 // Start with toggle buttons for serlog and serout:
1533 bool slusb = false, slhard = false, schanged = false;
1534 auto sl = e->getParamValUnique<jevois::engine::SerPort>("engine:serlog");
1535 switch (sl)
1536 {
1537 case jevois::engine::SerPort::None: slusb = false; slhard = false; break;
1538 case jevois::engine::SerPort::All: slusb = true; slhard = true; break;
1539 case jevois::engine::SerPort::Hard: slusb = false; slhard = true; break;
1540 case jevois::engine::SerPort::USB: slusb = true; slhard = false; break;
1541 }
1542 ImGui::AlignTextToFramePadding();
1543 ImGui::TextUnformatted("Log messages:"); ImGui::SameLine();
1544 if (itsUSBserial == false) // grey out USB button if no driver
1545 {
1546 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
1547 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
1548 if (toggleButton("USB##serlogu", &slusb)) schanged = true;
1549 ImGui::PopItemFlag();
1550 ImGui::PopStyleVar();
1551#ifdef JEVOIS_PLATFORM
1552 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
1553 {
1554 ImGui::BeginTooltip();
1555 ImGui::TextUnformatted("Disabled - enable USB serial in the System tab");
1556 ImGui::EndTooltip();
1557 }
1558#else
1559 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
1560 {
1561 ImGui::BeginTooltip();
1562 ImGui::TextUnformatted("Disabled - not available on host");
1563 ImGui::EndTooltip();
1564 }
1565#endif
1566 }
1567 else if (toggleButton("USB##serlogu", &slusb)) schanged = true;
1568
1569 ImGui::SameLine(); if (toggleButton("Hard##serlogh", &slhard)) schanged = true;
1570 ImGui::SameLine(); toggleButton("Cons##serlogc", &itsSerLogEnabled);
1571 ImGui::SameLine(0, 50);
1572
1573 bool sousb = false, sohard = false;
1574 auto so = e->getParamValUnique<jevois::engine::SerPort>("engine:serout");
1575 switch (so)
1576 {
1577 case jevois::engine::SerPort::None: sousb = false; sohard = false; break;
1578 case jevois::engine::SerPort::All: sousb = true; sohard = true; break;
1579 case jevois::engine::SerPort::Hard: sousb = false; sohard = true; break;
1580 case jevois::engine::SerPort::USB: sousb = true; sohard = false; break;
1581 }
1582 ImGui::TextUnformatted("Module output:"); ImGui::SameLine();
1583 if (itsUSBserial == false) // grey out USB button if no driver
1584 {
1585 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
1586 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
1587 if (toggleButton("USB##seroutu", &sousb)) schanged = true;
1588 ImGui::PopItemFlag();
1589 ImGui::PopStyleVar();
1590#ifdef JEVOIS_PLATFORM
1591 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
1592 {
1593 ImGui::BeginTooltip();
1594 ImGui::TextUnformatted("Disabled - enable USB serial in the System tab");
1595 ImGui::EndTooltip();
1596 }
1597#else
1598 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
1599 {
1600 ImGui::BeginTooltip();
1601 ImGui::Text("Disabled - not available on host");
1602 ImGui::EndTooltip();
1603 }
1604#endif
1605 }
1606 else if (toggleButton("USB##seroutu", &sousb)) schanged = true;
1607 ImGui::SameLine(); if (toggleButton("Hard##serouth", &sohard)) schanged = true;
1608 ImGui::SameLine(); toggleButton("Cons##seroutc", &itsSerOutEnabled);
1609
1610 if (schanged)
1611 {
1612 if (slusb)
1613 {
1614 if (slhard) e->setParamValUnique("engine:serlog", jevois::engine::SerPort::All);
1615 else e->setParamValUnique("engine:serlog", jevois::engine::SerPort::USB);
1616 }
1617 else
1618 {
1619 if (slhard) e->setParamValUnique("engine:serlog", jevois::engine::SerPort::Hard);
1620 else e->setParamValUnique("engine:serlog", jevois::engine::SerPort::None);
1621 }
1622
1623 if (sousb)
1624 {
1625 if (sohard) e->setParamValUnique("engine:serout", jevois::engine::SerPort::All);
1626 else e->setParamValUnique("engine:serout", jevois::engine::SerPort::USB);
1627 }
1628 else
1629 {
1630 if (sohard) e->setParamValUnique("engine:serout", jevois::engine::SerPort::Hard);
1631 else e->setParamValUnique("engine:serout", jevois::engine::SerPort::None);
1632 }
1633 }
1634
1635 // Now a combo for serstyle:
1636 auto m = dynamic_cast<jevois::StdModule *>(e->module().get());
1637 if (m)
1638 {
1639 auto sp = m->getParamValUnique<jevois::modul::SerStyle>("serstyle");
1640 int idx = 0; for (auto const & v : jevois::modul::SerStyle_Values) if (v == sp) break; else ++idx;
1641
1642 ImGui::SameLine();
1643 if (combo("serstyle", jevois::modul::SerStyle_Strings, idx))
1644 try { e->setParamValUnique("serstyle", jevois::modul::SerStyle_Values[idx]); }
1645 catch (...) { jevois::warnAndIgnoreException(); }
1646 }
1647 ImGui::Separator();
1648
1649 // Now a console:
1650 auto c = e->getComponent<jevois::GUIconsole>("guiconsole");
1651 if (c) c->draw();
1652}
1653
1654// ##############################################################################################################
1656{ return itsSerLogEnabled; }
1657
1658// ##############################################################################################################
1660{ return itsSerOutEnabled; }
1661
1662// ##############################################################################################################
1664{
1665 engine()->drawCameraGUI();
1666}
1667
1668// ##############################################################################################################
1669int jevois::GUIhelper::modal(std::string const & title, char const * text, int * default_val,
1670 char const * b1txt, char const * b2txt)
1671{
1672 // Handle optional default_val pointer:
1673 int ret = 0; int * retptr = default_val ? default_val : &ret;
1674
1675 // Do we want to just return the default value?
1676 if (*retptr == 1 || *retptr == 2) return *retptr;
1677
1678 // Open the modal if needed, and remember it:
1679 if (itsOpenModals.find(title) == itsOpenModals.end())
1680 {
1681 ImGui::OpenPopup(title.c_str());
1682 itsOpenModals.insert(title);
1683 }
1684
1685 // Display the modal and get any button clicks:
1686 bool dont_ask_me_next_time = (*retptr == 3);
1687
1688 if (ImGui::BeginPopupModal(title.c_str(), NULL, ImGuiWindowFlags_AlwaysAutoResize))
1689 {
1690 ImGui::TextUnformatted(text); ImGui::TextUnformatted(" ");
1691 ImGui::Separator();
1692 if (default_val)
1693 {
1694 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
1695 ImGui::PushStyleColor(ImGuiCol_FrameBg, 0xf0ffe0e0); // ABGR - in light theme, can't see the box
1696 ImGui::Checkbox("Don't ask me next time", &dont_ask_me_next_time);
1697 ImGui::PopStyleColor();
1698 ImGui::PopStyleVar();
1699 }
1700 float const b1w = std::max(120.0F, ImGui::CalcTextSize(b1txt).x + 20.0F);
1701 if (ImGui::Button(b1txt, ImVec2(b1w, 0))) ret = 1;
1702 ImGui::SetItemDefaultFocus();
1703 ImGui::SameLine();
1704 float const b2w = std::max(120.0F, ImGui::CalcTextSize(b2txt).x + 20.0F);
1705 if (ImGui::Button(b2txt, ImVec2(b2w, 0))) ret = 2;
1706 ImGui::EndPopup();
1707 }
1708
1709 // Close the modal if button clicked:
1710 if (ret == 1 || ret == 2)
1711 {
1712 ImGui::CloseCurrentPopup();
1713 itsOpenModals.erase(title);
1714 if (dont_ask_me_next_time) *retptr = ret; // remember the choice as new default choice
1715 }
1716 else *retptr = dont_ask_me_next_time ? 3 : 0; // propagate checkbox status
1717
1718 return ret;
1719}
1720
1721// ##############################################################################################################
1722bool jevois::GUIhelper::combo(std::string const & name, std::vector<std::string> const & items, int & selected_index)
1723{
1724 return ImGui::Combo(name.c_str(), &selected_index,
1725 [](void * vec, int idx, const char ** out_text)
1726 {
1727 auto & ve = *static_cast<std::vector<std::string>*>(vec);
1728 if (idx < 0 || idx >= static_cast<int>(ve.size())) return false;
1729 *out_text = ve.at(idx).c_str();
1730 return true;
1731 },
1732 const_cast<void *>(static_cast<void const *>(&items)), items.size());
1733}
1734
1735// ##############################################################################################################
1736bool jevois::GUIhelper::selectImageBox(int & state, ImVec2 & tl, ImVec2 & br, ImU32 col)
1737{
1738 if (ImGui::GetMouseCursor() != ImGuiMouseCursor_ResizeAll) ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll);
1739
1740 switch (state)
1741 {
1742 case 0: // Wait for mouse down
1743 if (ImGui::IsMouseClicked(0))
1744 {
1745 tl = ImGui::GetMousePos();
1746 state = 1;
1747 }
1748 break;
1749
1750 case 1: // Draw a rectangle and check for mouse up
1751 {
1752 br = ImGui::GetMousePos();
1753 auto dlb = ImGui::GetBackgroundDrawList();
1754 dlb->AddRectFilled(tl, br, applyFillAlpha(col));
1755 ImDrawFlags constexpr flags = ImDrawFlags_None; // ImDrawFlags_RoundCornersAll
1756 float constexpr rounding = 0.0F;
1757 dlb->AddRect(tl, br, col, rounding, flags, linethick::get());
1758
1759 if (ImGui::IsMouseReleased(0))
1760 {
1761 // Normalize our points so that they actually are top-left and bottom-right:
1762 int xmin = std::min(tl.x, br.x), xmax = std::max(tl.x, br.x);
1763 int ymin = std::min(tl.y, br.y), ymax = std::max(tl.y, br.y);
1764
1765 if (xmax - xmin < 10 || ymax - ymin < 10)
1766 state = 0; // too small
1767 else
1768 {
1769 tl.x = xmin; tl.y = ymin; br.x = xmax; br.y = ymax;
1770 state = -1; // as a courtesy to the caller, for next time
1771 ImGui::SetMouseCursor(ImGuiMouseCursor_Arrow);
1772 return true;
1773 }
1774 }
1775 }
1776 break;
1777
1778 default: LFATAL("Unknown state reached -- make sure you initialize state to 0 when starting a selection");
1779 }
1780 return false;
1781}
1782
1783// ##############################################################################################################
1784void jevois::GUIhelper::newModEntry(char const * wname, std::string & str, char const * desc,
1785 char const * hint, char const * hlp)
1786{
1787 static char buf[256];
1788 ImGui::AlignTextToFramePadding();
1789 ImGui::TextUnformatted(desc);
1790 ImGui::NextColumn();
1791 helpMarker(desc, hlp);
1792 ImGui::NextColumn();
1793 strncpy(buf, str.c_str(), sizeof(buf)-1);
1794 if (ImGui::InputTextWithHint(wname, hint, buf, sizeof(buf))) str = buf;
1795 ImGui::NextColumn();
1796}
1797
1798// ##############################################################################################################
1800{
1801 float const fontw = ImGui::GetFontSize();
1802
1803 static int refresh = 1;
1804 static std::string cpu, mem, ver;
1805 static size_t npu, tpu, vpu, spu; static int fan;
1806 if (--refresh == 0)
1807 {
1808 refresh = 60;
1809 cpu = jevois::getSysInfoCPU();
1810 mem = jevois::getSysInfoMem();
1816 fan = jevois::getFanSpeed();
1817 }
1818 ImGui::Text("JeVois-Pro v%s -- %s", JEVOIS_VERSION_STRING, ver.c_str());
1819 ImGui::TextUnformatted(cpu.c_str());
1820 ImGui::TextUnformatted(mem.c_str());
1821 ImGui::Text("NPU: %zu, TPU: %zu, VPU: %zu, SPU: %zu. Fan: %d%%", npu, tpu, vpu, spu, fan);
1822 ImGui::Separator();
1823
1824 // #################### Create new module:
1825 drawNewModuleForm();
1826 ImGui::Separator();
1827
1828 // #################### Open serial monitors:
1829 if (ImGui::Button("Open Hardware serial monitor...")) itsShowHardSerialWin = true;
1830 ImGui::SameLine();
1831 if (ImGui::Button("Open USB serial monitor...")) itsShowUsbSerialWin = true;
1832 ImGui::Separator();
1833
1834 // #################### ping:
1835 static std::string pingstr;
1836 static int showping = 0;
1837 if (ImGui::Button("Ping jevois.usc.edu"))
1838 {
1839 std::string ret = jevois::system("/usr/bin/ping -c 1 -w 2 jevois.usc.edu");
1840 std::vector<std::string> rvec = jevois::split(ret, "\n");
1841 if (rvec.size() < 2) reportError("Unable to ping jevois.usc.edu");
1842 else { pingstr = rvec[1]; showping = 60; }
1843 }
1844 if (showping)
1845 {
1846 ImGui::SameLine();
1847 ImGui::TextUnformatted(pingstr.c_str());
1848 --showping;
1849 }
1850
1851 ImGui::Separator();
1852
1853 // #################### dnnget:
1854 static std::string zip;
1855 static std::string donestr;
1856 static int state = 0;
1857
1858 ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue;
1859 if (state) flags |= ImGuiInputTextFlags_ReadOnly;
1860 ImGui::SetNextItemWidth(fontw * 6.0F);
1861 char buf[5] = { };
1862 if (ImGui::InputText("Load Custom DNN", buf, sizeof(buf), flags)) state = 1;
1863 ImGui::SameLine();
1864
1865 switch (state)
1866 {
1867 case 0:
1868 ImGui::TextUnformatted(" ");
1869 break;
1870
1871 case 1:
1872 {
1873 ImGui::TextUnformatted("-- Downloading...");
1874 zip = std::string(buf) + ".zip";
1875 itsDnnGetFut =
1877 {
1878 return jevois::system("/usr/bin/curl " JEVOIS_CUSTOM_DNN_URL "/" + zip + " -o "
1879 JEVOIS_CUSTOM_DNN_PATH "/" + zip, true);
1880 });
1881 state = 2;
1882 }
1883 break;
1884
1885 case 2:
1886 {
1887 if (itsDnnGetFut.valid() == false) { reportError("Unknown error while loading custom DNN"); state = 0; break; }
1888 ImGui::TextUnformatted("-- Downloading...");
1889 if (itsDnnGetFut.wait_for(std::chrono::microseconds(100)) == std::future_status::ready)
1890 {
1891 itsDnnGetFut.get();
1892
1893 // Check that the file exists:
1894 std::ifstream ifs(JEVOIS_CUSTOM_DNN_PATH "/" + zip);
1895 if (ifs.is_open() == false)
1896 {
1897 reportError("Failed to download. Check network connectivity and available disk space.");
1898 state = 0;
1899 break;
1900 }
1901 itsDnnGetFut =
1903 {
1904 return jevois::system("/usr/bin/unzip -o " JEVOIS_CUSTOM_DNN_PATH "/" + zip +
1905 " -d " JEVOIS_CUSTOM_DNN_PATH, true);
1906 });
1907 state = 3;
1908 }
1909 }
1910 break;
1911
1912 case 3:
1913 {
1914 if (itsDnnGetFut.valid() == false) { reportError("Unknown error while unpacking custom DNN"); state = 0; break; }
1915 ImGui::TextUnformatted("-- Installing...");
1916 if (itsDnnGetFut.wait_for(std::chrono::microseconds(100)) == std::future_status::ready)
1917 {
1918 std::string ret = itsDnnGetFut.get();
1919 std::vector<std::string> rvec = jevois::split(ret, "\n");
1920 if (rvec.size() > 2 && jevois::stringStartsWith(rvec[1], " End-of-central-directory signature not found"))
1921 donestr = "-- Invalid file, check DNN download key.";
1922 else
1923 donestr = "-- Done. Reload model zoo to take effect.";
1924 jevois::system("/bin/rm " JEVOIS_CUSTOM_DNN_PATH "/" + zip, true);
1925 state = 4;
1926 }
1927 }
1928 break;
1929
1930 case 200:
1931 ImGui::TextUnformatted(" ");
1932 state = 0;
1933 break;
1934
1935 default:
1936 ImGui::TextUnformatted(donestr.c_str());
1937 ++state;
1938 }
1939 ImGui::Separator();
1940
1941#ifdef JEVOIS_PLATFORM
1942 // #################### boot mode:
1943 static int bootmode = 0;
1944 ImGui::AlignTextToFramePadding();
1945 ImGui::TextUnformatted("On boot, start:");
1946 ImGui::SameLine();
1947 if (ImGui::Combo("##onboot", &bootmode, "(no change)\0JeVois-Pro\0Ubuntu Console\0Ubuntu Graphical\0\0"))
1948 {
1949 switch (bootmode)
1950 {
1951 case 0: break;
1952 case 2: jevois::system("systemctl --no-reload set-default multi-user.target"); break;
1953 case 3: jevois::system("systemctl --no-reload set-default graphical.target"); break;
1954 default: jevois::system("systemctl --no-reload set-default jevoispro.target"); break;
1955 }
1956 jevois::system("sync");
1957 }
1958 ImGui::Separator();
1959
1960 // #################### gadget serial:
1961 static bool gserial = false;
1962 try { gserial = (0 != std::stoi(jevois::getFileString(JEVOISPRO_GSERIAL_FILE))); } catch (...) { }
1963 if (ImGui::Checkbox("Enable serial outputs/logs over mini-USB (on next reboot)", &gserial))
1964 {
1965 std::ofstream ofs(JEVOISPRO_GSERIAL_FILE);
1966 ofs << (gserial ? 1 : 0) << std::endl;
1967 ofs.close();
1968 jevois::system("sync");
1969 }
1970 ImGui::Separator();
1971
1972 // #################### Edit fan settings:
1973 static bool show_fan_modal = false;
1974 if (ImGui::Button("Edit fan settings"))
1975 {
1976 itsCfgEditor->loadFile("/lib/systemd/system/jevoispro-fan.service");
1977 show_fan_modal = true;
1978 }
1979
1980 if (show_fan_modal)
1981 {
1982 static int doit_default = 0;
1983 int ret = modal("Ready to edit", "File will now be loaded in the Config tab of the main window and "
1984 "ready to edit.\nPlease switch to the Config tab in the main window.\n\n"
1985 "When you save it, we will reboot the camera.",
1986 &doit_default, "Ok", "Thanks");
1987 switch (ret)
1988 {
1989 case 1: show_fan_modal = false; break;
1990 case 2: show_fan_modal = false; break;
1991 default: break; // Need to wait
1992 }
1993 }
1994
1995 ImGui::Separator();
1996
1997#endif
1998
1999}
2000
2001// ##############################################################################################################
2002namespace
2003{
2004 unsigned int get_v4l2_fmt(int idx)
2005 {
2006 switch (idx)
2007 {
2008 case 0: return V4L2_PIX_FMT_YUYV;
2009 case 1: return V4L2_PIX_FMT_RGB24;
2010 case 2: return V4L2_PIX_FMT_RGB32;
2011 case 3: return V4L2_PIX_FMT_GREY;
2012 case 4: return V4L2_PIX_FMT_SBGGR16;
2013 default: return 0;
2014 }
2015 }
2016
2017 unsigned int get_v4l2_idx(int fcc)
2018 {
2019 switch (fcc)
2020 {
2021 case V4L2_PIX_FMT_YUYV: return 0;
2022 case V4L2_PIX_FMT_RGB24: return 1;
2023 case V4L2_PIX_FMT_RGB32: return 2;
2024 case V4L2_PIX_FMT_GREY: return 3;
2025 case V4L2_PIX_FMT_SBGGR16: return 4;
2026 default: return 0;
2027 }
2028 }
2029
2030 // Copy a file and replace special fields:
2031 void cookedCopy(std::filesystem::path const & src, std::filesystem::path const & dst,
2032 std::string const & name, std::string const & vendor, std::string const & synopsis,
2033 std::string const & author, std::string const & email, std::string const & website,
2034 std::string const & license, std::string const & videomapping)
2035 {
2036 std::ifstream f(src);
2037 if (f.is_open() == false) LFATAL("Cannot read " << src);
2038 std::string str((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
2039
2040 std::string proj = jevois::tolower(name);
2041
2042 jevois::replaceStringAll(str, "__MODULE__", name);
2043 jevois::replaceStringAll(str, "__PROJECT__", proj);
2044 jevois::replaceStringAll(str, "__VENDOR__", vendor);
2045 jevois::replaceStringAll(str, "__SYNOPSIS__", synopsis);
2046 jevois::replaceStringAll(str, "__AUTHOR__", author);
2047 jevois::replaceStringAll(str, "__EMAIL__", email);
2048 jevois::replaceStringAll(str, "__WEBSITE__", website);
2049 jevois::replaceStringAll(str, "__LICENSE__", license);
2050 jevois::replaceStringAll(str, "__VIDEOMAPPING__", videomapping);
2051
2052 std::ofstream ofs(dst);
2053 if (ofs.is_open() == false) LFATAL("Cannot write " << dst << " -- check that you are running as root.");
2054 ofs << str << std::endl;
2055 LINFO("Translated copy " << src << " => " << dst);
2056 }
2057
2058 // Copy an existing CMakeLists.txt and replace some fields:
2059 void cookedCmakeClone(std::filesystem::path const & src, std::filesystem::path const & dst,
2060 std::string const & oldname, std::string const & newname,
2061 std::string const & oldvendor, std::string const & newvendor, std::string const & synopsis,
2062 std::string const & author, std::string const & email, std::string const & website,
2063 std::string const & license, std::string const & videomapping)
2064 {
2065 std::ifstream f(src);
2066 if (f.is_open() == false) LFATAL("Cannot read " << src);
2067 std::string str((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
2068
2069 std::string oldproj = jevois::tolower(oldname);
2070 std::string newproj = jevois::tolower(newname);
2071
2072 jevois::replaceStringAll(str, oldname, newname);
2073 jevois::replaceStringAll(str, oldproj, newproj);
2074 jevois::replaceStringAll(str, oldvendor, newvendor);
2075 jevois::replaceStringAll(str, "__SYNOPSIS__", synopsis);
2076 jevois::replaceStringAll(str, "__AUTHOR__", author);
2077 jevois::replaceStringAll(str, "__EMAIL__", email);
2078 jevois::replaceStringAll(str, "__WEBSITE__", website);
2079 jevois::replaceStringAll(str, "__LICENSE__", license);
2080 jevois::replaceStringAll(str, "__VIDEOMAPPING__", videomapping);
2081
2082 std::ofstream ofs(dst);
2083 if (ofs.is_open() == false) LFATAL("Cannot write " << dst << " -- check that you are running as root.");
2084 ofs << str << std::endl;
2085 LINFO("Translated copy " << src << " => " << dst);
2086 }
2087
2088 // Try to replace class name and namespace name. This is very hacky...
2089 void cookedCodeClone(std::filesystem::path const & src, std::filesystem::path const & dst,
2090 std::string const & oldname, std::string const & newname)
2091 {
2092 std::ifstream f(src);
2093 if (f.is_open() == false) LFATAL("Cannot read " << src);
2094 std::string str((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
2095
2096 std::string oldnspc = jevois::tolower(oldname);
2097 std::string newnspc = jevois::tolower(newname);
2098
2099 jevois::replaceStringAll(str, "class " + oldname, "class " + newname);
2100 jevois::replaceStringAll(str, oldname + '(', newname + '('); // constructor and destructor
2101 jevois::replaceStringAll(str, '(' + oldname, '(' + newname); // JEVOIS_REGISTER_MODULE
2102 jevois::replaceStringAll(str, oldname + "::", newname + "::");
2103
2104 jevois::replaceStringAll(str, "namespace " + oldnspc, "namespace " + newnspc);
2105 jevois::replaceStringAll(str, oldnspc + "::", newnspc + "::");
2106
2107 // Revert a few replacements if jevois was involved. E.g., when cloning the DNN module that uses the
2108 // jevois::dnn namespace...
2109 jevois::replaceStringAll(str, "jevois::" + newnspc, "jevois::" + oldnspc);
2110 jevois::replaceStringAll(str, "jevois::" + newname, "jevois::" + oldname);
2111
2112 std::ofstream ofs(dst);
2113 if (ofs.is_open() == false) LFATAL("Cannot write " << dst << " -- check that you are running as root.");
2114 ofs << str << std::endl;
2115 LINFO("Translated copy " << src << " => " << dst);
2116 }
2117}
2118
2119// ##############################################################################################################
2121{
2122 float const fontw = ImGui::GetFontSize();
2123 auto e = engine();
2124 static int templ = 0; // Which code template to use (pro, legacy, headless, clone). Resets for each new module.
2125
2126 ImGui::PushStyleColor(ImGuiCol_PopupBg, 0xf0e0ffe0);
2127
2128 if (ImGui::Button("Create new machine vision module..."))
2129 {
2130 ImGui::OpenPopup("Create new machine vision module");
2131 itsIdleBlocked = true; // prevent GUI from disappearing if we are slow to fill the form...
2132 templ = 0; // default template pro/GUI each time
2133 }
2134
2135 ImVec2 const center = ImGui::GetMainViewport()->GetCenter();
2136 ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
2137 ImGui::SetNextWindowContentSize(ImVec2(940, 750));
2138
2139 if (ImGui::BeginPopupModal("Create new machine vision module", NULL, ImGuiWindowFlags_AlwaysAutoResize))
2140 {
2141 try
2142 {
2143 static std::string name, vendor, synopsis, author, email, website, license, srcvendor, srcname;
2144 static int language = 0;
2145 static int ofmt = 0; static int ow = 320, oh = 240; static float ofps = 30.0F;
2146 static int cmode = 0;
2147 static int wdrmode = 0;
2148 static int cfmt = 0; static int cw = 1920, ch = 1080; static float cfps = 30.0F;
2149 static int c2fmt = 1; static int c2w = 512, c2h = 288;
2150 static jevois::VideoMapping m;
2151
2152 ImGui::AlignTextToFramePadding();
2153 ImGui::TextUnformatted("Fill out the details below, or clone from");
2154 ImGui::SameLine();
2155
2156 static std::map<std::string, size_t> mods;
2157 static std::string currstr = "...";
2158
2159 size_t idx = 0;
2160 e->foreachVideoMapping([&idx](jevois::VideoMapping const & m) { mods[m.menustr2()] = idx++; });
2161
2162 // Draw the module clone selector and allow selection:
2163 if (ImGui::BeginCombo("##clonemodule", currstr.c_str()))
2164 {
2165 for (auto const & mod : mods)
2166 {
2167 bool is_selected = false;
2168 if (ImGui::Selectable(mod.first.c_str(), is_selected))
2169 {
2170 m = e->getVideoMapping(mod.second);
2171 currstr = mod.first;
2172
2173 // Prefill some data:
2174 vendor = "Testing";
2175 name = "My" + m.modulename; int i = 2;
2176 while (std::filesystem::exists(JEVOIS_MODULE_PATH "/" + vendor + '/' + name))
2177 { name = "My" + m.modulename + std::to_string(i++); }
2178
2179 language = m.ispython ? 0 : 1;
2180 templ = 3; // clone
2181 ofmt = m.ofmt; ow = m.ow; oh = m.oh; ofps = m.ofps;
2182 switch (m.crop)
2183 {
2184 case jevois::CropType::CropScale: cmode = 0; break;
2185 case jevois::CropType::Crop: cmode = 1; break;
2186 case jevois::CropType::Scale: cmode = 2; break;
2187 }
2188 switch (m.wdr)
2189 {
2190 case jevois::WDRtype::Linear: wdrmode = 0; break;
2191 case jevois::WDRtype::DOL: wdrmode = 1; break;
2192 }
2193 cfmt = get_v4l2_idx(m.cfmt); cw = m.cw; ch = m.ch; cfps = m.cfps;
2194 c2fmt = get_v4l2_idx(m.c2fmt); c2w = m.c2w; c2h = m.c2h;
2195 srcvendor = m.vendor; srcname = m.modulename;
2196 }
2197 }
2198 ImGui::EndCombo();
2199 }
2200
2201 // Draw the form items:
2202 ImGui::Separator();
2203
2204 ImGui::Columns(3, "new module");
2205
2206 newModEntry("##NewModname", name, "Module Name", "MyModule",
2207 "Required, even when cloning. Must start with an uppercase letter. "
2208 "Will be a folder name under /jevoispro/modules/VendorName");
2209
2210 newModEntry("##NewModvendor", vendor, "Vendor Name", "MyVendor",
2211 "Required, even when cloning. Must start with an uppercase letter. "
2212 "Will be a folder name under /jevoispro/modules/");
2213
2214 // If cloning, disable all edits except for vendor and module names:
2215 if (templ == 3)
2216 {
2217 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
2218 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
2219 }
2220
2221 newModEntry("##NewModsynopsis", synopsis, "Synopsis", "Detect Object of type X",
2222 "Optional. Brief description of what the module does.");
2223
2224 newModEntry("##NewModauthor", author, "Author Name", "John Smith", "Optional");
2225
2226 newModEntry("##NewModemail", email, "Author Email", "you@yourcompany.com", "Optional");
2227
2228 newModEntry("##NewModwebsite", website, "Author Website", "http://yourcompany.com", "Optional");
2229
2230 newModEntry("##NewModlicense", license, "License", "GPL v3", "Optional");
2231
2232 // Language:
2233 ImGui::AlignTextToFramePadding();
2234 ImGui::TextUnformatted("Module Language");
2235 ImGui::NextColumn();
2236 helpMarker("Module Language", "Machine language to use for your module.");
2237 ImGui::NextColumn();
2238 ImGui::Combo("##NewModlanguage", &language, "Python\0C++\0\0");
2239 ImGui::NextColumn();
2240
2241 // Template:
2242 ImGui::AlignTextToFramePadding();
2243 ImGui::TextUnformatted("Module Template");
2244 ImGui::NextColumn();
2245 helpMarker("Module Template", "Type of placeholder code that will be provided to get your module started.");
2246 ImGui::NextColumn();
2247 if (templ < 3) ImGui::Combo("##NewModtemplate", &templ, "Pro/GUI\0Legacy\0Headless\0\0");
2248 else ImGui::Combo("##NewModtemplate", &templ, "Pro/GUI\0Legacy\0Headless\0Clone\0\0");
2249 ImGui::NextColumn();
2250
2251 // Output video mapping:
2252 int oflags = ImGuiInputTextFlags_None;
2253 if (templ != 1)
2254 {
2255 oflags |= ImGuiInputTextFlags_ReadOnly;
2256 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
2257 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
2258 }
2259
2260 ImGui::AlignTextToFramePadding();
2261 ImGui::TextUnformatted("Module Output");
2262 ImGui::NextColumn();
2263 helpMarker("Module Output", "Output video format for legacy module.");
2264 ImGui::NextColumn();
2265 ImGui::SetNextItemWidth(fontw * 5.0F);
2266 ImGui::Combo("##NewModofmt", &ofmt, "YUYV\0RGB\0RGBA\0GREY\0BAYER\0\0");
2267 ImGui::SameLine();
2268 ImGui::SetNextItemWidth(fontw * 4.0F);
2269 ImGui::InputInt("##NewModow", &ow, 0, 0, ImGuiInputTextFlags_CharsDecimal | oflags);
2270 ImGui::SameLine();
2271 ImGui::TextUnformatted("x");
2272 ImGui::SameLine();
2273 ImGui::SetNextItemWidth(fontw * 4.0F);
2274 ImGui::InputInt("##NewModoh", &oh, 0, 0, ImGuiInputTextFlags_CharsDecimal | oflags);
2275 ImGui::SameLine();
2276 ImGui::TextUnformatted("@");
2277 ImGui::SameLine();
2278 ImGui::SetNextItemWidth(fontw * 4.0F);
2279 ImGui::InputFloat("##NewModofps", &ofps, 0.0F, 0.0F, "%.1f", ImGuiInputTextFlags_CharsDecimal | oflags);
2280 ImGui::SameLine();
2281 ImGui::TextUnformatted("fps");
2282 ImGui::NextColumn();
2283
2284 if (templ != 1)
2285 {
2286 ImGui::PopItemFlag();
2287 ImGui::PopStyleVar();
2288 }
2289
2290 // Camera mode:
2291 ImGui::AlignTextToFramePadding();
2292 ImGui::TextUnformatted("Camera Mode");
2293 ImGui::NextColumn();
2294 helpMarker("Camera Mode", "Camera sensor configuration for your module.");
2295 ImGui::NextColumn();
2296 ImGui::SetNextItemWidth(fontw * 18.0F);
2297 ImGui::Combo("##NewModcmode", &cmode, "Dual-resolution (Crop+Scale)\0Single-resolution Crop\0"
2298 "Single-resolution Scale\0\0");
2299 ImGui::NextColumn();
2300
2301 // Camera WDR mode:
2302 ImGui::AlignTextToFramePadding();
2303 ImGui::TextUnformatted("Camera WDR");
2304 ImGui::NextColumn();
2305 helpMarker("Camera WDR", "Camera sensor wide-dynamic-range (WDR) setting for your module. Linear is for no WDR, "
2306 "DOL is for digital overlap (merging short and long exposure frames).");
2307 ImGui::NextColumn();
2308 ImGui::Combo("##NewModwdrmode", &wdrmode, "Linear\0\0"); // FIXME
2309 //ImGui::Combo("##NewModwdrmode", &wdrmode, "Linear\0DOL\0\0");
2310 ImGui::NextColumn();
2311
2312 // Camera video mapping:
2313 ImGui::AlignTextToFramePadding();
2314 ImGui::TextUnformatted("Camera Format");
2315 ImGui::NextColumn();
2316 helpMarker("Camera Format", "Camera video format to use for input to module and for GUI display.");
2317 ImGui::NextColumn();
2318 ImGui::SetNextItemWidth(fontw * 5.0F);
2319 ImGui::Combo("##NewModcfmt", &cfmt, "YUYV\0RGB\0RGBA\0GREY\0BAYER\0\0");
2320 ImGui::SameLine();
2321 ImGui::SetNextItemWidth(fontw * 4.0F);
2322 ImGui::InputInt("##NewModcw", &cw, 0, 0, ImGuiInputTextFlags_CharsDecimal);
2323 ImGui::SameLine();
2324 ImGui::TextUnformatted("x");
2325 ImGui::SameLine();
2326 ImGui::SetNextItemWidth(fontw * 4.0F);
2327 ImGui::InputInt("##NewModch", &ch, 0, 0, ImGuiInputTextFlags_CharsDecimal);
2328 ImGui::SameLine();
2329 ImGui::TextUnformatted("@");
2330 ImGui::SameLine();
2331 ImGui::SetNextItemWidth(fontw * 4.0F);
2332 ImGui::InputFloat("##NewModcfps", &cfps, 0.0F, 0.0F, "%.1f", ImGuiInputTextFlags_CharsDecimal);
2333 ImGui::SameLine();
2334 ImGui::TextUnformatted("fps");
2335 ImGui::NextColumn();
2336
2337 // Camera second frame video mapping:
2338 int c2flags = ImGuiInputTextFlags_None;
2339 if (cmode != 0)
2340 {
2341 oflags |= ImGuiInputTextFlags_ReadOnly;
2342 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
2343 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
2344 }
2345
2346 ImGui::AlignTextToFramePadding();
2347 ImGui::TextUnformatted("Camera Format 2");
2348 ImGui::NextColumn();
2349 helpMarker("Camera Format 2", "Camera video format for the second stream (for processing).");
2350 ImGui::NextColumn();
2351 ImGui::SetNextItemWidth(fontw * 5.0F);
2352 ImGui::Combo("##NewModc2fmt", &c2fmt, "YUYV\0RGB\0RGBA\0GREY\0BAYER\0\0");
2353 ImGui::SameLine();
2354 ImGui::SetNextItemWidth(fontw * 4.0F);
2355 ImGui::InputInt("##NewModc2w", &c2w, 0, 0, ImGuiInputTextFlags_CharsDecimal | c2flags);
2356 ImGui::SameLine();
2357 ImGui::TextUnformatted("x");
2358 ImGui::SameLine();
2359 ImGui::SetNextItemWidth(fontw * 4.0F);
2360 ImGui::InputInt("##NewModc2h", &c2h, 0, 0, ImGuiInputTextFlags_CharsDecimal | c2flags);
2361 ImGui::NextColumn();
2362
2363 if (cmode != 0)
2364 {
2365 ImGui::PopItemFlag();
2366 ImGui::PopStyleVar();
2367 }
2368
2369 if (templ == 3)
2370 {
2371 ImGui::PopItemFlag();
2372 ImGui::PopStyleVar();
2373 }
2374
2375 // Adjust columns:
2376 ImGui::SetColumnWidth(0, fontw * 10.0F);
2377 ImGui::SetColumnWidth(1, ImGui::CalcTextSize("(?)").x + 30.0F);
2378 //ImGui::SetColumnWidth(2, 800.0F);
2379
2380 ImGui::Columns(1);
2381
2382 ImGui::Separator();
2383 ImGui::Separator();
2384
2385 ImVec2 const button_size(ImGui::GetFontSize() * 7.0f, 0.0f);
2386 if (ImGui::Button("Cancel", button_size))
2387 {
2388 ImGui::CloseCurrentPopup();
2389 ImGui::EndPopup();
2390 ImGui::PopStyleColor();
2391 itsIdleBlocked = false;
2392 currstr = "...";
2393 return;
2394 }
2395
2396 ImGui::SameLine(0, 540);
2397
2398 // ====================================================================================================
2399 if (ImGui::Button("Create", button_size))
2400 {
2401 // Validate inputs:
2402 if (name.empty()) LFATAL("New Module cannot have an empty name.");
2403 if (name[0]<'A' || name[0]>'Z') LFATAL("New Module name must start with an uppercase letter.");
2404 if (vendor.empty()) LFATAL("New Module cannot have empty vendor name.");
2405 LINFO("New Module data valid...");
2406
2407 // First create a directory for vendor, if not already present:
2408 std::filesystem::path const newvdir = JEVOIS_MODULE_PATH "/" + vendor;
2409 if (std::filesystem::exists(newvdir) == false && std::filesystem::create_directory(newvdir) == false)
2410 PLFATAL("Error creating dir " << newvdir << " -- check that you are running as root");
2411 std::filesystem::path const newmdir = newvdir / name;
2412 std::string vmstr; // machine string version of our videomapping
2413
2414 // ----------------------------------------------------------------------------------------------------
2415 // If cloning, clone and cook:
2416 if (templ == 3)
2417 {
2418 // Remember original module's source path:
2419 std::filesystem::path srcpath = m.srcpath();
2420 std::filesystem::path cmakepath = m.cmakepath();
2421
2422 // Replace vendor and module names in our mapping, keep all other data from source module:
2423 m.vendor = vendor;
2424 m.modulename = name;
2425
2426 // Copy all files, so we get any icons, aux source files, calibration data, etc:
2427 std::string const copycmd = jevois::sformat("/bin/cp -ar %s/%s/%s %s", JEVOIS_MODULE_PATH, srcvendor.c_str(),
2428 srcname.c_str(), newmdir.c_str());
2429 jevois::system(copycmd);
2430 LINFO("Cloned module using: " << copycmd);
2431
2432 // Delete source and .so files, we will replace those with cooking:
2433 unlink((m.path() + '/' + srcname + ".C").c_str());
2434 unlink((m.path() + '/' + srcname + ".py").c_str());
2435 unlink((m.path() + '/' + srcname + ".so").c_str());
2436 std::filesystem::remove_all(m.path() + "/__pycache__");
2437 std::filesystem::remove_all(m.path() + "/build");
2438
2439 // Cook and copy the module source:
2440 std::ostringstream oss; oss << m; vmstr = oss.str();
2441 cookedCodeClone(srcpath, m.srcpath(), srcname, name);
2442
2443 // If C++, also copy and cook CMakeLists.txt, if present, otherwise make one from template:
2444 if (language == 1)
2445 {
2446 if (std::filesystem::exists(cmakepath))
2447 cookedCmakeClone(cmakepath, m.cmakepath(), srcname, name, srcvendor, vendor, synopsis, author,
2448 email, website, license, vmstr);
2449 else
2450 cookedCopy(JEVOIS_SHARE_PATH "/templates/CMakeLists.txt", m.cmakepath(),
2451 name, vendor, synopsis, author, email, website, license, vmstr);
2452 }
2453 }
2454 // ----------------------------------------------------------------------------------------------------
2455 // If not cloning, copy and cook from template:
2456 else
2457 {
2458 // Create a new directory and copy the template into it:
2459 if (std::filesystem::exists(newmdir))
2460 LFATAL("Directory [" << newmdir << "] already exists -- Choose another name");
2461 if (std::filesystem::create_directory(newmdir) == false)
2462 PLFATAL("Error creating directory [" << newmdir << "] for new module. Maybe not running as root?");
2463 LINFO("Created new Module directory: " << newmdir);
2464
2465 // Create a new video mapping to add to videomappimgs.cfg:
2466 m = { };
2467 switch (templ)
2468 {
2469 case 0: m.ofmt = JEVOISPRO_FMT_GUI; break;
2470 case 1: m.ofmt = get_v4l2_fmt(ofmt); m.ow = ow; m.oh = oh; m.ofps = ofps; break;
2471 case 2: m.ofmt = 0; break;
2472 default: break;
2473 }
2474
2475 m.cfmt = get_v4l2_fmt(cfmt);
2476 m.cw = cw; m.ch = ch; m.cfps = cfps;
2477
2478 m.vendor = vendor;
2479 m.modulename = name;
2480
2481 switch (wdrmode)
2482 {
2483 case 0: m.wdr = jevois::WDRtype::Linear; break;
2484 case 1: m.wdr = jevois::WDRtype::DOL; break;
2485 default: break;
2486 }
2487
2488 switch (cmode)
2489 {
2490 case 0: m.crop = jevois::CropType::CropScale;
2491 m.c2fmt = get_v4l2_fmt(c2fmt);
2492 m.c2w = c2w; m.c2h = c2h;
2493 break;
2494 case 1: m.crop = jevois::CropType::Crop; break;
2495 case 2: m.crop = jevois::CropType::Scale; break;
2496 default: break;
2497 }
2498
2499 m.ispython = (language == 0);
2500 std::ostringstream oss; oss << m; vmstr = oss.str();
2501
2502 // Copy the desired code template and cook it:
2503 std::string code; std::string tname;
2504 switch (language)
2505 {
2506 case 0:
2507 tname = "PyModule.py";
2508 break;
2509
2510 case 1:
2511 {
2512 tname = "Module.C";
2513
2514 // Also copy and cook the CMakeLists.txt
2515 std::filesystem::path srccmak = JEVOIS_SHARE_PATH "/templates/CMakeLists.txt";
2516 std::filesystem::path dstcmak = m.cmakepath();
2517 cookedCopy(srccmak, dstcmak, name, vendor, synopsis, author, email, website, license, vmstr);
2518 }
2519 break;
2520
2521 default: LFATAL("Invalid language " << language);
2522 }
2523
2524 std::filesystem::path srcmod = JEVOIS_SHARE_PATH "/templates/" + tname;
2525 std::filesystem::path dstmod = m.srcpath();
2526 cookedCopy(srcmod, dstmod, name, vendor, synopsis, author, email, website, license, vmstr);
2527 }
2528
2529 // Add the video mapping:
2530 jevois::system(JEVOIS "-add-videomapping " + vmstr);
2531 LINFO("Added videomapping: " << vmstr);
2532
2533 // Remember this new mapping in case we need it to compile and load the module later:
2534 itsNewMapping = m;
2535
2536 // For python modules, load right now. For C++ modules, we will not find the mapping because the .so is missing,
2537 // we need to compile the module first:
2538 if (language == 0) runNewModule();
2539 else itsCompileState = CompilationState::Start;
2540
2541 // Clear a few things before the next module:
2542 name.clear(); vendor.clear(); synopsis.clear(); currstr = "...";
2543
2544 ImGui::CloseCurrentPopup();
2545 itsIdleBlocked = false;
2546 }
2547 }
2548 catch (...) { reportAndIgnoreException(); }
2549
2550 // Make sure we always end the popup, even if we had an exception:
2551 ImGui::EndPopup();
2552 }
2553
2554 ImGui::PopStyleColor();
2555}
2556
2557// ##############################################################################################################
2559{
2560 engine()->reloadVideoMappings();
2561 size_t idx = 0; size_t foundidx = 12345678;
2562 engine()->foreachVideoMapping([&](VideoMapping const & mm)
2563 { if (itsNewMapping.isSameAs(mm)) foundidx = idx; ++idx; });
2564
2565 if (foundidx != 12345678) engine()->requestSetFormat(foundidx);
2566 else LFATAL("Internal error, could not find the module we just created -- CHECK LOGS");
2567
2568 itsRefreshVideoMappings = true; // Force a refresh of our list of video mappings
2569 //itsRefreshCfgList = true; // Force a refresh of videomappings.cfg in the config editor
2570
2571 // Switch to the mapping list that contains our new module:
2572 if (itsNewMapping.ofmt == JEVOISPRO_FMT_GUI) itsVideoMappingListType = 0;
2573 else if (itsNewMapping.ofmt != 0 && itsNewMapping.ofmt != JEVOISPRO_FMT_GUI) itsVideoMappingListType = 1;
2574 else if (itsNewMapping.ofmt == 0) itsVideoMappingListType = 2;
2575 else LERROR("Internal error: cannot determine video mapping list type -- IGNORED");
2576}
2577
2578// ##############################################################################################################
2579// imgui opengl demo in 1024 bytes (before we renamed a couple things for clearer docs):
2580using V=ImVec2;using F=float;int h;struct TinyImGUIdemo{V p;F z,w;bool operator<(TinyImGUIdemo&o){return z<o.z;}};TinyImGUIdemo G[999];
2581#define L(i,x,y,z)for(F i=x;i<y;i+=z)
2582#define Q(y)sin((y+t)*.03)*(1-sin(t*3)*cos(y/99+t))*9
2583#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);
2584#define J(b)L(i,0,h,1){TinyImGUIdemo&o=G[int(i)];if(b*o.z>0)H(o.p,(o.z*.3+4)*o.w)}
2585#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)
2586void drawTinyImGUIdemo(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;
2587F 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);}}
2588h=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);
2589 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)}
2590
2591// ##############################################################################################################
2593{
2594 if (ImGui::Button("Open Style Editor")) itsShowStyleEditor = true;
2595 ImGui::SameLine();
2596 if (ImGui::Button("Open App Metrics")) itsShowAppMetrics = true;
2597 ImGui::SameLine();
2598 if (ImGui::Button("Open ImGui Demo")) itsShowImGuiDemo = true;
2599 ImGui::Separator();
2600
2601
2602 float camz = pixel_perfect_z;
2603 float yaw = 0.0f;
2604 float pitch = 0.0f;
2605 float roll = 0.0f;
2606 static float fudgex = 0.375f;
2607 static float fudgey = 0.375f;
2608 static float fudgez = 0.0f;
2609 unsigned short winw, winh; itsBackend.getWindowSize(winw, winh);
2610
2611 ImGui::SliderFloat("OpenGL Camera z", &camz, -2.0f * winh, -1.0f);
2612 ImGui::SliderAngle("OpenGL yaw", &yaw, -179.0f, 180.0f);
2613 ImGui::SliderAngle("OpenGL pitch", &pitch, -179.0f, 180.0f);
2614 ImGui::SliderAngle("OpenGL roll", &roll, -179.0f, 180.0f);
2615
2616 ImGui::SliderFloat("fudge x", &fudgex, -1.0f, 1.0f);
2617 ImGui::SliderFloat("fudge y", &fudgey, -1.0f, 1.0f);
2618 ImGui::SliderFloat("fudge z", &fudgez, -1.0f, 1.0f);
2619
2620 // mess with the projection matrix:
2621 proj = glm::perspective(glm::radians(45.0f), float(winw) / float(winh), 1.0f, winh * 2.0f);
2622 proj = glm::translate(proj, glm::vec3(fudgex, fudgey, fudgez));
2623
2624 // update our view matrix
2625 view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, camz));
2626 view *= glm::yawPitchRoll(yaw, pitch, roll);
2627
2628 // Note: the ortho projection of ImGui is like: glm::ortho(0.0f, 1920.0f, 1080.0f, 0.0f);
2629 /*
2630 glm::mat4 gogo = glm::ortho(0.0f, 1920.0f, 1080.0f, 0.0f);
2631 gogo = glm::translate(gogo, glm::vec3(0.125f, 0.125f, 0.0f));
2632
2633 for (int i = 0; i < 4; ++i)
2634 for (int j = 0; j < 4; ++j)
2635 printf("gogo[%d][%d] = %f\n", i, j, gogo[i][j]);
2636 */
2637
2638 //static bool demo = false;
2639 //if (ImGui::Checkbox("OpenGL Demo", &demo))
2640 //{
2641 // from https://github.com/ocornut/imgui/issues/3606
2642 ImGuiIO& io = ImGui::GetIO();
2643 ImGui::Begin("Tiny OpenGL demo", NULL, ImGuiWindowFlags_AlwaysAutoResize);
2644 ImVec2 size(320.0f, 180.0f);
2645 ImGui::InvisibleButton("canvas", size);
2646 ImVec2 p0 = ImGui::GetItemRectMin();
2647 ImVec2 p1 = ImGui::GetItemRectMax();
2648 ImDrawList* draw_list = ImGui::GetWindowDrawList();
2649 draw_list->PushClipRect(p0, p1);
2650
2651 ImVec4 mouse_data;
2652 mouse_data.x = (io.MousePos.x - p0.x) / size.x;
2653 mouse_data.y = (io.MousePos.y - p0.y) / size.y;
2654 mouse_data.z = io.MouseDownDuration[0];
2655 mouse_data.w = io.MouseDownDuration[1];
2656
2657 drawTinyImGUIdemo(draw_list, p0, p1, size, mouse_data, (float)ImGui::GetTime());
2658 draw_list->PopClipRect();
2659 ImGui::End();
2660 //}
2661
2662}
2663
2664// ##############################################################################################################
2665void jevois::GUIhelper::reportError(std::string const & err)
2666{ reportErrorOrInfo(err, false); }
2667
2668// ##############################################################################################################
2669void jevois::GUIhelper::reportInfo(std::string const & err)
2670{ reportErrorOrInfo(err, true); }
2671
2672// ##############################################################################################################
2673void jevois::GUIhelper::reportErrorOrInfo(std::string const & err, bool is_info)
2674{
2675 //if (is_info) LINFO(err); else LERROR(err);
2676
2677 auto now = std::chrono::steady_clock::now();
2678
2679 std::lock_guard<std::mutex> _(itsErrorMtx);
2680
2681 // Did we already report this error? If so, just update the last received time:
2682 for (auto & e : itsErrors) if (e.err == err) { e.lasttime = now; return; }
2683
2684 // Too many errors already?
2685 if (itsErrors.size() > 10) return;
2686 else if (itsErrors.size() == 10)
2687 {
2688 ErrorData d { "Too many errors -- TRUNCATING", now, now, false };
2689 itsErrors.emplace(itsErrors.end(), std::move(d));
2690 return;
2691 }
2692
2693 // It's a new error, push a new entry into our list:
2694 ErrorData d { err, now, now, is_info };
2695 itsErrors.emplace(itsErrors.end(), std::move(d));
2696
2697 // drawErrorPopup() will clear old entries
2698}
2699
2700// ##############################################################################################################
2702{
2703 std::lock_guard<std::mutex> _(itsErrorMtx);
2704 itsErrors.clear();
2705}
2706
2707// ##############################################################################################################
2708void jevois::GUIhelper::reportAndIgnoreException(std::string const & prefix)
2709{
2710 if (prefix.empty())
2711 {
2712 try { throw; }
2713 catch (std::exception const & e)
2714 { reportError(e.what()); }
2715 catch (boost::python::error_already_set & e)
2716 { reportError("Python error:\n"+jevois::getPythonExceptionString(e)); }
2717 catch (...)
2718 { reportError("Unknown error"); }
2719 }
2720 else
2721 {
2722 try { throw; }
2723 catch (std::exception const & e)
2724 { reportError(prefix + ": " + e.what()); }
2725 catch (boost::python::error_already_set & e)
2726 { reportError(prefix + ": Python error:\n"+jevois::getPythonExceptionString(e)); }
2727 catch (...)
2728 { reportError(prefix + ": Unknown error"); }
2729 }
2730}
2731
2732// ##############################################################################################################
2733void jevois::GUIhelper::reportAndRethrowException(std::string const & prefix)
2734{
2735 reportAndIgnoreException(prefix);
2736 throw;
2737}
2738
2739// ##############################################################################################################
2741{
2742 std::lock_guard<std::mutex> _(itsErrorMtx);
2743 if (itsErrors.empty()) return;
2744
2745 bool err_hovered = false, inf_hovered = false;
2746
2747 // Split into errors vs info:
2748 std::vector<char const *> errors, infos;
2749 for (auto const & e : itsErrors)
2750 if (e.is_info) infos.push_back(e.err.c_str());
2751 else errors.push_back(e.err.c_str());
2752
2753 ImGuiWindowFlags constexpr window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings |
2754 ImGuiWindowFlags_NoNav | ImGuiWindowFlags_AlwaysAutoResize;
2755 ImGui::SetNextWindowPos(ImVec2(10.0f, 10.0f), ImGuiCond_Always);
2756 bool show = true;
2757 float const wrappos = ImGui::GetFontSize() * 35.0f;
2758
2759 // First draw the errors:
2760 if (errors.empty() == false)
2761 {
2762 ImGui::PushStyleColor(ImGuiCol_WindowBg, 0xc0e0e0ff);
2763 if (ImGui::Begin("Error popup", &show, window_flags))
2764 {
2765 ImGui::PushTextWrapPos(wrappos);
2766 ImGui::TextUnformatted("Error detected! ");
2767 for (char const * e : errors) { ImGui::Separator(); ImGui::TextUnformatted(e); }
2768 ImGui::PopTextWrapPos();
2769 }
2770 err_hovered = ImGui::IsWindowHovered();
2771 ImGui::SetNextWindowPos(ImVec2(10.0f, 15.0f + ImGui::GetWindowSize().y), ImGuiCond_Always);
2772 ImGui::End();
2773 ImGui::PopStyleColor();
2774 }
2775
2776 // Then the infos:
2777 if (infos.empty() == false)
2778 {
2779 ImGui::PushStyleColor(ImGuiCol_WindowBg, 0xc0d0ffe0);
2780 if (ImGui::Begin("Info popup", &show, window_flags))
2781 {
2782 ImGui::PushTextWrapPos(wrappos);
2783 ImGui::TextUnformatted("Info message ");
2784 for (char const * e : infos) { ImGui::Separator(); ImGui::TextUnformatted(e); }
2785 ImGui::PopTextWrapPos();
2786 }
2787 inf_hovered = ImGui::IsWindowHovered();
2788 ImGui::End();
2789 ImGui::PopStyleColor();
2790 }
2791
2792 // Age the messages:
2793 auto itr = itsErrors.begin();
2794 while (itr != itsErrors.end())
2795 {
2796 // Clear the error after a while, unless mouse cursor is on it:
2797 std::chrono::duration<float> d = std::chrono::steady_clock::now() - itr->lasttime;
2798 std::chrono::duration<float> d2 = std::chrono::steady_clock::now() - itr->firsttime;
2799
2800 if (itr->is_info)
2801 {
2802 if (d.count() >= 1.0f && d2.count() >= 3.0F && inf_hovered == false) itr = itsErrors.erase(itr);
2803 else ++itr;
2804 }
2805 else
2806 {
2807 if (d.count() >= 1.0f && d2.count() >= 10.0F && err_hovered == false) itr = itsErrors.erase(itr);
2808 else ++itr;
2809 }
2810 }
2811}
2812
2813// ##############################################################################################################
2814void jevois::GUIhelper::helpMarker(char const * msg, char const * msg2, char const * msg3)
2815{
2816 //ImGui::TextDisabled("(?)");
2817 ImGui::TextUnformatted("(?)");
2818 if (ImGui::IsItemHovered())
2819 {
2820 ImGui::BeginTooltip();
2821 ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
2822 ImGui::TextUnformatted(msg);
2823 if (msg2) { ImGui::Separator(); ImGui::TextUnformatted(msg2); }
2824 if (msg3) { ImGui::Separator(); ImGui::TextUnformatted(msg3); }
2825 ImGui::PopTextWrapPos();
2826 ImGui::EndTooltip();
2827 }
2828}
2829
2830// ##############################################################################################################
2831bool jevois::GUIhelper::toggleButton(char const * name, bool * val)
2832{
2833 bool changed = false;
2834 if (*val)
2835 {
2836 ImGui::PushID(name);
2837 ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(4.0f/7.0f, 1.0f, 1.0f));
2838 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(4.0f/7.0f, 1.0f, 1.0f));
2839 ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(4.0f/7.0f, 0.5f, 0.5f));
2840 if (ImGui::Button(name)) { *val = false; changed = true; }
2841 ImGui::PopStyleColor(3);
2842 ImGui::PopID();
2843 }
2844 else if (ImGui::Button(name)) { *val = true; changed = true; }
2845
2846 return changed;
2847}
2848
2849// ##############################################################################################################
2851{
2852 unsigned short winw, winh;
2853 startFrame(winw, winh);
2854
2855 if (itsHeadless.loaded() == false)
2856 try { itsHeadless.load(JEVOIS_SHARE_PATH "/icons/headless.png"); }
2857 catch (...)
2858 {
2860 cv::Mat blank(winh, winw, CV_8UC4, 0);
2861 itsHeadless.load(blank);
2862 }
2863
2864 if (itsHeadless.loaded()) itsHeadless.draw(ImVec2(0, 0), ImVec2(winw, winh), ImGui::GetBackgroundDrawList());
2865
2866 endFrame();
2867}
2868
2869// ##############################################################################################################
2870void jevois::GUIhelper::highlightText(std::string const & str)
2871{
2872 ImGui::PushStyleColor(ImGuiCol_Text, 0xff0000ff);
2873 ImGui::TextUnformatted(str.c_str());
2874 ImGui::PopStyleColor();
2875}
2876
2877// ##############################################################################################################
2878void jevois::GUIhelper::demoBanner(std::string const & title, std::string const & msg)
2879{
2880 // Here we just update our internals, we will do the drawing in the main loop:
2881 itsBannerTitle = title;
2882 itsBannerMsg = msg;
2883}
2884
2885// ##############################################################################################################
2887{
2888 if (itsCompileState == CompilationState::Cmake || itsCompileState == CompilationState::Make ||
2889 itsCompileState == CompilationState::Install || itsCompileState == CompilationState::CPack)
2890 reportError("Still compiling... Try again later...");
2891 else if (std::filesystem::exists(itsNewMapping.srcpath()) && std::filesystem::exists(itsNewMapping.cmakepath()))
2892 itsCompileState = CompilationState::Start;
2893 else
2894 {
2895 // Maybe the user opened another file than the currently running module, which can occur if a module gives a hard
2896 // crash and cannot be loaded anymore. See if we can compile it using the code editor's current file path:
2897 std::filesystem::path const & fpath = itsCodeEditor->getLoadedFilePath();
2898 std::filesystem::path const fn = fpath.filename();
2899
2900 if (fn == "CMakeLists.txt" || fn.extension() == ".C")
2901 {
2902 itsNewMapping = { };
2903 std::filesystem::path p = fpath.parent_path();
2904 itsNewMapping.modulename = p.filename();
2905 itsNewMapping.vendor = p.parent_path().filename();
2906 itsNewMapping.ispython = false;
2907
2908 // Beware that the rest of the mapping is uninitialized but that should be enough to recompile...
2909 if (std::filesystem::exists(itsNewMapping.srcpath()) && std::filesystem::exists(itsNewMapping.cmakepath()))
2910 itsCompileState = CompilationState::Start;
2911 else reportError("Cannot find " + itsNewMapping.srcpath() + " or " + itsNewMapping.cmakepath() + " -- IGNORED");
2912 }
2913 else reportError("Cannot compile " + fpath.string() + " -- IGNORED");
2914 }
2915}
2916
2917// ##############################################################################################################
2919{
2920 ImGui::PushStyleColor(ImGuiCol_WindowBg, 0xf0e0ffe0);
2921 // Set window size applied only on first use ever, otherwise from imgui.ini:
2922 ImGui::SetNextWindowSize(ImVec2(800, 680), ImGuiCond_FirstUseEver);
2923
2924 // Open the window, allow closing when we are in error:
2925 int constexpr flags = ImGuiWindowFlags_HorizontalScrollbar;
2926 bool keep_window_open = true;
2927 if (itsCompileState == CompilationState::Error) ImGui::Begin("C++ Module Compilation", &keep_window_open, flags);
2928 else ImGui::Begin("C++ Module Compilation", nullptr, flags);
2929
2930 ImGui::TextUnformatted(("Compiling " + itsNewMapping.srcpath()).c_str());
2931 ImGui::TextUnformatted(("Using " + itsNewMapping.cmakepath()).c_str());
2932 ImGui::Separator();
2933 std::string const modpath = itsNewMapping.path();
2934 std::string const buildpath = modpath + "/build";
2935
2936 try
2937 {
2938 switch (itsCompileState)
2939 {
2940 // ----------------------------------------------------------------------------------------------------
2941 case CompilationState::Start:
2942 LINFO("Start compiling " << itsNewMapping.srcpath());
2943 itsIdleBlocked = true;
2944 for (std::string & s : itsCompileMessages) s.clear();
2945 std::filesystem::remove_all(buildpath);
2946 itsCompileState = CompilationState::Cmake;
2947
2948 // Write a small script to allow users to recompile by hand in case of a bad crashing module:
2949 try
2950 {
2951 std::filesystem::path sp(modpath + "/rebuild.sh");
2952
2953 std::ofstream ofs(sp);
2954 if (ofs.is_open() == false)
2955 reportError("Cannot write " + sp.string() + " -- check that you are running as root.");
2956 else
2957 {
2958 // Keep this in sync with the commands run below:
2959 ofs << "#!/bin/sh" << std::endl << "set -e" << std::endl;
2960 ofs << "cmake -S " << modpath << " -B " << buildpath << " -DJEVOIS_HARDWARE=PRO"
2961#ifdef JEVOIS_PLATFORM
2962 << " -DJEVOIS_PLATFORM=ON -DJEVOIS_NATIVE=ON"
2963#endif
2964 << std::endl;
2965 ofs << "JEVOIS_SRC_ROOT=none cmake --build " << buildpath << std::endl;
2966 ofs << "cmake --install " << buildpath << std::endl;
2967 ofs << "cd " << buildpath <<
2968 " && cpack && mkdir -p /jevoispro/debs && /bin/mv *.deb /jevoispro/debs/" << std::endl;
2969 ofs.close();
2970
2971 // Set the file as executable:
2972 using std::filesystem::perms;
2973 std::filesystem::permissions(sp, perms::owner_all | perms::group_read | perms::group_exec |
2974 perms::others_read | perms::others_exec);
2975 }
2976 } catch (...) { }
2977 break;
2978
2979 // ----------------------------------------------------------------------------------------------------
2980 case CompilationState::Cmake:
2981 if (compileCommand("cmake -S " + modpath + " -B " + buildpath +
2982 " -DJEVOIS_HARDWARE=PRO"
2983#ifdef JEVOIS_PLATFORM
2984 " -DJEVOIS_PLATFORM=ON -DJEVOIS_NATIVE=ON"
2985#endif
2986 , itsCompileMessages[0]))
2987 itsCompileState = CompilationState::Make;
2988 break;
2989
2990 // ----------------------------------------------------------------------------------------------------
2991 case CompilationState::Make:
2992 if (compileCommand("JEVOIS_SRC_ROOT=none cmake --build " + buildpath, itsCompileMessages[1]))
2993 itsCompileState = CompilationState::Install;
2994 break;
2995
2996 // ----------------------------------------------------------------------------------------------------
2997 case CompilationState::Install:
2998 if (compileCommand("cmake --install " + buildpath, itsCompileMessages[2]))
2999 itsCompileState = CompilationState::CPack;
3000 break;
3001
3002 // ----------------------------------------------------------------------------------------------------
3003 case CompilationState::CPack:
3004 if (compileCommand("cd " + buildpath + " && cpack && mkdir -p /jevoispro/debs && /bin/mv *.deb /jevoispro/debs/",
3005 itsCompileMessages[3]))
3006 itsCompileState = CompilationState::Success;
3007 break;
3008
3009 // ----------------------------------------------------------------------------------------------------
3010 case CompilationState::Success:
3011 {
3012 ImGui::TextUnformatted("Compilation success!");
3013 ImGui::TextUnformatted("See below for details...");
3014 ImGui::Separator();
3015 static int run_default = 0;
3016 int ret = modal("Success! Run the module now?", "Compilation success!\n\nA deb package of your module is in "
3017 "/jevoispro/debs/\nif you want to share it.\n\nLoad and run the new module now?",
3018 &run_default, "Yes", "No");
3019 switch (ret)
3020 {
3021 case 1:
3022 runNewModule();
3023 itsIdleBlocked = false;
3024 itsCompileState = CompilationState::Idle;
3025 break;
3026
3027 case 2:
3028 // make sure we have the mapping for the new module...
3029 itsIdleBlocked = false;
3030 itsCompileState = CompilationState::Idle;
3031 engine()->reloadVideoMappings();
3032 itsRefreshVideoMappings = true;
3033 break;
3034
3035 default: break; // Need to wait
3036 }
3037 }
3038 break;
3039
3040 case CompilationState::Error:
3041 {
3042 itsIdleBlocked = false;
3043 highlightText("Compilation failed!");
3044 ImGui::TextUnformatted("You may need to edit the two files above.");
3045 ImGui::TextUnformatted("See below for details...");
3046 ImGui::Separator();
3047 static bool show_modal = false;
3048
3049 ImGui::AlignTextToFramePadding();
3050 if (ImGui::Button("Edit CMakeLists.txt"))
3051 { itsCodeEditor->loadFile(itsNewMapping.cmakepath()); show_modal = true; }
3052
3053 ImGui::SameLine(); ImGui::TextUnformatted(" "); ImGui::SameLine();
3054 if (ImGui::Button(("Edit " + itsNewMapping.modulename + ".C").c_str()))
3055 { itsCodeEditor->loadFile(itsNewMapping.srcpath()); show_modal = true; }
3056
3057 ImGui::SameLine(); ImGui::TextUnformatted(" "); ImGui::SameLine();
3058 if (ImGui::Button("Compile again")) itsCompileState = CompilationState::Start;
3059
3060 ImGui::Separator();
3061
3062 if (show_modal)
3063 {
3064 static int doit_default = 0;
3065 int ret = modal("Ready to edit", "File will now be loaded in the Code tab of the main window and "
3066 "ready to edit. Please switch to the Code tab in the main window.\n\n"
3067 "When you save it, we will try to compile it again.",
3068 &doit_default, "Close Compilation Window", "Keep Open");
3069 switch (ret)
3070 {
3071 case 1: show_modal = false; itsCompileState = CompilationState::Idle; break;
3072 case 2: show_modal = false; break; // user want to keep this window open
3073 default: break; // Need to wait
3074 }
3075 }
3076 }
3077 break;
3078
3079 // ----------------------------------------------------------------------------------------------------
3080 default:
3081 LERROR("Internal error: invalid compilation state " << int(itsCompileState) << " -- RESET TO IDLE");
3082 itsCompileState = CompilationState::Idle;
3083 itsIdleBlocked = false;
3084 }
3085 } catch (...) { reportAndIgnoreException(); }
3086
3087 // Display the messages:
3088 for (std::string & s : itsCompileMessages)
3089 if (s.empty() == false)
3090 {
3091 ImGui::TextUnformatted(s.c_str());
3092 ImGui::Separator();
3093 }
3094
3095 ImGui::End();
3096 ImGui::PopStyleColor();
3097
3098 // Switch to idle if we were in error and user closed the window:
3099 if (keep_window_open == false) { itsIdleBlocked = false; itsCompileState = CompilationState::Idle; }
3100}
3101
3102
3103// ##############################################################################################################
3104bool jevois::GUIhelper::compileCommand(std::string const & cmd, std::string & msg)
3105{
3106 if (itsCompileFut.valid() == false)
3107 {
3108 itsCompileFut = jevois::async([&](std::string cmd) { return jevois::system(cmd, true); }, cmd);
3109 msg = "Running: " + cmd + "\nPlease wait ...\n";
3110 LINFO("Running: " + cmd);
3111 }
3112 else if (itsCompileFut.wait_for(std::chrono::milliseconds(1)) == std::future_status::ready)
3113 try
3114 {
3115 msg += itsCompileFut.get() + "\nSuccess!";
3116 LINFO("Success running: " << cmd);
3117 return true;
3118 }
3119 catch (...)
3120 {
3121 LERROR("Failed running: " << cmd);
3122 itsCompileState = CompilationState::Error;
3124 }
3125
3126 return false;
3127}
3128
3129#endif // JEVOIS_PRO
#define JEVOIS_MODULE_PATH
Base path for modules.
Definition Config.H:76
#define JEVOIS_CUSTOM_DNN_URL
URL where custom converted DNN models can be downloaded:
Definition Config.H:91
#define JEVOIS
Helper string that evaluates to "jevois" or "jevoispro" depending on values of JEVOIS_A33 and JEVOIS_...
Definition Config.H:58
#define JEVOIS_VERSION_STRING
Software version, as string.
Definition Config.H:70
#define JEVOIS_CUSTOM_DNN_PATH
Directory where custom DNN models are stored:
Definition Config.H:85
#define JEVOISPRO_GSERIAL_FILE
Flag file for whether to enable g_serial at boot on jevoispro.
Definition Config.H:115
#define JEVOIS_SHARE_PATH
Base path for shared files (e.g., neural network weights, etc)
Definition Config.H:82
#define JEVOIS_PYDNN_PATH
Directory where python pre/net/post DNN processors are stored:
Definition Config.H:88
#define JEVOIS_ROOT_PATH
Root path for runtime jevois config files, videomappings.cfg, modules, etc.
Definition Config.H:73
#define o
Definition Font10x20.C:6
void drawTinyImGUIdemo(ImDrawList *d, V a, V b, V, ImVec4, F t)
Definition GUIhelper.C:2586
int h
Definition GUIhelper.C:2580
#define J(b)
Definition GUIhelper.C:2584
#define O(b)
Definition GUIhelper.C:2585
TinyImGUIdemo G[999]
Definition GUIhelper.C:2580
float F
Definition GUIhelper.C:2580
#define L(i, x, y, z)
Definition GUIhelper.C:2581
#define Q(y)
Definition GUIhelper.C:2582
ImVec2 V
Definition GUIhelper.C:2580
#define JEVOIS_WAIT_GET_FUTURE(f)
Wait for a future to become ready for 5 seconds, get(), warn and ignore exception,...
Definition Log.H:336
#define JEVOISPRO_FMT_GUI
JeVois-Pro zero-copy display of camera input frame (to be used as output mode in VideoMapping)
Definition Utils.H:31
A component of a model hierarchy.
Definition Component.H:182
void setParamValUnique(std::string const &paramdescriptor, T const &val)
Set a parameter value, simple version assuming only one parameter match.
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:589
std::string getParamStringUnique(std::string const &paramdescriptor) const
Get a parameter value by string, simple version assuming only one parameter match.
Definition Component.C:405
void setParamStringUnique(std::string const &paramdescriptor, std::string const &val)
Set a parameter value by string, simple version assuming only one parameter match.
Definition Component.C:374
T getParamValUnique(std::string const &paramdescriptor) const
Get a parameter value, simple version assuming only one parameter match.
JeVois processing engine - gets images from camera sensor, processes them, and sends results over USB...
Definition Engine.H:416
void requestSetFormat(int idx)
Use this to request a format change from within process()
Definition Engine.C:942
void drawCameraGUI()
Draw all camera controls into our GUI.
Definition Engine.C:2878
void nextDemo()
When in demo mode, switch to next demo.
Definition Engine.C:610
std::shared_ptr< Module > module() const
Get a pointer to our current module (may be null)
Definition Engine.C:1477
void abortDemo()
When in demo mode, abort demo mode.
Definition Engine.C:614
void quit()
Terminate the program.
Definition Engine.C:1888
VideoMapping const & getCurrentVideoMapping() const
Get the current video mapping.
Definition Engine.C:1541
void reloadVideoMappings()
Re-load video mappings from videomappings.cfg.
Definition Engine.C:638
void foreachVideoMapping(std::function< void(VideoMapping const &m)> &&func)
Run a function on every video mapping.
Definition Engine.C:1597
Class to hold a GPUtexture, GPUprogram, and other data associated with rendering an image in OpenGL.
Definition GPUimage.H:39
ImVec2 i2ds(ImVec2 const &p)
Convert a 2D size from within a rendered image to on-screen.
Definition GPUimage.C:413
ImVec2 i2d(ImVec2 const &p)
Convert coordinates of a point from within a rendered image to on-screen.
Definition GPUimage.C:406
ImVec2 d2i(ImVec2 const &p)
Convert coordinates of a point from on-screen to within a rendered image.
Definition GPUimage.C:420
ImVec2 d2is(ImVec2 const &p)
Convert a 2D size from on-screen to within a rendered image.
Definition GPUimage.C:427
Simple console with coloring and completion.
Definition GUIconsole.H:32
void draw()
Render into ImGui.
Definition GUIconsole.C:84
Editor panel for JeVois-Pro GUI.
Definition GUIeditor.H:47
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:608
void endFrame()
Finish current frame and render it.
Definition GUIhelper.C:786
bool seroutEnabled() const
Tell whether user enabled serout messages to GUI console.
Definition GUIhelper.C:1659
bool combo(std::string const &name, std::vector< std::string > const &items, int &selected_index)
Helper to draw a combobox from a vector of strings.
Definition GUIhelper.C:1722
void resetstate(bool modulechanged=true)
Reset to default state, typically called on Module or video format change.
Definition GUIhelper.C:114
std::shared_ptr< GUIeditor > itsCfgEditor
Definition GUIhelper.H:516
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:654
void reportAndIgnoreException(std::string const &prefix="")
Report current exception in a modal dialog, then ignore it.
Definition GUIhelper.C:2708
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:334
bool startFrame(unsigned short &w, unsigned short &h)
Start a new rendering frame.
Definition GUIhelper.C:169
ImVec2 i2ds(ImVec2 p, char const *name=nullptr)
Convert a 2D size from within a rendered image to on-screen.
Definition GUIhelper.C:446
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:634
void reportErrorOrInfo(std::string const &err, bool is_info)
Definition GUIhelper.C:2673
void drawEllipse(float x, float y, float rx, float ry, float rot=0.0F, ImU32 col=IM_COL32(128, 255, 128, 255), bool filled=true)
Draw ellipse over an image.
Definition GUIhelper.C:621
virtual ~GUIhelper()
Destructor.
Definition GUIhelper.C:107
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:488
void onParamChange(gui::scale const &param, float const &newval) override
bool frameStarted() const
Helper to indicate that startFrame() was called, and thus endFrame() should be called.
Definition GUIhelper.C:283
void startCompilation()
Compile a newly created module.
Definition GUIhelper.C:2886
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:366
std::shared_ptr< GUIeditor > itsCodeEditor
Definition GUIhelper.H:519
void drawNewModuleForm()
Definition GUIhelper.C:2120
void clearErrors()
Clear all errors currently displayed in the JeVois-Pro GUI.
Definition GUIhelper.C:2701
void newModEntry(char const *wname, std::string &str, char const *desc, char const *hint, char const *hlp)
Definition GUIhelper.C:1784
void releaseImage(char const *name)
Release an image.
Definition GUIhelper.C:698
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:677
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:2814
void demoBanner(std::string const &title="", std::string const &msg="")
Display some text in a big banner, used by demo mode.
Definition GUIhelper.C:2878
void highlightText(std::string const &str)
Like ImGui::Textunformatted but in a highlight color (typically, red)
Definition GUIhelper.C:2870
void reportError(std::string const &err)
Report an error in an overlay window.
Definition GUIhelper.C:2665
void headlessDisplay()
Show a message that we are running headless.
Definition GUIhelper.C:2850
ImVec2 i2d(ImVec2 p, char const *name=nullptr)
Convert coordinates of a point from within a rendered image to on-screen.
Definition GUIhelper.C:410
void reportAndRethrowException(std::string const &prefix="")
Report current exception in a modal dialog, then re-throw it.
Definition GUIhelper.C:2733
bool selectImageBox(int &state, ImVec2 &tl, ImVec2 &br, ImU32 col=IM_COL32(128, 255, 128, 255))
Helper to select a rectangular box by dragging the mouse over the display.
Definition GUIhelper.C:1736
void drawModuleSelect()
Definition GUIhelper.C:1031
bool idle() const
Check for idle in case startFrame() was called elsewhere.
Definition GUIhelper.C:227
ImVec2 d2is(ImVec2 p, char const *name=nullptr)
Convert a 2D size from on-screen to within a rendered image.
Definition GUIhelper.C:749
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:482
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:664
void releaseImage2(char const *name)
Release an image, second video stream.
Definition GUIhelper.C:705
InputFrame const * getInputFrame() const
Get access to the InputFrame last drawn with drawInputFrame()
Definition GUIhelper.C:390
ImU32 applyFillAlpha(ImU32 col) const
Definition GUIhelper.C:646
int modal(std::string const &title, char const *text, int *default_val=nullptr, char const *b1txt="Ok", char const *b2txt="Cancel")
Helper to draw a modal with 2 choices.
Definition GUIhelper.C:1669
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:524
void drawPolyInternal(ImVec2 const *pts, size_t npts, ImU32 col, bool filled)
Definition GUIhelper.C:502
void setparstr(std::string const &descriptor, std::string const &val)
Definition GUIhelper.C:1285
std::string itsWindowTitle
Definition GUIhelper.H:471
bool compileCommand(std::string const &cmd, std::string &msg)
Definition GUIhelper.C:3104
void reportInfo(std::string const &inf)
Report a transient info message in an overlay window.
Definition GUIhelper.C:2669
GUIhelper(std::string const &instance, bool conslock=false)
Constructor.
Definition GUIhelper.C:43
ImVec2 d2i(ImVec2 p, char const *name=nullptr)
Convert coordinates of a point from on-screen to within a rendered image.
Definition GUIhelper.C:712
bool toggleButton(char const *name, bool *val)
Helper to draw a toggle button.
Definition GUIhelper.C:2831
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:287
bool serlogEnabled() const
Tell whether user enabled serlog messages to GUI console.
Definition GUIhelper.C:1655
Simple class to monitor a serial port in the JeVois-Pro GUI.
Definition GUIserial.H:34
Exception-safe wrapper around a raw camera input frame.
Definition InputFrame.H:51
RawImage const & get(bool casync=false) const
Get the next captured camera image.
Definition InputFrame.C:50
RawImage const & get2(bool casync=false) const
Get the next captured camera image, ISP-scaled second frame.
Definition InputFrame.C:67
bool hasScaledImage() const
Check whether a second input image scaled by the JeVoisPro Platform ISP is available.
Definition InputFrame.C:61
std::shared_ptr< Comp > getComponent(std::string const &instanceName) const
Get a top-level component by instance name.
Base class for Parameter.
Definition Parameter.H:126
bool hidden() const
Returns whether parameter is hidden.
virtual ParameterSummary const summary() const =0
Get summary info about this parameter.
ParameterSummary provides a summary about a parameter.
Definition Parameter.H:85
std::string name
Plain name of the parameter.
Definition Parameter.H:91
std::string category
Category of the parameter, as a string.
Definition Parameter.H:109
A generic range class.
Definition Range.H:81
T const & min() const
Return the minimum value.
T const & max() const
Return the maximum value.
A raw image as coming from a V4L2 Camera and/or being sent out to a USB Gadget.
Definition RawImage.H:111
unsigned int fmt
Pixel format as a V4L2_PIX_FMT_XXX.
Definition RawImage.H:147
size_t bufindex
The index of the data buffer in the kernel driver.
Definition RawImage.H:150
unsigned int width
Image width in pixels.
Definition RawImage.H:145
unsigned int height
Image height in pixels.
Definition RawImage.H:146
Base class for a module that supports standardized serial messages.
Definition Module.H:234
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
Definition Log.H:230
std::string getSysInfoCPU()
Get CPU info: frequency, thermal, load.
Definition SysInfo.C:24
std::string warnAndIgnoreException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
Definition Log.C:236
#define PLFATAL(msg)
Like LDEBUG but appends errno and strerror(errno), to be used when some system call fails.
Definition Log.H:239
std::string getSysInfoMem()
Get memory info.
Definition SysInfo.C:66
std::string getSysInfoVersion()
Get O.S. version info.
Definition SysInfo.C:74
#define LERROR(msg)
Convenience macro for users to print out console or syslog messages, ERROR level.
Definition Log.H:211
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
Definition Log.H:194
std::string getPythonExceptionString(boost::python::error_already_set &)
Python exception translation to string so we can print the traceback to our serlog stream.
std::string shapestr(cv::Mat const &m)
Get a string of the form: "nD AxBxC... TYPE" from an n-dimensional cv::Mat with data type TYPE.
Definition Utils.C:126
void paramStringToVal(std::string const &valstring, T &result)
Machine-readable conversion from string to T, for use in jevois::Parameter.
std::future< std::invoke_result_t< std::decay_t< Function >, std::decay_t< Args >... > > async(Function &&f, Args &&... args)
Async execution using a thread pool.
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:317
std::string tolower(std::string const &str)
Convert string to lowercase.
Definition Utils.C:378
std::string getFileString(char const *fname, int skip=0)
Read one line from a file and return it as a string.
Definition Utils.C:542
std::string system(std::string const &cmd, bool errtoo=true)
Execute a command and grab stdout output to a string.
Definition Utils.C:462
std::string join(std::vector< T > const &tokens, std::string const &delimiter)
Concatenate a vector of tokens into a string.
std::string sformat(char const *fmt,...) __attribute__((format(__printf__
Create a string using printf style arguments.
Definition Utils.C:440
size_t replaceStringAll(std::string &str, std::string const &from, std::string const &to)
Replace all instances of 'from' with 'to'.
Definition Utils.C:345
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:295
std::string fccstr(unsigned int fcc)
Convert a V4L2 four-cc code (V4L2_PIX_FMT_...) to a 4-char string.
Definition Utils.C:45
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:270
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.
Main namespace for all JeVois classes and functions.
Definition Concepts.dox:2
size_t getNumInstalledVPUs()
Get the number of Myriad-X VPUs present on this system.
Definition SysInfo.C:112
int getFanSpeed()
Get fan speed in percent, only meaningful on JeVois-Pro Platform, all others return 0.
Definition SysInfo.C:153
size_t getNumInstalledNPUs()
Get the number of JeVois-Pro NPUs present on this system.
Definition SysInfo.C:127
size_t getNumInstalledTPUs()
Get the number of Coral TPUs present on this system.
Definition SysInfo.C:87
size_t getNumInstalledSPUs()
Get the number of Hailo8 SPUs present on this system.
Definition SysInfo.C:138
bool operator<(TinyImGUIdemo &o)
Definition GUIhelper.C:2580
Simple struct to hold video mapping definitions for the processing Engine.
WDRtype wdr
Type of wide-dynamic-range (WDR) to use, if sensor supports it.
unsigned int cfmt
camera pixel format
unsigned int ow
output width
unsigned int c2fmt
When crop is CropScale, pixel format of the scaled images, otherwise 0.
std::string modinfopath() const
Return the full absolute path and file name of the module's modinfo.html file.
unsigned int c2w
When crop is CropScale, width of the scaled images, otherwise 0.
CropType crop
Type of crop/scale to apply if camera size does not match sensor native.
std::string modulename
Name of the Module that will process this mapping.
std::string str() const
Convenience function to print out the whole mapping in a human-friendly way.
float cfps
camera frame rate in frames/sec
bool ispython
True if the module is written in Python; affects behavior of sopath() only.
std::string menustr2() const
Convenience function to print out the whole mapping in a human-friendly way to be used in a menu.
std::string path() const
Return the full absolute path the module's directory.
unsigned int cw
camera width
float ofps
output frame rate in frames/sec
unsigned int c2h
When crop is CropScale, height of the scaled images, otherwise 0.
unsigned int ch
camera height
unsigned int oh
output height
std::string srcpath() const
Return the full absolute path and file name of the module's .C or .py file.
std::string menustr() const
Convenience function to print out the whole mapping in a human-friendly way to be used in a menu.
std::string vendor
Module creator name, used as a directory to organize the modules.
std::string cmakepath() const
Return the full absolute path and file name of the module's CMakeLists.txt file.
unsigned int ofmt
output pixel format, or 0 for no output over USB