2 * Copyright 2009 (c) Florian Frankenberger (darkblue.de)
4 * This file is part of LEA.
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
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
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/>.
24 public class Classifier {
26 private ScanArea[] scanAreas;
28 // private float possibilityFace = 0f;
29 private float[] possibilities_FaceYes;
30 private float[] possibilities_FaceNo;
31 private int possibilityFaceYes = 0;
32 private int possibilityFaceNo = 0;
34 public static final long serialVersionUID = 5168971806943656945l;
36 public Classifier(int numScanAreas) {
37 this.scanAreas = new ScanArea[numScanAreas];
38 this.possibilities_FaceYes = new float[numScanAreas];
39 this.possibilities_FaceNo = new float[numScanAreas];
42 public void setScanArea(int idx, ScanArea area) {
43 scanAreas[idx] = area;
46 public void setPossibilitiesFaceYes(float[] arr) {
47 this.possibilities_FaceYes = arr;
50 public void setPossibilityFaceYes(int v) {
51 this.possibilityFaceYes = v;
54 public void setPossibilitiesFaceNo(float[] arr) {
55 this.possibilities_FaceNo = arr;
58 public void setPossibilityFaceNo(int v) {
59 this.possibilityFaceNo = v;
62 // public void learn(IntegralImageData image, boolean isFace) {
63 // long values[] = new long[this.scanAreas.length];
65 // System.out.println("HERE:"+image.getIntegralAt(image.getDimension().width-1,
66 // // image.getDimension().height-1));
67 // // we assume the image is rectangular so we can simply use one side to
69 // // the scale factor
70 // float scaleFactor = image.getDimension().width / 100.0f;
74 // for (int i = 0; i < this.scanAreas.length; ++i) {
75 // ScanArea scanArea = this.scanAreas[i];
78 // values[i] += image.getIntegralAt(scanArea.getToX(scaleFactor),
79 // scanArea.getToY(scaleFactor));
81 // image.getIntegralAt(scanArea.getFromX(scaleFactor),
82 // scanArea.getFromY(scaleFactor));
85 // image.getIntegralAt(scanArea.getToX(scaleFactor),
86 // scanArea.getFromY(scaleFactor));
88 // image.getIntegralAt(scanArea.getFromX(scaleFactor),
89 // scanArea.getToY(scaleFactor));
91 // values[i] = (long) (values[i] / ((float) scanArea.getSize(scaleFactor)));
92 // avg = ((avgItems * avg) + values[i]) / (++avgItems);
96 // this.possibilityFaceYes++;
98 // this.possibilityFaceNo++;
100 // for (int i = 0; i < this.scanAreas.length; ++i) {
101 // boolean bright = (values[i] >= avg);
104 // // here we change the possibility of P(Scanarea_N = (Bright | NotBright)
106 // this.possibilities_FaceYes[i] =
107 // (((this.possibilityFaceYes - 1) * this.possibilities_FaceYes[i]) + (bright
109 // : 0.001f)) / this.possibilityFaceYes;
111 // System.out.println("P(Scannarea"+i+"=bright|Face=Yes) = "+this.possibilities_FaceYes[i]);
113 // System.out.println("P(Scannarea"+i+"=dark|Face=Yes) = "+(1.0f-this.possibilities_FaceYes[i]));
115 // // here we change the possibility of P(Scanarea_N = (Bright | NotBright)
117 // this.possibilities_FaceNo[i] =
118 // (((this.possibilityFaceNo - 1) * this.possibilities_FaceNo[i]) + (bright ?
120 // : 0.001f)) / this.possibilityFaceNo;
122 // System.out.println("P(Scannarea"+i+"=bright|Face=No) = "+this.possibilities_FaceNo[i]);
124 // System.out.println("P(Scannarea"+i+"=dark|Face=No) = "+(1.0f-this.possibilities_FaceNo[i]));
129 // // System.out.println("Average: "+avg);
130 // // System.out.println(this);
134 * Classifies an images region as face
138 * please be aware of the fact that the scanareas are scaled for use
139 * with 100x100 px images
140 * @param translationX
141 * @param translationY
142 * @return true if this region was classified as face, else false
144 public boolean classifyFace(IntegralImageData image, float scaleFactor, int translationX,
145 int translationY, float borderline) {
147 long values[] = new long[this.scanAreas.length];
151 for (int i = 0; i < this.scanAreas.length; ++i) {
152 ScanArea scanArea = this.scanAreas[i];
153 // System.out.println("scanarea="+scanArea);
157 image.getIntegralAt(translationX + scanArea.getToX(scaleFactor),
158 translationY + scanArea.getToY(scaleFactor));
160 image.getIntegralAt(translationX + scanArea.getFromX(scaleFactor), translationY
161 + scanArea.getFromY(scaleFactor));
164 image.getIntegralAt(translationX + scanArea.getToX(scaleFactor),
165 translationY + scanArea.getFromY(scaleFactor));
167 image.getIntegralAt(translationX + scanArea.getFromX(scaleFactor), translationY
168 + scanArea.getToY(scaleFactor));
170 values[i] = (long) (values[i] / ((float) scanArea.getSize(scaleFactor)));
171 avg = ((avgItems * avg) + values[i]) / (++avgItems);
173 // System.out.println("avg=" + avg);
175 // int amountYesNo = this.possibilityFaceNo + this.possibilityFaceYes;
177 // calculate the possibilites for face=yes and face=no with naive bayes
178 // P(Yes | M1 and ... and Mn) = P(Yes) * P(M1 | Yes) * ... * P(Mn | Yes) /xx
179 // P(No | M1 and ... and Mn) = P(No) * P(M1 | No) * ... * P(Mn | No) / xx
180 // as we just maximize the args we don't actually calculate the accurate
183 float isFaceYes = 1.0f;// this.possibilityFaceYes / (float)amountYesNo;
184 float isFaceNo = 1.0f;// this.possibilityFaceNo / (float)amountYesNo;
186 for (int i = 0; i < this.scanAreas.length; ++i) {
187 boolean bright = (values[i] >= avg);
188 isFaceYes *= (bright ? this.possibilities_FaceYes[i] : 1 - this.possibilities_FaceYes[i]);
189 isFaceNo *= (bright ? this.possibilities_FaceNo[i] : 1 - this.possibilities_FaceNo[i]);
191 // System.out.println("avg=" + avg + " yes=" + isFaceYes + " no=" + isFaceNo);
193 return (isFaceYes >= isFaceNo && (isFaceYes / (isFaceYes + isFaceNo)) > borderline);
196 public ScanArea[] getScanAreas() {
197 return this.scanAreas;
200 public int getLearnedFacesYes() {
201 return this.possibilityFaceYes;
204 public int getLearnedFacesNo() {
205 return this.possibilityFaceNo;
208 public float getPossibility(int scanAreaID, boolean faceYes, boolean bright) {
210 return (bright ? this.possibilities_FaceYes[scanAreaID]
211 : 1 - this.possibilities_FaceYes[scanAreaID]);
213 return (bright ? this.possibilities_FaceNo[scanAreaID]
214 : 1 - this.possibilities_FaceNo[scanAreaID]);
218 public int compareTo(Classifier o) {
219 if (o.getScanAreas().length > this.getScanAreas().length) {
221 } else if (o.getScanAreas().length < this.getScanAreas().length) {
227 public String toString() {
230 for (int i = 0; i < scanAreas.length; i++) {
231 str += scanAreas[i].toString() + "\n";
238 // public String toString() {
239 // StringBuilder sb = new StringBuilder();
240 // sb.append("Classifier [ScanAreas: " + this.scanAreas.length);
241 // int yesNo = this.possibilityFaceYes + this.possibilityFaceNo;
242 // sb.append(String.format("|Yes: %3.2f| No:%3.2f] (",
243 // (this.possibilityFaceYes / (float) yesNo) * 100.0f,
244 // (this.possibilityFaceNo / (float) yesNo) * 100.0f));
245 // for (int i = 0; i < this.scanAreas.length; ++i) {
246 // sb.append(String.format("[%3d|Yes: %3.2f| No: %3.2f], ", i + 1,
247 // (this.possibilities_FaceYes[i] * 100.0f), (this.possibilities_FaceNo[i] *
252 // return sb.toString();
256 * Generates a new set of classifiers each with more ScanAreas than the last
257 * classifier. You can specifiy the amount of classifiers you want to generate
260 * amount of classifiers to create
261 * @param startAmountScanAreas
262 * the start amount of scanAreas - if your first classifiers should
263 * contain 3 items you should give 3 here
264 * @param incAmountScanAreas
265 * the amount of which the scanAreas should increase - a simple 2
266 * will increase them by 2 every step
267 * @return a List of classifiers
269 // public static List<Classifier> generateNewClassifiers(int amount, int
270 // startAmountScanAreas,
271 // float incAmountScanAreas) {
272 // List<Classifier> classifiers = new ArrayList<Classifier>();
275 // Random random = new Random(System.currentTimeMillis());
276 // double maxSpace = 2 * Math.PI * Math.pow(50, 2);
278 // for (int i = 0; i < amount; ++i) {
279 // // we create an odd amount of ScanAreas starting with 1 (3, 5, 7, ...)
280 // int scanAreaAmount = startAmountScanAreas + (int)
281 // Math.pow(incAmountScanAreas, i);// +
282 // // ((i)*incAmountScanAreas+1);
284 // int scanAreaSize =
285 // randomInt(random, scanAreaAmount * 20, (int) Math.min(maxDim * maxDim,
288 // // System.out.println("scanAreaSize = "+scanAreaSize);
290 // List<ScanArea> scanAreas = new ArrayList<ScanArea>();
292 // for (int j = 0; j < scanAreaAmount; ++j) {
295 // ScanArea scanArea = null;
297 // // new the width has the first choice
298 // int minWidth = (int) Math.ceil(scanAreaSize / (float) maxDim);
300 // int scanAreaWidth = randomInt(random, minWidth, Math.min(maxDim,
301 // scanAreaSize / 2));
302 // int scanAreaHeight = (int) Math.ceil(scanAreaSize / (float) scanAreaWidth);
305 // randomInt(random, 5, Math.min(50 - scanAreaHeight / 2, 50 - scanAreaWidth /
307 // double angle = random.nextFloat() * 2 * Math.PI;
309 // int posX = (int) (50 + Math.cos(angle) * radius) - (scanAreaWidth / 2);
310 // int posY = (int) (50 + Math.sin(angle) * radius) - (scanAreaHeight / 2);
312 // // System.out.println("[Angle: "+(angle /
313 // // (Math.PI*2)*180)+" | radius: "+radius+"]");
315 // System.out.println("Area"+j+" is "+posX+", "+posY+" ("+scanAreaWidth+" x "+scanAreaHeight+" = "+((scanAreaWidth*scanAreaHeight))+")");
317 // // now we get random position for this area
318 // scanArea = new ScanArea(posX, posY, scanAreaWidth, scanAreaHeight);
321 // } while (scanAreas.contains(scanArea) && counter < 30);
323 // if (counter == 30) {
328 // scanAreas.add(scanArea);
331 // Classifier classifier = new Classifier(scanAreas.toArray(new ScanArea[0]));
332 // classifiers.add(classifier);
335 // return classifiers;
338 // private static int randomInt(Random random, int from, int to) {
339 // if (to - from <= 0)
341 // return from + random.nextInt(to - from);
344 // public static List<Classifier> getDefaultClassifier() {
345 // List<Classifier> classifier = new ArrayList<Classifier>();
347 // classifier.add(new Classifier(new ScanArea(30, 30, 30, 30), new
348 // ScanArea(15, 8, 15, 82),
349 // new ScanArea(75, 8, 15, 82)));
351 // return classifier;