Commit 1af38761 authored by Laurent Belcour's avatar Laurent Belcour

Merged branch wip-tinyexr into master

parents 63aa6840 55788034
[submodule "external/tinyexr"]
path = external/tinyexr
url = https://github.com/syoyo/tinyexr.git
......@@ -75,7 +75,7 @@ vars.Add('INSTALL_PREFIX', 'Parent installation directory',
vars.Add('CXX', 'C++ compiler',
default = program_file_name(cxx_compilers))
vars.Add('CCFLAGS', 'Compiler\'s flags',
default = '-std=c++11 -g -O2 -Wall')
default = '-std=c++11 -g -Wall')
vars.Add('LINKFLAGS', 'Linker\'s flags',
default = '')
vars.Add('EIGEN_INC', 'Eigen include directory (mandatory)')
......@@ -191,12 +191,17 @@ def library_available(env, pkgspec='', lib='', header='',
return result
def openexr_available(env):
"""Return True if OpenEXR is available."""
return library_available(env, pkgspec='OpenEXR',
inc_var='OPENEXR_INC',
lib_var='OPENEXR_DIR',
lib='OPENEXR_LIB',
header='ImfRgbaFile.h')
"""Return True if OpenEXR is available."""
env.AppendUnique(CPPPATH = '#external')
conf = Configure(env)
has_tinyexr = conf.CheckCXXHeader('tinyexr/tinyexr.h')
conf.Finish()
return has_tinyexr
#return library_available(env, pkgspec='OpenEXR',
# inc_var='OPENEXR_INC',
# lib_var='OPENEXR_DIR',
# lib='OPENEXR_LIB',
# header='ImfRgbaFile.h')
def CheckOpenMP(context):
"""
......
......@@ -17,6 +17,6 @@ The following people have been involved in ALTA development:
</ul>
More information on the project are available on the
<a href="https://gforge.inria.fr/projects/alta/">Gforge project webpage</a>
<a href="https://gitlab.inria.fr/alta/alta">Inria Gitlab project</a>.
*/
......@@ -5,7 +5,7 @@
To access the sources of ALTA, clone the public [Git][git] repository:
$ git clone https://gforge.inria.fr/git/alta/alta.git
$ git clone https://gitlab.inria.fr/alta/alta.git
## Dependencies
......
......@@ -39,7 +39,7 @@ ALTA is free software available under the [Mozilla Public License,
version 2.0](http://mozilla.org/MPL/2.0/). The latest release, version
0.2, can be obtained from the Git repository:
$ git clone https://gforge.inria.fr/git/alta/alta.git
$ git clone https://gitlab.inria.fr/alta/alta.git
$ git checkout v0.2
See \ref install "the installation instruction" for more information.
......
Subproject commit 46d5063d69d0264ccb200c50ae26b03d64ccd85e
/* ALTA --- Analysis of Bidirectional Reflectance Distribution Functions
Copyright (C) 2014, 2015 Universtie de Montreal
Copyright (C) 2013, 2014 Inria
Copyright (C) 2013, 2014, 2017 Inria
This file is part of ALTA.
......@@ -14,6 +14,7 @@
#include <string>
#include <sstream>
#include <map>
#include <initializer_list>
#include <cstdlib>
#include <iostream>
#include <cctype>
......@@ -33,12 +34,20 @@ namespace alta {
*/
class arguments
{
private:
typedef std::pair<const std::string, std::string> pair_type;
public: // functions
// Constructor and destructor
arguments()
{
}
arguments(std::initializer_list<pair_type> lst)
: _map(lst)
{
}
arguments(int argc, char** const argv)
{
for(int i=0; i<argc; ++i)
......
/* ALTA --- Analysis of Bidirectional Reflectance Distribution Functions
Copyright (C) 2014 CNRS
Copyright (C) 2013, 2014, 2015, 2016 Inria
Copyright (C) 2013, 2014, 2015, 2016, 2017 Inria
This file is part of ALTA.
......@@ -38,6 +38,8 @@
typedef Eigen::VectorXd vec;
typedef Eigen::Ref<vec> vecref;
typedef Eigen::Ref<const vec> const_vecref;
typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
RowMatrixXd;
// Convenience functions.
static inline double norm(const vec& v)
......
/* ALTA --- Analysis of Bidirectional Reflectance Distribution Functions
Copyright (C) 2013, 2014, 2015, 2016 Inria
Copyright (C) 2013, 2014, 2015, 2016, 2017 Inria
This file is part of ALTA.
......@@ -63,8 +63,8 @@ static bool cosine_correction(vecref v, unsigned int dimX, unsigned int dimY,
static bool within_bounds(const vecref v,
const vec& min, const vec& max)
{
return (v.array() < min.array()).all()
&& (v.array() > max.array()).all();
return (v.array() >= min.array()).all()
&& (v.array() <= max.array()).all();
}
// Return the 'ci_kind' value corresponding to VS_VALUE, an integer found in
......@@ -115,7 +115,7 @@ static void read_confidence_interval(std::istream& input,
else
{
// Confidence interval data not provided in INPUT.
double dt = args.get_float("dt", 0.1f);
double dt = args.get_double("dt", 0.1d);
min_dt = -dt;
max_dt = dt;
}
......@@ -153,12 +153,9 @@ static void read_confidence_interval(std::istream& input,
}
alta::data* alta::load_data_from_text(std::istream& input,
const alta::arguments& header)
const alta::arguments& header,
const alta::arguments& args)
{
// FIXME: Eventually reinstate support for extra arguments when loading a
// file.
static alta::arguments args;
vec min, max ;
vec ymin, ymax;
......@@ -217,25 +214,26 @@ alta::data* alta::load_data_from_text(std::istream& input,
for (int i = 0; i < 2 * dim.second; i++)
content.push_back(0.);
// If data is not in the interval of fit
// std::cout << " start = " << start << " rows = " << row_count
// << " size = " << content.size() << "\n";
Map<VectorXd> v(&content[start], row_count);
// Read the confidence interval data if available.
read_confidence_interval(linestream, v, kind,
dim.first, dim.second, args);
// Check if we need to filter out what we just read according to ARGS.
// TODO: Move filtering to a post-parsing operation on 'data'.
if (!(within_bounds(v.segment(0, dim.first), min, max)
&& within_bounds(v.segment(dim.first, dim.second),
ymin, ymax)))
continue;
if(args.is_defined("data-correct-cosine"))
{
content.resize(start);
}
else if (args.is_defined("data-correct-cosine"))
{
if (!cosine_correction(v.segment(0, dim.first + dim.second),
dim.first, dim.second, in_param))
continue;
content.resize(start);
}
// Read the confidence interval data if available.
read_confidence_interval(linestream, v, kind,
dim.first, dim.second, args);
}
}
......
/* ALTA --- Analysis of Bidirectional Reflectance Distribution Functions
Copyright (C) 2013, 2014, 2015, 2016 Inria
Copyright (C) 2013, 2014, 2015, 2016, 2017 Inria
This file is part of ALTA.
......@@ -25,9 +25,12 @@ namespace alta
void save_data_as_binary(std::ostream& out, const alta::data& data);
// Return the data read from INPUT in ALTA's native text format.
// Return the data read from INPUT in ALTA's native text format. Read
// extra parsing parameters from ARGS. XXX: ARGS is currently used for
// extra filtering, which should really happen elsewhere.
data* load_data_from_text(std::istream& input,
const alta::arguments& header);
const alta::arguments& header,
const alta::arguments& args = alta::arguments());
// Return the data read from the binary-formatted stream IN.
data* load_data_from_binary(std::istream& in, const alta::arguments& header);
......
......@@ -831,3 +831,21 @@ void params::print_input_params()
std::cout << it->second.info << std::endl;
}
}
bool
params::is_above_hemisphere( double* const invec, params::input in_param_type )
{
vec input_in_cartesian(6);
params::convert(invec, in_param_type, params::CARTESIAN, &input_in_cartesian[0]);
return input_in_cartesian[2] >= 0.0 && input_in_cartesian[5] >= 0.0;
}
......@@ -415,6 +415,17 @@ class params
static void print_input_params();
//! \brief Check whether or not a (light,view) configuration is above the hemisphere
//! \return true if both vectors (light and view) are above and false otherwise
//! \remark the function returns true also for grazing, tangential configuration
//! (i.e., where light dot normal == 0 or view dot normal == 0)
static bool is_above_hemisphere( double* invec, params::input intype );
static bool inline is_below_hemisphere( double* invec, params::input intype )
{
return ! is_above_hemisphere(invec, intype);
}
};
/*! \brief A parameters object. Allow to define function object (either data
......
/* ALTA --- Analysis of Bidirectional Reflectance Distribution Functions
Copyright (C) 2014, 2015 CNRS
Copyright (C) 2013, 2014, 2015, 2016 Inria
Copyright (C) 2013, 2014, 2015, 2016, 2017 Inria
This file is part of ALTA.
......@@ -486,8 +486,7 @@ ptr<data> plugins_manager::load_data(const std::string& type, std::istream& inpu
if (header["FORMAT"] == "binary") {
result = ptr<data>(load_data_from_binary(input, header));
} else {
// FIXME: ARGS is currently ignored.
result = ptr<data>(load_data_from_text(input, header));
result = ptr<data>(load_data_from_text(input, header, args));
}
}
else
......
/* ALTA --- Analysis of Bidirectional Reflectance Distribution Functions
Copyright (C) 2013, 2014, 2015, 2016 Inria
Copyright (C) 2013, 2014, 2015, 2016, 2017 Inria
This file is part of ALTA.
......@@ -108,41 +108,28 @@ void vertical_segment::get(int i, vec& x, vec& yl, vec& yu) const
// Make sure we have the lower and upper bounds of Y.
assert(confidence_interval_kind() == ASYMMETRICAL_CONFIDENCE_INTERVAL);
auto matrix = matrix_view();
auto row = matrix_view().row(i);
#ifdef DEBUG
assert(i >= 0 && i < matrix.size());
#endif
x.resize(_parameters.dimX()); yl.resize(_parameters.dimY()) ; yu.resize(_parameters.dimY()) ;
for(int j=0; j<_parameters.dimX(); ++j)
{
x[j] = matrix(i, j);
}
for(int j=0; j<_parameters.dimY(); ++j)
{
yl[j] = matrix(i, _parameters.dimX() + 1*_parameters.dimY() + j);
yu[j] = matrix(i, _parameters.dimX() + 2*_parameters.dimY() + j);
}
x = row.segment(0, _parameters.dimX());
auto y = row.segment(_parameters.dimX(), _parameters.dimY());
yl = row.segment(_parameters.dimX() + _parameters.dimY(),
_parameters.dimY());
yu = row.segment(_parameters.dimX() + 2 * _parameters.dimY(),
_parameters.dimY());
}
void vertical_segment::get(int i, vec& yl, vec& yu) const
{
// Make sure we have the lower and upper bounds of Y.
assert(confidence_interval_kind() == ASYMMETRICAL_CONFIDENCE_INTERVAL);
auto matrix = matrix_view();
yl.resize(_parameters.dimY()) ; yu.resize(_parameters.dimY()) ;
for(int j=0; j<_parameters.dimY(); ++j)
{
yl[j] = matrix(i, _parameters.dimX() + _parameters.dimY() + j);
yu[j] = matrix(i, _parameters.dimX() + 2*_parameters.dimY() + j);
}
vec x;
get(i, x, yl, yu);
}
vec vertical_segment::get(int i) const
{
return data_view().col(i);
return data_view().row(i);
}
void vertical_segment::set(int i, const vec& x)
......
/* ALTA --- Analysis of Bidirectional Reflectance Distribution Functions
Copyright (C) 2013, 2014, 2015, 2016 Inria
Copyright (C) 2013, 2014, 2015, 2016, 2017 Inria
This file is part of ALTA.
......@@ -164,20 +164,19 @@ class vertical_segment : public data
// Thus, it has (dimX + dimY + N * dimY) columns, where N is between 0
// and 2 depending on the confidence interval data available, and SIZE
// rows.
Eigen::Map<Eigen::MatrixXd> matrix_view() const
Eigen::Map<RowMatrixXd> matrix_view() const
{
return Eigen::Map<Eigen::MatrixXd>(_data.get(), size(),
column_number());
return Eigen::Map<RowMatrixXd>(_data.get(), size(), column_number());
}
private: // method
//! \brief Return a matrix view of DATA that excludes confidence
// interval data. It has (dimX + dimY) columns and SIZE rows.
Eigen::Map<Eigen::MatrixXd, 0, Eigen::OuterStride<> > data_view() const
Eigen::Map<RowMatrixXd, 0, Eigen::OuterStride<> > data_view() const
{
return Eigen::Map<Eigen::MatrixXd, 0, Eigen::OuterStride<> >
(_data.get(), _parameters.dimX() + _parameters.dimY(), size(),
return Eigen::Map<RowMatrixXd, 0, Eigen::OuterStride<> >
(_data.get(), _size, _parameters.dimX() + _parameters.dimY(),
Eigen::OuterStride<>(column_number()));
}
......
#ifndef EXR_IO_H_
#define EXR_IO_H_
/*
* Author: Cyril Soler
*/
/* ALTA --- Analysis of Bidirectional Reflectance Distribution Functions
Copyright (C) 2017 Unity Technologies
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/. */
#pragma once
// STL includes
#include <stdexcept>
#include <cassert>
#include <ImfRgbaFile.h>
#include <ImfArray.h>
// TinyEXR includes
#define TINYEXR_IMPLEMENTATION
#include <tinyexr/tinyexr.h>
// XXX: This header is not installed as of version 2.2.0 of OpenEXR, see
// <https://lists.nongnu.org/archive/html/openexr-devel/2016-06/msg00001.html>
// and <https://github.com/openexr/openexr/pull/184>.
#include <ImfStdIO.h>
/*! \class EXR_IO
*
* \details
* EXR_IO provides static method to either load an RGB EXR file into a floatting
* point array or save a RGB floatting point array to disk._data
*
* \author Laurent Belcour
*/
template<typename FType>
class t_EXR_IO
{
public:
static bool LoadEXR(std::istream& input, int& W,int& H, FType *& pix,int nC=3)
/*! \brief Load an EXR file from an input file stream.
*/
static bool LoadEXR(std::istream& input, int& W,int& H, FType *& pix,int nC=3)
{
// XXX: OpenEXR implements its own IStream and OStream classes, but
// they are completely independent from those of libstdc++. The
// closest thing it has is 'StdIFStream', hence this hack.
std::ifstream* ifstream = dynamic_cast<std::ifstream*>(&input);
assert(ifstream != NULL);
Imf::StdIFStream iifstream(*ifstream, "unknown file name");
Imf::RgbaInputFile file(iifstream);
Imath::Box2i dw = file.dataWindow();
W = dw.max.x - dw.min.x + 1;
H = dw.max.y - dw.min.y + 1;
Imf::Array2D<Imf::Rgba> pixels;
pixels.resizeErase(H, W);
file.setFrameBuffer (&pixels[0][0] - dw.min.x - dw.min.y * W, 1, W);
file.readPixels (dw.min.y, dw.max.y);
pix = new FType[W*H*3] ;
switch(nC)
{
case 3: for(int i=0;i<H;++i)
for(int j=0;j<W;++j)
{
pix[3*(j+i*W)+0] = pixels[H-i-1][j].r ;
pix[3*(j+i*W)+1] = pixels[H-i-1][j].g ;
pix[3*(j+i*W)+2] = pixels[H-i-1][j].b ;
}
break ;
case 1: for(int i=0;i<H;++i)
for(int j=0;j<W;++j)
pix[j+i*W] = 0.3*pixels[H-i-1][j].r + 0.59*pixels[H-i-1][j].g + 0.11*pixels[H-i-1][j].b ;
break ;
default:
throw std::runtime_error("Unexpected case in EXR_IO.") ;
/* Convert the input std::istream into an unsigned char array */
std::vector<unsigned char> _memory;
size_t _n = 0, _m = 0;
while(input.good()) {
if(_n == _m) {
_m += 256;
_memory.resize(_m);
}
_memory[_n++] = (unsigned char)input.get();
}
/* Check the different part of loading */
int _r = -1;
const char* _err;
/* Load the EXR version using TinyEXR */
EXRVersion _version;
_r = ParseEXRVersionFromMemory(&_version, &_memory[0]);
if(_r != TINYEXR_SUCCESS) {
std::cerr << "<<ERROR>> Could not load EXR version from stream" << std::endl;
return false;
} else {
std::cout << "<<DEBUG>> Loading a v" << _version.version << " EXR file" << std::endl;
}
/* Load the EXR header */
EXRHeader _header;
InitEXRHeader(&_header);
_r = ParseEXRHeaderFromMemory(&_header, &_version, &_memory[0], &_err);
if(_r != TINYEXR_SUCCESS) {
std::cerr << "<<ERROR>> Could not load EXR header from stream" << std::endl;
std::cerr << "<<ERROR>> " << _err << std::endl;
return false;
} else {
std::cout << "<<DEBUG>> Loading a " << _header.num_channels << " channels EXR file" << std::endl;
}
/* Set the requested pixel types */
for(int i=0; i<_header.num_channels; ++i) {
_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
}
/* Load the EXR image */
EXRImage _image;
InitEXRImage(&_image);
_r = LoadEXRImageFromMemory(&_image, &_header, &_memory[0], &_err);
if (_r != TINYEXR_SUCCESS) {
std::cerr << "<<ERROR>> Could not load EXR image from stream" << std::endl;
std::cerr << "<<ERROR>> " << _err << std::endl;
return false;
} else {
std::cout << "<<DEBUG>> Loading a " << _image.width << "x" << _image.height << " EXR file" << std::endl;
}
/*! \todo Check the different channels if they match RGB */
/*! \todo handle VS data using multi-channel */
/* Recopy the image into the provided pixel array */
W = _image.width;
H = _image.height;
pix = new FType[W*H*3];
for(int k=0; k<_image.num_channels; ++k) {
for(int i=0; i<W*H; ++i) {
const FType val = (FType)(reinterpret_cast<float **>(_image.images)[k][i]);
switch(_header.channels[k].name[0]) {
case 'R':
pix[3*i + 0] = val;
break;
case 'G':
pix[3*i + 1] = val;
break;
case 'B':
pix[3*i + 2] = val;
break;
default:
std::cout << "<<DEBUG>> Unknow EXR channel \'" << _header.channels[k].name[0] << "\'" << std::endl;
}
}
}
/* Free TinyEXR memory */
_r = FreeEXRHeader(&_header);
_r = FreeEXRImage(&_image);
return true ;
}
/*! \brief Save a RGB image into and OpenEXR file using TinyEXR.
* This code uses TinyEXR's reference implementation of saving a file as
* we do not manipulate streams here.
*/
static bool SaveEXR(const char *filename,int W,int H, const FType *pix,int nC=3)
{
Imf::Array2D<Imf::Rgba> pixels(H,W);
/* Convert separated channel representation to per pixel representation */
switch(nC)
{
case 3:
for (int row=0;row<H;row++)
for(int i=0;i<W;i++)
{
Imf::Rgba &p = pixels[H-row-1][i];
p.r = pix[3*(i+row*W)+0] ;
p.g = pix[3*(i+row*W)+1] ;
p.b = pix[3*(i+row*W)+2] ;
p.a = 1.0 ;
}
break ;
case 1:
for (int row=0;row<H;row++)
for(int i=0;i<W;i++)
{
Imf::Rgba &p = pixels[H-row-1][i];
p.r = pix[i+row*W] ;
p.g = pix[i+row*W] ;
p.b = pix[i+row*W] ;
p.a = FType(1.0) ;
}
break ;
default:
throw std::runtime_error("Unexpected case in EXR_IO.") ;
EXRHeader header;
InitEXRHeader(&header);
EXRImage image;
InitEXRImage(&image);
image.num_channels = 3;
std::vector<float> images[3];
images[0].resize(W*H);
images[1].resize(W*H);
images[2].resize(W*H);
// Split RGBRGBRGB... into R, G and B layer
for (int i = 0; i < W*H; i++) {
images[0][i] = pix[3*i+0];
images[1][i] = pix[3*i+1];
images[2][i] = pix[3*i+2];
}
float* image_ptr[3];
image_ptr[0] = &(images[2].at(0)); // B
image_ptr[1] = &(images[1].at(0)); // G
image_ptr[2] = &(images[0].at(0)); // R
image.images = (unsigned char**)image_ptr;
image.width = W;
image.height = H;
header.num_channels = 3;
header.channels = (EXRChannelInfo *)malloc(sizeof(EXRChannelInfo) * header.num_channels);
// Must be (A)BGR order, since most of EXR viewers expect this channel order.
strncpy(header.channels[0].name, "B", 255); header.channels[0].name[strlen("B")] = '\0';
strncpy(header.channels[1].name, "G", 255); header.channels[1].name[strlen("G")] = '\0';
strncpy(header.channels[2].name, "R", 255); header.channels[2].name[strlen("R")] = '\0';
header.pixel_types = (int *)malloc(sizeof(int) * header.num_channels);
header.requested_pixel_types = (int *)malloc(sizeof(int) * header.num_channels);
for (int i = 0; i < header.num_channels; i++) {
header.pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image
header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_HALF; // pixel type of output image to be stored in .EXR
}
Imf::RgbaOutputFile file(filename, W, H, Imf::WRITE_RGBA);
file.setFrameBuffer(&pixels[0][0], 1, W);
file.writePixels(H);
const char* err;
int ret = SaveEXRImageToFile(&image, &header, filename, &err);
if (ret != TINYEXR_SUCCESS) {
std::cerr << "<<ERROR>> Unable to save to EXR file" << std::endl;
std::cerr << "<<ERROR>> " << err << std::endl;
return ret;
}
free(header.channels);
free(header.pixel_types);
free(header.requested_pixel_types);
return true ;
}
};
typedef t_EXR_IO<float> EXR_IO ;
#endif
typedef t_EXR_IO<float> EXR_IO ;
\ No newline at end of file
......@@ -2,13 +2,17 @@ Import('env', 'openexr_available')
env = env.Clone()
have_openexr = openexr_available(env)
if not have_openexr:
print "<<ERROR>> Could not locate TinyEXR : EXR related plugins won't be built."
print " Please use `git submodule init` and 'git submodule update` to get TinyEXR."
if have_openexr:
# Make sure we have the 'ImfStdIO.h' header, which OpenEXR 2.2.0
# does not install:
# <https://lists.nongnu.org/archive/html/openexr-devel/2016-06/msg00001.html>.
conf = Configure(env)
have_openexr = conf.CheckCXXHeader('ImfStdIO.h')
have_openexr = conf.CheckCXXHeader('tinyexr/tinyexr.h')
conf.Finish()
# Special linking flags, defined in the OS dependant configuration file
......
......@@ -90,7 +90,7 @@ private: // data
public: // methods
MERL(const parameters& params) :
data(params, MERL_SIZE), _nSlice(MERL_SIZE) {
data(parameters(3, 3, params::RUSIN_TH_TD_PD, params::RGB_COLOR), MERL_SIZE), _nSlice(MERL_SIZE) {
brdf = new double[3*_nSlice];
std::fill(brdf, brdf + 3*_nSlice, 0.0);
......@@ -151,7 +151,7 @@ public: // methods
}
void set(int i, const vec& x) {
assert(x.size() == parametrization().dimY());
assert(x.size() == parametrization().dimX() + parametrization().dimY());
int iR = i;
int iG = iR + _nSlice;
int iB = iG + _nSlice;
......
......@@ -18,8 +18,9 @@
using namespace alta;
// Allow for a different parametrization depending on the arguments provided.
static const parameters
static const alta::parameters
brdf_slice_parameters(const arguments& args)
{
auto result = alta::parameters(2, 3,
......