4 * Copyright 2009 (c) Florian Frankenberger (darkblue.de)
6 * This file is part of LEA.
8 * LEA is free software: you can redistribute it and/or modify it under the
9 * terms of the GNU Lesser General Public License as published by the Free
10 * Software Foundation, either version 3 of the License, or (at your option) any
13 * LEA is distributed in the hope that it will be useful, but WITHOUT ANY
14 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with LEA. If not, see <http://www.gnu.org/licenses/>.
27 public class ClassifierTree {
29 private Classifier classifiers[];
31 public ClassifierTree(int size) {
32 classifiers = new Classifier[size];
35 public void addClassifier(int idx, Classifier c) {
40 * Locates a face by searching radial starting at the last known position. If lastCoordinates are
41 * null we simply start in the center of the image.
43 * TODO: This method could quite possible be tweaked so that face recognition would be much faster
46 * the image to process
47 * @param lastCoordinates
48 * the last known coordinates or null if unknown
49 * @return an rectangle representing the actual face position on success or null if no face could
53 public Rectangle2D locateFaceRadial(Image smallImage, Rectangle2D lastCoordinates) {
55 IntegralImageData imageData = new IntegralImageData(smallImage);
56 float originalImageFactor = 1;
58 if (lastCoordinates == null) {
59 // if we don't have a last coordinate we just begin in the center
60 int smallImageMaxDimension = Math.min(smallImage.getWidth(), smallImage.getHeight());
62 new Rectangle2D((smallImage.getWidth() - smallImageMaxDimension) / 2.0,
63 (smallImage.getHeight() - smallImageMaxDimension) / 2.0, smallImageMaxDimension,
64 smallImageMaxDimension);
65 // System.out.println("lastCoordinates=" + lastCoordinates);
67 // first we have to scale the last coodinates back relative to the resized
70 new Rectangle2D((lastCoordinates.getX() * (1 / originalImageFactor)),
71 (lastCoordinates.getY() * (1 / originalImageFactor)),
72 (lastCoordinates.getWidth() * (1 / originalImageFactor)),
73 (lastCoordinates.getHeight() * (1 / originalImageFactor)));
76 float startFactor = (float) (lastCoordinates.getWidth() / 100.0f);
78 // first we calculate the maximum scale factor for our 200x200 image
79 float maxScaleFactor = Math.min(imageData.getWidth() / 100f, imageData.getHeight() / 100f);
80 // maxScaleFactor = 1.0f;
82 // we simply won't recognize faces that are smaller than 40x40 px
83 float minScaleFactor = 0.5f;
85 float maxScaleDifference = Math.max(Math.abs(maxScaleFactor - startFactor), Math.abs(minScaleFactor - startFactor));
87 // border for faceYes-possibility must be greater that that
88 float maxBorder = 0.999f;
90 int startPosX = (int) lastCoordinates.getX();
91 int startPosY = (int) lastCoordinates.getX();
94 TERMINATE: for (float factorDiff = 0.0f; Math.abs(factorDiff) <= maxScaleDifference; factorDiff =
95 (factorDiff + sgn(factorDiff) * 0.1f) * -1 // we alternate between
96 // negative and positiv
100 if (++loopidx > 1000) {
104 float factor = startFactor + factorDiff;
105 if (factor > maxScaleFactor || factor < minScaleFactor)
108 // now we calculate the actualDimmension
109 int actualDimmension = (int) (100 * factor);
110 int maxX = imageData.getWidth() - actualDimmension;
111 int maxY = imageData.getHeight() - actualDimmension;
113 int maxDiffX = Math.max(Math.abs(startPosX - maxX), startPosX);
114 int maxDiffY = Math.max(Math.abs(startPosY - maxY), startPosY);
117 TERMINATE: for (float xDiff = 0.1f; Math.abs(xDiff) <= maxDiffX; xDiff =
118 (xDiff + sgn(xDiff) * 0.5f) * -1) {
124 int xPos = Math.round((float) (startPosX + xDiff));
126 if (xPos < 0 || xPos > maxX)
131 TERMINATE: for (float yDiff = 0.1f; Math.abs(yDiff) <= maxDiffY; yDiff =
132 (yDiff + sgn(yDiff) * 0.5f) * -1) {
138 int yPos = Math.round(startPosY + yDiff);
139 if (yPos < 0 || yPos > maxY)
142 // by now we should have a valid coordinate to process which we should
144 boolean backToYLines = false;
145 for (int idx = 0; idx < classifiers.length; ++idx) {
146 float borderline = 0.8f + (idx / (classifiers.length - 1)) * (maxBorder - 0.8f);
147 if (!classifiers[idx].classifyFace(imageData, factor, xPos, yPos, borderline)) {
154 // if we reach here we have a face recognized because our image went
162 Rectangle2D faceRect = new Rectangle2D(xPos * originalImageFactor, yPos * originalImageFactor, actualDimmension * originalImageFactor, actualDimmension * originalImageFactor);
172 // System.out.println("Time: "+(System.currentTimeMillis()-timeStart)+"ms");
177 private static int sgn(float value) {
178 return (value < 0 ? -1 : (value > 0 ? +1 : 1));