git-svn-id: svn://svn.code.sf.net/p/wiigee/code/trunk@57 c7eff9ee-dd40-0410-8832-91a4d88773cf
This commit is contained in:
108
src/control/WiimoteDeviceDiscovery.java
Executable file
108
src/control/WiimoteDeviceDiscovery.java
Executable file
@@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package control;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import javax.bluetooth.DeviceClass;
|
||||||
|
import javax.bluetooth.DiscoveryListener;
|
||||||
|
import javax.bluetooth.RemoteDevice;
|
||||||
|
import javax.bluetooth.ServiceRecord;
|
||||||
|
|
||||||
|
import util.Log;
|
||||||
|
|
||||||
|
import device.Wiimote;
|
||||||
|
|
||||||
|
public class WiimoteDeviceDiscovery implements DiscoveryListener {
|
||||||
|
|
||||||
|
private Vector<RemoteDevice> devices;
|
||||||
|
private boolean isInquiring;
|
||||||
|
private Object lock;
|
||||||
|
|
||||||
|
public WiimoteDeviceDiscovery(Object lock) {
|
||||||
|
super();
|
||||||
|
this.lock=lock;
|
||||||
|
this.devices = new Vector<RemoteDevice>();
|
||||||
|
//this.isInquiring=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deviceDiscovered(RemoteDevice newdevice, DeviceClass devclass) {
|
||||||
|
Log.write("Device discovered: "
|
||||||
|
+ newdevice.getBluetoothAddress() + " - ");
|
||||||
|
// add the device to the vector
|
||||||
|
if (!devices.contains(newdevice)
|
||||||
|
&& devclass.getMajorDeviceClass() == 1280
|
||||||
|
&& devclass.getMinorDeviceClass() == 4) {
|
||||||
|
Log.write("Is a Wiimote!");
|
||||||
|
devices.addElement(newdevice);
|
||||||
|
} else {
|
||||||
|
Log.write("Is NOT a Wiimote!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void inquiryCompleted(int discType) {
|
||||||
|
switch (discType) {
|
||||||
|
case WiimoteDeviceDiscovery.INQUIRY_COMPLETED:
|
||||||
|
Log.write("Inquiry completed.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WiimoteDeviceDiscovery.INQUIRY_ERROR:
|
||||||
|
Log.write("Inquiry error.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WiimoteDeviceDiscovery.INQUIRY_TERMINATED:
|
||||||
|
Log.write("Inquiry terminated.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
synchronized(this.lock) {
|
||||||
|
this.lock.notify();
|
||||||
|
}
|
||||||
|
//this.isInquiring=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void serviceSearchCompleted(int arg0, int arg1) {
|
||||||
|
// not necessary
|
||||||
|
}
|
||||||
|
|
||||||
|
public void servicesDiscovered(int arg0, ServiceRecord[] arg1) {
|
||||||
|
// not necessary
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInquirying() {
|
||||||
|
return this.isInquiring;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector<Wiimote> getDiscoveredWiimotes() throws IOException {
|
||||||
|
Vector<Wiimote> wiimotes = new Vector<Wiimote>();
|
||||||
|
|
||||||
|
for (int i = 0; i < devices.size(); i++) {
|
||||||
|
wiimotes.add(new Wiimote(devices.elementAt(i).getBluetoothAddress()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return wiimotes;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
188
src/control/WiimoteWiigee.java
Executable file
188
src/control/WiimoteWiigee.java
Executable file
@@ -0,0 +1,188 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package control;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import javax.bluetooth.DiscoveryAgent;
|
||||||
|
import javax.bluetooth.LocalDevice;
|
||||||
|
|
||||||
|
import util.Log;
|
||||||
|
import device.Wiimote;
|
||||||
|
import event.DeviceListener;
|
||||||
|
import event.GestureListener;
|
||||||
|
import filter.Filter;
|
||||||
|
|
||||||
|
// Singleton
|
||||||
|
public class WiimoteWiigee {
|
||||||
|
|
||||||
|
protected static String version = "1.3.1 alpha";
|
||||||
|
protected static String releasedate = "20081215";
|
||||||
|
|
||||||
|
protected static WiimoteWiigee instance;
|
||||||
|
private static Object lock = new Object();
|
||||||
|
private Vector<Wiimote> devices;
|
||||||
|
|
||||||
|
private WiimoteWiigee() throws IOException {
|
||||||
|
Log.write("This is wiigee version "+version+" ("+releasedate+")");
|
||||||
|
this.devices=this.discoverWiimotes();
|
||||||
|
for(int i=0; i<this.devices.size(); i++) {
|
||||||
|
this.devices.elementAt(i).setLED(i+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private WiimoteWiigee(String btaddr) throws IOException {
|
||||||
|
Log.write("This is wiigee version "+version+" ("+releasedate+")");
|
||||||
|
this.devices = new Vector<Wiimote>();
|
||||||
|
this.devices.add(new Wiimote(btaddr));
|
||||||
|
for(int i=0; i<this.devices.size(); i++) {
|
||||||
|
this.devices.elementAt(i).setLED(i+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WiimoteWiigee getInstance() throws IOException {
|
||||||
|
if(instance==null) {
|
||||||
|
instance=new WiimoteWiigee();
|
||||||
|
return instance;
|
||||||
|
} else {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WiimoteWiigee getInstance(String btaddr) throws IOException {
|
||||||
|
if(instance==null) {
|
||||||
|
instance=new WiimoteWiigee(btaddr);
|
||||||
|
return instance;
|
||||||
|
} else {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of discovered wiimotes.
|
||||||
|
*
|
||||||
|
* @return Array of discovered wiimotes or null if
|
||||||
|
* none discoverd.
|
||||||
|
*/
|
||||||
|
public Wiimote[] getDevices() {
|
||||||
|
Wiimote[] out = new Wiimote[this.devices.size()];
|
||||||
|
for(int i=0; i<this.devices.size(); i++) {
|
||||||
|
out[i] = this.devices.elementAt(i);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover the wiimotes around the bluetooth host and
|
||||||
|
* make them available public via getWiimotes method.
|
||||||
|
*
|
||||||
|
* @return Array of discovered wiimotes.
|
||||||
|
*/
|
||||||
|
private Vector<Wiimote> discoverWiimotes() throws IOException {
|
||||||
|
WiimoteDeviceDiscovery deviceDiscovery = new WiimoteDeviceDiscovery(lock);
|
||||||
|
LocalDevice localDevice = LocalDevice.getLocalDevice();
|
||||||
|
Log.write("Your Computers Bluetooth MAC: "+localDevice.getBluetoothAddress());
|
||||||
|
|
||||||
|
DiscoveryAgent discoveryAgent = localDevice.getDiscoveryAgent();
|
||||||
|
|
||||||
|
Log.write("Starting device inquiry...");
|
||||||
|
discoveryAgent.startInquiry(DiscoveryAgent.GIAC, deviceDiscovery);
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
synchronized(lock){
|
||||||
|
lock.wait();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Log.write("Problems during device discovery.");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.write("Device discovery completed!");
|
||||||
|
return deviceDiscovery.getDiscoveredWiimotes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of wiimotes discovered.
|
||||||
|
*
|
||||||
|
* @return Number of wiimotes discovered.
|
||||||
|
*/
|
||||||
|
public int getNumberOfDevices() {
|
||||||
|
return this.devices.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the Trainbutton for all wiimotes;
|
||||||
|
*
|
||||||
|
* @param b Button encoding, see static Wiimote values
|
||||||
|
*/
|
||||||
|
public void setTrainButton(int b) {
|
||||||
|
for(int i=0; i<this.devices.size(); i++) {
|
||||||
|
this.devices.elementAt(i).setTrainButton(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the Recognitionbutton for all wiimotes;
|
||||||
|
*
|
||||||
|
* @param b Button encoding, see static Wiimote values
|
||||||
|
*/
|
||||||
|
public void setRecognitionButton(int b) {
|
||||||
|
for(int i=0; i<this.devices.size(); i++) {
|
||||||
|
this.devices.elementAt(i).setRecognitionButton(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the CloseGesturebutton for all wiimotes;
|
||||||
|
*
|
||||||
|
* @param b Button encoding, see static Wiimote values
|
||||||
|
*/
|
||||||
|
public void setCloseGestureButton(int b) {
|
||||||
|
for(int i=0; i<this.devices.size(); i++) {
|
||||||
|
this.devices.elementAt(i).setCloseGestureButton(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDeviceListener(DeviceListener listener) {
|
||||||
|
if(this.devices.size()>0) {
|
||||||
|
this.devices.elementAt(0).addDeviceListener(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addGestureListener(GestureListener listener) {
|
||||||
|
if(this.devices.size()>0) {
|
||||||
|
this.devices.elementAt(0).addGestureListener(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addFilter(Filter filter) {
|
||||||
|
if(this.devices.size()>0) {
|
||||||
|
this.devices.elementAt(0).addFilter(filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
220
src/device/Device.java
Normal file
220
src/device/Device.java
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package device;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import logic.ProcessingUnit;
|
||||||
|
import logic.*;
|
||||||
|
import event.*;
|
||||||
|
import filter.DirectionalEquivalenceFilter;
|
||||||
|
import filter.Filter;
|
||||||
|
import filter.IdleStateFilter;
|
||||||
|
import filter.MotionDetectFilter;
|
||||||
|
|
||||||
|
public class Device {
|
||||||
|
|
||||||
|
// Fixed number values.
|
||||||
|
public static final int MOTION = 0;
|
||||||
|
|
||||||
|
// Buttons for action coordination
|
||||||
|
protected int recognitionbutton;
|
||||||
|
protected int trainbutton;
|
||||||
|
protected int closegesturebutton;
|
||||||
|
|
||||||
|
// Functional
|
||||||
|
protected boolean accelerationenabled;
|
||||||
|
|
||||||
|
// Filters, can filter the data stream
|
||||||
|
protected Vector<Filter> filters = new Vector<Filter>();
|
||||||
|
|
||||||
|
// Listeners, receive generated events
|
||||||
|
protected Vector<DeviceListener> devicelistener = new Vector<DeviceListener>();
|
||||||
|
protected ProcessingUnit analyzer = new TriggeredProcessingUnit();
|
||||||
|
|
||||||
|
public Device() {
|
||||||
|
this.addFilter(new IdleStateFilter());
|
||||||
|
this.addFilter(new MotionDetectFilter(this));
|
||||||
|
this.addFilter(new DirectionalEquivalenceFilter());
|
||||||
|
this.addDeviceListener(this.analyzer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a Filter for processing the acceleration values.
|
||||||
|
* @param filter The Filter instance.
|
||||||
|
*/
|
||||||
|
public void addFilter(Filter filter) {
|
||||||
|
this.filters.add(filter);
|
||||||
|
System.out.println("Filter added...");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets all the filters, which are resetable.
|
||||||
|
* Sometimes they have to be resettet if a new gesture starts.
|
||||||
|
*/
|
||||||
|
public void resetFilters() {
|
||||||
|
for(int i=0; i<this.filters.size(); i++) {
|
||||||
|
this.filters.elementAt(i).reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an WiimoteListener to the wiimote. Everytime an action
|
||||||
|
* on the wiimote is performed the WiimoteListener would receive
|
||||||
|
* an event of this action.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void addDeviceListener(DeviceListener listener) {
|
||||||
|
this.devicelistener.add(listener);
|
||||||
|
System.out.println("WiimoteListener added...");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a GestureListener to the wiimote. Everytime a gesture
|
||||||
|
* is performed the GestureListener would receive an event of
|
||||||
|
* this gesture.
|
||||||
|
*/
|
||||||
|
public void addGestureListener(GestureListener listener) {
|
||||||
|
this.analyzer.addGestureListener(listener);
|
||||||
|
System.out.println("GestureListener added...");
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRecognitionButton() {
|
||||||
|
return this.recognitionbutton;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRecognitionButton(int b) {
|
||||||
|
this.recognitionbutton=b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTrainButton() {
|
||||||
|
return this.trainbutton;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTrainButton(int b) {
|
||||||
|
this.trainbutton=b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCloseGestureButton() {
|
||||||
|
return this.closegesturebutton;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCloseGestureButton(int b) {
|
||||||
|
this.closegesturebutton=b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProcessingUnit getAccelerationStreamAnalyzer() {
|
||||||
|
return this.analyzer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean accelerationEnabled() {
|
||||||
|
return this.accelerationenabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enableAccelerationSensors() throws IOException {
|
||||||
|
this.accelerationenabled=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disableAccelerationSensors() throws IOException {
|
||||||
|
this.accelerationenabled=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ###### Event-Methoden
|
||||||
|
|
||||||
|
/** Fires an acceleration event.
|
||||||
|
* @param x
|
||||||
|
* Acceleration in x direction
|
||||||
|
* @param y
|
||||||
|
* Acceleration in y direction
|
||||||
|
* @param z
|
||||||
|
* Acceleration in z direction
|
||||||
|
*/
|
||||||
|
public void fireAccelerationEvent(double[] vector) {
|
||||||
|
for(int i=0; i<this.filters.size(); i++) {
|
||||||
|
vector = this.filters.get(i).filter(vector);
|
||||||
|
// cannot return here if null, because of time-dependent filters
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't need to create an event if filtered away
|
||||||
|
if(vector!=null) {
|
||||||
|
// calculate the absolute value for the accelerationevent
|
||||||
|
double absvalue = Math.sqrt((vector[0]*vector[0])+
|
||||||
|
(vector[1]*vector[1])+(vector[2]*vector[2]));
|
||||||
|
|
||||||
|
AccelerationEvent w = new AccelerationEvent(this,
|
||||||
|
vector[0], vector[1], vector[2], absvalue);
|
||||||
|
for(int i=0; i<this.devicelistener.size(); i++) {
|
||||||
|
this.devicelistener.get(i).accelerationReceived(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // fireaccelerationevent
|
||||||
|
|
||||||
|
/** Fires a button pressed event.
|
||||||
|
* @param button
|
||||||
|
* Integer value of the pressed button.
|
||||||
|
*/
|
||||||
|
public void fireButtonPressedEvent(int button) {
|
||||||
|
ButtonPressedEvent w = new ButtonPressedEvent(this, button);
|
||||||
|
for(int i=0; i<this.devicelistener.size(); i++) {
|
||||||
|
this.devicelistener.get(i).buttonPressReceived(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(w.isRecognitionInitEvent() || w.isTrainInitEvent()) {
|
||||||
|
this.resetFilters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Fires a button released event.
|
||||||
|
*/
|
||||||
|
public void fireButtonReleasedEvent() {
|
||||||
|
ButtonReleasedEvent w = new ButtonReleasedEvent(this);
|
||||||
|
for(int i=0; i<this.devicelistener.size(); i++) {
|
||||||
|
this.devicelistener.get(i).buttonReleaseReceived(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires a motion start event.
|
||||||
|
*/
|
||||||
|
public void fireMotionStartEvent() {
|
||||||
|
MotionStartEvent w = new MotionStartEvent(this);
|
||||||
|
for(int i=0; i<this.devicelistener.size(); i++) {
|
||||||
|
this.devicelistener.get(i).motionStartReceived(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires a motion stop event.
|
||||||
|
*/
|
||||||
|
public void fireMotionStopEvent() {
|
||||||
|
MotionStopEvent w = new MotionStopEvent(this);
|
||||||
|
for(int i=0; i<this.devicelistener.size(); i++) {
|
||||||
|
this.devicelistener.get(i).motionStopReceived(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
108
src/device/JSR256Phone.java
Executable file
108
src/device/JSR256Phone.java
Executable file
@@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package device;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.microedition.io.Connector;
|
||||||
|
import javax.microedition.sensor.Data;
|
||||||
|
import javax.microedition.sensor.DataListener;
|
||||||
|
import javax.microedition.sensor.SensorConnection;
|
||||||
|
import javax.microedition.sensor.SensorInfo;
|
||||||
|
import javax.microedition.sensor.SensorManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
* This class represents the basic functions of a JSR256 enabled mobile phone.
|
||||||
|
* You need the JSR256 specifications to compile this code correctly. Your device
|
||||||
|
* should be J2ME v1.5 enabled, most mobile phones are at J2ME 1.4.2 at the moment.
|
||||||
|
* So, with this restrictions, this is more or less only a template you can continue
|
||||||
|
* developing with.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class JSR256Phone extends Device implements DataListener {
|
||||||
|
|
||||||
|
private SensorConnection sensor;
|
||||||
|
|
||||||
|
public JSR256Phone() throws IOException {
|
||||||
|
super();
|
||||||
|
// TODO: define buttons, depending on device
|
||||||
|
this.sensor = (SensorConnection) Connector.open(this.getSensorURL());
|
||||||
|
this.sensor.setDataListener(this, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
* Called from DataListener, JSR 256, if an acceleration happend.
|
||||||
|
*/
|
||||||
|
public void dataReceived(SensorConnection sensor, Data[] data, boolean arg2) {
|
||||||
|
|
||||||
|
int x=0, y=0, z=0;
|
||||||
|
double[] acc = new double[3];
|
||||||
|
|
||||||
|
for(int i=0; i<data.length; i++) {
|
||||||
|
if(data[i].getChannelInfo().getName().compareTo("axis_x") == 0) {
|
||||||
|
x = data[i].getIntValues()[0];
|
||||||
|
} else if(data[i].getChannelInfo().getName().compareTo("axis_y") == 0) {
|
||||||
|
y = data[i].getIntValues()[0];
|
||||||
|
} else if(data[i].getChannelInfo().getName().compareTo("axis_y") == 0) {
|
||||||
|
z = data[i].getIntValues()[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calibration has to be done here
|
||||||
|
// at the moment: fixed values for Sony Ericsson K850i
|
||||||
|
// -- may work for other devices, too
|
||||||
|
int x0 = -63;
|
||||||
|
int x3 = -1044;
|
||||||
|
int y0 = 45;
|
||||||
|
int y2 = -936;
|
||||||
|
int z0 = 45;
|
||||||
|
int z1 = -936;
|
||||||
|
|
||||||
|
acc[0] = (double) (x - x0) / (double) (x3 - x0);
|
||||||
|
acc[1] = (double) (y - y0) / (double) (y2 - y0);
|
||||||
|
acc[2] = (double) (z - z0) / (double) (z1 - z0);
|
||||||
|
|
||||||
|
this.fireAccelerationEvent(acc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// Button press events should be delegated from current GUI
|
||||||
|
// to the fireButtonPress() methods. I don't know at the moment,
|
||||||
|
// if there exist different value encodings for different mobile
|
||||||
|
// phones.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
* Helper to determine the accelerometer URL
|
||||||
|
*/
|
||||||
|
private String getSensorURL() {
|
||||||
|
SensorInfo[] si = SensorManager.findSensors("acceleration",
|
||||||
|
SensorInfo.CONTEXT_TYPE_USER);
|
||||||
|
return si[0].getUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
398
src/device/Wiimote.java
Executable file
398
src/device/Wiimote.java
Executable file
@@ -0,0 +1,398 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package device;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Random;
|
||||||
|
import javax.bluetooth.L2CAPConnection;
|
||||||
|
import javax.microedition.io.Connector;
|
||||||
|
import event.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
* This class represents the basic functions of the wiimote.
|
||||||
|
* If you want your wiimote to e.g. vibrate you'll do this here.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Wiimote extends Device {
|
||||||
|
|
||||||
|
// Fixed number values.
|
||||||
|
public static final int MOTION = 0;
|
||||||
|
public static final int BUTTON_2 = 1;
|
||||||
|
public static final int BUTTON_1 = 2;
|
||||||
|
public static final int BUTTON_B = 3;
|
||||||
|
public static final int BUTTON_A = 4;
|
||||||
|
public static final int BUTTON_MINUS = 5;
|
||||||
|
public static final int BUTTON_HOME = 8;
|
||||||
|
public static final int BUTTON_LEFT = 9;
|
||||||
|
public static final int BUTTON_RIGHT = 10;
|
||||||
|
public static final int BUTTON_DOWN = 11;
|
||||||
|
public static final int BUTTON_UP = 12;
|
||||||
|
public static final int BUTTON_PLUS = 13;
|
||||||
|
|
||||||
|
// Reports
|
||||||
|
public static final byte CMD_SET_REPORT = 0x52;
|
||||||
|
|
||||||
|
// IR Modes
|
||||||
|
public static final byte IR_MODE_STANDARD = 1;
|
||||||
|
public static final byte IR_MODE_EXTENDED = 0x03;
|
||||||
|
|
||||||
|
// Modes / Channels
|
||||||
|
public static final byte MODE_BUTTONS = 0x30;
|
||||||
|
public static final byte MODE_BUTTONS_ACCELERATION = 0x31;
|
||||||
|
public static final byte MODE_BUTTONS_ACCELERATION_INFRARED = 0x33;
|
||||||
|
|
||||||
|
|
||||||
|
// Bluetooth-adress as string representation
|
||||||
|
private String btaddress;
|
||||||
|
|
||||||
|
// LED encoded as byte
|
||||||
|
byte ledencoding;
|
||||||
|
|
||||||
|
|
||||||
|
// control connection, send commands to wiimote
|
||||||
|
private L2CAPConnection controlCon;
|
||||||
|
|
||||||
|
// receive connection, receive answers from wiimote
|
||||||
|
private L2CAPConnection receiveCon;
|
||||||
|
|
||||||
|
|
||||||
|
// Functional
|
||||||
|
private boolean vibrating;
|
||||||
|
private boolean calibrated;
|
||||||
|
private boolean infraredenabled;
|
||||||
|
private WiimoteStreamer wms;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new wiimote-device with a specific bluetooth mac-adress.
|
||||||
|
*
|
||||||
|
* @param btaddress
|
||||||
|
* String representation of the mac-adress e.g. 00191D68B57C
|
||||||
|
*/
|
||||||
|
public Wiimote(String btaddress) throws IOException {
|
||||||
|
btaddress = this.removeChar(btaddress, ':');
|
||||||
|
this.btaddress=btaddress;
|
||||||
|
this.vibrating=false;
|
||||||
|
this.connect();
|
||||||
|
this.calibrateAccelerometer();
|
||||||
|
this.streamData(true);
|
||||||
|
this.enableAccelerationSensors();
|
||||||
|
//this.enableInfraredCamera();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the two needed connections to send and receive commands
|
||||||
|
* to and from the wiimote-device.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void connect() throws IOException {
|
||||||
|
this.controlCon = (L2CAPConnection)Connector.open("btl2cap://"+
|
||||||
|
this.btaddress+":11;authenticate=false;encrypt=false;master=false",
|
||||||
|
Connector.WRITE); // 11
|
||||||
|
this.receiveCon = (L2CAPConnection)Connector.open("btl2cap://"+
|
||||||
|
this.btaddress+":13;authenticate=false;encrypt=false;master=false",
|
||||||
|
Connector.READ); // 13
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects the wiimote and closes the two connections.
|
||||||
|
*/
|
||||||
|
public void disconnect() {
|
||||||
|
this.vibrating=false;
|
||||||
|
try {
|
||||||
|
this.controlCon.close();
|
||||||
|
this.receiveCon.close();
|
||||||
|
System.out.println("Disconnected wiimote.");
|
||||||
|
} catch(Exception e) {
|
||||||
|
System.out.println("Failure during disconnect of wiimote.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
* Receiving data connection
|
||||||
|
*/
|
||||||
|
public L2CAPConnection getReceiveConnection() {
|
||||||
|
return this.receiveCon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method makes the Wiimote-Class reacting to incoming data.
|
||||||
|
* For just controlling and sending commands to the wiimote
|
||||||
|
* (vibration, LEDs, ...) it's not necessary to call this method.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* true, if the class should react to incoming data.
|
||||||
|
* false, if you only want to send commands to wiimote and
|
||||||
|
* only the control-connection is used.
|
||||||
|
*/
|
||||||
|
public void streamData(boolean value) {
|
||||||
|
if(value==true) {
|
||||||
|
if(this.wms==null) {
|
||||||
|
this.wms = new WiimoteStreamer(this);
|
||||||
|
}
|
||||||
|
wms.start(); }
|
||||||
|
else if(this.wms!=null) {
|
||||||
|
wms.stopThread();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data to a register inside of the wiimote.
|
||||||
|
*
|
||||||
|
* @param offset The memory offset, 3 bytes.
|
||||||
|
* @param data The data to be written, max. 16 bytes.
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void writeRegister(byte[] offset, byte[] data) throws IOException {
|
||||||
|
byte[] raw = new byte[23];
|
||||||
|
raw[0] = CMD_SET_REPORT;
|
||||||
|
raw[1] = 0x16; // Write channel
|
||||||
|
raw[2] = 0x04; // Register
|
||||||
|
for(int i=0; i<offset.length; i++) {
|
||||||
|
raw[3+i] = offset[i];
|
||||||
|
}
|
||||||
|
raw[6] = (byte)data.length;
|
||||||
|
for(int i=0; i<data.length; i++) {
|
||||||
|
raw[7+i] = data[i];
|
||||||
|
}
|
||||||
|
this.sendRaw(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the Wiimote respond the data of an register. The wiimotestreamer
|
||||||
|
* doesn't react to the reponse yet.
|
||||||
|
*
|
||||||
|
* @param offset The memory offset.
|
||||||
|
* @param size The size which has to be read out.
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void readRegister(byte[] offset, byte[] size) throws IOException {
|
||||||
|
byte[] raw = new byte[8];
|
||||||
|
raw[0] = CMD_SET_REPORT;
|
||||||
|
raw[1] = 0x17; // Read channel
|
||||||
|
raw[2] = 0x04; // Register
|
||||||
|
for(int i=0; i<offset.length; i++) {
|
||||||
|
raw[3+i] = offset[i];
|
||||||
|
}
|
||||||
|
for(int i=0; i<size.length; i++) {
|
||||||
|
raw[6+i] = size[i];
|
||||||
|
}
|
||||||
|
this.sendRaw(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads data out of the EEPROM of the wiimote.
|
||||||
|
* At the moment this method is only used to read out the
|
||||||
|
* calibration data, so the wiimotestreamer doesn't react for
|
||||||
|
* every answer on this request.
|
||||||
|
*
|
||||||
|
* @param offset The memory offset.
|
||||||
|
* @param size The size.
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void readEEPROM(byte[] offset, byte[] size) throws IOException {
|
||||||
|
byte[] raw = new byte[8];
|
||||||
|
raw[0] = CMD_SET_REPORT;
|
||||||
|
raw[1] = 0x17; // Read channel
|
||||||
|
raw[2] = 0x00; // EEPROM
|
||||||
|
for(int i=0; i<offset.length; i++) {
|
||||||
|
raw[3+i] = offset[i];
|
||||||
|
}
|
||||||
|
for(int i=0; i<size.length; i++) {
|
||||||
|
raw[6+i] = size[i];
|
||||||
|
}
|
||||||
|
this.sendRaw(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends pure hexdata to the wiimote. If you want your wiimote
|
||||||
|
* to vibrate use sendRaw(new byte[] {0x52, 0x13, 0x01}). For other raw-commands use
|
||||||
|
* the specific wiki-sites around the web (wiili.org, wiibrew.org, ...)
|
||||||
|
* @param raw
|
||||||
|
* byte representation of an command
|
||||||
|
*/
|
||||||
|
public void sendRaw(byte[] raw) throws IOException {
|
||||||
|
if(this.controlCon!=null) {
|
||||||
|
this.controlCon.send(raw);
|
||||||
|
try {
|
||||||
|
Thread.sleep(20l);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
System.out.println("sendRaw() interrupted");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables one or more LEDs, where the value could be between 0 and 8.
|
||||||
|
* If value=1 only the left LED would light up, for value=2 the second
|
||||||
|
* led would light up, for value=3 the first and second led would light up,
|
||||||
|
* and so on...
|
||||||
|
*
|
||||||
|
* @param value Between 0 and 8, indicating which LEDs should light up
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void setLED(int value) throws IOException {
|
||||||
|
if(value<16 && value>0) {
|
||||||
|
byte tmp = (byte)value;
|
||||||
|
this.ledencoding=(byte)(tmp<<4);
|
||||||
|
this.sendRaw(new byte[] {CMD_SET_REPORT, 0x11, this.ledencoding});
|
||||||
|
} else {
|
||||||
|
// Random LED change :)
|
||||||
|
this.setLED(new Random().nextInt(16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the calibration of the accerlarometer. This is done once
|
||||||
|
* per each controller in program lifetime.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private void calibrateAccelerometer() throws IOException {
|
||||||
|
// calibration command
|
||||||
|
this.readEEPROM(new byte[] {0x00, 0x00, 0x20}, new byte[] {0x00, 0x07});
|
||||||
|
this.calibrated=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activates the acceleration sensor. You have to call the
|
||||||
|
* streamData(true) method to react to this acceleration data.
|
||||||
|
* Otherwise the wiimote would send data the whole time and
|
||||||
|
* nothing else would happen.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void enableAccelerationSensors() throws IOException {
|
||||||
|
super.enableAccelerationSensors();
|
||||||
|
if(!this.calibrated) {
|
||||||
|
this.calibrateAccelerometer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// enable acceleration in continuous mode
|
||||||
|
this.sendRaw(new byte[] {CMD_SET_REPORT, 0x12, 0x04, 0x31});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deactivates the acceleration sensors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void disableAccelerationSensors() throws IOException {
|
||||||
|
super.disableAccelerationSensors();
|
||||||
|
this.sendRaw(new byte[] {CMD_SET_REPORT, 0x12, 0x00, 0x30});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables the infrared camera in front of the wiimote to track
|
||||||
|
* IR sources in the field of view of the camera. This could be used
|
||||||
|
* to a lot of amazing stuff. Using this Mode could slow down the
|
||||||
|
* recognition of acceleration gestures during the increased data
|
||||||
|
* size transmitted.
|
||||||
|
*/
|
||||||
|
public void enableInfraredCamera() throws IOException {
|
||||||
|
this.accelerationenabled=true;
|
||||||
|
this.infraredenabled=true;
|
||||||
|
if(!this.calibrated) {
|
||||||
|
this.calibrateAccelerometer();
|
||||||
|
}
|
||||||
|
|
||||||
|
//write 0x04 to output 0x13
|
||||||
|
this.sendRaw(new byte[] {CMD_SET_REPORT, 0x13, 0x04});
|
||||||
|
|
||||||
|
// write 0x04 to output 0x1a
|
||||||
|
this.sendRaw(new byte[] {CMD_SET_REPORT, 0x1a, 0x04});
|
||||||
|
|
||||||
|
// write 0x08 to reguster 0xb00030
|
||||||
|
this.writeRegister(new byte[] {(byte)0xb0, 0x00, 0x30}, new byte[] {0x08});
|
||||||
|
|
||||||
|
// write sensivity block 1 to register 0xb00000
|
||||||
|
this.writeRegister(new byte[] {(byte)0xb0, 0x00, 0x00}, new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte)0x90, 0x00, (byte)0xc0});
|
||||||
|
|
||||||
|
// write sensivity block 2 to register 0xb0001a
|
||||||
|
this.writeRegister(new byte[] {(byte)0xb0, 0x00, (byte)0x1a}, new byte[] {0x40, 0x00});
|
||||||
|
|
||||||
|
// write ir-mode to register 0xb00033
|
||||||
|
this.writeRegister(new byte[] {(byte)0xb0, 0x00, 0x33}, new byte[] {0x03});
|
||||||
|
|
||||||
|
// enable continuous acceleration and IR cam on channel 33
|
||||||
|
this.sendRaw(new byte[] {CMD_SET_REPORT, 0x12, 0x00, 0x33});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* With this method you gain access over the vibrate function of
|
||||||
|
* the wiimote. You got to try which time in milliseconds would
|
||||||
|
* fit your requirements.
|
||||||
|
*
|
||||||
|
* @param milliseconds
|
||||||
|
* time the wiimote would vibrate
|
||||||
|
*/
|
||||||
|
public void vibrateForTime(long milliseconds) throws IOException {
|
||||||
|
try {
|
||||||
|
if(!vibrating) {
|
||||||
|
this.vibrating=true;
|
||||||
|
byte tmp = (byte)(this.ledencoding | 0x01);
|
||||||
|
this.sendRaw(new byte[] {CMD_SET_REPORT, 0x11, tmp});
|
||||||
|
Thread.sleep(milliseconds);
|
||||||
|
this.sendRaw(new byte[] {CMD_SET_REPORT, 0x11, this.ledencoding});
|
||||||
|
this.vibrating=false;
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
System.out.println("WiiMoteThread interrupted.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean infraredEnabled() {
|
||||||
|
return this.infraredenabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ###### Hilfsmethoden
|
||||||
|
// TODO
|
||||||
|
private String removeChar(String s, char c) {
|
||||||
|
String r = "";
|
||||||
|
for (int i = 0; i < s.length(); i ++) {
|
||||||
|
if (s.charAt(i) != c) r += s.charAt(i);
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires a infrared event
|
||||||
|
*
|
||||||
|
* @param coordinates
|
||||||
|
* @param size
|
||||||
|
*/
|
||||||
|
public void fireInfraredEvent(int[][] coordinates, int[] size) {
|
||||||
|
InfraredEvent w = new InfraredEvent(this, coordinates, size);
|
||||||
|
for(int i=0; i<this.devicelistener.size(); i++) {
|
||||||
|
this.devicelistener.get(i).infraredReceived(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
296
src/device/WiimoteStreamer.java
Executable file
296
src/device/WiimoteStreamer.java
Executable file
@@ -0,0 +1,296 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package device;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.EventObject;
|
||||||
|
|
||||||
|
import javax.bluetooth.L2CAPConnection;
|
||||||
|
|
||||||
|
import event.ButtonPressedEvent;
|
||||||
|
import event.ButtonReleasedEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class listens to data sended by the wiimote and generates specific
|
||||||
|
* events for acceleration, buttonpress, ...
|
||||||
|
*
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class WiimoteStreamer extends Thread {
|
||||||
|
|
||||||
|
boolean running;
|
||||||
|
double x0, x1, y0, y1, z0, z1;
|
||||||
|
|
||||||
|
Wiimote wiimote;
|
||||||
|
|
||||||
|
private L2CAPConnection receiveCon;
|
||||||
|
|
||||||
|
EventObject lastevent;
|
||||||
|
|
||||||
|
protected WiimoteStreamer(Wiimote wiimote) {
|
||||||
|
this.wiimote = wiimote;
|
||||||
|
this.receiveCon = wiimote.getReceiveConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
this.running = true;
|
||||||
|
int xraw, yraw, zraw;
|
||||||
|
double x, y, z;
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (running) {
|
||||||
|
// connection has data and we're ready.
|
||||||
|
|
||||||
|
byte[] b = this.getRaw(); // blocks application
|
||||||
|
String[] input = this.byte2hex(b);
|
||||||
|
|
||||||
|
// debug output
|
||||||
|
/*
|
||||||
|
* for(int i=0; i<input.length; i++) {
|
||||||
|
* System.out.print(input[i]); if(i==input.length-1) {
|
||||||
|
* System.out.println(""); } else { System.out.print(":"); } }
|
||||||
|
* /* for(int i=0; i<b.length; i++) {
|
||||||
|
* System.out.print((int)b[i]&0xFF); if(i==input.length-1) {
|
||||||
|
* System.out.println(""); } else { System.out.print(":"); } }
|
||||||
|
*/
|
||||||
|
|
||||||
|
// wiimote wants to tell the calibration data
|
||||||
|
if ((b[1] & 0xFF) == 33) {
|
||||||
|
// if(((b[1] & 0xFF) & 0x21 ) == 0x21) {
|
||||||
|
this.x0 = b[7] & 0xFF;
|
||||||
|
this.y0 = b[8] & 0xFF;
|
||||||
|
this.z0 = b[9] & 0xFF;
|
||||||
|
this.x1 = b[11] & 0xFF;
|
||||||
|
this.y1 = b[12] & 0xFF;
|
||||||
|
this.z1 = b[13] & 0xFF;
|
||||||
|
System.out.println("Autocalibration successful!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// infrared is enabled, so have a look at the last bytes
|
||||||
|
if (this.wiimote.infraredEnabled()
|
||||||
|
&& (((b[1] & 0xFF) & 0x31) == 0x31 || ((b[1] & 0xFF) & 0x33) == 0x33)) {
|
||||||
|
int[][] coordinates = new int[4][2];
|
||||||
|
int[] size = new int[4];
|
||||||
|
int j = 0;
|
||||||
|
|
||||||
|
for (int i = 7; i < 18; i += 3) { // for each IR byte
|
||||||
|
// triple
|
||||||
|
int tailX = b[i] & 0xFF;
|
||||||
|
int tailY = b[i + 1] & 0xFF;
|
||||||
|
int preY = (b[i + 2] & 0xC0) << 2;
|
||||||
|
int preX = (b[i + 2] & 0x30) << 4;
|
||||||
|
|
||||||
|
coordinates[j][0] = tailX + preX;
|
||||||
|
coordinates[j][1] = tailY + preY;
|
||||||
|
size[j++] = (b[i + 2] & 0x0F);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.wiimote.fireInfraredEvent(coordinates, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the wiimote is sending acceleration data...
|
||||||
|
if (this.wiimote.accelerationEnabled()
|
||||||
|
&& (((b[1] & 0xFF) & 0x31) == 0x31 || ((b[1] & 0xFF) & 0x33) == 0x33)) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* calculation of acceleration vectors starts here. further
|
||||||
|
* information about normation exist in the public papers or
|
||||||
|
* the various www-sources.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
xraw = (b[4] & 0xFF);
|
||||||
|
yraw = (b[5] & 0xFF);
|
||||||
|
zraw = (b[6] & 0xFF);
|
||||||
|
|
||||||
|
x = (double) (xraw - x0) / (double) (x1 - x0);
|
||||||
|
y = (double) (yraw - y0) / (double) (y1 - y0);
|
||||||
|
z = (double) (zraw - z0) / (double) (z1 - z0);
|
||||||
|
|
||||||
|
// try to fire event, there could be filters added to the
|
||||||
|
// wiimote class which may prevents from firing.
|
||||||
|
this.wiimote.fireAccelerationEvent(new double[] {x, y, z});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button 1 matches button "2" on wiimote
|
||||||
|
if (((b[3] & 0xFF) & 0x01) == 0x01
|
||||||
|
&& !(this.lastevent instanceof ButtonPressedEvent)) {
|
||||||
|
this.wiimote.fireButtonPressedEvent(1);
|
||||||
|
this.lastevent = new ButtonPressedEvent(
|
||||||
|
this.wiimote, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button 2 matches button "1" on wiimote
|
||||||
|
else if (((b[3] & 0xFF) & 0x02) == 0x02
|
||||||
|
&& !(this.lastevent instanceof ButtonPressedEvent)) {
|
||||||
|
this.wiimote.fireButtonPressedEvent(2);
|
||||||
|
this.lastevent = new ButtonPressedEvent(
|
||||||
|
this.wiimote, 2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button 3 matches button "B" on wiimote
|
||||||
|
else if (((b[3] & 0xFF) & 0x04) == 0x04
|
||||||
|
&& !(this.lastevent instanceof ButtonPressedEvent)) {
|
||||||
|
this.wiimote.fireButtonPressedEvent(3);
|
||||||
|
this.lastevent = new ButtonPressedEvent(
|
||||||
|
this.wiimote, 3);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button 4 matches button "A" on wiimote
|
||||||
|
else if (((b[3] & 0xFF) & 0x08) == 0x08
|
||||||
|
&& !(this.lastevent instanceof ButtonPressedEvent)) {
|
||||||
|
this.wiimote.fireButtonPressedEvent(4);
|
||||||
|
this.lastevent = new ButtonPressedEvent(
|
||||||
|
this.wiimote, 4);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button 5 matches button "MINUS" on wiimote
|
||||||
|
else if (((b[3] & 0xFF) & 0x10) == 0x10
|
||||||
|
&& !(this.lastevent instanceof ButtonPressedEvent)) {
|
||||||
|
this.wiimote.fireButtonPressedEvent(5);
|
||||||
|
this.lastevent = new ButtonPressedEvent(
|
||||||
|
this.wiimote, 5);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button 6 unknown
|
||||||
|
// Button 7 unknown
|
||||||
|
|
||||||
|
// Button 8 matches button "HOME" on wiimote
|
||||||
|
else if (((b[3] & 0xFF) & 0x80) == 0x80
|
||||||
|
&& !(this.lastevent instanceof ButtonPressedEvent)) {
|
||||||
|
this.wiimote.fireButtonPressedEvent(8);
|
||||||
|
this.lastevent = new ButtonPressedEvent(
|
||||||
|
this.wiimote, 8);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button 9 matches "CROSS LEFT" on wiimote
|
||||||
|
else if (((b[2] & 0xFF) & 0x01) == 0x01
|
||||||
|
&& !(this.lastevent instanceof ButtonPressedEvent)) {
|
||||||
|
this.wiimote.fireButtonPressedEvent(9);
|
||||||
|
this.lastevent = new ButtonPressedEvent(
|
||||||
|
this.wiimote, 9);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button 10 matches "CROSS RIGHT" on wiimote
|
||||||
|
else if (((b[2] & 0xFF) & 0x02) == 0x02
|
||||||
|
&& !(this.lastevent instanceof ButtonPressedEvent)) {
|
||||||
|
this.wiimote.fireButtonPressedEvent(10);
|
||||||
|
this.lastevent = new ButtonPressedEvent(
|
||||||
|
this.wiimote, 10);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button 11 matches "CROSS DOWN" on wiimote
|
||||||
|
else if (((b[2] & 0xFF) & 0x04) == 0x04
|
||||||
|
&& !(this.lastevent instanceof ButtonPressedEvent)) {
|
||||||
|
this.wiimote.fireButtonPressedEvent(11);
|
||||||
|
this.lastevent = new ButtonPressedEvent(
|
||||||
|
this.wiimote, 11);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button 12 matches "CROSS UP" on wiimote
|
||||||
|
else if (((b[2] & 0xFF) & 0x08) == 0x08
|
||||||
|
&& !(this.lastevent instanceof ButtonPressedEvent)) {
|
||||||
|
this.wiimote.fireButtonPressedEvent(12);
|
||||||
|
this.lastevent = new ButtonPressedEvent(
|
||||||
|
this.wiimote, 12);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button 13 matches button "PLUS" on wiimote
|
||||||
|
else if (((b[2] & 0xFF) & 0x10) == 0x10
|
||||||
|
&& !(this.lastevent instanceof ButtonPressedEvent)) {
|
||||||
|
this.wiimote.fireButtonPressedEvent(13);
|
||||||
|
this.lastevent = new ButtonPressedEvent(
|
||||||
|
this.wiimote, 13);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button 14 unknown
|
||||||
|
// Button 15 unknown
|
||||||
|
// Button 16 unknown
|
||||||
|
|
||||||
|
// Button released
|
||||||
|
else if ((input[2].equals("20") || input[2].equals("40") || input[2]
|
||||||
|
.equals("60"))
|
||||||
|
&& (input[3].equals("00") || input[3].equals("20")
|
||||||
|
|| input[3].equals("40") || input[3]
|
||||||
|
.equals("60"))
|
||||||
|
&& !(this.lastevent instanceof ButtonReleasedEvent)) {
|
||||||
|
this.wiimote.fireButtonReleasedEvent();
|
||||||
|
this.lastevent = new ButtonReleasedEvent(
|
||||||
|
this.wiimote);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // while(running)
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("Streamer: Connection to Wiimote lost.");
|
||||||
|
this.running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] getRaw() throws IOException {
|
||||||
|
byte[] b = new byte[19];
|
||||||
|
this.receiveCon.receive(b);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stops this thread.
|
||||||
|
*/
|
||||||
|
protected void stopThread() {
|
||||||
|
this.running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isRunning() {
|
||||||
|
return this.running;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] byte2hex(byte[] b) {
|
||||||
|
String[] out = new String[b.length];
|
||||||
|
String stmp = "";
|
||||||
|
for (int n = 0; n < b.length; n++) {
|
||||||
|
stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
|
||||||
|
if (stmp.length() == 1)
|
||||||
|
out[n] = ("0" + stmp).toUpperCase();
|
||||||
|
else
|
||||||
|
out[n] = stmp.toUpperCase();
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
85
src/event/AccelerationEvent.java
Executable file
85
src/event/AccelerationEvent.java
Executable file
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package event;
|
||||||
|
|
||||||
|
import java.util.EventObject;
|
||||||
|
|
||||||
|
import device.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This Event would be generated if an acceleration has been detected.
|
||||||
|
* It contains information about the force applied to the device in each
|
||||||
|
* direction (x, y, z). Further it contains the absolute value of this
|
||||||
|
* vector and the source which generated this event (wiimote).
|
||||||
|
*
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class AccelerationEvent extends EventObject {
|
||||||
|
|
||||||
|
double X, Y, Z;
|
||||||
|
double absvalue;
|
||||||
|
Device source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a WiimoteAccelerationEvent with a specific source,
|
||||||
|
* all the three acceleration values and the calculated absolute
|
||||||
|
* value.
|
||||||
|
*
|
||||||
|
* @param source The source which has been accelerated (Wiimote).
|
||||||
|
* @param X The value of acceleration in the x direction.
|
||||||
|
* @param Y The value of acceleration in the y direction.
|
||||||
|
* @param Z The value of acceleration in the z direction.
|
||||||
|
* @param absvalue The absolute value of this acceleration vector.
|
||||||
|
*/
|
||||||
|
public AccelerationEvent(Device source, double X, double Y, double Z, double absvalue) {
|
||||||
|
super(source);
|
||||||
|
this.source=source;
|
||||||
|
this.X=X;
|
||||||
|
this.Y=Y;
|
||||||
|
this.Z=Z;
|
||||||
|
this.absvalue=absvalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Device getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getX() {
|
||||||
|
return X;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getY() {
|
||||||
|
return Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getZ() {
|
||||||
|
return Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getAbsValue() {
|
||||||
|
return absvalue;
|
||||||
|
}
|
||||||
|
}
|
||||||
83
src/event/ActionStartEvent.java
Executable file
83
src/event/ActionStartEvent.java
Executable file
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package event;
|
||||||
|
|
||||||
|
import java.util.EventObject;
|
||||||
|
|
||||||
|
import device.Device;
|
||||||
|
|
||||||
|
public class ActionStartEvent extends EventObject {
|
||||||
|
|
||||||
|
protected Device source;
|
||||||
|
protected boolean trainbutton;
|
||||||
|
protected boolean recognitionbutton;
|
||||||
|
protected boolean closegesturebutton;
|
||||||
|
|
||||||
|
public ActionStartEvent(Device source) {
|
||||||
|
super(source);
|
||||||
|
this.source=source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Device getSource() {
|
||||||
|
return this.source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is true if this button press has been done by the
|
||||||
|
* individual defined RecognitionButton which has to be
|
||||||
|
* set during initialization of a Wiimote.
|
||||||
|
*
|
||||||
|
* @return Is this button press initiated by the recognition button.
|
||||||
|
* @see device.Wiimote#setRecognitionButton(int) setRecognitionButton()
|
||||||
|
*/
|
||||||
|
public boolean isRecognitionInitEvent() {
|
||||||
|
return this.recognitionbutton;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is true if this button press has been done by the
|
||||||
|
* individual defined TrainButton which has to be
|
||||||
|
* set during initialization of a Wiimote.
|
||||||
|
*
|
||||||
|
* @return Is this button pres initiated by the training button.
|
||||||
|
* @see device.Wiimote#setTrainButton(int) setTrainButton()
|
||||||
|
*/
|
||||||
|
public boolean isTrainInitEvent() {
|
||||||
|
return this.trainbutton;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is true if this button press has been done by the
|
||||||
|
* individual defined CloseGestureButton which has to be
|
||||||
|
* set during initialization of a Wiimote.
|
||||||
|
*
|
||||||
|
* @return Is this button press initiated by the close gesture button.
|
||||||
|
* @see device.Wiimote#setCloseGestureButton(int) setCloseGestureButton()
|
||||||
|
*/
|
||||||
|
public boolean isCloseGestureInitEvent() {
|
||||||
|
return this.closegesturebutton;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
43
src/event/ActionStopEvent.java
Executable file
43
src/event/ActionStopEvent.java
Executable file
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package event;
|
||||||
|
|
||||||
|
import java.util.EventObject;
|
||||||
|
import device.Device;
|
||||||
|
|
||||||
|
public class ActionStopEvent extends EventObject {
|
||||||
|
|
||||||
|
protected Device source;
|
||||||
|
|
||||||
|
public ActionStopEvent(Device source) {
|
||||||
|
super(source);
|
||||||
|
this.source=source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Device getSource() {
|
||||||
|
return this.source;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
97
src/event/ButtonPressedEvent.java
Executable file
97
src/event/ButtonPressedEvent.java
Executable file
@@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package event;
|
||||||
|
|
||||||
|
import device.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* This Event would be generated if a button on a wiimote has been
|
||||||
|
* pressed by user. It contains the source (wiimote) and an integer
|
||||||
|
* representation of which button has been pressed. Please note that
|
||||||
|
* there exist enumeration constants in the class, so you don't
|
||||||
|
* have to use this integer values directly.
|
||||||
|
*
|
||||||
|
* Button 1 matches button "2" on wiimote
|
||||||
|
* Button 2 matches button "1" on wiimote
|
||||||
|
* Button 3 matches button "B" on wiimote
|
||||||
|
* Button 4 matches button "A" on wiimote
|
||||||
|
* Button 5 matches button "MINUS" on wiimote
|
||||||
|
* Button 6 unknown
|
||||||
|
* Button 7 unknown
|
||||||
|
* Button 8 matches button "HOME" on wiimote
|
||||||
|
* Button 9 matches "CROSS LEFT" on wiimote
|
||||||
|
* Button 10 matches "CROSS RIGHT" on wiimote
|
||||||
|
* Button 11 matches "CROSS DOWN" on wiimote
|
||||||
|
* Button 12 matches "CROSS UP" on wiimote
|
||||||
|
* Button 13 matches button "PLUS" on wiimote
|
||||||
|
* Button 14 unknown
|
||||||
|
* Button 15 unknown
|
||||||
|
* Button 16 unknown
|
||||||
|
*
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
*/
|
||||||
|
public class ButtonPressedEvent extends ActionStartEvent {
|
||||||
|
|
||||||
|
// Fixed number values.
|
||||||
|
public static final int BUTTON_2 = 1;
|
||||||
|
public static final int BUTTON_1 = 2;
|
||||||
|
public static final int BUTTON_B = 3;
|
||||||
|
public static final int BUTTON_A = 4;
|
||||||
|
public static final int BUTTON_MINUS = 5;
|
||||||
|
public static final int BUTTON_HOME = 8;
|
||||||
|
public static final int BUTTON_LEFT = 9;
|
||||||
|
public static final int BUTTON_RIGHT = 10;
|
||||||
|
public static final int BUTTON_DOWN = 11;
|
||||||
|
public static final int BUTTON_UP = 12;
|
||||||
|
public static final int BUTTON_PLUS = 13;
|
||||||
|
|
||||||
|
int button;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a WiimoteButtonPressedEvent with the Wiimote source whose
|
||||||
|
* Button has been pressed and the integer representation of the button.
|
||||||
|
*
|
||||||
|
* @param source
|
||||||
|
* @param button
|
||||||
|
*/
|
||||||
|
public ButtonPressedEvent(Device source, int button) {
|
||||||
|
super(source);
|
||||||
|
this.button=button;
|
||||||
|
|
||||||
|
if(source.getRecognitionButton()==button) {
|
||||||
|
this.recognitionbutton=true;
|
||||||
|
} else if(source.getTrainButton()==button) {
|
||||||
|
this.trainbutton=true;
|
||||||
|
} else if(source.getCloseGestureButton()==button) {
|
||||||
|
this.closegesturebutton=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getButton() {
|
||||||
|
return this.button;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
45
src/event/ButtonReleasedEvent.java
Executable file
45
src/event/ButtonReleasedEvent.java
Executable file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package event;
|
||||||
|
|
||||||
|
import java.util.EventObject;
|
||||||
|
|
||||||
|
import device.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* This event would be generated if a button was released.
|
||||||
|
* contains: source (wiimote).
|
||||||
|
*
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
*/
|
||||||
|
public class ButtonReleasedEvent extends ActionStopEvent {
|
||||||
|
|
||||||
|
public ButtonReleasedEvent(Device source) {
|
||||||
|
super(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
83
src/event/DeviceListener.java
Executable file
83
src/event/DeviceListener.java
Executable file
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package event;
|
||||||
|
|
||||||
|
import java.util.EventListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface has to be implemented if the application should react
|
||||||
|
* to pure acceleration data or button press/release. This could be
|
||||||
|
* useful if you want to graphically display the acceleration data or
|
||||||
|
* something else in your application.
|
||||||
|
*
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
*/
|
||||||
|
public interface DeviceListener extends EventListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method would be called if a Wiimote source has been accelerated.
|
||||||
|
*
|
||||||
|
* @param event The acceleration representation as an event.
|
||||||
|
*/
|
||||||
|
public abstract void accelerationReceived(AccelerationEvent event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method would be called if a Wiimote button has been pressed.
|
||||||
|
*
|
||||||
|
* @param event The button representation as an event.
|
||||||
|
*/
|
||||||
|
public abstract void buttonPressReceived(ButtonPressedEvent event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method would be called if a Wiimote button has been released.
|
||||||
|
*
|
||||||
|
* @param event This is actually a meta-event NOT containing which button
|
||||||
|
* has been released.
|
||||||
|
*/
|
||||||
|
public abstract void buttonReleaseReceived(ButtonReleasedEvent event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method would be called if a Wiimote is in idle state and then a
|
||||||
|
* motion starts or if a Wiimote is in motion and then the motion stops and
|
||||||
|
* the Wiimote is in idle state.
|
||||||
|
*
|
||||||
|
* @param event This is the event which contains if the Wiimote is now
|
||||||
|
* in motion or not.
|
||||||
|
*/
|
||||||
|
public abstract void motionStartReceived(MotionStartEvent event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method would be called if a Wiimote is in motion and then the motion
|
||||||
|
* stops and the Wiimote is in idle state.
|
||||||
|
*
|
||||||
|
* @param event This is the event which contains if the Wiimote is now
|
||||||
|
* in motion or not.
|
||||||
|
*/
|
||||||
|
public abstract void motionStopReceived(MotionStopEvent event);
|
||||||
|
|
||||||
|
public abstract void infraredReceived(InfraredEvent event);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
66
src/event/GestureEvent.java
Executable file
66
src/event/GestureEvent.java
Executable file
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package event;
|
||||||
|
|
||||||
|
import logic.ProcessingUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event would be generated if a gesture has been detected.
|
||||||
|
* It contains information about the gesture "name" or type,
|
||||||
|
* the accelerationstreamanalyzer which generated the event (source)
|
||||||
|
* and the probability calculated from the bayes classifier.
|
||||||
|
*
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
*/
|
||||||
|
public class GestureEvent {
|
||||||
|
|
||||||
|
int id;
|
||||||
|
double probability;
|
||||||
|
ProcessingUnit analyzer;
|
||||||
|
|
||||||
|
/** Create a GestureEvent
|
||||||
|
*
|
||||||
|
* @param source The Source, which detected the gesture.
|
||||||
|
* @param id A gesture ID for identifying a gesture.
|
||||||
|
* @param probability The Bayes-Classifier calculated probability.
|
||||||
|
*/
|
||||||
|
public GestureEvent(ProcessingUnit source, int id, double probability) {
|
||||||
|
this.analyzer=source;
|
||||||
|
this.id=id;
|
||||||
|
this.probability=probability;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getProbability() {
|
||||||
|
return this.probability;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProcessingUnit getSource() {
|
||||||
|
return this.analyzer;
|
||||||
|
}
|
||||||
|
}
|
||||||
57
src/event/GestureListener.java
Executable file
57
src/event/GestureListener.java
Executable file
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package event;
|
||||||
|
|
||||||
|
import java.util.EventListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* This is the GestureListener interface which has to be implemented
|
||||||
|
* by any application which should receive recognized gestures.
|
||||||
|
*
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface GestureListener extends EventListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method would be called if a gesture has been recognized.
|
||||||
|
*
|
||||||
|
* @param event The GestureEvent containing information about
|
||||||
|
* the recognized gesture.
|
||||||
|
*/
|
||||||
|
public abstract void gestureReceived(GestureEvent event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method would be called if the gesture-recognizing system
|
||||||
|
* switched the state from recognition to training or otherwise.
|
||||||
|
*
|
||||||
|
* @param event The StateEvent containing information about the
|
||||||
|
* state-change.
|
||||||
|
*/
|
||||||
|
public abstract void stateReceived(StateEvent event);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
63
src/event/InfraredEvent.java
Executable file
63
src/event/InfraredEvent.java
Executable file
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package event;
|
||||||
|
|
||||||
|
import java.util.EventObject;
|
||||||
|
import device.Wiimote;
|
||||||
|
|
||||||
|
public class InfraredEvent extends EventObject {
|
||||||
|
|
||||||
|
protected Wiimote wiimote;
|
||||||
|
protected int[][] coordinates;
|
||||||
|
protected int[] size;
|
||||||
|
protected boolean[] valid;
|
||||||
|
|
||||||
|
public InfraredEvent(Wiimote source, int[][] coordinates, int[] size) {
|
||||||
|
super(source);
|
||||||
|
this.coordinates=coordinates;
|
||||||
|
this.size=size;
|
||||||
|
this.valid = new boolean[4];
|
||||||
|
for(int i=0; i<this.coordinates.length; i++) {
|
||||||
|
this.valid[i] = (this.coordinates[i][0]<1023 && this.coordinates[i][1]<1023);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Wiimote getSource() {
|
||||||
|
return this.wiimote;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValid(int i) {
|
||||||
|
return this.valid[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[][] getCoordinates() {
|
||||||
|
return this.coordinates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] getSize() {
|
||||||
|
return this.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
58
src/event/MotionStartEvent.java
Executable file
58
src/event/MotionStartEvent.java
Executable file
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package event;
|
||||||
|
|
||||||
|
import device.Device;
|
||||||
|
|
||||||
|
public class MotionStartEvent extends ActionStartEvent {
|
||||||
|
|
||||||
|
public MotionStartEvent(Device source) {
|
||||||
|
super(source);
|
||||||
|
|
||||||
|
if(source.getRecognitionButton()==Device.MOTION) {
|
||||||
|
this.recognitionbutton=true;
|
||||||
|
} else if(source.getTrainButton()==Device.MOTION) {
|
||||||
|
this.trainbutton=true;
|
||||||
|
} else if(source.getCloseGestureButton()==Device.MOTION) {
|
||||||
|
this.closegesturebutton=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTrainInitEvent() {
|
||||||
|
return this.trainbutton;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCloseGestureInitEvent() {
|
||||||
|
return this.closegesturebutton;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecognitionInitEvent() {
|
||||||
|
return this.recognitionbutton;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
43
src/event/MotionStopEvent.java
Executable file
43
src/event/MotionStopEvent.java
Executable file
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package event;
|
||||||
|
|
||||||
|
import device.Device;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* This event would be generated if a motion stops.
|
||||||
|
* contains: source (wiimote).
|
||||||
|
*
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
*/
|
||||||
|
public class MotionStopEvent extends ActionStopEvent {
|
||||||
|
|
||||||
|
public MotionStopEvent(Device source) {
|
||||||
|
super(source);
|
||||||
|
// TODO Auto-generated constructor stub
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
67
src/event/StateEvent.java
Executable file
67
src/event/StateEvent.java
Executable file
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package event;
|
||||||
|
|
||||||
|
import java.util.EventObject;
|
||||||
|
import logic.ProcessingUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* This is a StateEvent, telling the listeners in which state of recognition
|
||||||
|
* the tool is:
|
||||||
|
* 1 = training,
|
||||||
|
* 2 = recognition
|
||||||
|
*
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
*/
|
||||||
|
public class StateEvent extends EventObject {
|
||||||
|
|
||||||
|
public final int STATE_LEARNING=1;
|
||||||
|
public final int STATE_RECOGNIZING=2;
|
||||||
|
|
||||||
|
int state;
|
||||||
|
ProcessingUnit analyzer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a StateEvent.
|
||||||
|
*
|
||||||
|
* @param source The source of which the state has changed.
|
||||||
|
* @param state The state the source has switched to.
|
||||||
|
*/
|
||||||
|
public StateEvent(ProcessingUnit source, int state) {
|
||||||
|
super(source);
|
||||||
|
this.analyzer=source;
|
||||||
|
this.state=state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getState() {
|
||||||
|
return this.state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProcessingUnit getSource() {
|
||||||
|
return this.analyzer;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
64
src/filter/DirectionalEquivalenceFilter.java
Executable file
64
src/filter/DirectionalEquivalenceFilter.java
Executable file
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package filter;
|
||||||
|
|
||||||
|
public class DirectionalEquivalenceFilter extends Filter {
|
||||||
|
|
||||||
|
private double sensivity;
|
||||||
|
private double[] reference;
|
||||||
|
|
||||||
|
public DirectionalEquivalenceFilter() {
|
||||||
|
super();
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
this.sensivity=0.2;
|
||||||
|
this.reference = new double[] {0.0, 0.0, 0.0};
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[] filterAlgorithm(double[] vector) {
|
||||||
|
if(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[2]<reference[2]-this.sensivity ||
|
||||||
|
vector[2]>reference[2]+this.sensivity) {
|
||||||
|
this.reference=vector;
|
||||||
|
return vector;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSensivity(double sensivity) {
|
||||||
|
this.sensivity=sensivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getSensivity() {
|
||||||
|
return this.sensivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
63
src/filter/Filter.java
Executable file
63
src/filter/Filter.java
Executable file
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package filter;
|
||||||
|
|
||||||
|
public abstract class Filter {
|
||||||
|
|
||||||
|
public Filter() {
|
||||||
|
// nothing, but should be called via SUPER.
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
// reset filter, if necessary.
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 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
|
||||||
|
* not NULL it would be forwarded to the actual implemented method - filterAlgorithm().
|
||||||
|
* @param vector The acceleration vector, encoding: 0/x, 1/y, 2/z
|
||||||
|
* @return a new, filtered acceleration vector, encoded the same way
|
||||||
|
*/
|
||||||
|
public double[] filter(double[] vector) {
|
||||||
|
if(vector==null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return filterAlgorithm(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
|
||||||
|
* processing of the filter should be really fast, since every acceleration of the wiimote
|
||||||
|
* passes the filter.
|
||||||
|
* @param vector
|
||||||
|
* @param absvalue
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
abstract public double[] filterAlgorithm(double[] vector);
|
||||||
|
|
||||||
|
}
|
||||||
74
src/filter/IdleStateFilter.java
Executable file
74
src/filter/IdleStateFilter.java
Executable file
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package filter;
|
||||||
|
|
||||||
|
public class IdleStateFilter extends Filter {
|
||||||
|
|
||||||
|
private double sensivity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since an acceleration sensor usually provides information even
|
||||||
|
* if it doesn't move, this filter removes the data if it's in the
|
||||||
|
* idle state.
|
||||||
|
*/
|
||||||
|
public IdleStateFilter() {
|
||||||
|
super();
|
||||||
|
this.sensivity = 0.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[] filterAlgorithm(double[] vector) {
|
||||||
|
// calculate values needed for filtering:
|
||||||
|
// absolute value
|
||||||
|
double absvalue = Math.sqrt((vector[0]*vector[0])+
|
||||||
|
(vector[1]*vector[1])+(vector[2]*vector[2]));
|
||||||
|
|
||||||
|
// filter formulaes and return values
|
||||||
|
if(absvalue > 1+this.sensivity ||
|
||||||
|
absvalue < 1-this.sensivity) {
|
||||||
|
return vector;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the absolute value when the wiimote should react to acceleration.
|
||||||
|
* 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
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @param sensivity
|
||||||
|
* acceleration data values smaller than this value wouldn't be detected.
|
||||||
|
*/
|
||||||
|
public void setSensivity(double sensivity) {
|
||||||
|
this.sensivity = sensivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getSensivity() {
|
||||||
|
return this.sensivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
96
src/filter/MotionDetectFilter.java
Executable file
96
src/filter/MotionDetectFilter.java
Executable file
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package filter;
|
||||||
|
|
||||||
|
import device.Device;
|
||||||
|
|
||||||
|
public class MotionDetectFilter extends Filter {
|
||||||
|
|
||||||
|
private int motionchangetime;
|
||||||
|
private boolean nowinmotion;
|
||||||
|
private long motionstartstamp;
|
||||||
|
private Device device;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Detects wheather the wiimote receives acceleration or not and
|
||||||
|
* raises an event, if the device starts or stops. This is actual a
|
||||||
|
* null filter, not manipulating anything. But looks pretty good in
|
||||||
|
* this datatype since it could be removed easily.
|
||||||
|
*
|
||||||
|
* @param wiimote The Wiimote object which is controlled by the filter.
|
||||||
|
*/
|
||||||
|
public MotionDetectFilter(Device device) {
|
||||||
|
super();
|
||||||
|
this.device=device;
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
super.reset();
|
||||||
|
this.motionstartstamp=System.currentTimeMillis();
|
||||||
|
this.nowinmotion=false;
|
||||||
|
this.motionchangetime=190;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[] filter(double[] vector) {
|
||||||
|
|
||||||
|
if(this.nowinmotion &&
|
||||||
|
(System.currentTimeMillis()-this.motionstartstamp)>=
|
||||||
|
this.motionchangetime) {
|
||||||
|
this.nowinmotion=false;
|
||||||
|
this.device.fireMotionStopEvent();
|
||||||
|
} // fi
|
||||||
|
|
||||||
|
return filterAlgorithm(vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[] filterAlgorithm(double[] vector) {
|
||||||
|
if(vector!=null) {
|
||||||
|
this.motionstartstamp=System.currentTimeMillis();
|
||||||
|
if(!this.nowinmotion) {
|
||||||
|
this.nowinmotion=true;
|
||||||
|
this.motionstartstamp=System.currentTimeMillis();
|
||||||
|
this.device.fireMotionStartEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* about what you're doing.
|
||||||
|
* @param time Time in ms
|
||||||
|
*/
|
||||||
|
public void setMotionChangeTime(int time) {
|
||||||
|
this.motionchangetime=time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMotionChangeTime() {
|
||||||
|
return this.motionchangetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
105
src/logic/Classifier.java
Normal file
105
src/logic/Classifier.java
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package logic;
|
||||||
|
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
public class Classifier {
|
||||||
|
|
||||||
|
private Vector<GestureModel> gesturemodel; // each gesturetype got its own
|
||||||
|
// gesturemodel in this vector
|
||||||
|
private double lastprob;
|
||||||
|
|
||||||
|
public Classifier() {
|
||||||
|
this.gesturemodel=new Vector<GestureModel>();
|
||||||
|
this.lastprob=0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method recognize a specific gesture, given to the procedure.
|
||||||
|
* For classification a bayes classification algorithm is used.
|
||||||
|
*
|
||||||
|
* @param g gesture to classify
|
||||||
|
*/
|
||||||
|
public int classifyGesture(Gesture g) {
|
||||||
|
//System.out.println("Recognizing gesture...");
|
||||||
|
|
||||||
|
// Wert im Nenner berechnen, nach Bayes
|
||||||
|
double sum = 0;
|
||||||
|
for(int i=0; i<this.gesturemodel.size(); i++) {
|
||||||
|
sum+=this.gesturemodel.elementAt(i).getDefaultProbability()*
|
||||||
|
this.gesturemodel.elementAt(i).matches(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
int recognized = -1; // which gesture has been recognized
|
||||||
|
double recogprob = Integer.MIN_VALUE; // probability of this gesture
|
||||||
|
double probgesture = 0; // temporal value for bayes algorithm
|
||||||
|
double probmodel = 0; // temporal value for bayes algorithm
|
||||||
|
for(int i=0; i<this.gesturemodel.size(); i++) {
|
||||||
|
//this.gesturemodel.elementAt(i).print(); // Debug
|
||||||
|
double tmpgesture = this.gesturemodel.elementAt(i).matches(g);
|
||||||
|
double tmpmodel = this.gesturemodel.elementAt(i).getDefaultProbability();
|
||||||
|
|
||||||
|
if(((tmpmodel*tmpgesture)/sum)>recogprob) {
|
||||||
|
probgesture=tmpgesture;
|
||||||
|
probmodel=tmpmodel;
|
||||||
|
recogprob=((tmpmodel*tmpgesture)/sum);
|
||||||
|
recognized=i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// a gesture could be recognized
|
||||||
|
if(recogprob>0 && probmodel>0 && probgesture>0 && sum>0) {
|
||||||
|
this.lastprob=recogprob;
|
||||||
|
return recognized;
|
||||||
|
} else {
|
||||||
|
// no gesture could be recognized
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getLastProbability() {
|
||||||
|
return this.lastprob;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addGestureModel(GestureModel gm) {
|
||||||
|
this.gesturemodel.add(gm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GestureModel getGestureModel(int id) {
|
||||||
|
return this.gesturemodel.elementAt(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector<GestureModel> getGestureModels() {
|
||||||
|
return this.gesturemodel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
this.gesturemodel = new Vector<GestureModel>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
155
src/logic/Gesture.java
Executable file
155
src/logic/Gesture.java
Executable file
@@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package logic;
|
||||||
|
|
||||||
|
import java.util.Vector;
|
||||||
|
import event.AccelerationEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents ONE movement trajectory in a
|
||||||
|
* concrete instance.
|
||||||
|
*
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Gesture implements Cloneable {
|
||||||
|
|
||||||
|
/** Min/MaxAcceleration setup manually? */
|
||||||
|
private boolean minmaxmanual;
|
||||||
|
private double minacc;
|
||||||
|
private double maxacc;
|
||||||
|
|
||||||
|
/** The complete trajectory as WiimoteAccelerationEvents
|
||||||
|
* as a vector. It's a vector because we don't want to
|
||||||
|
* loose the chronology of the stored events.
|
||||||
|
*/
|
||||||
|
private Vector<AccelerationEvent> data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an empty Gesture.
|
||||||
|
*/
|
||||||
|
public Gesture() {
|
||||||
|
this.data = new Vector<AccelerationEvent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a deep copy of another Gesture object.
|
||||||
|
*
|
||||||
|
* @param original Another Gesture object
|
||||||
|
*/
|
||||||
|
public Gesture(Gesture original) {
|
||||||
|
this.data = new Vector<AccelerationEvent>();
|
||||||
|
Vector<AccelerationEvent> origin = original.getData();
|
||||||
|
for (int i = 0; i < origin.size(); i++) {
|
||||||
|
this.add((AccelerationEvent) origin.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new acceleration event to this gesture.
|
||||||
|
*
|
||||||
|
* @param event The WiimoteAccelerationEvent to add.
|
||||||
|
*/
|
||||||
|
public void add(AccelerationEvent event) {
|
||||||
|
this.data.add(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the last acceleration added to this gesture.
|
||||||
|
*
|
||||||
|
* @return the last acceleration event added.
|
||||||
|
*/
|
||||||
|
public AccelerationEvent getLastData() {
|
||||||
|
return (AccelerationEvent) this.data.get(this.data.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the whole chronological sequence of accelerations as
|
||||||
|
* a vector.
|
||||||
|
*
|
||||||
|
* @return chronological sequence of accelerations.
|
||||||
|
*/
|
||||||
|
public Vector<AccelerationEvent> getData() {
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the first element of the acceleration queue of a gesture
|
||||||
|
*/
|
||||||
|
public void removeFirstData() {
|
||||||
|
this.data.remove(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCountOfData() {
|
||||||
|
return this.data.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxAndMinAcceleration(double max, double min) {
|
||||||
|
this.maxacc = max;
|
||||||
|
this.minacc = min;
|
||||||
|
this.minmaxmanual = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getMaxAcceleration() {
|
||||||
|
if(!this.minmaxmanual) {
|
||||||
|
double maxacc = Double.MIN_VALUE;
|
||||||
|
for(int i=0; i<this.data.size(); i++) {
|
||||||
|
if(Math.abs(this.data.get(i).getX()) > maxacc) {
|
||||||
|
maxacc=Math.abs(this.data.get(i).getX());
|
||||||
|
}
|
||||||
|
if(Math.abs(this.data.get(i).getY()) > maxacc) {
|
||||||
|
maxacc=Math.abs(this.data.get(i).getY());
|
||||||
|
}
|
||||||
|
if(Math.abs(this.data.get(i).getZ()) > maxacc) {
|
||||||
|
maxacc=Math.abs(this.data.get(i).getZ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxacc;
|
||||||
|
} else {
|
||||||
|
return this.maxacc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getMinAcceleration() {
|
||||||
|
if(!this.minmaxmanual) {
|
||||||
|
double minacc = Double.MAX_VALUE;
|
||||||
|
for(int i=0; i<this.data.size(); i++) {
|
||||||
|
if(Math.abs(this.data.get(i).getX()) < minacc) {
|
||||||
|
minacc=Math.abs(this.data.get(i).getX());
|
||||||
|
}
|
||||||
|
if(Math.abs(this.data.get(i).getY()) < minacc) {
|
||||||
|
minacc=Math.abs(this.data.get(i).getY());
|
||||||
|
}
|
||||||
|
if(Math.abs(this.data.get(i).getZ()) < minacc) {
|
||||||
|
minacc=Math.abs(this.data.get(i).getZ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return minacc;
|
||||||
|
} else {
|
||||||
|
return this.minacc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
217
src/logic/GestureModel.java
Executable file
217
src/logic/GestureModel.java
Executable file
@@ -0,0 +1,217 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package logic;
|
||||||
|
|
||||||
|
import java.util.Vector;
|
||||||
|
import event.AccelerationEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This Class units a Quantizer-Component and an Model-Component.
|
||||||
|
* In this implementation a k-mean-algorithm for quantization and
|
||||||
|
* a hidden markov model as instance for the model has been used.
|
||||||
|
*
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
*/
|
||||||
|
public class GestureModel {
|
||||||
|
|
||||||
|
/** The number of states the hidden markov model consists of */
|
||||||
|
private int numStates;
|
||||||
|
|
||||||
|
/** The number of observations for the hmm and k-mean */
|
||||||
|
private int numObservations;
|
||||||
|
|
||||||
|
/** The id representation of this model */
|
||||||
|
private int id;
|
||||||
|
|
||||||
|
/** The quantization component */
|
||||||
|
private Quantizer quantizer;
|
||||||
|
|
||||||
|
/** The statistical model, hidden markov model */
|
||||||
|
private HMM markovmodell;
|
||||||
|
|
||||||
|
/** The default probability of this gesturemodel,
|
||||||
|
* needed for the bayes classifier */
|
||||||
|
private double defaultprobability;
|
||||||
|
|
||||||
|
|
||||||
|
/** Creates a Unit (Quantizer&Model).
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* int representation of a gesture "name"/class.
|
||||||
|
*/
|
||||||
|
public GestureModel(int id) {
|
||||||
|
this.id=id;
|
||||||
|
this.numStates=8; // n=8 states empirical value
|
||||||
|
this.numObservations=14; // k=14 observations empirical value
|
||||||
|
this.markovmodell = new HMM(numStates, numObservations); // init model
|
||||||
|
this.quantizer = new Quantizer(numStates); // init quantizer
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trains the model to a set of motion-sequences, representing
|
||||||
|
* different evaluations of a gesture
|
||||||
|
*
|
||||||
|
* @param trainsequence a vector of gestures
|
||||||
|
*/
|
||||||
|
public void train(Vector<Gesture> trainsequence) {
|
||||||
|
// summarize all vectors from the different gestures in one
|
||||||
|
// gesture called sum.
|
||||||
|
double maxacc=0;
|
||||||
|
double minacc=0;
|
||||||
|
Gesture sum = new Gesture();
|
||||||
|
|
||||||
|
for(int i=0; i<trainsequence.size(); i++) {
|
||||||
|
Vector<AccelerationEvent> t = trainsequence.elementAt(i).getData();
|
||||||
|
|
||||||
|
// add the max and min acceleration, we later get the average
|
||||||
|
maxacc+=trainsequence.elementAt(i).getMaxAcceleration();
|
||||||
|
minacc+=trainsequence.elementAt(i).getMinAcceleration();
|
||||||
|
|
||||||
|
// transfer every single accelerationevent of each gesture to
|
||||||
|
// the new gesture sum
|
||||||
|
for(int j=0; j<trainsequence.elementAt(i).getData().size(); j++) {
|
||||||
|
sum.add(t.elementAt(j));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the average and set it to the sum gesture
|
||||||
|
sum.setMaxAndMinAcceleration(maxacc/trainsequence.size(), minacc/trainsequence.size());
|
||||||
|
|
||||||
|
// train the centeroids of the quantizer with this master gesture sum.
|
||||||
|
this.quantizer.trainCenteroids(sum);
|
||||||
|
|
||||||
|
// convert gesture vector to a sequence of discrete values
|
||||||
|
Vector<int[]> seqs = new Vector<int[]>();
|
||||||
|
for(int i=0; i<trainsequence.size(); i++) {
|
||||||
|
seqs.add(this.quantizer.getObservationSequence(trainsequence.elementAt(i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// train the markov model with this derived discrete sequences
|
||||||
|
this.markovmodell.train(seqs);
|
||||||
|
|
||||||
|
// set the default probability for use with the bayes classifier
|
||||||
|
this.setDefaultProbability(trainsequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the probability that a gesture matches to this
|
||||||
|
* gesture model.
|
||||||
|
*
|
||||||
|
* @param gesture a gesture to test.
|
||||||
|
* @return probability that the gesture belongs to this gesture
|
||||||
|
* model.
|
||||||
|
*/
|
||||||
|
public double matches(Gesture gesture) {
|
||||||
|
int[] sequence = quantizer.getObservationSequence(gesture);
|
||||||
|
return this.markovmodell.getProbability(sequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For debug purposes or very technical interested people. :)
|
||||||
|
*/
|
||||||
|
public void printMap() {
|
||||||
|
System.out.println("Gesture "+this.id+" Quantizer-Map:");
|
||||||
|
this.quantizer.printMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* For debug purposes or very technical interested people. :)
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public void print() {
|
||||||
|
System.out.println("Debug Output for Gesture "+this.id);
|
||||||
|
System.out.println("HMM-Print:");
|
||||||
|
this.markovmodell.print();
|
||||||
|
System.out.println("Quanzizer-Print:");
|
||||||
|
this.quantizer.printMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(int id) {
|
||||||
|
this.id=id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumStates() {
|
||||||
|
return this.numStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumObservations() {
|
||||||
|
return this.numObservations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the model probability for bayes.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* the model probability
|
||||||
|
*/
|
||||||
|
public double getDefaultProbability() {
|
||||||
|
return this.defaultprobability;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since the bayes classifier needs a model probability for
|
||||||
|
* each model this has to be set once after training. As model
|
||||||
|
* probability the average probability value has been choosen.
|
||||||
|
*
|
||||||
|
* TODO: try lowest or highest model probability as alternative
|
||||||
|
*
|
||||||
|
* @param defsequence the vector of training sequences.
|
||||||
|
*/
|
||||||
|
private void setDefaultProbability(Vector<Gesture> defsequence) {
|
||||||
|
double prob=0;
|
||||||
|
for(int i=0; i<defsequence.size(); i++) {
|
||||||
|
prob+=this.matches(defsequence.elementAt(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.defaultprobability=(prob)/defsequence.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultProbability(double prob) {
|
||||||
|
this.defaultprobability = prob;
|
||||||
|
System.out.println("def-prob. set to = "+this.defaultprobability);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Quantizer getQuantizer() {
|
||||||
|
return this.quantizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQuantizer(Quantizer q) {
|
||||||
|
this.quantizer = q;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HMM getHMM() {
|
||||||
|
return this.markovmodell;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHMM(HMM hmm) {
|
||||||
|
this.markovmodell = hmm;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
315
src/logic/HMM.java
Executable file
315
src/logic/HMM.java
Executable file
@@ -0,0 +1,315 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package logic;
|
||||||
|
import java.text.*;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a Hidden Markov Model implementation which internally provides
|
||||||
|
* the basic algorithms for training and recognition (forward and backward
|
||||||
|
* algorithm). Since a regular Hidden Markov Model doesn't provide a possibility
|
||||||
|
* to train multiple sequences, this implementation has been optimized for this
|
||||||
|
* purposes using some state-of-the-art technologies described in several papers.
|
||||||
|
*
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class HMM {
|
||||||
|
/** The number of states */
|
||||||
|
private int numStates;
|
||||||
|
|
||||||
|
/** The number of observations */
|
||||||
|
private int numObservations;
|
||||||
|
|
||||||
|
/** The initial probabilities for each state: p[state] */
|
||||||
|
private double pi[];
|
||||||
|
|
||||||
|
/** The state change probability to switch from state A to
|
||||||
|
* state B: a[stateA][stateB] */
|
||||||
|
private double a[][];
|
||||||
|
|
||||||
|
/** The probability to emit symbol S in state A: b[stateA][symbolS] */
|
||||||
|
private double b[][];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the Hidden Markov Model in a left-to-right version.
|
||||||
|
*
|
||||||
|
* @param numStates Number of states
|
||||||
|
* @param numObservations Number of observations
|
||||||
|
*/
|
||||||
|
public HMM(int numStates, int numObservations) {
|
||||||
|
this.numStates = numStates;
|
||||||
|
this.numObservations = numObservations;
|
||||||
|
pi = new double[numStates];
|
||||||
|
a = new double[numStates][numStates];
|
||||||
|
b = new double[numStates][numObservations];
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the Hidden Markov Model to the initial left-to-right values.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private void reset() {
|
||||||
|
int jumplimit = 2;
|
||||||
|
|
||||||
|
// set startup probability
|
||||||
|
pi[0] = 1;
|
||||||
|
for(int i=1; i<numStates; i++) {
|
||||||
|
pi[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set state change probabilities in the left-to-right version
|
||||||
|
// NOTE: i now that this is dirty and very static. :)
|
||||||
|
for(int i=0; i<numStates; i++) {
|
||||||
|
for(int j=0; j<numStates; j++) {
|
||||||
|
if(i==numStates-1 && j==numStates-1) { // last row
|
||||||
|
a[i][j] = 1.0;
|
||||||
|
} else if(i==numStates-2 && j==numStates-2) { // next to last row
|
||||||
|
a[i][j] = 0.5;
|
||||||
|
} else if(i==numStates-2 && j==numStates-1) { // next to last row
|
||||||
|
a[i][j] = 0.5;
|
||||||
|
} else if(i<=j && i>j-jumplimit-1) {
|
||||||
|
a[i][j] = 1.0/(jumplimit+1);
|
||||||
|
} else {
|
||||||
|
a[i][j] = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// emission probability
|
||||||
|
for(int i=0; i<numStates; i++) {
|
||||||
|
for(int j=0; j<numObservations; j++) {
|
||||||
|
b[i][j] = 1.0/(double)numObservations;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trains the Hidden Markov Model with multiple sequences.
|
||||||
|
* This method is normally not known to basic hidden markov
|
||||||
|
* models, because they usually use the Baum-Welch-Algorithm.
|
||||||
|
* This method is NOT the traditional Baum-Welch-Algorithm.
|
||||||
|
*
|
||||||
|
* If you want to know in detail how it works please consider
|
||||||
|
* my Individuelles Projekt paper on the wiigee Homepage. Also
|
||||||
|
* there exist some english literature on the world wide web.
|
||||||
|
* Try to search for some papers by Rabiner or have a look at
|
||||||
|
* Vesa-Matti Mäntylä - "Discrete Hidden Markov Models with
|
||||||
|
* application to isolated user-dependent hand gesture recognition".
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void train(Vector<int[]> trainsequence) {
|
||||||
|
|
||||||
|
double[][] a_new = new double[a.length][a.length];
|
||||||
|
double[][] b_new = new double[b.length][b[0].length];
|
||||||
|
// re calculate state change probability a
|
||||||
|
for(int i=0; i<a.length; i++) {
|
||||||
|
for(int j=0; j<a[i].length; j++) {
|
||||||
|
double zaehler=0;
|
||||||
|
double nenner=0;
|
||||||
|
|
||||||
|
for(int k=0; k<trainsequence.size(); k++) {
|
||||||
|
this.reset();
|
||||||
|
int[] sequence = trainsequence.elementAt(k);
|
||||||
|
|
||||||
|
double[][] fwd = this.forwardProc(sequence);
|
||||||
|
double[][] bwd = this.backwardProc(sequence);
|
||||||
|
double prob = this.getProbability(sequence);
|
||||||
|
|
||||||
|
double zaehler_innersum=0;
|
||||||
|
double nenner_innersum=0;
|
||||||
|
|
||||||
|
|
||||||
|
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];
|
||||||
|
nenner_innersum+=fwd[i][t]*bwd[i][t];
|
||||||
|
}
|
||||||
|
zaehler+=(1/prob)*zaehler_innersum;
|
||||||
|
nenner+=(1/prob)*nenner_innersum;
|
||||||
|
} // k
|
||||||
|
|
||||||
|
a_new[i][j] = zaehler/nenner;
|
||||||
|
} // j
|
||||||
|
} // i
|
||||||
|
|
||||||
|
// re calculate emission probability b
|
||||||
|
for(int i=0; i<b.length; i++) { // zustaende
|
||||||
|
for(int j=0; j<b[i].length; j++) { // symbole
|
||||||
|
double zaehler=0;
|
||||||
|
double nenner=0;
|
||||||
|
|
||||||
|
for(int k=0; k<trainsequence.size(); k++) {
|
||||||
|
this.reset();
|
||||||
|
int[] sequence = trainsequence.elementAt(k);
|
||||||
|
|
||||||
|
double[][] fwd = this.forwardProc(sequence);
|
||||||
|
double[][] bwd = this.backwardProc(sequence);
|
||||||
|
double prob = this.getProbability(sequence);
|
||||||
|
|
||||||
|
double zaehler_innersum=0;
|
||||||
|
double nenner_innersum=0;
|
||||||
|
|
||||||
|
|
||||||
|
for(int t=0; t<sequence.length-1; t++) {
|
||||||
|
if(sequence[t]==j) {
|
||||||
|
zaehler_innersum+=fwd[i][t]*bwd[i][t];
|
||||||
|
}
|
||||||
|
nenner_innersum+=fwd[i][t]*bwd[i][t];
|
||||||
|
}
|
||||||
|
zaehler+=(1/prob)*zaehler_innersum;
|
||||||
|
nenner+=(1/prob)*nenner_innersum;
|
||||||
|
} // k
|
||||||
|
|
||||||
|
b_new[i][j] = zaehler/nenner;
|
||||||
|
} // j
|
||||||
|
} // i
|
||||||
|
|
||||||
|
this.a=a_new;
|
||||||
|
this.b=b_new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traditional Forward Algorithm.
|
||||||
|
*
|
||||||
|
* @param o the observationsequence O
|
||||||
|
* @return Array[State][Time]
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private double[][] forwardProc(int[] o) {
|
||||||
|
double[][] f = new double[numStates][o.length];
|
||||||
|
for (int l = 0; l < f.length; l++) {
|
||||||
|
f[l][0] = pi[l] * b[l][o[0]];
|
||||||
|
}
|
||||||
|
for (int i = 1; i < o.length; i++) {
|
||||||
|
for (int k = 0; k < f.length; k++) {
|
||||||
|
double sum = 0;
|
||||||
|
for (int l = 0; l < numStates; l++) {
|
||||||
|
sum += f[l][i-1] * a[l][k];
|
||||||
|
}
|
||||||
|
f[k][i] = sum * b[k][o[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the probability that a observation sequence O belongs
|
||||||
|
* to this Hidden Markov Model without using the bayes classifier.
|
||||||
|
* Internally the well known forward algorithm is used.
|
||||||
|
*
|
||||||
|
* @param o observation sequence
|
||||||
|
* @return probability that sequence o belongs to this hmm
|
||||||
|
*/
|
||||||
|
public double getProbability(int[] o) {
|
||||||
|
double prob = 0.0;
|
||||||
|
double[][] forward = this.forwardProc(o);
|
||||||
|
// add probabilities
|
||||||
|
for (int i = 0; i < forward.length; i++) { // for every state
|
||||||
|
prob += forward[i][forward[i].length - 1];
|
||||||
|
}
|
||||||
|
return prob;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backward algorithm.
|
||||||
|
*
|
||||||
|
* @param o observation sequence o
|
||||||
|
* @return Array[State][Time]
|
||||||
|
*/
|
||||||
|
public double[][] backwardProc(int[] o) {
|
||||||
|
int T = o.length;
|
||||||
|
double[][] bwd = new double[numStates][T];
|
||||||
|
/* Basisfall */
|
||||||
|
for (int i = 0; i < numStates; i++)
|
||||||
|
bwd[i][T - 1] = 1;
|
||||||
|
/* Induktion */
|
||||||
|
for (int t = T - 2; t >= 0; t--) {
|
||||||
|
for (int i = 0; i < numStates; i++) {
|
||||||
|
bwd[i][t] = 0;
|
||||||
|
for (int j = 0; j < numStates; j++)
|
||||||
|
bwd[i][t] += (bwd[j][t + 1] * a[i][j] * b[j][o[t + 1]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints everything about this model, including
|
||||||
|
* all values. For debug purposes or if you want
|
||||||
|
* to comprehend what happend to the model.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void print() {
|
||||||
|
DecimalFormat fmt = new DecimalFormat();
|
||||||
|
fmt.setMinimumFractionDigits(5);
|
||||||
|
fmt.setMaximumFractionDigits(5);
|
||||||
|
for (int i = 0; i < numStates; i++)
|
||||||
|
System.out.println("pi(" + i + ") = " + fmt.format(pi[i]));
|
||||||
|
System.out.println();
|
||||||
|
for (int i = 0; i < numStates; i++) {
|
||||||
|
for (int j = 0; j < numStates; j++)
|
||||||
|
System.out.print("a(" + i + "," + j + ") = "
|
||||||
|
+ fmt.format(a[i][j]) + " ");
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
for (int i = 0; i < numStates; i++) {
|
||||||
|
for (int k = 0; k < numObservations; k++)
|
||||||
|
System.out.print("b(" + i + "," + k + ") = "
|
||||||
|
+ fmt.format(b[i][k]) + " ");
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[] getPi() {
|
||||||
|
return this.pi;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPi(double[] pi) {
|
||||||
|
this.pi = pi;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[][] getA() {
|
||||||
|
return this.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setA(double[][] a) {
|
||||||
|
this.a = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[][] getB() {
|
||||||
|
return this.b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setB(double[][] b) {
|
||||||
|
this.b=b;
|
||||||
|
}
|
||||||
|
}
|
||||||
448
src/logic/PreciseHMM.java
Executable file
448
src/logic/PreciseHMM.java
Executable file
@@ -0,0 +1,448 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package logic;
|
||||||
|
import java.text.*;
|
||||||
|
import java.util.Vector;
|
||||||
|
import java.lang.Math;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a Hidden Markov Model implementation which internally provides
|
||||||
|
* the basic algorithms for training and recognition (forward and backward
|
||||||
|
* algorithm). Since a regular Hidden Markov Model doesn't provide a possibility
|
||||||
|
* to train multiple sequences, this implementation has been optimized for this
|
||||||
|
* purposes using some state-of-the-art technologies described in several papers.
|
||||||
|
*
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class PreciseHMM {
|
||||||
|
/** The number of states */
|
||||||
|
private int numStates;
|
||||||
|
|
||||||
|
/** The number of observations */
|
||||||
|
private int sigmaSize;
|
||||||
|
|
||||||
|
/** The initial probabilities for each state: p[state] */
|
||||||
|
public double pi[];
|
||||||
|
|
||||||
|
/** The state change probability to switch from state A to
|
||||||
|
* state B: a[stateA][stateB] */
|
||||||
|
public double a[][];
|
||||||
|
|
||||||
|
/** The probability to emit symbol S in state A: b[stateA][symbolS] */
|
||||||
|
public double b[][];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the Hidden Markov Model in a left-to-right version.
|
||||||
|
*
|
||||||
|
* @param numStates Number of states
|
||||||
|
* @param sigmaSize Number of observations
|
||||||
|
*/
|
||||||
|
public PreciseHMM(int numStates, int sigmaSize) {
|
||||||
|
this.numStates = numStates;
|
||||||
|
this.sigmaSize = sigmaSize;
|
||||||
|
pi = new double[numStates];
|
||||||
|
a = new double[numStates][numStates];
|
||||||
|
b = new double[numStates][sigmaSize];
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the Hidden Markov Model to the initial left-to-right values.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private void reset() {
|
||||||
|
int jumplimit = 2;
|
||||||
|
|
||||||
|
// set startup probability
|
||||||
|
pi[0] = 1.0;
|
||||||
|
for(int i=1; i<numStates; i++) {
|
||||||
|
pi[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set state change probabilities in the left-to-right version
|
||||||
|
// NOTE: i now that this is dirty and very static. :)
|
||||||
|
for(int i=0; i<numStates; i++) {
|
||||||
|
for(int j=0; j<numStates; j++) {
|
||||||
|
if(i==numStates-1 && j==numStates-1) { // last row
|
||||||
|
a[i][j] = 1.0;
|
||||||
|
} else if(i==numStates-2 && j==numStates-2) { // next to last row
|
||||||
|
a[i][j] = 0.5;
|
||||||
|
} else if(i==numStates-2 && j==numStates-1) { // next to last row
|
||||||
|
a[i][j] = 0.5;
|
||||||
|
} else if(i<=j && i>j-jumplimit-1) {
|
||||||
|
a[i][j] = 1.0/(jumplimit+1);
|
||||||
|
} else {
|
||||||
|
a[i][j] = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// emission probability
|
||||||
|
for(int i=0; i<numStates; i++) {
|
||||||
|
for(int j=0; j<sigmaSize; j++) {
|
||||||
|
b[i][j] = 1.0/(double)sigmaSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trains the Hidden Markov Model with multiple sequences.
|
||||||
|
* This method is normally not known to basic hidden markov
|
||||||
|
* models, because they usually use the Baum-Welch-Algorithm.
|
||||||
|
* This method is NOT the traditional Baum-Welch-Algorithm.
|
||||||
|
*
|
||||||
|
* If you want to know in detail how it works please consider
|
||||||
|
* my Individuelles Projekt paper on the wiigee Homepage. Also
|
||||||
|
* there exist some english literature on the world wide web.
|
||||||
|
* Try to search for some papers by Rabiner or have a look at
|
||||||
|
* Vesa-Matti Mäntylä - "Discrete Hidden Markov Models with
|
||||||
|
* application to isolated user-dependent hand gesture recognition".
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void train(Vector<int[]> trainsequence) {
|
||||||
|
|
||||||
|
double[][] a_new = new double[a.length][a.length];
|
||||||
|
double[][] b_new = new double[b.length][b[0].length];
|
||||||
|
// re calculate state change probability a
|
||||||
|
for(int i=0; i<a.length; i++) {
|
||||||
|
for(int j=0; j<a[i].length; j++) {
|
||||||
|
double zaehler=0;
|
||||||
|
double nenner=0;
|
||||||
|
|
||||||
|
for(int k=0; k<trainsequence.size(); k++) {
|
||||||
|
//this.reset();
|
||||||
|
int[] sequence = trainsequence.elementAt(k);
|
||||||
|
|
||||||
|
double[] sf = this.calculateScalingFactor(sequence);
|
||||||
|
double[][] fwd = this.scaledForwardProc(sequence);
|
||||||
|
double[][] bwd = this.scaledBackwardProc(sequence, sf);
|
||||||
|
|
||||||
|
double zaehler_innersum=0;
|
||||||
|
double nenner_innersum=0;
|
||||||
|
|
||||||
|
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];
|
||||||
|
nenner_innersum+=fwd[i][t]*bwd[i][t];
|
||||||
|
}
|
||||||
|
zaehler+=zaehler_innersum;
|
||||||
|
nenner+=nenner_innersum;
|
||||||
|
} // k
|
||||||
|
|
||||||
|
a_new[i][j] = zaehler/nenner;
|
||||||
|
} // j
|
||||||
|
} // i
|
||||||
|
|
||||||
|
// re calculate emission probability b
|
||||||
|
for(int i=0; i<b.length; i++) { // zustaende
|
||||||
|
for(int j=0; j<b[i].length; j++) { // symbole
|
||||||
|
double zaehler=0;
|
||||||
|
double nenner=0;
|
||||||
|
|
||||||
|
for(int k=0; k<trainsequence.size(); k++) {
|
||||||
|
//this.reset();
|
||||||
|
int[] sequence = trainsequence.elementAt(k);
|
||||||
|
|
||||||
|
double[] sf = this.calculateScalingFactor(sequence);
|
||||||
|
double[][] fwd = this.scaledForwardProc(sequence);
|
||||||
|
double[][] bwd = this.scaledBackwardProc(sequence, sf);
|
||||||
|
|
||||||
|
double zaehler_innersum=0;
|
||||||
|
double nenner_innersum=0;
|
||||||
|
|
||||||
|
for(int t=0; t<sequence.length-1; t++) {
|
||||||
|
if(sequence[t]==j) {
|
||||||
|
zaehler_innersum+=fwd[i][t]*bwd[i][t]*sf[t];
|
||||||
|
}
|
||||||
|
nenner_innersum+=fwd[i][t]*bwd[i][t]*sf[t];
|
||||||
|
}
|
||||||
|
zaehler+=zaehler_innersum;
|
||||||
|
nenner+=nenner_innersum;
|
||||||
|
} // k
|
||||||
|
|
||||||
|
b_new[i][j] = zaehler/nenner;
|
||||||
|
} // j
|
||||||
|
} // i
|
||||||
|
|
||||||
|
this.a=a_new;
|
||||||
|
this.b=b_new;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private double[] calculateScalingFactor(int[] sequence) {
|
||||||
|
// for all indexing: [state][time]
|
||||||
|
double[][] fwd = this.forwardProc(sequence); // normal
|
||||||
|
double[][] help = new double[fwd.length][fwd[0].length];
|
||||||
|
double[][] scaled = new double[fwd.length][fwd[0].length];
|
||||||
|
double[] sf = new double[sequence.length];
|
||||||
|
|
||||||
|
// ************** BASIS *************
|
||||||
|
// Basis, fixed t=0
|
||||||
|
// setup, because needed for further calculations
|
||||||
|
for(int i=0; i<help.length; i++) {
|
||||||
|
help[i][0] = fwd[i][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup initial scaled array
|
||||||
|
double sum0 = 0;
|
||||||
|
for(int i=0; i<help.length; i++) {
|
||||||
|
sum0+=help[i][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i=0; i<scaled.length; i++) {
|
||||||
|
scaled[i][0] = help[i][0] / sum0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate scaling factor
|
||||||
|
sf[0] = 1/sum0;
|
||||||
|
|
||||||
|
// **************** INDUCTION ***************
|
||||||
|
// end of fixed t = 0
|
||||||
|
// starting with t>1 to sequence.length
|
||||||
|
// induction, further calculations
|
||||||
|
for(int t=1; t<sequence.length; t++) {
|
||||||
|
// calculate help
|
||||||
|
for(int i=0; i<help.length; i++) {
|
||||||
|
for(int j=0; j<this.numStates; j++) {
|
||||||
|
help[i][t]+=scaled[j][t-1]*a[j][i]*b[i][sequence[t]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double sum = 0;
|
||||||
|
for(int i=0; i<help.length; i++) {
|
||||||
|
sum+=help[i][t];
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i=0; i<scaled.length; i++) {
|
||||||
|
scaled[i][t] = help[i][t] / sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate scaling factor
|
||||||
|
sf[t] = 1 / sum;
|
||||||
|
|
||||||
|
} // t
|
||||||
|
|
||||||
|
return sf;
|
||||||
|
} // calculateScalingFactor
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Returns the scaled Forward variable.
|
||||||
|
* TODO: Maybe try out if the other precalculated method is faster.
|
||||||
|
* @param sequence
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private double[][] scaledForwardProc(int[] sequence) {
|
||||||
|
double[][] fwd = this.forwardProc(sequence);
|
||||||
|
double[][] out = new double[fwd.length][fwd[0].length];
|
||||||
|
for(int i=0; i<fwd.length; i++) {
|
||||||
|
for(int t=0; t<sequence.length; t++) {
|
||||||
|
double sum = 0;
|
||||||
|
for(int j=0; j<fwd.length; j++) {
|
||||||
|
sum+=fwd[j][t];
|
||||||
|
}
|
||||||
|
out[i][t] = fwd[i][t] / sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double[][] scaledBackwardProc(int[] sequence, double[] sf) {
|
||||||
|
double[][] bwd = this.backwardProc(sequence);
|
||||||
|
double[][] out = new double[bwd.length][bwd[0].length];
|
||||||
|
for(int i=0; i<bwd.length; i++) {
|
||||||
|
for(int t=0; t<sequence.length; t++) {
|
||||||
|
out[i][t]=1;
|
||||||
|
for(int r=t+1; r<sequence.length; r++) {
|
||||||
|
out[i][t]*=sf[r]*bwd[i][t];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the probability that a observation sequence O belongs
|
||||||
|
* to this Hidden Markov Model without using the bayes classifier.
|
||||||
|
* Internally the well known forward algorithm is used.
|
||||||
|
*
|
||||||
|
* @param o observation sequence
|
||||||
|
* @return probability that sequence o belongs to this hmm
|
||||||
|
*/
|
||||||
|
public double getProbability(int[] o) {
|
||||||
|
return scaledViterbi(o);
|
||||||
|
//return sProbability(o);
|
||||||
|
/*double prob = 0.0;
|
||||||
|
double[][] forward = this.forwardProc(o);
|
||||||
|
// add probabilities
|
||||||
|
for (int i = 0; i < forward.length; i++) { // for every state
|
||||||
|
prob += forward[i][forward[i].length - 1];
|
||||||
|
}
|
||||||
|
return prob;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public double sProbability(int[] o) {
|
||||||
|
double prod = 1.0;
|
||||||
|
double[][] fwd = this.scaledForwardProc(o);
|
||||||
|
for(int t=0; t<o.length; t++) {
|
||||||
|
double sum = 0.0;
|
||||||
|
for(int i=0; i<this.numStates; i++) {
|
||||||
|
sum+=fwd[i][t];
|
||||||
|
}
|
||||||
|
sum = 1/sum;
|
||||||
|
prod*=sum;
|
||||||
|
}
|
||||||
|
return 1/prod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double scaledViterbi(int[] o) {
|
||||||
|
double[][] phi = new double[this.numStates][o.length]; //phi[states][oseq]
|
||||||
|
// init
|
||||||
|
for(int i=0; i<this.numStates; i++) {
|
||||||
|
phi[i][0] = Math.log(pi[i]) + Math.log(b[i][o[0]]);
|
||||||
|
}
|
||||||
|
// induction
|
||||||
|
for(int t=1; t<o.length; t++) {
|
||||||
|
for(int j=0; j<this.numStates; j++) {
|
||||||
|
double max = Double.NEGATIVE_INFINITY;
|
||||||
|
for(int i=0; i<this.numStates; i++) {
|
||||||
|
double val = phi[i][t-1] + Math.log(this.a[i][j]);
|
||||||
|
if(val>max) {
|
||||||
|
max = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
phi[j][t] = max + Math.log(this.b[j][o[t]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// conclusion
|
||||||
|
double lp = Double.NEGATIVE_INFINITY;
|
||||||
|
for(int i=0; i<this.numStates; i++) {
|
||||||
|
if(phi[i][o.length-1]>lp) {
|
||||||
|
lp = phi[i][o.length-1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//System.out.println("log p = "+lp);
|
||||||
|
//return lp;
|
||||||
|
// we now have log10(p) calculated, transform to p.
|
||||||
|
System.out.println("prob = "+Math.exp(lp));
|
||||||
|
return Math.exp(lp);
|
||||||
|
//return Math.pow(10, lp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traditional Forward Algorithm.
|
||||||
|
*
|
||||||
|
* @param o the observationsequence O
|
||||||
|
* @return Array[State][Time]
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private double[][] forwardProc(int[] o) {
|
||||||
|
double[][] f = new double[numStates][o.length];
|
||||||
|
for (int l = 0; l < f.length; l++) {
|
||||||
|
f[l][0] = pi[l] * b[l][o[0]];
|
||||||
|
}
|
||||||
|
for (int i = 1; i < o.length; i++) {
|
||||||
|
for (int k = 0; k < f.length; k++) {
|
||||||
|
double sum = 0;
|
||||||
|
for (int l = 0; l < numStates; l++) {
|
||||||
|
sum += f[l][i-1] * a[l][k];
|
||||||
|
}
|
||||||
|
f[k][i] = sum * b[k][o[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backward algorithm.
|
||||||
|
*
|
||||||
|
* @param o observation sequence o
|
||||||
|
* @return Array[State][Time]
|
||||||
|
*/
|
||||||
|
private double[][] backwardProc(int[] o) {
|
||||||
|
int T = o.length;
|
||||||
|
double[][] bwd = new double[numStates][T];
|
||||||
|
/* Basisfall */
|
||||||
|
for (int i = 0; i < numStates; i++)
|
||||||
|
bwd[i][T - 1] = 1;
|
||||||
|
/* Induktion */
|
||||||
|
for (int t = T - 2; t >= 0; t--) {
|
||||||
|
for (int i = 0; i < numStates; i++) {
|
||||||
|
bwd[i][t] = 0;
|
||||||
|
for (int j = 0; j < numStates; j++)
|
||||||
|
bwd[i][t] += (bwd[j][t + 1] * a[i][j] * b[j][o[t + 1]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints everything about this model, including
|
||||||
|
* all values. For debug purposes or if you want
|
||||||
|
* to comprehend what happend to the model.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void print() {
|
||||||
|
DecimalFormat fmt = new DecimalFormat();
|
||||||
|
fmt.setMinimumFractionDigits(10);
|
||||||
|
fmt.setMaximumFractionDigits(10);
|
||||||
|
for (int i = 0; i < numStates; i++)
|
||||||
|
System.out.println("pi(" + i + ") = " + fmt.format(pi[i]));
|
||||||
|
System.out.println();
|
||||||
|
for (int i = 0; i < numStates; i++) {
|
||||||
|
for (int j = 0; j < numStates; j++)
|
||||||
|
System.out.print("a(" + i + "," + j + ") = "
|
||||||
|
+ fmt.format(a[i][j]) + " ");
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
for (int i = 0; i < numStates; i++) {
|
||||||
|
for (int k = 0; k < sigmaSize; k++)
|
||||||
|
System.out.print("b(" + i + "," + k + ") = "
|
||||||
|
+ fmt.format(b[i][k]) + " ");
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[][] getA() {
|
||||||
|
return this.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setA(double[][] a) {
|
||||||
|
this.a = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[][] getB() {
|
||||||
|
return this.b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setB(double[][] b) {
|
||||||
|
this.b=b;
|
||||||
|
}
|
||||||
|
}
|
||||||
85
src/logic/ProcessingUnit.java
Normal file
85
src/logic/ProcessingUnit.java
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package logic;
|
||||||
|
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import event.AccelerationEvent;
|
||||||
|
import event.ButtonPressedEvent;
|
||||||
|
import event.ButtonReleasedEvent;
|
||||||
|
import event.DeviceListener;
|
||||||
|
import event.GestureEvent;
|
||||||
|
import event.GestureListener;
|
||||||
|
import event.InfraredEvent;
|
||||||
|
import event.MotionStartEvent;
|
||||||
|
import event.MotionStopEvent;
|
||||||
|
import event.StateEvent;
|
||||||
|
|
||||||
|
public abstract class ProcessingUnit implements DeviceListener {
|
||||||
|
|
||||||
|
// Classifier
|
||||||
|
protected Classifier classifier;
|
||||||
|
|
||||||
|
// Listener
|
||||||
|
private Vector<GestureListener> listen = new Vector<GestureListener>();
|
||||||
|
|
||||||
|
protected int gesturecount;
|
||||||
|
|
||||||
|
public ProcessingUnit() {
|
||||||
|
this.classifier = new Classifier();
|
||||||
|
this.gesturecount=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an GestureListener to receive Gesture/StateEvents.
|
||||||
|
*
|
||||||
|
* @param g
|
||||||
|
* Class which implements GestureListener interface
|
||||||
|
*/
|
||||||
|
public void addGestureListener(GestureListener g) {
|
||||||
|
this.listen.add(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fireGestureEvent(int id, double probability) {
|
||||||
|
GestureEvent w = new GestureEvent(this, id, probability);
|
||||||
|
for(int i=0; i<this.listen.size(); i++) {
|
||||||
|
this.listen.get(i).gestureReceived(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fireStateEvent(int state) {
|
||||||
|
StateEvent w = new StateEvent(this, state);
|
||||||
|
for(int i=0; i<this.listen.size(); i++) {
|
||||||
|
this.listen.get(i).stateReceived(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void accelerationReceived(AccelerationEvent event);
|
||||||
|
|
||||||
|
public abstract void buttonPressReceived(ButtonPressedEvent event);
|
||||||
|
|
||||||
|
public abstract void buttonReleaseReceived(ButtonReleasedEvent event);
|
||||||
|
|
||||||
|
public abstract void infraredReceived(InfraredEvent event);
|
||||||
|
|
||||||
|
public abstract void motionStartReceived(MotionStartEvent event);
|
||||||
|
|
||||||
|
public abstract void motionStopReceived(MotionStopEvent event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the complete gesturemodel. After reset no gesture is known
|
||||||
|
* to the system.
|
||||||
|
*/
|
||||||
|
public void reset() {
|
||||||
|
if(this.gesturecount>0) {
|
||||||
|
this.classifier.clear();
|
||||||
|
System.out.println("### Model reset ###");
|
||||||
|
} else {
|
||||||
|
System.out.println("There doesn't exist any data to reset.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// File IO
|
||||||
|
public abstract void loadGesture(String name);
|
||||||
|
|
||||||
|
public abstract void saveGesture(int id, String name);
|
||||||
|
|
||||||
|
}
|
||||||
308
src/logic/Quantizer.java
Executable file
308
src/logic/Quantizer.java
Executable file
@@ -0,0 +1,308 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package logic;
|
||||||
|
|
||||||
|
import java.util.Vector;
|
||||||
|
import event.AccelerationEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class implements a quantization component. In this case a
|
||||||
|
* k-mean-algorithm is used. In this case the initial values of the algorithm
|
||||||
|
* are ordered as two intersected circles, representing an abstract globe with
|
||||||
|
* k=14 elements. As a special feature the radius of this globe would be
|
||||||
|
* calculated dynamically before the training of this component.
|
||||||
|
*
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
*/
|
||||||
|
public class Quantizer {
|
||||||
|
|
||||||
|
/** This is the initial radius of this model. */
|
||||||
|
private double radius;
|
||||||
|
|
||||||
|
/** Number of states from the following Hidden Markov Model */
|
||||||
|
private int numStates;
|
||||||
|
|
||||||
|
/** The representation of the so called Centeroids */
|
||||||
|
private double[][] map;
|
||||||
|
|
||||||
|
/** True, if map is already trained. */
|
||||||
|
private boolean maptrained;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a empty quantizer. The states variable is necessary since some
|
||||||
|
* algorithms need this value to calculate their values correctly.
|
||||||
|
*
|
||||||
|
* @param numStates
|
||||||
|
* number of hidden markov model states
|
||||||
|
*/
|
||||||
|
public Quantizer(int numStates) {
|
||||||
|
this.numStates = numStates;
|
||||||
|
this.map = new double[14][3];
|
||||||
|
this.maptrained = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trains this Quantizer with a specific gesture. This means that the
|
||||||
|
* positions of the centeroids would adapt to this training gesture. In our
|
||||||
|
* case this would happen with a summarized virtual gesture, containing all
|
||||||
|
* the other gestures.
|
||||||
|
*
|
||||||
|
* @param gesture
|
||||||
|
* the summarized virtual gesture
|
||||||
|
*/
|
||||||
|
public void trainCenteroids(Gesture gesture) {
|
||||||
|
Vector<AccelerationEvent> data = gesture.getData();
|
||||||
|
double pi = Math.PI;
|
||||||
|
this.radius = (gesture.getMaxAcceleration() + gesture
|
||||||
|
.getMinAcceleration()) / 2;
|
||||||
|
System.out.println("Using radius: " + this.radius);
|
||||||
|
|
||||||
|
// x , z , y
|
||||||
|
if (!this.maptrained) {
|
||||||
|
this.maptrained = true;
|
||||||
|
this.map[0] = new double[] { this.radius, 0.0, 0.0 };
|
||||||
|
this.map[1] = new double[] { Math.cos(pi / 4) * this.radius, 0.0,
|
||||||
|
Math.sin(pi / 4) * 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,
|
||||||
|
0.0, Math.sin(pi * 3 / 4) * this.radius };
|
||||||
|
this.map[4] = new double[] { -this.radius, 0.0, 0.0 };
|
||||||
|
this.map[5] = new double[] { Math.cos(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[7] = new double[] { Math.cos(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[9] = new double[] { 0.0, Math.cos(pi / 4) * this.radius,
|
||||||
|
Math.sin(pi / 4) * this.radius };
|
||||||
|
this.map[10] = new double[] { 0.0,
|
||||||
|
Math.cos(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[12] = new double[] { 0.0,
|
||||||
|
Math.cos(pi * 5 / 4) * this.radius,
|
||||||
|
Math.sin(pi * 5 / 4) * this.radius };
|
||||||
|
this.map[13] = new double[] { 0.0,
|
||||||
|
Math.cos(pi * 7 / 4) * this.radius,
|
||||||
|
Math.sin(pi * 7 / 4) * this.radius };
|
||||||
|
}
|
||||||
|
|
||||||
|
int[][] g_alt = new int[this.map.length][data.size()];
|
||||||
|
int[][] g = new int[this.map.length][data.size()];
|
||||||
|
|
||||||
|
do {
|
||||||
|
// Derive new Groups...
|
||||||
|
g_alt = this.copyarray(g);
|
||||||
|
g = this.deriveGroups(gesture);
|
||||||
|
|
||||||
|
// calculate new centeroids
|
||||||
|
for (int i = 0; i < this.map.length; i++) {
|
||||||
|
double zaehlerX = 0;
|
||||||
|
double zaehlerY = 0;
|
||||||
|
double zaehlerZ = 0;
|
||||||
|
int nenner = 0;
|
||||||
|
for (int j = 0; j < data.size(); j++) {
|
||||||
|
if (g[i][j] == 1) {
|
||||||
|
zaehlerX += data.elementAt(j).getX();
|
||||||
|
zaehlerY += data.elementAt(j).getY();
|
||||||
|
zaehlerZ += data.elementAt(j).getZ();
|
||||||
|
nenner++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nenner > 1) { // nur wenn der nenner>0 oder >1??? ist muss
|
||||||
|
// was
|
||||||
|
// geaendert werden
|
||||||
|
// System.out.println("Setze neuen Centeroid!");
|
||||||
|
this.map[i] = new double[] {(zaehlerX / (double) nenner),
|
||||||
|
(zaehlerY / (double) nenner),
|
||||||
|
(zaehlerZ / (double) nenner) };
|
||||||
|
// System.out.println("Centeroid: "+i+": "+newcenteroid[0]+":"+newcenteroid[1]);
|
||||||
|
}
|
||||||
|
} // new centeroids
|
||||||
|
|
||||||
|
} while (!equalarrays(g_alt, g));
|
||||||
|
|
||||||
|
// Debug: Printout groups
|
||||||
|
/*
|
||||||
|
* for (int i = 0; i < n; i++) { for (int j = 0; j < this.data.size();
|
||||||
|
* j++) { System.out.print(g[i][j] + "|"); } System.out.println(""); }
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This methods looks up a Gesture to a group matrix, used by the
|
||||||
|
* k-mean-algorithm (traincenteroid method) above.
|
||||||
|
*
|
||||||
|
* @param gesture
|
||||||
|
* the gesture
|
||||||
|
*/
|
||||||
|
public int[][] deriveGroups(Gesture gesture) {
|
||||||
|
Vector<AccelerationEvent> data = gesture.getData();
|
||||||
|
int[][] groups = new int[this.map.length][data.size()];
|
||||||
|
|
||||||
|
// Calculate cartesian distance
|
||||||
|
double[][] d = new double[this.map.length][data.size()];
|
||||||
|
double[] curr = new double[3];
|
||||||
|
double[] vector = new double[3];
|
||||||
|
for (int i = 0; i < this.map.length; i++) { // zeilen
|
||||||
|
double[] ref = this.map[i];
|
||||||
|
for (int j = 0; j < data.size(); j++) { // spalten
|
||||||
|
|
||||||
|
curr[0] = data.elementAt(j).getX();
|
||||||
|
curr[1] = data.elementAt(j).getY();
|
||||||
|
curr[2] = data.elementAt(j).getZ();
|
||||||
|
|
||||||
|
vector[0] = ref[0] - curr[0];
|
||||||
|
vector[1] = ref[1] - curr[1];
|
||||||
|
vector[2] = ref[2] - curr[2];
|
||||||
|
d[i][j] = Math.sqrt((vector[0] * vector[0])
|
||||||
|
+ (vector[1] * vector[1]) + (vector[2] * vector[2]));
|
||||||
|
// System.out.print(d[i][j] + "|");
|
||||||
|
}
|
||||||
|
// System.out.println("");
|
||||||
|
}
|
||||||
|
|
||||||
|
// look, to which group a value belongs
|
||||||
|
for (int j = 0; j < data.size(); j++) {
|
||||||
|
double smallest = Double.MAX_VALUE;
|
||||||
|
int row = 0;
|
||||||
|
for (int i = 0; i < this.map.length; i++) {
|
||||||
|
if (d[i][j] < smallest) {
|
||||||
|
smallest = d[i][j];
|
||||||
|
row = i;
|
||||||
|
}
|
||||||
|
groups[i][j] = 0;
|
||||||
|
}
|
||||||
|
groups[row][j] = 1; // guppe gesetzt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug output
|
||||||
|
/*
|
||||||
|
* for (int i = 0; i < groups.length; i++) { // zeilen for (int j = 0; j
|
||||||
|
* < groups[i].length; j++) { System.out.print(groups[i][j] + "|"); }
|
||||||
|
* System.out.println(""); }
|
||||||
|
*/
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* With this method you can transform a gesture to a discrete symbol
|
||||||
|
* sequence with values between 0 and granularity (number of observations).
|
||||||
|
*
|
||||||
|
* @param gesture
|
||||||
|
* Gesture to get the observationsequence to.
|
||||||
|
*/
|
||||||
|
public int[] getObservationSequence(Gesture gesture) {
|
||||||
|
int[][] groups = this.deriveGroups(gesture);
|
||||||
|
Vector<Integer> sequence = new Vector<Integer>();
|
||||||
|
|
||||||
|
// System.out.print("Visible symbol sequence: ");
|
||||||
|
|
||||||
|
for (int j = 0; j < groups[0].length; j++) { // spalten
|
||||||
|
for (int i = 0; i < groups.length; i++) { // zeilen
|
||||||
|
if (groups[i][j] == 1) {
|
||||||
|
// System.out.print(" "+ i);
|
||||||
|
sequence.add(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// die sequenz darf nicht zu kurz sein... mindestens so lang
|
||||||
|
// 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
|
||||||
|
// too short sequences would cause an error. i've to think about a
|
||||||
|
// better resolution than copying the old value a few time.
|
||||||
|
while (sequence.size() < this.numStates) {
|
||||||
|
sequence.add(sequence.elementAt(sequence.size() - 1));
|
||||||
|
// System.out.print(" "+sequence.elementAt(sequence.size()-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// System.out.println("");
|
||||||
|
|
||||||
|
int[] out = new int[sequence.size()];
|
||||||
|
for (int i = 0; i < sequence.size(); i++) {
|
||||||
|
out[i] = sequence.elementAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints out the current centeroids-map. Its for debug or technical
|
||||||
|
* interests.
|
||||||
|
*/
|
||||||
|
public void printMap() {
|
||||||
|
System.out.println("Centeroids:");
|
||||||
|
for (int i = 0; i < this.map.length; i++) {
|
||||||
|
System.out.println(i + ". :" + this.map[i][0] + ":"
|
||||||
|
+ this.map[i][1] + ":" + this.map[i][2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to deepcopy an array.
|
||||||
|
*/
|
||||||
|
private int[][] copyarray(int[][] alt) {
|
||||||
|
int[][] neu = new int[alt.length][alt[0].length];
|
||||||
|
for (int i = 0; i < alt.length; i++) {
|
||||||
|
for (int j = 0; j < alt[i].length; j++) {
|
||||||
|
neu[i][j] = alt[i][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return neu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to look if the two arrays containing the same values.
|
||||||
|
*/
|
||||||
|
private boolean equalarrays(int[][] one, int[][] two) {
|
||||||
|
for (int i = 0; i < one.length; i++) {
|
||||||
|
for (int j = 0; j < one[i].length; j++) {
|
||||||
|
if (!(one[i][j] == two[i][j])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getRadius() {
|
||||||
|
return this.radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[][] getHashMap() {
|
||||||
|
return this.map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUpManually(double[][] map, double radius) {
|
||||||
|
this.map = map;
|
||||||
|
this.radius = radius;
|
||||||
|
}
|
||||||
|
}
|
||||||
336
src/logic/ScaledHMM.java
Normal file
336
src/logic/ScaledHMM.java
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package logic;
|
||||||
|
import java.text.*;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a Hidden Markov Model implementation which internally provides
|
||||||
|
* the basic algorithms for training and recognition (forward and backward
|
||||||
|
* algorithm). Since a regular Hidden Markov Model doesn't provide a possibility
|
||||||
|
* to train multiple sequences, this implementation has been optimized for this
|
||||||
|
* purposes using some state-of-the-art technologies described in several papers.
|
||||||
|
*
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ScaledHMM {
|
||||||
|
/** The number of states */
|
||||||
|
private int numStates;
|
||||||
|
|
||||||
|
/** The number of observations */
|
||||||
|
private int sigmaSize;
|
||||||
|
|
||||||
|
/** The initial probabilities for each state: p[state] */
|
||||||
|
public double pi[];
|
||||||
|
|
||||||
|
/** The state change probability to switch from state A to
|
||||||
|
* state B: a[stateA][stateB] */
|
||||||
|
public double a[][];
|
||||||
|
|
||||||
|
/** The probability to emit symbol S in state A: b[stateA][symbolS] */
|
||||||
|
public double b[][];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the Hidden Markov Model in a left-to-right version.
|
||||||
|
*
|
||||||
|
* @param numStates Number of states
|
||||||
|
* @param sigmaSize Number of observations
|
||||||
|
*/
|
||||||
|
public ScaledHMM(int numStates, int sigmaSize) {
|
||||||
|
this.numStates = numStates;
|
||||||
|
this.sigmaSize = sigmaSize;
|
||||||
|
pi = new double[numStates];
|
||||||
|
a = new double[numStates][numStates];
|
||||||
|
b = new double[numStates][sigmaSize];
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the Hidden Markov Model to the initial left-to-right values.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private void reset() {
|
||||||
|
int jumplimit = 2;
|
||||||
|
|
||||||
|
// set startup probability
|
||||||
|
pi[0] = 1;
|
||||||
|
for(int i=1; i<numStates; i++) {
|
||||||
|
pi[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
for(int i=0; i<numStates; i++) {
|
||||||
|
for(int j=0; j<numStates; j++) {
|
||||||
|
a[i][j]=1/numStates;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// set state change probabilities in the left-to-right version
|
||||||
|
// NOTE: i now that this is dirty and very static. :)
|
||||||
|
for(int i=0; i<numStates; i++) {
|
||||||
|
for(int j=0; j<numStates; j++) {
|
||||||
|
if(i==numStates-1 && j==numStates-1) { // last row
|
||||||
|
a[i][j] = 1.0;
|
||||||
|
} else if(i==numStates-2 && j==numStates-2) { // next to last row
|
||||||
|
a[i][j] = 0.5;
|
||||||
|
} else if(i==numStates-2 && j==numStates-1) { // next to last row
|
||||||
|
a[i][j] = 0.5;
|
||||||
|
} else if(i<=j && i>j-jumplimit-1) {
|
||||||
|
a[i][j] = 1.0/(jumplimit+1);
|
||||||
|
} else {
|
||||||
|
a[i][j] = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// emission probability
|
||||||
|
for(int i=0; i<numStates; i++) {
|
||||||
|
for(int j=0; j<sigmaSize; j++) {
|
||||||
|
b[i][j] = 1.0/(double)sigmaSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trains the Hidden Markov Model with multiple sequences.
|
||||||
|
* This method is normally not known to basic hidden markov
|
||||||
|
* models, because they usually use the Baum-Welch-Algorithm.
|
||||||
|
* This method is NOT the traditional Baum-Welch-Algorithm.
|
||||||
|
*
|
||||||
|
* If you want to know in detail how it works please consider
|
||||||
|
* my Individuelles Projekt paper on the wiigee Homepage. Also
|
||||||
|
* there exist some english literature on the world wide web.
|
||||||
|
* Try to search for some papers by Rabiner or have a look at
|
||||||
|
* Vesa-Matti Mäntylä - "Discrete Hidden Markov Models with
|
||||||
|
* application to isolated user-dependent hand gesture recognition".
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void train(Vector<int[]> trainsequence) {
|
||||||
|
|
||||||
|
double[][] a_new = new double[a.length][a.length];
|
||||||
|
double[][] b_new = new double[b.length][b[0].length];
|
||||||
|
// re calculate state change probability a
|
||||||
|
for(int i=0; i<a.length; i++) {
|
||||||
|
for(int j=0; j<a[i].length; j++) {
|
||||||
|
double zaehler=0;
|
||||||
|
double nenner=0;
|
||||||
|
|
||||||
|
for(int k=0; k<trainsequence.size(); k++) {
|
||||||
|
this.reset();
|
||||||
|
int[] sequence = trainsequence.elementAt(k);
|
||||||
|
|
||||||
|
double[][] fwd = this.forwardProc(sequence);
|
||||||
|
double[][] bwd = this.backwardProc(sequence);
|
||||||
|
double prob = this.getProbability(sequence);
|
||||||
|
|
||||||
|
double zaehler_innersum=0;
|
||||||
|
double nenner_innersum=0;
|
||||||
|
|
||||||
|
for(int t=0; t<sequence.length-1; t++) {
|
||||||
|
double scaled_fwd = this.getScaledValue(fwd, fwd, i, t);
|
||||||
|
double scaled_bwd = this.getScaledValue(bwd, fwd, i, t);
|
||||||
|
double scaled_bwd_tp1 = this.getScaledValue(bwd, fwd, i, t+1);
|
||||||
|
double scaling_factor_tp1 = this.getScalingFactor(fwd, t+1);
|
||||||
|
zaehler_innersum+=scaled_fwd*a[i][j]*b[j][sequence[t+1]]*scaled_bwd_tp1*scaling_factor_tp1;
|
||||||
|
nenner_innersum+=scaled_fwd*scaled_bwd;
|
||||||
|
System.out.println("scaled_fwd = "+scaled_fwd);
|
||||||
|
System.out.println("scaled_bwd = "+scaled_bwd);
|
||||||
|
System.out.println("scaling factor = "+scaling_factor_tp1);
|
||||||
|
}
|
||||||
|
zaehler+=zaehler_innersum;
|
||||||
|
nenner+=nenner_innersum;
|
||||||
|
} // k
|
||||||
|
|
||||||
|
a_new[i][j] = zaehler/nenner;
|
||||||
|
} // j
|
||||||
|
} // i
|
||||||
|
|
||||||
|
// re calculate emission probability b
|
||||||
|
for(int i=0; i<b.length; i++) { // zustaende
|
||||||
|
for(int j=0; j<b[i].length; j++) { // symbole
|
||||||
|
double zaehler=0;
|
||||||
|
double nenner=0;
|
||||||
|
|
||||||
|
for(int k=0; k<trainsequence.size(); k++) {
|
||||||
|
this.reset();
|
||||||
|
int[] sequence = trainsequence.elementAt(k);
|
||||||
|
|
||||||
|
double[][] fwd = this.forwardProc(sequence);
|
||||||
|
double[][] bwd = this.backwardProc(sequence);
|
||||||
|
double prob = this.getProbability(sequence);
|
||||||
|
|
||||||
|
double zaehler_innersum=0;
|
||||||
|
double nenner_innersum=0;
|
||||||
|
|
||||||
|
|
||||||
|
for(int t=0; t<sequence.length-1; t++) {
|
||||||
|
if(sequence[t]==j) {
|
||||||
|
zaehler_innersum+=fwd[i][t]*bwd[i][t];
|
||||||
|
}
|
||||||
|
nenner_innersum+=fwd[i][t]*bwd[i][t];
|
||||||
|
}
|
||||||
|
zaehler+=(1/prob)*zaehler_innersum;
|
||||||
|
nenner+=(1/prob)*nenner_innersum;
|
||||||
|
} // k
|
||||||
|
|
||||||
|
b_new[i][j] = zaehler/nenner;
|
||||||
|
} // j
|
||||||
|
} // i
|
||||||
|
|
||||||
|
this.a=a_new;
|
||||||
|
this.b=b_new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traditional Forward Algorithm.
|
||||||
|
*
|
||||||
|
* @param o the observationsequence O
|
||||||
|
* @return Array[State][Time]
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private double[][] forwardProc(int[] o) {
|
||||||
|
double[][] f = new double[numStates][o.length];
|
||||||
|
for (int l = 0; l < f.length; l++) {
|
||||||
|
f[l][0] = pi[l] * b[l][o[0]];
|
||||||
|
}
|
||||||
|
for (int i = 1; i < o.length; i++) {
|
||||||
|
for (int k = 0; k < f.length; k++) {
|
||||||
|
double sum = 0;
|
||||||
|
for (int l = 0; l < numStates; l++) {
|
||||||
|
sum += f[l][i-1] * a[l][k];
|
||||||
|
}
|
||||||
|
f[k][i] = sum * b[k][o[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the probability that a observation sequence O belongs
|
||||||
|
* to this Hidden Markov Model without using the bayes classifier.
|
||||||
|
* Internally the well known forward algorithm is used.
|
||||||
|
*
|
||||||
|
* @param o observation sequence
|
||||||
|
* @return probability that sequence o belongs to this hmm
|
||||||
|
*/
|
||||||
|
public double getProbability(int[] o) {
|
||||||
|
double prob = 0.0;
|
||||||
|
double[][] forward = this.forwardProc(o);
|
||||||
|
// add probabilities
|
||||||
|
for (int i = 0; i < forward.length; i++) { // for every state
|
||||||
|
prob += forward[i][forward[i].length - 1];
|
||||||
|
}
|
||||||
|
return prob;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backward algorithm.
|
||||||
|
*
|
||||||
|
* @param o observation sequence o
|
||||||
|
* @return Array[State][Time]
|
||||||
|
*/
|
||||||
|
public double[][] backwardProc(int[] o) {
|
||||||
|
int T = o.length;
|
||||||
|
double[][] bwd = new double[numStates][T];
|
||||||
|
/* Basisfall */
|
||||||
|
for (int i = 0; i < numStates; i++)
|
||||||
|
bwd[i][T - 1] = 1;
|
||||||
|
/* Induktion */
|
||||||
|
for (int t = T - 2; t >= 0; t--) {
|
||||||
|
for (int i = 0; i < numStates; i++) {
|
||||||
|
bwd[i][t] = 0;
|
||||||
|
for (int j = 0; j < numStates; j++)
|
||||||
|
bwd[i][t] += (bwd[j][t + 1] * a[i][j] * b[j][o[t + 1]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// scales a fwd/bwd-procedure at i/t.
|
||||||
|
private double getScaledValue(double[][] pcd, double[][] divisor, int i, int t) {
|
||||||
|
double sum = 0;
|
||||||
|
for(int v=0; v<this.numStates; v++) {
|
||||||
|
sum += divisor[v][t];
|
||||||
|
}
|
||||||
|
return pcd[i][t]/sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getScalingFactor(double[][] divisor, int t) {
|
||||||
|
double ret = 0;
|
||||||
|
for(int i=0; i<this.numStates; i++) {
|
||||||
|
ret += divisor[i][t];
|
||||||
|
}
|
||||||
|
return 1/ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints everything about this model, including
|
||||||
|
* all values. For debug purposes or if you want
|
||||||
|
* to comprehend what happend to the model.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void print() {
|
||||||
|
DecimalFormat fmt = new DecimalFormat();
|
||||||
|
fmt.setMinimumFractionDigits(5);
|
||||||
|
fmt.setMaximumFractionDigits(5);
|
||||||
|
for (int i = 0; i < numStates; i++)
|
||||||
|
System.out.println("pi(" + i + ") = " + fmt.format(pi[i]));
|
||||||
|
System.out.println();
|
||||||
|
for (int i = 0; i < numStates; i++) {
|
||||||
|
for (int j = 0; j < numStates; j++)
|
||||||
|
System.out.print("a(" + i + "," + j + ") = "
|
||||||
|
+ fmt.format(a[i][j]) + " ");
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
for (int i = 0; i < numStates; i++) {
|
||||||
|
for (int k = 0; k < sigmaSize; k++)
|
||||||
|
System.out.print("b(" + i + "," + k + ") = "
|
||||||
|
+ fmt.format(b[i][k]) + " ");
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[][] getA() {
|
||||||
|
return this.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setA(double[][] a) {
|
||||||
|
this.a = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[][] getB() {
|
||||||
|
return this.b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setB(double[][] b) {
|
||||||
|
this.b=b;
|
||||||
|
}
|
||||||
|
}
|
||||||
196
src/logic/TriggeredProcessingUnit.java
Normal file
196
src/logic/TriggeredProcessingUnit.java
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package logic;
|
||||||
|
|
||||||
|
import java.util.Vector;
|
||||||
|
import event.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class analyzes the WiimoteAccelerationEvents emitted from a Wiimote
|
||||||
|
* and further creates and manages the different models for each type
|
||||||
|
* of gesture.
|
||||||
|
*
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
*/
|
||||||
|
public class TriggeredProcessingUnit extends ProcessingUnit {
|
||||||
|
|
||||||
|
// gesturespecific values
|
||||||
|
private Gesture current; // current gesture
|
||||||
|
|
||||||
|
private Vector<Gesture> trainsequence;
|
||||||
|
|
||||||
|
// State variables
|
||||||
|
private boolean learning, analyzing;
|
||||||
|
|
||||||
|
public TriggeredProcessingUnit() {
|
||||||
|
super();
|
||||||
|
this.learning=false;
|
||||||
|
this.analyzing=false;
|
||||||
|
this.current=new Gesture();
|
||||||
|
this.trainsequence=new Vector<Gesture>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since this class implements the WiimoteListener this procedure is
|
||||||
|
* necessary. It contains the filtering (directional equivalence filter)
|
||||||
|
* and adds the incoming data to the current motion, we want to train
|
||||||
|
* or recognize.
|
||||||
|
*
|
||||||
|
* @param event The acceleration event which has to be processed by the
|
||||||
|
* directional equivalence filter and which has to be added to the current
|
||||||
|
* motion in recognition or training process.
|
||||||
|
*/
|
||||||
|
public void accelerationReceived(AccelerationEvent event) {
|
||||||
|
if(this.learning || this.analyzing) {
|
||||||
|
this.current.add(event); // add event to gesture
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is from the WiimoteListener interface. A button press
|
||||||
|
* is used to control the data flow inside the structures.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void buttonPressReceived(ButtonPressedEvent event) {
|
||||||
|
this.handleStartEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void buttonReleaseReceived(ButtonReleasedEvent event) {
|
||||||
|
this.handleStopEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void infraredReceived(InfraredEvent event) {
|
||||||
|
// NOTHING TO DO HERE
|
||||||
|
}
|
||||||
|
|
||||||
|
public void motionStartReceived(MotionStartEvent event) {
|
||||||
|
this.handleStartEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void motionStopReceived(MotionStopEvent event) {
|
||||||
|
this.handleStopEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleStartEvent(ActionStartEvent event) {
|
||||||
|
|
||||||
|
// TrainButton = record a gesture for learning
|
||||||
|
if((!this.analyzing && !this.learning) &&
|
||||||
|
event.isTrainInitEvent()) {
|
||||||
|
System.out.println("Training started!");
|
||||||
|
this.learning=true;
|
||||||
|
this.fireStateEvent(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecognitionButton = record a gesture for recognition
|
||||||
|
if((!this.analyzing && !this.learning) &&
|
||||||
|
event.isRecognitionInitEvent()) {
|
||||||
|
System.out.println("Recognition started!");
|
||||||
|
this.analyzing=true;
|
||||||
|
this.fireStateEvent(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseGestureButton = starts the training of the model with multiple
|
||||||
|
// recognized gestures, contained in trainsequence
|
||||||
|
if((!this.analyzing && !this.learning) &&
|
||||||
|
event.isCloseGestureInitEvent()) {
|
||||||
|
|
||||||
|
if(this.trainsequence.size()>0) {
|
||||||
|
System.out.println("Training the model with "+this.trainsequence.size()+" gestures...");
|
||||||
|
this.fireStateEvent(1);
|
||||||
|
this.learning=true;
|
||||||
|
|
||||||
|
GestureModel m = new GestureModel(this.gesturecount++);
|
||||||
|
m.train(this.trainsequence);
|
||||||
|
m.print();
|
||||||
|
this.classifier.addGestureModel(m);
|
||||||
|
|
||||||
|
this.trainsequence=new Vector<Gesture>();
|
||||||
|
this.learning=false;
|
||||||
|
} else {
|
||||||
|
System.out.println("There is nothing to do. Please record some gestures first.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleStopEvent(ActionStopEvent event) {
|
||||||
|
if(this.learning) { // button release and state=learning, stops learning
|
||||||
|
if(this.current.getCountOfData()>0) {
|
||||||
|
System.out.println("Finished recording (training)...");
|
||||||
|
System.out.println("Data: "+this.current.getCountOfData());
|
||||||
|
Gesture gesture = new Gesture(this.current);
|
||||||
|
this.trainsequence.add(gesture);
|
||||||
|
this.current=new Gesture();
|
||||||
|
this.learning=false;
|
||||||
|
} else {
|
||||||
|
System.out.println("There is no data.");
|
||||||
|
System.out.println("Please train the gesture again.");
|
||||||
|
this.learning=false; // ?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(this.analyzing) { // button release and state=analyzing, stops analyzing
|
||||||
|
if(this.current.getCountOfData()>0) {
|
||||||
|
System.out.println("Finished recording (recognition)...");
|
||||||
|
System.out.println("Compare gesture with "+this.gesturecount+" other gestures.");
|
||||||
|
Gesture gesture = new Gesture(this.current);
|
||||||
|
|
||||||
|
int recognized = this.classifier.classifyGesture(gesture);
|
||||||
|
if(recognized!=-1) {
|
||||||
|
double recogprob = this.classifier.getLastProbability();
|
||||||
|
this.fireGestureEvent(recognized, recogprob);
|
||||||
|
System.out.println("######");
|
||||||
|
System.out.println("Gesture No. "+recognized+" recognized: "+recogprob);
|
||||||
|
System.out.println("######");
|
||||||
|
} else {
|
||||||
|
this.fireStateEvent(0);
|
||||||
|
System.out.println("######");
|
||||||
|
System.out.println("No gesture recognized.");
|
||||||
|
System.out.println("######");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.current=new Gesture();
|
||||||
|
this.analyzing=false;
|
||||||
|
} else {
|
||||||
|
System.out.println("There is no data.");
|
||||||
|
System.out.println("Please recognize the gesture again.");
|
||||||
|
this.analyzing=false; // ?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadGesture(String name) {
|
||||||
|
this.gesturecount++;
|
||||||
|
GestureModel g = util.FileIO.readFromFile(name);
|
||||||
|
this.classifier.addGestureModel(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveGesture(int id, String name) {
|
||||||
|
util.FileIO.writeToFile(this.classifier.getGestureModel(id), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
244
src/util/FileIO.java
Normal file
244
src/util/FileIO.java
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
/*
|
||||||
|
* wiigee - accelerometerbased gesture recognition
|
||||||
|
* Copyright (C) 2007, 2008 Benjamin Poppinga
|
||||||
|
*
|
||||||
|
* Developed at University of Oldenburg
|
||||||
|
* Contact: benjamin.poppinga@informatik.uni-oldenburg.de
|
||||||
|
*
|
||||||
|
* This file is part of wiigee.
|
||||||
|
*
|
||||||
|
* wiigee is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package util;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import logic.GestureModel;
|
||||||
|
import logic.HMM;
|
||||||
|
import logic.Quantizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a static class to support saving and loading complete gestures. I've
|
||||||
|
* choosen not to use some kind of XML, because the big multidimensional arrays
|
||||||
|
* would cause a huge senseless amount of xml-data. So KISS: Keep It Simple,
|
||||||
|
* Stupid! ;) Comma-separated-vectors with some control data.
|
||||||
|
*
|
||||||
|
* @author Benjamin 'BePo' Poppinga
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class FileIO {
|
||||||
|
|
||||||
|
public static void writeToFile(GestureModel m, String name) {
|
||||||
|
try {
|
||||||
|
// initialize file and get values
|
||||||
|
BufferedWriter out = new BufferedWriter(new FileWriter(name+".txt"));
|
||||||
|
int id = m.getId();
|
||||||
|
int numStates = m.getNumStates();
|
||||||
|
int numObservations = m.getNumObservations();
|
||||||
|
double defaultProbability = m.getDefaultProbability();
|
||||||
|
Quantizer quantizer = m.getQuantizer();
|
||||||
|
HMM hmm = m.getHMM();
|
||||||
|
|
||||||
|
// write to file
|
||||||
|
out.write("# ID:");
|
||||||
|
out.newLine();
|
||||||
|
out.write(Integer.toString(id));
|
||||||
|
out.newLine();
|
||||||
|
|
||||||
|
out.write("# numStates:");
|
||||||
|
out.newLine();
|
||||||
|
out.write(Integer.toString(numStates));
|
||||||
|
out.newLine();
|
||||||
|
|
||||||
|
out.write("# numObservations:");
|
||||||
|
out.newLine();
|
||||||
|
out.write(Integer.toString(numObservations));
|
||||||
|
out.newLine();
|
||||||
|
|
||||||
|
out.write("# defaultProbability:");
|
||||||
|
out.newLine();
|
||||||
|
out.write(Double.toString(defaultProbability));
|
||||||
|
out.newLine();
|
||||||
|
|
||||||
|
out.write("# Quantizer: Radius");
|
||||||
|
out.newLine();
|
||||||
|
out.write(Double.toString(quantizer.getRadius()));
|
||||||
|
out.newLine();
|
||||||
|
out.write("# Quantizer: MAP");
|
||||||
|
out.newLine();
|
||||||
|
double[][] map = quantizer.getHashMap();
|
||||||
|
for(int v=0; v<map.length; v++) {
|
||||||
|
double[] d = map[v];
|
||||||
|
out.write(Double.toString(d[0])+", "+Double.toString(d[1])+", "+Double.toString(d[2]));
|
||||||
|
out.newLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
out.write("# HMM: PI");
|
||||||
|
out.newLine();
|
||||||
|
double[] pi = hmm.getPi();
|
||||||
|
for (int i=0; i<numStates; i++) {
|
||||||
|
if(i==numStates-1) {
|
||||||
|
out.write(Double.toString(pi[i]));
|
||||||
|
out.newLine();
|
||||||
|
} else {
|
||||||
|
out.write(Double.toString(pi[i])+", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.write("# HMM: A");
|
||||||
|
out.newLine();
|
||||||
|
double[][] a = hmm.getA();
|
||||||
|
for(int i=0; i<numStates; i++) {
|
||||||
|
for(int j=0; j<numStates; j++) {
|
||||||
|
if(j==numStates-1) {
|
||||||
|
out.write(Double.toString(a[i][j]));
|
||||||
|
out.newLine();
|
||||||
|
} else {
|
||||||
|
out.write(Double.toString(a[i][j])+", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.write("# HMM: B");
|
||||||
|
out.newLine();
|
||||||
|
double[][] b = hmm.getB();
|
||||||
|
for(int i=0; i<numStates; i++) {
|
||||||
|
for(int j=0; j<numObservations; j++) {
|
||||||
|
if(j==numObservations-1) {
|
||||||
|
out.write(Double.toString(b[i][j]));
|
||||||
|
out.newLine();
|
||||||
|
} else {
|
||||||
|
out.write(Double.toString(b[i][j])+", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.write("# END");
|
||||||
|
|
||||||
|
// close file
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("Error: Write to File!");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GestureModel readFromFile(String name) {
|
||||||
|
try {
|
||||||
|
// initialize file and create values
|
||||||
|
BufferedReader in = new BufferedReader(new FileReader(name+".txt"));
|
||||||
|
int id = 0;
|
||||||
|
int numStates = 0;
|
||||||
|
int numObservations = 0;
|
||||||
|
double defaultprobability = 0;
|
||||||
|
double radius = 0;
|
||||||
|
double[][] map = new double[numObservations][3];
|
||||||
|
double[] pi = new double[numStates];
|
||||||
|
double[][] a = new double[numStates][numStates];
|
||||||
|
double[][] b = new double[numStates][numObservations];
|
||||||
|
|
||||||
|
String line;
|
||||||
|
int position = 0;
|
||||||
|
while(in.ready()) {
|
||||||
|
line = in.readLine();
|
||||||
|
if(!line.startsWith("#")) { // isn't a comment
|
||||||
|
switch (position++) {
|
||||||
|
case 0:
|
||||||
|
id = Integer.parseInt(line);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
numStates = Integer.parseInt(line);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
numObservations = Integer.parseInt(line);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
defaultprobability = Double.parseDouble(line);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
radius = Double.parseDouble(line);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
map = new double[numObservations][3];
|
||||||
|
for(int i=0; i<numObservations; i++) {
|
||||||
|
String s[] = line.split(", ");
|
||||||
|
double[] d = new double[] { Double.parseDouble(s[0]),
|
||||||
|
Double.parseDouble(s[1]),
|
||||||
|
Double.parseDouble(s[2]) };
|
||||||
|
map[i] = d;
|
||||||
|
line = in.ready() ? in.readLine() : "";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
pi = new double[numStates];
|
||||||
|
String pi_row[] = line.split(", ");
|
||||||
|
for(int i=0; i<numStates; i++) {
|
||||||
|
pi[i] = Double.parseDouble(pi_row[i]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
a = new double[numStates][numStates];
|
||||||
|
for(int i=0; i<numStates; i++) {
|
||||||
|
String a_row[] = line.split(", ");
|
||||||
|
for(int j=0; j<numStates; j++) {
|
||||||
|
a[i][j] = Double.parseDouble(a_row[j]);
|
||||||
|
}
|
||||||
|
line = in.ready() ? in.readLine() : "";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
b = new double[numStates][numObservations];
|
||||||
|
for(int i=0; i<numStates; i++) {
|
||||||
|
String b_row[] = line.split(", ");
|
||||||
|
for(int j=0; j<numObservations; j++) {
|
||||||
|
b[i][j] = Double.parseDouble(b_row[j]);
|
||||||
|
}
|
||||||
|
line = in.ready() ? in.readLine() : "";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
System.out.println("SWITCH EMPTY!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GestureModel ret = new GestureModel(id);
|
||||||
|
ret.setDefaultProbability(defaultprobability);
|
||||||
|
|
||||||
|
Quantizer quantizer = new Quantizer(numStates);
|
||||||
|
quantizer.setUpManually(map, radius);
|
||||||
|
ret.setQuantizer(quantizer);
|
||||||
|
|
||||||
|
HMM hmm = new HMM(numStates, numObservations);
|
||||||
|
hmm.setPi(pi);
|
||||||
|
hmm.setA(a);
|
||||||
|
hmm.setB(b);
|
||||||
|
ret.setHMM(hmm);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println("Error: Read from File!");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null; // bad error case
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
43
src/util/Log.java
Normal file
43
src/util/Log.java
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package util;
|
||||||
|
|
||||||
|
public class Log {
|
||||||
|
|
||||||
|
public static final int OFF = -1;
|
||||||
|
public static final int NORMAL = 0;
|
||||||
|
public static final int DEBUG = 1;
|
||||||
|
|
||||||
|
public static final int PRINT = 0;
|
||||||
|
public static final int FILE = 1;
|
||||||
|
|
||||||
|
public static int level = Log.NORMAL;
|
||||||
|
public static int mode = Log.PRINT;
|
||||||
|
|
||||||
|
public static void setLevel(int n) {
|
||||||
|
level = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void write(String s) {
|
||||||
|
write(Log.NORMAL, s, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void write(String s, Object o) {
|
||||||
|
write(Log.NORMAL, s, o);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void write(int n, String s, Object o) {
|
||||||
|
if(level>=n) {
|
||||||
|
if(mode==Log.PRINT) {
|
||||||
|
// console output enabled
|
||||||
|
if(o!=null) {
|
||||||
|
System.out.println(o.getClass()+": "+s);
|
||||||
|
} else {
|
||||||
|
System.out.println(s);
|
||||||
|
}
|
||||||
|
} else if(mode==Log.FILE) {
|
||||||
|
// file output enabled
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user