Commit 640286ff authored by Laurent Belcour's avatar Laurent Belcour

[Merge] Python into master. Adding the possiblity to pass arguments to

the loading of data objects in the Python interface.
parent 865a6c3f
......@@ -2,6 +2,7 @@
#
# Copyright (C) 2014, 2015 CNRS
# Copyright (C) 2013, 2014, 2015 Inria
# Copyright (C) 2015 Universite de Montreal
#
# This file is part of ALTA.
#
......@@ -15,7 +16,8 @@ import SCons.SConf as C
## Add ALTA custom cmd configurations
##
AddOption('--cfg', help='Specify a configuration file (see config.example')
AddOption('--cfg', help='Specify a configuration file')
AddOption('--no-externals', action="store_false", dest="obtain_externals", default=True, help='Do not download and build externals')
## Import configuration from a config file
......@@ -99,6 +101,7 @@ if len(envVars['PKG_CONFIG_PATH']) > 0:
envVars['PKG_CONFIG_PATH'] += ':'
envVars['PKG_CONFIG_PATH'] += os.path.abspath('external' + os.sep + 'build' + os.sep + 'lib' + os.sep + 'pkgconfig')
env = Environment(variables = vars, ENV = envVars )
env['DL_EXTERNALS'] = GetOption('obtain_externals')
# Generate help text for the build variables.
......
/**
\page python Python Interface
To have access to the python interface, you need to compile the python module. This can be done using the *python* tag in the scons call:
To have access to the python interface, you need to compile the python module. This can be done using the `python` tag in the scons call:
$ scons --cfg=myconfig.py python
$ scons python
Once the python interface (alta.so) is compiled, you need to update the PYTHONPATH environment variable to the build directory. If not, you will not have access to the interface. On UNIX-like systems, it can be done using by sourcing the *setpath.sh* script. On MS-Windows, you need to update the variable by hand.
Once the python interface (alta.so) is compiled, you need to update the `PYTHONPATH` environment variable to the build directory. If not, you will not have access to the interface. On UNIX-like systems, it can be done using by sourcing the *setpath.sh* script. On MS-Windows, you need to update the variable by hand.
In python, the ALTA module is simply loaded using:
>>> import alta
You can obtain \ref functions, \ref datas and \ref fitters objects using the get_function, get_data and get_fitter functions:
This module provide different basic types such as `arguments` and `vec`. They have the same behaviour as ALTA C++ classes \ref arguments and \ref vec. However, they can be intialized from Python types. An `arguments` object can be created using a dictionnary:
>>> args = alta.arguments({'foo' : 'bar', 'hello' : 'world'})
This correspond to the argument `--foo bar --hello world`. Similarly, a `vec` type can be created from a list of floatting point values.
You can obtain \ref functions, \ref datas and \ref fitters objects using the `get_function`, `get_data` and `get_fitter` functions with the name of the plugin to load as argument:
>>> func = alta.get_function('nonlinear_function_blinn')
>>> dat = alta.get_data('data_merl')
>>> fitter = alta.get_fitter('nonlinear_fitter_ceres')
The different \ref softs are available as python functions:
Note that you can also provide an \ref arguments object to set specific options for the plugin (see below).
\ref functions and \ref datas can load from files and save to files:
>>> dat = alta.get_data('data_merl')
>>> dat.load('blue_metallic_paint.binary')
>>> dat.save('blue_metallic_paint.copy.binary')
In both cases, an \ref arguments object can be passed to control the behaviour of the load or save functions. For example, if you want to save a function in a specific format, you can use the `export` argument:
>>> args = alta.arguments({'export' : 'shader'})
>>> func = alta.get_function('nonlinear_function_blinn')
>>> func.save('my_function.func', args)
Some \ref softs are available as python functions:
>>> dat1 = alta.load_data('data_merl', 'blue-metallic-paint.binary')
>>> dat2 = alta.get_data('data_utia')
>>> alta.data2data(dat1, dat2)
This will perform a data conversion from the *blue-metallic-paint* of the MERL database to the UTIA format.
This will perform a data conversion from the `blue-metallic-paint` of the [MERL][MERL] database to the \ref UTIA format.
Available \ref softs are:
+ `data2data` to convert a \ref data object into another one. This function will try to evaluate the input data at every location specified by the output data format when the format has a static set of configurations (this is the case for all `data_io` plugins). Function call: `data2data(data_in, data_out)`.
+ `brdf2data` to evalute a \ref function at the configurations defined by the data object. This will replace the content of the data object by the values of the function at the specified locations. Function call `brdf2data(func_in, data_out)`.
[MERL]: http://www.merl.com/brdf/
*/
# ALTA --- Analysis of Bidirectional Reflectance Distribution Functions
#
# Copyright (C) 2014, 2015 CNRS
# Copyright (C) 2013, 2014, 2015 Inria
# Copyright (C) 2015 Universite de Montreal
#
# This file is part of ALTA.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import shutil
import SCons.SConf as C
......@@ -54,21 +66,21 @@ else:
# IpOpt dependencies
##
env2 = env.Clone()
if not on_windows and not library_available(env2, pkgspec='ipopt', inc_var='IPOPT_INC', lib_var='IPOPT_DIR', lib='IPOPT_LIB', header='coin/IpTNLP.hpp'):
if not on_windows and not library_available(env2, pkgspec='ipopt', inc_var='IPOPT_INC', lib_var='IPOPT_DIR', lib='IPOPT_LIB', header='coin/IpTNLP.hpp') and env['DL_EXTERNALS']:
execfile('obtain_ipopt.py')
##
# NlOpt dependencies
##
env2 = env.Clone()
if not on_windows and not library_available(env2, pkgspec='nlopt', inc_var='NLOPT_INC', lib_var='NLOPT_DIR', lib='NLOPT_LIB', header='nlopt.h'):
if not on_windows and not library_available(env2, pkgspec='nlopt', inc_var='NLOPT_INC', lib_var='NLOPT_DIR', lib='NLOPT_LIB', header='nlopt.h') and env['DL_EXTERNALS']:
execfile('obtain_nlopt.py')
##
# CERES dependencies
##
env2 = env.Clone()
if not library_available(env2, pkgspec='ceres', inc_var='CERES_INC', lib_var='CERES_DIR', lib='CERES_LIB', header='ceres/ceres.h'):
if not library_available(env2, pkgspec='ceres', inc_var='CERES_INC', lib_var='CERES_DIR', lib='CERES_LIB', header='ceres/ceres.h') and env['DL_EXTERNALS']:
execfile('obtain_ceres.py')
C.progress_display("external libraries are built as shared libraries")
......
/* ALTA --- Analysis of Bidirectional Reflectance Distribution Functions
Copyright (C) 2014, 2015 Universtie de Montreal
Copyright (C) 2013, 2014 Inria
This file is part of ALTA.
......
......@@ -381,10 +381,6 @@ vec nonlinear_function::getParametersMin() const
/*--- Compound functions implementation ----*/
compound_function::~compound_function()
{
for( unsigned int i=0; i < fs.size(); i++)
{
delete fs[i];
}
}
vec compound_function::operator()(const vec& x) const
......@@ -413,7 +409,7 @@ vec compound_function::parametersJacobian(const vec& x) const
// Export the sub-Jacobian for each function
for(unsigned int f=0; f<fs.size(); ++f)
{
nonlinear_function* func = fs[f];
const ptr<nonlinear_function>& func = fs[f];
int nb_f_params = func->nbParameters();
// Only export Jacobian if there are non-linear parameters
......@@ -439,7 +435,7 @@ vec compound_function::parametersJacobian(const vec& x) const
return jac;
}
void compound_function::push_back(nonlinear_function* f, const arguments& f_args)
void compound_function::push_back(const ptr<nonlinear_function>& f, const arguments& f_args)
{
// Update the input param
if(input_parametrization() == params::UNKNOWN_INPUT)
......@@ -472,7 +468,7 @@ nonlinear_function* compound_function::operator[](int i) const
#ifdef DEBUG
assert(i >= 0 && i < fs.size());
#endif
return fs[i];
return fs[i].get();
}
unsigned int compound_function::size() const
......@@ -580,9 +576,9 @@ void compound_function::bootstrap(const ::ptr<data> d, const arguments& args)
{
std::streampos pos = file.tellg();
if(dynamic_cast<product_function*>(fs[i]) != NULL)
{
product_function* p = dynamic_cast<product_function*>(fs[i]);
ptr<product_function> p = dynamic_pointer_cast<product_function>(fs[i]);
if(p) {
nonlinear_function* f1 = p->first();
nonlinear_function* f2 = p->second();
......@@ -837,7 +833,8 @@ void compound_function::save_call(std::ostream& out, const arguments& args) cons
/*--- Product functions implementation ----*/
product_function::product_function( nonlinear_function* g1, nonlinear_function* g2,
product_function::product_function(const ptr<nonlinear_function>& g1,
const ptr<nonlinear_function>& g2,
bool is_g1_fixed, bool is_g2_fixed)
: f1( g1 ),
f2( g2 ),
......@@ -859,8 +856,6 @@ product_function::product_function( nonlinear_function* g1, nonlinear_function*
product_function::~product_function()
{
delete f1;
delete f2;
}
......@@ -895,8 +890,7 @@ bool product_function::load(std::istream& in)
std::streampos pos = in.tellg();
// Load the first function
if(f1 != NULL)
{
if(f1) {
loaded_f1 = f1->load(in);
if(! loaded_f1)
{
......@@ -906,8 +900,7 @@ bool product_function::load(std::istream& in)
pos = in.tellg();
// Load the second function
if(f2 != NULL)
{
if(f2) {
loaded_f2 = f2->load(in);
if(! loaded_f2)
{
......@@ -1296,10 +1289,10 @@ void product_function::setParametrization(params::output new_param)
nonlinear_function* product_function::first() const
{
return f1;
return f1.get();
}
nonlinear_function* product_function::second() const
{
return f2;
return f2.get();
}
......@@ -183,7 +183,7 @@ class compound_function: public nonlinear_function
//! This function allows to put a new nonlinear function \a f in the
//! compound object. This function will be processed for nonlinear
//! optimisation only if \a fixed equals true.
virtual void push_back(nonlinear_function* f, const arguments& f_args);
virtual void push_back(const ptr<nonlinear_function>& f, const arguments& f_args);
//! \brief Access to the i-th function of the compound
nonlinear_function* operator[](int i) const;
......@@ -214,7 +214,7 @@ class compound_function: public nonlinear_function
//!
//! <u>Local/Global policy:</u></br />
//! Local bootstrap can not overload the global bootstrap.
virtual void bootstrap(const ::ptr<data> d, const arguments& args);
virtual void bootstrap(const ptr<data> d, const arguments& args);
//! Set the dimension of the input space of the function
virtual void setDimX(int nX);
......@@ -272,7 +272,7 @@ class compound_function: public nonlinear_function
virtual void save_call(std::ostream& out, const arguments& args) const;
protected:
std::vector<nonlinear_function*> fs;
std::vector<ptr<nonlinear_function>> fs;
std::vector<arguments> fs_args;
std::vector<bool> is_fixed;
......@@ -289,9 +289,8 @@ class product_function : public nonlinear_function
//! \brief Constructor of the product function, affect the two function
//! to already created nonlinear_function objects.
product_function(nonlinear_function* g1, nonlinear_function* g2,
bool is_g1_fixed = false,
bool is_g2_fixed = false);
product_function(const ptr<nonlinear_function>& g1, const ptr<nonlinear_function>& g2,
bool is_g1_fixed = false, bool is_g2_fixed = false);
~product_function();
......@@ -372,7 +371,7 @@ class product_function : public nonlinear_function
private: // data
// Function composing the product
nonlinear_function *f1, *f2;
ptr<nonlinear_function> f1, f2;
std::pair<bool,bool> _is_fixed; /*!< represents whether or not the parameters of each function is fixed regardint the optimizer */
......
......@@ -424,7 +424,7 @@ ptr<function> plugins_manager::get_function(const std::string& n)
ptr<data> plugins_manager::get_data(const std::string& n, const arguments& args)
{
if(n.empty())
if(n.empty() || n == "vertical_segment")
{
#ifdef DEBUG
std::cout << "<<DEBUG>> no data plugin specified, returning a vertical_segment loader" << std::endl;
......
......@@ -40,6 +40,7 @@ class blinn_function : public nonlinear_function
{
setParametrization(params::COS_TH);
setDimX(1);
setDimY(1);
}
// Overload the function operator
......
......@@ -15,7 +15,9 @@
#include <core/common.h>
#include <core/ptr.h>
#include <core/function.h>
#include <core/rational_function.h>
#include <core/plugins_manager.h>
#include <core/vertical_segment.h>
// STL include
#include <iostream>
......@@ -26,15 +28,18 @@
/* The following code register ALTA's shared pointer as a valid shared ptr
* to be used by boost::python .
*/
template <typename T>
T* get_pointer(ptr<T> const& p) {
template <typename T> T* get_pointer(ptr<T>& p) {
return const_cast<T*>(p.get());
}
template <typename T> const T* get_pointer(const ptr<T>& p) {
return p.get();
}
namespace boost {
namespace python {
template <typename T>
struct pointee< ::ptr<T> > {
template <typename T> struct pointee< ::ptr<T> > {
typedef T type;
};
}
......@@ -131,13 +136,168 @@ ptr<function> get_function_from_args(const python_arguments& args) {
return func;
}
/* Load a function object from a file. The arguments object is never used here.
* The file is supposed to load the function correctly.
*/
ptr<function> load_function(const std::string& filename) {
return plugins_manager::load_function(filename);
}
ptr<function> load_function_with_args(const std::string& filename, const arguments&) {
return plugins_manager::load_function(filename);
}
/* Loading a function object from file
*
* TODO: Add exceptions
*/
void load_from_file_with_args(const ptr<function>& func, const std::string& filename,
const arguments& args) {
// Open a stream
std::ifstream file;
file.open(filename.c_str()) ;
if(!file.is_open()) {
std::cerr << "<<ERROR>> unable to open file \"" << filename << "\"" << std::endl ;
return;
}
// Parse the associated header
arguments header = arguments::parse_header(file);
// Load the function stream
func->load(file);
}
void load_from_file(const ptr<function>& func, const std::string& filename) {
arguments args;
load_from_file_with_args(func, filename, args);
}
/* Operators on function object. This provide the ability to create compounds
* and product in the command line. This is only possible for nonlinear_functions
*
* TODO: The compound and product function should store the shared pointer to the
* function objects. They might stay in memory longer than the input functions.
*/
ptr<function> add_function(const ptr<function>& f1, const ptr<function>& f2) {
// Convert to a nonlinear function
ptr<nonlinear_function> nf1 = dynamic_pointer_cast<nonlinear_function>(f1);
ptr<nonlinear_function> nf2 = dynamic_pointer_cast<nonlinear_function>(f2);
arguments args;
compound_function* cf = new compound_function();
if(nf1 && nf2) {
cf->push_back(nf1, args);
cf->push_back(nf2, args);
return ptr<function>(cf);
// Failure case, one of the function is a NULL ptr.
} else {
std::cerr << "<<ERROR>> One of the input functions is NULL" << std::endl;
return ptr<function>(NULL);
}
}
ptr<function> mult_function(const ptr<function>& f1, const ptr<function>& f2) {
// Convert to a nonlinear function
ptr<nonlinear_function> nf1 = dynamic_pointer_cast<nonlinear_function>(f1);
ptr<nonlinear_function> nf2 = dynamic_pointer_cast<nonlinear_function>(f2);
// Check
if(!nf1 || !nf2) {
std::cerr << "<<ERROR>> One of the input function of the product is NULL" << std::endl;
return ptr<function>(NULL);
}
product_function* pf = new product_function(nf1, nf2);
return ptr<function>(pf);
}
/* Setting/Get the parameters of a function object
*
* TODO: Add the rational function interface
*/
void set_function_params(ptr<function>& f, const vec& v) {
// Try to set the parameter as a nonlinear function
ptr<nonlinear_function> nf = dynamic_pointer_cast<nonlinear_function>(f);
if(nf) {
if(nf->nbParameters() == v.size()) {
nf->setParameters(v);
} else {
std::cerr << "<<ERROR>> Vector of params and function have different sizes." << std::endl;
}
return;
}
// ptr<rational_function> rf = dynamic_pointer_cast<rational_function>(f);
// if(rf) {
// int np, nq;
// rf->size(np, nq);
// if(np+nq == v.size()) {
// rf->setParameters(v);
// } else {
// std::cerr << "<<ERROR>> Vector of parameters has different size that the functions number of parameters" << std::endl;
// }
// return;
// }
}
python_vec get_function_params(ptr<function>& f) {
// Try to set the parameter as a nonlinear function
ptr<nonlinear_function> nf = dynamic_pointer_cast<nonlinear_function>(f);
if(nf) {
return nf->parameters();
}
std::cerr << "<<ERROR>> Parameters cannot be retrieved" << std::endl;
vec res(1);
return res;
}
/* Save a function object to a file, without any argument option. This will save the function
* object in ALTA's format.
*/
void save_function_without_args(const ptr<function>& f, const std::string& filename) {
arguments args;
f->save(filename, args);
}
/* Fitter interface to allow to launch fit without providing an arguments
* object.
*/
bool fit_data(const ptr<fitter>& _fitter, const ptr<data>& _data, ptr<function>& _func) {
arguments args;
return _fitter->fit_data(_data, _func, args);
}
/* Softs functions. Those function recopy the softs main function, without
* the command line arguments.
* TODO: Add the command line arguments in the parameters
*/
void data2data(const data* d_in, data* d_out) {
#pragma omp parallel for
void data2data(const data* d_in, data* d_out)
{
if(dynamic_cast<vertical_segment*>(d_out)!=NULL)
{
d_out->setParametrization(d_in->input_parametrization());
d_out->setDimX(params::dimension(d_in->input_parametrization()));
d_out->setDimY(d_in->dimY());
vec temp(d_out->dimX() + d_out->dimY());
for(int i=0; i<d_in->size(); ++i)
{
// Copy the input vector
vec x = d_in->get(i);
params::convert(&x[0], d_in->parametrization(), d_out->parametrization(), &temp[0]);
params::convert(&x[d_in->dimX()], d_in->output_parametrization(), d_in->dimY(), d_out->output_parametrization(), d_out->dimY(), &temp[d_out->dimX()]);
d_out->set(temp);
}
}
else
{
#pragma omp parallel for
for(int i=0; i<d_out->size(); ++i)
{
vec temp(d_in->dimX());
......@@ -159,20 +319,52 @@ void data2data(const data* d_in, data* d_out) {
d_out->set(x);
}
}
}
void fit_data(ptr<fitter>& f, const ptr<data>& d, ptr<function>& fn, const arguments& args) {
f->fit_data(d, fn, args);
/* This function provides a similar behaviour that the brdf2data function.
* An input function object is evaluated on the input position of a data
* object. To do so, the data object must contains some positions. It can
* be so when the data object is a laoded data sample or when the data type
* has predefined sample sets.
*/
void brdf2data(const ptr<function>& f, ptr<data>& d) {
if(d->size() == 0) {
std::cerr << "<<ERROR>> Please provide a data object with a sample structure or load a data file with defined positions." << std::endl;
return;
}
vec temp(f->dimX());
for(int i=0; i<d->size(); ++i) {
// Copy the input vector
vec x = d->get(i);
// Convert the data to the function's input space.
if(f->input_parametrization() == params::UNKNOWN_INPUT) {
memcpy(&temp[0], &x[0], f->dimX()*sizeof(double));
} else {
params::convert(&x[0], d->parametrization(), f->parametrization(), &temp[0]);
}
vec y = f->value(temp);
for(int j=0; j<d->dimY(); ++j) {
x[d->dimX() + j] = y[j];
}
d->set(i, y);
}
}
/* Exporting the ALTA module
/*! \inpage python
* Exporting the ALTA module
*/
BOOST_PYTHON_MODULE(alta)
{
// Argument class
//
bp::class_<python_arguments>("arguments")
bp::class_<arguments>("_arguments");
bp::class_<python_arguments, bp::bases<arguments>>("arguments")
.def(bp::init<>())
.def(bp::init<bp::dict>())
.def("update", &arguments::update);
......@@ -194,11 +386,20 @@ BOOST_PYTHON_MODULE(alta)
// Function interface
//
bp::class_<function, ptr<function>, boost::noncopyable>("function", bp::no_init)
.def("__add__", &add_function)
.def("__mul__", &mult_function)
.def("__rmul__", &mult_function)
.def("value", &function::value)
.def("load", &function::load)
.def("save", &function::save);
.def("load", &load_from_file)
.def("load", &load_from_file_with_args)
.def("save", &function::save)
.def("save", &save_function_without_args)
.def("set", &set_function_params)
.def("get", &get_function_params);
bp::def("get_function", get_function);
bp::def("get_function", get_function_from_args);
bp::def("load_function", load_function);
bp::def("load_function", load_function_with_args);
......@@ -209,6 +410,7 @@ BOOST_PYTHON_MODULE(alta)
.def("get", &data::get)
//.def("set", &data::set)
.def("load", static_cast< void(data::*)(const std::string&)>(&data::load))
.def("load", static_cast< void(data::*)(const std::string&, const arguments&)>(&data::load))
.def("save", &data::save);
bp::def("get_data", get_data);
bp::def("get_data", get_data_with_args);
......@@ -218,11 +420,12 @@ BOOST_PYTHON_MODULE(alta)
// Fitter interface
//
bp::class_<fitter, ptr<fitter>, boost::noncopyable>("fitter", bp::no_init)
.def("fit_data", &fitter::fit_data);
.def("fit_data", &fitter::fit_data)
.def("fit_data", &fit_data);
bp::def("get_fitter", plugins_manager::get_fitter);
bp::def("fit_data", fit_data);
// Softs
//
bp::def("data2data", data2data);
bp::def("brdf2data", brdf2data);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment