Uses 1482K of libraries. Click here for Pure Java version (11161L/59K).
1 | !7 |
2 | |
3 | !include early #1033506 // Compact Module Include Gazelle V |
4 | |
5 | module GazelleV > DynPrintLogAndEnabled { |
6 | /*switchable int x1; |
7 | switchable int y1; |
8 | switchable int w = screenWidth(); |
9 | switchable int h = screenHeight();*/ |
10 | switchable double maxFPS = 10; |
11 | switchable int previewWidth = 200; |
12 | switchable double statsEvery = 5; |
13 | switchable volatile bool brokenMethod; |
14 | int lookAtScreen; |
15 | S previewMode; |
16 | int gridRows = 20; |
17 | bool scaleDownPreviewImage; |
18 | int previewPosterization = 256; |
19 | |
20 | PersistableThrowable lastProcessingError; |
21 | transient WithTimestamp<OKOrError<BufferedImage>> lastScreen; |
22 | transient new Average screenShotTime; |
23 | transient new Average iiTime; |
24 | transient Timestamp loopStarted; |
25 | transient Sleeping screenShotCountdown; |
26 | transient long screenShotCountdownTotalSleepTime; |
27 | //transient ImageSurface imageSurface; |
28 | transient IIImageSurface imageSurface; |
29 | transient Throwable currentProcessingError; |
30 | transient JButton popDownButton; |
31 | transient JComponent topControls; |
32 | transient long screenShotsTaken; |
33 | transient double fps; |
34 | transient Compression ongoingCompression, completedCompression; |
35 | transient S longTermCompressionProbability; |
36 | transient S longTermCompressionScore; |
37 | transient JComponent topControls2; |
38 | transient JTabbedPane tabs; |
39 | transient ThreadPool threadPool; |
40 | |
41 | // ping source for everything we do in the background, including |
42 | // analyzing current screen. |
43 | // normally limited to 50% of one core. |
44 | transient PingSource backgroundPingSource; |
45 | |
46 | transient OnOffOscillator onOffOscillator; |
47 | |
48 | Rect area() { |
49 | ret screenBounds(min(lookAtScreen, screenCount()-1)); |
50 | } |
51 | |
52 | class Compression { |
53 | settable BufferedImage image; |
54 | settable BWIntegralImage ii; |
55 | CompressionSearch_AnyType search; |
56 | int baseLineLength; // length of trivial compression |
57 | GridCodec1 codec; |
58 | double compressionPercentage; |
59 | |
60 | S score() { |
61 | var submission = search?.bestSubmission(); |
62 | Int compressedSize = or(submission.compressedSize(), baseLineLength); |
63 | compressionPercentage = doubleRatio(compressedSize, baseLineLength)*100; |
64 | ret formatDouble(compressionPercentage, 3) + "%"; |
65 | } |
66 | |
67 | IProbabilisticScheduler scheduler() { |
68 | ret search?.scheduler(); |
69 | } |
70 | |
71 | S probability() { |
72 | var scheduler = scheduler(); |
73 | if (scheduler == null) ret "-"; |
74 | var prob = scheduler.lastExecutedProbability(); |
75 | ret formatDouble(prob, 3); |
76 | } |
77 | |
78 | event compressionDone; |
79 | |
80 | run { |
81 | int lHex = pixelCount(image)*8; |
82 | baseLineLength = calculateLengthOfFunctionCall imageFromHex(lHex+2); |
83 | print(+baseLineLength); |
84 | |
85 | codec = new GridCodec1(image); |
86 | codec.rows = gridRows; |
87 | search = codec.forward(); |
88 | //showImage(codec.renderCellsLinearly()); |
89 | |
90 | thread "Gazelle Visual" { |
91 | repeat 60 { |
92 | stepForNSeconds(1, search); |
93 | //print(stepCount := search.scheduler().stepCount()); |
94 | setFields( |
95 | longTermCompressionProbability := probability(), |
96 | longTermCompressionScore := score()); |
97 | } |
98 | |
99 | try { |
100 | compressionDone(); |
101 | |
102 | L<IJavaExpr> cellCompressions = codec.strat.codeForElements(); |
103 | //showText("Compression", lines(map shorten_str(cellCompressions))); |
104 | |
105 | /*S code = str(codec.winnerCode()); |
106 | S name = "Screenshot " + ymdMinusHMS(); |
107 | File f = makeFileNameUnique_beforeExtension( |
108 | javaxDataDir("Compressed Screenshots/" + name + ".javax")); |
109 | saveTextFile(f, code); |
110 | printFileInfo(f); |
111 | saveGZTextFile(f = replaceExtension(f, ".jgz"), code); |
112 | printFileInfo(f);*/ |
113 | |
114 | /* |
115 | print("Evaling " + nChars(code)); |
116 | //LL<Int> lol = cast dm_javaEval(code); |
117 | BufferedImage restored = cast dm_javaEval(code); |
118 | assertImagesIdentical(restored, image); |
119 | print("Image restored!"); |
120 | |
121 | dm_showImage("Restored from " + nChars(code), restored); |
122 | */ |
123 | } catch e { printStackTrace(e); } |
124 | } |
125 | } |
126 | |
127 | void drawOverlay(ImageSurface is, Graphics2D g) pcall { |
128 | codec.drawOverlay(is, g); |
129 | } |
130 | |
131 | int gridCols() { ret widthForHeight(ii.getWidth(), ii.getHeight(), gridRows); } |
132 | } |
133 | |
134 | start { |
135 | if (threadPool == null) threadPool = maxThreadPool(); |
136 | |
137 | backgroundPingSource = new PingSource(threadPool, "Background"); |
138 | |
139 | onOffOscillator = new OnOffOscillator(backgroundPingSource); |
140 | ownResource(onOffOscillator); |
141 | onOffOscillator.start(); |
142 | |
143 | componentFieldsToKeep = litset("tabs"); |
144 | |
145 | dm_registerAs_direct gazelleV(); |
146 | dm_watchField enabled(r { if (enabled) dm_reload(); }); |
147 | |
148 | // TODO: react to event sent by OS |
149 | dm_doEvery(1.0, r { |
150 | if (dm_moduleIsPoppedOut()) adjustUIForPoppedOutWindow(); |
151 | }); |
152 | |
153 | print("Screen bounds: " + allScreenBounds()); |
154 | |
155 | // We are making some UI elements even if the module is hidden |
156 | // because that's easier. |
157 | |
158 | tabs = jtabs( |
159 | "Log", super.visualize(), |
160 | "Local Data", jcenteredlabel("TODO")); |
161 | |
162 | dm_doEvery(min(statsEvery, 10.0), statsEvery, r { if (enabled) printStats(); }); |
163 | |
164 | loopStarted = tsNow(); |
165 | shootAndAnalyze(); |
166 | } |
167 | |
168 | void shootAndAnalyze { |
169 | backgroundPingSource.do(r shootAndAnalyze_impl); |
170 | } |
171 | |
172 | void shootAndAnalyze_impl enter { |
173 | if (!enabled) ret; |
174 | |
175 | var targetTime = tsNow().plus(1.0/maxFPS); |
176 | long time = nanoTime(); |
177 | |
178 | try { |
179 | lastScreen = withTimestamp(okOrError(-> screenshot(area()))); |
180 | ++screenShotsTaken; |
181 | setField(fps := doubleRatio(screenShotsTaken, elapsedSeconds(loopStarted))); |
182 | screenShotTime.add(nanosToSeconds(nanoTime()-time)); |
183 | time = nanoTime(); |
184 | ping(); |
185 | if (lastScreen->isOK()) { |
186 | var img = lastScreen!!; |
187 | var ii = BWIntegralImage(img); |
188 | //var ii = BWIntegralImage_luminosity(lastScreen!!); |
189 | iiTime.add(nanosToSeconds(nanoTime()-time)); |
190 | |
191 | if (eq(previewMode, "showLatestGrabbed")) |
192 | imageSurface?.setImage(ii, img); |
193 | |
194 | if (ongoingCompression == null) |
195 | startCompression(ii); |
196 | } |
197 | |
198 | cpuTotal(); |
199 | currentProcessingError = null; |
200 | } catch print e { |
201 | setField(lastProcessingError := currentProcessingError = e); |
202 | //sleepSeconds(60); // errors in processing are no good |
203 | } |
204 | |
205 | screenShotCountdown = sleeper().doLater(targetTime, r shootAndAnalyze); |
206 | screenShotCountdownTotalSleepTime += screenShotCountdown.remainingMS(); |
207 | } |
208 | |
209 | void startCompression(BWIntegralImage ii) { |
210 | if (ongoingCompression == null) { |
211 | ping(); |
212 | ongoingCompression = new Compression() |
213 | .image(lastScreen!!) |
214 | .ii(ii); |
215 | ongoingCompression.onCompressionDone(-> { |
216 | completedCompression = ongoingCompression; |
217 | ongoingCompression = null; |
218 | |
219 | IBWIntegralImage ii2 = completedCompression.ii; |
220 | if (scaleDownPreviewImage) { |
221 | ii2 = ScaledIBWIntegralImage(completedCompression.gridCols(), gridRows, ii2); |
222 | ii2 = TruncatedIBWIntegralImage(ii2); |
223 | |
224 | if (previewPosterization < 256) |
225 | imageSurface?.setImage(posterizeIBWImage(previewPosterization, ii2)); |
226 | else |
227 | imageSurface?.setImage(ii2); |
228 | |
229 | imageSurface.setOverlay(null); |
230 | } else { |
231 | imageSurface?.setImage(completedCompression.ii, completedCompression.image); |
232 | imageSurface.setOverlay(g -> completedCompression.drawOverlay(imageSurface, g)); |
233 | } |
234 | }); |
235 | ongoingCompression.run(); |
236 | } |
237 | } |
238 | |
239 | void printStats() { print(stats()); } |
240 | |
241 | S stats() { |
242 | var screen = lastScreen?!; |
243 | ret formatColonProperties_noNulls( |
244 | "Monitored area", area(), |
245 | "Error", screen?.getError(), |
246 | "Last screenshot taken", lastScreen?.timeStamp(), |
247 | "Average time to take a screenshot", screenShotTime, |
248 | "Average time to make integral image", iiTime, |
249 | "Capacity left in first core", percentRatioStrOneDigit(freeInCore()), |
250 | "Total CPU used", cpuTotal(), |
251 | ); |
252 | } |
253 | |
254 | S cpuTotal() { |
255 | ret setFieldAndReturn(cpuTotal := percentRatioStrOneDigit((1-freeInCore())/numberOfCores())); |
256 | } |
257 | |
258 | double freeInCore() { |
259 | ret ratio(toSeconds(screenShotCountdownTotalSleepTime), |
260 | elapsedSeconds(loopStarted)); |
261 | } |
262 | |
263 | replace jSection with jCenteredSection. |
264 | {} |
265 | visualize { |
266 | imageSurface = new IIImageSurface; |
267 | setDoubleBuffered(imageSurface, true); |
268 | imageSurface.noAlpha = true; |
269 | |
270 | var screenSelectors = jRadioButtons( |
271 | countIteratorAsList(screenCount(), |
272 | i -> "Screen " + (i+1))); |
273 | selectRadioButton(screenSelectors, lookAtScreen); |
274 | onRadioButtonChange(screenSelectors, i -> { setField(lookAtScreen := i); }); |
275 | |
276 | ret hsplit(northAndCenterWithMargin( |
277 | topControls = hgrid( |
278 | jSection("FPS", dm_calculatedCenteredLabel(() -> iround(fps))), |
279 | jSection("Compression", dm_centeredFieldLabel longTermCompressionScore()), |
280 | jSection("Probability", dm_centeredFieldLabel longTermCompressionProbability()), |
281 | jSection("CPU (" + numberOfCores() + ")", dm_centeredFieldLabel cpuTotal()) |
282 | ), |
283 | tabs), |
284 | northAndCenterWithMargin( |
285 | topControls2 = /*jflowCenter*/ jPanel(new WrapLayout, listPlus(wideningListCast Component(buttonsInGroup(screenSelectors)), |
286 | dm_spinnerWithLabel gridRows("Rows", 1, 500), |
287 | dm_checkBox("Scale down", "scaleDownPreviewImage"), |
288 | dm_spinnerWithLabel previewPosterization("Posterize", 2, 256) |
289 | )), |
290 | jscroll_center(imageSurface))); |
291 | } |
292 | |
293 | BufferedImage lastScreenImage() { ret getVar(getVar(lastScreen)); } |
294 | |
295 | void adjustUIForPoppedOutWindow swing { |
296 | if (popDownButton == null) { |
297 | Window window = cast getWindow(dm_vis()); |
298 | |
299 | var popBackInButton = findButton(window, "Pop Back In"); |
300 | var parent = getParent(popBackInButton); |
301 | print(+parent); |
302 | |
303 | removeFromParent(parent); // hide controls |
304 | |
305 | popDownButton = jPopDownButton_noText( |
306 | "Pop Back In", r { dm_popInModule(me()) }, |
307 | jCheckBoxMenuItem("Always On Top", isAlwaysOnTop(window), |
308 | rEnter dm_toggleAlwaysOnTop), |
309 | ); |
310 | addControl(popDownButton); |
311 | } |
312 | } |
313 | |
314 | public bool useErrorHandling() { false; } |
315 | } |
Began life as a copy of #1033526
download show line numbers debug dex old transpilations
Travelled to 2 computer(s): bhatertpkbcr, mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1033576 |
Snippet name: | Gazelle V, November Edition v1 [backup] |
Eternal ID of this version: | #1033576/1 |
Text MD5: | 04269bda3866405fefd276d745379d46 |
Transpilation MD5: | ca94348891839b8973794a9c26216301 |
Author: | stefan |
Category: | javax / screen recognition |
Type: | JavaX source code (Dynamic Module) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2021-11-04 01:26:49 |
Source code size: | 10570 bytes / 315 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 132 / 199 |
Referenced in: | [show references] |