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

200
LINES

< > BotCompany Repo | #1035054 // G22RegionThinner_v2 - thin a region to 1 pixel wide [backup]

JavaX fragment (include)

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

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: #1035054
Snippet name: G22RegionThinner_v2 - thin a region to 1 pixel wide [backup]
Eternal ID of this version: #1035054/1
Text MD5: 99493fe26989d941813764994be20c8e
Author: stefan
Category: javax
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2022-03-26 02:53:29
Source code size: 5693 bytes / 200 lines
Pitched / IR pitched: No / No
Views / Downloads: 56 / 64
Referenced in: [show references]