8c3dfffef7ad8dc697a67bae018b6ffbdb426357
[IRC.git] / Robust / src / Benchmarks / SSJava / EyeTracking / ClassifierTree.java
1 /*
2  * Copyright 2009 (c) Florian Frankenberger (darkblue.de)
3  * 
4  * This file is part of LEA.
5  * 
6  * LEA is free software: you can redistribute it and/or modify it under the
7  * terms of the GNU Lesser General Public License as published by the Free
8  * Software Foundation, either version 3 of the License, or (at your option) any
9  * later version.
10  * 
11  * LEA is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13  * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
14  * details.
15  * 
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with LEA. If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /**
21  * 
22  * @author Florian
23  */
24 public class ClassifierTree {
25
26   private ArrayList classifiers;
27
28   public ClassifierTree() {
29     classifiers = new ArrayList();
30   }
31
32   public void addClassifier(Classifier c) {
33     classifiers.add(c);
34   }
35
36   // public static BufferedImage resizeImageFittingInto(BufferedImage image, int
37   // dimension) {
38   //
39   // int newHeight = 0;
40   // int newWidth = 0;
41   // float factor = 0;
42   // if (image.getWidth() > image.getHeight()) {
43   // factor = dimension / (float) image.getWidth();
44   // newWidth = dimension;
45   // newHeight = (int) (factor * image.getHeight());
46   // } else {
47   // factor = dimension / (float) image.getHeight();
48   // newHeight = dimension;
49   // newWidth = (int) (factor * image.getWidth());
50   // }
51   //
52   // if (factor > 1) {
53   // BufferedImageOp op = new
54   // ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
55   // BufferedImage tmpImage = op.filter(image, null);
56   //
57   // return tmpImage;
58   // }
59   //
60   // BufferedImage resizedImage = new BufferedImage(newWidth, newHeight,
61   // BufferedImage.TYPE_INT_RGB);
62   //
63   // Graphics2D g2D = resizedImage.createGraphics();
64   // g2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
65   // RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
66   //
67   // g2D.drawImage(image, 0, 0, newWidth - 1, newHeight - 1, 0, 0,
68   // image.getWidth() - 1,
69   // image.getHeight() - 1, null);
70   //
71   // BufferedImageOp op = new
72   // ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
73   // BufferedImage tmpImage = op.filter(resizedImage, null);
74   //
75   // return tmpImage;
76   // }
77   //
78   // /**
79   // * Image should have 100x100px and should be in b/w
80   // *
81   // * @param image
82   // */
83   // public void learn(BufferedImage image, boolean isFace) {
84   // IntegralImageData imageData = new IntegralImageData(image);
85   // for (Classifier classifier : this.classifiers) {
86   // classifier.learn(imageData, isFace);
87   // }
88   // }
89   //
90   // public int getLearnedFacesYes() {
91   // return this.classifiers.get(0).getLearnedFacesYes();
92   // }
93   //
94   // public int getLearnedFacesNo() {
95   // return this.classifiers.get(0).getLearnedFacesNo();
96   // }
97
98   /**
99    * Locates a face by linear iteration through all probable face positions
100    * 
101    * @deprecated use locateFaceRadial instead for improved performance
102    * @param image
103    * @return an rectangle representing the actual face position on success or
104    *         null if no face could be detected
105    */
106   // public Rectangle2D locateFace(BufferedImage image) {
107   // long timeStart = System.currentTimeMillis();
108   //
109   // int resizeTo = 600;
110   //
111   // BufferedImage smallImage = resizeImageFittingInto(image, resizeTo);
112   // IntegralImageData imageData = new IntegralImageData(smallImage);
113   //
114   // float factor = image.getWidth() / (float) smallImage.getWidth();
115   //
116   // int maxIterations = 0;
117   //
118   // // first we calculate the maximum scale factor for our 200x200 image
119   // float maxScaleFactor = Math.min(imageData.getWidth() / 100f,
120   // imageData.getHeight() / 100f);
121   //
122   // // we simply won't recognize faces that are smaller than 40x40 px
123   // float minScaleFactor = 0.5f;
124   //
125   // // border for faceYes-possibility must be greater that that
126   // float maxBorder = 0.999f;
127   //
128   // for (float scale = maxScaleFactor; scale > minScaleFactor; scale -= 0.25) {
129   // int actualDimension = (int) (scale * 100);
130   // int borderX = imageData.getWidth() - actualDimension;
131   // int borderY = imageData.getHeight() - actualDimension;
132   // for (int x = 0; x <= borderX; ++x) {
133   // yLines: for (int y = 0; y <= borderY; ++y) {
134   //
135   // for (int iterations = 0; iterations < this.classifiers.size();
136   // ++iterations) {
137   // Classifier classifier = this.classifiers.get(iterations);
138   //
139   // float borderline =
140   // 0.8f + (iterations / this.classifiers.size() - 1) * (maxBorder - 0.8f);
141   // if (iterations > maxIterations)
142   // maxIterations = iterations;
143   // if (!classifier.classifyFace(imageData, scale, x, y, borderline)) {
144   // continue yLines;
145   // }
146   // }
147   //
148   // // if we reach here we have a face recognized because our image went
149   // // through all
150   // // classifiers
151   //
152   // Rectangle2D faceRect =
153   // new Rectangle2D.Float(x * factor, y * factor, actualDimension * factor,
154   // actualDimension * factor);
155   //
156   // System.out.println("Time: " + (System.currentTimeMillis() - timeStart) +
157   // "ms");
158   // return faceRect;
159   //
160   // }
161   // }
162   // }
163   //
164   // return null;
165   // }
166
167   /**
168    * Locates a face by searching radial starting at the last known position. If
169    * lastCoordinates are null we simply start in the center of the image.
170    * <p>
171    * TODO: This method could quite possible be tweaked so that face recognition
172    * would be much faster
173    * 
174    * @param image
175    *          the image to process
176    * @param lastCoordinates
177    *          the last known coordinates or null if unknown
178    * @return an rectangle representing the actual face position on success or
179    *         null if no face could be detected
180    */
181
182   public Rectangle2D locateFaceRadial(Image smallImage, Rectangle2D lastCoordinates) {
183
184     IntegralImageData imageData = new IntegralImageData(smallImage);
185     float originalImageFactor = 1;
186
187     if (lastCoordinates == null) {
188       // if we don't have a last coordinate we just begin in the center
189       int smallImageMaxDimension = Math.min(smallImage.getWidth(), smallImage.getHeight());
190       lastCoordinates =
191           new Rectangle2D((smallImage.getWidth() - smallImageMaxDimension) / 2.0,
192               (smallImage.getHeight() - smallImageMaxDimension) / 2.0, smallImageMaxDimension,
193               smallImageMaxDimension);
194 //      System.out.println("lastCoordinates=" + lastCoordinates);
195     } else {
196       // first we have to scale the last coodinates back relative to the resized
197       // image
198       lastCoordinates =
199           new Rectangle2D((lastCoordinates.getX() * (1 / originalImageFactor)),
200               (lastCoordinates.getY() * (1 / originalImageFactor)),
201               (lastCoordinates.getWidth() * (1 / originalImageFactor)),
202               (lastCoordinates.getHeight() * (1 / originalImageFactor)));
203     }
204
205     float startFactor = (float) (lastCoordinates.getWidth() / 100.0f);
206
207     // first we calculate the maximum scale factor for our 200x200 image
208     float maxScaleFactor = Math.min(imageData.getWidth() / 100f, imageData.getHeight() / 100f);
209     // maxScaleFactor = 1.0f;
210
211     // we simply won't recognize faces that are smaller than 40x40 px
212     float minScaleFactor = 0.5f;
213
214     float maxScaleDifference =
215         Math.max(Math.abs(maxScaleFactor - startFactor), Math.abs(minScaleFactor - startFactor));
216
217     // border for faceYes-possibility must be greater that that
218     float maxBorder = 0.999f;
219
220     int startPosX = (int) lastCoordinates.getX();
221     int startPosY = (int) lastCoordinates.getX();
222
223     for (float factorDiff = 0.0f; Math.abs(factorDiff) <= maxScaleDifference; factorDiff =
224         (factorDiff + sgn(factorDiff) * 0.1f) * -1 // we alternate between
225                                                    // negative and positiv
226                                                    // factors
227     ) {
228
229       float factor = startFactor + factorDiff;
230 //      System.out.println("factor=" + factor);
231       if (factor > maxScaleFactor || factor < minScaleFactor)
232         continue;
233
234       // now we calculate the actualDimmension
235       int actualDimmension = (int) (100 * factor);
236       int maxX = imageData.getWidth() - actualDimmension;
237       int maxY = imageData.getHeight() - actualDimmension;
238
239       int maxDiffX = Math.max(Math.abs(startPosX - maxX), startPosX);
240       int maxDiffY = Math.max(Math.abs(startPosY - maxY), startPosY);
241
242       for (float xDiff = 0.1f; Math.abs(xDiff) <= maxDiffX; xDiff =
243           (xDiff + sgn(xDiff) * 0.5f) * -1) {
244         int xPos = Math.round((float) (startPosX + xDiff));
245         if (xPos < 0 || xPos > maxX)
246           continue;
247
248         // yLines:
249         for (float yDiff = 0.1f; Math.abs(yDiff) <= maxDiffY; yDiff =
250             (yDiff + sgn(yDiff) * 0.5f) * -1) {
251           int yPos = Math.round(startPosY + yDiff);
252           if (yPos < 0 || yPos > maxY)
253             continue;
254
255           // by now we should have a valid coordinate to process which we should
256           // do now
257           boolean backToYLines = false;
258           for (int iterations = 0; iterations < classifiers.size(); ++iterations) {
259             Classifier classifier = (Classifier) classifiers.get(iterations);
260
261             float borderline = 0.8f + (iterations / (classifiers.size() - 1)) * (maxBorder - 0.8f);
262             if (!classifier.classifyFace(imageData, factor, xPos, yPos, borderline)) {
263 //              System.out.println("continue yLines; ");
264               backToYLines = true;
265               break;
266               // continue yLines;              
267             }
268           }
269
270           
271           // if we reach here we have a face recognized because our image went
272           // through all
273           // classifiers
274
275           if (backToYLines) {
276             continue;
277           }
278           Rectangle2D faceRect =
279               new Rectangle2D(xPos * originalImageFactor, yPos * originalImageFactor,
280                   actualDimmension * originalImageFactor, actualDimmension * originalImageFactor);
281
282           return faceRect;
283
284         }
285
286       }
287
288     }
289
290     // System.out.println("Time: "+(System.currentTimeMillis()-timeStart)+"ms");
291     return null;
292
293   }
294
295   // public Rectangle2D locateFaceRadial(BufferedImage image, Rectangle2D
296   // lastCoordinates) {
297   //
298   // int resizeTo = 600;
299   //
300   // BufferedImage smallImage = resizeImageFittingInto(image, resizeTo);
301   // float originalImageFactor = image.getWidth() / (float)
302   // smallImage.getWidth();
303   // IntegralImageData imageData = new IntegralImageData(smallImage);
304   //
305   // if (lastCoordinates == null) {
306   // // if we don't have a last coordinate we just begin in the center
307   // int smallImageMaxDimension = Math.min(smallImage.getWidth(),
308   // smallImage.getHeight());
309   // lastCoordinates =
310   // new Rectangle2D.Float((smallImage.getWidth() - smallImageMaxDimension) /
311   // 2.0f,
312   // (smallImage.getHeight() - smallImageMaxDimension) / 2.0f,
313   // smallImageMaxDimension,
314   // smallImageMaxDimension);
315   // } else {
316   // // first we have to scale the last coodinates back relative to the resized
317   // // image
318   // lastCoordinates =
319   // new Rectangle2D.Float((float) (lastCoordinates.getX() * (1 /
320   // originalImageFactor)),
321   // (float) (lastCoordinates.getY() * (1 / originalImageFactor)),
322   // (float) (lastCoordinates.getWidth() * (1 / originalImageFactor)),
323   // (float) (lastCoordinates.getHeight() * (1 / originalImageFactor)));
324   // }
325   //
326   // float startFactor = (float) (lastCoordinates.getWidth() / 100.0f);
327   //
328   // // first we calculate the maximum scale factor for our 200x200 image
329   // float maxScaleFactor = Math.min(imageData.getWidth() / 100f,
330   // imageData.getHeight() / 100f);
331   // // maxScaleFactor = 1.0f;
332   //
333   // // we simply won't recognize faces that are smaller than 40x40 px
334   // float minScaleFactor = 0.5f;
335   //
336   // float maxScaleDifference =
337   // Math.max(Math.abs(maxScaleFactor - startFactor), Math.abs(minScaleFactor -
338   // startFactor));
339   //
340   // // border for faceYes-possibility must be greater that that
341   // float maxBorder = 0.999f;
342   //
343   // int startPosX = (int) lastCoordinates.getX();
344   // int startPosY = (int) lastCoordinates.getX();
345   //
346   // for (float factorDiff = 0.0f; Math.abs(factorDiff) <= maxScaleDifference;
347   // factorDiff =
348   // (factorDiff + sgn(factorDiff) * 0.1f) * -1 // we alternate between
349   // // negative and positiv
350   // // factors
351   // ) {
352   //
353   // float factor = startFactor + factorDiff;
354   // if (factor > maxScaleFactor || factor < minScaleFactor)
355   // continue;
356   //
357   // // now we calculate the actualDimmension
358   // int actualDimmension = (int) (100 * factor);
359   // int maxX = imageData.getWidth() - actualDimmension;
360   // int maxY = imageData.getHeight() - actualDimmension;
361   //
362   // int maxDiffX = Math.max(Math.abs(startPosX - maxX), startPosX);
363   // int maxDiffY = Math.max(Math.abs(startPosY - maxY), startPosY);
364   //
365   // for (float xDiff = 0.1f; Math.abs(xDiff) <= maxDiffX; xDiff =
366   // (xDiff + sgn(xDiff) * 0.5f) * -1) {
367   // int xPos = Math.round(startPosX + xDiff);
368   // if (xPos < 0 || xPos > maxX)
369   // continue;
370   //
371   // yLines: for (float yDiff = 0.1f; Math.abs(yDiff) <= maxDiffY; yDiff =
372   // (yDiff + sgn(yDiff) * 0.5f) * -1) {
373   // int yPos = Math.round(startPosY + yDiff);
374   // if (yPos < 0 || yPos > maxY)
375   // continue;
376   //
377   // // by now we should have a valid coordinate to process which we should
378   // // do now
379   // for (int iterations = 0; iterations < this.classifiers.size();
380   // ++iterations) {
381   // Classifier classifier = this.classifiers.get(iterations);
382   //
383   // float borderline =
384   // 0.8f + (iterations / (this.classifiers.size() - 1)) * (maxBorder - 0.8f);
385   //
386   // if (!classifier.classifyFace(imageData, factor, xPos, yPos, borderline)) {
387   // continue yLines;
388   // }
389   // }
390   //
391   // // if we reach here we have a face recognized because our image went
392   // // through all
393   // // classifiers
394   //
395   // Rectangle2D faceRect =
396   // new Rectangle2D.Float(xPos * originalImageFactor, yPos *
397   // originalImageFactor,
398   // actualDimmension * originalImageFactor, actualDimmension *
399   // originalImageFactor);
400   //
401   // return faceRect;
402   //
403   // }
404   //
405   // }
406   //
407   // }
408   //
409   // //
410   // System.out.println("Time: "+(System.currentTimeMillis()-timeStart)+"ms");
411   // return null;
412   //
413   // }
414
415   // public List<Classifier> getClassifiers() {
416   // return new ArrayList<Classifier>(this.classifiers);
417   // }
418   //
419   // public static void saveToXml(OutputStream out, ClassifierTree tree) throws
420   // IOException {
421   // PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8"));
422   // writer.write(xStream.toXML(tree));
423   // writer.close();
424   // }
425   //
426   // public static ClassifierTree loadFromXml(InputStream in) throws IOException
427   // {
428   // Reader reader = new InputStreamReader(in, "UTF-8");
429   // StringBuilder sb = new StringBuilder();
430   //
431   // char[] buffer = new char[1024];
432   // int read = 0;
433   // do {
434   // read = reader.read(buffer);
435   // if (read > 0) {
436   // sb.append(buffer, 0, read);
437   // }
438   // } while (read > -1);
439   // reader.close();
440   //
441   // return (ClassifierTree) xStream.fromXML(sb.toString());
442   // }
443
444   private static int sgn(float value) {
445     return (value < 0 ? -1 : (value > 0 ? +1 : 1));
446   }
447
448 }