+import SSJava.PCLOC;
+
/*
* Copyright 2009 (c) Florian Frankenberger (darkblue.de)
*
* along with LEA. If not, see <http://www.gnu.org/licenses/>.
*/
-import java.awt.RenderingHints;
-import java.awt.color.ColorSpace;
-import java.awt.geom.Rectangle2D;
-import java.awt.image.BufferedImage;
-import java.awt.image.BufferedImageOp;
-import java.awt.image.ColorConvertOp;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.Reader;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
/**
*
* @author Florian
*/
+@LATTICE("CS<C,C*")
+@METHODDEFAULT("OUT<THIS,THIS<IN,THISLOC=THIS,RETURNLOC=OUT")
public class ClassifierTree {
- private List<Classifier> classifiers;
- private static XStream xStream = new XStream(new DomDriver());
-
- static {
- xStream.alias("ClassifierTree", ClassifierTree.class);
- xStream.alias("Classifier", Classifier.class);
- xStream.alias("ScanArea", ScanArea.class);
- }
+ @LOC("CS")
+ private Classifier classifiers[];
- public ClassifierTree(List<Classifier> classifier) {
- this.classifiers = new ArrayList<Classifier>(classifier);
- Collections.sort(this.classifiers);
+ public ClassifierTree(int size) {
+ classifiers = new Classifier[size];
}
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("ClassifierTree {\n");
- for (Classifier classifier : this.classifiers) {
- sb.append(classifier.toString());
- sb.append('\n');
- }
- sb.append("}\n");
- return sb.toString();
- }
-
- public static BufferedImage resizeImageFittingInto(BufferedImage image, int dimension) {
-
- int newHeight = 0;
- int newWidth = 0;
- float factor = 0;
- if (image.getWidth() > image.getHeight()) {
- factor = dimension / (float) image.getWidth();
- newWidth = dimension;
- newHeight = (int) (factor * image.getHeight());
- } else {
- factor = dimension / (float) image.getHeight();
- newHeight = dimension;
- newWidth = (int) (factor * image.getWidth());
- }
-
- if (factor > 1) {
- BufferedImageOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
- BufferedImage tmpImage = op.filter(image, null);
-
- return tmpImage;
- }
-
- BufferedImage resizedImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
-
- Graphics2D g2D = resizedImage.createGraphics();
- g2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
- RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
-
- g2D.drawImage(image, 0, 0, newWidth - 1, newHeight - 1, 0, 0, image.getWidth() - 1,
- image.getHeight() - 1, null);
-
- BufferedImageOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
- BufferedImage tmpImage = op.filter(resizedImage, null);
-
- return tmpImage;
- }
-
- /**
- * Image should have 100x100px and should be in b/w
- *
- * @param image
- */
- public void learn(BufferedImage image, boolean isFace) {
- IntegralImageData imageData = new IntegralImageData(image);
- for (Classifier classifier : this.classifiers) {
- classifier.learn(imageData, isFace);
- }
- }
-
- public int getLearnedFacesYes() {
- return this.classifiers.get(0).getLearnedFacesYes();
- }
-
- public int getLearnedFacesNo() {
- return this.classifiers.get(0).getLearnedFacesNo();
- }
-
- /**
- * Locates a face by linear iteration through all probable face positions
- *
- * @deprecated use locateFaceRadial instead for improved performance
- * @param image
- * @return an rectangle representing the actual face position on success or
- * null if no face could be detected
- */
- public Rectangle2D locateFace(BufferedImage image) {
- long timeStart = System.currentTimeMillis();
-
- int resizeTo = 600;
-
- BufferedImage smallImage = resizeImageFittingInto(image, resizeTo);
- IntegralImageData imageData = new IntegralImageData(smallImage);
-
- float factor = image.getWidth() / (float) smallImage.getWidth();
-
- int maxIterations = 0;
-
- // first we calculate the maximum scale factor for our 200x200 image
- float maxScaleFactor = Math.min(imageData.getWidth() / 100f, imageData.getHeight() / 100f);
-
- // we simply won't recognize faces that are smaller than 40x40 px
- float minScaleFactor = 0.5f;
-
- // border for faceYes-possibility must be greater that that
- float maxBorder = 0.999f;
-
- for (float scale = maxScaleFactor; scale > minScaleFactor; scale -= 0.25) {
- int actualDimension = (int) (scale * 100);
- int borderX = imageData.getWidth() - actualDimension;
- int borderY = imageData.getHeight() - actualDimension;
- for (int x = 0; x <= borderX; ++x) {
- yLines: for (int y = 0; y <= borderY; ++y) {
-
- for (int iterations = 0; iterations < this.classifiers.size(); ++iterations) {
- Classifier classifier = this.classifiers.get(iterations);
-
- float borderline =
- 0.8f + (iterations / this.classifiers.size() - 1) * (maxBorder - 0.8f);
- if (iterations > maxIterations)
- maxIterations = iterations;
- if (!classifier.classifyFace(imageData, scale, x, y, borderline)) {
- continue yLines;
- }
- }
-
- // if we reach here we have a face recognized because our image went
- // through all
- // classifiers
-
- Rectangle2D faceRect =
- new Rectangle2D.Float(x * factor, y * factor, actualDimension * factor,
- actualDimension * factor);
-
- System.out.println("Time: " + (System.currentTimeMillis() - timeStart) + "ms");
- return faceRect;
-
- }
- }
- }
-
- return null;
+ public void addClassifier(@LOC("IN") int idx, @LOC("IN") Classifier c) {
+ classifiers[idx] = c;
}
/**
* @return an rectangle representing the actual face position on success or
* null if no face could be detected
*/
- public Rectangle2D locateFaceRadial(BufferedImage image, Rectangle2D lastCoordinates) {
+ @LATTICE("OUT<CXY,CXY<THIS,THIS<V,V<IMG,IMG<C,C<IN,C*,V*,FACTOR*,CXY*,THISLOC=THIS,RETURNLOC=OUT,GLOBALLOC=IN")
+ @PCLOC("C")
+ public Rectangle2D locateFaceRadial(@LOC("IN") Image smallImage,
+ @LOC("THIS,ClassifierTree.C") Rectangle2D lastCoordinates) {
- int resizeTo = 600;
-
- BufferedImage smallImage = resizeImageFittingInto(image, resizeTo);
- float originalImageFactor = image.getWidth() / (float) smallImage.getWidth();
- IntegralImageData imageData = new IntegralImageData(smallImage);
+ @LOC("IMG") IntegralImageData imageData = new IntegralImageData(smallImage);
+ @LOC("IN") float originalImageFactor = 1;
if (lastCoordinates == null) {
// if we don't have a last coordinate we just begin in the center
- int smallImageMaxDimension = Math.min(smallImage.getWidth(), smallImage.getHeight());
+ @LOC("THIS,ClassifierTree.C") int smallImageMaxDimension =
+ Math.min(smallImage.getWidth(), smallImage.getHeight());
lastCoordinates =
- new Rectangle2D.Float((smallImage.getWidth() - smallImageMaxDimension) / 2.0f,
- (smallImage.getHeight() - smallImageMaxDimension) / 2.0f, smallImageMaxDimension,
+ new Rectangle2D((smallImage.getWidth() - smallImageMaxDimension) / 2.0,
+ (smallImage.getHeight() - smallImageMaxDimension) / 2.0, smallImageMaxDimension,
smallImageMaxDimension);
+ // System.out.println("lastCoordinates=" + lastCoordinates);
} else {
// first we have to scale the last coodinates back relative to the resized
// image
lastCoordinates =
- new Rectangle2D.Float((float) (lastCoordinates.getX() * (1 / originalImageFactor)),
- (float) (lastCoordinates.getY() * (1 / originalImageFactor)),
- (float) (lastCoordinates.getWidth() * (1 / originalImageFactor)),
- (float) (lastCoordinates.getHeight() * (1 / originalImageFactor)));
+ new Rectangle2D((lastCoordinates.getX() * (1 / originalImageFactor)),
+ (lastCoordinates.getY() * (1 / originalImageFactor)),
+ (lastCoordinates.getWidth() * (1 / originalImageFactor)),
+ (lastCoordinates.getHeight() * (1 / originalImageFactor)));
}
- float startFactor = (float) (lastCoordinates.getWidth() / 100.0f);
+ @LOC("THIS,ClassifierTree.C") float startFactor = (float) (lastCoordinates.getWidth() / 100.0f);
// first we calculate the maximum scale factor for our 200x200 image
- float maxScaleFactor = Math.min(imageData.getWidth() / 100f, imageData.getHeight() / 100f);
+ @LOC("THIS,ClassifierTree.C") float maxScaleFactor =
+ Math.min(imageData.getWidth() / 100f, imageData.getHeight() / 100f);
// maxScaleFactor = 1.0f;
// we simply won't recognize faces that are smaller than 40x40 px
- float minScaleFactor = 0.5f;
+ @LOC("THIS,ClassifierTree.C") float minScaleFactor = 0.5f;
- float maxScaleDifference =
+ @LOC("THIS,ClassifierTree.C") float maxScaleDifference =
Math.max(Math.abs(maxScaleFactor - startFactor), Math.abs(minScaleFactor - startFactor));
// border for faceYes-possibility must be greater that that
- float maxBorder = 0.999f;
+ @LOC("THIS,ClassifierTree.C") float maxBorder = 0.999f;
- int startPosX = (int) lastCoordinates.getX();
- int startPosY = (int) lastCoordinates.getX();
+ @LOC("THIS,ClassifierTree.C") int startPosX = (int) lastCoordinates.getX();
+ @LOC("THIS,ClassifierTree.C") int startPosY = (int) lastCoordinates.getX();
- for (float factorDiff = 0.0f; Math.abs(factorDiff) <= maxScaleDifference; factorDiff =
+ @LOC("THIS,ClassifierTree.C") int loopidx = 0;
+ TERMINATE: for (@LOC("THIS,ClassifierTree.C") float factorDiff = 0.0f; Math.abs(factorDiff) <= maxScaleDifference; factorDiff =
(factorDiff + sgn(factorDiff) * 0.1f) * -1 // we alternate between
// negative and positiv
// factors
) {
- float factor = startFactor + factorDiff;
+ if (++loopidx > 1000) {
+ return null;
+ }
+
+ @LOC("THIS,ClassifierTree.C") float factor = startFactor + factorDiff;
if (factor > maxScaleFactor || factor < minScaleFactor)
continue;
// now we calculate the actualDimmension
- int actualDimmension = (int) (100 * factor);
- int maxX = imageData.getWidth() - actualDimmension;
- int maxY = imageData.getHeight() - actualDimmension;
+ @LOC("THIS,ClassifierTree.C") int actualDimmension = (int) (100 * factor);
+ @LOC("THIS,ClassifierTree.C") int maxX = imageData.getWidth() - actualDimmension;
+ @LOC("THIS,ClassifierTree.C") int maxY = imageData.getHeight() - actualDimmension;
- int maxDiffX = Math.max(Math.abs(startPosX - maxX), startPosX);
- int maxDiffY = Math.max(Math.abs(startPosY - maxY), startPosY);
+ @LOC("THIS,ClassifierTree.C") int maxDiffX = Math.max(Math.abs(startPosX - maxX), startPosX);
+ @LOC("THIS,ClassifierTree.C") int maxDiffY = Math.max(Math.abs(startPosY - maxY), startPosY);
- for (float xDiff = 0.1f; Math.abs(xDiff) <= maxDiffX; xDiff =
+ @LOC("CXY") int xidx = 0;
+ TERMINATE: for (@LOC("CXY") float xDiff = 0.1f; Math.abs(xDiff) <= maxDiffX; xDiff =
(xDiff + sgn(xDiff) * 0.5f) * -1) {
- int xPos = Math.round(startPosX + xDiff);
+
+ if (++xidx > 1000) {
+ return null;
+ }
+
+ @LOC("CXY") int xPos = Math.round((float) (startPosX + xDiff));
+
if (xPos < 0 || xPos > maxX)
continue;
- yLines: for (float yDiff = 0.1f; Math.abs(yDiff) <= maxDiffY; yDiff =
+ @LOC("CXY") int yidx = 0;
+ // yLines:
+ TERMINATE: for (@LOC("CXY") float yDiff = 0.1f; Math.abs(yDiff) <= maxDiffY; yDiff =
(yDiff + sgn(yDiff) * 0.5f) * -1) {
- int yPos = Math.round(startPosY + yDiff);
+
+ if (++yidx > 1000) {
+ return null;
+ }
+
+ @LOC("CXY") int yPos = Math.round(startPosY + yDiff);
if (yPos < 0 || yPos > maxY)
continue;
// by now we should have a valid coordinate to process which we should
// do now
- for (int iterations = 0; iterations < this.classifiers.size(); ++iterations) {
- Classifier classifier = this.classifiers.get(iterations);
-
- float borderline =
- 0.8f + (iterations / (this.classifiers.size() - 1)) * (maxBorder - 0.8f);
-
- if (!classifier.classifyFace(imageData, factor, xPos, yPos, borderline)) {
- continue yLines;
+ @LOC("CXY") boolean backToYLines = false;
+ for (@LOC("CXY") int idx = 0; idx < classifiers.length; ++idx) {
+ @LOC("CXY") float borderline =
+ 0.8f + (idx / (classifiers.length - 1)) * (maxBorder - 0.8f);
+ if (!classifiers[idx].classifyFace(imageData, factor, xPos, yPos, borderline)) {
+ backToYLines = true;
+ break;
+ // continue yLines;
}
}
// through all
// classifiers
- Rectangle2D faceRect =
- new Rectangle2D.Float(xPos * originalImageFactor, yPos * originalImageFactor,
+ if (backToYLines) {
+ continue;
+ }
+ @LOC("OUT") Rectangle2D faceRect =
+ new Rectangle2D(xPos * originalImageFactor, yPos * originalImageFactor,
actualDimmension * originalImageFactor, actualDimmension * originalImageFactor);
return faceRect;
}
- public List<Classifier> getClassifiers() {
- return new ArrayList<Classifier>(this.classifiers);
- }
-
- public static void saveToXml(OutputStream out, ClassifierTree tree) throws IOException {
- PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8"));
- writer.write(xStream.toXML(tree));
- writer.close();
- }
-
- public static ClassifierTree loadFromXml(InputStream in) throws IOException {
- Reader reader = new InputStreamReader(in, "UTF-8");
- StringBuilder sb = new StringBuilder();
-
- char[] buffer = new char[1024];
- int read = 0;
- do {
- read = reader.read(buffer);
- if (read > 0) {
- sb.append(buffer, 0, read);
- }
- } while (read > -1);
- reader.close();
-
- return (ClassifierTree) xStream.fromXML(sb.toString());
- }
-
- private static int sgn(float value) {
+ @LATTICE("OUT<IN,OUT<P,P<THIS,THISLOC=THIS,RETURNLOC=OUT")
+ @PCLOC("P")
+ private static int sgn(@LOC("IN") float value) {
return (value < 0 ? -1 : (value > 0 ? +1 : 1));
}