// U for stands for the "Ultra-Fast Recognition" family of recognizers sclass URecognizer { IIntegralImage image; *() {} *(IIntegralImage *image) {} *(BufferedImage image) { this.image = IntegralImage(image); } // returns 0, 1 or 2 int posterizeToInt(double x) { ret ifloor(clampZeroToOne(x)*3); } // returns 0, .5 or 1 double posterizeInPlace(double x) { ret posterizeToInt(x)*.5; } class Cell { // Which part of the image we are looking at (x/y) // dimension 0 = x, dimension 1 = y DoubleRange[] clip = new[2]; // color transformations in place DoubleRange[] colorClips = new[3]; Cell copy() { ret copyTo(new Cell); } Cell copyTo(Cell c) { arrayCopy(clip, c.clip); arrayCopy(colorClips, c.colorClips); ret c; } DoubleRect rawDoubleRect() { ret doubleRectFromRanges(clip[0], clip[1]); } Rect rectInImage() { ret rectMinSize(1, 1, toRect_floor(doubleRectFromRanges(clip[0], clip[1]))); } // uses full pixels to match the pixel queries double screenArea() { ret rectArea(rectInImage()); } // get color of cell double color(int channel) { double color = ii_averageBrightnessOfArea(image, rectInImage(), channel); ret transformBetweenDoubleRanges(color, colorClips[channel], zeroToOne()); } // unposterized color of all 3 channels double[] colors() { double[] c = new[3]; for i to 3: c[i] = color(i); ret c; } RGB rgb aka averageColor() { ret RGB(colors()); } toString { ret rectInImage() + ", color: " + map(asList(colors()), x -> formatDoubleX(x, 4)); } int posterizedIntColor(int channel) { ret posterizeToInt(color(channel)); } // get sub-cells Cell[] cellArray(int n) { ret new Cell[n]; } Cell[] split(int dimension, int n default 3) { Cell[] cells = cellArray(n); for i to n: cells[i] = slice(dimension, doubleRatio(i, n), doubleRatio(i+1, n)); ret cells; } Cell slice(int dimension, double a, double b) { Cell c = copy(); c.clip[dimension] = transformBetweenDoubleRanges(DoubleRange(a, b), zeroToOne(), clip[dimension]); ret c; } // rect is within 0 to 1 in both dimensions Cell clip(DoubleRect r) { ret slice(0, r.x1(), r.x2()).slice(1, r.y1(), r.y2()); } // index = 0 to 2 Cell third(int dimension, int index) { ret slice(dimension, index/3.0, (index+1)/3.0); } // convert to B/W BWCell channel(int channel) { new BWCell c; copyTo(c); c.colorToBW = rgb -> rgb[channel]; ret c; } BWCell brightness() { new BWCell c; copyTo(c); ret c; } double averageBrightness aka get() { ret brightness().color(); } double getWidth() { ret clip[0].length(); } double getHeight() { ret clip[1].length(); } double pixelSum() { ret image.rectSum(rectInImage()); } } class BWCell extends Cell { IF1 colorToBW = rgb -> (rgb[0]+rgb[1]+rgb[2])/3; double color() { ret colorToBW.get(colors()); } BWCell copy() { new BWCell c; copyTo(c); ret c; } void copyTo(BWCell c) { super.copyTo(c); c.colorToBW = colorToBW; } toString { ret rectInImage() + ", Color: " + formatDoubleX(color(), 4); } // lift methods BWCell[] cellArray(int n) { ret new BWCell[n]; } BWCell slice(int dimension, double a, double b) { ret (BWCell) super.slice(dimension, a, b); } BWCell[] split(int dimension) { ret (BWCell[]) super.split(dimension); } BWCell third(int dimension, int index) { ret (BWCell) super.third(dimension, index); } } // make the root cell Cell rootCell aka cell() { new Cell c; c.clip[0] = doubleRange(0, image.getWidth()); c.clip[1] = doubleRange(0, image.getHeight()); for i to 3: c.colorClips[i] = doubleRange(0, 255); ret c; } int outlierPosition(BWCell[] cells) { double a = cells[0].color(); double b = cells[1].color(); double c = cells[2].color(); double ab = abs(a-b), bc = abs(b-c), ac = abs(a-c); if (bc < min(ab, ac)) ret 0; // b and c close together if (ac < min(ab, bc)) ret 1; // a and c close together if (ab < min(ac, bc)) ret 2; // a and b close together ret -1; // unknown } }