!7 replace Submission with BackgroundPlus. cm CircleFinder > DynImageSurface { int nCircles = 1; bool keepGoing; new Best best; transient RGBImage original; transient Thread recognizerThread; transient ImageSurface isCircles; transient Submission rendered; S switchableFields() { ret "nCircles keepGoing"; } visualize { JSpinner spinner = liveValueSpinner(dm_fieldLiveValue('nCircles), 1, 50); main.onChange(spinner, r startRecognition ); ret jvsplit( vgrid(super.visualize(), jscroll_centered(isCircles = imageSurface())), northAndCenter( withSideMargin(rightAlignedLine( dm_fieldCheckBox('keepGoing), jbutton("Start", r startRecognition), jbutton("Stop", r { cancelThread(recognizerThread) }), withLabel("Circles:", spinner))), dm_printLogComponent())); } start { if (!hasImage()) setImage(whiteImage(50, 50)); // so we can paste... onNewImage = r startRecognition; doEvery(1.0, r renderCircles); } double scoreImage(RGBImage image) { ret rgbImageSimilarityPercent(image, original); } void startRecognition enter { cancelThread(recognizerThread); recognizerThread = startThread(r { original = RGBImage(getImage()); final int w = original.getWidth(), h = original.getHeight(); print("Starting recognition on " + w + "*" + h + " image"); best = newBest_sfu(); //new AIStrategy_RandomWithVariation strategy; new AIStrategy_Racer_RandomWithVary strategy; strategy.verbose = true; strategy.best = best; strategy.submit = func(Submission s) -> double { scoreImage(rgbRenderRenderable(w, h, s)) }; strategy.random = func -> Submission { randomBackgroundPlusCircles(w, h, nCircles) }; strategy.vary = func(Submission s) -> Submission { varyBackgroundPlusCircles(s, w, h) }; runStrategy(best, strategy, keepGoing); print("Done"); persistMe(); }); } void renderCircles enter { if (original == null || best! == rendered) ret; rendered = best!; isCircles.setImage(renderRenderable(original.w(), original.h(), rendered)); } }