Uses 108K of libraries. Click here for Pure Java version (13374L/82K).
do not include class IntegralImage. do not include class IIntegralImage. srecord noeq MinimalRecognizer(BufferedImage inputImage) { replace Channel with int. IIntegralImage mainImage; static final int grayscale = 3; // channel number for grayscale abstract class IIntegralImage { // width and height of image int w, h; abstract double integralValue(int x, int y, Channel channel); BufferedImage render() { ret imageFromFunction(w, h, (x, y) -> rgbPixel(x, y, x+1, y+1) | fullAlphaMask()); } double getPixel(Rect r, int channel) { ret getPixel(r.x, r.y, r.x2(), r.y2(), channel); } double getPixel(int channel) { ret getPixel(0, 0, w, h, channel); } // return value ranges from 0 to 1 (usually) double getPixel(int x1, int y1, int x2, int y2, int channel) { ret doubleRatio(rectSum(x1, y1, x2, y2, channel), (x2-x1)*(y2-y1)*255.0); } double rectSum(Rect r, int channel) { ret rectSum(r.x, r.y, r.x2(), r.y2(), channel); } double rectSum(int x1, int y1, int x2, int y2, int channel) { double bottomLeft = integralValue(x1-1, y2-1, channel); double bottomRight = integralValue(x2-1, y2-1, channel); double topLeft = integralValue(x1-1, y1-1, channel); double topRight = integralValue(x2-1, y1-1, channel); ret bottomRight-topRight-bottomLeft+topLeft; } int rgbPixel(int x1, int y1, int x2, int y2) { int r = iround(clampZeroToOne(getPixel(x1, y1, x2, y2, 0))*255); int g = iround(clampZeroToOne(getPixel(x1, y1, x2, y2, 1))*255); int b = iround(clampZeroToOne(getPixel(x1, y1, x2, y2, 2))*255); ret rgbInt(r, g, b); } double liveliness(int channel) { // optimization (but no change in semantics): // if (w <= 1 && h <= 1) ret 0; // liveliness of single pixel is 0 ret standardDeviation(map(q -> q.getPixel(channel), quadrants())); } L<IIntegralImage> descentShapes() { ret centerPlusQuadrants(); } L<IIntegralImage> centerPlusQuadrants() { int midX = w/2, midY = h/2; Rect r = rectAround(midX, midY, max(midX/2, 1), max(midY/2, 1)); ret itemPlusList(clip(r), quadrants()); } L<IIntegralImage> quadrants() { if (w <= 1 && h <= 1) null; // let's really not have quadrants of a single pixel int midX = w/2, midY = h/2; ret mapLL clip( rect(0, 0, max(midX, 1), max(midY, 1)), rect(midX, 0, w-midX, max(midY, 1)), rect(0, midY, max(midX, 1), h-midY), rect(midX, midY, w-midX, h-midY) ); } IIntegralImage liveliestQuadrant(int channel) { ret highestBy(q -> q.liveliness(channel), quadrants()); } Clip clip(Rect r) { ret new Clip(this, r); } Clip clip(int x1, int y1, int w, int h) { ret clip(rect(x1, y1, w, h)); } Rect positionInImage(IIntegralImage mainImage) { if (this == mainImage) ret rect(0, 0, w, h); null; } } // virtual clip of an integral image class Clip extends IIntegralImage { IIntegralImage fullImage; int x1, y1; *(IIntegralImage *fullImage, Rect r) { x1 = r.x; y1 = r.y; w = r.w; h = r.h; } *(IIntegralImage *fullImage, int *x1, int *y1, int *w, int *h) {} public double integralValue(int x, int y, int channel) { ret fullImage.integralValue(x+x1, y+y1, channel); } // don't clip a clip - be smarter than that! Clip clip(Rect r) { ret new Clip(fullImage, translateRect(r, x1, y1)); } Rect rectInImage() { ret rect(x1, y1, w, h); } Rect positionInImage(IIntegralImage mainImage) { try object Rect r = super.positionInImage(mainImage); if (fullImage == mainImage) ret rect(x1, y1, w, h); null; } toString { ret rectInImage() + " in " + fullImage; } } class IntegralImage extends IIntegralImage { int[] data; *(BufferedImage img) { w = img.getWidth(); h = img.getHeight(); if (longMul(w, h) > 8000000) fail("Image too big: " + w + "*" + h); int[] pixels = pixelsOfBufferedImage(img); data = new int[w*h*3]; int i = 0, j = 0; int[] sum = new[3]; for y to h: { for c to 3: sum[c] = 0; for x to w: { int rgb = pixels[j++] & 0xFFFFFF; for c to 3: { data[i] = (sum[c] += rgb >> 16); if (y > 0) data[i] += data[i-w*3]; i++; rgb = (rgb << 8) & 0xFFFFFF; } } } } public double integralValue(int x, int y, Channel channel) { if (channel == grayscale) ret doubleAvg(countIterator(3, c -> integralValue(x, y, c))); ret x < 0 || y < 0 ? 0 : data[(min(y, h-1)*w+min(x, w-1))*3+channel]; } } run { mainImage = new IntegralImage(inputImage); inputImage = null; // save space print(liveliness := mainImage.liveliness(grayscale)); IIntegralImage clip = mainImage; clip = applyUntilEqual_goOneBackOnNull(c -> c.liveliestQuadrant(grayscale), clip); print(liveliestPoint := clip); showImageWithSelection(mainImage.render(), growRect(10, clip.positionInImage(mainImage))); } }
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: | #1032203 |
Snippet name: | Minimal Recognizer [backup] |
Eternal ID of this version: | #1032203/1 |
Text MD5: | c343d8e8b52eb8d5426a77a556abdbf4 |
Transpilation MD5: | 83cb647ad179ffbb182f80cdcc0f51f9 |
Author: | stefan |
Category: | |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2021-08-20 05:09:28 |
Source code size: | 5425 bytes / 168 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 141 / 190 |
Referenced in: | -