1 | srecord noeq FastRegions_BWImage(BWImage image) is Runnable, IImageRegions<BWImage> { |
2 | int w, h, runner; |
3 | gettable int size; |
4 | |
5 | new IntBuffer stack; // locations as y*w+x |
6 | int[] regionMatrix; // for each pixel: region index (starting at 1) |
7 | new IntBuffer regionPixels; // collect all pixels for regions |
8 | |
9 | // initialize these to use them |
10 | |
11 | new IntBuffer regionFirstPixel; // for each region: index of first pixel found in regionPixels |
12 | new IntBuffer regionSize; // for each region: number of pixels |
13 | new IntBuffer regionBounds; // for each region: bounds (x1, y1, x2, y2) |
14 | |
15 | // index = dual log of region size, value = region index |
16 | new L<IntBuffer> regionsBySize; |
17 | |
18 | int regionCounter; |
19 | bool verbose; |
20 | |
21 | double regionStep = .1; // for rendering in regionsImage |
22 | |
23 | int x(int pos) { ret pos % w; } |
24 | int y(int pos) { ret pos / w; } |
25 | int pos(int x, int y) { ret y*w+x; } |
26 | Pt pt(int pos) { ret Pt(x(pos), y(pos)); } |
27 | bool validPos(int x, int y) { ret x >= 0 && y >= 0 && x < w && y < h; } |
28 | |
29 | int getColor(int pos) { ret image.getInt(x(pos), y(pos)); } |
30 | |
31 | *(BufferedImage img) { this(toBWImage(img)); } |
32 | |
33 | run { |
34 | w = image.getWidth(); h = image.getHeight(); |
35 | size = w*h; |
36 | regionMatrix = new int[size]; |
37 | |
38 | // 0 entries are unused |
39 | regionFirstPixel?.add(0); |
40 | regionSize?.add(0); |
41 | |
42 | regionPixels?.setSize(size); |
43 | |
44 | while (runner < size) { |
45 | if (regionMatrix[runner] == 0) { |
46 | // make a new region, get color |
47 | int region = ++regionCounter; |
48 | regionFirstPixel?.add(regionPixels != null ? l(regionPixels) : runner); |
49 | stack.add(runner); |
50 | int color = getColor(runner); |
51 | int rsize = 0, x1 = w, y1 = h, x2 = 0, y2 = 0; |
52 | ifdef FastRegions_debug |
53 | printVars FastRegions(+region, +runner); |
54 | endifdef |
55 | |
56 | // flood-fill region |
57 | while (nempty(stack)) { |
58 | int pos = stack.popLast(); |
59 | if (regionMatrix[pos] != 0) continue; // touching myself (or someone else) |
60 | if (getColor(pos) != color) continue; // wrong color |
61 | |
62 | // new pixel found, mark as ours |
63 | regionMatrix[pos] = region; |
64 | ++rsize; |
65 | regionPixels?.add(pos); |
66 | int x = x(pos), y = y(pos); |
67 | |
68 | ifdef FastRegions_debug |
69 | printVars FastRegions(+x, +y); |
70 | endifdef |
71 | |
72 | if (x < x1) x1 = x; |
73 | if (x > x2) x2 = x; |
74 | if (y < y1) y1 = y; |
75 | if (y > y2) y2 = y; |
76 | |
77 | // explore neighborhood |
78 | if (x > 0) stack.add(pos-1); |
79 | if (x < w-1) stack.add(pos+1); |
80 | if (y > 0) stack.add(pos-w); |
81 | if (y < h-1) stack.add(pos+w); |
82 | } |
83 | |
84 | regionSize?.add(rsize); |
85 | regionBounds?.addAll(x1, y1, x2+1, y2+1); |
86 | if (regionsBySize != null) { |
87 | int iBucket = dualLog(rsize); |
88 | var buffer = listGetOrCreate(regionsBySize, iBucket, -> new IntBuffer); |
89 | buffer.add(region); |
90 | } |
91 | } |
92 | |
93 | ++runner; |
94 | } |
95 | } |
96 | |
97 | IBWImage regionsImage() { |
98 | ret iBWImageFromFunction((x, y) -> { |
99 | var region = regionMatrix[pos(x, y)]; |
100 | ret ((region-1)*regionStep) % (1.0+regionStep-0.0001); |
101 | }, w, h); |
102 | } |
103 | |
104 | int regionCount aka nRegions() { ret regionCounter; } |
105 | |
106 | abstract class RegionIterator { |
107 | int pos; |
108 | |
109 | abstract bool next(); |
110 | |
111 | int pos() { ret pos; } |
112 | int x() { ret BWImage_FastRegions.this.x(pos); } |
113 | int y() { ret BWImage_FastRegions.this.y(pos); } |
114 | } |
115 | |
116 | // returns points in no particular order |
117 | class FloodRegionIterator > RegionIterator { |
118 | int region; |
119 | new IntBuffer stack; // locations as y*w+x |
120 | BitSet seen = new(size); |
121 | |
122 | *(int *region) { |
123 | int pos = regionFirstPixel.get(region); |
124 | printVars(+region, +pos); |
125 | seen.set(pos); |
126 | stack.add(pos); |
127 | } |
128 | |
129 | // flood-fill region |
130 | bool next() { |
131 | if (empty(stack)) false; |
132 | |
133 | pos = stack.popLast(); |
134 | |
135 | // explore neighborhood |
136 | int x = x(), y = y(); |
137 | if (x > 0) tryPosition(pos-1); |
138 | if (x < w-1) tryPosition(pos+1); |
139 | if (y > 0) tryPosition(pos-w); |
140 | if (y < h-1) tryPosition(pos+w); |
141 | |
142 | true; |
143 | } |
144 | |
145 | private void tryPosition(int p) { |
146 | if (!seen.get(p) && regionMatrix[p] == region) { |
147 | seen.set(p); |
148 | stack.add(p); |
149 | } |
150 | } |
151 | } |
152 | |
153 | class CachedRegionIterator > RegionIterator { |
154 | int i, to; |
155 | |
156 | *(int region) { |
157 | i = regionFirstPixel.get(region); |
158 | to = region+1 < l(regionFirstPixel) ? regionFirstPixel.get(region+1) : l(regionPixels); |
159 | |
160 | ifdef FastRegions_debug |
161 | printVars CachedRegionIterator(+region, +i, +to); |
162 | endifdef |
163 | } |
164 | |
165 | bool next() { |
166 | if (i >= to) false; |
167 | pos = regionPixels.get(i++); |
168 | true; |
169 | } |
170 | } |
171 | |
172 | int regionSize(int iRegion) { ret regionSize.get(iRegion); } |
173 | |
174 | Pt samplePixel aka firstPixel(int iRegion) { |
175 | ret pt(firstPixelPos(iRegion)); |
176 | } |
177 | |
178 | int firstPixelPos(int iRegion) { |
179 | int i = regionFirstPixel.get(iRegion); |
180 | ret regionPixels != null ? regionPixels.get(i) : i; |
181 | } |
182 | |
183 | bool inRegion(int iRegion, int x, int y) { |
184 | ret validPos(x, y) && regionMatrix[pos(x, y)] == iRegion; |
185 | } |
186 | Rect regionBounds(int iRegion) { ret rectFromPoints( |
187 | regionBounds.get((iRegion-1)*4), |
188 | regionBounds.get((iRegion-1)*4+1), |
189 | regionBounds.get((iRegion-1)*4+2), |
190 | regionBounds.get((iRegion-1)*4+3), |
191 | ); } |
192 | |
193 | int regionAt(Pt p) { ret regionAt(p.x, p.y); } |
194 | |
195 | int regionAt(int x, int y) { |
196 | ret !validPos(x, y) ? 0 : regionMatrix[pos(x, y)]; |
197 | } |
198 | |
199 | int regionAt(int pos) { |
200 | ret regionMatrix[pos]; |
201 | } |
202 | |
203 | RegionIterator regionIterator(int iRegion) { |
204 | ret regionPixels != null |
205 | ? new CachedRegionIterator(iRegion) |
206 | : new FloodRegionIterator(iRegion); |
207 | } |
208 | |
209 | L<Pt> regionPixels(int iRegion) { |
210 | var it = regionIterator(iRegion); |
211 | new L<Pt> l; |
212 | while (it.next()) |
213 | l.add(Pt(it.x(), it.y())); |
214 | ret l; |
215 | } |
216 | |
217 | // select extra features before regions are made |
218 | // (not necessary anymore) |
219 | |
220 | void collectFirstPixels { /*regionFirstPixel = new IntBuffer;*/ } |
221 | void collectBounds { /*regionBounds = new IntBuffer;*/ } |
222 | |
223 | BitMatrix regionBitMatrix(int iRegion) { |
224 | ret new AbstractBitMatrix(w, h) { |
225 | public Bool get(int x, int y) { |
226 | ret inRegion(iRegion, x, y); |
227 | } |
228 | }; |
229 | } |
230 | |
231 | void markRegionInPixelArray(int[] pixels, int iRegion, int rgba) { |
232 | if (iRegion <= 0) ret; |
233 | for i over pixels: |
234 | if (regionAt(i) == iRegion) |
235 | pixels[i] = rgba; |
236 | } |
237 | |
238 | L<Int> regionIndices() { ret virtualCountList(1, nRegions()+1); } |
239 | |
240 | ItIt<Int> regionsRoughlyByDecreasingSize() { |
241 | ret nestedIterator(countIterator_inclusive_backwards(regionsBySize.size()-1, 0), |
242 | iBucket -> iterator(regionsBySize.get(iBucket))); |
243 | } |
244 | |
245 | IImageRegion<BWImage> getRegion aka get(int iRegion) { |
246 | ret new ImageRegion(iRegion); |
247 | } |
248 | |
249 | public L<IImageRegion<BWImage>> regions() { |
250 | ret listFromFunction(i -> getRegion(i+1), nRegions()); |
251 | } |
252 | |
253 | record ImageRegion(int iRegion) is IImageRegion<BWImage> { |
254 | public BWImage image() { ret image; } |
255 | public O creator() { ret FastRegions_BWImage.this; } |
256 | public int indexInCreator() { ret iRegion; } |
257 | |
258 | public Rect bounds() { ret regionBounds(iRegion); } |
259 | |
260 | public int numberOfPixels() { ret regionSize(iRegion); } |
261 | |
262 | public Pt firstPixel() { ret pt(firstPixelPos()); } |
263 | public int firstPixelPos() { ret FastRegions_BWImage.this.firstPixelPos(iRegion); } |
264 | |
265 | public Iterator<Pt> pixelIterator() { |
266 | var it = regionIterator(iRegion); |
267 | ret iteratorFromFunction(-> it.next() ? pt(it.pos()) : null); |
268 | } |
269 | |
270 | public bool contains(int x, int y) { ret inRegion(iRegion, x, y); } |
271 | |
272 | public RGB color() { |
273 | ret rgbFromGrayscale(brightness()); |
274 | } |
275 | |
276 | public int brightness() { |
277 | ret getColor(firstPixelPos()); |
278 | } |
279 | |
280 | toString { |
281 | ret renderRecordVars("Region", +brightness(), +color(), pixels := numberOfPixels(), +bounds()); |
282 | } |
283 | } |
284 | } |
Began life as a copy of #1033761
download show line numbers debug dex old transpilations
Travelled to 3 computer(s): bhatertpkbcr, mowyntqkapby, mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1034521 |
Snippet name: | FastRegions_BWImage (backup) |
Eternal ID of this version: | #1034521/1 |
Text MD5: | 484e489088fcb98df200f266817573c1 |
Author: | stefan |
Category: | javax / imaging |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-02-10 07:39:37 |
Source code size: | 8297 bytes / 284 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 126 / 140 |
Referenced in: | [show references] |