srecord noeq BWImage_FastRegions(BWImage image) is Runnable { int w, h, size, runner; new IntBuffer stack; // locations as y*w+x int[] regionMatrix; // for each pixel: region index (starting at 1) // initialize these to use them IntBuffer regionFirstPixel; // for each region: index of first pixel found IntBuffer regionSize; // for each region: number of pixels int regionCounter; bool verbose; double regionStep = .1; // for rendering in regionsImage int x(int pos) { ret pos % w; } int y(int pos) { ret pos / w; } int pos(int x, int y) { ret y*w+x; } int getColor(int pos) { ret image.getInt(x(pos), y(pos)); } *(BufferedImage img) { this(toBWImage(img)); } run { w = image.getWidth(); h = image.getHeight(); size = w*h; regionMatrix = new int[size]; regionFirstPixel?.add(0); // unused while (runner < size) { if (regionMatrix[runner] == 0) { // make a new region, get color int region = ++regionCounter; regionFirstPixel?.add(runner); stack.add(runner); int color = getColor(runner); int rsize = 0; // flood-fill region while (nempty(stack)) { int pos = stack.popLast(); if (regionMatrix[pos] != 0) continue; // touching myself (or someone else) if (getColor(pos) != color) continue; // wrong color // new pixel found, mark as ours regionMatrix[pos] = region; ++rsize; // explore neighborhood int x = x(pos), y = y(pos); if (x > 0) stack.add(pos-1); if (x < w-1) stack.add(pos+1); if (y > 0) stack.add(pos-w); if (y < h-1) stack.add(pos+w); } regionSize?.add(rsize); } ++runner; } } IBWImage regionsImage() { ret iBWImageFromFunction((x, y) -> { var region = regionMatrix[pos(x, y)]; ret ((region-1)*regionStep) % (1.0+regionStep-0.0001); }, w, h); } int regionCount() { ret regionCounter; } // returns points in no particular order record noeq RegionIterator(int region) { new IntBuffer stack; // locations as y*w+x BitSet seen = new(size); int pos; { int pos = regionFirstPixel.get(region); seen.set(pos); stack.add(pos); } // flood-fill region bool next() { if (empty(stack)) false; pos = stack.popLast(); // explore neighborhood int x = x(), y = y(); if (x > 0) tryPosition(pos-1); if (x < w-1) tryPosition(pos+1); if (y > 0) tryPosition(pos-w); if (y < h-1) tryPosition(pos+w); true; } private void tryPosition(int p) { if (!seen.get(p) && regionMatrix[p] == region) { seen.set(p); stack.add(p); } } int pos() { ret pos; } int x() { ret BWImage_FastRegions.this.x(pos); } int y() { ret BWImage_FastRegions.this.y(pos); } } int regionSize(int iRegion) { ret regionSize.get(iRegion); } Pt samplePixel(int iRegion) { ret regionFirstPixel.get(iRegion); } }