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