/*
* Catroid: An on-device visual programming system for Android devices
* Copyright (C) 2010-2016 The Catrobat Team
* (<http://developer.catrobat.org/credits>)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* An additional term exception under section 7 of the GNU Affero
* General Public License, version 3, is available at
* http://developer.catrobat.org/license_additional_term
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.catrobat.catroid.test.facedetection;
import android.graphics.PointF;
import android.hardware.Camera;
import android.test.InstrumentationTestCase;
import org.catrobat.catroid.common.ScreenValues;
import org.catrobat.catroid.facedetection.SlowFaceDetector;
import org.catrobat.catroid.formulaeditor.SensorCustomEvent;
import org.catrobat.catroid.formulaeditor.SensorCustomEventListener;
import org.catrobat.catroid.formulaeditor.Sensors;
import org.catrobat.catroid.test.utils.Reflection;
import org.catrobat.catroid.test.utils.Reflection.ParameterList;
import java.util.Random;
public class SlowFaceDetectorTest extends InstrumentationTestCase {
private static final int DETECTION_WIDTH = 400;
private static final int DETECTION_HEIGHT = 300;
private static final float EYE_DISTANCE = 4.0f;
private static final int COUNTER_INDEX = 0;
private static final int SIZE_INDEX = 1;
private static final int X_POSITION_INDEX = 2;
private static final int Y_POSITION_INDEX = 3;
@Override
public void setUp() throws Exception {
super.setUp();
ScreenValues.SCREEN_WIDTH = 720;
ScreenValues.SCREEN_HEIGHT = 1080;
}
public void testNotAvailable() {
Camera camera = null;
try {
camera = Camera.open();
SlowFaceDetector detector = new SlowFaceDetector();
boolean success = detector.startFaceDetection();
assertFalse("SlowFaceDetector should not start if camera not available.", success);
} catch (Exception exc) {
fail("Camera not available (" + exc.getMessage() + ")");
} finally {
if (camera != null) {
camera.release();
}
}
}
public void testStartAndStop() {
Camera camera = null;
try {
camera = Camera.open();
} catch (Exception exc) {
fail("Camera not available (" + exc.getMessage() + ")");
} finally {
if (camera != null) {
camera.release();
}
}
SlowFaceDetector detector = new SlowFaceDetector();
assertNotNull("Cannot instantiate SlowFaceDetector", detector);
try {
detector.startFaceDetection();
} catch (Exception exc) {
fail("Cannot start face detection (" + exc.getMessage() + ")");
}
try {
detector.stopFaceDetection();
} catch (Exception exc) {
fail("Cannot stop face detection (" + exc.getMessage() + ")");
}
camera = null;
try {
camera = Camera.open();
} catch (Exception exc) {
fail("Ressources were not propperly released");
} finally {
if (camera != null) {
camera.release();
}
}
}
public void testDoubleStart() {
SlowFaceDetector detector = new SlowFaceDetector();
detector.startFaceDetection();
try {
detector.startFaceDetection();
} catch (Exception e) {
fail("Second start of face detector should be ignored and not cause errors: " + e.getMessage());
} finally {
detector.stopFaceDetection();
}
}
public void testOnFaceDetectedListener() {
SlowFaceDetector detector = new SlowFaceDetector();
final int[] detectedFaces = new int[4];
SensorCustomEventListener detectionListener = new SensorCustomEventListener() {
public void onCustomSensorChanged(SensorCustomEvent event) {
detectedFaces[COUNTER_INDEX]++;
int value = (int) event.values[0];
float intFloatDifference = event.values[0] - value;
assertEquals("Face detection values should be integer", intFloatDifference, 0f);
switch (event.sensor) {
case FACE_X_POSITION:
detectedFaces[X_POSITION_INDEX] = value;
break;
case FACE_Y_POSITION:
detectedFaces[Y_POSITION_INDEX] = value;
break;
case FACE_SIZE:
detectedFaces[SIZE_INDEX] = value;
break;
default:
fail("Unexpected Sensor on Face Detection event. Expected face size or position.");
}
}
};
detector.addOnFaceDetectedListener(detectionListener);
assertEquals("Face Detection Listener receives unexpected calls", 0, detectedFaces[COUNTER_INDEX]);
PointF centerPoint = new PointF(DETECTION_WIDTH / 2, DETECTION_HEIGHT / 2);
ParameterList parameters = new ParameterList(centerPoint, Float.valueOf(EYE_DISTANCE),
Integer.valueOf(DETECTION_WIDTH), Integer.valueOf(DETECTION_HEIGHT));
Reflection.invokeMethod(detector, "onFaceFound", parameters);
assertEquals("Face Detection Listener does not receive calls", 3, detectedFaces[COUNTER_INDEX]);
int expectedSize = (int) (EYE_DISTANCE * 400 / DETECTION_WIDTH);
assertEquals("Unexpected size of face", expectedSize, detectedFaces[SIZE_INDEX]);
int expectedXPosition = (int) (centerPoint.x / DETECTION_WIDTH * (-1) * ScreenValues.SCREEN_WIDTH)
+ ScreenValues.SCREEN_WIDTH / 2;
assertEquals("Unexpected x position of face", expectedXPosition, detectedFaces[X_POSITION_INDEX]);
int expectedYPosition = (int) (centerPoint.y / DETECTION_HEIGHT * (-1) * ScreenValues.SCREEN_HEIGHT)
+ ScreenValues.SCREEN_HEIGHT / 2;
assertEquals("Unexpected y position of face", expectedYPosition, detectedFaces[Y_POSITION_INDEX]);
Reflection.invokeMethod(detector, "onFaceFound", parameters);
assertTrue("Face Detection Listener reveices too many calls", detectedFaces[COUNTER_INDEX] <= 6);
assertEquals("Face Detection Listener does not receive calls", 6, detectedFaces[COUNTER_INDEX]);
}
public void testFaceSizeBounds() {
SlowFaceDetector detector = new SlowFaceDetector();
final float[] faceSize = new float[1];
SensorCustomEventListener detectionListener = new SensorCustomEventListener() {
public void onCustomSensorChanged(SensorCustomEvent event) {
if (event.sensor == Sensors.FACE_SIZE) {
faceSize[0] = event.values[0];
}
}
};
detector.addOnFaceDetectedListener(detectionListener);
ParameterList parameters = new ParameterList(new PointF(), Float.valueOf(EYE_DISTANCE),
Integer.valueOf(DETECTION_WIDTH), Integer.valueOf(DETECTION_HEIGHT));
Reflection.invokeMethod(detector, "onFaceFound", parameters);
assertTrue("Face size must not be negative", faceSize[0] >= 0);
assertTrue("Illegal face size, range is [0,100]", faceSize[0] <= 100);
Random random = new Random();
parameters = new ParameterList(new PointF(), Float.valueOf(random.nextInt(DETECTION_WIDTH)),
Integer.valueOf(DETECTION_WIDTH), Integer.valueOf(DETECTION_HEIGHT));
Reflection.invokeMethod(detector, "onFaceFound", parameters);
assertTrue("Face size must not be negative", faceSize[0] >= 0);
assertTrue("Illegal face size, range is [0,100]", faceSize[0] <= 100);
detector.removeOnFaceDetectedListener(detectionListener);
}
}