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