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
17 * private Point readEyes(@LOC("IN") Image image, @LOC("IN") Rectangle2D rect) {
18 @LOC("OUT") EyeDetector ed = new EyeDetector(image, rect);
19 return ed.detectEye();
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with LEA. If not, see <http://www.gnu.org/licenses/>.
29 @LATTICE("C<CS,CS<CUR,CUR<PREV,PREV<SIZE,PREV*,C*,CUR*")
30 @METHODDEFAULT("OUT<THIS,THIS<IN,THISLOC=THIS,RETURNLOC=OUT")
31 public class ClassifierTree {
34 private Classifier classifiers[];
48 @LATTICE("THIS<IN,THISLOC=THIS")
49 public ClassifierTree(@LOC("IN") int size) {
51 classifiers = new Classifier[size];
58 public void addClassifier(@LOC("IN") int idx, @LOC("IN") Classifier c) {
63 * Locates a face by searching radial starting at the last known position. If lastCoordinates are
64 * null we simply start in the center of the image.
66 * TODO: This method could quite possible be tweaked so that face recognition would be much faster
69 * the image to process
70 * @param lastCoordinates
71 * the last known coordinates or null if unknown
72 * @return an rectangle representing the actual face position on success or null if no face could
75 @LATTICE("OUT<CXY,CXY<THIS,THIS<V,V<IMG,IMG<C,C<IN,C*,V*,FACTOR*,CXY*,THISLOC=THIS,RETURNLOC=OUT,GLOBALLOC=IN")
76 public void locateFaceRadial(@LOC("IN") Image smallImage) {
78 @LOC("THIS,ClassifierTree.CUR") double px = x;
79 @LOC("THIS,ClassifierTree.CUR") double py = y;
80 @LOC("THIS,ClassifierTree.CUR") double pwidth = width;
81 @LOC("THIS,ClassifierTree.CUR") double pheight = height;
88 @LOC("IMG") IntegralImageData imageData = new IntegralImageData(smallImage);
89 @LOC("IN") float originalImageFactor = 1;
92 // if we don't have a last coordinate we just begin in the center
93 @LOC("THIS,ClassifierTree.PREV") int smallImageMaxDimension =
94 Math.min(smallImage.getWidth(), smallImage.getHeight());
96 px = (smallImage.getWidth() - smallImageMaxDimension) / 2.0;
97 py = (smallImage.getHeight() - smallImageMaxDimension) / 2.0;
98 pwidth = smallImageMaxDimension;
99 pheight = smallImageMaxDimension;
101 // first we have to scale the last coodinates back relative to the resized
103 px = px * (1 / originalImageFactor);
104 py = py * (1 / originalImageFactor);
105 pwidth = pwidth * (1 / originalImageFactor);
106 pheight = pheight * (1 / originalImageFactor);
110 @LOC("THIS,ClassifierTree.CUR") float startFactor = (float) (pwidth / 100.0f);
112 // first we calculate the maximum scale factor for our 200x200 image
113 @LOC("THIS,ClassifierTree.CUR") float maxScaleFactor =
114 Math.min(imageData.getWidth() / 100f, imageData.getHeight() / 100f);
115 // maxScaleFactor = 1.0f;
117 // we simply won't recognize faces that are smaller than 40x40 px
118 @LOC("THIS,ClassifierTree.CUR") float minScaleFactor = 0.5f;
120 @LOC("THIS,ClassifierTree.CUR") float maxScaleDifference =
121 Math.max(Math.abs(maxScaleFactor - startFactor), Math.abs(minScaleFactor - startFactor));
123 // border for faceYes-possibility must be greater that that
124 @LOC("THIS,ClassifierTree.CUR") float maxBorder = 0.999f;
126 @LOC("THIS,ClassifierTree.CUR") int startPosX = (int) px;
127 @LOC("THIS,ClassifierTree.CUR") int startPosY = (int) py;
129 @LOC("THIS,ClassifierTree.CUR") int loopidx = 0;
130 TERMINATE: for (@LOC("THIS,ClassifierTree.CUR") float factorDiff = 0.0f; Math.abs(factorDiff) <= maxScaleDifference; factorDiff =
131 (factorDiff + sgn(factorDiff) * 0.1f) * -1 // we alternate between
132 // negative and positiv
136 if (++loopidx > 1000) {
144 @LOC("THIS,ClassifierTree.CUR") float factor = startFactor + factorDiff;
145 if (factor > maxScaleFactor || factor < minScaleFactor)
148 // now we calculate the actualDimmension
149 @LOC("THIS,ClassifierTree.CUR") int actualDimmension = (int) (100 * factor);
150 @LOC("THIS,ClassifierTree.CUR") int maxX = imageData.getWidth() - actualDimmension;
151 @LOC("THIS,ClassifierTree.CUR") int maxY = imageData.getHeight() - actualDimmension;
153 @LOC("THIS,ClassifierTree.CUR") int maxDiffX =
154 Math.max(Math.abs(startPosX - maxX), startPosX);
155 @LOC("THIS,ClassifierTree.CUR") int maxDiffY =
156 Math.max(Math.abs(startPosY - maxY), startPosY);
158 @LOC("THIS,ClassifierTree.CUR") int xidx = 0;
159 TERMINATE: for (@LOC("THIS,ClassifierTree.CUR") float xDiff = 0.1f; Math.abs(xDiff) <= maxDiffX; xDiff =
160 (xDiff + sgn(xDiff) * 0.5f) * -1) {
170 @LOC("THIS,ClassifierTree.CUR") int xPos = Math.round((float) (startPosX + xDiff));
172 if (xPos < 0 || xPos > maxX)
175 @LOC("THIS,ClassifierTree.CUR") int yidx = 0;
177 TERMINATE: for (@LOC("THIS,ClassifierTree.CUR") float yDiff = 0.1f; Math.abs(yDiff) <= maxDiffY; yDiff =
178 (yDiff + sgn(yDiff) * 0.5f) * -1) {
188 @LOC("THIS,ClassifierTree.CUR") int yPos = Math.round(startPosY + yDiff);
189 if (yPos < 0 || yPos > maxY)
192 // by now we should have a valid coordinate to process which we should
194 @LOC("THIS,ClassifierTree.C") boolean backToYLines = false;
195 for (@LOC("THIS,ClassifierTree.CUR") int idx = 0; idx < size; ++idx) {
196 @LOC("THIS,ClassifierTree.CUR") float borderline =
197 0.8f + (idx / (size - 1)) * (maxBorder - 0.8f);
198 if (!classifiers[idx].classifyFace(imageData, factor, xPos, yPos, borderline)) {
205 // if we reach here we have a face recognized because our image went
213 x = xPos * originalImageFactor;
214 y = yPos * originalImageFactor;
215 width = actualDimmension * originalImageFactor;
216 height = actualDimmension * originalImageFactor;
225 // System.out.println("Time: "+(System.currentTimeMillis()-timeStart)+"ms");
230 @LATTICE("OUT<IN,OUT<P,P<THIS,THISLOC=THIS,RETURNLOC=OUT")
232 private static int sgn(@LOC("IN") float value) {
233 return (value < 0 ? -1 : (value > 0 ? +1 : 1));
236 @LATTICE("OUT<P,P<ED,ED<V,V<THIS,THIS<IN,V*,THISLOC=THIS,RETURNLOC=OUT,GLOBALLOC=IN")
237 public FaceAndEyePosition getEyePosition(@LOC("IN") Image image) {
242 @LOC("IN") float originalImageFactor = 1;
244 locateFaceRadial(image);
246 if (width > image.getWidth() || height > image.getHeight()) {
250 @LOC("OUT") EyePosition eyePosition = null;
253 @LOC("ED") EyeDetector ed = new EyeDetector(image, x, y, width, height);
254 @LOC("P") Point point = ed.detectEye();
256 eyePosition = new EyePosition(point.getX(), point.getY());
260 System.out.println("eyePosition=" + eyePosition);
262 @LOC("OUT") FaceAndEyePosition fep = new FaceAndEyePosition(x, y, width, height, eyePosition);