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

352
LINES

< > BotCompany Repo | #1032248 // ImageSimplifier v1 [dev. - reduces images to a mathematical function]

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

Transpiled version (6668L) is out of date.

1  
srecord noeq ImageSimplifier(BufferedImage inputImage) {
2  
  replace Channel with int.
3  
  
4  
  // input image (mandatory)
5  
6  
  CompactIntegralImage mainImage;
7  
  
8  
  // Be verbose?
9  
  
10  
  bool verbose, verboseImageSize, debug;
11  
    
12  
  // the big internal cache
13  
14  
  new Map<Rect, IIntegralImage> clipCache;
15  
  
16  
  // channel definitions (r, g, b and grayscale are channels)
17  
  
18  
  static final int grayscale = 3; // channel number for grayscale
19  
  static final int channels = 4;
20  
  
21  
  // user-settable maximums
22  
  
23  
  long maxSteps = 1000;
24  
25  
  // very important value. size of smallest features in relation to min(image width, image height)
26  
  
27  
  double featureSize = 0.1;
28  
29  
  long steps;
30  
  double lowestExecutedProbability;
31  
  
32  
  // OUTPUT of the algorithm (found points)
33  
  
34  
  new ProbabilisticList<IIntegralImage> liveliestPoints;
35  
36  
  // INTERNAL (probabilistic scheduling, memory)
37  
  
38  
  ProbabilisticList<IIntegralImage> scheduler;
39  
  Set<IIntegralImage> lookedAt;
40  
41  
  abstract class IIntegralImage implements main IIntegralImage {
42  
    // width and height of image
43  
    int w, h;
44  
    
45  
    public int getWidth() { ret w; }
46  
    public int getHeight() { ret h; }
47  
48  
    int liveliness_cachedChannel = -1;
49  
    double liveliness_cache;
50  
    
51  
    long discoveredInStep;
52  
    
53  
    public abstract double getIntegralValue(int x, int y, Channel channel);
54  
    
55  
    BufferedImage render() {
56  
      ret imageFromFunction(w, h, (x, y) -> rgbPixel(x, y, x+1, y+1) | fullAlphaMask());
57  
    }
58  
59  
    double getPixel(Rect r, int channel) {
60  
      ret getPixel(r.x, r.y, r.x2(), r.y2(), channel);
61  
    }
62  
63  
    double getPixel(int channel) { ret getPixel(0, 0, w, h, channel); }
64  
    
65  
    // return value ranges from 0 to 1 (usually)
66  
    double getPixel(int x1, int y1, int x2, int y2, int channel) {
67  
      ret doubleRatio(rectSum(x1, y1, x2, y2, channel), (x2-x1)*(y2-y1)*255.0);
68  
    }
69  
    
70  
    public double rectSum(Rect r, int channel) {
71  
      ret rectSum(r.x, r.y, r.x2(), r.y2(), channel);
72  
    }
73  
    
74  
    public double rectSum(int x1, int y1, int x2, int y2, int channel) {
75  
      double bottomRight = getIntegralValue(x2-1, y2-1, channel);
76  
      double topRight    = getIntegralValue(x2-1, y1-1, channel);
77  
      double bottomLeft  = getIntegralValue(x1-1, y2-1, channel);
78  
      double topLeft     = getIntegralValue(x1-1, y1-1, channel);
79  
      ret bottomRight-topRight-bottomLeft+topLeft;
80  
    }
81  
82  
    int rgbPixel(int x1, int y1, int x2, int y2) {
83  
      int r = iround(clampZeroToOne(getPixel(x1, y1, x2, y2, 0))*255);
84  
      int g = iround(clampZeroToOne(getPixel(x1, y1, x2, y2, 1))*255);
85  
      int b = iround(clampZeroToOne(getPixel(x1, y1, x2, y2, 2))*255);
86  
      ret rgbInt(r, g, b);
87  
    }
88  
    
89  
    double liveliness(int channel) {
90  
      if (liveliness_cachedChannel != channel) {
91  
        // optimization (but no change in semantics):
92  
        // if (w <= 1 && h <= 1) ret 0; // liveliness of single pixel is 0
93  
        liveliness_cache = standardDeviation(map(q -> q.getPixel(channel), quadrants()));
94  
        liveliness_cachedChannel = channel;
95  
      }
96  
      ret liveliness_cache;
97  
    }
98  
99  
    // no duplicates, without full image
100  
    L<IIntegralImage> descentShapes_cleaned() {
101  
      ret uniquify(listMinus(descentShapes(), this));
102  
    }
103  
104  
    L<IIntegralImage> descentShapes() {
105  
      ret centerPlusQuadrants();
106  
    }
107  
    
108  
    L<IIntegralImage> centerPlusQuadrants() {
109  
      int midX = w/2, midY = h/2;
110  
      Rect r = rectAround(iround(midX), iround(midY), max(midX, 1), max(midY, 1));
111  
      ret itemPlusList(clip(r), quadrants());
112  
    }
113  
    
114  
    L<IIntegralImage> quadrants() {
115  
      if (w <= 1 && h <= 1) null; // let's really not have quadrants of a single pixel
116  
      int midX = w/2, midY = h/2;
117  
      ret mapLL clip(
118  
        rect(0, 0, max(midX, 1), max(midY, 1)),
119  
        rect(midX, 0, w-midX, max(midY, 1)),
120  
        rect(0, midY, max(midX, 1), h-midY),
121  
        rect(midX, midY, w-midX, h-midY)
122  
      );
123  
    }
124  
125  
    IIntegralImage liveliestSubshape(int channel) {
126  
      ret highestBy(q -> q.liveliness(channel), quadrants());
127  
    }
128  
    
129  
    ProbabilisticList<IIntegralImage> liveliestSubshape_probabilistic(int channel) {
130  
      ret new ProbabilisticList<IIntegralImage>(map(descentShapes(), shape ->
131  
        withProbability(shape.liveliness(channel), shape)));
132  
    }
133  
134  
    public IIntegralImage clip(Rect r) {
135  
      Rect me = rect(0, 0, w, h);
136  
      r = intersectRects(r, 0, 0, w, h);
137  
      if (r.x == 0 && r.y == 0 && r.w == w && r.h == h) this;
138  
      ret actuallyClip(r);
139  
    }
140  
141  
    IIntegralImage actuallyClip(Rect r) {
142  
      ret newClip(this, r);
143  
    }    
144  
    
145  
    public IIntegralImage clip(int x1, int y1, int w, int h) { ret clip(rect(x1, y1, w, h)); }
146  
    
147  
    Rect positionInImage(IIntegralImage mainImage) {
148  
      ret this == mainImage ? positionInImage() : null;
149  
    }
150  
151  
    Rect positionInImage() {
152  
      ret rect(0, 0, w, h);
153  
    }
154  
    
155  
    Pt center() {
156  
      ret main center(positionInImage());
157  
    }
158  
    
159  
    double area() { ret w*h; }
160  
    double relativeArea() { ret area()/mainImage.area(); }
161  
162  
    bool singlePixel() { ret w <= 1 && h <= 1; }
163  
164  
    toString { ret w + "*" + h; }
165  
  }
166  
167  
  // virtual clip of an integral image
168  
  class Clip extends IIntegralImage {
169  
    IIntegralImage fullImage;
170  
    int x1, y1;
171  
172  
    *(IIntegralImage *fullImage, Rect r) {
173  
      x1 = r.x; y1 = r.y; w = r.w; h = r.h;
174  
    }
175  
     
176  
    *(IIntegralImage *fullImage, int *x1, int *y1, int *w, int *h) {}
177  
    
178  
    public double getIntegralValue(int x, int y, int channel) {
179  
      ret fullImage.getIntegralValue(x+x1, y+y1, channel);
180  
    }
181  
182  
    // don't clip a clip - be smarter than that!
183  
    IIntegralImage actuallyClip(Rect r) {
184  
      ret newClip(fullImage, translateRect(r, x1, y1));
185  
    }
186  
187  
    Rect positionInImage() {
188  
      ret rect(x1, y1, w, h);
189  
    }
190  
191  
    Rect positionInImage(IIntegralImage mainImage) {
192  
      try object Rect r = super.positionInImage(mainImage);
193  
      if (fullImage == mainImage) ret rect(x1, y1, w, h);
194  
      null;
195  
    }
196  
197  
    toString { ret positionInImage() + " in " + fullImage; }
198  
  }
199  
200  
  class CompactIntegralImage extends IIntegralImage {
201  
    short[] data;
202  
    
203  
    // for each 8*8 block, 4 shorts
204  
    // We store the bits 15 to 30 so we have one overlapping
205  
    // bit with the lower words (only way to get the addition right)
206  
    short[] highWords;
207  
    static final int blockShift = 3;
208  
    static final int blockSize = 1 << 3;
209  
    int gridW, gridH;
210  
211  
    *(CompactIntegralImage img) {
212  
      w = img.w;
213  
      h = img.h;
214  
      data = img.data;
215  
      highWords = img.highWords;
216  
      gridW = img.gridW;
217  
      gridH = img.gridH;
218  
    }
219  
    
220  
    *(BufferedImage img) {
221  
      w = img.getWidth(); h = img.getHeight();
222  
      int safety = 64;
223  
      if (longMul(w, h)*channels*256 > Int.MAX_VALUE-safety) fail("Image too big: " + w + "*" + h);
224  
      gridW = iceil_divideByPowerOfTwo(blockShift, w);
225  
      gridH = iceil_divideByPowerOfTwo(blockShift, h);
226  
      highWords = new short[gridW*gridH*channels];
227  
      
228  
      int[] pixels = pixelsOfBufferedImage(img);
229  
      data = new short[w*h*channels];
230  
      int i = 0, j = 0;
231  
      int[] sum = new[channels];
232  
      int[] lastRow = new[w*channels];
233  
      int iHighWords = 0;
234  
      for y to h: {
235  
        for c to channels: sum[c] = 0;
236  
        int iLastRow = 0;
237  
        for x to w: {
238  
          int rgb = pixels[j++] & 0xFFFFFF;
239  
          for c to channels: {
240  
            int current;
241  
            if (c == grayscale)
242  
              current = iround((sum[0]+sum[1]+sum[2])/3);
243  
            else {
244  
              current = (sum[c] += rgb >> 16);
245  
              rgb = (rgb << 8) & 0xFFFFFF;
246  
            }
247  
            int value = current + lastRow[iLastRow];
248  
            short low = (short) value;
249  
            data[i] = low;
250  
            lastRow[iLastRow++] = value;
251  
            i++;
252  
            
253  
            if (debug)
254  
              printVars(+x, +y, +c, rgb := intToHex(rgb), +current, +value, low := ushortToInt(low), +iLastRow, +iHighWords);
255  
256  
            // if top-left corner of a block, set high word
257  
            if ((x & (blockSize-1)) == 0 && (y & (blockSize-1)) == 0) {
258  
              short high = (short) (value >> 15);
259  
              highWords[iHighWords++] = high;
260  
              if (debug)
261  
                printVars(+high);
262  
              ifdef CompactIntegralImage_safetyTests
263  
                int blockX = x >> blockShift, blockY = y >> blockShift;
264  
                assertEquals(iHighWords-1, (blockY*gridW+blockX)*channels+c);
265  
                assertEquals(ushortToInt(high), getHighWord(x, y, c));
266  
                if (blockX == 141) {
267  
                  printVars(+x, +y, +c, rgb := intToHex(rgb), +current, +value, low := ushortToInt(low), +iLastRow, +iHighWords);
268  
                  printVars(+blockX, +blockY, +high);
269  
                }
270  
              endifdef
271  
            }              
272  
          }
273  
        }
274  
      }
275  
    }
276  
277  
    int getHighWord(int x, int y, int channel) {    
278  
      int blockX = x >> blockShift, blockY = y >> blockShift;
279  
      ret ushortToInt(highWords[(blockY*gridW+blockX)*channels+channel]);
280  
    }
281  
    
282  
    public double getIntegralValue(int x, int y, Channel channel) {
283  
      if (x < 0 || y < 0) ret 0;
284  
      y = min(y, h-1);
285  
      x = min(x, w-1);
286  
      int blockX = x >> blockShift, blockY = y >> blockShift;
287  
      int low = data[(y*w+x)*channels+channel];
288  
      int high = ushortToInt(highWords[(blockY*gridW+blockX)*channels+channel]);
289  
      
290  
      int value = ((high & 1) == 0)
291  
        //   - need unsigned expansion of low
292  
        // bit 15 in block is 0 in top left corner
293  
        ? (high << 15) + (low & 0xFFFF)
294  
        
295  
        // bit 15 in block is 1 in top left corner
296  
        //   - need signed expansion of low
297  
        : ((high+1) << 15) + low;
298  
        
299  
      if (debug)
300  
        printVars getIntegralValue(+x, +y, +channel, +blockX, +blockY, +high, low := low + "/" + ushortToInt((short) low), +value);
301  
        
302  
      ret value;
303  
    }
304  
  }
305  
306  
  IIntegralImage newClip(IIntegralImage fullImage, Rect r) {
307  
    assertSame(fullImage, mainImage);
308  
    ret getOrCreate(clipCache, r, () -> new Clip(fullImage, r));
309  
  }
310  
311  
  IIntegralImage liveliestPointIn(IIntegralImage image) {
312  
    ret applyUntilEqual_goOneBackOnNull(c -> c.liveliestSubshape(grayscale), image);
313  
  }
314  
315  
  // level++ <=> a fourth the area
316  
  double level(IIntegralImage image) {
317  
    ret -log(image.relativeArea(), 4);
318  
  }
319  
  
320  
  // featureSize = relative to smaller image dimension
321  
  double actualFeatureSize() {
322  
    ret featureSize*min(mainImage.w, mainImage.h);
323  
  }
324  
325  
  void clearCaches {
326  
    clipCache.clear();
327  
  }
328  
329  
  void prepareImage {
330  
    if (mainImage == null)
331  
      // make integral image
332  
      mainImage = new CompactIntegralImage(inputImage);
333  
    else
334  
      // not sure why we are doing this one or whether we should do it
335  
      mainImage = new CompactIntegralImage(mainImage);
336  
    inputImage = null;
337  
    
338  
    if (verbose || verboseImageSize) print("Full image size: " + mainImage.w + "*" + mainImage.h);
339  
  }
340  
  
341  
  run {
342  
    prepareImage();
343  
344  
    time "Simplification" {
345  
    }
346  
  }
347  
  
348  
  void setInputImage aka setImage(BufferedImage image) {
349  
    inputImage = image;
350  
    mainImage = null;
351  
  }
352  
}

Author comment

Began life as a copy of #1032199

download  show line numbers  debug dex  old transpilations   

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

No comments. add comment

Snippet ID: #1032248
Snippet name: ImageSimplifier v1 [dev. - reduces images to a mathematical function]
Eternal ID of this version: #1032248/62
Text MD5: 5168e46fdf1a3f1253fa799a389c3b46
Author: stefan
Category: javax / image recognition
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2021-08-23 03:13:43
Source code size: 11341 bytes / 352 lines
Pitched / IR pitched: No / No
Views / Downloads: 193 / 377
Version history: 61 change(s)
Referenced in: [show references]