sclass G22CriticalPixelSearch is Steppable { // input settable IG22MasksHolder masksHolder; settable bool keepAllEntries; // internal int w, h; ItIt pixelIterator; // output new Best bestPixel; Entry bestEntry; Pt lastPixelLookedAt; Entry lastEntry; Entry[] pixelEntries; float[] pixelScores; *() {} *(IG22MasksHolder *masksHolder) {} class Entry { Pt pixel; // child mask holders for pixel dark or pixel bright //IG22MasksHolder darkChild, brightChild; L darkMasks, brightMasks; gettable IG22MasksHolder darkHolder; gettable IG22MasksHolder brightHolder; toString { ret commaCombine( "Split at " + pixel, "dark=" + darkHolder.renderCountAndCertainty(), "bright=" + brightHolder.renderCountAndCertainty()); } } public bool step() { if (w == 0) { meta-for w also as h { w = masksHolder.maskSize().w(); } pixelEntries = new Entry[w*h]; pixelScores = new float[w*h]; arrayfill(pixelScores, -1); pixelIterator = new WeightlessShuffledIterator(pixelsInImage(w, h)); } if (!pixelIterator.hasNext()) false; lastPixelLookedAt = pixelIterator.next(); testPixel(lastPixelLookedAt); true; } int idx(Pt p) { ret p.y*w+p.x; } void testPixel(Pt p) { double score; Entry e = null; // Find out if pixel is the same in all masks float ghostBrightness = masksHolder.ghost().getFloatPixel(p); if (ghostBrightness == 0 || ghostBrightness == 1) // Pixel is determined, not usable for splitting score = -2; else { // Pixel is not determined - let's make the 2 subsets e = new Entry; e.pixel = p; lastEntry = e; if (keepAllEntries) pixelEntries[idx(p)] = e; /*Pair>*/var pair = filterAntiFilter(masksHolder.masks(), mask -> !mask.image().getBoolPixel(p)); e.darkMasks = (L) pair.a; e.brightMasks = (L) pair.b; e.darkHolder = new G22HashedMasks(e.darkMasks); e.brightHolder = new G22HashedMasks(e.brightMasks); score = avg(e.darkHolder().certainty(), e.brightHolder().certainty()); } pixelScores[idx(p)] = (float) score; if (score > 0 && bestPixel.put(p, score)) bestEntry = e; } double bestScore() { ret bestPixel.score(); } BWImage scoreImage() { double min = masksHolder.certainty(); double max = bestPixel.score(); ret bwImageFromFunction(w, h, (x, y) -> transformToZeroToOne(pixelScores[y*w+x], min, max)); } }