Replace tabs with spaces

This commit is contained in:
2015-09-01 13:14:13 +01:00
parent b1de26ba09
commit 8aa00cf80c
25 changed files with 1966 additions and 1966 deletions

View File

@@ -175,7 +175,7 @@ public class Device {
// don't need to create an event if filtered away // don't need to create an event if filtered away
if (vector != null) { if (vector != null) {
// calculate the absolute value for the accelerationevent // calculate the absolute value for the accelerationevent
double absvalue = Math.sqrt((vector[0] * vector[0]) + double absvalue = Math.sqrt((vector[0] * vector[0]) +
(vector[1] * vector[1]) + (vector[2] * vector[2])); (vector[1] * vector[1]) + (vector[2] * vector[2]));
@@ -190,7 +190,7 @@ public class Device {
/** Fires a button pressed event. /** Fires a button pressed event.
* @param button * @param button
* Integer value of the pressed button. * Integer value of the pressed button.
*/ */
public void fireButtonPressedEvent(int button) { public void fireButtonPressedEvent(int button) {
ButtonPressedEvent w = new ButtonPressedEvent(this, button); ButtonPressedEvent w = new ButtonPressedEvent(this, button);

View File

@@ -38,42 +38,42 @@ import org.wiigee.device.*;
* *
*/ */
public class AccelerationEvent extends EventObject { public class AccelerationEvent extends EventObject {
double X, Y, Z; double X, Y, Z;
double absvalue; double absvalue;
/** /**
* Create an AccelerationEvent with a specific source, * Create an AccelerationEvent with a specific source,
* all the three acceleration values and the calculated absolute * all the three acceleration values and the calculated absolute
* value. * value.
* *
* @param source The source which has been accelerated (Wiimote). * @param source The source which has been accelerated (Wiimote).
* @param X The value of acceleration in the x direction. * @param X The value of acceleration in the x direction.
* @param Y The value of acceleration in the y direction. * @param Y The value of acceleration in the y direction.
* @param Z The value of acceleration in the z direction. * @param Z The value of acceleration in the z direction.
* @param absvalue The absolute value of this acceleration vector. * @param absvalue The absolute value of this acceleration vector.
*/ */
public AccelerationEvent(Device source, double X, double Y, double Z, double absvalue) { public AccelerationEvent(Device source, double X, double Y, double Z, double absvalue) {
super(source); super(source);
this.X=X; this.X=X;
this.Y=Y; this.Y=Y;
this.Z=Z; this.Z=Z;
this.absvalue=absvalue; this.absvalue=absvalue;
} }
public double getX() { public double getX() {
return X; return X;
} }
public double getY() { public double getY() {
return Y; return Y;
} }
public double getZ() { public double getZ() {
return Z; return Z;
} }
public double getAbsValue() { public double getAbsValue() {
return absvalue; return absvalue;
} }
} }

View File

@@ -34,32 +34,32 @@ import java.util.EventListener;
* @author Benjamin 'BePo' Poppinga * @author Benjamin 'BePo' Poppinga
*/ */
public interface AccelerationListener extends EventListener { public interface AccelerationListener extends EventListener {
/** /**
* This method would be called if a Device source has been accelerated. * This method would be called if a Device source has been accelerated.
* *
* @param event The acceleration representation as an event. * @param event The acceleration representation as an event.
*/ */
public abstract void accelerationReceived(AccelerationEvent event); public abstract void accelerationReceived(AccelerationEvent event);
/** /**
* This method would be called if a Device is in idle state and then a * This method would be called if a Device is in idle state and then a
* motion starts or if a Device is in motion and then the motion stops and * motion starts or if a Device is in motion and then the motion stops and
* the Device is in idle state. * the Device is in idle state.
* *
* @param event This is the event which contains if the Wiimote is now * @param event This is the event which contains if the Wiimote is now
* in motion or not. * in motion or not.
*/ */
public abstract void motionStartReceived(MotionStartEvent event); public abstract void motionStartReceived(MotionStartEvent event);
/** /**
* This method would be called if a Device is in motion and then the motion * This method would be called if a Device is in motion and then the motion
* stops and the Device is in idle state. * stops and the Device is in idle state.
* *
* @param event This is the event which contains if the Device is now * @param event This is the event which contains if the Device is now
* in motion or not. * in motion or not.
*/ */
public abstract void motionStopReceived(MotionStopEvent event); public abstract void motionStopReceived(MotionStopEvent event);
} }

View File

@@ -37,48 +37,48 @@ import org.wiigee.device.Device;
*/ */
public class ActionStartEvent extends EventObject { public class ActionStartEvent extends EventObject {
protected boolean trainbutton; protected boolean trainbutton;
protected boolean recognitionbutton; protected boolean recognitionbutton;
protected boolean closegesturebutton; protected boolean closegesturebutton;
public ActionStartEvent(Device source) { public ActionStartEvent(Device source) {
super(source); super(source);
} }
/** /**
* Is true if this button press has been done by the * Is true if this button press has been done by the
* individual defined RecognitionButton which has to be * individual defined RecognitionButton which has to be
* set during initialization of a Wiimote. * set during initialization of a Wiimote.
* *
* @return Is this button press initiated by the recognition button. * @return Is this button press initiated by the recognition button.
* @see device.Wiimote#setRecognitionButton(int) setRecognitionButton() * @see device.Wiimote#setRecognitionButton(int) setRecognitionButton()
*/ */
public boolean isRecognitionInitEvent() { public boolean isRecognitionInitEvent() {
return this.recognitionbutton; return this.recognitionbutton;
} }
/** /**
* Is true if this button press has been done by the * Is true if this button press has been done by the
* individual defined TrainButton which has to be * individual defined TrainButton which has to be
* set during initialization of a Wiimote. * set during initialization of a Wiimote.
* *
* @return Is this button pres initiated by the training button. * @return Is this button pres initiated by the training button.
* @see device.Wiimote#setTrainButton(int) setTrainButton() * @see device.Wiimote#setTrainButton(int) setTrainButton()
*/ */
public boolean isTrainInitEvent() { public boolean isTrainInitEvent() {
return this.trainbutton; return this.trainbutton;
} }
/** /**
* Is true if this button press has been done by the * Is true if this button press has been done by the
* individual defined CloseGestureButton which has to be * individual defined CloseGestureButton which has to be
* set during initialization of a Wiimote. * set during initialization of a Wiimote.
* *
* @return Is this button press initiated by the close gesture button. * @return Is this button press initiated by the close gesture button.
* @see device.Wiimote#setCloseGestureButton(int) setCloseGestureButton() * @see device.Wiimote#setCloseGestureButton(int) setCloseGestureButton()
*/ */
public boolean isCloseGestureInitEvent() { public boolean isCloseGestureInitEvent() {
return this.closegesturebutton; return this.closegesturebutton;
} }
} }

View File

@@ -35,9 +35,9 @@ import org.wiigee.device.Device;
* @author Benjamin 'BePo' Poppinga * @author Benjamin 'BePo' Poppinga
*/ */
public class ActionStopEvent extends EventObject { public class ActionStopEvent extends EventObject {
public ActionStopEvent(Device source) { public ActionStopEvent(Device source) {
super(source); super(source);
} }
} }

View File

@@ -35,20 +35,20 @@ import java.util.EventListener;
public interface ButtonListener extends EventListener { public interface ButtonListener extends EventListener {
/** /**
* This method would be called if a Device button has been pressed. * This method would be called if a Device button has been pressed.
* *
* @param event The button representation as an event. * @param event The button representation as an event.
*/ */
public abstract void buttonPressReceived(ButtonPressedEvent event); public abstract void buttonPressReceived(ButtonPressedEvent event);
/** /**
* This method would be called if a Device button has been released. * This method would be called if a Device button has been released.
* *
* @param event This is actually a meta-event NOT containing which button * @param event This is actually a meta-event NOT containing which button
* has been released. * has been released.
*/ */
public abstract void buttonReleaseReceived(ButtonReleasedEvent event); public abstract void buttonReleaseReceived(ButtonReleasedEvent event);
} }

View File

@@ -37,44 +37,44 @@ import org.wiigee.device.*;
* @author Benjamin 'BePo' Poppinga * @author Benjamin 'BePo' Poppinga
*/ */
public class ButtonPressedEvent extends ActionStartEvent { public class ButtonPressedEvent extends ActionStartEvent {
// Fixed number values. // Fixed number values.
public static final int BUTTON_2 = 1; public static final int BUTTON_2 = 1;
public static final int BUTTON_1 = 2; public static final int BUTTON_1 = 2;
public static final int BUTTON_B = 3; public static final int BUTTON_B = 3;
public static final int BUTTON_A = 4; public static final int BUTTON_A = 4;
public static final int BUTTON_MINUS = 5; public static final int BUTTON_MINUS = 5;
public static final int BUTTON_HOME = 8; public static final int BUTTON_HOME = 8;
public static final int BUTTON_LEFT = 9; public static final int BUTTON_LEFT = 9;
public static final int BUTTON_RIGHT = 10; public static final int BUTTON_RIGHT = 10;
public static final int BUTTON_DOWN = 11; public static final int BUTTON_DOWN = 11;
public static final int BUTTON_UP = 12; public static final int BUTTON_UP = 12;
public static final int BUTTON_PLUS = 13; public static final int BUTTON_PLUS = 13;
int button; int button;
/** /**
* Create a WiimoteButtonPressedEvent with the Wiimote source whose * Create a WiimoteButtonPressedEvent with the Wiimote source whose
* Button has been pressed and the integer representation of the button. * Button has been pressed and the integer representation of the button.
* *
* @param source * @param source
* @param button * @param button
*/ */
public ButtonPressedEvent(Device source, int button) { public ButtonPressedEvent(Device source, int button) {
super(source); super(source);
this.button=button; this.button=button;
if(source.getRecognitionButton()==button) { if(source.getRecognitionButton()==button) {
this.recognitionbutton=true; this.recognitionbutton=true;
} else if(source.getTrainButton()==button) { } else if(source.getTrainButton()==button) {
this.trainbutton=true; this.trainbutton=true;
} else if(source.getCloseGestureButton()==button) { } else if(source.getCloseGestureButton()==button) {
this.closegesturebutton=true; this.closegesturebutton=true;
} }
} }
public int getButton() { public int getButton() {
return this.button; return this.button;
} }
} }

View File

@@ -37,13 +37,13 @@ public class ButtonReleasedEvent extends ActionStopEvent {
int button; int button;
public ButtonReleasedEvent(Device source, int button) { public ButtonReleasedEvent(Device source, int button) {
super(source); super(source);
this.button = button; this.button = button;
} }
public int getButton() { public int getButton() {
return this.button; return this.button;
} }
} }

View File

@@ -36,12 +36,12 @@ import java.util.EventListener;
*/ */
public interface GestureListener extends EventListener { public interface GestureListener extends EventListener {
/** /**
* This method would be called if a gesture has been recognized. * This method would be called if a gesture has been recognized.
* *
* @param event The GestureEvent containing information about * @param event The GestureEvent containing information about
* the recognized gesture. * the recognized gesture.
*/ */
public abstract void gestureReceived(GestureEvent event); public abstract void gestureReceived(GestureEvent event);
} }

View File

@@ -32,32 +32,32 @@ import org.wiigee.device.Device;
* @author Benjamin 'BePo' Poppinga * @author Benjamin 'BePo' Poppinga
*/ */
public class MotionStartEvent extends ActionStartEvent { public class MotionStartEvent extends ActionStartEvent {
public MotionStartEvent(Device source) { public MotionStartEvent(Device source) {
super(source); super(source);
if(source.getRecognitionButton()==Device.MOTION) { if(source.getRecognitionButton()==Device.MOTION) {
this.recognitionbutton=true; this.recognitionbutton=true;
} else if(source.getTrainButton()==Device.MOTION) { } else if(source.getTrainButton()==Device.MOTION) {
this.trainbutton=true; this.trainbutton=true;
} else if(source.getCloseGestureButton()==Device.MOTION) { } else if(source.getCloseGestureButton()==Device.MOTION) {
this.closegesturebutton=true; this.closegesturebutton=true;
} }
} }
@Override @Override
public boolean isTrainInitEvent() { public boolean isTrainInitEvent() {
return this.trainbutton; return this.trainbutton;
} }
@Override @Override
public boolean isCloseGestureInitEvent() { public boolean isCloseGestureInitEvent() {
return this.closegesturebutton; return this.closegesturebutton;
} }
@Override @Override
public boolean isRecognitionInitEvent() { public boolean isRecognitionInitEvent() {
return this.recognitionbutton; return this.recognitionbutton;
} }
} }

View File

@@ -35,8 +35,8 @@ import org.wiigee.device.Device;
*/ */
public class MotionStopEvent extends ActionStopEvent { public class MotionStopEvent extends ActionStopEvent {
public MotionStopEvent(Device source) { public MotionStopEvent(Device source) {
super(source); super(source);
} }
} }

View File

@@ -31,40 +31,40 @@ package org.wiigee.filter;
* @author Benjamin 'BePo' Poppinga * @author Benjamin 'BePo' Poppinga
*/ */
public class DirectionalEquivalenceFilter extends Filter { public class DirectionalEquivalenceFilter extends Filter {
private double sensivity; private double sensivity;
private double[] reference; private double[] reference;
public DirectionalEquivalenceFilter() { public DirectionalEquivalenceFilter() {
super(); super();
this.reset(); this.reset();
} }
public void reset() { public void reset() {
this.sensivity=0.2; this.sensivity=0.2;
this.reference = new double[] {0.0, 0.0, 0.0}; this.reference = new double[] {0.0, 0.0, 0.0};
} }
public double[] filterAlgorithm(double[] vector) { public double[] filterAlgorithm(double[] vector) {
if(vector[0]<reference[0]-this.sensivity || if(vector[0]<reference[0]-this.sensivity ||
vector[0]>reference[0]+this.sensivity || vector[0]>reference[0]+this.sensivity ||
vector[1]<reference[1]-this.sensivity || vector[1]<reference[1]-this.sensivity ||
vector[1]>reference[1]+this.sensivity || vector[1]>reference[1]+this.sensivity ||
vector[2]<reference[2]-this.sensivity || vector[2]<reference[2]-this.sensivity ||
vector[2]>reference[2]+this.sensivity) { vector[2]>reference[2]+this.sensivity) {
this.reference=vector; this.reference=vector;
return vector; return vector;
} else { } else {
return null; return null;
} }
} }
public void setSensivity(double sensivity) { public void setSensivity(double sensivity) {
this.sensivity=sensivity; this.sensivity=sensivity;
} }
public double getSensivity() { public double getSensivity() {
return this.sensivity; return this.sensivity;
} }
} }

View File

@@ -30,33 +30,33 @@ package org.wiigee.filter;
* @author Benjamin 'BePo' Poppinga * @author Benjamin 'BePo' Poppinga
*/ */
public abstract class Filter { public abstract class Filter {
/*** /***
* The actual called method to filter anything. It checks if the vector is * The actual called method to filter anything. It checks if the vector is
* already set to NULL by another filter and won't process it anymore. If it's * already set to NULL by another filter and won't process it anymore. If it's
* not NULL it would be forwarded to the actual implemented method - filterAlgorithm(). * not NULL it would be forwarded to the actual implemented method - filterAlgorithm().
* @param vector The acceleration vector, encoding: 0/x, 1/y, 2/z * @param vector The acceleration vector, encoding: 0/x, 1/y, 2/z
* @return a new, filtered acceleration vector, encoded the same way * @return a new, filtered acceleration vector, encoded the same way
*/ */
public double[] filter(double[] vector) { public double[] filter(double[] vector) {
if(vector==null) { if(vector==null) {
return null; return null;
} else { } else {
return filterAlgorithm(vector); return filterAlgorithm(vector);
} }
} }
/*** /***
* A filter receives a triple of acceleration values within the variable 'vector'. * A filter receives a triple of acceleration values within the variable 'vector'.
* It's encoded as vector[0]=x, vector[1]=y, vector[2]=z. This is not an object since the * It's encoded as vector[0]=x, vector[1]=y, vector[2]=z. This is not an object since the
* processing of the filter should be really fast, since every acceleration of the wiimote * processing of the filter should be really fast, since every acceleration of the wiimote
* passes the filter. * passes the filter.
* @param vector * @param vector
* @param absvalue * @param absvalue
* @return * @return
*/ */
abstract public double[] filterAlgorithm(double[] vector); abstract public double[] filterAlgorithm(double[] vector);
abstract public void reset(); abstract public void reset();
} }

View File

@@ -31,18 +31,18 @@ package org.wiigee.filter;
* @author Benjamin 'BePo' Poppinga * @author Benjamin 'BePo' Poppinga
*/ */
public class IdleStateFilter extends Filter { public class IdleStateFilter extends Filter {
private double sensivity; private double sensivity;
/** /**
* Since an acceleration sensor usually provides information even * Since an acceleration sensor usually provides information even
* if it doesn't move, this filter removes the data if it's in the * if it doesn't move, this filter removes the data if it's in the
* idle state. * idle state.
*/ */
public IdleStateFilter() { public IdleStateFilter() {
super(); super();
this.sensivity = 0.1; this.sensivity = 0.1;
} }
@Override @Override
public void reset() { public void reset() {
@@ -50,37 +50,37 @@ public class IdleStateFilter extends Filter {
} }
@Override @Override
public double[] filterAlgorithm(double[] vector) { public double[] filterAlgorithm(double[] vector) {
// calculate values needed for filtering: // calculate values needed for filtering:
// absolute value // absolute value
double absvalue = Math.sqrt((vector[0]*vector[0])+ double absvalue = Math.sqrt((vector[0]*vector[0])+
(vector[1]*vector[1])+(vector[2]*vector[2])); (vector[1]*vector[1])+(vector[2]*vector[2]));
// filter formulaes and return values // filter formulaes and return values
if(absvalue > 1+this.sensivity || if(absvalue > 1+this.sensivity ||
absvalue < 1-this.sensivity) { absvalue < 1-this.sensivity) {
return vector; return vector;
} else { } else {
return null; return null;
} }
} }
/** /**
* Defines the absolute value when the wiimote should react to acceleration. * Defines the absolute value when the wiimote should react to acceleration.
* This is a parameter for the first of the two filters: idle state * This is a parameter for the first of the two filters: idle state
* filter. For example: sensivity=0.2 makes the wiimote react to acceleration * filter. For example: sensivity=0.2 makes the wiimote react to acceleration
* where the absolute value is equal or greater than 1.2g. The default value 0.1 * where the absolute value is equal or greater than 1.2g. The default value 0.1
* should work well. Only change if you are sure what you're doing. * should work well. Only change if you are sure what you're doing.
* *
* @param sensivity * @param sensivity
* acceleration data values smaller than this value wouldn't be detected. * acceleration data values smaller than this value wouldn't be detected.
*/ */
public void setSensivity(double sensivity) { public void setSensivity(double sensivity) {
this.sensivity = sensivity; this.sensivity = sensivity;
} }
public double getSensivity() { public double getSensivity() {
return this.sensivity; return this.sensivity;
} }
} }

View File

@@ -34,69 +34,69 @@ import org.wiigee.device.Device;
*/ */
public class MotionDetectFilter extends Filter { public class MotionDetectFilter extends Filter {
private int motionchangetime; private int motionchangetime;
private boolean nowinmotion; private boolean nowinmotion;
private long motionstartstamp; private long motionstartstamp;
private Device device; private Device device;
/*** /***
* Detects wheather the wiimote receives acceleration or not and * Detects wheather the wiimote receives acceleration or not and
* raises an event, if the device starts or stops. This is actual a * raises an event, if the device starts or stops. This is actual a
* null filter, not manipulating anything. But looks pretty good in * null filter, not manipulating anything. But looks pretty good in
* this datatype since it could be removed easily. * this datatype since it could be removed easily.
* *
* @param wiimote The Wiimote object which is controlled by the filter. * @param wiimote The Wiimote object which is controlled by the filter.
*/ */
public MotionDetectFilter(Device device) { public MotionDetectFilter(Device device) {
super(); super();
this.device=device; this.device=device;
this.reset(); this.reset();
} }
public void reset() { public void reset() {
this.motionstartstamp=System.currentTimeMillis(); this.motionstartstamp=System.currentTimeMillis();
this.nowinmotion=false; this.nowinmotion=false;
this.motionchangetime=190; this.motionchangetime=190;
} }
@Override @Override
public double[] filter(double[] vector) { public double[] filter(double[] vector) {
if(this.nowinmotion && if(this.nowinmotion &&
(System.currentTimeMillis()-this.motionstartstamp)>= (System.currentTimeMillis()-this.motionstartstamp)>=
this.motionchangetime) { this.motionchangetime) {
this.nowinmotion=false; this.nowinmotion=false;
this.device.fireMotionStopEvent(); this.device.fireMotionStopEvent();
} // fi } // fi
return filterAlgorithm(vector); return filterAlgorithm(vector);
} }
public double[] filterAlgorithm(double[] vector) { public double[] filterAlgorithm(double[] vector) {
if(vector!=null) { if(vector!=null) {
this.motionstartstamp=System.currentTimeMillis(); this.motionstartstamp=System.currentTimeMillis();
if(!this.nowinmotion) { if(!this.nowinmotion) {
this.nowinmotion=true; this.nowinmotion=true;
this.motionstartstamp=System.currentTimeMillis(); this.motionstartstamp=System.currentTimeMillis();
this.device.fireMotionStartEvent(); this.device.fireMotionStartEvent();
} }
} }
return vector; return vector;
} }
/** /**
* Defines the time the wiimote has to be in idle state before a new motion change * Defines the time the wiimote has to be in idle state before a new motion change
* event appears. The default value 500ms should work well, only change it if you are sure * event appears. The default value 500ms should work well, only change it if you are sure
* about what you're doing. * about what you're doing.
* @param time Time in ms * @param time Time in ms
*/ */
public void setMotionChangeTime(int time) { public void setMotionChangeTime(int time) {
this.motionchangetime=time; this.motionchangetime=time;
} }
public int getMotionChangeTime() { public int getMotionChangeTime() {
return this.motionchangetime; return this.motionchangetime;
} }
} }

View File

@@ -28,82 +28,82 @@ import java.util.Vector;
public class Classifier { public class Classifier {
private Vector<GestureModel> gesturemodel; // each gesturetype got its own private Vector<GestureModel> gesturemodel; // each gesturetype got its own
// gesturemodel in this vector // gesturemodel in this vector
private double lastprob; private double lastprob;
public Classifier() { public Classifier() {
this.gesturemodel=new Vector<GestureModel>(); this.gesturemodel=new Vector<GestureModel>();
this.lastprob=0.0; this.lastprob=0.0;
} }
/** /**
* This method recognize a specific gesture, given to the procedure. * This method recognize a specific gesture, given to the procedure.
* For classification a bayes classification algorithm is used. * For classification a bayes classification algorithm is used.
* *
* @param g gesture to classify * @param g gesture to classify
*/ */
public int classifyGesture(Gesture g) { public int classifyGesture(Gesture g) {
//Log.write("Recognizing gesture..."); //Log.write("Recognizing gesture...");
// Wert im Nenner berechnen, nach Bayes // Wert im Nenner berechnen, nach Bayes
double sum = 0; double sum = 0;
for(int i=0; i<this.gesturemodel.size(); i++) { for(int i=0; i<this.gesturemodel.size(); i++) {
sum+=this.gesturemodel.elementAt(i).getDefaultProbability()* sum+=this.gesturemodel.elementAt(i).getDefaultProbability()*
this.gesturemodel.elementAt(i).matches(g); this.gesturemodel.elementAt(i).matches(g);
} }
int recognized = -1; // which gesture has been recognized int recognized = -1; // which gesture has been recognized
double recogprob = Integer.MIN_VALUE; // probability of this gesture double recogprob = Integer.MIN_VALUE; // probability of this gesture
double probgesture = 0; // temporal value for bayes algorithm double probgesture = 0; // temporal value for bayes algorithm
double probmodel = 0; // temporal value for bayes algorithm double probmodel = 0; // temporal value for bayes algorithm
for(int i=0; i<this.gesturemodel.size(); i++) { for(int i=0; i<this.gesturemodel.size(); i++) {
//this.gesturemodel.elementAt(i).print(); // Debug //this.gesturemodel.elementAt(i).print(); // Debug
double tmpgesture = this.gesturemodel.elementAt(i).matches(g); double tmpgesture = this.gesturemodel.elementAt(i).matches(g);
double tmpmodel = this.gesturemodel.elementAt(i).getDefaultProbability(); double tmpmodel = this.gesturemodel.elementAt(i).getDefaultProbability();
if(((tmpmodel*tmpgesture)/sum)>recogprob) { if(((tmpmodel*tmpgesture)/sum)>recogprob) {
probgesture=tmpgesture; probgesture=tmpgesture;
probmodel=tmpmodel; probmodel=tmpmodel;
recogprob=((tmpmodel*tmpgesture)/sum); recogprob=((tmpmodel*tmpgesture)/sum);
recognized=i; recognized=i;
} }
} }
// a gesture could be recognized // a gesture could be recognized
if(recogprob>0 && probmodel>0 && probgesture>0 && sum>0) { if(recogprob>0 && probmodel>0 && probgesture>0 && sum>0) {
this.lastprob=recogprob; this.lastprob=recogprob;
return recognized; return recognized;
} else { } else {
// no gesture could be recognized // no gesture could be recognized
return -1; return -1;
} }
} }
public double getLastProbability() { public double getLastProbability() {
return this.lastprob; return this.lastprob;
} }
public void addGestureModel(GestureModel gm) { public void addGestureModel(GestureModel gm) {
this.gesturemodel.add(gm); this.gesturemodel.add(gm);
} }
public GestureModel getGestureModel(int id) { public GestureModel getGestureModel(int id) {
return this.gesturemodel.elementAt(id); return this.gesturemodel.elementAt(id);
} }
public Vector<GestureModel> getGestureModels() { public Vector<GestureModel> getGestureModels() {
return this.gesturemodel; return this.gesturemodel;
} }
public int getCountOfGestures() { public int getCountOfGestures() {
return this.gesturemodel.size(); return this.gesturemodel.size();
} }
public void clear() { public void clear() {
this.gesturemodel = new Vector<GestureModel>(); this.gesturemodel = new Vector<GestureModel>();
} }
} }

View File

@@ -36,120 +36,120 @@ import org.wiigee.event.AccelerationEvent;
public class Gesture implements Cloneable { public class Gesture implements Cloneable {
/** Min/MaxAcceleration setup manually? */ /** Min/MaxAcceleration setup manually? */
private boolean minmaxmanual; private boolean minmaxmanual;
private double minacc; private double minacc;
private double maxacc; private double maxacc;
/** The complete trajectory as WiimoteAccelerationEvents /** The complete trajectory as WiimoteAccelerationEvents
* as a vector. It's a vector because we don't want to * as a vector. It's a vector because we don't want to
* loose the chronology of the stored events. * loose the chronology of the stored events.
*/ */
private Vector<AccelerationEvent> data; private Vector<AccelerationEvent> data;
/** /**
* Create an empty Gesture. * Create an empty Gesture.
*/ */
public Gesture() { public Gesture() {
this.data = new Vector<AccelerationEvent>(); this.data = new Vector<AccelerationEvent>();
} }
/** /**
* Make a deep copy of another Gesture object. * Make a deep copy of another Gesture object.
* *
* @param original Another Gesture object * @param original Another Gesture object
*/ */
public Gesture(Gesture original) { public Gesture(Gesture original) {
this.data = new Vector<AccelerationEvent>(); this.data = new Vector<AccelerationEvent>();
Vector<AccelerationEvent> origin = original.getData(); Vector<AccelerationEvent> origin = original.getData();
for (int i = 0; i < origin.size(); i++) { for (int i = 0; i < origin.size(); i++) {
this.add((AccelerationEvent) origin.get(i)); this.add((AccelerationEvent) origin.get(i));
} }
} }
/** /**
* Adds a new acceleration event to this gesture. * Adds a new acceleration event to this gesture.
* *
* @param event The WiimoteAccelerationEvent to add. * @param event The WiimoteAccelerationEvent to add.
*/ */
public void add(AccelerationEvent event) { public void add(AccelerationEvent event) {
this.data.add(event); this.data.add(event);
} }
/** /**
* Returns the last acceleration added to this gesture. * Returns the last acceleration added to this gesture.
* *
* @return the last acceleration event added. * @return the last acceleration event added.
*/ */
public AccelerationEvent getLastData() { public AccelerationEvent getLastData() {
return (AccelerationEvent) this.data.get(this.data.size() - 1); return (AccelerationEvent) this.data.get(this.data.size() - 1);
} }
/** /**
* Returns the whole chronological sequence of accelerations as * Returns the whole chronological sequence of accelerations as
* a vector. * a vector.
* *
* @return chronological sequence of accelerations. * @return chronological sequence of accelerations.
*/ */
public Vector<AccelerationEvent> getData() { public Vector<AccelerationEvent> getData() {
return this.data; return this.data;
} }
/** /**
* Removes the first element of the acceleration queue of a gesture * Removes the first element of the acceleration queue of a gesture
*/ */
public void removeFirstData() { public void removeFirstData() {
this.data.remove(0); this.data.remove(0);
} }
public int getCountOfData() { public int getCountOfData() {
return this.data.size(); return this.data.size();
} }
public void setMaxAndMinAcceleration(double max, double min) { public void setMaxAndMinAcceleration(double max, double min) {
this.maxacc = max; this.maxacc = max;
this.minacc = min; this.minacc = min;
this.minmaxmanual = true; this.minmaxmanual = true;
} }
public double getMaxAcceleration() { public double getMaxAcceleration() {
if(!this.minmaxmanual) { if(!this.minmaxmanual) {
double maxacc = Double.MIN_VALUE; double maxacc = Double.MIN_VALUE;
for(int i=0; i<this.data.size(); i++) { for(int i=0; i<this.data.size(); i++) {
if(Math.abs(this.data.get(i).getX()) > maxacc) { if(Math.abs(this.data.get(i).getX()) > maxacc) {
maxacc=Math.abs(this.data.get(i).getX()); maxacc=Math.abs(this.data.get(i).getX());
} }
if(Math.abs(this.data.get(i).getY()) > maxacc) { if(Math.abs(this.data.get(i).getY()) > maxacc) {
maxacc=Math.abs(this.data.get(i).getY()); maxacc=Math.abs(this.data.get(i).getY());
} }
if(Math.abs(this.data.get(i).getZ()) > maxacc) { if(Math.abs(this.data.get(i).getZ()) > maxacc) {
maxacc=Math.abs(this.data.get(i).getZ()); maxacc=Math.abs(this.data.get(i).getZ());
} }
} }
return maxacc; return maxacc;
} else { } else {
return this.maxacc; return this.maxacc;
} }
} }
public double getMinAcceleration() { public double getMinAcceleration() {
if(!this.minmaxmanual) { if(!this.minmaxmanual) {
double minacc = Double.MAX_VALUE; double minacc = Double.MAX_VALUE;
for(int i=0; i<this.data.size(); i++) { for(int i=0; i<this.data.size(); i++) {
if(Math.abs(this.data.get(i).getX()) < minacc) { if(Math.abs(this.data.get(i).getX()) < minacc) {
minacc=Math.abs(this.data.get(i).getX()); minacc=Math.abs(this.data.get(i).getX());
} }
if(Math.abs(this.data.get(i).getY()) < minacc) { if(Math.abs(this.data.get(i).getY()) < minacc) {
minacc=Math.abs(this.data.get(i).getY()); minacc=Math.abs(this.data.get(i).getY());
} }
if(Math.abs(this.data.get(i).getZ()) < minacc) { if(Math.abs(this.data.get(i).getZ()) < minacc) {
minacc=Math.abs(this.data.get(i).getZ()); minacc=Math.abs(this.data.get(i).getZ());
} }
} }
return minacc; return minacc;
} else { } else {
return this.minacc; return this.minacc;
} }
} }
} }

View File

@@ -36,170 +36,170 @@ import org.wiigee.util.Log;
* @author Benjamin 'BePo' Poppinga * @author Benjamin 'BePo' Poppinga
*/ */
public class GestureModel { public class GestureModel {
/** The number of states the hidden markov model consists of */ /** The number of states the hidden markov model consists of */
private int numStates; private int numStates;
/** The number of observations for the hmm and k-mean */ /** The number of observations for the hmm and k-mean */
private int numObservations; private int numObservations;
/** The quantization component */ /** The quantization component */
private Quantizer quantizer; private Quantizer quantizer;
/** The statistical model, hidden markov model */ /** The statistical model, hidden markov model */
private HMM markovmodell; private HMM markovmodell;
/** The default probability of this gesturemodel, /** The default probability of this gesturemodel,
* needed for the bayes classifier */ * needed for the bayes classifier */
private double defaultprobability; private double defaultprobability;
/** Creates a Unit (Quantizer&Model). /** Creates a Unit (Quantizer&Model).
* *
* @param id * @param id
* int representation of a gesture "name"/class. * int representation of a gesture "name"/class.
*/ */
public GestureModel() { public GestureModel() {
this.numStates=8; // n=8 states empirical value this.numStates=8; // n=8 states empirical value
this.numObservations=14; // k=14 observations empirical value this.numObservations=14; // k=14 observations empirical value
this.markovmodell = new HMM(numStates, numObservations); // init model this.markovmodell = new HMM(numStates, numObservations); // init model
this.quantizer = new Quantizer(numStates); // init quantizer this.quantizer = new Quantizer(numStates); // init quantizer
} }
/** /**
* Trains the model to a set of motion-sequences, representing * Trains the model to a set of motion-sequences, representing
* different evaluations of a gesture * different evaluations of a gesture
* *
* @param trainsequence a vector of gestures * @param trainsequence a vector of gestures
*/ */
public void train(Vector<Gesture> trainsequence) { public void train(Vector<Gesture> trainsequence) {
// summarize all vectors from the different gestures in one // summarize all vectors from the different gestures in one
// gesture called sum. // gesture called sum.
double maxacc=0; double maxacc=0;
double minacc=0; double minacc=0;
Gesture sum = new Gesture(); Gesture sum = new Gesture();
for(int i=0; i<trainsequence.size(); i++) { for(int i=0; i<trainsequence.size(); i++) {
Vector<AccelerationEvent> t = trainsequence.elementAt(i).getData(); Vector<AccelerationEvent> t = trainsequence.elementAt(i).getData();
// add the max and min acceleration, we later get the average // add the max and min acceleration, we later get the average
maxacc+=trainsequence.elementAt(i).getMaxAcceleration(); maxacc+=trainsequence.elementAt(i).getMaxAcceleration();
minacc+=trainsequence.elementAt(i).getMinAcceleration(); minacc+=trainsequence.elementAt(i).getMinAcceleration();
// transfer every single accelerationevent of each gesture to // transfer every single accelerationevent of each gesture to
// the new gesture sum // the new gesture sum
for(int j=0; j<trainsequence.elementAt(i).getData().size(); j++) { for(int j=0; j<trainsequence.elementAt(i).getData().size(); j++) {
sum.add(t.elementAt(j)); sum.add(t.elementAt(j));
} }
} }
// get the average and set it to the sum gesture // get the average and set it to the sum gesture
sum.setMaxAndMinAcceleration(maxacc/trainsequence.size(), minacc/trainsequence.size()); sum.setMaxAndMinAcceleration(maxacc/trainsequence.size(), minacc/trainsequence.size());
// train the centeroids of the quantizer with this master gesture sum. // train the centeroids of the quantizer with this master gesture sum.
this.quantizer.trainCenteroids(sum); this.quantizer.trainCenteroids(sum);
// convert gesture vector to a sequence of discrete values // convert gesture vector to a sequence of discrete values
Vector<int[]> seqs = new Vector<int[]>(); Vector<int[]> seqs = new Vector<int[]>();
for(int i=0; i<trainsequence.size(); i++) { for(int i=0; i<trainsequence.size(); i++) {
seqs.add(this.quantizer.getObservationSequence(trainsequence.elementAt(i))); seqs.add(this.quantizer.getObservationSequence(trainsequence.elementAt(i)));
} }
// train the markov model with this derived discrete sequences // train the markov model with this derived discrete sequences
this.markovmodell.train(seqs); this.markovmodell.train(seqs);
// set the default probability for use with the bayes classifier // set the default probability for use with the bayes classifier
this.setDefaultProbability(trainsequence); this.setDefaultProbability(trainsequence);
} }
/** /**
* Returns the probability that a gesture matches to this * Returns the probability that a gesture matches to this
* gesture model. * gesture model.
* *
* @param gesture a gesture to test. * @param gesture a gesture to test.
* @return probability that the gesture belongs to this gesture * @return probability that the gesture belongs to this gesture
* model. * model.
*/ */
public double matches(Gesture gesture) { public double matches(Gesture gesture) {
int[] sequence = quantizer.getObservationSequence(gesture); int[] sequence = quantizer.getObservationSequence(gesture);
return this.markovmodell.getProbability(sequence); return this.markovmodell.getProbability(sequence);
} }
/** /**
* For debug purposes or very technical interested people. :) * For debug purposes or very technical interested people. :)
*/ */
public void printMap() { public void printMap() {
Log.write("Gesture Quantizer-Map:"); Log.write("Gesture Quantizer-Map:");
this.quantizer.printMap(); this.quantizer.printMap();
} }
/*** /***
* For debug purposes or very technical interested people. :) * For debug purposes or very technical interested people. :)
* @return * @return
*/ */
public void print() { public void print() {
Log.write("HMM-Print:"); Log.write("HMM-Print:");
this.markovmodell.print(); this.markovmodell.print();
Log.write("Quanzizer-Print:"); Log.write("Quanzizer-Print:");
this.quantizer.printMap(); this.quantizer.printMap();
} }
public int getNumStates() { public int getNumStates() {
return this.numStates; return this.numStates;
} }
public int getNumObservations() { public int getNumObservations() {
return this.numObservations; return this.numObservations;
} }
/** /**
* Returns the model probability for bayes. * Returns the model probability for bayes.
* *
* @return * @return
* the model probability * the model probability
*/ */
public double getDefaultProbability() { public double getDefaultProbability() {
return this.defaultprobability; return this.defaultprobability;
} }
/** /**
* Since the bayes classifier needs a model probability for * Since the bayes classifier needs a model probability for
* each model this has to be set once after training. As model * each model this has to be set once after training. As model
* probability the average probability value has been choosen. * probability the average probability value has been choosen.
* *
* TODO: try lowest or highest model probability as alternative * TODO: try lowest or highest model probability as alternative
* *
* @param defsequence the vector of training sequences. * @param defsequence the vector of training sequences.
*/ */
private void setDefaultProbability(Vector<Gesture> defsequence) { private void setDefaultProbability(Vector<Gesture> defsequence) {
double prob=0; double prob=0;
for(int i=0; i<defsequence.size(); i++) { for(int i=0; i<defsequence.size(); i++) {
prob+=this.matches(defsequence.elementAt(i)); prob+=this.matches(defsequence.elementAt(i));
} }
this.defaultprobability=(prob)/defsequence.size(); this.defaultprobability=(prob)/defsequence.size();
} }
public void setDefaultProbability(double prob) { public void setDefaultProbability(double prob) {
this.defaultprobability = prob; this.defaultprobability = prob;
Log.write("def-prob. set to = "+this.defaultprobability); Log.write("def-prob. set to = "+this.defaultprobability);
} }
public Quantizer getQuantizer() { public Quantizer getQuantizer() {
return this.quantizer; return this.quantizer;
} }
public void setQuantizer(Quantizer q) { public void setQuantizer(Quantizer q) {
this.quantizer = q; this.quantizer = q;
} }
public HMM getHMM() { public HMM getHMM() {
return this.markovmodell; return this.markovmodell;
} }
public void setHMM(HMM hmm) { public void setHMM(HMM hmm) {
this.markovmodell = hmm; this.markovmodell = hmm;
} }
} }

View File

@@ -39,276 +39,276 @@ import org.wiigee.util.Log;
*/ */
public class HMM { public class HMM {
/** The number of states */ /** The number of states */
protected int numStates; protected int numStates;
/** The number of observations */ /** The number of observations */
protected int numObservations; protected int numObservations;
/** The initial probabilities for each state: p[state] */ /** The initial probabilities for each state: p[state] */
protected double pi[]; protected double pi[];
/** The state change probability to switch from state A to /** The state change probability to switch from state A to
* state B: a[stateA][stateB] */ * state B: a[stateA][stateB] */
protected double a[][]; protected double a[][];
/** The probability to emit symbol S in state A: b[stateA][symbolS] */ /** The probability to emit symbol S in state A: b[stateA][symbolS] */
protected double b[][]; protected double b[][];
/** /**
* Initialize the Hidden Markov Model in a left-to-right version. * Initialize the Hidden Markov Model in a left-to-right version.
* *
* @param numStates Number of states * @param numStates Number of states
* @param numObservations Number of observations * @param numObservations Number of observations
*/ */
public HMM(int numStates, int numObservations) { public HMM(int numStates, int numObservations) {
this.numStates = numStates; this.numStates = numStates;
this.numObservations = numObservations; this.numObservations = numObservations;
pi = new double[numStates]; pi = new double[numStates];
a = new double[numStates][numStates]; a = new double[numStates][numStates];
b = new double[numStates][numObservations]; b = new double[numStates][numObservations];
this.reset(); this.reset();
} }
/** /**
* Reset the Hidden Markov Model to the initial left-to-right values. * Reset the Hidden Markov Model to the initial left-to-right values.
* *
*/ */
private void reset() { private void reset() {
int jumplimit = 2; int jumplimit = 2;
// set startup probability // set startup probability
pi[0] = 1; pi[0] = 1;
for(int i=1; i<numStates; i++) { for(int i=1; i<numStates; i++) {
pi[i] = 0; pi[i] = 0;
} }
// set state change probabilities in the left-to-right version // set state change probabilities in the left-to-right version
// NOTE: i now that this is dirty and very static. :) // NOTE: i now that this is dirty and very static. :)
for(int i=0; i<numStates; i++) { for(int i=0; i<numStates; i++) {
for(int j=0; j<numStates; j++) { for(int j=0; j<numStates; j++) {
if(i==numStates-1 && j==numStates-1) { // last row if(i==numStates-1 && j==numStates-1) { // last row
a[i][j] = 1.0; a[i][j] = 1.0;
} else if(i==numStates-2 && j==numStates-2) { // next to last row } else if(i==numStates-2 && j==numStates-2) { // next to last row
a[i][j] = 0.5; a[i][j] = 0.5;
} else if(i==numStates-2 && j==numStates-1) { // next to last row } else if(i==numStates-2 && j==numStates-1) { // next to last row
a[i][j] = 0.5; a[i][j] = 0.5;
} else if(i<=j && i>j-jumplimit-1) { } else if(i<=j && i>j-jumplimit-1) {
a[i][j] = 1.0/(jumplimit+1); a[i][j] = 1.0/(jumplimit+1);
} else { } else {
a[i][j] = 0.0; a[i][j] = 0.0;
} }
} }
} }
// emission probability // emission probability
for(int i=0; i<numStates; i++) { for(int i=0; i<numStates; i++) {
for(int j=0; j<numObservations; j++) { for(int j=0; j<numObservations; j++) {
b[i][j] = 1.0/(double)numObservations; b[i][j] = 1.0/(double)numObservations;
} }
} }
} }
/** /**
* Trains the Hidden Markov Model with multiple sequences. * Trains the Hidden Markov Model with multiple sequences.
* This method is normally not known to basic hidden markov * This method is normally not known to basic hidden markov
* models, because they usually use the Baum-Welch-Algorithm. * models, because they usually use the Baum-Welch-Algorithm.
* This method is NOT the traditional Baum-Welch-Algorithm. * This method is NOT the traditional Baum-Welch-Algorithm.
* *
* If you want to know in detail how it works please consider * If you want to know in detail how it works please consider
* my Individuelles Projekt paper on the wiigee Homepage. Also * my Individuelles Projekt paper on the wiigee Homepage. Also
* there exist some english literature on the world wide web. * there exist some english literature on the world wide web.
* Try to search for some papers by Rabiner or have a look at * Try to search for some papers by Rabiner or have a look at
* Vesa-Matti Mäntylä - "Discrete Hidden Markov Models with * Vesa-Matti Mäntylä - "Discrete Hidden Markov Models with
* application to isolated user-dependent hand gesture recognition". * application to isolated user-dependent hand gesture recognition".
* *
*/ */
public void train(Vector<int[]> trainsequence) { public void train(Vector<int[]> trainsequence) {
double[][] a_new = new double[a.length][a.length]; double[][] a_new = new double[a.length][a.length];
double[][] b_new = new double[b.length][b[0].length]; double[][] b_new = new double[b.length][b[0].length];
// re calculate state change probability a // re calculate state change probability a
for(int i=0; i<a.length; i++) { for(int i=0; i<a.length; i++) {
for(int j=0; j<a[i].length; j++) { for(int j=0; j<a[i].length; j++) {
double zaehler=0; double zaehler=0;
double nenner=0; double nenner=0;
for(int k=0; k<trainsequence.size(); k++) { for(int k=0; k<trainsequence.size(); k++) {
int[] sequence = trainsequence.elementAt(k); int[] sequence = trainsequence.elementAt(k);
double[][] fwd = this.forwardProc(sequence); double[][] fwd = this.forwardProc(sequence);
double[][] bwd = this.backwardProc(sequence); double[][] bwd = this.backwardProc(sequence);
double prob = this.getProbability(sequence); double prob = this.getProbability(sequence);
double zaehler_innersum=0; double zaehler_innersum=0;
double nenner_innersum=0; double nenner_innersum=0;
for(int t=0; t<sequence.length-1; t++) { for(int t=0; t<sequence.length-1; t++) {
zaehler_innersum+=fwd[i][t]*a[i][j]*b[j][sequence[t+1]]*bwd[j][t+1]; zaehler_innersum+=fwd[i][t]*a[i][j]*b[j][sequence[t+1]]*bwd[j][t+1];
nenner_innersum+=fwd[i][t]*bwd[i][t]; nenner_innersum+=fwd[i][t]*bwd[i][t];
} }
zaehler+=(1/prob)*zaehler_innersum; zaehler+=(1/prob)*zaehler_innersum;
nenner+=(1/prob)*nenner_innersum; nenner+=(1/prob)*nenner_innersum;
} // k } // k
a_new[i][j] = zaehler/nenner; a_new[i][j] = zaehler/nenner;
} // j } // j
} // i } // i
// re calculate emission probability b // re calculate emission probability b
for(int i=0; i<b.length; i++) { // zustaende for(int i=0; i<b.length; i++) { // zustaende
for(int j=0; j<b[i].length; j++) { // symbole for(int j=0; j<b[i].length; j++) { // symbole
double zaehler=0; double zaehler=0;
double nenner=0; double nenner=0;
for(int k=0; k<trainsequence.size(); k++) { for(int k=0; k<trainsequence.size(); k++) {
int[] sequence = trainsequence.elementAt(k); int[] sequence = trainsequence.elementAt(k);
double[][] fwd = this.forwardProc(sequence); double[][] fwd = this.forwardProc(sequence);
double[][] bwd = this.backwardProc(sequence); double[][] bwd = this.backwardProc(sequence);
double prob = this.getProbability(sequence); double prob = this.getProbability(sequence);
double zaehler_innersum=0; double zaehler_innersum=0;
double nenner_innersum=0; double nenner_innersum=0;
for(int t=0; t<sequence.length-1; t++) { for(int t=0; t<sequence.length-1; t++) {
if(sequence[t]==j) { if(sequence[t]==j) {
zaehler_innersum+=fwd[i][t]*bwd[i][t]; zaehler_innersum+=fwd[i][t]*bwd[i][t];
} }
nenner_innersum+=fwd[i][t]*bwd[i][t]; nenner_innersum+=fwd[i][t]*bwd[i][t];
} }
zaehler+=(1/prob)*zaehler_innersum; zaehler+=(1/prob)*zaehler_innersum;
nenner+=(1/prob)*nenner_innersum; nenner+=(1/prob)*nenner_innersum;
} // k } // k
b_new[i][j] = zaehler/nenner; b_new[i][j] = zaehler/nenner;
} // j } // j
} // i } // i
this.a=a_new; this.a=a_new;
this.b=b_new; this.b=b_new;
} }
/** /**
* Traditional Forward Algorithm. * Traditional Forward Algorithm.
* *
* @param o the observationsequence O * @param o the observationsequence O
* @return Array[State][Time] * @return Array[State][Time]
* *
*/ */
protected double[][] forwardProc(int[] o) { protected double[][] forwardProc(int[] o) {
double[][] f = new double[numStates][o.length]; double[][] f = new double[numStates][o.length];
for (int l = 0; l < f.length; l++) { for (int l = 0; l < f.length; l++) {
f[l][0] = pi[l] * b[l][o[0]]; f[l][0] = pi[l] * b[l][o[0]];
} }
for (int i = 1; i < o.length; i++) { for (int i = 1; i < o.length; i++) {
for (int k = 0; k < f.length; k++) { for (int k = 0; k < f.length; k++) {
double sum = 0; double sum = 0;
for (int l = 0; l < numStates; l++) { for (int l = 0; l < numStates; l++) {
sum += f[l][i-1] * a[l][k]; sum += f[l][i-1] * a[l][k];
} }
f[k][i] = sum * b[k][o[i]]; f[k][i] = sum * b[k][o[i]];
} }
} }
return f; return f;
} }
/** /**
* Returns the probability that a observation sequence O belongs * Returns the probability that a observation sequence O belongs
* to this Hidden Markov Model without using the bayes classifier. * to this Hidden Markov Model without using the bayes classifier.
* Internally the well known forward algorithm is used. * Internally the well known forward algorithm is used.
* *
* @param o observation sequence * @param o observation sequence
* @return probability that sequence o belongs to this hmm * @return probability that sequence o belongs to this hmm
*/ */
public double getProbability(int[] o) { public double getProbability(int[] o) {
double prob = 0.0; double prob = 0.0;
double[][] forward = this.forwardProc(o); double[][] forward = this.forwardProc(o);
// add probabilities // add probabilities
for (int i = 0; i < forward.length; i++) { // for every state for (int i = 0; i < forward.length; i++) { // for every state
prob += forward[i][forward[i].length - 1]; prob += forward[i][forward[i].length - 1];
} }
return prob; return prob;
} }
/** /**
* Backward algorithm. * Backward algorithm.
* *
* @param o observation sequence o * @param o observation sequence o
* @return Array[State][Time] * @return Array[State][Time]
*/ */
protected double[][] backwardProc(int[] o) { protected double[][] backwardProc(int[] o) {
int T = o.length; int T = o.length;
double[][] bwd = new double[numStates][T]; double[][] bwd = new double[numStates][T];
/* Basisfall */ /* Basisfall */
for (int i = 0; i < numStates; i++) for (int i = 0; i < numStates; i++)
bwd[i][T - 1] = 1; bwd[i][T - 1] = 1;
/* Induktion */ /* Induktion */
for (int t = T - 2; t >= 0; t--) { for (int t = T - 2; t >= 0; t--) {
for (int i = 0; i < numStates; i++) { for (int i = 0; i < numStates; i++) {
bwd[i][t] = 0; bwd[i][t] = 0;
for (int j = 0; j < numStates; j++) for (int j = 0; j < numStates; j++)
bwd[i][t] += (bwd[j][t + 1] * a[i][j] * b[j][o[t + 1]]); bwd[i][t] += (bwd[j][t + 1] * a[i][j] * b[j][o[t + 1]]);
} }
} }
return bwd; return bwd;
} }
/** /**
* Prints everything about this model, including * Prints everything about this model, including
* all values. For debug purposes or if you want * all values. For debug purposes or if you want
* to comprehend what happend to the model. * to comprehend what happend to the model.
* *
*/ */
public void print() { public void print() {
DecimalFormat fmt = new DecimalFormat(); DecimalFormat fmt = new DecimalFormat();
fmt.setMinimumFractionDigits(5); fmt.setMinimumFractionDigits(5);
fmt.setMaximumFractionDigits(5); fmt.setMaximumFractionDigits(5);
for (int i = 0; i < numStates; i++) for (int i = 0; i < numStates; i++)
Log.write("pi(" + i + ") = " + fmt.format(pi[i])); Log.write("pi(" + i + ") = " + fmt.format(pi[i]));
Log.write(""); Log.write("");
for (int i = 0; i < numStates; i++) { for (int i = 0; i < numStates; i++) {
for (int j = 0; j < numStates; j++) for (int j = 0; j < numStates; j++)
Log.write("a(" + i + "," + j + ") = " Log.write("a(" + i + "," + j + ") = "
+ fmt.format(a[i][j]) + " "); + fmt.format(a[i][j]) + " ");
Log.write(""); Log.write("");
} }
Log.write(""); Log.write("");
for (int i = 0; i < numStates; i++) { for (int i = 0; i < numStates; i++) {
for (int k = 0; k < numObservations; k++) for (int k = 0; k < numObservations; k++)
Log.write("b(" + i + "," + k + ") = " Log.write("b(" + i + "," + k + ") = "
+ fmt.format(b[i][k]) + " "); + fmt.format(b[i][k]) + " ");
Log.write(""); Log.write("");
} }
} }
public double[] getPi() { public double[] getPi() {
return this.pi; return this.pi;
} }
public void setPi(double[] pi) { public void setPi(double[] pi) {
this.pi = pi; this.pi = pi;
} }
public double[][] getA() { public double[][] getA() {
return this.a; return this.a;
} }
public void setA(double[][] a) { public void setA(double[][] a) {
this.a = a; this.a = a;
} }
public double[][] getB() { public double[][] getB() {
return this.b; return this.b;
} }
public void setB(double[][] b) { public void setB(double[][] b) {
this.b=b; this.b=b;
} }
} }

View File

@@ -40,410 +40,410 @@ import org.wiigee.util.Log;
*/ */
public class PreciseHMM { public class PreciseHMM {
/** The number of states */ /** The number of states */
private int numStates; private int numStates;
/** The number of observations */ /** The number of observations */
private int sigmaSize; private int sigmaSize;
/** The initial probabilities for each state: p[state] */ /** The initial probabilities for each state: p[state] */
public double pi[]; public double pi[];
/** The state change probability to switch from state A to /** The state change probability to switch from state A to
* state B: a[stateA][stateB] */ * state B: a[stateA][stateB] */
public double a[][]; public double a[][];
/** The probability to emit symbol S in state A: b[stateA][symbolS] */ /** The probability to emit symbol S in state A: b[stateA][symbolS] */
public double b[][]; public double b[][];
/** /**
* Initialize the Hidden Markov Model in a left-to-right version. * Initialize the Hidden Markov Model in a left-to-right version.
* *
* @param numStates Number of states * @param numStates Number of states
* @param sigmaSize Number of observations * @param sigmaSize Number of observations
*/ */
public PreciseHMM(int numStates, int sigmaSize) { public PreciseHMM(int numStates, int sigmaSize) {
this.numStates = numStates; this.numStates = numStates;
this.sigmaSize = sigmaSize; this.sigmaSize = sigmaSize;
pi = new double[numStates]; pi = new double[numStates];
a = new double[numStates][numStates]; a = new double[numStates][numStates];
b = new double[numStates][sigmaSize]; b = new double[numStates][sigmaSize];
this.reset(); this.reset();
} }
/** /**
* Reset the Hidden Markov Model to the initial left-to-right values. * Reset the Hidden Markov Model to the initial left-to-right values.
* *
*/ */
private void reset() { private void reset() {
int jumplimit = 2; int jumplimit = 2;
// set startup probability // set startup probability
pi[0] = 1.0; pi[0] = 1.0;
for(int i=1; i<numStates; i++) { for(int i=1; i<numStates; i++) {
pi[i] = 0; pi[i] = 0;
} }
// set state change probabilities in the left-to-right version // set state change probabilities in the left-to-right version
// NOTE: i now that this is dirty and very static. :) // NOTE: i now that this is dirty and very static. :)
for(int i=0; i<numStates; i++) { for(int i=0; i<numStates; i++) {
for(int j=0; j<numStates; j++) { for(int j=0; j<numStates; j++) {
if(i==numStates-1 && j==numStates-1) { // last row if(i==numStates-1 && j==numStates-1) { // last row
a[i][j] = 1.0; a[i][j] = 1.0;
} else if(i==numStates-2 && j==numStates-2) { // next to last row } else if(i==numStates-2 && j==numStates-2) { // next to last row
a[i][j] = 0.5; a[i][j] = 0.5;
} else if(i==numStates-2 && j==numStates-1) { // next to last row } else if(i==numStates-2 && j==numStates-1) { // next to last row
a[i][j] = 0.5; a[i][j] = 0.5;
} else if(i<=j && i>j-jumplimit-1) { } else if(i<=j && i>j-jumplimit-1) {
a[i][j] = 1.0/(jumplimit+1); a[i][j] = 1.0/(jumplimit+1);
} else { } else {
a[i][j] = 0.0; a[i][j] = 0.0;
} }
} }
} }
// emission probability // emission probability
for(int i=0; i<numStates; i++) { for(int i=0; i<numStates; i++) {
for(int j=0; j<sigmaSize; j++) { for(int j=0; j<sigmaSize; j++) {
b[i][j] = 1.0/(double)sigmaSize; b[i][j] = 1.0/(double)sigmaSize;
} }
} }
} }
/** /**
* Trains the Hidden Markov Model with multiple sequences. * Trains the Hidden Markov Model with multiple sequences.
* This method is normally not known to basic hidden markov * This method is normally not known to basic hidden markov
* models, because they usually use the Baum-Welch-Algorithm. * models, because they usually use the Baum-Welch-Algorithm.
* This method is NOT the traditional Baum-Welch-Algorithm. * This method is NOT the traditional Baum-Welch-Algorithm.
* *
* If you want to know in detail how it works please consider * If you want to know in detail how it works please consider
* my Individuelles Projekt paper on the wiigee Homepage. Also * my Individuelles Projekt paper on the wiigee Homepage. Also
* there exist some english literature on the world wide web. * there exist some english literature on the world wide web.
* Try to search for some papers by Rabiner or have a look at * Try to search for some papers by Rabiner or have a look at
* Vesa-Matti Mäntylä - "Discrete Hidden Markov Models with * Vesa-Matti Mäntylä - "Discrete Hidden Markov Models with
* application to isolated user-dependent hand gesture recognition". * application to isolated user-dependent hand gesture recognition".
* *
*/ */
public void train(Vector<int[]> trainsequence) { public void train(Vector<int[]> trainsequence) {
double[][] a_new = new double[a.length][a.length]; double[][] a_new = new double[a.length][a.length];
double[][] b_new = new double[b.length][b[0].length]; double[][] b_new = new double[b.length][b[0].length];
// re calculate state change probability a // re calculate state change probability a
for(int i=0; i<a.length; i++) { for(int i=0; i<a.length; i++) {
for(int j=0; j<a[i].length; j++) { for(int j=0; j<a[i].length; j++) {
double zaehler=0; double zaehler=0;
double nenner=0; double nenner=0;
for(int k=0; k<trainsequence.size(); k++) { for(int k=0; k<trainsequence.size(); k++) {
//this.reset(); //this.reset();
int[] sequence = trainsequence.elementAt(k); int[] sequence = trainsequence.elementAt(k);
double[] sf = this.calculateScalingFactor(sequence); double[] sf = this.calculateScalingFactor(sequence);
double[][] fwd = this.scaledForwardProc(sequence); double[][] fwd = this.scaledForwardProc(sequence);
double[][] bwd = this.scaledBackwardProc(sequence, sf); double[][] bwd = this.scaledBackwardProc(sequence, sf);
double zaehler_innersum=0; double zaehler_innersum=0;
double nenner_innersum=0; double nenner_innersum=0;
for(int t=0; t<sequence.length-1; t++) { for(int t=0; t<sequence.length-1; t++) {
zaehler_innersum+=fwd[i][t]*a[i][j]*b[j][sequence[t+1]]*bwd[j][t+1]*sf[t+1]; zaehler_innersum+=fwd[i][t]*a[i][j]*b[j][sequence[t+1]]*bwd[j][t+1]*sf[t+1];
nenner_innersum+=fwd[i][t]*bwd[i][t]; nenner_innersum+=fwd[i][t]*bwd[i][t];
} }
zaehler+=zaehler_innersum; zaehler+=zaehler_innersum;
nenner+=nenner_innersum; nenner+=nenner_innersum;
} // k } // k
a_new[i][j] = zaehler/nenner; a_new[i][j] = zaehler/nenner;
} // j } // j
} // i } // i
// re calculate emission probability b // re calculate emission probability b
for(int i=0; i<b.length; i++) { // zustaende for(int i=0; i<b.length; i++) { // zustaende
for(int j=0; j<b[i].length; j++) { // symbole for(int j=0; j<b[i].length; j++) { // symbole
double zaehler=0; double zaehler=0;
double nenner=0; double nenner=0;
for(int k=0; k<trainsequence.size(); k++) { for(int k=0; k<trainsequence.size(); k++) {
//this.reset(); //this.reset();
int[] sequence = trainsequence.elementAt(k); int[] sequence = trainsequence.elementAt(k);
double[] sf = this.calculateScalingFactor(sequence); double[] sf = this.calculateScalingFactor(sequence);
double[][] fwd = this.scaledForwardProc(sequence); double[][] fwd = this.scaledForwardProc(sequence);
double[][] bwd = this.scaledBackwardProc(sequence, sf); double[][] bwd = this.scaledBackwardProc(sequence, sf);
double zaehler_innersum=0; double zaehler_innersum=0;
double nenner_innersum=0; double nenner_innersum=0;
for(int t=0; t<sequence.length-1; t++) { for(int t=0; t<sequence.length-1; t++) {
if(sequence[t]==j) { if(sequence[t]==j) {
zaehler_innersum+=fwd[i][t]*bwd[i][t]*sf[t]; zaehler_innersum+=fwd[i][t]*bwd[i][t]*sf[t];
} }
nenner_innersum+=fwd[i][t]*bwd[i][t]*sf[t]; nenner_innersum+=fwd[i][t]*bwd[i][t]*sf[t];
} }
zaehler+=zaehler_innersum; zaehler+=zaehler_innersum;
nenner+=nenner_innersum; nenner+=nenner_innersum;
} // k } // k
b_new[i][j] = zaehler/nenner; b_new[i][j] = zaehler/nenner;
} // j } // j
} // i } // i
this.a=a_new; this.a=a_new;
this.b=b_new; this.b=b_new;
} }
private double[] calculateScalingFactor(int[] sequence) { private double[] calculateScalingFactor(int[] sequence) {
// for all indexing: [state][time] // for all indexing: [state][time]
double[][] fwd = this.forwardProc(sequence); // normal double[][] fwd = this.forwardProc(sequence); // normal
double[][] help = new double[fwd.length][fwd[0].length]; double[][] help = new double[fwd.length][fwd[0].length];
double[][] scaled = new double[fwd.length][fwd[0].length]; double[][] scaled = new double[fwd.length][fwd[0].length];
double[] sf = new double[sequence.length]; double[] sf = new double[sequence.length];
// ************** BASIS ************* // ************** BASIS *************
// Basis, fixed t=0 // Basis, fixed t=0
// setup, because needed for further calculations // setup, because needed for further calculations
for(int i=0; i<help.length; i++) { for(int i=0; i<help.length; i++) {
help[i][0] = fwd[i][0]; help[i][0] = fwd[i][0];
} }
// setup initial scaled array // setup initial scaled array
double sum0 = 0; double sum0 = 0;
for(int i=0; i<help.length; i++) { for(int i=0; i<help.length; i++) {
sum0+=help[i][0]; sum0+=help[i][0];
} }
for(int i=0; i<scaled.length; i++) { for(int i=0; i<scaled.length; i++) {
scaled[i][0] = help[i][0] / sum0; scaled[i][0] = help[i][0] / sum0;
} }
// calculate scaling factor // calculate scaling factor
sf[0] = 1/sum0; sf[0] = 1/sum0;
// **************** INDUCTION *************** // **************** INDUCTION ***************
// end of fixed t = 0 // end of fixed t = 0
// starting with t>1 to sequence.length // starting with t>1 to sequence.length
// induction, further calculations // induction, further calculations
for(int t=1; t<sequence.length; t++) { for(int t=1; t<sequence.length; t++) {
// calculate help // calculate help
for(int i=0; i<help.length; i++) { for(int i=0; i<help.length; i++) {
for(int j=0; j<this.numStates; j++) { for(int j=0; j<this.numStates; j++) {
help[i][t]+=scaled[j][t-1]*a[j][i]*b[i][sequence[t]]; help[i][t]+=scaled[j][t-1]*a[j][i]*b[i][sequence[t]];
} }
} }
double sum = 0; double sum = 0;
for(int i=0; i<help.length; i++) { for(int i=0; i<help.length; i++) {
sum+=help[i][t]; sum+=help[i][t];
} }
for(int i=0; i<scaled.length; i++) { for(int i=0; i<scaled.length; i++) {
scaled[i][t] = help[i][t] / sum; scaled[i][t] = help[i][t] / sum;
} }
// calculate scaling factor // calculate scaling factor
sf[t] = 1 / sum; sf[t] = 1 / sum;
} // t } // t
return sf; return sf;
} // calculateScalingFactor } // calculateScalingFactor
/*** /***
* Returns the scaled Forward variable. * Returns the scaled Forward variable.
* TODO: Maybe try out if the other precalculated method is faster. * TODO: Maybe try out if the other precalculated method is faster.
* @param sequence * @param sequence
* @return * @return
*/ */
private double[][] scaledForwardProc(int[] sequence) { private double[][] scaledForwardProc(int[] sequence) {
double[][] fwd = this.forwardProc(sequence); double[][] fwd = this.forwardProc(sequence);
double[][] out = new double[fwd.length][fwd[0].length]; double[][] out = new double[fwd.length][fwd[0].length];
for(int i=0; i<fwd.length; i++) { for(int i=0; i<fwd.length; i++) {
for(int t=0; t<sequence.length; t++) { for(int t=0; t<sequence.length; t++) {
double sum = 0; double sum = 0;
for(int j=0; j<fwd.length; j++) { for(int j=0; j<fwd.length; j++) {
sum+=fwd[j][t]; sum+=fwd[j][t];
} }
out[i][t] = fwd[i][t] / sum; out[i][t] = fwd[i][t] / sum;
} }
} }
return out; return out;
} }
private double[][] scaledBackwardProc(int[] sequence, double[] sf) { private double[][] scaledBackwardProc(int[] sequence, double[] sf) {
double[][] bwd = this.backwardProc(sequence); double[][] bwd = this.backwardProc(sequence);
double[][] out = new double[bwd.length][bwd[0].length]; double[][] out = new double[bwd.length][bwd[0].length];
for(int i=0; i<bwd.length; i++) { for(int i=0; i<bwd.length; i++) {
for(int t=0; t<sequence.length; t++) { for(int t=0; t<sequence.length; t++) {
out[i][t]=1; out[i][t]=1;
for(int r=t+1; r<sequence.length; r++) { for(int r=t+1; r<sequence.length; r++) {
out[i][t]*=sf[r]*bwd[i][t]; out[i][t]*=sf[r]*bwd[i][t];
} }
} }
} }
return out; return out;
} }
/** /**
* Returns the probability that a observation sequence O belongs * Returns the probability that a observation sequence O belongs
* to this Hidden Markov Model without using the bayes classifier. * to this Hidden Markov Model without using the bayes classifier.
* Internally the well known forward algorithm is used. * Internally the well known forward algorithm is used.
* *
* @param o observation sequence * @param o observation sequence
* @return probability that sequence o belongs to this hmm * @return probability that sequence o belongs to this hmm
*/ */
public double getProbability(int[] o) { public double getProbability(int[] o) {
return scaledViterbi(o); return scaledViterbi(o);
//return sProbability(o); //return sProbability(o);
/*double prob = 0.0; /*double prob = 0.0;
double[][] forward = this.forwardProc(o); double[][] forward = this.forwardProc(o);
// add probabilities // add probabilities
for (int i = 0; i < forward.length; i++) { // for every state for (int i = 0; i < forward.length; i++) { // for every state
prob += forward[i][forward[i].length - 1]; prob += forward[i][forward[i].length - 1];
} }
return prob;*/ return prob;*/
} }
public double sProbability(int[] o) { public double sProbability(int[] o) {
double prod = 1.0; double prod = 1.0;
double[][] fwd = this.scaledForwardProc(o); double[][] fwd = this.scaledForwardProc(o);
for(int t=0; t<o.length; t++) { for(int t=0; t<o.length; t++) {
double sum = 0.0; double sum = 0.0;
for(int i=0; i<this.numStates; i++) { for(int i=0; i<this.numStates; i++) {
sum+=fwd[i][t]; sum+=fwd[i][t];
} }
sum = 1/sum; sum = 1/sum;
prod*=sum; prod*=sum;
} }
return 1/prod; return 1/prod;
} }
public double scaledViterbi(int[] o) { public double scaledViterbi(int[] o) {
double[][] phi = new double[this.numStates][o.length]; //phi[states][oseq] double[][] phi = new double[this.numStates][o.length]; //phi[states][oseq]
// init // init
for(int i=0; i<this.numStates; i++) { for(int i=0; i<this.numStates; i++) {
phi[i][0] = Math.log(pi[i]) + Math.log(b[i][o[0]]); phi[i][0] = Math.log(pi[i]) + Math.log(b[i][o[0]]);
} }
// induction // induction
for(int t=1; t<o.length; t++) { for(int t=1; t<o.length; t++) {
for(int j=0; j<this.numStates; j++) { for(int j=0; j<this.numStates; j++) {
double max = Double.NEGATIVE_INFINITY; double max = Double.NEGATIVE_INFINITY;
for(int i=0; i<this.numStates; i++) { for(int i=0; i<this.numStates; i++) {
double val = phi[i][t-1] + Math.log(this.a[i][j]); double val = phi[i][t-1] + Math.log(this.a[i][j]);
if(val>max) { if(val>max) {
max = val; max = val;
} }
} }
phi[j][t] = max + Math.log(this.b[j][o[t]]); phi[j][t] = max + Math.log(this.b[j][o[t]]);
} }
} }
// conclusion // conclusion
double lp = Double.NEGATIVE_INFINITY; double lp = Double.NEGATIVE_INFINITY;
for(int i=0; i<this.numStates; i++) { for(int i=0; i<this.numStates; i++) {
if(phi[i][o.length-1]>lp) { if(phi[i][o.length-1]>lp) {
lp = phi[i][o.length-1]; lp = phi[i][o.length-1];
} }
} }
//Log.write("log p = "+lp); //Log.write("log p = "+lp);
//return lp; //return lp;
// we now have log10(p) calculated, transform to p. // we now have log10(p) calculated, transform to p.
Log.write("prob = "+Math.exp(lp)); Log.write("prob = "+Math.exp(lp));
return Math.exp(lp); return Math.exp(lp);
//return Math.pow(10, lp); //return Math.pow(10, lp);
} }
/** /**
* Traditional Forward Algorithm. * Traditional Forward Algorithm.
* *
* @param o the observationsequence O * @param o the observationsequence O
* @return Array[State][Time] * @return Array[State][Time]
* *
*/ */
private double[][] forwardProc(int[] o) { private double[][] forwardProc(int[] o) {
double[][] f = new double[numStates][o.length]; double[][] f = new double[numStates][o.length];
for (int l = 0; l < f.length; l++) { for (int l = 0; l < f.length; l++) {
f[l][0] = pi[l] * b[l][o[0]]; f[l][0] = pi[l] * b[l][o[0]];
} }
for (int i = 1; i < o.length; i++) { for (int i = 1; i < o.length; i++) {
for (int k = 0; k < f.length; k++) { for (int k = 0; k < f.length; k++) {
double sum = 0; double sum = 0;
for (int l = 0; l < numStates; l++) { for (int l = 0; l < numStates; l++) {
sum += f[l][i-1] * a[l][k]; sum += f[l][i-1] * a[l][k];
} }
f[k][i] = sum * b[k][o[i]]; f[k][i] = sum * b[k][o[i]];
} }
} }
return f; return f;
} }
/** /**
* Backward algorithm. * Backward algorithm.
* *
* @param o observation sequence o * @param o observation sequence o
* @return Array[State][Time] * @return Array[State][Time]
*/ */
private double[][] backwardProc(int[] o) { private double[][] backwardProc(int[] o) {
int T = o.length; int T = o.length;
double[][] bwd = new double[numStates][T]; double[][] bwd = new double[numStates][T];
/* Basisfall */ /* Basisfall */
for (int i = 0; i < numStates; i++) for (int i = 0; i < numStates; i++)
bwd[i][T - 1] = 1; bwd[i][T - 1] = 1;
/* Induktion */ /* Induktion */
for (int t = T - 2; t >= 0; t--) { for (int t = T - 2; t >= 0; t--) {
for (int i = 0; i < numStates; i++) { for (int i = 0; i < numStates; i++) {
bwd[i][t] = 0; bwd[i][t] = 0;
for (int j = 0; j < numStates; j++) for (int j = 0; j < numStates; j++)
bwd[i][t] += (bwd[j][t + 1] * a[i][j] * b[j][o[t + 1]]); bwd[i][t] += (bwd[j][t + 1] * a[i][j] * b[j][o[t + 1]]);
} }
} }
return bwd; return bwd;
} }
/** /**
* Prints everything about this model, including * Prints everything about this model, including
* all values. For debug purposes or if you want * all values. For debug purposes or if you want
* to comprehend what happend to the model. * to comprehend what happend to the model.
* *
*/ */
public void print() { public void print() {
DecimalFormat fmt = new DecimalFormat(); DecimalFormat fmt = new DecimalFormat();
fmt.setMinimumFractionDigits(10); fmt.setMinimumFractionDigits(10);
fmt.setMaximumFractionDigits(10); fmt.setMaximumFractionDigits(10);
for (int i = 0; i < numStates; i++) for (int i = 0; i < numStates; i++)
Log.write("pi(" + i + ") = " + fmt.format(pi[i])); Log.write("pi(" + i + ") = " + fmt.format(pi[i]));
Log.write(""); Log.write("");
for (int i = 0; i < numStates; i++) { for (int i = 0; i < numStates; i++) {
for (int j = 0; j < numStates; j++) for (int j = 0; j < numStates; j++)
Log.write("a(" + i + "," + j + ") = " Log.write("a(" + i + "," + j + ") = "
+ fmt.format(a[i][j]) + " "); + fmt.format(a[i][j]) + " ");
Log.write(""); Log.write("");
} }
Log.write(""); Log.write("");
for (int i = 0; i < numStates; i++) { for (int i = 0; i < numStates; i++) {
for (int k = 0; k < sigmaSize; k++) for (int k = 0; k < sigmaSize; k++)
Log.write("b(" + i + "," + k + ") = " Log.write("b(" + i + "," + k + ") = "
+ fmt.format(b[i][k]) + " "); + fmt.format(b[i][k]) + " ");
Log.write(""); Log.write("");
} }
} }
public double[][] getA() { public double[][] getA() {
return this.a; return this.a;
} }
public void setA(double[][] a) { public void setA(double[][] a) {
this.a = a; this.a = a;
} }
public double[][] getB() { public double[][] getB() {
return this.b; return this.b;
} }
public void setB(double[][] b) { public void setB(double[][] b) {
this.b=b; this.b=b;
} }
} }

View File

@@ -29,7 +29,7 @@ public abstract class ProcessingUnit implements AccelerationListener, ButtonList
* Add an GestureListener to receive GestureEvents. * Add an GestureListener to receive GestureEvents.
* *
* @param g * @param g
* Class which implements GestureListener interface. * Class which implements GestureListener interface.
*/ */
public void addGestureListener(GestureListener g) { public void addGestureListener(GestureListener g) {
this.gesturelistener.add(g); this.gesturelistener.add(g);

View File

@@ -39,271 +39,271 @@ import org.wiigee.util.Log;
*/ */
public class Quantizer { public class Quantizer {
/** This is the initial radius of this model. */ /** This is the initial radius of this model. */
private double radius; private double radius;
/** Number of states from the following Hidden Markov Model */ /** Number of states from the following Hidden Markov Model */
private int numStates; private int numStates;
/** The representation of the so called Centeroids */ /** The representation of the so called Centeroids */
private double[][] map; private double[][] map;
/** True, if map is already trained. */ /** True, if map is already trained. */
private boolean maptrained; private boolean maptrained;
/** /**
* Initialize a empty quantizer. The states variable is necessary since some * Initialize a empty quantizer. The states variable is necessary since some
* algorithms need this value to calculate their values correctly. * algorithms need this value to calculate their values correctly.
* *
* @param numStates * @param numStates
* number of hidden markov model states * number of hidden markov model states
*/ */
public Quantizer(int numStates) { public Quantizer(int numStates) {
this.numStates = numStates; this.numStates = numStates;
this.map = new double[14][3]; this.map = new double[14][3];
this.maptrained = false; this.maptrained = false;
} }
/** /**
* Trains this Quantizer with a specific gesture. This means that the * Trains this Quantizer with a specific gesture. This means that the
* positions of the centeroids would adapt to this training gesture. In our * positions of the centeroids would adapt to this training gesture. In our
* case this would happen with a summarized virtual gesture, containing all * case this would happen with a summarized virtual gesture, containing all
* the other gestures. * the other gestures.
* *
* @param gesture * @param gesture
* the summarized virtual gesture * the summarized virtual gesture
*/ */
public void trainCenteroids(Gesture gesture) { public void trainCenteroids(Gesture gesture) {
Vector<AccelerationEvent> data = gesture.getData(); Vector<AccelerationEvent> data = gesture.getData();
double pi = Math.PI; double pi = Math.PI;
this.radius = (gesture.getMaxAcceleration() + gesture this.radius = (gesture.getMaxAcceleration() + gesture
.getMinAcceleration()) / 2; .getMinAcceleration()) / 2;
Log.write("Using radius: " + this.radius); Log.write("Using radius: " + this.radius);
// x , z , y // x , z , y
if (!this.maptrained) { if (!this.maptrained) {
this.maptrained = true; this.maptrained = true;
this.map[0] = new double[] { this.radius, 0.0, 0.0 }; this.map[0] = new double[] { this.radius, 0.0, 0.0 };
this.map[1] = new double[] { Math.cos(pi / 4) * this.radius, 0.0, this.map[1] = new double[] { Math.cos(pi / 4) * this.radius, 0.0,
Math.sin(pi / 4) * this.radius }; Math.sin(pi / 4) * this.radius };
this.map[2] = new double[] { 0.0, 0.0, this.radius }; this.map[2] = new double[] { 0.0, 0.0, this.radius };
this.map[3] = new double[] { Math.cos(pi * 3 / 4) * this.radius, this.map[3] = new double[] { Math.cos(pi * 3 / 4) * this.radius,
0.0, Math.sin(pi * 3 / 4) * this.radius }; 0.0, Math.sin(pi * 3 / 4) * this.radius };
this.map[4] = new double[] { -this.radius, 0.0, 0.0 }; this.map[4] = new double[] { -this.radius, 0.0, 0.0 };
this.map[5] = new double[] { Math.cos(pi * 5 / 4) * this.radius, this.map[5] = new double[] { Math.cos(pi * 5 / 4) * this.radius,
0.0, Math.sin(pi * 5 / 4) * this.radius }; 0.0, Math.sin(pi * 5 / 4) * this.radius };
this.map[6] = new double[] { 0.0, 0.0, -this.radius }; this.map[6] = new double[] { 0.0, 0.0, -this.radius };
this.map[7] = new double[] { Math.cos(pi * 7 / 4) * this.radius, this.map[7] = new double[] { Math.cos(pi * 7 / 4) * this.radius,
0.0, Math.sin(pi * 7 / 4) * this.radius }; 0.0, Math.sin(pi * 7 / 4) * this.radius };
this.map[8] = new double[] { 0.0, this.radius, 0.0 }; this.map[8] = new double[] { 0.0, this.radius, 0.0 };
this.map[9] = new double[] { 0.0, Math.cos(pi / 4) * this.radius, this.map[9] = new double[] { 0.0, Math.cos(pi / 4) * this.radius,
Math.sin(pi / 4) * this.radius }; Math.sin(pi / 4) * this.radius };
this.map[10] = new double[] { 0.0, this.map[10] = new double[] { 0.0,
Math.cos(pi * 3 / 4) * this.radius, Math.cos(pi * 3 / 4) * this.radius,
Math.sin(pi * 3 / 4) * this.radius }; Math.sin(pi * 3 / 4) * this.radius };
this.map[11] = new double[] { 0.0, -this.radius, 0.0 }; this.map[11] = new double[] { 0.0, -this.radius, 0.0 };
this.map[12] = new double[] { 0.0, this.map[12] = new double[] { 0.0,
Math.cos(pi * 5 / 4) * this.radius, Math.cos(pi * 5 / 4) * this.radius,
Math.sin(pi * 5 / 4) * this.radius }; Math.sin(pi * 5 / 4) * this.radius };
this.map[13] = new double[] { 0.0, this.map[13] = new double[] { 0.0,
Math.cos(pi * 7 / 4) * this.radius, Math.cos(pi * 7 / 4) * this.radius,
Math.sin(pi * 7 / 4) * this.radius }; Math.sin(pi * 7 / 4) * this.radius };
} }
int[][] g_alt = new int[this.map.length][data.size()]; int[][] g_alt = new int[this.map.length][data.size()];
int[][] g = new int[this.map.length][data.size()]; int[][] g = new int[this.map.length][data.size()];
do { do {
// Derive new Groups... // Derive new Groups...
g_alt = this.copyarray(g); g_alt = this.copyarray(g);
g = this.deriveGroups(gesture); g = this.deriveGroups(gesture);
// calculate new centeroids // calculate new centeroids
for (int i = 0; i < this.map.length; i++) { for (int i = 0; i < this.map.length; i++) {
double zaehlerX = 0; double zaehlerX = 0;
double zaehlerY = 0; double zaehlerY = 0;
double zaehlerZ = 0; double zaehlerZ = 0;
int nenner = 0; int nenner = 0;
for (int j = 0; j < data.size(); j++) { for (int j = 0; j < data.size(); j++) {
if (g[i][j] == 1) { if (g[i][j] == 1) {
zaehlerX += data.elementAt(j).getX(); zaehlerX += data.elementAt(j).getX();
zaehlerY += data.elementAt(j).getY(); zaehlerY += data.elementAt(j).getY();
zaehlerZ += data.elementAt(j).getZ(); zaehlerZ += data.elementAt(j).getZ();
nenner++; nenner++;
} }
} }
if (nenner > 1) { // nur wenn der nenner>0 oder >1??? ist muss if (nenner > 1) { // nur wenn der nenner>0 oder >1??? ist muss
// was // was
// geaendert werden // geaendert werden
// Log.write("Setze neuen Centeroid!"); // Log.write("Setze neuen Centeroid!");
this.map[i] = new double[] {(zaehlerX / (double) nenner), this.map[i] = new double[] {(zaehlerX / (double) nenner),
(zaehlerY / (double) nenner), (zaehlerY / (double) nenner),
(zaehlerZ / (double) nenner) }; (zaehlerZ / (double) nenner) };
// Log.write("Centeroid: "+i+": "+newcenteroid[0]+":"+newcenteroid[1]); // Log.write("Centeroid: "+i+": "+newcenteroid[0]+":"+newcenteroid[1]);
} }
} // new centeroids } // new centeroids
} while (!equalarrays(g_alt, g)); } while (!equalarrays(g_alt, g));
// Debug: Printout groups // Debug: Printout groups
/* /*
* for (int i = 0; i < n; i++) { for (int j = 0; j < this.data.size(); * for (int i = 0; i < n; i++) { for (int j = 0; j < this.data.size();
* j++) { Log.write(g[i][j] + "|"); } Log.write(""); } * j++) { Log.write(g[i][j] + "|"); } Log.write(""); }
*/ */
} }
/** /**
* This methods looks up a Gesture to a group matrix, used by the * This methods looks up a Gesture to a group matrix, used by the
* k-mean-algorithm (traincenteroid method) above. * k-mean-algorithm (traincenteroid method) above.
* *
* @param gesture * @param gesture
* the gesture * the gesture
*/ */
public int[][] deriveGroups(Gesture gesture) { public int[][] deriveGroups(Gesture gesture) {
Vector<AccelerationEvent> data = gesture.getData(); Vector<AccelerationEvent> data = gesture.getData();
int[][] groups = new int[this.map.length][data.size()]; int[][] groups = new int[this.map.length][data.size()];
// Calculate cartesian distance // Calculate cartesian distance
double[][] d = new double[this.map.length][data.size()]; double[][] d = new double[this.map.length][data.size()];
double[] curr = new double[3]; double[] curr = new double[3];
double[] vector = new double[3]; double[] vector = new double[3];
for (int i = 0; i < this.map.length; i++) { // zeilen for (int i = 0; i < this.map.length; i++) { // zeilen
double[] ref = this.map[i]; double[] ref = this.map[i];
for (int j = 0; j < data.size(); j++) { // spalten for (int j = 0; j < data.size(); j++) { // spalten
curr[0] = data.elementAt(j).getX(); curr[0] = data.elementAt(j).getX();
curr[1] = data.elementAt(j).getY(); curr[1] = data.elementAt(j).getY();
curr[2] = data.elementAt(j).getZ(); curr[2] = data.elementAt(j).getZ();
vector[0] = ref[0] - curr[0]; vector[0] = ref[0] - curr[0];
vector[1] = ref[1] - curr[1]; vector[1] = ref[1] - curr[1];
vector[2] = ref[2] - curr[2]; vector[2] = ref[2] - curr[2];
d[i][j] = Math.sqrt((vector[0] * vector[0]) d[i][j] = Math.sqrt((vector[0] * vector[0])
+ (vector[1] * vector[1]) + (vector[2] * vector[2])); + (vector[1] * vector[1]) + (vector[2] * vector[2]));
// Log.write(d[i][j] + "|"); // Log.write(d[i][j] + "|");
} }
// Log.write(""); // Log.write("");
} }
// look, to which group a value belongs // look, to which group a value belongs
for (int j = 0; j < data.size(); j++) { for (int j = 0; j < data.size(); j++) {
double smallest = Double.MAX_VALUE; double smallest = Double.MAX_VALUE;
int row = 0; int row = 0;
for (int i = 0; i < this.map.length; i++) { for (int i = 0; i < this.map.length; i++) {
if (d[i][j] < smallest) { if (d[i][j] < smallest) {
smallest = d[i][j]; smallest = d[i][j];
row = i; row = i;
} }
groups[i][j] = 0; groups[i][j] = 0;
} }
groups[row][j] = 1; // guppe gesetzt groups[row][j] = 1; // guppe gesetzt
} }
// Debug output // Debug output
/* /*
* for (int i = 0; i < groups.length; i++) { // zeilen for (int j = 0; j * for (int i = 0; i < groups.length; i++) { // zeilen for (int j = 0; j
* < groups[i].length; j++) { Log.write(groups[i][j] + "|"); } * < groups[i].length; j++) { Log.write(groups[i][j] + "|"); }
* Log.write(""); } * Log.write(""); }
*/ */
return groups; return groups;
} }
/** /**
* With this method you can transform a gesture to a discrete symbol * With this method you can transform a gesture to a discrete symbol
* sequence with values between 0 and granularity (number of observations). * sequence with values between 0 and granularity (number of observations).
* *
* @param gesture * @param gesture
* Gesture to get the observationsequence to. * Gesture to get the observationsequence to.
*/ */
public int[] getObservationSequence(Gesture gesture) { public int[] getObservationSequence(Gesture gesture) {
int[][] groups = this.deriveGroups(gesture); int[][] groups = this.deriveGroups(gesture);
Vector<Integer> sequence = new Vector<Integer>(); Vector<Integer> sequence = new Vector<Integer>();
// Log.write("Visible symbol sequence: "); // Log.write("Visible symbol sequence: ");
for (int j = 0; j < groups[0].length; j++) { // spalten for (int j = 0; j < groups[0].length; j++) { // spalten
for (int i = 0; i < groups.length; i++) { // zeilen for (int i = 0; i < groups.length; i++) { // zeilen
if (groups[i][j] == 1) { if (groups[i][j] == 1) {
// Log.write(" "+ i); // Log.write(" "+ i);
sequence.add(i); sequence.add(i);
break; break;
} }
} }
} }
// die sequenz darf nicht zu kurz sein... mindestens so lang // die sequenz darf nicht zu kurz sein... mindestens so lang
// wie die anzahl der zustände. weil sonst die formeln nicht klappen. // wie die anzahl der zustände. weil sonst die formeln nicht klappen.
// english: this is very dirty! it have to be here because if not // english: this is very dirty! it have to be here because if not
// too short sequences would cause an error. i've to think about a // too short sequences would cause an error. i've to think about a
// better resolution than copying the old value a few time. // better resolution than copying the old value a few time.
while (sequence.size() < this.numStates) { while (sequence.size() < this.numStates) {
sequence.add(sequence.elementAt(sequence.size() - 1)); sequence.add(sequence.elementAt(sequence.size() - 1));
// Log.write(" "+sequence.elementAt(sequence.size()-1)); // Log.write(" "+sequence.elementAt(sequence.size()-1));
} }
// Log.write(""); // Log.write("");
int[] out = new int[sequence.size()]; int[] out = new int[sequence.size()];
for (int i = 0; i < sequence.size(); i++) { for (int i = 0; i < sequence.size(); i++) {
out[i] = sequence.elementAt(i); out[i] = sequence.elementAt(i);
} }
return out; return out;
} }
/** /**
* Prints out the current centeroids-map. Its for debug or technical * Prints out the current centeroids-map. Its for debug or technical
* interests. * interests.
*/ */
public void printMap() { public void printMap() {
Log.write("Centeroids:"); Log.write("Centeroids:");
for (int i = 0; i < this.map.length; i++) { for (int i = 0; i < this.map.length; i++) {
Log.write(i + ". :" + this.map[i][0] + ":" Log.write(i + ". :" + this.map[i][0] + ":"
+ this.map[i][1] + ":" + this.map[i][2]); + this.map[i][1] + ":" + this.map[i][2]);
} }
} }
/** /**
* Function to deepcopy an array. * Function to deepcopy an array.
*/ */
private int[][] copyarray(int[][] alt) { private int[][] copyarray(int[][] alt) {
int[][] neu = new int[alt.length][alt[0].length]; int[][] neu = new int[alt.length][alt[0].length];
for (int i = 0; i < alt.length; i++) { for (int i = 0; i < alt.length; i++) {
for (int j = 0; j < alt[i].length; j++) { for (int j = 0; j < alt[i].length; j++) {
neu[i][j] = alt[i][j]; neu[i][j] = alt[i][j];
} }
} }
return neu; return neu;
} }
/** /**
* Function to look if the two arrays containing the same values. * Function to look if the two arrays containing the same values.
*/ */
private boolean equalarrays(int[][] one, int[][] two) { private boolean equalarrays(int[][] one, int[][] two) {
for (int i = 0; i < one.length; i++) { for (int i = 0; i < one.length; i++) {
for (int j = 0; j < one[i].length; j++) { for (int j = 0; j < one[i].length; j++) {
if (!(one[i][j] == two[i][j])) { if (!(one[i][j] == two[i][j])) {
return false; return false;
} }
} }
} }
return true; return true;
} }
public double getRadius() { public double getRadius() {
return this.radius; return this.radius;
} }
public double[][] getHashMap() { public double[][] getHashMap() {
return this.map; return this.map;
} }
public void setUpManually(double[][] map, double radius) { public void setUpManually(double[][] map, double radius) {
this.map = map; this.map = map;
this.radius = radius; this.radius = radius;
} }
} }

View File

@@ -37,152 +37,152 @@ import org.wiigee.util.Log;
*/ */
public class TriggeredProcessingUnit extends ProcessingUnit { public class TriggeredProcessingUnit extends ProcessingUnit {
// gesturespecific values // gesturespecific values
private Gesture current; // current gesture private Gesture current; // current gesture
private Vector<Gesture> trainsequence; private Vector<Gesture> trainsequence;
// State variables // State variables
private boolean learning, analyzing; private boolean learning, analyzing;
public TriggeredProcessingUnit() { public TriggeredProcessingUnit() {
super(); super();
this.learning=false; this.learning=false;
this.analyzing=false; this.analyzing=false;
this.current=new Gesture(); this.current=new Gesture();
this.trainsequence=new Vector<Gesture>(); this.trainsequence=new Vector<Gesture>();
} }
/** /**
* Since this class implements the WiimoteListener this procedure is * Since this class implements the WiimoteListener this procedure is
* necessary. It contains the filtering (directional equivalence filter) * necessary. It contains the filtering (directional equivalence filter)
* and adds the incoming data to the current motion, we want to train * and adds the incoming data to the current motion, we want to train
* or recognize. * or recognize.
* *
* @param event The acceleration event which has to be processed by the * @param event The acceleration event which has to be processed by the
* directional equivalence filter and which has to be added to the current * directional equivalence filter and which has to be added to the current
* motion in recognition or training process. * motion in recognition or training process.
*/ */
public void accelerationReceived(AccelerationEvent event) { public void accelerationReceived(AccelerationEvent event) {
if(this.learning || this.analyzing) { if(this.learning || this.analyzing) {
this.current.add(event); // add event to gesture this.current.add(event); // add event to gesture
} }
} }
/** /**
* This method is from the WiimoteListener interface. A button press * This method is from the WiimoteListener interface. A button press
* is used to control the data flow inside the structures. * is used to control the data flow inside the structures.
* *
*/ */
public void buttonPressReceived(ButtonPressedEvent event) { public void buttonPressReceived(ButtonPressedEvent event) {
this.handleStartEvent(event); this.handleStartEvent(event);
} }
public void buttonReleaseReceived(ButtonReleasedEvent event) { public void buttonReleaseReceived(ButtonReleasedEvent event) {
this.handleStopEvent(event); this.handleStopEvent(event);
} }
public void motionStartReceived(MotionStartEvent event) { public void motionStartReceived(MotionStartEvent event) {
// this.handleStartEvent(event); // this.handleStartEvent(event);
} }
public void motionStopReceived(MotionStopEvent event) { public void motionStopReceived(MotionStopEvent event) {
// this.handleStopEvent(event); // this.handleStopEvent(event);
} }
public void handleStartEvent(ActionStartEvent event) { public void handleStartEvent(ActionStartEvent event) {
// TrainButton = record a gesture for learning // TrainButton = record a gesture for learning
if((!this.analyzing && !this.learning) && if((!this.analyzing && !this.learning) &&
event.isTrainInitEvent()) { event.isTrainInitEvent()) {
Log.write("Training started!"); Log.write("Training started!");
this.learning=true; this.learning=true;
} }
// RecognitionButton = record a gesture for recognition // RecognitionButton = record a gesture for recognition
if((!this.analyzing && !this.learning) && if((!this.analyzing && !this.learning) &&
event.isRecognitionInitEvent()) { event.isRecognitionInitEvent()) {
Log.write("Recognition started!"); Log.write("Recognition started!");
this.analyzing=true; this.analyzing=true;
} }
// CloseGestureButton = starts the training of the model with multiple // CloseGestureButton = starts the training of the model with multiple
// recognized gestures, contained in trainsequence // recognized gestures, contained in trainsequence
if((!this.analyzing && !this.learning) && if((!this.analyzing && !this.learning) &&
event.isCloseGestureInitEvent()) { event.isCloseGestureInitEvent()) {
if(this.trainsequence.size()>0) { if(this.trainsequence.size()>0) {
Log.write("Training the model with "+this.trainsequence.size()+" gestures..."); Log.write("Training the model with "+this.trainsequence.size()+" gestures...");
this.learning=true; this.learning=true;
GestureModel m = new GestureModel(); GestureModel m = new GestureModel();
m.train(this.trainsequence); m.train(this.trainsequence);
m.print(); m.print();
this.classifier.addGestureModel(m); this.classifier.addGestureModel(m);
this.trainsequence=new Vector<Gesture>(); this.trainsequence=new Vector<Gesture>();
this.learning=false; this.learning=false;
} else { } else {
Log.write("There is nothing to do. Please record some gestures first."); Log.write("There is nothing to do. Please record some gestures first.");
} }
} }
} }
public void handleStopEvent(ActionStopEvent event) { public void handleStopEvent(ActionStopEvent event) {
if(this.learning) { // button release and state=learning, stops learning if(this.learning) { // button release and state=learning, stops learning
if(this.current.getCountOfData()>0) { if(this.current.getCountOfData()>0) {
Log.write("Finished recording (training)..."); Log.write("Finished recording (training)...");
Log.write("Data: "+this.current.getCountOfData()); Log.write("Data: "+this.current.getCountOfData());
Gesture gesture = new Gesture(this.current); Gesture gesture = new Gesture(this.current);
this.trainsequence.add(gesture); this.trainsequence.add(gesture);
this.current=new Gesture(); this.current=new Gesture();
this.learning=false; this.learning=false;
} else { } else {
Log.write("There is no data."); Log.write("There is no data.");
Log.write("Please train the gesture again."); Log.write("Please train the gesture again.");
this.learning=false; // ? this.learning=false; // ?
} }
} }
else if(this.analyzing) { // button release and state=analyzing, stops analyzing else if(this.analyzing) { // button release and state=analyzing, stops analyzing
if(this.current.getCountOfData()>0) { if(this.current.getCountOfData()>0) {
Log.write("Finished recording (recognition)..."); Log.write("Finished recording (recognition)...");
Log.write("Compare gesture with "+this.classifier.getCountOfGestures()+" other gestures."); Log.write("Compare gesture with "+this.classifier.getCountOfGestures()+" other gestures.");
Gesture gesture = new Gesture(this.current); Gesture gesture = new Gesture(this.current);
int recognized = this.classifier.classifyGesture(gesture); int recognized = this.classifier.classifyGesture(gesture);
if(recognized!=-1) { if(recognized!=-1) {
double recogprob = this.classifier.getLastProbability(); double recogprob = this.classifier.getLastProbability();
this.fireGestureEvent(true, recognized, recogprob); this.fireGestureEvent(true, recognized, recogprob);
Log.write("######"); Log.write("######");
Log.write("Gesture No. "+recognized+" recognized: "+recogprob); Log.write("Gesture No. "+recognized+" recognized: "+recogprob);
Log.write("######"); Log.write("######");
} else { } else {
this.fireGestureEvent(false, 0, 0.0); this.fireGestureEvent(false, 0, 0.0);
Log.write("######"); Log.write("######");
Log.write("No gesture recognized."); Log.write("No gesture recognized.");
Log.write("######"); Log.write("######");
} }
this.current=new Gesture(); this.current=new Gesture();
this.analyzing=false; this.analyzing=false;
} else { } else {
Log.write("There is no data."); Log.write("There is no data.");
Log.write("Please recognize the gesture again."); Log.write("Please recognize the gesture again.");
this.analyzing=false; // ? this.analyzing=false; // ?
} }
} }
} }
@Override @Override
public void loadGesture(String filename) { public void loadGesture(String filename) {
GestureModel g = org.wiigee.util.FileIO.readFromFile(filename); GestureModel g = org.wiigee.util.FileIO.readFromFile(filename);
this.classifier.addGestureModel(g); this.classifier.addGestureModel(g);
} }
@Override @Override
public void saveGesture(int id, String filename) { public void saveGesture(int id, String filename) {
org.wiigee.util.FileIO.writeToFile(this.classifier.getGestureModel(id), filename); org.wiigee.util.FileIO.writeToFile(this.classifier.getGestureModel(id), filename);
} }
} }

View File

@@ -44,190 +44,190 @@ import org.wiigee.logic.Quantizer;
*/ */
public class FileIO { public class FileIO {
public static void writeToFile(GestureModel m, String name) { public static void writeToFile(GestureModel m, String name) {
try { try {
// initialize file and get values // initialize file and get values
BufferedWriter out = new BufferedWriter(new FileWriter(name+".txt")); BufferedWriter out = new BufferedWriter(new FileWriter(name+".txt"));
int numStates = m.getNumStates(); int numStates = m.getNumStates();
int numObservations = m.getNumObservations(); int numObservations = m.getNumObservations();
double defaultProbability = m.getDefaultProbability(); double defaultProbability = m.getDefaultProbability();
Quantizer quantizer = m.getQuantizer(); Quantizer quantizer = m.getQuantizer();
HMM hmm = m.getHMM(); HMM hmm = m.getHMM();
out.write("# numStates:"); out.write("# numStates:");
out.newLine(); out.newLine();
out.write(Integer.toString(numStates)); out.write(Integer.toString(numStates));
out.newLine(); out.newLine();
out.write("# numObservations:"); out.write("# numObservations:");
out.newLine(); out.newLine();
out.write(Integer.toString(numObservations)); out.write(Integer.toString(numObservations));
out.newLine(); out.newLine();
out.write("# defaultProbability:"); out.write("# defaultProbability:");
out.newLine(); out.newLine();
out.write(Double.toString(defaultProbability)); out.write(Double.toString(defaultProbability));
out.newLine(); out.newLine();
out.write("# Quantizer: Radius"); out.write("# Quantizer: Radius");
out.newLine(); out.newLine();
out.write(Double.toString(quantizer.getRadius())); out.write(Double.toString(quantizer.getRadius()));
out.newLine(); out.newLine();
out.write("# Quantizer: MAP"); out.write("# Quantizer: MAP");
out.newLine(); out.newLine();
double[][] map = quantizer.getHashMap(); double[][] map = quantizer.getHashMap();
for(int v=0; v<map.length; v++) { for(int v=0; v<map.length; v++) {
double[] d = map[v]; double[] d = map[v];
out.write(Double.toString(d[0])+", "+Double.toString(d[1])+", "+Double.toString(d[2])); out.write(Double.toString(d[0])+", "+Double.toString(d[1])+", "+Double.toString(d[2]));
out.newLine(); out.newLine();
} }
out.write("# HMM: PI"); out.write("# HMM: PI");
out.newLine(); out.newLine();
double[] pi = hmm.getPi(); double[] pi = hmm.getPi();
for (int i=0; i<numStates; i++) { for (int i=0; i<numStates; i++) {
if(i==numStates-1) { if(i==numStates-1) {
out.write(Double.toString(pi[i])); out.write(Double.toString(pi[i]));
out.newLine(); out.newLine();
} else { } else {
out.write(Double.toString(pi[i])+", "); out.write(Double.toString(pi[i])+", ");
} }
} }
out.write("# HMM: A"); out.write("# HMM: A");
out.newLine(); out.newLine();
double[][] a = hmm.getA(); double[][] a = hmm.getA();
for(int i=0; i<numStates; i++) { for(int i=0; i<numStates; i++) {
for(int j=0; j<numStates; j++) { for(int j=0; j<numStates; j++) {
if(j==numStates-1) { if(j==numStates-1) {
out.write(Double.toString(a[i][j])); out.write(Double.toString(a[i][j]));
out.newLine(); out.newLine();
} else { } else {
out.write(Double.toString(a[i][j])+", "); out.write(Double.toString(a[i][j])+", ");
} }
} }
} }
out.write("# HMM: B"); out.write("# HMM: B");
out.newLine(); out.newLine();
double[][] b = hmm.getB(); double[][] b = hmm.getB();
for(int i=0; i<numStates; i++) { for(int i=0; i<numStates; i++) {
for(int j=0; j<numObservations; j++) { for(int j=0; j<numObservations; j++) {
if(j==numObservations-1) { if(j==numObservations-1) {
out.write(Double.toString(b[i][j])); out.write(Double.toString(b[i][j]));
out.newLine(); out.newLine();
} else { } else {
out.write(Double.toString(b[i][j])+", "); out.write(Double.toString(b[i][j])+", ");
} }
} }
} }
out.write("# END"); out.write("# END");
// close file // close file
out.flush(); out.flush();
out.close(); out.close();
} catch (IOException e) { } catch (IOException e) {
System.out.println("Error: Write to File!"); System.out.println("Error: Write to File!");
e.printStackTrace(); e.printStackTrace();
} }
} }
public static GestureModel readFromFile(String name) { public static GestureModel readFromFile(String name) {
try { try {
// initialize file and create values // initialize file and create values
BufferedReader in = new BufferedReader(new FileReader(name+".txt")); BufferedReader in = new BufferedReader(new FileReader(name+".txt"));
int numStates = 0; int numStates = 0;
int numObservations = 0; int numObservations = 0;
double defaultprobability = 0; double defaultprobability = 0;
double radius = 0; double radius = 0;
double[][] map = new double[numObservations][3]; double[][] map = new double[numObservations][3];
double[] pi = new double[numStates]; double[] pi = new double[numStates];
double[][] a = new double[numStates][numStates]; double[][] a = new double[numStates][numStates];
double[][] b = new double[numStates][numObservations]; double[][] b = new double[numStates][numObservations];
String line; String line;
int position = 0; int position = 0;
while(in.ready()) { while(in.ready()) {
line = in.readLine(); line = in.readLine();
if(!line.startsWith("#")) { // isn't a comment if(!line.startsWith("#")) { // isn't a comment
switch (position++) { switch (position++) {
case 0: case 0:
numStates = Integer.parseInt(line); numStates = Integer.parseInt(line);
break; break;
case 1: case 1:
numObservations = Integer.parseInt(line); numObservations = Integer.parseInt(line);
break; break;
case 2: case 2:
defaultprobability = Double.parseDouble(line); defaultprobability = Double.parseDouble(line);
break; break;
case 3: case 3:
radius = Double.parseDouble(line); radius = Double.parseDouble(line);
break; break;
case 4: case 4:
map = new double[numObservations][3]; map = new double[numObservations][3];
for(int i=0; i<numObservations; i++) { for(int i=0; i<numObservations; i++) {
String s[] = line.split(", "); String s[] = line.split(", ");
double[] d = new double[] { Double.parseDouble(s[0]), double[] d = new double[] { Double.parseDouble(s[0]),
Double.parseDouble(s[1]), Double.parseDouble(s[1]),
Double.parseDouble(s[2]) }; Double.parseDouble(s[2]) };
map[i] = d; map[i] = d;
line = in.ready() ? in.readLine() : ""; line = in.ready() ? in.readLine() : "";
} }
break; break;
case 5: case 5:
pi = new double[numStates]; pi = new double[numStates];
String pi_row[] = line.split(", "); String pi_row[] = line.split(", ");
for(int i=0; i<numStates; i++) { for(int i=0; i<numStates; i++) {
pi[i] = Double.parseDouble(pi_row[i]); pi[i] = Double.parseDouble(pi_row[i]);
} }
break; break;
case 6: case 6:
a = new double[numStates][numStates]; a = new double[numStates][numStates];
for(int i=0; i<numStates; i++) { for(int i=0; i<numStates; i++) {
String a_row[] = line.split(", "); String a_row[] = line.split(", ");
for(int j=0; j<numStates; j++) { for(int j=0; j<numStates; j++) {
a[i][j] = Double.parseDouble(a_row[j]); a[i][j] = Double.parseDouble(a_row[j]);
} }
line = in.ready() ? in.readLine() : ""; line = in.ready() ? in.readLine() : "";
} }
break; break;
case 7: case 7:
b = new double[numStates][numObservations]; b = new double[numStates][numObservations];
for(int i=0; i<numStates; i++) { for(int i=0; i<numStates; i++) {
String b_row[] = line.split(", "); String b_row[] = line.split(", ");
for(int j=0; j<numObservations; j++) { for(int j=0; j<numObservations; j++) {
b[i][j] = Double.parseDouble(b_row[j]); b[i][j] = Double.parseDouble(b_row[j]);
} }
line = in.ready() ? in.readLine() : ""; line = in.ready() ? in.readLine() : "";
} }
break; break;
default: default:
System.out.println("SWITCH EMPTY!"); System.out.println("SWITCH EMPTY!");
break; break;
} }
} }
} }
GestureModel ret = new GestureModel(); GestureModel ret = new GestureModel();
ret.setDefaultProbability(defaultprobability); ret.setDefaultProbability(defaultprobability);
Quantizer quantizer = new Quantizer(numStates); Quantizer quantizer = new Quantizer(numStates);
quantizer.setUpManually(map, radius); quantizer.setUpManually(map, radius);
ret.setQuantizer(quantizer); ret.setQuantizer(quantizer);
HMM hmm = new HMM(numStates, numObservations); HMM hmm = new HMM(numStates, numObservations);
hmm.setPi(pi); hmm.setPi(pi);
hmm.setA(a); hmm.setA(a);
hmm.setB(b); hmm.setB(b);
ret.setHMM(hmm); ret.setHMM(hmm);
return ret; return ret;
} catch (Exception e) { } catch (Exception e) {
System.out.println("Error: Read from File!"); System.out.println("Error: Read from File!");
e.printStackTrace(); e.printStackTrace();
} }
return null; // bad error case return null; // bad error case
} }
} }

View File

@@ -2,42 +2,42 @@ package org.wiigee.util;
public class Log { public class Log {
public static final int OFF = -1; public static final int OFF = -1;
public static final int NORMAL = 0; public static final int NORMAL = 0;
public static final int DEBUG = 1; public static final int DEBUG = 1;
public static final int PRINT = 0; public static final int PRINT = 0;
public static final int FILE = 1; public static final int FILE = 1;
public static int level = Log.NORMAL; public static int level = Log.NORMAL;
public static int mode = Log.PRINT; public static int mode = Log.PRINT;
public static void setLevel(int n) { public static void setLevel(int n) {
level = n; level = n;
} }
public static void write(String s) { public static void write(String s) {
write(Log.NORMAL, s, null); write(Log.NORMAL, s, null);
} }
public static void write(String s, Object o) { public static void write(String s, Object o) {
write(Log.NORMAL, s, o); write(Log.NORMAL, s, o);
} }
public static void write(int n, String s, Object o) { public static void write(int n, String s, Object o) {
if(level>=n) { if(level>=n) {
if(mode==Log.PRINT) { if(mode==Log.PRINT) {
// console output enabled // console output enabled
if(o!=null) { if(o!=null) {
System.out.println(o.getClass()+": "+s); System.out.println(o.getClass()+": "+s);
} else { } else {
System.out.println(s); System.out.println(s);
} }
} else if(mode==Log.FILE) { } else if(mode==Log.FILE) {
// file output enabled // file output enabled
// TODO // TODO
} }
} }
} }
} }