View Javadoc

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