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

249
LINES

< > BotCompany Repo | #1035112 // G22RegionThinner_v2 - thin a region to 1 pixel wide [abandoned alternate rhythm]

JavaX fragment (include)

1  
srecord noeq G22RegionThinner_v2<Img extends WidthAndHeight>(IImageRegion<Img> originalRegion) is Steppable {
2  
  Rect bounds;
3  
  IImageRegion<Img> region;
4  
5  
  settable bool debug;
6  
  settable bool doFinalStep = true;
7  
  settable int lastPhaseThreshold1 = 2;
8  
  settable int lastPhaseThreshold = 5;
9  
  
10  
  settable byte[] lookupTable;
11  
  
12  
  // 0 = outside of region
13  
  // 1 = inner pixel
14  
  // 2 = border pixel
15  
  // index is in bounds coordinates
16  
  byte[] pixels;
17  
  
18  
  int idx(int x, int y) {
19  
    ret (y-bounds.y)*bounds.w+x-bounds.x;
20  
  }
21  
  int idx(Pt p) { ret idx(p.x, p.y); }
22  
  
23  
  Pt idxToPt(int idx) {
24  
    ret pt(bounds.x+(idx % bounds.w), bounds.y+idx/bounds.w);
25  
  }
26  
  
27  
  byte getPixel(long p) {
28  
    ret getPixel(firstIntFromLong(p), secondIntFromLong(p));
29  
  }
30  
  
31  
  byte getPixel(Pt p) {
32  
    ret !containsPt(bounds, p) ? 0 : pixels[idx(p)];
33  
  }
34  
  
35  
  byte getPixel(int x, int y) {
36  
    ret !containsPt(bounds, x, y) ? 0 : pixels[idx(x, y)];
37  
  }
38  
  
39  
  bool contains(int x, int y) {
40  
    ret getPixel(x, y) != 0;
41  
  }
42  
  
43  
  bool clearPixel(int x, int y) {
44  
    if (!region.contains(x, y)) false;
45  
    pixels[idx(x, y)] = 0;
46  
    true;
47  
  }
48  
  
49  
  void init {
50  
    if (bounds != null) ret;
51  
    bounds = originalRegion.bounds();
52  
    pixels = new byte[area(bounds)];
53  
    for (Pt p : originalRegion.pixelIterator())
54  
      pixels[idx(p.x, p.y)] = 1;
55  
      
56  
    region = new ThinnedRegion;
57  
  }
58  
  
59  
  class ThinnedRegion is IImageRegion<Img> {
60  
    public Img image() { ret originalRegion.image(); }
61  
    public Rect bounds() { ret bounds; }
62  
  
63  
    public bool contains(int x, int y) {
64  
      ret containsPt(bounds, x, y) && pixels[idx(x, y)] > 0;
65  
    }
66  
    
67  
    public ItIt<Pt> pixelIterator() {
68  
      ret iff_null(new IF0<Pt> {
69  
        int idx = 0;
70  
        
71  
        public Pt get() {
72  
          for (; idx < pixels.length; idx++)
73  
            if (pixels[idx] > 0)
74  
              ret idxToPt(idx++);
75  
          null;
76  
        }
77  
      });
78  
    }
79  
  }
80  
  
81  
  public bool step() {
82  
    init();
83  
    
84  
    bool change = finalStep();
85  
    change |= phase1step();
86  
    ret change;
87  
  }
88  
  
89  
  bool phase1step() {
90  
    L<PtBuffer> traces = g22_allBorderTraces_withDiagonals(region);
91  
    for (points : traces)
92  
      for (p : points)
93  
        pixels[idx(p)] = 2;
94  
        
95  
    new PtBuffer toDelete;
96  
97  
    for (points : traces) {
98  
      int nPoints = l(points);
99  
      BitSet deletable = emptyBitSet(nPoints);
100  
      for i to nPoints: {
101  
        bool del = deletableBorderPoint(points, i);
102  
        /*bool del2 = deletableBorderPoint_old(points, i);
103  
        if (del != del2)
104  
          failWithVars("deletableBorderPoint", +i, +nPoints, +del);*/
105  
        if (del)
106  
          deletable.set(i);
107  
      }
108  
109  
      // handle special cases      
110  
      for i to nPoints:
111  
        if (cyclicGet(deletable, nPoints, i-1)
112  
          && !deletable.get(i)
113  
          && cyclicGet(deletable, nPoints, i+1)) {
114  
          Pt p = points.get(i);
115  
          Pt prev = ptMinus(cyclicGet(points, i-1), p);
116  
          Pt next = ptMinus(cyclicGet(points, i+1), p);
117  
          int dir1 = onePathLookupDirection(prev);
118  
          int dir2 = onePathLookupDirection(next);
119  
          int diff = mod(dir2-dir1, 8);
120  
          if (debug) printVars("special case", +p, +prev, +next, +dir1, +dir2, +diff);
121  
          if (diff == 1 || diff == 7)
122  
            deletable.set(i);
123  
        }
124  
125  
      for i to nPoints:
126  
        if (deletable.get(i))
127  
          toDelete.add(points.get(i));
128  
    }
129  
130  
    for (p : toDelete)
131  
      pixels[idx(p)] = 0;
132  
133  
    ret nempty(toDelete);
134  
  }
135  
  
136  
  bool deletableBorderPoint(PtBuffer points, int i) {
137  
    //L<Pt> range = cyclicSubList_incl(points, i-3, i+3);
138  
    LongBuffer pointsBuf = points.buf;
139  
    long p_long = pointsBuf.get(i);
140  
    
141  
    // special case
142  
    
143  
    if (p_long == cyclicGet(pointsBuf, i-2)
144  
      || p_long == cyclicGet(pointsBuf, i+2)) {
145  
      printVars ifdef G22RegionThinner_debug("special case", p := ptFromLong(p_long));
146  
      false;
147  
    }
148  
    
149  
    int surroundingBorderPixels = 0, surroundingInnerPixels = 0;
150  
    for (int dir = 1; dir <= 8; dir++) {
151  
      long p2_long = onePathDirection_long(p_long, dir);
152  
      byte value = getPixel(p2_long);
153  
      if (value == 2 && !rangeContains(pointsBuf, i, p2_long)) {
154  
        printVars ifdef G22RegionThinner_debug("surroundingBorderPixel", p2 := ptFromLong(p2_long));
155  
        //surroundingBorderPixels++;
156  
        false;
157  
      } else if (value == 1) {
158  
        printVars ifdef G22RegionThinner_debug("surroundingInnerPixel", p2 := ptFromLong(p2_long));
159  
        surroundingInnerPixels++;
160  
      }
161  
    }
162  
    
163  
    ret surroundingInnerPixels > 0;
164  
    
165  
    //bool deletable = surroundingInnerPixels > 0 && surroundingBorderPixels == 0;
166  
    //printVars ifdef G22RegionThinner_debug(+p, +surroundingInnerPixels, +surroundingBorderPixels, +deletable, +range);
167  
    //ret deletable;
168  
  }
169  
  
170  
  bool rangeContains(LongBuffer pointsBuf, int i, long p2_long) {
171  
    for (int j = i-3; j <= i+3; j++)
172  
      if (cyclicGet(pointsBuf, j) == p2_long)
173  
        true;
174  
    false;
175  
  }
176  
  
177  
  bool deletableBorderPoint_old(PtBuffer points, int i) {
178  
    L<Pt> range = cyclicSubList_incl(points, i-3, i+3);
179  
    Pt p = points.get(i);
180  
    
181  
    // special case
182  
    if (eq(range.get(1), p) || eq(range.get(5), p)) false;
183  
    
184  
    int surroundingBorderPixels = 0, surroundingInnerPixels = 0;
185  
    for (int dir = 1; dir <= 8; dir++) {
186  
      Pt p2 = ptPlus(p, onePathDirection(dir));
187  
      byte value = getPixel(p2);
188  
      if (value == 2 && !range.contains(p2))
189  
        surroundingBorderPixels++;
190  
      else if (value == 1)
191  
        surroundingInnerPixels++;
192  
    }
193  
    
194  
    bool deletable = surroundingInnerPixels > 0 && surroundingBorderPixels == 0;
195  
      
196  
    printVars ifdef G22RegionThinner_debug(+p, +surroundingInnerPixels, +surroundingBorderPixels, +deletable, +range);
197  
    ret deletable;
198  
  }
199  
  
200  
  IImageRegion region aka get() { ret or(region, originalRegion); }
201  
  
202  
  // go from 2 pixels wide to 1 pixel wide (TODO)
203  
  bool finalStep() {
204  
    if (!doFinalStep) false;
205  
    
206  
    if (lookupTable == null)
207  
      lookupTable = new G22RegionThinner_LookupTable()
208  
        .lastPhaseThreshold(lastPhaseThreshold)
209  
        .lastPhaseThreshold1(lastPhaseThreshold1)!;
210  
    
211  
    bool change;
212  
    int x1 = bounds.x, x2 = bounds.x2();
213  
    int y1 = bounds.y, y2 = bounds.y2();
214  
    printVars ifdef G22RegionThinner_lastPhase_debug("finalStep", +x1, +y1, +x2, +y2);
215  
    
216  
    var pingSource = pingSource();
217  
    
218  
    for (int y = y1; y < y2; y++) {
219  
      ping(pingSource);
220  
      for (int x = x1; x < x2; x++) {
221  
        // need a set pixel
222  
        if (!contains(x, y)) continue;
223  
224  
        // check if this pixel is essential to hold the structure
225  
        // together by doing a floodfill in the 3x3 neighborhood
226  
        // (simulating the pixel being cleared already)
227  
        
228  
        var imgPattern = ubyteFromBits(
229  
          contains(x-1, y-1),
230  
          contains(x,   y-1),
231  
          contains(x+1, y-1),
232  
          contains(x-1, y),
233  
          contains(x+1, y),
234  
          contains(x-1, y+1),
235  
          contains(x,   y+1),
236  
          contains(x+1, y+1));
237  
        
238  
        bool delete = getBit(lookupTable, imgPattern);
239  
        
240  
        printVars ifdef G22RegionThinner_lastPhase_debug(
241  
          +x, +y, +imgPattern, +pixels, +delete);
242  
        
243  
        if (delete)
244  
          change |= clearPixel(x, y);
245  
      }
246  
    }
247  
    ret change;
248  
  }
249  
}

Author comment

Began life as a copy of #1034983

download  show line numbers  debug dex  old transpilations   

Travelled to 3 computer(s): bhatertpkbcr, mowyntqkapby, mqqgnosmbjvj

No comments. add comment

Snippet ID: #1035112
Snippet name: G22RegionThinner_v2 - thin a region to 1 pixel wide [abandoned alternate rhythm]
Eternal ID of this version: #1035112/1
Text MD5: 6993c12c7452250e0b7c11adec488a1d
Author: stefan
Category: javax
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2022-03-30 03:53:46
Source code size: 7499 bytes / 249 lines
Pitched / IR pitched: No / No
Views / Downloads: 116 / 131
Referenced in: [show references]