Intro
This tutorial expands on the first post about getting the Dream Cheeky Thunder Launcher working within Java. This tutorial is on Face Detection. There will be another tutorial on Face Recognition. The difference is Face Detection can find a face in a complex scene where Face Recognition will find a face in a complex scene, normalize it and attempt to match it to a database of faces that it has learnt previously. (Roughly) Letting the missile launcher carrying out targeted hits.
Following on from the code in the last tutorial we will replace the keyboard controller class with one that will control the launcher based on face locations. Originally I was using OpenIMAJ However I switched to jviolajones as I have used it elsewhere and know roughly how to speed it up however the OpenIMAJ library does more than just find the face. This may become useful later when attempting to Recognise the face however for now I want to keep it minimal as I would like to get a mobile Raspberry Pi implementation working.
An extremely high overview of Viola-Jones object detection
An accepted and speedy way of extracting faces from a noisy image. Using a sub window that changes in scale it scans the image looking for a pattern or classifiers leant in the training phase (Lucky we have a trained xml file we can load in). It means this method can be used to detect a variety of things such as eyes, fronts of cars and cats. I won’t go any deeper than this but I find it all very interesting. If you can get your little hands on [this] paper I would recommend reading it, also to get a real idea of what is going on underneath; even implement you own from scratch I found this guide invaluable: [Link]
Code
This is a basic example of how face detection can be implemented with the missile launcher. It will just move towards where it sees a face and fires if the center of it is within limits.
Face Controller Code (dumb)
package thunderlauncher;
import com.github.sarxos.webcam.Webcam;
import com.github.sarxos.webcam.WebcamResolution;
import detection.Detector;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
*
* @author James
*/
public class FaceController extends JPanel {
private Webcam webcam; //An instance of the webcam
JFrame frame = new JFrame("Faces"); //Show some images for debugging
FaceController(Launcher t) {
frame.add(this);//attach this JPanel
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
List cameras = Webcam.getWebcams();
for (int i = 0; i < cameras.size(); i++) {
// For multiple cameras kick out some names
System.out.println(i + " - " + cameras.get(i).getName());
}
webcam = cameras.get(0);//Just get the first one
if (webcam != null) { // If webcam exists
webcam.setViewSize(WebcamResolution.QVGA.getSize()); //set view size (Smaller = Faster processing) http://en.wikipedia.org/wiki/Display_resolution
webcam.open(); // Open the webcam
} else {
System.out.println("No webcam detected, Exiting");
System.exit(0);
}
Detector d = Detector.create("haarcascade.xml"); //Create a detector with a generated xml file
BufferedImage img = null;
int centerx = webcam.getViewSize().getSize().width;
int centery = webcam.getViewSize().getSize().height;
centerx = centerx / 2;//Know where the center is
centery = centery / 2;
ArrayList faces = new ArrayList(); //Array of detected faces
while (true) {//Infinate loop
img = webcam.getImage();//Get an image
faces.addAll(d.getFaces(img, 1.2f, 1.1f, 0.1f, 1, true));//Add all detected faces
if (faces.size() > 0) {//If there is a face (We will only bother with the 1st one)
float x, y, h, w;
x = faces.get(0).getBounds().x;
y = faces.get(0).getBounds().y;
w = faces.get(0).getBounds().width;
h = faces.get(0).getBounds().height;
x = x + (w / 2);//Find center of face
y = y + (h / 2);//Find center of face
if (Math.abs(((x - centerx) + (y - centery)) / 2) < 5) {//If the distance is above 5
t.execute(Launcher.Command.FIRE, 3500);//Fire
} else {//Else move towards the face using the image and face centre
if (Math.abs(x - centerx) < Math.abs(y - centery)) { if (y > centery) {
t.execute(Launcher.Command.DOWN, 40);//move down for 40ms
} else {
t.execute(Launcher.Command.UP, 40);
}
} else {
if (x > centerx) {
t.execute(Launcher.Command.RIGHT, 40);
} else {
t.execute(Launcher.Command.LEFT, 40);
}
}
}
this.getGraphics().drawImage(img, 0, 0, frame);//do some drawing
this.getGraphics().drawOval(centerx - 5, centery - 5, 5, 5);
this.getGraphics().drawOval((int) (x - 5), (int) (y - 5), (int) (5), (int) (5));
this.getGraphics().drawRect((int) faces.get(0).getBounds().x, (int) faces.get(0).getBounds().y, (int) faces.get(0).getBounds().width, (int) faces.get(0).getBounds().height);
}
faces.clear();//Clear the face array
}
}
}
Control Smart Task
For a slightly smarter turret, which at the same time is quite strange to be around, add a timer. For example after finding no faces for 40 seconds or so. Randomly move to keep passers by on their toes. Another addition is taking into account the distance between the center of the face and the center of the image. Then moving to the scale of the distance. One last one is to zero the turret and keep track of all the movements resting every 20 minutes or so to reset any drift. When a face is detected calculate the movements from zero and record it. This gives the launcher memory, then when no face is detected and none have been found from randomly drifting around it can go to one of the spots it has detected a face in the past.
[May be nice enough to add code for this soon]
Results
[Coming up Next; Recognition!]