passes the flow down rule
[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 @LATTICE("CS<C,C*")
25 @METHODDEFAULT("OUT<THIS,THIS<IN,THISLOC=THIS,RETURNLOC=OUT")
26 public class ClassifierTree {
27
28   @LOC("CS")
29   private Classifier classifiers[];
30
31   public ClassifierTree(int size) {
32     classifiers = new Classifier[size];
33   }
34
35   public void addClassifier(@LOC("IN") int idx, @LOC("IN") Classifier c) {
36     classifiers[idx] = c;
37   }
38
39   /**
40    * Locates a face by searching radial starting at the last known position. If
41    * lastCoordinates are null we simply start in the center of the image.
42    * <p>
43    * TODO: This method could quite possible be tweaked so that face recognition
44    * would be much faster
45    * 
46    * @param image
47    *          the image to process
48    * @param lastCoordinates
49    *          the last known coordinates or null if unknown
50    * @return an rectangle representing the actual face position on success or
51    *         null if no face could be detected
52    */
53   @LATTICE("OUT<CXY,CXY<THIS,THIS<V,V<IMG,IMG<C,C<IN,C*,V*,FACTOR*,CXY*,THISLOC=THIS,RETURNLOC=OUT,GLOBALLOC=IN")
54   public Rectangle2D locateFaceRadial(@LOC("IN") Image smallImage,
55       @LOC("THIS,ClassifierTree.C") Rectangle2D lastCoordinates) {
56
57     @LOC("IMG") IntegralImageData imageData = new IntegralImageData(smallImage);
58     @LOC("IN") float originalImageFactor = 1;
59
60     if (lastCoordinates == null) {
61       // if we don't have a last coordinate we just begin in the center
62       @LOC("THIS,ClassifierTree.C") int smallImageMaxDimension =
63           Math.min(smallImage.getWidth(), smallImage.getHeight());
64       lastCoordinates =
65           new Rectangle2D((smallImage.getWidth() - smallImageMaxDimension) / 2.0,
66               (smallImage.getHeight() - smallImageMaxDimension) / 2.0, smallImageMaxDimension,
67               smallImageMaxDimension);
68       // System.out.println("lastCoordinates=" + lastCoordinates);
69     } else {
70       // first we have to scale the last coodinates back relative to the resized
71       // image
72       lastCoordinates =
73           new Rectangle2D((lastCoordinates.getX() * (1 / originalImageFactor)),
74               (lastCoordinates.getY() * (1 / originalImageFactor)),
75               (lastCoordinates.getWidth() * (1 / originalImageFactor)),
76               (lastCoordinates.getHeight() * (1 / originalImageFactor)));
77     }
78
79     @LOC("THIS,ClassifierTree.C") float startFactor = (float) (lastCoordinates.getWidth() / 100.0f);
80
81     // first we calculate the maximum scale factor for our 200x200 image
82     @LOC("THIS,ClassifierTree.C") float maxScaleFactor =
83         Math.min(imageData.getWidth() / 100f, imageData.getHeight() / 100f);
84     // maxScaleFactor = 1.0f;
85
86     // we simply won't recognize faces that are smaller than 40x40 px
87     @LOC("THIS,ClassifierTree.C") float minScaleFactor = 0.5f;
88
89     @LOC("THIS,ClassifierTree.C") float maxScaleDifference =
90         Math.max(Math.abs(maxScaleFactor - startFactor), Math.abs(minScaleFactor - startFactor));
91
92     // border for faceYes-possibility must be greater that that
93     @LOC("THIS,ClassifierTree.C") float maxBorder = 0.999f;
94
95     @LOC("THIS,ClassifierTree.C") int startPosX = (int) lastCoordinates.getX();
96     @LOC("THIS,ClassifierTree.C") int startPosY = (int) lastCoordinates.getX();
97
98     for (@LOC("THIS,ClassifierTree.C") float factorDiff = 0.0f; Math.abs(factorDiff) <= maxScaleDifference; factorDiff =
99         (factorDiff + sgn(factorDiff) * 0.1f) * -1 // we alternate between
100                                                    // negative and positiv
101                                                    // factors
102     ) {
103
104       @LOC("THIS,ClassifierTree.C") float factor = startFactor + factorDiff;
105       // System.out.println("factor=" + factor);
106       if (factor > maxScaleFactor || factor < minScaleFactor)
107         continue;
108
109       // now we calculate the actualDimmension
110       @LOC("THIS,ClassifierTree.C") int actualDimmension = (int) (100 * factor);
111       @LOC("THIS,ClassifierTree.C") int maxX = imageData.getWidth() - actualDimmension;
112       @LOC("THIS,ClassifierTree.C") int maxY = imageData.getHeight() - actualDimmension;
113
114       @LOC("THIS,ClassifierTree.C") int maxDiffX = Math.max(Math.abs(startPosX - maxX), startPosX);
115       @LOC("THIS,ClassifierTree.C") int maxDiffY = Math.max(Math.abs(startPosY - maxY), startPosY);
116
117       for (@LOC("CXY") float xDiff = 0.1f; Math.abs(xDiff) <= maxDiffX; xDiff =
118           (xDiff + sgn(xDiff) * 0.5f) * -1) {
119         @LOC("CXY") int xPos = Math.round((float) (startPosX + xDiff));
120         if (xPos < 0 || xPos > maxX)
121           continue;
122
123         // yLines:
124         for (@LOC("CXY") float yDiff = 0.1f; Math.abs(yDiff) <= maxDiffY; yDiff =
125             (yDiff + sgn(yDiff) * 0.5f) * -1) {
126           @LOC("CXY") int yPos = Math.round(startPosY + yDiff);
127           if (yPos < 0 || yPos > maxY)
128             continue;
129
130           // by now we should have a valid coordinate to process which we should
131           // do now
132           @LOC("CXY") boolean backToYLines = false;
133           for (@LOC("CXY") int idx = 0; idx < classifiers.length; ++idx) {
134             @LOC("CXY") float borderline =
135                 0.8f + (idx / (classifiers.length - 1)) * (maxBorder - 0.8f);
136             if (!classifiers[idx].classifyFace(imageData, factor, xPos, yPos, borderline)) {
137               backToYLines = true;
138               break;
139               // continue yLines;
140             }
141           }
142
143           // if we reach here we have a face recognized because our image went
144           // through all
145           // classifiers
146
147           if (backToYLines) {
148             continue;
149           }
150           @LOC("OUT") Rectangle2D faceRect =
151               new Rectangle2D(xPos * originalImageFactor, yPos * originalImageFactor,
152                   actualDimmension * originalImageFactor, actualDimmension * originalImageFactor);
153
154           return faceRect;
155
156         }
157
158       }
159
160     }
161
162     // System.out.println("Time: "+(System.currentTimeMillis()-timeStart)+"ms");
163     return null;
164
165   }
166
167   @LATTICE("OUT<IN,OUT<THIS,THISLOC=THIS,RETURNLOC=OUT")
168   private static int sgn(@LOC("IN") float value) {
169     return (value < 0 ? -1 : (value > 0 ? +1 : 1));
170   }
171
172 }