changes: 1) fixes problems in the original EyeTracking benchmark 2) fix a bug in...
[IRC.git] / Robust / src / Benchmarks / SSJava / EyeTrackingInfer / ClassifierTree.java
1 import SSJava.PCLOC;
2
3 /*
4  * Copyright 2009 (c) Florian Frankenberger (darkblue.de)
5  * 
6  * This file is part of LEA.
7  * 
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
11  * later version.
12  * 
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
16  * details.
17  *   private Point readEyes( Image image,  Rectangle2D rect) {
18   EyeDetector ed = new EyeDetector(image, rect);
19  return ed.detectEye();
20  }
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/>.
23  */
24
25 /**
26  * 
27  * @author Florian
28  */
29
30
31 public class ClassifierTree {
32
33   
34   private Classifier[] classifiers;
35   
36   double x;
37   
38   double y;
39   
40   double width;
41   
42   double height;
43
44   
45   int size;
46
47   
48   public ClassifierTree( int size) {
49     this.size = size;
50     classifiers = new Classifier[size];
51     x = -1;
52     y = -1;
53     width = -1;
54     height = -1;
55   }
56
57   public void addClassifier( int idx,  Classifier c) {
58     classifiers[idx] = c;
59   }
60
61   
62   /**
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.
65    * <p>
66    * TODO: This method could quite possible be tweaked so that face recognition would be much faster
67    * 
68    * @param image
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
73    *         be detected
74    */
75   
76   public void locateFaceRadial( Image smallImage) {
77
78      double px = x;
79      double py = y;
80      double pwidth = width;
81      double pheight = height;
82
83     x = -1;
84     y = -1;
85     width = -1;
86     height = -1;
87
88      IntegralImageData imageData = new IntegralImageData(smallImage);
89      float originalImageFactor = 1;
90     if (px == -1) {
91       // if(true){
92       // if we don't have a last coordinate we just begin in the center
93        int smallImageMaxDimension = Math.min(smallImage.getWidth(), smallImage.getHeight());
94
95       px = (smallImage.getWidth() - smallImageMaxDimension) / 2.0;
96       py = (smallImage.getHeight() - smallImageMaxDimension) / 2.0;
97       pwidth = smallImageMaxDimension;
98       pheight = smallImageMaxDimension;
99     } else {
100       // first we have to scale the last coodinates back relative to the resized
101       // image
102       px = px * (1 / originalImageFactor);
103       py = py * (1 / originalImageFactor);
104       pwidth = pwidth * (1 / originalImageFactor);
105       pheight = pheight * (1 / originalImageFactor);
106     }
107
108
109      float startFactor = (float) (pwidth / 100.0f);
110
111     // first we calculate the maximum scale factor for our 200x200 image
112      float maxScaleFactor = Math.min(imageData.getWidth() / 100f, imageData.getHeight() / 100f);
113     // maxScaleFactor = 1.0f;
114
115     // we simply won't recognize faces that are smaller than 40x40 px
116      float minScaleFactor = 0.5f;
117
118      float maxScaleDifference = Math.max(Math.abs(maxScaleFactor - startFactor), Math.abs(minScaleFactor - startFactor));
119
120     // border for faceYes-possibility must be greater that that
121      float maxBorder = 0.999f;
122
123      int startPosX = (int) px;
124      int startPosY = (int) py;
125
126      int loopidx = 0;
127     TERMINATE: for ( float factorDiff = 0.0f; Math.abs(factorDiff) <= maxScaleDifference; factorDiff =
128         (factorDiff + sgn(factorDiff) * 0.1f) * -1 // we alternate between
129                                                    // negative and positiv
130                                                    // factors
131     ) {
132
133       if (++loopidx > 1000) {
134         px = -1;
135         py = -1;
136         pwidth = -1;
137         pheight = -1;
138         return;
139       }
140
141        float factor = startFactor + factorDiff;
142       if (factor > maxScaleFactor || factor < minScaleFactor)
143         continue;
144
145       // now we calculate the actualDimmension
146        int actualDimmension = (int) (100 * factor);
147        int maxX = imageData.getWidth() - actualDimmension;
148        int maxY = imageData.getHeight() - actualDimmension;
149
150        int maxDiffX = Math.max(Math.abs(startPosX - maxX), startPosX);
151        int maxDiffY = Math.max(Math.abs(startPosY - maxY), startPosY);
152
153        int xidx = 0;
154       TERMINATE: for ( float xDiff = 0.1f; Math.abs(xDiff) <= maxDiffX; xDiff =
155           (xDiff + sgn(xDiff) * 0.5f) * -1) {
156
157         if (++xidx > 1000) {
158           px = -1;
159           py = -1;
160           pwidth = -1;
161           pheight = -1;
162           return;
163         }
164
165          int xPos = Math.round((float) (startPosX + xDiff));
166
167         if (xPos < 0 || xPos > maxX)
168           continue;
169
170          int yidx = 0;
171         // yLines:
172         TERMINATE: for ( float yDiff = 0.1f; Math.abs(yDiff) <= maxDiffY; yDiff =
173             (yDiff + sgn(yDiff) * 0.5f) * -1) {
174
175           if (++yidx > 1000) {
176             px = -1;
177             py = -1;
178             pwidth = -1;
179             pheight = -1;
180             return;
181           }
182
183            int yPos = Math.round(startPosY + yDiff);
184           if (yPos < 0 || yPos > maxY)
185             continue;
186
187           // by now we should have a valid coordinate to process which we should
188           // do now
189            boolean backToYLines = false;
190           for ( int idx = 0; idx < size; ++idx) {
191              float borderline = 0.8f + (idx / (size - 1)) * (maxBorder - 0.8f);
192             if (!classifiers[idx].classifyFace(imageData, factor, xPos, yPos, borderline)) {
193               backToYLines = true;
194               break;
195               // continue yLines;
196             }
197           }
198
199           // if we reach here we have a face recognized because our image went
200           // through all
201           // classifiers
202
203           if (backToYLines) {
204             continue;
205           }
206
207           x = xPos * originalImageFactor;
208           y = yPos * originalImageFactor;
209           width = actualDimmension * originalImageFactor;
210           height = actualDimmension * originalImageFactor;
211           return;
212
213         }
214
215       }
216
217     }
218
219
220   }
221
222   
223   
224   private static int sgn( float value) {
225     return (value < 0 ? -1 : (value > 0 ? +1 : 1));
226   }
227
228   
229   public FaceAndEyePosition getEyePosition( Image image) {
230     if (image == null) {
231       return null;
232     }
233
234      float originalImageFactor = 1;
235
236     locateFaceRadial(image);
237
238     if (width > image.getWidth() || height > image.getHeight()) {
239       return null;
240     }
241
242      EyePosition eyePosition = null;
243
244     if (x != -1) {
245        EyeDetector ed = new EyeDetector(image, x, y, width, height);
246        Point point = ed.detectEye();
247       if (point != null) {
248         eyePosition = new EyePosition(point.getX(), point.getY());
249       }
250     }
251
252     System.out.println("eyePosition=" + eyePosition);
253
254      FaceAndEyePosition fep = new FaceAndEyePosition(x, y, width, height, eyePosition);
255
256
257     return fep;
258   }
259
260
261 }