Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

375
LINES

< > BotCompany Repo | #1036012 // DifferentialRegionsMaker [dev.]

JavaX fragment (include) [tags: use-pretranspiled]

Transpiled version (14789L) is out of date.

1  
// Flood-filling everywhere with neighboring pixel similarity
2  
// threshold instead of posterizing
3  
4  
// TODO: handle withDiagonals properly (do diagonal similarity testing)
5  
6  
abstract srecord noeq DifferentialRegionsMaker<Img extends WidthAndHeight>(Img image) is Runnable, IImageRegions<Img> {
7  
  int w, h, runner;
8  
  gettable int size; // =w*h
9  
  
10  
  new IntBuffer stack; // locations as y*w+x
11  
  gettable int[] regionMatrix; // for each pixel: region index (starting at 1)
12  
  new IntBuffer regionPixels; // collect all pixels for regions
13  
  
14  
  settable bool withDiagonals; // also walk diagonally?
15  
  
16  
  // How different pixels may be from their neighbor in "the
17  
  // "same region. From 0 to 1.
18  
  settable double tolerance = 0.05;
19  
  
20  
  // initialize these to use them
21  
  
22  
  new IntBuffer regionFirstPixel; // for each region: index of first pixel found in regionPixels
23  
  new IntBuffer regionSize; // for each region: number of pixels
24  
  new IntBuffer regionBounds; // for each region: bounds (x1, y1, x2, y2)
25  
  
26  
  // index = dual log of region size, value = region index
27  
  new L<IntBuffer> regionsBySize;
28  
  
29  
  int regionCounter;
30  
  bool verbose;
31  
  
32  
  double regionStep = .1; // for rendering in regionsImage
33  
  
34  
  int x(int pos) { ret pos % w; }
35  
  int y(int pos) { ret pos / w; }
36  
  int pos(int x, int y) { ret y*w+x; }
37  
  Pt pt(int pos) { ret Pt(x(pos), y(pos)); }
38  
  bool validPos(int x, int y) { ret x >= 0 && y >= 0 && x < w && y < h; }
39  
  
40  
  abstract int getRGB(int pos);
41  
  
42  
  bool similarPixels(int pos1, int pos2) {
43  
    int col1 = getRGB(pos1);
44  
    int col2 = getRGB(pos2);
45  
    ret rgbDiff(col1, col2) <= tolerance;
46  
  }
47  
  
48  
  run {
49  
    if (regionMatrix != null) ret;
50  
    
51  
    if (withDiagonals) fail("Can't handle diagonals yet");
52  
    
53  
    w = image.getWidth(); h = image.getHeight();
54  
    size = w*h;
55  
    regionMatrix = new int[size];
56  
    
57  
    // 0 entries are unused
58  
    regionFirstPixel?.add(0); 
59  
    regionSize?.add(0);
60  
    
61  
    regionPixels?.setSize(size);
62  
    
63  
    while (runner < size) {
64  
      if (regionMatrix[runner] != 0)
65  
        continue with ++runner;
66  
      
67  
      makeRegion_fast();
68  
    }
69  
  }
70  
  
71  
  void makeRegion_fast {
72  
    // make a new region
73  
    int region = ++regionCounter;
74  
    regionFirstPixel?.add(regionPixels != null ? l(regionPixels) : runner);
75  
    stack.add(runner);
76  
    int rsize = 0, x1 = w, y1 = h, x2 = 0, y2 = 0;
77  
    ifdef FastRegions_debug
78  
      printVars FastRegions(+region, +runner);
79  
    endifdef
80  
81  
    // flood-fill region
82  
    while (nempty(stack)) {
83  
      int pos = stack.popLast();
84  
      if (regionMatrix[pos] != 0) continue; // check again if set in the meantime (color has been checked before)
85  
86  
      // new pixel found!
87  
      int x = x(pos), y = y(pos);
88  
      
89  
      // march to the left
90  
      int lineStart = pos-x;
91  
      int xLeft = x;
92  
      while (xLeft > 0 && addable(lineStart+xLeft-1, -1))
93  
        --xLeft;
94  
        
95  
      // march to the right
96  
      int xRight = x+1;
97  
      while (xRight < w && addable(lineStart+xRight, 1))
98  
        ++xRight;
99  
      
100  
      // Now we know [xLeft; xRight) are our pixels
101  
      // mark them in matrix
102  
      for (x = xLeft; x < xRight; x++) {
103  
        regionMatrix[lineStart+x] = region;
104  
        regionPixels?.add(lineStart+x);
105  
      }
106  
        
107  
      // increase region size
108  
      rsize += xRight-xLeft;
109  
      
110  
      ifdef FastRegions_debug
111  
        printVars FastRegions(+region, +rsize, +y, +xLeft, +xRight);
112  
      endifdef
113  
      
114  
      // update bounds
115  
      if (xLeft < x1) x1 = xLeft;
116  
      if (xRight-1 > x2) x2 = xRight-1;
117  
      if (y < y1) y1 = y;
118  
      if (y > y2) y2 = y;
119  
      
120  
      // explore above & below
121  
      int xLeft2 = withDiagonals ? max(0, xLeft-1) : xLeft;
122  
      int xRight2 = withDiagonals ? min(w, xRight+1) : xRight;
123  
      if (y > 0)
124  
        addStreaks(lineStart-w, xLeft2, xRight2, -w);
125  
      if (y < h-1)
126  
        addStreaks(lineStart+w, xLeft2, xRight2, w);
127  
    }
128  
    
129  
    regionSize?.add(rsize);
130  
    regionBounds?.addAll(x1, y1, x2+1, y2+1);
131  
    if (regionsBySize != null) {
132  
      int iBucket = dualLog(rsize);
133  
      var buffer = listGetOrCreate(regionsBySize, iBucket, -> new IntBuffer);
134  
      buffer.add(region);
135  
    }
136  
  }
137  
  
138  
  bool addable(int pos, int lastDirection) {
139  
    if (regionMatrix[pos] != 0) false; // touching myself (or someone else)
140  
    // check color similarity
141  
    if (!similarPixels(pos, pos-lastDirection)) false;
142  
    true;
143  
  }
144  
  
145  
  bool addIfAddable(int pos, int lastDirection) {
146  
    if (!addable(pos, lastDirection)) false;
147  
    stack.add(pos);
148  
    true;
149  
  }
150  
  
151  
  void addStreaks(int lineStart, int xLeft, int xRight, int lastDirection) {
152  
    int x = xLeft;
153  
    while (x < xRight) {
154  
      if (addIfAddable(lineStart+x, lastDirection))
155  
        while (x+1 < xRight
156  
          && addable(lineStart+x+1, 1)
157  
          && addable(lineStart+x+1, lastDirection))
158  
          ++x;
159  
      ++x;
160  
    }
161  
  }
162  
163  
  IBWImage regionsImage() {
164  
    ret iBWImageFromFunction((x, y) -> {
165  
      var region = regionMatrix[pos(x, y)];
166  
      ret ((region-1)*regionStep) % (1.0+regionStep-0.0001);
167  
    }, w, h);
168  
  }
169  
  
170  
  public int regionCount aka nRegions() { ret regionCounter; }
171  
  
172  
  abstract class RegionIterator {
173  
    int pos;
174  
    
175  
    abstract bool next();
176  
    
177  
    int pos() { ret pos; }
178  
    int x() { ret DifferentialRegionsMaker.this.x(pos); }
179  
    int y() { ret DifferentialRegionsMaker.this.y(pos); }
180  
    
181  
    int[] pixelsAsIntArray() { throw todo(); }
182  
  }
183  
  
184  
  // returns points in no particular order
185  
  class FloodRegionIterator > RegionIterator {
186  
    int region;
187  
    new IntBuffer stack; // locations as y*w+x
188  
    BitSet seen = new(size);
189  
    
190  
    *(int *region) {
191  
      int pos = regionFirstPixel.get(region);
192  
      printVars(+region, +pos);
193  
      seen.set(pos);
194  
      stack.add(pos);
195  
    }
196  
    
197  
    // flood-fill region
198  
    bool next() {
199  
      if (empty(stack)) false;
200  
      
201  
      pos = stack.popLast();
202  
203  
      // explore neighborhood
204  
      int x = x(), y = y();
205  
      if (x > 0)   tryPosition(pos-1);
206  
      if (x < w-1) tryPosition(pos+1);
207  
      if (y > 0)   tryPosition(pos-w);
208  
      if (y < h-1) tryPosition(pos+w);
209  
210  
      true;
211  
    }
212  
    
213  
    private void tryPosition(int p) {
214  
      if (!seen.get(p) && regionMatrix[p] == region) {
215  
        seen.set(p);
216  
        stack.add(p);
217  
      }
218  
    }
219  
  }
220  
  
221  
  class CachedRegionIterator > RegionIterator {
222  
    int i, to;
223  
224  
    *(int region) {
225  
      i = regionFirstPixel.get(region);
226  
      to = region+1 < l(regionFirstPixel) ? regionFirstPixel.get(region+1) : l(regionPixels);
227  
      
228  
      ifdef FastRegions_debug
229  
        printVars CachedRegionIterator(+region, +i, +to);
230  
      endifdef
231  
    }
232  
    
233  
    bool next() {
234  
      if (i >= to) false;
235  
      pos = regionPixels.get(i++);
236  
      true;
237  
    }
238  
    
239  
    int[] pixelsAsIntArray() {
240  
      ret regionPixels.subArray(i, to);
241  
    }
242  
  }
243  
  
244  
  int regionSize(int iRegion) { ret regionSize.get(iRegion); }
245  
  
246  
  Pt samplePixel aka firstPixel(int iRegion) {
247  
    ret pt(firstPixelPos(iRegion));
248  
  }
249  
  
250  
  int firstPixelPos(int iRegion) {
251  
    int i = regionFirstPixel.get(iRegion);
252  
    ret regionPixels != null ? regionPixels.get(i) : i;
253  
  }
254  
  
255  
  bool inRegion(int iRegion, int x, int y) {
256  
    ret validPos(x, y) && regionMatrix[pos(x, y)] == iRegion;
257  
  }
258  
  Rect regionBounds(int iRegion) { ret rectFromPoints(
259  
    regionBounds.get((iRegion-1)*4),
260  
    regionBounds.get((iRegion-1)*4+1),
261  
    regionBounds.get((iRegion-1)*4+2),
262  
    regionBounds.get((iRegion-1)*4+3),
263  
  ); }
264  
  
265  
  int regionAt(Pt p) { ret regionAt(p.x, p.y); }
266  
  
267  
  int regionAt(int x, int y) {
268  
    ret !validPos(x, y) ? 0 : regionMatrix[pos(x, y)];
269  
  }
270  
  
271  
  int regionAt(int pos) {
272  
    ret regionMatrix[pos];
273  
  }
274  
  
275  
  RegionIterator regionIterator(int iRegion) {
276  
    ret regionPixels != null
277  
      ? new CachedRegionIterator(iRegion)
278  
      : new FloodRegionIterator(iRegion);
279  
  }
280  
  
281  
  L<Pt> regionPixels(int iRegion) {
282  
    var it = regionIterator(iRegion);
283  
    new L<Pt> l;
284  
    while (it.next())
285  
      l.add(Pt(it.x(), it.y()));
286  
    ret l;
287  
  }
288  
  
289  
  // select extra features before regions are made
290  
  // (not necessary anymore)
291  
  
292  
  void collectFirstPixels { /*regionFirstPixel = new IntBuffer;*/ }
293  
  void collectBounds { /*regionBounds = new IntBuffer;*/ }
294  
  
295  
  BitMatrix regionBitMatrix(int iRegion) {
296  
    ret new AbstractBitMatrix(w, h) {
297  
      public Bool get(int x, int y) {
298  
        ret inRegion(iRegion, x, y);
299  
      }
300  
    };
301  
  }
302  
  
303  
  void markRegionInPixelArray(int[] pixels, int iRegion, int rgba) {
304  
    if (iRegion <= 0) ret;
305  
    for i over pixels:
306  
      if (regionAt(i) == iRegion)
307  
        pixels[i] = rgba;
308  
  }
309  
  
310  
  L<Int> regionIndices() { ret virtualCountList(1, nRegions()+1); }
311  
  
312  
  ItIt<Int> regionsRoughlyByDecreasingSize() {
313  
    ret nestedIterator(countIterator_inclusive_backwards(regionsBySize.size()-1, 0),
314  
      iBucket -> iterator(regionsBySize.get(iBucket)));
315  
  }
316  
  
317  
  public IImageRegion<Img> getRegion aka get(int iRegion) {
318  
    ret new ImageRegion(iRegion);
319  
  }
320  
  
321  
  public L<IImageRegion<Img>> regions aka get() {
322  
    run();
323  
    ret listFromFunction(i -> getRegion(i+1), nRegions());
324  
  }
325  
  
326  
  record noeq ImageRegion(int iRegion) is IImageRegion<Img> {
327  
    public int hashCode() { ret iRegion; }
328  
    public bool equals(O o) {
329  
      if (!o instanceof DifferentialRegionsMaker.ImageRegion) false;
330  
      ret ((DifferentialRegionsMaker.ImageRegion) o).creator() == creator()
331  
        && ((DifferentialRegionsMaker.ImageRegion) o).iRegion == iRegion;
332  
    }
333  
    
334  
    public Img image() { ret image; }
335  
    public O creator() { ret DifferentialRegionsMaker.this; }
336  
    public int indexInCreator() { ret iRegion; }
337  
    public Bool createdWithDiagonals() { ret withDiagonals; }
338  
  
339  
    public Rect bounds() { ret regionBounds(iRegion); }
340  
    
341  
    public int numberOfPixels() { ret regionSize(iRegion); }
342  
    
343  
    public Pt firstPixel() { ret pt(firstPixelPos()); }
344  
    public int firstPixelPos() { ret DifferentialRegionsMaker.this.firstPixelPos(iRegion); }
345  
    
346  
    public ItIt<Pt> pixelIterator() {
347  
      var it = regionIterator(iRegion);
348  
      ret iteratorFromFunction(-> it.next() ? pt(it.pos()) : null);
349  
    }
350  
    
351  
    public int[] pixelsAsIntArray() {
352  
      ret regionIterator(iRegion).pixelsAsIntArray();
353  
    }
354  
    
355  
    public bool contains(int x, int y) { ret inRegion(iRegion, x, y); }
356  
    
357  
    public Color color() {
358  
      ret toColor(rgbForRegion(this));
359  
    }
360  
    
361  
    public int firstPixelRGB() {
362  
      ret getRGB(firstPixelPos());
363  
    }
364  
    
365  
    toString {
366  
      ret renderRecordVars("Region", +brightness(), +color(), pixels := numberOfPixels(), +bounds());
367  
    }
368  
  }
369  
  
370  
  RGB rgbForRegion(ImageRegion r) {
371  
    ret RGB(r.firstPixelRGB());
372  
  }
373  
  
374  
  public Img image() { ret image; }
375  
}

Author comment

Began life as a copy of #1034520

download  show line numbers  debug dex  old transpilations   

Travelled to 2 computer(s): elmgxqgtpvxh, mqqgnosmbjvj

No comments. add comment

Snippet ID: #1036012
Snippet name: DifferentialRegionsMaker [dev.]
Eternal ID of this version: #1036012/15
Text MD5: 713f51e9191ef78bb59b4b8990ef03cb
Author: stefan
Category: javax / imaging
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2022-08-27 02:15:49
Source code size: 10922 bytes / 375 lines
Pitched / IR pitched: No / No
Views / Downloads: 141 / 232
Version history: 14 change(s)
Referenced in: [show references]