#!/usr/bin/env python
#$ -cwd
#$ -S /usr/bin/python

########################### usual importation of modules #####################

# import of standard module
import sys
import os
from optparse import OptionParser

# import the user configuration from ~/.pythonrc.py
import user

######################### importation of pyVerif modules #####################

# import necessary classes from pyVerif
from pyVerif.Executor import Executor
from pyVerifseb.MyExperiments import GenerativeExperiment

##################### the "main" program ##################

if __name__ == '__main__':

    print """
          
	  pyVerif program to perform a face verification experiment based on GMM/MAP adaptation.

	  Authors: Sebastien Marcel, Johnny Mariethoz and Olivier Bornet

          Version: 1.0
	  
	  Date: August 2006
	  
          """

    # program usage
    usage = "usage: %prog [options]"

    parser = OptionParser(usage)

    # set options
    parser.set_description ("pyVerif program to perform a face verification experiment")

    parser.add_option ("-t", "--test", dest="test",
                       help="Perform also test set",
                       action="store_true", default=False)

    parser.add_option ("-d", "--features-dir", dest="features_dir",
                       help="Directory containing the features files",
                       type="string", default="none")

    parser.add_option ("-o", "--output-dir", dest="output_dir",
                       help="Directory containing the results files",
                       type="string", default="./noname")
                       
    parser.add_option ("-p", "--protocol-dir", dest="protocol_dir",
                       help="Directory containing the protocol files",
                       type="string", default="lp")
    
    parser.add_option ("-b", "--base-dir", dest="base_dir",
                       help="Directory containing protocols and features "
		       + "(default xm2vts)",
                       type="string", default="xm2vts")
    
    parser.add_option ("-v", "--variant", dest="variant",
                       help="Variant of the protocol (default lp1)",
                       type="string", default="lp1")
    
    parser.add_option ("-l", "--log-on-screen", dest="log_on_screen",
                       help="Output log on screen",
                       action="store_true", default=False)
    
    parser.add_option ("-n", "--number-of-gaussians",
                       dest="number_of_gaussians",
                       help="Number of Gaussians for each model "
                       + "(default 512)",
                       type="int", default=512)

    parser.add_option ("-m", "--map-factor", dest="map_factor",
                       help="MAP adaptation factor (default 0.5)",
                       type="float", default=0.5)

    parser.add_option ("-s", "--seed", dest="seed",
                       help="seed (default -1)",
                       type="int", default=-1)
    
    parser.add_option ("", "--prior-train", dest="prior_train",
                       help="prior for world model training (default 0.1)",
                       type="float", default=0.1)

    parser.add_option ("", "--prior-adapt", dest="prior_adapt",
                       help="prior for model adaptation (default 0.1)",
                       type="float", default=0.1)

    parser.add_option ("", "--variance-flooring", dest="variance_floor",
                       help="variance flooring for training (default 0.0005)",
                       type="float", default=0.0005)

    parser.add_option ("", "--accuracy", dest="accuracy",
                       help="end of accuracy (default 0.0005)",
                       type="float", default=0.0005)

    parser.add_option ("", "--kmean-iter", dest="kmean_iter",
                       help="maximum number of K-means iterations for both training (default 500)",
                       type="int", default=500)

    parser.add_option ("", "--em-iter", dest="em_iter",
                       help="maximum number of EM iterations for both training (default 500)",
                       type="int", default=500)

    parser.add_option ("", "--map-iter", dest="map_iter",
                       help="maximum number of EM iterations for adaptation (default 10)",
                       type="int", default=10)

    parser.add_option ("-w", "--worldmodel", dest="worldmodel",
                       help="Prior world model to use",
                       type="string", default="")
    
    parser.add_option ("", "--perf", dest="compute_perf",
                       help="compute the performance",
                       action="store_true", default=False)
    
    # parse options
    (options,args) = parser.parse_args ()

    # check the number of options
    if len(args) != 0:
        parser.error("Error: incorrect number of arguments, try --help")

    if options.features_dir == "none":
        parser.error("Error: no feature dir provided")

    #
    # prepare the directories
    #

    # base dir where is this script (for instance /home/vision/marcel/work/experiments/faceverification/pyverif/verification)
    base_dir = os.path.join ('/home', 'vision', 'marcel', 'work', 'experiments', 'faceverification', 'pyverif', options.base_dir)

    # directory containing the features
    features_dir = os.path.join (base_dir, 'features', options.features_dir)

    # directory containing the binary programs
    bin_dir = os.path.join (base_dir, 'verification','bin')

    # directory for the output files
    out_dir = os.path.join (options.output_dir, options.variant)

    # base dir containing the protocol files
    base_dir_protocols = os.path.join (base_dir, 'protocols', options.protocol_dir)
    
    # directory containing the results (created if it does'nt exist)
    results_dir = os.path.join (out_dir, 'results')
    
    if not os.path.isdir (results_dir):
       os.makedirs (results_dir)
    
    # directory containing the temporary files (created if it does'nt exist)
    tmp_dir = os.path.join (out_dir, 'tmp')
    
    if not os.path.isdir (tmp_dir):
       os.makedirs (tmp_dir)
    

    #
    # prepare the experiment
    #

    # create an instance of the Experiment class
    experiment = GenerativeExperiment (
        out_dir = out_dir,
        features_dir = features_dir,
        features_ext = '.bindata',
        bin_dir = bin_dir,
        tmp_dir = tmp_dir,
        log_on_screen = options.log_on_screen,
        log_file = os.path.join (out_dir, 'gmm-' + options.variant + '.log'),
        norm_file = 'train_world_' + options.variant + '.lst')
    
    # log the used options
    experiment.log_options (options.__dict__)

    # load the specified protocol
    experiment.load_protocols (base_dir_protocols, options.variant)

    # specify the main program to train and test a GMM
    cmd = "gmm"

    # check if a world model is provided
    if not options.worldmodel:
    	prior_model = os.path.join (experiment.world_models_dir, experiment.world_model_name)
    else:
    	prior_model = options.worldmodel
	print "using prior model %s" % prior_model
    	experiment.world_models_dir = prior_model[:prior_model.rfind('/')]
    	experiment.world_model_name = prior_model[prior_model.rfind('/')+1:len(prior_model)]
    	#print experiment.world_models_dir, experiment.world_model_name

    # options to train an GMM (world model training)
    train_opt = "-bin "
    train_opt = train_opt + "-prior %g " % options.prior_train
    train_opt = train_opt + "-iterk %d " % options.kmean_iter
    train_opt = train_opt + "-iterg %d " % options.em_iter
    train_opt = train_opt + "-threshold %g " % options.variance_floor
    train_opt = train_opt + "-e %g " % options.accuracy
    train_opt = train_opt + "-seed %d " % options.seed
    train_opt = train_opt + "-n_gaussians %s " % options.number_of_gaussians
    train_opt = train_opt + "-dir %TMPDIR -save %MODEL %FILES "
    train_opt = train_opt + " > %TMPDIR/train_model.stdout"

    # options to adapt a GMM (client model training)
    adapt_opt = "--adapt -bin "
    adapt_opt = adapt_opt + "-prior %g " % options.prior_adapt
    adapt_opt = adapt_opt + "-iterg %d " % options.map_iter
    adapt_opt = adapt_opt + "-e %g " % options.accuracy
    adapt_opt = adapt_opt + "-map %s " % options.map_factor
    adapt_opt = adapt_opt + "-dir %TMPDIR -save %MODEL " + prior_model + " %FILES "
    adapt_opt = adapt_opt + " > %TMPDIR/adapt_model.stdout"

    # options to compute the scores against a GMM (world/client model testing)
    test_opt = "--test -bin -dir %TMPDIR %MODEL %FILES %SCORES "
    test_opt = test_opt + " > %TMPDIR/test_model.stdout"

    # set main program and options
    experiment.set_cmds (cmd=cmd, world_options=train_opt,
                         test_options=test_opt, client_options=adapt_opt)


    #
    # run the experiment
    #

    # train the world model if no world model provided
    if not options.worldmodel:
    	experiment.train_world_model ()
   
    # compute all the scores against the world model for the development set
    experiment.build_world_scores (for_dev = True)

    # train all the client models
    experiment.train_clients_models (for_dev = True)

    # compute all the scores against the clients models for the development set
    experiment.build_clients_scores (for_dev = True)
    
    # merge scores from the world model and from the client model for the development set
    experiment.merge_score (os.path.join (results_dir, 'scores-dev'), for_dev = True)

    if options.test:
    	# compute all the scores against the world model for the test set
        experiment.build_world_scores (for_dev = False)
	
	# Warning: no client models to train in test with XM2VTS
	if options.base_dir != "xm2vts":
		# train all the client models
		experiment.train_clients_models (for_dev=False)

	# compute all the scores against the clients models for the test set
        experiment.build_clients_scores (for_dev = False)
	
        # merge scores from the world model and from the client model for the test set
        experiment.merge_score (os.path.join (results_dir, 'scores-test'), for_dev = False)

    if options.compute_perf:
	# compute the performance with pymeasure
	executor = Executor()
	executor.enable_log(on_screen=True,filename=experiment.executor.log_filename)
	cmd_perf = "pyerror"
	perf_opt = "-b --dev-set=" + os.path.join (results_dir, 'scores-dev') + " " + os.path.join (results_dir, 'scores-test')
	executor.run(cmd_perf,perf_opt)

	# generate DET/EPC curves
	cmd_perf = "pydet"
	perf_opt = "-b -s " + os.path.join (results_dir, 'det.eps') + " " + os.path.join (results_dir, 'scores-dev') + " " + os.path.join (results_dir, 'scores-test')
	executor.run(cmd_perf,perf_opt)

	cmd_perf = "pyepc"
	perf_opt = "-b -n 10 -s " + os.path.join (results_dir, 'epc.eps') + " " + os.path.join (results_dir, 'scores-dev') + " " + os.path.join (results_dir, 'scores-test')
	executor.run(cmd_perf,perf_opt)


