...
 
Commits (31)
......@@ -8,7 +8,9 @@ install:
- SET "PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
- python --version
- python -m ensurepip
- python -m pip install --egg scons==2.4.1
- pip install -U setuptools
- pip install -U wheel
- pip install scons==2.4.1
- cinst nsis.portable
before_build:
......
......@@ -4,3 +4,6 @@
[submodule "external/Catch"]
path = external/Catch
url = https://github.com/philsquared/Catch.git
[submodule "external/pybind11"]
path = external/pybind11
url = https://github.com/pybind/pybind11.git
This diff is collapsed.
This diff is collapsed.
# Ceres Solver - A fast non-linear least squares minimizer
# Copyright 2015 Google Inc. All rights reserved.
# http://ceres-solver.org/
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Google Inc. nor the names of its contributors may be
# used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Author: pablo.speciale@gmail.com (Pablo Speciale)
#
# FIND_PACKAGE() searches for a <package>Config.cmake file and an associated
# <package>Version.cmake file, which it loads to check the version number.
#
# This file can be used with CONFIGURE_FILE() to generate such a file for a
# project with very basic logic.
#
# It sets PACKAGE_VERSION_EXACT if the current version string and the requested
# version string are exactly the same and it sets PACKAGE_VERSION_COMPATIBLE
# if the current version is >= requested version.
set(PACKAGE_VERSION 1.14.0)
if ("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else ("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if ("${PACKAGE_FIND_VERSION}" STREQUAL "${PACKAGE_VERSION}")
set(PACKAGE_VERSION_EXACT TRUE)
endif ("${PACKAGE_FIND_VERSION}" STREQUAL "${PACKAGE_VERSION}")
endif ("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
# - Try to find Eigen3 lib
#
# This module supports requiring a minimum version, e.g. you can do
# find_package(Eigen3 3.1.2)
# to require version 3.1.2 or newer of Eigen3.
#
# Once done this will define
#
# EIGEN3_FOUND - system has eigen lib with correct version
# EIGEN3_INCLUDE_DIR - the eigen include directory
# EIGEN3_VERSION - eigen version
#
# This module reads hints about search locations from
# the following enviroment variables:
#
# EIGEN3_ROOT
# EIGEN3_ROOT_DIR
# Copyright (c) 2006, 2007 Montel Laurent, <montel@kde.org>
# Copyright (c) 2008, 2009 Gael Guennebaud, <g.gael@free.fr>
# Copyright (c) 2009 Benoit Jacob <jacob.benoit.1@gmail.com>
# Redistribution and use is allowed according to the terms of the 2-clause BSD license.
if(NOT Eigen3_FIND_VERSION)
if(NOT Eigen3_FIND_VERSION_MAJOR)
set(Eigen3_FIND_VERSION_MAJOR 2)
endif(NOT Eigen3_FIND_VERSION_MAJOR)
if(NOT Eigen3_FIND_VERSION_MINOR)
set(Eigen3_FIND_VERSION_MINOR 91)
endif(NOT Eigen3_FIND_VERSION_MINOR)
if(NOT Eigen3_FIND_VERSION_PATCH)
set(Eigen3_FIND_VERSION_PATCH 0)
endif(NOT Eigen3_FIND_VERSION_PATCH)
set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}")
endif(NOT Eigen3_FIND_VERSION)
macro(_eigen3_check_version)
file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header)
string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}")
set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}")
string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}")
set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}")
string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}")
set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}")
set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION})
if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
set(EIGEN3_VERSION_OK FALSE)
else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
set(EIGEN3_VERSION_OK TRUE)
endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
if(NOT EIGEN3_VERSION_OK)
message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, "
"but at least version ${Eigen3_FIND_VERSION} is required")
endif(NOT EIGEN3_VERSION_OK)
endmacro(_eigen3_check_version)
if (EIGEN3_INCLUDE_DIR)
# in cache already
_eigen3_check_version()
set(EIGEN3_FOUND ${EIGEN3_VERSION_OK})
else (EIGEN3_INCLUDE_DIR)
# search first if an Eigen3Config.cmake is available in the system,
# if successful this would set EIGEN3_INCLUDE_DIR and the rest of
# the script will work as usual
find_package(Eigen3 ${Eigen3_FIND_VERSION} NO_MODULE QUIET)
if(NOT EIGEN3_INCLUDE_DIR)
find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library
HINTS
ENV EIGEN3_ROOT
ENV EIGEN3_ROOT_DIR
PATHS
${CMAKE_INSTALL_PREFIX}/include
${KDE4_INCLUDE_DIR}
PATH_SUFFIXES eigen3 eigen
)
endif(NOT EIGEN3_INCLUDE_DIR)
if(EIGEN3_INCLUDE_DIR)
_eigen3_check_version()
endif(EIGEN3_INCLUDE_DIR)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK)
mark_as_advanced(EIGEN3_INCLUDE_DIR)
endif(EIGEN3_INCLUDE_DIR)
# Copyright (c) 2011-2018, The DART development contributors
# All rights reserved.
#
# The list of contributors can be found at:
# https://github.com/dartsim/dart/blob/master/LICENSE
#
# This file is provided under the "BSD-style" License
# Find NLOPT
#
# This sets the following variables:
# NLOPT_FOUND
# NLOPT_INCLUDE_DIRS
# NLOPT_LIBRARIES
# NLOPT_DEFINITIONS
# NLOPT_VERSION
# 2018 : SMALL MODIFICATIONS FROM R.P. romain dot pacanowski @ institutoptique DOT fr
find_package(PkgConfig QUIET)
# Check to see if pkgconfig is installed.
pkg_check_modules(PC_NLOPT nlopt QUIET)
# Definitions
set(NLOPT_DEFINITIONS ${PC_NLOPT_CFLAGS_OTHER})
# Include directories
find_path(NLOPT_INCLUDE_DIRS
NAMES nlopt.h
HINTS ${PC_NLOPT_INCLUDEDIR}
PATHS "${CMAKE_INSTALL_PREFIX}/include" "C:/Program Files/NLOPT/include" )
# Libraries
find_library(NLOPT_LIBRARIES
NAMES nlopt nlopt_cxx
HINTS ${PC_NLOPT_LIBDIR} "C:/Program Files/NLOPT/lib")
# Version
set(NLOPT_VERSION ${PC_NLOPT_VERSION})
# Set (NAME)_FOUND if all the variables and the version are satisfied.
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(NLOPT
FAIL_MESSAGE DEFAULT_MSG
REQUIRED_VARS NLOPT_INCLUDE_DIRS NLOPT_LIBRARIES
VERSION_VAR NLOPT_VERSION)
\ No newline at end of file
/*!
\page contribute Contribute
\page contribute ALTA Developer Guide
You can contribute and expand ALTA by providing new plugins. The three kind
of plugins (\ref function, \ref data and \ref fitter) can be provided.
Examples of such plugins can be found in the `$ALTA/sources/plugins`
directory.
You can contribute and expand ALTA by providing new plugins. The three kind of
plugins (\ref functions "functions", \ref datas "datas" and \ref fitters
"fitters") can be provided. Examples of such plugins can be found in the
`$ALTA/sources/plugins` directory. We advise to use source version control to
communicate with the other developpers.
ALTA has two repositories: one for active development (hosted on Inria’s
gitlab) and another for continuous integration and pull-requests (hosted on
github). We advise internal developpers to use the gitlab platform (for
development branches) and external contributors to make forks using github.
## Local Configuration
To work easily with both remote repositories in your local repository, you need
to setup them as different remotes. When doing your initial clone, we advise to
set the name corresponding to the remote you are using. For example, to clone
the gitlab instance, use the following command:
$ git clone -o gitlab https://gitlab.inria.fr/alta/alta.git
To add the github remote (once you have a running local repository), you will
need to do:
$ git remote add github https://github.com/belcour/alta.git
Once both remotes are set, we advise the fetch all the tags and names to see
the various developpement branches. However, you only need to access both
master (gitlab and github) to work. If you are an external developper, forking
the github repository is sufficient.
## Create a New Branch (internal only)
Development branches should be created on the gitlab remote if possible. We
advise to sync with the master branch first
$ git checkout gitlab/master
$ git checkout -b my_branch --track gitlab/my_branch
## Continuous Integration & Merge Request
Push finished branches on the github remote for continuous integration or
create a pull request there:
$ git push -f github my_branch
Then, one of the internal developper will merge on the master and close the
pull request.
## Sync (internal only)
Once your changes are merged into github’s master branch and all the continuous
integration tests are green, you can sync the two remotes together by
forwarding gitlab master on top of github master:
$ git checkout gitlab/master
$ git reset --hard github/master
$ git push -f gitlab master
<center style="color:red">
Work in progress
</center>
*/
......@@ -3,11 +3,12 @@
<tab type="mainpage" visible="yes" title="Overview"/>
<tab type="user" url="@ref features" title="Features"/>
<tab type="user" url="@ref install" title="Installation"/>
<tab type="user" url="@ref tutorials" title="Tutorials"/>
<tab type="user" url="@ref documentation" title="API &amp; References" />
<tab type="user" url="@ref tools" title="Tools"/>
<tab type="user" url="@ref data" title="Data"/>
<tab type="user" url="@ref license" title="License"/>
<tab type="user" url="@ref tutorials" title="Tutorials"/>
<tab type="user" url="@ref documentation" title="API &amp; References" />
<tab type="user" url="@ref tools" title="Tools"/>
<tab type="user" url="@ref data" title="Data"/>
<tab type="user" url="@ref license" title="License"/>
<tab type="user" url="@ref contribute" title="Contribute"/>
<tab type="user" url="@ref contacts" title="Contacts"/>
</navindex>
......
......@@ -22,6 +22,7 @@ And Python scripts using:
+ <a href="tutorial3.html">BRDF Data conversion</a>
+ <a href="tutorial2.html">BRDF Rational fitting</a>
+ <a href="tutorial1.html">BRDF Non-Linear fitting</a>
+ <a href="tutorial-export.html">Use BRDF data and functions</a>
### Tutorial using the XML interface
......@@ -306,6 +307,27 @@ compareRational.update();
\page tutorial-export Export BRDF data and functions
It is possible to use ALTA BRDF fit or BRDF data inside different rendering engines. In this tutorial, we will see different ways to use the outputs of ALTA in different softwares. You can find the list of tools and renderers that can be used using ALTA's data or function in the \ref tools "tools page".
## Using BRDF Explorer
Disney's [BRDF Explorer][brdf-explorer] is a very good tool to check fits and export a first render. The simplest way to use a data or function from ALTA in this software is to convert them into a MERL data object. For example, if we have a BRDF function file `data.brdf`, we can use the following command to convert it to a MERL data file:
$ brdf2data --input function.brdf --output data.binary --data data_merl
Once the file `data.binary` is created, it can be viewed inside BRDF Explorer and compared to another BRDF file or shader for example.
It is also possible to generate a BRDF Explorer shader from an ALTA function when the functionality is encoded in the function plugin using the following command:
$ brdf2brdf --input function.brdf --output function.shader --export explorer
This will generate `function.shader`, a BRDF Explorer compatible shader.
[brdf-explorer]: https://www.disneyanimation.com/technology/brdf.html
\page xml-tutorial1 Using the XML interface
ALTA \ref commands "commands" can be performed using an XML specification, to simplify sharing of fitting, conversion and analysis procedures. The following script performs the fitting of the <tt>blue-metallic-paint</tt> from the MERL database using a Beckmann lobe (note there is no shadowing term, nor Fresnel term):
......
Subproject commit f117a48ea2fd446d2865826a58d08027d4579cb3
......@@ -24,7 +24,7 @@
using namespace alta;
//#define DEBUG
//#define DEBUG_CORE
//! Add dynamic library extension (.so or .dll) to a dynamic object as well as
......
......@@ -22,7 +22,7 @@
#include <cmath>
#include <utility>
using namespace alta;
namespace alta {
/*! \ingroup datas
* \ingroup plugins
......@@ -56,7 +56,7 @@ public: //methods
: vertical_segment(params, size, input_data)
{ }
ASTM(const parameters& params, size_t size)
ASTM(const alta::parameters& params, size_t size)
: vertical_segment(params, size)
{ }
};
......@@ -159,10 +159,9 @@ ALTA_DLL_EXPORT data* load_data(std::istream& input, const arguments& args)
{
assert(input.good());
std::getline(input, line);
if(line.size() == 0 || line.rfind(',') == std::string::npos)
continue;
do {
std::getline(input, line);
} while(line.size() == 0 || line.rfind(',') == std::string::npos);
std::replace(line.begin(), line.end(), ',', ' ');
......@@ -172,7 +171,9 @@ ALTA_DLL_EXPORT data* load_data(std::istream& input, const arguments& args)
for(int j = 0; j < n; ++j) {
stream >> data[i + j];
}
}
}
return new ASTM(params, size, std::shared_ptr<double>(data));
}
return new ASTM(params, n, std::shared_ptr<double>(data));
}
......@@ -19,6 +19,7 @@
#include <core/data.h>
#include <core/args.h>
#include <core/common.h>
#include <core/params.h>
using namespace alta;
......
......@@ -19,15 +19,17 @@
#include <cmath>
#include <core/common.h>
#include <core/params.h>
using namespace alta;
ALTA_DLL_EXPORT function* provide_function(const parameters& params)
ALTA_DLL_EXPORT function* provide_function(const alta::parameters& params)
{
return new lafortune_function(params);
}
lafortune_function::lafortune_function(const parameters& params)
lafortune_function::lafortune_function(const alta::parameters& params) :
nonlinear_function(params)
{
auto nY = params.dimY();
......@@ -50,7 +52,8 @@ vec lafortune_function::operator()(const vec& x) const
}
vec lafortune_function::value(const vec& x) const
{
vec res(dimY());
const int nY = _parameters.dimY();
vec res(nY);
#ifdef ADAPT_TO_PARAM
vec y(6);
......@@ -69,7 +72,7 @@ vec lafortune_function::value(const vec& x) const
#endif
// For each color channel
for(int i=0; i<dimY(); ++i)
for(int i=0; i<nY; ++i)
{
// Start with the diffuse term
res[i] = _kd[i];
......@@ -97,7 +100,8 @@ vec lafortune_function::value(const vec& x, const vec& p)
assert(p.size() == nbParameters());
setParameters(p);
vec res(dimY());
const int nY = _parameters.dimY();
vec res(nY);
#ifdef ADAPT_TO_PARAM
vec y(6);
......@@ -116,7 +120,7 @@ vec lafortune_function::value(const vec& x, const vec& p)
#endif
// For each lobe and for each color channel
for(int i=0; i<dimY(); ++i)
for(int i=0; i<nY; ++i)
{
// Start with the diffuse
res[i] = _kd[i];
......@@ -140,6 +144,7 @@ vec lafortune_function::value(const vec& x, const vec& p)
void lafortune_function::setNbLobes(int N)
{
_n = N;
const int _nY = _parameters.dimY();
// Update the length of the vectors
if(_isotropic)
......@@ -152,47 +157,49 @@ void lafortune_function::setNbLobes(int N)
//! Number of parameters to this non-linear function
int lafortune_function::nbParameters() const
{
const int nY = _parameters.dimY();
#ifdef FIT_DIFFUSE
if(_isotropic)
return (3*_n+1)*dimY();
return (3*_n+1)*nY;
else
return (4*_n+1)*dimY();
return (4*_n+1)*nY;
#else
if(_isotropic)
return (3*_n)*dimY();
return (3*_n)*nY;
else
return (4*_n)*dimY();
return (4*_n)*nY;
#endif
}
//! Get the vector of parameters for the function
vec lafortune_function::parameters() const
{
const int nY = _parameters.dimY();
vec res(nbParameters());
for(int n=0; n<_n; ++n)
for(int i=0; i<dimY(); ++i)
for(int i=0; i<nY; ++i)
{
if(_isotropic)
{
res[(n*dimY() + i)*3 + 0] = _C[(n*dimY() + i)*2 + 0];
res[(n*dimY() + i)*3 + 1] = _C[(n*dimY() + i)*2 + 1];
res[(n*dimY() + i)*3 + 2] = _N[n*dimY() + i];
res[(n*nY + i)*3 + 0] = _C[(n*nY + i)*2 + 0];
res[(n*nY + i)*3 + 1] = _C[(n*nY + i)*2 + 1];
res[(n*nY + i)*3 + 2] = _N[n*nY + i];
}
else
{
res[(n*dimY() + i)*4 + 0] = _C[(n*dimY() + i)*3 + 0];
res[(n*dimY() + i)*4 + 1] = _C[(n*dimY() + i)*3 + 1];
res[(n*dimY() + i)*4 + 2] = _C[(n*dimY() + i)*3 + 2];
res[(n*dimY() + i)*4 + 3] = _N[n*dimY() + i];
res[(n*nY + i)*4 + 0] = _C[(n*nY + i)*3 + 0];
res[(n*nY + i)*4 + 1] = _C[(n*nY + i)*3 + 1];
res[(n*nY + i)*4 + 2] = _C[(n*nY + i)*3 + 2];
res[(n*nY + i)*4 + 3] = _N[n*nY + i];
}
}
#ifdef FIT_DIFFUSE
for(int i=0; i<dimY(); ++i)
for(int i=0; i<nY; ++i)
{
if(_isotropic)
{
res[3*_n*dimY() + i] = _kd[i];
res[3*_n*nY + i] = _kd[i];
}
}
#endif
......@@ -202,21 +209,22 @@ vec lafortune_function::parameters() const
//! Update the vector of parameters for the function
void lafortune_function::setParameters(const vec& p)
{
const int nY = _parameters.dimY();
// Safety check the number of parameters
assert(p.size() == nbParameters());
for(int n=0; n<_n; ++n)
for(int i=0; i<dimY(); ++i)
for(int i=0; i<nY; ++i)
{
_C[(n*dimY() + i)*3 + 0] = p[(n*dimY() + i)*4 + 0];
_C[(n*dimY() + i)*3 + 1] = p[(n*dimY() + i)*4 + 1];
_C[(n*dimY() + i)*3 + 2] = p[(n*dimY() + i)*4 + 2];
_N[n*dimY() + i] = p[(n*dimY() + i)*4 + 3];
_C[(n*nY + i)*3 + 0] = p[(n*nY + i)*4 + 0];
_C[(n*nY + i)*3 + 1] = p[(n*nY + i)*4 + 1];
_C[(n*nY + i)*3 + 2] = p[(n*nY + i)*4 + 2];
_N[n*nY + i] = p[(n*nY + i)*4 + 3];
}
#ifdef FIT_DIFFUSE
for(int i=0; i<dimY(); ++i)
for(int i=0; i<nY; ++i)
{
_kd[i] = p[4*_n*dimY() + i];
_kd[i] = p[4*_n*nY + i];
}
#endif
}
......@@ -225,6 +233,7 @@ void lafortune_function::setParameters(const vec& p)
//! parameters.
vec lafortune_function::parametersJacobian(const vec& x) const
{
const int nY = _parameters.dimY();
#ifdef ADAPT_TO_PARAM
vec y(6);
......@@ -242,14 +251,14 @@ vec lafortune_function::parametersJacobian(const vec& x) const
dz = x[2]*x[5];
#endif
vec jac(dimY()*nbParameters());
for(int i=0; i<dimY(); ++i)
vec jac(nY*nbParameters());
for(int i=0; i<nY; ++i)
{
for(int n=0; n<_n; ++n)
for(int j=0; j<dimY(); ++j)
for(int j=0; j<nY; ++j)
{
// index of the current monochromatic lobe
int index = i*nbParameters() + 4*(n*dimY() + j);
int index = i*nbParameters() + 4*(n*nY + j);
double Cx, Cy, Cz, N;
getCurrentLobe(n, j, Cx, Cy, Cz, N);
......@@ -283,10 +292,10 @@ vec lafortune_function::parametersJacobian(const vec& x) const
}
#ifdef FIT_DIFFUSE
for(int j=0; j<dimY(); ++j)
for(int j=0; j<nY; ++j)
{
// index of the current monochromatic lobe
int index = i*nbParameters() + 4*_n*dimY() + j;
int index = i*nbParameters() + 4*_n*nY + j;
jac[index] = 1.0;
}
......@@ -298,31 +307,33 @@ vec lafortune_function::parametersJacobian(const vec& x) const
void lafortune_function::bootstrap(const ptr<data> d, const arguments& args)
{
const int nY = _parameters.dimY();
// Check the arguments for the number of lobes
this->setNbLobes(args.get_int("lobes", 1));
// Set the diffuse component
vec x0 = d->get(0);
for(int i=0; i<d->dimY(); ++i)
_kd[i] = x0[d->dimX() + i];
for(int i=0; i<d->parametrization().dimY(); ++i)
_kd[i] = x0[d->parametrization().dimX() + i];
for(int i=1; i<d->size(); ++i)
{
vec xi = d->get(i);
for(int j=0; j<d->dimY(); ++j)
_kd[j] = std::min(xi[d->dimX() + j], _kd[j]);
for(int j=0; j<d->parametrization().dimY(); ++j)
_kd[j] = std::min(xi[d->parametrization().dimX() + j], _kd[j]);
}
// The remaining data will be equal to one
for(int n=0; n<_n; ++n)
for(int i=0; i<dimY(); ++i)
for(int i=0; i<_parameters.dimY(); ++i)
{
double theta = 0.5 * M_PI * n / (double)_n;
_C[(n*dimY() + i)*3 + 0] = -sin(theta);
_C[(n*dimY() + i)*3 + 1] = -sin(theta);
_C[(n*dimY() + i)*3 + 2] = cos(theta);
_N[n*dimY() + i] = (double)_n;
_C[(n*nY + i)*3 + 0] = -sin(theta);
_C[(n*nY + i)*3 + 1] = -sin(theta);
_C[(n*nY + i)*3 + 2] = cos(theta);
_N[n*nY + i] = (double)_n;
}
}
......@@ -396,6 +407,7 @@ bool lafortune_function::load(std::istream& in)
setNbLobes(nb_lobes);
// Parse the lobe
const int _nY = _parameters.dimY();
for(int n=0; n<_n; ++n)
{
for(int i=0; i<_nY; ++i)
......@@ -418,6 +430,7 @@ void lafortune_function::save_call(std::ostream& out, const arguments& args) con
{
bool is_alta = !args.is_defined("export") || args["export"] == "alta";
const int _nY = _parameters.dimY();
if(is_alta)
{
out << "#FUNC nonlinear_function_lafortune" << std::endl ;
......
......@@ -21,6 +21,7 @@
#include <core/fitter.h>
#include <core/args.h>
#include <core/common.h>
#include <core/params.h>
using namespace alta;
......@@ -44,7 +45,7 @@ class lafortune_function : public nonlinear_function
public: // methods
lafortune_function(const parameters& params);
lafortune_function(const alta::parameters& params);
// Overload the function operator
virtual vec operator()(const vec& x) const ;
......@@ -103,6 +104,7 @@ class lafortune_function : public nonlinear_function
//! n for the color channel number c.
void getCurrentLobe(int n, int c, double& Cx, double& Cy, double& Cz, double& N) const
{
const int _nY = _parameters.dimY();
if(_isotropic)
{
Cx = _C[(n*_nY + c)*2 + 0];
......@@ -128,11 +130,5 @@ class lafortune_function : public nonlinear_function
//!\brief Flags to get an isotropic lobe
bool _isotropic;
lafortune_function()
: nonlinear_function(6, 0,
params::CARTESIAN, params::UNKNOWN_OUTPUT)
{
};
} ;
......@@ -22,18 +22,18 @@
using namespace alta;
ALTA_DLL_EXPORT function* provide_function(const parameters& params)
ALTA_DLL_EXPORT function* provide_function(const alta::parameters& params)
{
return new spherical_gaussian_function(params);
}
spherical_gaussian_function::spherical_gaussian_function(const parameters& params)
spherical_gaussian_function::spherical_gaussian_function(const alta::parameters& params)
: nonlinear_function(params.set_input(6, params::CARTESIAN)),
_non_a(1), _type(Mirror)
_a(1.0), _type(Mirror)
{
// Update the length of the vectors
_n.resize(_nY) ;
_ks.resize(_nY) ;
_n.resize(_parameters.dimY()) ;
_ks.resize(_parameters.dimY()) ;
}
// Overload the function operator
......@@ -43,10 +43,10 @@ vec spherical_gaussian_function::operator()(const vec& x) const
}
vec spherical_gaussian_function::value(const vec& x) const
{
vec res(dimY());
vec res(_parameters.dimY());
double dot = compute_dot(x);
for(int i=0; i<dimY(); ++i)
for(int i=0; i<_parameters.dimY(); ++i)
{
res[i] = _ks[i] * std::exp(_n[i] * (dot-1));
}
......@@ -95,22 +95,23 @@ int spherical_gaussian_function::nbParameters() const
{
if(_type == Moment)
{
return 2*dimY()+1;
return 2*_parameters.dimY()+1;
}
else
{
return 2*dimY();
return 2*_parameters.dimY();
}
}
//! Get the vector of parameters for the function
vec spherical_gaussian_function::parameters() const
{
const int nY = _parameters.dimY();
if(_type == Moment)
{
vec res(2*dimY()+1);
res[2*dimY()] = _a;
for(int i=0; i<dimY(); ++i)
vec res(2*nY+1);
res[2*nY] = _a;
for(int i=0; i<nY; ++i)
{
res[i*2 + 0] = _ks[i];
res[i*2 + 1] = _n[i];
......@@ -120,8 +121,8 @@ vec spherical_gaussian_function::parameters() const
}
else
{
vec res(2*dimY());
for(int i=0; i<dimY(); ++i)
vec res(2*nY);
for(int i=0; i<nY; ++i)
{
res[i*2 + 0] = _ks[i];
res[i*2 + 1] = _n[i];
......@@ -133,11 +134,12 @@ vec spherical_gaussian_function::parameters() const
//! \brief get the min values for the parameters
vec spherical_gaussian_function::getParametersMin() const
{
const int nY = _parameters.dimY();
if(_type == Moment)
{
vec res(2*dimY()+1);
res[2*dimY()] = 0.0;
for(int i=0; i<dimY(); ++i)
vec res(2*nY+1);
res[2*nY] = 0.0;
for(int i=0; i<nY; ++i)
{
res[i*2 + 0] = 0.0;
res[i*2 + 1] = 0.0;
......@@ -147,8 +149,8 @@ vec spherical_gaussian_function::getParametersMin() const
}
else
{
vec res(2*dimY());
for(int i=0; i<dimY(); ++i)
vec res(2*nY);
for(int i=0; i<nY; ++i)
{
res[i*2 + 0] = 0.0;
res[i*2 + 1] = 0.0;
......@@ -163,10 +165,10 @@ void spherical_gaussian_function::setParameters(const vec& p)
{
if(_type == Moment)
{
_a = p[2*dimY()];
_a = p[2*_parameters.dimY()];
}
for(int i=0; i<dimY(); ++i)
for(int i=0; i<_parameters.dimY(); ++i)
{
_ks[i] = p[i*2 + 0];
_n[i] = p[i*2 + 1];
......@@ -179,10 +181,10 @@ vec spherical_gaussian_function::parametersJacobian(const vec& x) const
{
double dot = compute_dot(x);
vec jac(dimY()*nbParameters());
for(int i=0; i<dimY(); ++i)
vec jac(_parameters.dimY()*nbParameters());
for(int i=0; i<_parameters.dimY(); ++i)
{
for(int j=0; j<dimY(); ++j)
for(int j=0; j<_parameters.dimY(); ++j)
{
if(i == j)
{
......@@ -221,7 +223,7 @@ vec spherical_gaussian_function::parametersJacobian(const vec& x) const
void spherical_gaussian_function::bootstrap(const ptr<data> d, const arguments& args)
{
for(int i=0; i<dimY(); ++i)
for(int i=0; i<_parameters.dimY(); ++i)
{
_ks[i] = 1.0;
_n[i] = 1.0;
......@@ -287,7 +289,7 @@ bool spherical_gaussian_function::load(std::istream& in)
}
// Parse the lobe
for(int i=0; i<_nY; ++i)
for(int i=0; i<_parameters.dimY(); ++i)
{
in >> token >> _ks[i];
......@@ -309,11 +311,12 @@ void spherical_gaussian_function::save_call(std::ostream& out, const arguments&
{
bool is_alta = !args.is_defined("export") || args["export"] == "alta";
const int nY = _parameters.dimY();
if(is_alta)
{
out << "#FUNC nonlinear_function_spherical_gaussian" << std::endl ;
for(int i=0; i<_nY; ++i)
for(int i=0; i<nY; ++i)
{
out << "Ks " << _ks[i] << std::endl;
out << "N " << _n[i] << std::endl;
......@@ -329,17 +332,17 @@ void spherical_gaussian_function::save_call(std::ostream& out, const arguments&
else
{
out << "spherical_gaussian(L, V, N, X, Y, vec3(";
for(int i=0; i<_nY; ++i)
for(int i=0; i<nY; ++i)
{
out << _ks[i];
if(i < _nY-1) { out << ", "; }
if(i < nY-1) { out << ", "; }
}
out << "), vec3(";
for(int i=0; i<_nY; ++i)
for(int i=0; i<nY; ++i)
{
out << _n[i];
if(i < _nY-1) { out << ", "; }
if(i < nY-1) { out << ", "; }
}
out << "))";
......
......@@ -20,6 +20,7 @@
#include <core/fitter.h>
#include <core/args.h>
#include <core/common.h>
#include <core/params.h>
using namespace alta;
......@@ -56,7 +57,7 @@ class spherical_gaussian_function : public nonlinear_function
Moment
};
spherical_gaussian_function(const parameters& params);
spherical_gaussian_function(const alta::parameters& params);
// Overload the function operator
virtual vec operator()(const vec& x) const ;
......
......@@ -18,15 +18,16 @@
#include <cmath>
#include <core/common.h>
#include <core/params.h>
using namespace alta;
ALTA_DLL_EXPORT function* provide_function(const parameters& params)
ALTA_DLL_EXPORT function* provide_function(const alta::parameters& params)
{
return new schlick_masking(params);
}
schlick_masking::schlick_masking(const parameters& params)
schlick_masking::schlick_masking(const alta::parameters& params)
: nonlinear_function(params.set_input(6, params::CARTESIAN))
{
w.resize(params.dimY());
......@@ -66,7 +67,7 @@ bool schlick_masking::load(std::istream& in)
}
// R [double]
for(int i=0; i<dimY(); ++i)
for(int i=0; i<_parameters.dimY(); ++i)
{
in >> token >> w[i];
}
......@@ -80,7 +81,7 @@ void schlick_masking::save_call(std::ostream& out, const arguments& args) const
if(is_alta)
{
out << "#FUNC nonlinear_masking_schlick" << std::endl ;
for(int i=0; i<dimY(); ++i)
for(int i=0; i<_parameters.dimY(); ++i)
{
out << "K " << w[i] << std::endl;
}
......@@ -89,10 +90,10 @@ void schlick_masking::save_call(std::ostream& out, const arguments& args) const
else
{
out << "masking_schlick(L, V, N, X, Y, vec3";
for(int i=0; i<dimY(); ++i)
for(int i=0; i<_parameters.dimY(); ++i)
{
out << w[i];
if(i < _nY-1) { out << ", "; }
if(i < _parameters.dimY()-1) { out << ", "; }
}
out << "))";
}
......@@ -122,11 +123,11 @@ void schlick_masking::save_body(std::ostream& out, const arguments& args) const
vec schlick_masking::value(const vec& x) const
{
vec res(dimY());
vec res(_parameters.dimY());
const double u = x[5];
const double v = x[2];
for(int i=0; i<dimY(); ++i)
for(int i=0; i<_parameters.dimY(); ++i)
{
const double Gu = u / (u + w[i] * (1.0 - u));
const double Gv = v / (v + w[i] * (1.0 - v));
......@@ -138,35 +139,35 @@ vec schlick_masking::value(const vec& x) const
//! \brief Number of parameters to this non-linear function
int schlick_masking::nbParameters() const
{
return dimY();
return _parameters.dimY();
}
vec schlick_masking::getParametersMin() const
{
vec m(dimY());
for(int i=0; i<dimY(); ++i) { m[i] = 0.0; }
vec m(_parameters.dimY());
for(int i=0; i<_parameters.dimY(); ++i) { m[i] = 0.0; }
return m;
}
//! \brief Get the vector of parameters for the function
vec schlick_masking::parameters() const
{
vec p(dimY());
for(int i=0; i<dimY(); ++i) { p[i] = w[i]; }
vec p(_parameters.dimY());
for(int i=0; i<_parameters.dimY(); ++i) { p[i] = w[i]; }
return p;
}
//! \brief Update the vector of parameters for the function
void schlick_masking::setParameters(const vec& p)
{
for(int i=0; i<dimY(); ++i) { w[i] = p[i]; }
for(int i=0; i<_parameters.dimY(); ++i) { w[i] = p[i]; }
}
//! \brief Obtain the derivatives of the function with respect to the
//! parameters.
vec schlick_masking::parametersJacobian(const vec& x) const
{
const int nY = dimY();
const int nY = _parameters.dimY();
vec jac(nY*nY);
const double u = x[5];
......@@ -184,11 +185,11 @@ vec schlick_masking::parametersJacobian(const vec& x) const
const double dGu = - u*(1.0 - u) / pow(u + w[i]*(1.0-u), 2);
const double dGv = - v*(1.0 - v) / pow(v + w[i]*(1.0-v), 2);
jac[j*dimY() + i] = Gu*dGv + Gv*dGu;
jac[j*_parameters.dimY() + i] = Gu*dGv + Gv*dGu;
}
else
{
jac[j*dimY() + i] = 0.0;
jac[j*_parameters.dimY() + i] = 0.0;
}
}
......@@ -199,5 +200,5 @@ vec schlick_masking::parametersJacobian(const vec& x) const
void schlick_masking::bootstrap(const ptr<data>, const arguments&)
{
// Start with a non occluding value for k
for(int i=0; i<dimY(); ++i) { w[i] = 0.0; }
for(int i=0; i<_parameters.dimY(); ++i) { w[i] = 0.0; }
}
......@@ -33,7 +33,7 @@ class schlick_masking : public nonlinear_function
public: // methods
schlick_masking(const parameters& params);
schlick_masking(const alta::parameters& params);
//! \brief Load function specific files
virtual bool load(std::istream& in) ;
......@@ -69,7 +69,6 @@ class schlick_masking : public nonlinear_function
private: // data
//! Fresnel reflectance at theta = 0
vec w;
schlick_masking() {};
vec w;
} ;
......@@ -66,8 +66,6 @@ class smith : public nonlinear_function//fresnel
//! Fresnel reflectance at theta = 0 ?
//! RP: I DOUBLT IT . Seems to be a brute copy and paste ???
//! RP: w^2 should be the mean-square surface slope
vec w;
smith() {};
vec w;
} ;
......@@ -5,29 +5,32 @@ Import('env', 'library_available')
env = env.Clone()
env.Prepend(LIBS = ['core'])
## Add pybind path
env.AppendUnique(CPPPATH = '#external/pybind11/include/')
## Building a test function for boost::python
##
bp_test_source = """
// STL and Boost includes
#include <memory>
#include <boost/python.hpp>
#include <pybind11/pybind11.h>
// ALTA includes
#include <core/common.h>
#include <core/ptr.h>
#include <core/function.h>
int main(int argc, char** argv) {
boost::python::class_<alta::function, alta::ptr<alta::function>, boost::noncopyable>("function", boost::python::no_init);
boost::python::register_ptr_to_python<alta::ptr<alta::function>>();
namespace py = pybind11;
return 0;
PYBIND11_MODULE(example, m) {
m.doc() = "pybind11 example plugin"; // optional module docstring
m.def("norm", &norm, "Compute the norm of vector 'v'", py::arg("v"));
}
"""
def CheckBoostPython(context):
context.Message('Checking boost::python using small example...')
result = context.TryLink(bp_test_source, '.cpp')
result = context.TryCompile(bp_test_source, '.cpp')
context.Result(result)
return result
......@@ -41,16 +44,6 @@ if library_available(env, pkgspec='python-2.7',
lib='PYTHON_LIB', header='Python.h'):
conf = Configure(env, custom_tests = {'CheckBoostPython' : CheckBoostPython})
# On GNU/Linux the '-mt' suffix is no longer used, but it is still
# used on some other platforms (see
# <http://stackoverflow.com/questions/2293962/boost-libraries-in-multithreading-aware-mode>.)
build_lib = conf.CheckLibWithHeader('boost_python-mt',
'boost/python.hpp', 'c++')
if not build_lib:
build_lib = conf.CheckLibWithHeader('boost_python',
'boost/python.hpp', 'c++')
build_lib = conf.CheckBoostPython();
env = conf.Finish()
......
......@@ -2,6 +2,7 @@
Copyright (C) 2014, 2015, 2016 Inria
Copyright (C) 2015 Université de Montréal
Copyright (C) 2018 Unity
This file is part of ALTA.
......@@ -9,8 +10,9 @@
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/. */
// Boost includes
#include <boost/python.hpp>
// Pybind11 includes
#include <pybind11/pybind11.h>
namespace py = pybind11;
// ALTA include
#include <core/common.h>
......@@ -25,57 +27,13 @@
#include <iostream>
// Local includes
#include "wrapper_vec.hpp"
#define bp boost::python
#include "wrapper_args.h"
#include "wrapper_vec.h"
#include "wrapper_data.h"
using namespace alta;
/* This class is a wrapper to ALTA's arguments class to add Python specific
* behaviour such as dictionnary initialization.
*
* Right now it does not handle automatic conversion in function call for
* example. The following code is not possible:
*
* import alta
* alta.get_data('data_merl', {'params' : 'STARK_2D'})
*
* Instead, one has to construct the arguments object from the ALTA library
* to use it afterwards:
*
* import alta
* args = alta.arguments({'params' : 'STARK_2D'})
* alta.get_data('data_merl', args)
*/
struct python_arguments : public arguments {
python_arguments() : arguments() {}
python_arguments(bp::dict d) : arguments() {
bp::list keys = d.keys();
for(int i=0; i<bp::len(keys); ++i) {
const std::string s_key = bp::extract<std::string>(keys[i]);
const std::string s_val = bp::extract<std::string>(d[keys[i]]);
this->update(s_key, s_val);
}
}
};
/* Create a data object from a plugin's name and the data filename. This
* function is here to accelerate the loading of data file.
*/
static ptr<data> load_data(const std::string& plugin_name, const std::string& filename) {
ptr<data> d = plugins_manager::load_data(filename, plugin_name);
return d;
}
static ptr<data> get_data_with_args(const std::string& plugin_name,
size_t size,
const parameters& params,
const python_arguments& args) {
return plugins_manager::get_data(plugin_name, size, params, args);
}
static ptr<data> get_data(const std::string& plugin_name, size_t size,
const parameters& params) {
return plugins_manager::get_data(plugin_name, size, params);
}
/* Creating functions for the plugins_manager calls
*
......@@ -335,137 +293,110 @@ static void brdf2data(const ptr<function>& f, ptr<data>& d) {
/* Compute distance metric between 'in' and 'ref'.
*/
static bp::dict data2stats(const ptr<data>& in, const ptr<data>& ref) {
static py::dict data2stats(const ptr<data>& in, const ptr<data>& ref) {
// Compute the metrics
errors::metrics res;
errors::compute(in.get(), ref.get(), nullptr, res);
// Fill the resulting Python vector
bp::dict py_res;
py::dict py_res;
for(auto rpair : res) {
py_res.setdefault<std::string, vec>(rpair.first, rpair.second);
py_res[py::str(rpair.first)] = rpair.second;
}
return py_res;
}
inline void register_function(py::module& m) {
py::class_<function, ptr<function>>(m, "function")
.def("__add__", &add_function)
.def("__mul__", &mult_function)
.def("__rmul__", &mult_function)
.def("value", &function::value)
.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);
m.def("get_function", get_function, py::arg("name") = "nonlinear_diffuse",
py::arg("param") = parameters(6, 3, params::CARTESIAN, params::RGB_COLOR));
m.def("get_function", get_function_from_args);
m.def("load_function", load_function);
m.def("load_function", load_function_with_args);
}
inline void register_fitter(py::module& m) {
py::class_<fitter, ptr<fitter>>(m, "fitter")
.def("fit_data", &fit_data_with_args)
.def("fit_data", &fit_data_without_args);
m.def("get_fitter", plugins_manager::get_fitter);
}
#define STRINGIFY_(x) #x
#define STRINGIFY(x) STRINGIFY_(x)
/* Exporting the ALTA module
*/
BOOST_PYTHON_MODULE(alta)
{
// Argument class
//
bp::class_<arguments>("_arguments");
bp::class_<python_arguments, bp::bases<arguments>>("arguments")
.def(bp::init<>())
.def(bp::init<bp::dict>())
.def("__getitem__", &arguments::operator[])
.def("update", &arguments::update);
PYBIND11_MODULE(alta, m) {
m.doc() = "ALTA python bindinds";
m.def("norm", &norm, "Compute the norm of vector 'v'", py::arg("v"));
// Vec class
//
register_wrapper_vec();
// 'parameters' class.
bp::class_<parameters>("parameters")
.def(bp::init<unsigned int, unsigned int, params::input, params::output>());
// Parameterization enums.
/* Register the 'parameters' class and the enums */
py::class_<parameters>(m, "parameters")
.def(py::init<unsigned int, unsigned int, params::input, params::output>());
#define PARAM_VALUE(name) \
.value(STRINGIFY(name), params:: name)
bp::enum_<params::input>("input_parametrization")
PARAM_VALUE(RUSIN_TH_PH_TD_PD)
PARAM_VALUE(RUSIN_TH_PH_TD)
PARAM_VALUE(RUSIN_TH_TD_PD)
PARAM_VALUE(RUSIN_TH_TD)
PARAM_VALUE(RUSIN_VH_VD)
PARAM_VALUE(RUSIN_VH)
PARAM_VALUE(COS_TH_TD)
PARAM_VALUE(COS_TH)
PARAM_VALUE(SCHLICK_TK_PK)
PARAM_VALUE(SCHLICK_VK)
PARAM_VALUE(SCHLICK_TL_TK_PROJ_DPHI)
PARAM_VALUE(COS_TK)
PARAM_VALUE(RETRO_TL_TVL_PROJ_DPHI)
PARAM_VALUE(STEREOGRAPHIC)
PARAM_VALUE(SPHERICAL_TL_PL_TV_PV)
PARAM_VALUE(COS_TLV)
PARAM_VALUE(COS_TLR)
PARAM_VALUE(ISOTROPIC_TV_TL)
PARAM_VALUE(ISOTROPIC_TV_TL_DPHI)
PARAM_VALUE(ISOTROPIC_TV_PROJ_DPHI)
PARAM_VALUE(ISOTROPIC_TL_TV_PROJ_DPHI)
PARAM_VALUE(ISOTROPIC_TD_PD)
PARAM_VALUE(STARK_2D)
PARAM_VALUE(STARK_3D)
PARAM_VALUE(NEUMANN_2D)
PARAM_VALUE(NEUMANN_3D)
PARAM_VALUE(CARTESIAN)
PARAM_VALUE(UNKNOWN_INPUT);
bp::enum_<params::output>("output_parametrization")
PARAM_VALUE(INV_STERADIAN)
PARAM_VALUE(INV_STERADIAN_COSINE_FACTOR)
PARAM_VALUE(ENERGY)
PARAM_VALUE(RGB_COLOR)
PARAM_VALUE(XYZ_COLOR)
PARAM_VALUE(UNKNOWN_OUTPUT);
py::enum_<params::input>(m, "input_parametrization")
PARAM_VALUE(RUSIN_TH_PH_TD_PD)
PARAM_VALUE(RUSIN_TH_PH_TD)
PARAM_VALUE(RUSIN_TH_TD_PD)
PARAM_VALUE(RUSIN_TH_TD)
PARAM_VALUE(RUSIN_VH_VD)
PARAM_VALUE(RUSIN_VH)
PARAM_VALUE(COS_TH_TD)
PARAM_VALUE(COS_TH)
PARAM_VALUE(SCHLICK_TK_PK)
PARAM_VALUE(SCHLICK_VK)
PARAM_VALUE(SCHLICK_TL_TK_PROJ_DPHI)
PARAM_VALUE(COS_TK)
PARAM_VALUE(RETRO_TL_TVL_PROJ_DPHI)
PARAM_VALUE(STEREOGRAPHIC)
PARAM_VALUE(SPHERICAL_TL_PL_TV_PV)
PARAM_VALUE(COS_TLV)
PARAM_VALUE(COS_TLR)
PARAM_VALUE(ISOTROPIC_TV_TL)
PARAM_VALUE(ISOTROPIC_TV_TL_DPHI)
PARAM_VALUE(ISOTROPIC_TV_PROJ_DPHI)
PARAM_VALUE(ISOTROPIC_TL_TV_PROJ_DPHI)
PARAM_VALUE(ISOTROPIC_TD_PD)
PARAM_VALUE(STARK_2D)
PARAM_VALUE(STARK_3D)
PARAM_VALUE(NEUMANN_2D)
PARAM_VALUE(NEUMANN_3D)
PARAM_VALUE(CARTESIAN)
PARAM_VALUE(UNKNOWN_INPUT);
py::enum_<params::output>(m, "output_parametrization")
PARAM_VALUE(INV_STERADIAN)
PARAM_VALUE(INV_STERADIAN_COSINE_FACTOR)
PARAM_VALUE(ENERGY)
PARAM_VALUE(RGB_COLOR)
PARAM_VALUE(XYZ_COLOR)
PARAM_VALUE(UNKNOWN_OUTPUT);
#undef PARAM_VALUE
bp::register_ptr_to_python<ptr<parameters>>();