View Javadoc

1   /*
2    * @(#)$Id: GestureClassHelper.java 820 2010-05-26 21:19:20Z bsigner $
3    *
4    * Author		:	Ueli Kurmann, igesture@uelikurmann.ch
5    *                  
6    *
7    * Purpose		: 
8    *
9    * -----------------------------------------------------------------------
10   *
11   * Revision Information:
12   *
13   * Date				Who			Reason
14   *
15   * 11.06.2008			ukurmann	Initial Release
16   *
17   * -----------------------------------------------------------------------
18   *
19   * Copyright 1999-2009 ETH Zurich. All Rights Reserved.
20   *
21   * This software is the proprietary information of ETH Zurich.
22   * Use is subject to license terms.
23   * 
24   */
25  
26  
27  package org.ximtec.igesture.algorithm.rubine;
28  
29  import java.util.ArrayList;
30  import java.util.HashMap;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.concurrent.CountDownLatch;
34  import java.util.logging.Level;
35  import java.util.logging.Logger;
36  
37  import org.apache.commons.math.linear.RealMatrix;
38  import org.apache.commons.math.linear.RealMatrixImpl;
39  import org.sigtec.ink.Note;
40  import org.ximtec.igesture.algorithm.feature.FeatureException;
41  import org.ximtec.igesture.core.Gesture;
42  import org.ximtec.igesture.core.GestureClass;
43  import org.ximtec.igesture.core.SampleDescriptor;
44  import org.ximtec.igesture.util.DoubleVector;
45  import org.ximtec.igesture.util.VectorTools;
46  
47  
48  /**
49   * Comment
50   * @version 1.0 11.06.2008
51   * @author Ueli Kurmann
52   */
53  public class GestureClassHelper implements Runnable {
54  
55     private static final Logger LOGGER = Logger
56           .getLogger(GestureClassHelper.class.getName());
57  
58     private GestureClass gestureClass;
59     private RubineConfiguration configuration;
60  
61     private List<Gesture<Note>> samples;
62     private DoubleVector meanFeatureVector;
63  
64     /**
65      * Data structure for storing the feature vector per sample.
66      */
67     private Map<Gesture<Note>, GestureSampleHelper> sampleFeatureVector;
68  
69     private RealMatrix realMatrix;
70  
71     private double initialWeight;
72  
73     private DoubleVector weightsVector;
74  
75     private CountDownLatch latch;
76  
77  
78     public GestureClassHelper(GestureClass gestureClass,
79           RubineConfiguration configuration, CountDownLatch latch) {
80        this.gestureClass = gestureClass;
81        this.samples = getSamples();
82        this.configuration = configuration;
83        this.sampleFeatureVector = new HashMap<Gesture<Note>, GestureSampleHelper>();
84        this.latch = latch;
85        LOGGER.setLevel(Level.SEVERE);
86     }
87  
88  
89     /**
90      * Iterates over each sample of the gesture class and computes the feature
91      * vector. in a second step the mean feature vector of the samples is
92      * computed. Both vectors are stored in the corresponding data structure.
93      * 
94      * @param gestureClass the gesture class.
95      * @param featureList the feature list.
96      */
97     private void computeFeatureVectors(GestureClass gestureClass)
98           throws FeatureException {
99  
100       // computes the feature vector per class
101       List<DoubleVector> vectors = new ArrayList<DoubleVector>();
102 
103       for (Gesture<Note> sample : samples) {
104          try {
105             /**
106              * FIXME This method may throw a FeatureException. If an exception
107              * occurs, the vector is not added to the list BUT we should go
108              * further to the next sample. Before this refactoring, the process
109              * was interrupted and therefore the algorithm can't be instantiated
110              * as soon as one sample does not fulfill the requirements defined in
111              * the features.
112              */
113 
114             // FIXME correct?
115             GestureSampleHelper helper = new GestureSampleHelper(sample
116                   .getGesture(), configuration);
117             sampleFeatureVector.put(sample, helper);
118 
119             if (VectorTools.hasValidValues(helper.getFeatureVector())) {
120                vectors.add(helper.getFeatureVector());
121             }
122          }
123          catch (FeatureException exception) {
124             LOGGER.warning("Could not compute the Feature Vector.");
125          }
126       }
127 
128       // computes the mean vector
129       /**
130        * FIXME The if statement is inserted to guarantee a minimal number of
131        * feature vectors. It has to be checked, if 1 vector is enough. Maybe this
132        * is a mathematical issue.
133        */
134       if (vectors.size() > 0) {
135          meanFeatureVector = VectorTools.mean(vectors);
136       }
137       else {
138          throw new FeatureException(
139                "There are not enough samples for Gesture Class"
140                      + gestureClass.getName());
141       }
142    } // computeFeatureVectors
143 
144 
145    /**
146     * Returns a list of GestureSamples
147     * @return a list of GestureSamples
148     */
149    private List<Gesture<Note>> getSamples() {
150       SampleDescriptor descriptor = gestureClass
151             .getDescriptor(SampleDescriptor.class);
152       return descriptor.getSamples();
153    }
154 
155 
156    @Override
157    public void run() {
158       try {
159          computeFeatureVectors(gestureClass);
160       }
161       catch (FeatureException exception) {
162          exception.printStackTrace();
163          // FIXME throw new AlgorithmException(ExceptionType.Initialisation,
164          // exception);
165       }
166 
167       getCovarianceMatrix();
168       latch.countDown();
169    }
170 
171 
172    /**
173     * Computes the covariance matrix for a given gesture class. The covariance
174     * matrix is cached.
175     * 
176     * @param gestureClass the gesture class to compute the covariance matrix.
177     * @return the covariance matrix of the gesture class.
178     */
179    public RealMatrix getCovarianceMatrix() {
180 
181       if (realMatrix == null) {
182          int numOfFeatures = configuration.getNumberOfFeatures();
183          double[][] matrix = new double[numOfFeatures][numOfFeatures];
184 
185          for (int i = 0; i < numOfFeatures; i++) {
186 
187             for (int j = 0; j < numOfFeatures; j++) {
188                double sum = 0;
189 
190                for (Gesture< ? > sample : samples) {
191                   try {
192                      sum += (sampleFeatureVector.get(sample).getFeatureVector()
193                            .get(i) - meanFeatureVector.get(i))
194                            * (sampleFeatureVector.get(sample).getFeatureVector()
195                                  .get(j) - meanFeatureVector.get(j));
196                   }
197                   catch (Exception e) {
198                      e.printStackTrace();
199                   }
200                }
201                matrix[i][j] = sum;
202             }
203          }
204 
205          realMatrix = new RealMatrixImpl(matrix);
206       }
207 
208       return realMatrix;
209    } // getCovarianceMatrixPerClass
210 
211 
212    public DoubleVector getMeanFeatureVector() {
213       return meanFeatureVector;
214    } // getMeanFeatureVector
215 
216 
217    public int getNumberOfSamples() {
218       return samples.size();
219    } // getNumberOfSamples
220 
221 
222    public void computeWeights(RealMatrix inverse) {
223       weightsVector = new DoubleVector(meanFeatureVector.size());
224 
225       for (int j = 0; j < meanFeatureVector.size(); j++) {
226          double wci = 0;
227 
228          for (int i = 0; i < meanFeatureVector.size(); i++) {
229             wci += inverse.getEntry(i, j) * meanFeatureVector.get(i);
230          }
231 
232          weightsVector.set(j, wci);
233       }
234 
235       // compute the initial weight
236       double wc0 = 0;
237 
238       // TODO inline in for loop above? after weightVector.set(j, wci)
239       for (int f = 0; f < meanFeatureVector.size(); f++) {
240          wc0 += weightsVector.get(f) * meanFeatureVector.get(f);
241       }
242 
243       this.initialWeight = -0.5 * wc0;
244    }
245 
246 
247    public double getInitialWeight() {
248       return initialWeight;
249    } // getInitialWeight
250 
251 
252    public DoubleVector getWeights() {
253       return weightsVector;
254    } // getWeights
255 
256 
257    public GestureClass getGestureClass() {
258       return gestureClass;
259    } // getGestureClass
260 
261 }