diff --git a/bob/pad/base/algorithm/OCGMM.py b/bob/pad/base/algorithm/OCGMM.py
new file mode 100644
index 0000000000000000000000000000000000000000..1b8c30333146aee443b385b2bb77044895312543
--- /dev/null
+++ b/bob/pad/base/algorithm/OCGMM.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+import numpy
+from bob.pad.base.algorithm import Algorithm
+
+import bob.learn.em
+import bob.io.base
+
+class OCGMM(Algorithm):
+  """
+  This class interfaces a GMM-based "classifier" to perform PAD experiments
+
+  A GMM is used to model the bonafide features
+
+  """
+
+  def __init__(self, n_gaussians=2, max_iter=1000, conv_threshold=1e-5, **kwargs):
+
+    Algorithm.__init__(self,  
+                       performs_projection=True,
+                       requires_projector_training=True, 
+                       **kwargs)
+
+    self.n_gaussians = n_gaussians
+    self.max_iter = max_iter
+    self.conv_threshold = conv_threshold
+
+    self.machine = None
+    self.trainer = bob.learn.em.ML_GMMTrainer(update_means=True, update_variances=True, update_weights=True)
+
+
+  def train_projector(self, training_features, projector_file):
+    """
+    Trains the GMM using Expectation-Maximimazation with Maximum Likelihood criterion 
+
+    **Parameters**
+
+    training_features:
+    """
+    # training_features[0] - training features for the REAL class.
+    # training_features[1] - training features for the ATTACK class.
+    
+    # The data - "positive class only"
+    pos = numpy.array(training_features[0])
+    features_dim = pos.shape[1]
+   
+    # The machine
+    self.machine = bob.learn.em.GMMMachine(self.n_gaussians, features_dim)
+
+    # train
+    bob.learn.em.train(self.trainer, self.machine, pos, max_iterations=self.max_iter, convergence_threshold=self.conv_threshold)
+    f = bob.io.base.HDF5File(projector_file, 'w')
+    self.machine.save(f)
+    
+
+  def project(self, feature):
+    """
+    Compute the log-likelihood of the feature 
+    """
+    return self.machine(feature)
+
+  def score(self, toscore):
+    return [toscore[0]]
diff --git a/bob/pad/base/algorithm/SKLGMM.py b/bob/pad/base/algorithm/SKLGMM.py
new file mode 100644
index 0000000000000000000000000000000000000000..c50a7adeeb88d410bc157ede8c632a2110a28500
--- /dev/null
+++ b/bob/pad/base/algorithm/SKLGMM.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+import numpy
+from bob.pad.base.algorithm import Algorithm
+
+import bob.io.base
+from sklearn import mixture
+
+class SKLGMM(Algorithm):
+  """
+  This class interfaces a GMM-based "classifier" to perform PAD experiments
+
+  A GMM is used to model the bonafide features
+
+  """
+
+  def __init__(self, n_gaussians=2, max_iter=1000, conv_threshold=1e-5, **kwargs):
+
+    Algorithm.__init__(self,  
+                       performs_projection=True,
+                       requires_projector_training=True, 
+                       **kwargs)
+
+    self.n_gaussians = n_gaussians
+    self.max_iter = max_iter
+    self.conv_threshold = conv_threshold
+
+    self.machine = mixture.GaussianMixture(n_components=n_gaussians, tol=conv_threshold, max_iter=max_iter)
+    self.parameters_keys = [ "covariance_type", "covariances_", "lower_bound_", "means_",
+                             "n_components", "weights_", "converged_", "precisions_", "precisions_cholesky_"]
+
+
+  def train_projector(self, training_features, projector_file):
+    """
+    Trains the GMM using Expectation-Maximimazation with Maximum Likelihood criterion 
+
+    **Parameters**
+
+    training_features:
+    """
+    # training_features[0] - training features for the REAL class.
+    # training_features[1] - training features for the ATTACK class.
+    
+    # The data - "positive class only"
+    pos = numpy.array(training_features[0])
+    features_dim = pos.shape[1]
+   
+    # train
+    self.machine.fit(pos)
+
+    # save
+    f = bob.io.base.HDF5File(projector_file, 'w')
+    for key in self.parameters_keys:
+      data = getattr(self.machine, key)
+      f.set(key, data)
+    
+  def load_projector(self, projector_file):
+     
+    f = bob.io.base.HDF5File(projector_file, 'r')  # file to read the machine from
+    self.machine = mixture.GaussianMixture()
+
+    for key in self.parameters_keys:
+      data = f.read(key)
+      setattr(self.machine, key, data)
+
+  def project(self, feature):
+    """
+    Compute the log-likelihood of the feature 
+    """
+    # load
+    return self.machine.score_samples(feature)
+
+  def score(self, toscore):
+    return [toscore[0]]