1 | !7 |
2 | |
3 | need latest indexOf. |
4 | |
5 | // Note: It's ok again to use db_mainConcepts() |
6 | |
7 | !include early #1033884 // Compact Module Include Gazelle V [dev version] |
8 | |
9 | module GazelleScreenCam { |
10 | !include early #1025212 // +Enabled without visualization |
11 | |
12 | int pixelRows = 128, colors = 8; |
13 | S script = "64p 8c gradientImage"; |
14 | S testScreenScript; |
15 | S newScript; |
16 | S screenCamScript; |
17 | S selectedTab; |
18 | S javaCode; |
19 | bool animate = true; |
20 | bool horizontalLayout; // flat layout |
21 | int fpsTarget = 20; |
22 | S webCamName; |
23 | bool webCamAccessEnabled = true; |
24 | |
25 | WatchTarget watchTarget; |
26 | |
27 | transient ImageSurface isPosterized, isTestScreen; |
28 | transient new ScreenCamStream imageStream; |
29 | |
30 | transient Gazelle22_ImageToRegions imageToRegions_finished; |
31 | transient new DoubleFPSCounter fpsCounter; |
32 | transient int fps; |
33 | transient WatchTargetSelector watchTargetSelector; |
34 | transient new RollingAverage remainingMSPerFrame; |
35 | transient int remainingMS; |
36 | |
37 | transient new FunctionTimings<S> functionTimings; |
38 | |
39 | transient ReliableSingleThread rstRunScript = dm_rst(me(), r _runScript); |
40 | |
41 | transient JGazelleVScriptRunner scriptRunner; |
42 | transient JGazelleVScriptRunner testScreenScriptRunner; |
43 | |
44 | transient Animation animation; |
45 | |
46 | transient UIURLSystem uiURLs; |
47 | |
48 | //transient L<Webcam> availableWebCams; |
49 | transient JComboBox<Webcam> cbWebCam; |
50 | transient SingleComponentPanel scpWebCamImage; |
51 | transient WebcamPanel webCamPanel; |
52 | |
53 | transient JLeftArrowScriptIDE screenCamScriptIDE; |
54 | transient GazelleV_LeftArrowScript.Script runningScreenCamScript; |
55 | |
56 | // set by host |
57 | transient Concepts concepts; |
58 | |
59 | S uiURL = "Main Tabs"; |
60 | |
61 | transient FileWatchService fileWatcher; |
62 | |
63 | transient SimpleCRUD_v2<Label> labelCRUD; |
64 | transient SimpleCRUD_v2<GalleryImage> imageCRUD; |
65 | |
66 | transient JGallery gallery; |
67 | transient JComponent galleryComponent; |
68 | |
69 | transient FlexibleRateTimer screenCamTimer; |
70 | transient SingleComponentPanel scpMain; |
71 | transient JTabbedPane mainTabs; |
72 | transient bool showRegionsAsOutline = true; |
73 | transient JComponent watchScreenPane; |
74 | transient S screenCamRecognitionOutput; |
75 | |
76 | transient BackgroundProcessesUI backgroundProcessesUI; |
77 | |
78 | // add fields here |
79 | |
80 | start { |
81 | dm_onFieldChange horizontalLayout( |
82 | //r dm_revisualize // deh buggy |
83 | r dm_reload |
84 | ); |
85 | |
86 | if (concepts == null) { |
87 | // non-standalone mode (doesn't work yet) |
88 | printWithMS("Starting DB"); |
89 | db(); |
90 | concepts = db_mainConcepts(); |
91 | } else |
92 | assertSame(concepts, db_mainConcepts()); |
93 | |
94 | concepts.quietSave = true; |
95 | |
96 | // make indexes |
97 | indexConceptField(concepts, GalleryImage, "path"); |
98 | indexConceptField(concepts, Example, "item"); |
99 | |
100 | // update count in tab when tab isn't selected |
101 | onConceptChanges(concepts, -> { |
102 | labelCRUD?.updateEnclosingTabTitle(); |
103 | //imageCRUD?.updateEnclosingTabTitle(); |
104 | updateEnclosingTabTitleWithCount(galleryComponent, countConcepts(GalleryImage)); |
105 | }); |
106 | |
107 | uiURLs = new UIURLSystem(me(), dm_fieldLiveValue uiURL()); |
108 | |
109 | backgroundProcessesUI = new BackgroundProcessesUI; |
110 | |
111 | dm_watchFieldAndNow enabled(-> backgroundProcessesUI.addOrRemove(enabled, "Screen Cam")); |
112 | |
113 | scriptRunner = new JGazelleVScriptRunner(dm_fieldLiveValue script(me())); |
114 | testScreenScriptRunner = new JGazelleVScriptRunner(dm_fieldLiveValue testScreenScript(me())); |
115 | |
116 | printWithMS("Making image stream"); |
117 | |
118 | imageStream.onNewElement(img -> { |
119 | fpsCounter.inc(); |
120 | setField(fps := iround(fpsCounter!)); |
121 | |
122 | // perform analysis on screen cam image |
123 | |
124 | // new G22_ImageAnalysis screenCamImageAnalysis; |
125 | |
126 | assertTrue("meta", getMetaSrc(img) instanceof ScreenShotMeta); |
127 | Gazelle22_ImageToRegions itr = new(functionTimings, img, new SnPSettings(pixelRows, colors)); |
128 | itr.run(); |
129 | imageToRegions_finished = itr; |
130 | |
131 | // run "linear" script on image if any |
132 | |
133 | if (shouldRunScript()) rstRunScript.go(); |
134 | |
135 | // run left-arrow script |
136 | |
137 | if (screenCamScriptIDE != null |
138 | && screenCamScriptIDE.visible()) try { |
139 | g22_runPostAnalysisLeftArrowScript(itr, runningScreenCamScript); |
140 | } catch e { |
141 | printStackTrace(e); |
142 | screenCamScriptIDE.showRuntimeError(e); |
143 | } |
144 | |
145 | // make textual result of analysis |
146 | |
147 | S text = nRegions(itr.regions.regionCount()); |
148 | setField(screenCamRecognitionOutput := text); |
149 | |
150 | // display image after analysis so we can highlight a region |
151 | |
152 | g22_renderPosterizedHighlightedImage(isPosterized, itr, showRegionsAsOutline); |
153 | }); |
154 | |
155 | watchTargetSelector = new WatchTargetSelector; |
156 | |
157 | /*if (webCamAccessEnabled) { |
158 | printWithMS("Detecting web cams"); |
159 | watchTargetSelector.updateCamCount(); |
160 | printWithMS("Found " + n2(availableWebCams, "web cam")); |
161 | }*/ |
162 | |
163 | printWithMS("Starting screen cam"); |
164 | |
165 | ownResource(screenCamTimer = new FlexibleRateTimer(fpsTarget, rEnter { |
166 | if (!enabled) ret; |
167 | watchTargetSelector?.updateScreenCount(); |
168 | Timestamp deadline = tsNowPlusMS(1000/fpsTarget); |
169 | if (watchTarget cast WatchScreen) |
170 | imageStream.useScreen(watchTarget.screenNr-1); |
171 | else if (watchTarget cast WatchMouse) |
172 | imageStream.area(mouseArea(watchTarget.width, watchTarget.height)); |
173 | imageStream.step(); |
174 | long remaining = deadline.minus(tsNow()); |
175 | remainingMSPerFrame.add(remaining); |
176 | setField(remainingMS := iround(remainingMSPerFrame!)); |
177 | })); |
178 | screenCamTimer.start(); |
179 | dm_onFieldChange fpsTarget(-> screenCamTimer.setFrequencyImmediately(fpsTarget)); |
180 | |
181 | printWithMS("Starting dir watcher"); |
182 | |
183 | startDirWatcher(); |
184 | |
185 | printWithMS("Gathering images from disk"); |
186 | for (f : allImageFiles(galleryDir())) |
187 | addToGallery(f); |
188 | printWithMS("Got dem images"); |
189 | |
190 | transpileRaw_makeTranslator = -> hotwire(#7); |
191 | |
192 | // OSHI adds 4.5 MB to the jar... |
193 | //doAfter(5.0, r { print("Process size: ", toM_str(oshi_currentProcessResidentSize_noZGCFix())) }); |
194 | } |
195 | |
196 | void startDirWatcher { |
197 | fileWatcher = new FileWatchService; |
198 | fileWatcher.addRecursiveListener(picturesDir(), file -> { |
199 | if (!isImageFile(file)) ret; |
200 | addToGallery(file); |
201 | }); |
202 | } |
203 | |
204 | ImageSurface stdImageSurface() { |
205 | var is = pixelatedImageSurface().setAutoZoomToDisplay(true).repaintInThread(false); |
206 | new ImageSurface_PositionToolTip(is); |
207 | ret is; |
208 | } |
209 | |
210 | ImageSurface stdImageSurface(BufferedImage etc img) { |
211 | var is = stdImageSurface(); |
212 | is.setImage(img); |
213 | ret is; |
214 | } |
215 | |
216 | visualize { |
217 | gallery = new JGallery; |
218 | galleryComponent = gallery.visualize(); |
219 | |
220 | new AWTOnConceptChanges(concepts, galleryComponent, -> { |
221 | gallery.setImageFiles(map(list(GalleryImage), i -> i.path)); |
222 | }).install(); |
223 | |
224 | isPosterized = stdImageSurface(); |
225 | |
226 | //isRegions = stdImageSurface(); |
227 | isTestScreen = stdImageSurface(); |
228 | |
229 | // when test screen is visible, do the animation |
230 | awtEvery(isTestScreen, 1000/20, r stepAnimation); |
231 | |
232 | //print("Labels: " + list(concepts, Label)); |
233 | labelCRUD = new SimpleCRUD_v2(concepts, Label); |
234 | labelCRUD.hideFields("globalID"); |
235 | labelCRUD.addCountToEnclosingTab(true); |
236 | labelCRUD.itemToMap_inner2 = l |
237 | -> litorderedmap( |
238 | "Name" := l.name, |
239 | "# Examples" := n2(countConcepts(concepts, Example, label := l))); |
240 | |
241 | imageCRUD = new SimpleCRUD_v2(concepts, GalleryImage); |
242 | imageCRUD.addCountToEnclosingTab(true); |
243 | imageCRUD.itemToMap_inner2 = img |
244 | -> litorderedmap( |
245 | "File" := fileName(img.path), |
246 | "Folder" := dirPath(img.path)); |
247 | var imageCRUDVis = imageCRUD.visualize(); |
248 | imageCRUD.addButton(jPopDownButton_noText( |
249 | "Forget missing images" := rThreadEnter forgetMissingImages |
250 | )); |
251 | |
252 | // main visual |
253 | |
254 | var studyPanel = new StudyPanel; |
255 | |
256 | watchScreenPane = borderlessScrollPane(jHigherScrollPane( |
257 | jfullcenter(vstack( |
258 | withLeftAndRightMargin(hstack( |
259 | dm_rcheckBox enabled("Watch"), |
260 | watchTargetSelector.visualize(), |
261 | jlabel(" in "), |
262 | withLabelToTheRight("colors @ ", dm_spinner colors(2, 256)), |
263 | withLabelToTheRight("p", dm_powersOfTwoSpinner pixelRows(512)), |
264 | )), |
265 | verticalStrut(2), |
266 | withSideMargins(centerAndEastWithMargin( |
267 | dm_fieldLabel screenCamRecognitionOutput(), |
268 | dm_transientCalculatedToolTip speedInfo_long(rightAlignLabel(dm_transientCalculatedLabel speedInfo())) |
269 | )), |
270 | )))); |
271 | |
272 | mainTabs = scrollingTabs(jTopOrLeftTabs(horizontalLayout, |
273 | "Screen Cam" := jOnDemand screenCamPanel(), |
274 | "Screen Cam + Script" := jOnDemand screenCamPlusScriptPanel(), |
275 | WithToolTip("Study", "Here you can analyze gallery images") |
276 | := withTopAndBottomMargin(studyPanel.visualize()), |
277 | WithToolTip("Java", "Write & run actual Java code") |
278 | := withBottomMargin(jOnDemand javaPanel()), |
279 | WithToolTip("Gallery", "Gallery view with preview images") |
280 | := withBottomMargin(galleryComponent), |
281 | WithToolTip("Gallery 2", "Gallery view with more functions (delete etc)") |
282 | := wrapCRUD(imageCRUD, imageCRUDVis), |
283 | )); |
284 | |
285 | persistSelectedTabAsLiveValue(mainTabs, dm_fieldLiveValue selectedTab()); |
286 | |
287 | var cbEnabled = toolTip("Switch screen cam on or off", dm_checkBox enabled("")); |
288 | var lblScreenCam = setToolTip("Show scaled down and color-reduced screen image", |
289 | jlabel("Screen Cam")); |
290 | tabComponentClickFixer(lblScreenCam); |
291 | var screenCamTab = hstackWithSpacing(cbEnabled, lblScreenCam); |
292 | |
293 | replaceTabTitleComponent(mainTabs, "Screen Cam", screenCamTab); |
294 | |
295 | // for tab titles |
296 | labelCRUD.update(); |
297 | imageCRUD.update(); |
298 | |
299 | initUIURLs(); |
300 | |
301 | var urlBar = uiURLs.urlBar(); |
302 | setToolTip(uiURLs.comboBox, "UI navigation system (partially populated)"); |
303 | |
304 | scpMain = singleComponentPanel(); |
305 | uiURLs.scp(scpMain); |
306 | |
307 | uiURLs.showUIURL(uiURL); |
308 | |
309 | var vis = northAndCenter( |
310 | withSideAndTopMargin(centerAndEastWithMargin(urlBar, backgroundProcessesUI.shortLabel())), |
311 | //centerAndSouthOrEast(horizontalLayout, |
312 | /*withBottomMargin*/(scpMain), |
313 | /* watchScreenPane |
314 | )*/ |
315 | ); |
316 | setHorizontalMarginForAllButtons(vis, 4); |
317 | ret vis; |
318 | } |
319 | |
320 | JComponent screenCamPanel() { |
321 | ret centerAndSouthOrEast(horizontalLayout, |
322 | withTools(isPosterized), |
323 | watchScreenPane); |
324 | } |
325 | |
326 | JComponent screenCamPlusScriptPanel() enter { |
327 | print("screenCamPlusScriptPanel"); |
328 | //ret watchScreenPane; |
329 | try { |
330 | var ide = screenCamScriptIDE = leftArrowScriptIDE(); |
331 | ide.runButtonShouldBeEnabled = -> |
332 | eq(getText(ide.btnRun), "Stop") |
333 | || ide.runButtonShouldBeEnabled_base(); |
334 | |
335 | ide.runButtonAction = -> { |
336 | if (eq(getText(ide.btnRun), "Stop")) |
337 | runningScreenCamScript = null; |
338 | else { |
339 | runningScreenCamScript = screenCamScriptIDE.parsedScript(); |
340 | ide.showStatus("Running"); |
341 | } |
342 | setText(ide.btnRun, |
343 | runningScreenCamScript == null ? "Run" : "Stop"); |
344 | }; |
345 | |
346 | ret centerAndSouthOrEast(horizontalLayout, |
347 | //withTools( |
348 | jhsplit( |
349 | jscroll_centered_borderless(isPosterized), |
350 | screenCamScriptIDE |
351 | .lvScript(dm_fieldLiveValue screenCamScript()) |
352 | .visualize()) |
353 | //, isPosterized) |
354 | , watchScreenPane); |
355 | } on fail e { print(e); } |
356 | } |
357 | |
358 | S speedInfo() { |
359 | ret "FPS " + fps + " idle " + remainingMS + " ms"; |
360 | } |
361 | |
362 | S speedInfo_long() { |
363 | ret "Screen cam running at " + nFrames(fps) + "/second. " + n2(remainingMS) + " ms remaining per frame in first core" |
364 | + " (of targeted " + fpsTarget + " FPS)"; |
365 | } |
366 | |
367 | bool useErrorHandling() { false; } |
368 | |
369 | S renderFunctionTimings() { |
370 | ret lines(ciSorted(map(functionTimings!, (f, avg) -> |
371 | firstToUpper(f) + ": " + n2(iround(nsToMicroseconds(avg!))) + " " + microSymbol() + "s (" + n2(iround(avg.n())) + ")"))); |
372 | } |
373 | |
374 | transient long _runScript_idx; |
375 | |
376 | void _runScript() { |
377 | scriptRunner.parseAndRunOn(imageToRegions_finished.ii); |
378 | } |
379 | |
380 | bool shouldRunScript() { |
381 | ret isShowing(scriptRunner.scpScriptResult); |
382 | } |
383 | |
384 | void stepAnimation { |
385 | if (!animate) ret; |
386 | if (animation == null) { |
387 | animation = new AnimatedLine; |
388 | animation.start(); |
389 | } |
390 | animation.nextFrame(); |
391 | var img = whiteImage(animation.w, animation.h); |
392 | animation.setGraphics(createGraphics(img)); |
393 | animation.paint(); |
394 | isTestScreen?.setImage(img); |
395 | |
396 | var ii = bwIntegralImage_withMeta(img); |
397 | testScreenScriptRunner.parseAndRunOn(ii); |
398 | } |
399 | |
400 | JComponent testScreenPanel() { |
401 | ret centerAndSouthWithMargin( |
402 | hsplit( |
403 | northAndCenterWithMargin(centerAndEastWithMargin( |
404 | jlabel("Input"), dm_fieldCheckBox animate()), |
405 | jscroll_centered_borderless(isTestScreen)), |
406 | |
407 | northAndCenterWithMargin(centerAndEastWithMargin( |
408 | jlabel("Output"), testScreenScriptRunner.lblScore), |
409 | testScreenScriptRunner.scpScriptResult) |
410 | ), |
411 | |
412 | testScreenScriptRunner.scriptInputField() |
413 | ); |
414 | } |
415 | |
416 | L popDownItems() { |
417 | ret ll(jCheckBoxMenuItem_dyn("Horizontal Layout", |
418 | -> horizontalLayout, |
419 | b -> setField(horizontalLayout := b))); |
420 | } |
421 | |
422 | void unvisualize {} // don't zero transient component fields |
423 | |
424 | void resetTimings { functionTimings.reset(); } |
425 | |
426 | // add tool side bar to image surface |
427 | JComponent withTools( |
428 | JComponent component default jscroll_centered_borderless(is), |
429 | ImageSurface is) { |
430 | ret centerAndEastWithMargin(component, |
431 | vstack( |
432 | verticalStrut(5), |
433 | jimageButtonScaledToWidth(16, #1103054, "Save screenshot in gallery", rThread saveScreenshotToGallery), |
434 | /*jPopDownButton_noText( |
435 | jCheckBoxMenuItem_dyn("Live scripting", -> liveScripting, b -> setLiveScripting(b) |
436 | ),*/ |
437 | ) |
438 | ); |
439 | } |
440 | |
441 | class WatchTargetSelector { |
442 | JComboBox<WatchTarget> cb = jComboBox(); |
443 | int screenCount, camCount; |
444 | |
445 | visualize { |
446 | updateList(); |
447 | //print("Selecting watchTarget: " + watchTarget); |
448 | selectItem(cb, watchTarget); |
449 | main onChange(cb, watchTarget -> { |
450 | setField(+watchTarget); |
451 | //print("Chose watchTarget: " + GazelleScreenCam.this.watchTarget); |
452 | }); |
453 | ret cb; |
454 | } |
455 | |
456 | void updateScreenCount() { |
457 | if (screenCount != screenCount()) |
458 | updateList(); |
459 | } |
460 | |
461 | /*void updateCamCount() pcall { |
462 | availableWebCams = listWebCams(); |
463 | updateList(); |
464 | }*/ |
465 | |
466 | void updateList() swing { |
467 | setComboBoxItems(cb, makeWatchTargets()); |
468 | } |
469 | |
470 | L<WatchTarget> makeWatchTargets() { |
471 | ret flattenToList( |
472 | countIteratorAsList_incl(1, screenCount = screenCount(), |
473 | i -> WatchScreen(i)), |
474 | new WatchMouse, |
475 | /*countIteratorAsList_incl(1, l(availableWebCams), |
476 | i -> new WatchWebCam(i))*/ |
477 | ); |
478 | } |
479 | } |
480 | |
481 | class SnPSelector { |
482 | settable new SnPSettings settings; |
483 | |
484 | event change; |
485 | |
486 | visualize { |
487 | var colors = jspinner(settings.colors, 2, 256); |
488 | main onChange(colors, -> { |
489 | settings.colors = intFromSpinner(colors); |
490 | change(); |
491 | }); |
492 | |
493 | var pixelRows = jPowersOfTwoSpinner(512, settings.pixelRows); |
494 | main onChange(pixelRows, -> { |
495 | settings.pixelRows = intFromSpinner(pixelRows); |
496 | change(); |
497 | }); |
498 | |
499 | ret hstack( |
500 | colors, |
501 | jlabel(" colors @ "), |
502 | pixelRows, |
503 | jlabel(" p")); |
504 | } |
505 | } |
506 | |
507 | JComponent wrapCRUD(SimpleCRUD_v2 crud, JComponent vis default crud.visualize()) { |
508 | ret crud == null ?: withTopAndBottomMargin(jRaisedSection(withMargin(vis))); |
509 | } |
510 | |
511 | File galleryDir() { |
512 | ret picturesDir(gazelle22_imagesSubDirName()); |
513 | } |
514 | |
515 | void saveScreenshotToGallery enter { |
516 | var img = imageStream!; |
517 | saveImageWithCounter(galleryDir(), "Screenshot", img); |
518 | } |
519 | |
520 | GalleryImage addToGallery(File imgFile) { |
521 | if (!isImageFile(imgFile)) null; |
522 | var img = uniq(concepts, GalleryImage, path := imgFile); |
523 | //printVars("addToGallery", +imgFile, +img); |
524 | ret img; |
525 | } |
526 | |
527 | class StudyPanel { |
528 | new SnPSelector snpSelector; |
529 | GalleryImage image; |
530 | BufferedImage originalImage; |
531 | //ImageSurface isOriginal = stdImageSurface(); |
532 | ImageSurface isOriginalWithRegions = stdImageSurface(); |
533 | ImageSurface isPosterized = stdImageSurface(); |
534 | SingleComponentPanel scp = singleComponentPanel(); |
535 | SingleComponentPanel analysisPanel = singleComponentPanel(); |
536 | SingleComponentPanel scpExampleCRUD = singleComponentPanel(); |
537 | ConceptsComboBox<GalleryImage> cbImage = new(concepts, GalleryImage); |
538 | Gazelle22_ImageToRegions itr; |
539 | int iSelectedRegion; |
540 | SimpleCRUD_v2<SavedRegion> regionCRUD; |
541 | SimpleCRUD_v2<Example> exampleCRUD; |
542 | |
543 | *() { |
544 | //cbImage.sortTheList = l -> sortConceptsByIDDesc(l); |
545 | cbImage.sortTheList = l -> sortedByComparator(l, (a, b) |
546 | -> cmpAlphanumIC(fileName(a.path), fileName(b.path))); |
547 | |
548 | main onChangeAndNow(cbImage, img -> dm_q(me(), r { |
549 | image = img; |
550 | scp.setComponent(studyImagePanel()); |
551 | })); |
552 | |
553 | isPosterized.removeAllTools(); |
554 | isPosterized.onMousePositionChanged(r_dm_q(me(), l0 regionUpdate)); |
555 | |
556 | imageSurfaceOnLeftMouseDown(isPosterized, pt -> dm_q(me(), r { chooseRegionAt(pt) })); |
557 | |
558 | snpSelector.onChange(r_dm_q(me(), l0 runSnP)); |
559 | } |
560 | |
561 | void chooseRegionAt(Pt p) { |
562 | if (itr == null) ret; |
563 | iSelectedRegion = itr.regions.regionAt(p); |
564 | |
565 | if (iSelectedRegion > 0) { |
566 | var savedRegion = uniq_returnIfNew(concepts, SavedRegion, |
567 | +image, |
568 | snpSettings := snpSelector.settings.cloneMe(), |
569 | regionIndex := iSelectedRegion); |
570 | |
571 | // it's new, add values |
572 | if (savedRegion != null) { |
573 | print("Saved new region!"); |
574 | var bitMatrix = toScanlineBitMatrix(itr.regions.regionBitMatrix(iSelectedRegion)); |
575 | cset(savedRegion, +bitMatrix); |
576 | } |
577 | } |
578 | |
579 | regionUpdate(); |
580 | } |
581 | |
582 | // load new image |
583 | JComponent studyImagePanel() { |
584 | if (image == null) null; |
585 | originalImage = loadImage2(image.path); |
586 | if (originalImage == null) ret jcenteredlabel("Image not found"); |
587 | |
588 | //isOriginal.setImage(originalImage); |
589 | isOriginalWithRegions.setImage(originalImage); |
590 | |
591 | iSelectedRegion = 0; |
592 | itr = new Gazelle22_ImageToRegions(functionTimings, originalImage, snpSelector.settings); |
593 | runSnP(); |
594 | |
595 | regionCRUD = new SimpleCRUD_v2<SavedRegion>(concepts, SavedRegion); |
596 | regionCRUD.addFilter(+image); |
597 | regionCRUD |
598 | .showSearchBar(false) |
599 | .showAddButton(false) |
600 | .showEditButton(false) |
601 | .iconButtons(true); |
602 | |
603 | regionCRUD.itemToMap_inner2 = region -> { |
604 | var examples = conceptsWhere(concepts, Example, item := region); |
605 | |
606 | ret litorderedmap( |
607 | "Pixels" := region.bitMatrix == null ? "-" : n2(region.bitMatrix.pixelCount()), |
608 | //"Shape" := "TODO", |
609 | "Labels" := joinWithComma(map(examples, e -> e.label)), |
610 | |
611 | // TODO: scale using snpsettings |
612 | "Position" := region.bitMatrix == null ? "-" : region.bitMatrix.boundingBoxOfTrueBits()); |
613 | }; |
614 | |
615 | var regionCRUDComponent = regionCRUD.visualize(); |
616 | regionCRUD.onSelectionChanged(l0 updateExampleCRUD); |
617 | |
618 | ret hsplit( |
619 | jtabs( |
620 | //"Image" := jscroll_centered_borderless(isOriginal), |
621 | "Image + regions" := jscroll_centered_borderless(isOriginalWithRegions), |
622 | "Posterized" := jscroll_centered_borderless(isPosterized), |
623 | ), |
624 | northAndCenterWithMargin( |
625 | analysisPanel, |
626 | hsplit( |
627 | jCenteredSection("Saved regions", regionCRUDComponent), |
628 | scpExampleCRUD) |
629 | )); |
630 | } |
631 | |
632 | void runSnP { |
633 | if (itr == null) ret; |
634 | itr.run(); |
635 | regionUpdate(); |
636 | } |
637 | |
638 | void regionUpdate { |
639 | if (itr == null) ret; |
640 | |
641 | var pixels = itr.posterized.getRGBPixels(); |
642 | |
643 | // hovering region marked green |
644 | g22_highlightRegion(pixels, isPosterized, itr, showRegionsAsOutline); |
645 | |
646 | // selected region marked blue |
647 | itr.regions.markRegionInPixelArray(pixels, iSelectedRegion, 0xFFADD8E6); |
648 | |
649 | var highlighted = bufferedImage(pixels, itr.posterized.getWidth(), itr.posterized.getHeight()); |
650 | |
651 | isPosterized.setImage(highlighted); |
652 | isPosterized.performAutoZoom(); // seems to be necessary for some reason |
653 | |
654 | updateAnalysis(); |
655 | } |
656 | |
657 | void updateAnalysis { |
658 | /*new LS lines; |
659 | lines.add(nRegions(itr.regions.regionCount())); |
660 | lines.add("Selected region: " + iSelectedRegion); |
661 | S text = lines_rtrim(lines); |
662 | analysisPanel.setComponent(jLabel(text)));*/ |
663 | } |
664 | |
665 | void updateExampleCRUD { |
666 | var region = regionCRUD.selected(); |
667 | if (region == null) ret with scpExampleCRUD.set(null); |
668 | |
669 | exampleCRUD = new SimpleCRUD_v2<Example>(concepts, Example); |
670 | exampleCRUD.entityName = -> "label for region"; |
671 | exampleCRUD.addFilter(item := region); |
672 | exampleCRUD.showSearchBar(false); |
673 | exampleCRUD.iconButtons(true); |
674 | scpExampleCRUD.set(jCenteredSection("Labels for region", |
675 | exampleCRUD.visualize())); |
676 | } |
677 | |
678 | void importImage { |
679 | new JFileChooser fc; |
680 | if (fc.showOpenDialog(cbImage) == JFileChooser.APPROVE_OPTION) { |
681 | File file = fc.getSelectedFile().getAbsoluteFile(); |
682 | if (!isImageFile(file)) ret with infoMessage("Not an image file"); |
683 | var img = addToGallery(file); |
684 | waitUntil(250, 5.0, -> comboBoxContainsItem(cbImage, img)); |
685 | setSelectedItem(cbImage, img); |
686 | } |
687 | } |
688 | |
689 | visual |
690 | jRaisedSection(northAndCenterWithMargins( |
691 | centerAndEast(withLabel("Study", cbImage), |
692 | hstack( |
693 | jlabel(" in "), |
694 | snpSelector.visualize(), |
695 | horizontalStrut(10), |
696 | jPopDownButton_noText( |
697 | "Import image...", rThreadEnter importImage, |
698 | ), |
699 | )), |
700 | scp)); |
701 | } // end of StudyPanel |
702 | |
703 | void initUIURLs { |
704 | uiURLs.put("Main Tabs", -> |
705 | horizontalLayout ? withMargin(mainTabs) : withSideMargin(mainTabs)); |
706 | |
707 | // add main tabs to UI URLs |
708 | for (S tab : tabNames(mainTabs)) { |
709 | reMutable tab = dropTrailingBracketedCount(tab); |
710 | uiURLs.put(tab, -> { |
711 | int i = indexOfTabNameWithoutTrailingCount(mainTabs, tab); |
712 | if (i < 0) ret jcenteredlabel("Hmm. Tab not found"); |
713 | selectTab(mainTabs, i); |
714 | ret mainTabs; |
715 | }); |
716 | } |
717 | |
718 | uiURLs.put("Screen Cam FPS", -> jFullCenter( |
719 | vstackWithSpacing( |
720 | jCenteredLabel("FPS target (frames per second) for screen cam:"), |
721 | jFullCenter(dm_spinner fpsTarget(1, 60)), |
722 | jCenteredLabel("(You can lower this value if you have a slower computer)")))); |
723 | |
724 | uiURLs.put("Timings", -> { |
725 | JTextArea taTimings = jTextArea_noUndo(); |
726 | awtEveryAndNow(taTimings, .5, r { |
727 | setText(taTimings, renderFunctionTimings()) |
728 | }); |
729 | ret withRightAlignedButtons(taTimings, |
730 | Reset := r resetTimings); |
731 | }); |
732 | |
733 | uiURLs.put("Test Screen" := -> testScreenPanel()); |
734 | |
735 | //uiURLs.put("Operators" := -> operatorsPanel()); |
736 | |
737 | uiURLs.put("Left Arrow Script" := -> leftArrowScriptPanel()); |
738 | |
739 | uiURLs.put("Settings" := -> settingsPanel()); |
740 | |
741 | uiURLs.put("System Info" := -> systemInfoPanel()); |
742 | |
743 | uiURLs.put("Web Cam" := -> webCamPanel()); |
744 | |
745 | uiURLs.put("Labels" := -> wrapCRUD(labelCRUD)); |
746 | |
747 | uiURLs.put(WithToolTip("Screen Cam + Linear Script", "Run a simple (linear) Gazelle V script") |
748 | := -> withBottomMargin(scriptRunner.scriptAndResultPanel())); |
749 | |
750 | uiURLs.put(WithToolTip("Left Arrow Script", "Run a \"left-arrow script\"") |
751 | := -> withBottomMargin(leftArrowScriptPanel())); |
752 | // add ui urls above this line |
753 | } |
754 | |
755 | JComponent settingsPanel() { |
756 | var cbMinimizeToTray = jCheckBox(isTrue(getOpt(dm_stem(), "minimizeToTray"))); |
757 | onUpdate(cbMinimizeToTray, -> dm_callStem(me(), "setMinimizeToTray", isChecked(cbMinimizeToTray))); |
758 | ret jscroll(makeForm3( |
759 | "Minimize to tray", toolTip("Remove Gazelle from task bar when minimized (click Gazelle icon in system tray to reactivate)", cbMinimizeToTray), |
760 | "Access web cams", dm_checkBox webCamAccessEnabled(), |
761 | )); |
762 | } |
763 | |
764 | JComponent systemInfoPanel() { |
765 | var cbMinimizeToTray = jCheckBox(isTrue(getOpt(dm_stem(), "minimizeToTray"))); |
766 | onUpdate(cbMinimizeToTray, -> dm_callStem(me(), "setMinimizeToTray", isChecked(cbMinimizeToTray))); |
767 | ret jscrollVertical(makeForm3( |
768 | "Java Version", jlabel(javaVersion()), |
769 | "Gazelle Jar", jlabel(renderFileInfo(getBytecodePathForClass(this))), |
770 | "Compilation Date", jlabel(or2(loadTextFileResource(classLoader(this), "compilation-date.txt"), "unknown")), |
771 | "Gazelle Count" := toolTip("How many Gazelles are running in the world", |
772 | jLiveValueLabel((LiveValue) dm_callOSOpt lvComputerCount())), |
773 | )); |
774 | } |
775 | |
776 | JComponent operatorsPanel() { |
777 | ret jcenteredlabel("TODO"); |
778 | } |
779 | |
780 | JLeftArrowScriptIDE leftArrowScriptIDE() { |
781 | new JLeftArrowScriptIDE ide; |
782 | ide.modifyParser = parser -> parser.allowTheWorld(mc(), utils.class); |
783 | ret ide; |
784 | } |
785 | |
786 | JComponent leftArrowScriptPanel() { |
787 | var ide = leftArrowScriptIDE(); |
788 | ide.lvScript(dm_fieldLiveValue newScript()); |
789 | ret ide.visualize(); |
790 | } |
791 | |
792 | JComponent javaPanel() enter { |
793 | var scpResult = singleComponentPanel(); |
794 | |
795 | new JMiniJavaIDE ide; |
796 | ide.extraClassMembers = script -> { |
797 | LS tok = javaTok(script); |
798 | |
799 | if (contains(tok, "draw")) ret [[ |
800 | import java.awt.*; |
801 | import java.awt.image.*; |
802 | |
803 | interface SimpleRenderable { |
804 | void renderOn(Graphics2D g); |
805 | } |
806 | |
807 | static BufferedImage draw(int w, int h, SimpleRenderable r) { |
808 | BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); |
809 | Graphics2D g = img.createGraphics(); |
810 | g.setColor(Color.white); |
811 | g.fillRect(0, 0, w, h); |
812 | g.setColor(Color.black); |
813 | r.renderOn(g); |
814 | return img; |
815 | } |
816 | ]]; |
817 | |
818 | ret ""; |
819 | }; |
820 | |
821 | ide.callCompiledObject = o -> { |
822 | O result = ide.callCompiledObject_base(o); |
823 | if (result cast BufferedImage) |
824 | scpResult.set(jscroll_centered_borderless(stdImageSurface(result))); |
825 | ret result; |
826 | }; |
827 | |
828 | ide.lvScript(dm_fieldLiveValue javaCode()); |
829 | ret jhsplit( |
830 | jCenteredSection("Image will show here", scpResult), |
831 | ide.visualize()); |
832 | } |
833 | |
834 | JComponent webCamPanel() { |
835 | if (cbWebCam == null) { |
836 | var cams = listWebCams(); |
837 | cbWebCam = jTypedComboBox(cams, |
838 | findWebCamByName(cams, webCamName)); |
839 | } |
840 | |
841 | if (scpWebCamImage == null) scpWebCamImage = singleComponentPanel(); |
842 | |
843 | ret northAndCenterWithMargins( |
844 | centerAndEastWithMargin( |
845 | withLabel("Cam", cbWebCam), |
846 | jline( |
847 | jbutton("Start", rThreadEnter startWebCam), |
848 | jbutton("Stop", rThreadEnter stopWebCam) |
849 | )), |
850 | scpWebCamImage); |
851 | } |
852 | |
853 | void stopWebCam { |
854 | if (webCamPanel != null) { |
855 | webCamPanel.stop(); |
856 | webCamPanel = null; |
857 | scpWebCamImage?.clear(); |
858 | backgroundProcessesUI.remove("Web Cam"); |
859 | } |
860 | } |
861 | |
862 | void startWebCam { |
863 | temp tempInfoBox("Starting Web Cam"); |
864 | stopWebCam(); |
865 | var cam = getSelectedItem_typed(cbWebCam); |
866 | setField(webCamName := cam?.getName()); |
867 | if (cam != null) { |
868 | scpWebCamImage.set(webCamPanel = new WebcamPanel(cam, true)); |
869 | backgroundProcessesUI.add("Web Cam"); |
870 | } |
871 | } |
872 | |
873 | void forgetMissingImages { |
874 | int n = l(deleteConcepts(GalleryImage, img -> !img.imageExists())); |
875 | infoBox(n == 0 ? "Nothing to forget" : "Forgot " + nImages(n)); |
876 | } |
877 | } // end of module |
878 | |
879 | concept Label > ConceptWithGlobalID { |
880 | S name; |
881 | //new RefL examples; |
882 | |
883 | toString { ret /*"Label " +*/ name; } |
884 | } |
885 | |
886 | concept GalleryImage { |
887 | File path; |
888 | |
889 | toString { ret /*"[" + id + "] " +*/ fileName(path); } |
890 | |
891 | bool imageExists() { ret fileExists(path); } |
892 | } |
893 | |
894 | concept SavedRegion { |
895 | new Ref image; // e.g. a GalleryImage |
896 | SnPSettings snpSettings; |
897 | int regionIndex; // region number relative to snpSettings |
898 | //Rect bounds; // get it from bitMatrix instead |
899 | ScanlineBitMatrix bitMatrix; |
900 | //new RefL<Label> labels; |
901 | } |
902 | |
903 | concept Example { |
904 | new Ref<Label> label; |
905 | new Ref item; // e.g. a SavedRegion |
906 | double confidence = 1; |
907 | } |
908 | |
909 | concept IfThenTheory { |
910 | new Ref if_; |
911 | new Ref then; |
912 | } |
913 | |
914 | sclass WatchTarget {} |
915 | |
916 | // screenNr: 1 = screen 1 etc |
917 | srecord WatchScreen(int screenNr) > WatchTarget { |
918 | toString { ret "Screen " + screenNr; } |
919 | } |
920 | |
921 | srecord WatchMouse(int width, int height) > WatchTarget { |
922 | WatchMouse() { |
923 | height = 256; |
924 | width = iround(height*16.0/9); |
925 | } |
926 | |
927 | toString { ret "Mouse"; } |
928 | } |
929 | |
930 | srecord WatchWebCam(int camNr) > WatchTarget { |
931 | toString { ret "Webcam " + camNr; } |
932 | } |
933 | |
934 | !include once #1034034 // Gazelle 22 Function Include for Scripts |
Began life as a copy of #1033862
download show line numbers debug dex old transpilations
Travelled to 2 computer(s): bhatertpkbcr, mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1034151 |
Snippet name: | Gazelle Screen Cam / Gazelle 22 Module [backup] |
Eternal ID of this version: | #1034151/1 |
Text MD5: | 1babdf853e6fc6e4bc50dd29748e8cd9 |
Author: | stefan |
Category: | javax / gazelle v |
Type: | JavaX source code (Dynamic Module) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-01-24 17:16:48 |
Source code size: | 29721 bytes / 934 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 111 / 130 |
Referenced in: | [show references] |