├── .gitignore ├── LICENSE ├── README.md └── src ├── PFMReadWrite.cpp ├── PFMReadWrite.h ├── imageprocessing.cpp ├── imageprocessing.h ├── main.cpp ├── mathfunctions.cpp ├── mathfunctions.h ├── reflectance.cpp ├── reflectance.h └── reflectance_maps.pro /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reflectance Maps 2 | This project is an open-source implementation of the papers : 3 | 4 | * Rapid Acquisition of Specular and Diffuse Normal Maps from Polarized Spherical Gradient Illumination. [Ma et al. 2007] 5 | * Estimating Specular Roughness and Anisotropy from Second Order Spherical Gradient Illumination. [Ghosh et al. 2009] 6 | 7 | It is distributed under the LGPL license. 8 | 9 | #### Version 10 | 11 | Version 1.0 12 | 13 | ## Principle 14 | 15 | For an in-depth explanation (with pictures) of this program, please look at the [project page](https://www.antoinetlc.com/software/reflectance-maps-acquisition-with-gradient-illumination). 16 | 17 | This program can compute reflectance maps from pictures taken with polarised gradient illumination. These reflectance maps include spatially varying diffuse and specular albedo, normal map and roughness map. 18 | 19 | **Important note** : In this implementation the sample is supposed to be almost flat and illuminated by a flat light source (e.g LCD screen) and not a light stage. 20 | 21 | During the measurements the gradients illumination used were : order 0 (full white illumination), +X gradient, -X gradient, +Y gradient, -Y gradient, second order X gradient and Y second order gradient. The +Z and -Z gradients were omited due to the planar assumption of the surface (see note). The light source was also linearly polarised (LCD screen) and a polariser was added on the camera to allow the diffuse/specular separation. Please refer to the papers for more details. 22 | 23 | ## Compilation 24 | This program has been compiled and tested on a Windows environment. 25 | It requires the following libraries/API in order to compile : 26 | 27 | * OpenCV (tested with version 2.4.11) 28 | 29 | A "reflectance_maps.pro" file is provided for compilation with QtCreator IDE. Please update the libraries paths to match your installation. 30 | 31 | ## Installation 32 | After the measurements, create a directory with the following folders and files : 33 | * par/ 34 | * cross/ 35 | * texture/ 36 | * checker.txt 37 | * mask.jpg 38 | 39 | The "par" and "cross" folders contain the measurements with parallel polarised illumination and cross polarised illumination respectively. Each folder must contain the measurements with the 7 gradients in the following order : 40 | 41 | * order 0 gradient 42 | * order 1 +x gradient 43 | * order 1 -x gradient 44 | * order 1 +y gradient 45 | * order 1 -y gradient 46 | * order 2 x gradient 47 | * order 2 y gradient 48 | 49 | Each folder must also contain an ambient.jpg file that corresponds to the sample taken without the LCD screen switched on (recording of the ambient illumination of the room). 50 | 51 | The results will be saved in the "texture" folder. 52 | 53 | Finally two files have to be added. 54 | 55 | * A mask.jpg file i.e a mask that describes the area of the image where the sample is. This is important to avoid computations on the background of the image. The region of the sample must have a red color RGB = (255, 0 , 0). 56 | * A checker.txt file for the color calibration. 57 | 58 | A picture of a ColorChecker must be taken during the measurements for correct color calibration. These values are stored in the checker.txt file. It must have two lines. The first line corresponds to the RGB value of a given reflective patch on the ColorChecker for the measurement with parallel polarisation. The second line one to the values for measurements with cross polarisation. 59 | 60 | ``` 61 | R_par G_par B_par PatchReflectance 62 | R_cross G_cross B_cross PatchReflectance 63 | ``` 64 | 65 | Call the compute_maps function with its first parameter set to the path of this directory to start the computation (see main.cpp). 66 | 67 | ## License 68 | 69 | Reflectance Maps. Author : Antoine TOISOUL. Copyright © 2016 Antoine TOISOUL, Imperial College London. All rights reserved. 70 | 71 | Reflectance Maps is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Reflectance Maps is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . 72 | 73 | ## Acknowledgment 74 | I would like to thank Jérémy Rivière for his help during the measurements and for the helpful discussions. 75 | -------------------------------------------------------------------------------- /src/PFMReadWrite.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Reflectance Maps 3 | * 4 | * Authors: Antoine TOISOUL LE CANN 5 | * 6 | * Copyright © 2016 Antoine TOISOUL LE CANN 7 | * All rights reserved 8 | * 9 | * 10 | * Reflectance Maps is free software: you can redistribute it and/or modify 11 | * 12 | * it under the terms of the GNU Lesser General Public License as published by 13 | * 14 | * the Free Software Foundation, either version 3 of the License, or 15 | * 16 | * (at your option) any later version. 17 | * 18 | * Reflectance Maps is distributed in the hope that it will be useful, 19 | * 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * 24 | * GNU Lesser General Public License for more details. 25 | * 26 | * You should have received a copy of the GNU Lesser General Public License 27 | * 28 | * along with this program. If not, see . 29 | */ 30 | 31 | /** 32 | * \file PFMReadWrite.cpp 33 | * \brief Implementation of loadPFM and savePFM functions. 34 | * \author Antoine Toisoul Le Cann 35 | * \date September, 1st, 2016 36 | * 37 | * Implementation of loadPFM and savePFM functions. 38 | */ 39 | 40 | #include "PFMReadWrite.h" 41 | 42 | using namespace std; 43 | using namespace cv; 44 | 45 | /** 46 | * Loads a PFM image stored in little endian and returns the image as an OpenCV Mat. 47 | * @brief loadPFM 48 | * @param filePath 49 | * @return 50 | */ 51 | Mat loadPFM(const string filePath) 52 | { 53 | 54 | //Open binary file 55 | ifstream file(filePath.c_str(), ios::in | ios::binary); 56 | 57 | Mat imagePFM; 58 | 59 | //If file correctly openened 60 | if(file) 61 | { 62 | //Read the type of file plus the 0x0a UNIX return character at the end 63 | char type[3]; 64 | file.read(type, 3*sizeof(char)); 65 | 66 | //Read the width and height 67 | unsigned int width(0), height(0); 68 | file >> width >> height; 69 | 70 | //Read the 0x0a UNIX return character at the end 71 | char endOfLine; 72 | file.read(&endOfLine, sizeof(char)); 73 | 74 | int numberOfComponents(0); 75 | //The type gets the number of color channels 76 | if(type[1] == 'F') 77 | { 78 | imagePFM = Mat(height, width, CV_32FC3); 79 | numberOfComponents = 3; 80 | } 81 | else if(type[1] == 'f') 82 | { 83 | imagePFM = Mat(height, width, CV_32FC1); 84 | numberOfComponents = 1; 85 | } 86 | 87 | //TODO Read correctly depending on the endianness 88 | //Read the endianness plus the 0x0a UNIX return character at the end 89 | //Byte Order contains -1.0 or 1.0 90 | char byteOrder[4]; 91 | file.read(byteOrder, 4*sizeof(char)); 92 | 93 | //Find the last line return 0x0a before the pixels of the image 94 | char findReturn = ' '; 95 | while(findReturn != 0x0a) 96 | { 97 | file.read(&findReturn, sizeof(char)); 98 | } 99 | 100 | //Read each RGB colors as 3 floats and store it in the image. 101 | float *color = new float[numberOfComponents]; 102 | for(unsigned int i = 0 ; i(height-1-i,j) = Vec3f(color[2], color[1], color[0]); 113 | } 114 | else if(numberOfComponents == 1) 115 | { 116 | //OpenCV stores the color as BGR 117 | imagePFM.at(height-1-i,j) = color[0]; 118 | } 119 | } 120 | } 121 | 122 | delete[] color; 123 | 124 | //Close file 125 | file.close(); 126 | } 127 | else 128 | { 129 | cerr << "Could not open the file : " << filePath << endl; 130 | } 131 | 132 | return imagePFM; 133 | } 134 | 135 | 136 | /** 137 | * Saves the image as a PFM file. 138 | * @brief savePFM 139 | * @param image 140 | * @param filePath 141 | * @return 142 | */ 143 | bool savePFM(const cv::Mat image, const std::string filePath) 144 | { 145 | //Open the file as binary! 146 | ofstream imageFile(filePath.c_str(), ios::out | ios::trunc | ios::binary); 147 | 148 | if(imageFile) 149 | { 150 | int width(image.cols), height(image.rows); 151 | int numberOfComponents(image.channels()); 152 | 153 | //Write the type of the PFM file and ends by a line return 154 | char type[3]; 155 | type[0] = 'P'; 156 | type[2] = 0x0a; 157 | 158 | if(numberOfComponents == 3) 159 | { 160 | type[1] = 'F'; 161 | } 162 | else if(numberOfComponents == 1) 163 | { 164 | type[1] = 'f'; 165 | } 166 | 167 | imageFile << type[0] << type[1] << type[2]; 168 | 169 | //Write the width and height and ends by a line return 170 | imageFile << width << " " << height << type[2]; 171 | 172 | //Assumes little endian storage and ends with a line return 0x0a 173 | //Stores the type 174 | char byteOrder[10]; 175 | byteOrder[0] = '-'; byteOrder[1] = '1'; byteOrder[2] = '.'; byteOrder[3] = '0'; 176 | byteOrder[4] = '0'; byteOrder[5] = '0'; byteOrder[6] = '0'; byteOrder[7] = '0'; 177 | byteOrder[8] = '0'; byteOrder[9] = 0x0a; 178 | 179 | for(int i = 0 ; i<10 ; ++i) 180 | { 181 | imageFile << byteOrder[i]; 182 | } 183 | 184 | //Store the floating points RGB color upside down, left to right 185 | float* buffer = new float[numberOfComponents]; 186 | 187 | for(int i = 0 ; i(height-1-i,j); 194 | } 195 | else 196 | { 197 | Vec3f color = image.at(height-1-i,j); 198 | 199 | //OpenCV stores as BGR 200 | buffer[0] = color.val[2]; 201 | buffer[1] = color.val[1]; 202 | buffer[2] = color.val[0]; 203 | } 204 | 205 | //Write the values 206 | imageFile.write((char *) buffer, numberOfComponents*sizeof(float)); 207 | 208 | } 209 | } 210 | 211 | delete[] buffer; 212 | 213 | imageFile.close(); 214 | } 215 | else 216 | { 217 | cerr << "Could not open the file : " << filePath << endl; 218 | return false; 219 | } 220 | 221 | return true; 222 | } 223 | -------------------------------------------------------------------------------- /src/PFMReadWrite.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Reflectance Maps 3 | * 4 | * Authors: Antoine TOISOUL LE CANN 5 | * 6 | * Copyright © 2016 Antoine TOISOUL LE CANN 7 | * All rights reserved 8 | * 9 | * 10 | * Reflectance Maps is free software: you can redistribute it and/or modify 11 | * 12 | * it under the terms of the GNU Lesser General Public License as published by 13 | * 14 | * the Free Software Foundation, either version 3 of the License, or 15 | * 16 | * (at your option) any later version. 17 | * 18 | * Reflectance Maps is distributed in the hope that it will be useful, 19 | * 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * 24 | * GNU Lesser General Public License for more details. 25 | * 26 | * You should have received a copy of the GNU Lesser General Public License 27 | * 28 | * along with this program. If not, see . 29 | */ 30 | 31 | /** 32 | * \file PFMReadWrite.h 33 | * \brief Implementation of loadPFM and savePFM functions. 34 | * \author Antoine Toisoul Le Cann 35 | * \date September, 1st, 2016 36 | * 37 | * Implementation of loadPFM and savePFM functions. 38 | */ 39 | 40 | #ifndef PFMREADWRITE 41 | #define PFMREADWRITE 42 | 43 | #include 44 | #include 45 | 46 | #include 47 | #include 48 | 49 | /** 50 | * Loads a PFM image stored in little endian and returns the image as an OpenCV Mat. 51 | * @brief loadPFM 52 | * @param filePath 53 | * @return 54 | */ 55 | cv::Mat loadPFM(const std::string filePath); 56 | 57 | /** 58 | * Saves the image as a PFM file. 59 | * @brief savePFM 60 | * @param image 61 | * @param filePath 62 | * @return 63 | */ 64 | bool savePFM(const cv::Mat image, const std::string filePath); 65 | 66 | #endif // PFMREADWRITE 67 | 68 | -------------------------------------------------------------------------------- /src/imageprocessing.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Reflectance Maps 3 | * 4 | * Authors: Antoine TOISOUL LE CANN 5 | * 6 | * Copyright © 2016 Antoine TOISOUL LE CANN 7 | * All rights reserved 8 | * 9 | * 10 | * Reflectance Maps is free software: you can redistribute it and/or modify 11 | * 12 | * it under the terms of the GNU Lesser General Public License as published by 13 | * 14 | * the Free Software Foundation, either version 3 of the License, or 15 | * 16 | * (at your option) any later version. 17 | * 18 | * Reflectance Maps is distributed in the hope that it will be useful, 19 | * 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * 24 | * GNU Lesser General Public License for more details. 25 | * 26 | * You should have received a copy of the GNU Lesser General Public License 27 | * 28 | * along with this program. If not, see . 29 | */ 30 | 31 | /** 32 | * \file imageprocessing.h 33 | * \brief Implementation of functions related to image processing. 34 | * \author Antoine Toisoul Le Cann 35 | * \date September, 11th, 2016 36 | * 37 | * Implementation of functions related to image processing. 38 | */ 39 | 40 | #include "imageprocessing.h" 41 | 42 | using namespace cv; 43 | using namespace std; 44 | 45 | /** 46 | * Function that scales a float image to the 0;1 range. 47 | * Divides each color channel by the maximum. The maximum is calculated in the region of the image defined by the mask. 48 | * @brief scaleTo01Range 49 | * @param image 50 | */ 51 | void scaleTo01Range(Mat &image, const Mat &maskObject) 52 | { 53 | int width = image.cols; 54 | int height = image.rows; 55 | float R = 0.0, G = 0.0, B = 0.0; 56 | float maximumOfRGB = 0.0; 57 | float currentMaximumRGB = 0.0; 58 | 59 | //Find maximum 60 | for(int i = 0 ; i(i,j).val[2]>0.9) 66 | { 67 | R = image.at(i,j).val[2]; 68 | G = image.at(i,j).val[1]; 69 | B = image.at(i,j).val[0]; 70 | 71 | currentMaximumRGB = max(R,max(G,B)); 72 | 73 | if(currentMaximumRGB>maximumOfRGB) 74 | { 75 | maximumOfRGB = currentMaximumRGB; 76 | } 77 | } 78 | 79 | } 80 | } 81 | 82 | //Divide RGB by the same value to avoid color shifting 83 | for(int i = 0 ; i0.0)) 88 | { 89 | image.at(i,j).val[2] /= maximumOfRGB; 90 | image.at(i,j).val[1] /= maximumOfRGB; 91 | image.at(i,j).val[0] /= maximumOfRGB; 92 | } 93 | } 94 | } 95 | } 96 | 97 | /** 98 | * For each pixel of each image sets RGB to 0 if any of R, G, B is 0. 99 | * @brief setNegativePixelsTo0 100 | * @param images 101 | * @param numberOfImages 102 | */ 103 | void setNegativePixelsTo0(Mat images[], int numberOfImages) 104 | { 105 | for(int k = 0 ; k(i,j).val[2]; 128 | G = image.at(i,j).val[1]; 129 | B = image.at(i,j).val[0]; 130 | 131 | if(R<0.0 || G<0.0 || B<0.0) 132 | { 133 | image.at(i,j).val[2] = 0.0; 134 | image.at(i,j).val[1] = 0.0; 135 | image.at(i,j).val[0] = 0.0; 136 | } 137 | } 138 | } 139 | } 140 | 141 | /** 142 | * Apply a gamma correction to a RGB image (OpenCV Mat image). 143 | * @param INPUT : rgbImage is the image to which the gamma correction is applied. 144 | * @param OUTPUT : rgbImageWithGamma is the rgbImage with the gamma applied. 145 | * @param INPUT : gamma is a double corresponding to the value of the gamma correction. 146 | */ 147 | void gammaCorrection(const Mat &rgbImage, Mat &rgbImageWithGamma, double gamma) 148 | { 149 | Mat channel[3],channel32F[3], channelWithGamma[3]; 150 | 151 | split(rgbImage, channel); 152 | 153 | channel[0].convertTo(channel32F[0], CV_32F); 154 | channel[1].convertTo(channel32F[1], CV_32F); 155 | channel[2].convertTo(channel32F[2], CV_32F); 156 | 157 | pow(channel32F[0], 1.0/gamma, channelWithGamma[0]); 158 | pow(channel32F[1], 1.0/gamma, channelWithGamma[1]); 159 | pow(channel32F[2], 1.0/gamma, channelWithGamma[2]); 160 | 161 | merge(channelWithGamma,3,rgbImageWithGamma); 162 | } 163 | 164 | /** 165 | * Remove the gamma correction of a RGB image (OpenCV Mat image). 166 | * @param INPUT : rgbImage is the image to which the gamma correction is removed. rgbImage is an OpenCV CV_32FC3 matrix. (matrix of 3 channels of 32 bits floats). 167 | * @param OUTPUT : rgbImageWithGamma is the rgbImage with the gamma removed. It is a CV_32FC3 matrix (matrix of 3 channels of 32 bits floats). 168 | * @param INPUT : gamma is a double corresponding to the value of the gamma correction. 169 | */ 170 | void removeGammaCorrection(const Mat &rgbImage, Mat &rgbImageWithoutGamma, double gamma) 171 | { 172 | Mat channel[3], channelWithoutGamma[3]; 173 | 174 | split(rgbImage, channel); 175 | 176 | channel[0].convertTo(channel[0], CV_32F); 177 | channel[1].convertTo(channel[1], CV_32F); 178 | channel[2].convertTo(channel[2], CV_32F); 179 | 180 | pow(channel[0], gamma, channelWithoutGamma[0]); 181 | pow(channel[1], gamma, channelWithoutGamma[1]); 182 | pow(channel[2], gamma, channelWithoutGamma[2]); 183 | 184 | merge(channelWithoutGamma,3,rgbImageWithoutGamma); 185 | } 186 | -------------------------------------------------------------------------------- /src/imageprocessing.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Reflectance Maps 3 | * 4 | * Authors: Antoine TOISOUL LE CANN 5 | * 6 | * Copyright © 2016 Antoine TOISOUL LE CANN 7 | * All rights reserved 8 | * 9 | * 10 | * Reflectance Maps is free software: you can redistribute it and/or modify 11 | * 12 | * it under the terms of the GNU Lesser General Public License as published by 13 | * 14 | * the Free Software Foundation, either version 3 of the License, or 15 | * 16 | * (at your option) any later version. 17 | * 18 | * Reflectance Maps is distributed in the hope that it will be useful, 19 | * 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * 24 | * GNU Lesser General Public License for more details. 25 | * 26 | * You should have received a copy of the GNU Lesser General Public License 27 | * 28 | * along with this program. If not, see . 29 | */ 30 | 31 | /** 32 | * \file imageprocessing.cpp 33 | * \brief Implementation of functions related to image processing. 34 | * \author Antoine Toisoul Le Cann 35 | * \date September, 11th, 2016 36 | * 37 | * Implementation of functions related to image processing. 38 | */ 39 | 40 | #ifndef IMAGEPROCESSING_H 41 | #define IMAGEPROCESSING_H 42 | 43 | #include "mathfunctions.h" 44 | 45 | #define M_PI 3.14159265358979323846 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | #include 52 | #include 53 | #include 54 | #include 55 | 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | 62 | /** 63 | * Function that scales a float image to the 0;1 range. 64 | * Divides each color channel by the maximum. The maximum is calculated in the region of the image defined by the mask. 65 | * @brief scaleTo01Range 66 | * @param image 67 | */ 68 | void scaleTo01Range(cv::Mat &image, const cv::Mat &maskObject); 69 | 70 | /** 71 | * For each pixel of each image sets RGB to 0 if any of R, G, B is 0 72 | * @brief setNegativePixelsTo0 73 | * @param images 74 | * @param numberOfImages 75 | */ 76 | void setNegativePixelsTo0(cv::Mat images[], int numberOfImages); 77 | 78 | /** 79 | * For each pixel of the image sets RGB to 0 if any of R, G, B is 0 80 | * @brief setNegativePixelsTo0 81 | * @param images 82 | */ 83 | void setNegativePixelsTo0(cv::Mat &image); 84 | 85 | /** 86 | * Apply a gamma correction to a RGB image (OpenCV Mat image). 87 | * @param INPUT : rgbImage is the image to which the gamma correction is applied. 88 | * @param OUTPUT : rgbImageWithGamma is the rgbImage with the gamma applied. 89 | * @param INPUT : gamma is a double corresponding to the value of the gamma correction. 90 | */ 91 | void gammaCorrection(const cv::Mat &rgbImage, cv::Mat &rgbImageWithGamma, double gamma); 92 | 93 | /** 94 | * Remove the gamma correction of a RGB image (OpenCV Mat image). 95 | * @param INPUT : rgbImage is the image to which the gamma correction is removed. rgbImage is an OpenCV CV_32FC3 matrix. (matrix of 3 channels of 32 bits floats). 96 | * @param OUTPUT : rgbImageWithGamma is the rgbImage with the gamma removed. It is a CV_32FC3 matrix (matrix of 3 channels of 32 bits floats). 97 | * @param INPUT : gamma is a double corresponding to the value of the gamma correction. 98 | */ 99 | void removeGammaCorrection(const cv::Mat &rgbImage, cv::Mat &rgbImageWithoutGamma, double gamma); 100 | 101 | #endif // IMAGEPROCESSING_H 102 | 103 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Reflectance Maps 3 | * 4 | * Authors: Antoine TOISOUL LE CANN 5 | * 6 | * Copyright © 2016 Antoine TOISOUL LE CANN 7 | * All rights reserved 8 | * 9 | * 10 | * Reflectance Maps is free software: you can redistribute it and/or modify 11 | * 12 | * it under the terms of the GNU Lesser General Public License as published by 13 | * 14 | * the Free Software Foundation, either version 3 of the License, or 15 | * 16 | * (at your option) any later version. 17 | * 18 | * Reflectance Maps is distributed in the hope that it will be useful, 19 | * 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * 24 | * GNU Lesser General Public License for more details. 25 | * 26 | * You should have received a copy of the GNU Lesser General Public License 27 | * 28 | * along with this program. If not, see . 29 | */ 30 | 31 | /** 32 | * \file main.cpp 33 | * \brief Example of how to compute the reflectance maps. 34 | * \author Antoine Toisoul Le Cann 35 | * \date September, 11th, 2016 36 | * 37 | * Example of how to compute the reflectance maps. 38 | */ 39 | 40 | #include 41 | #include 42 | 43 | #include "reflectance.h" 44 | 45 | using namespace std; 46 | 47 | int main(void) 48 | { 49 | //Call this function to compute the maps 50 | //The first parameter is a path to the folder that contains the illumination measurements : par, cross, checkert.txt, mask.jpg 51 | //The second parameter is set to true to use the cross polarised measurements or false otherwise. 52 | //NOTE : the loading of the image file is currently hardcoded. 53 | //The images are supposed to have a name : IMG_XXXX where XXXX is a number 54 | //Within a folder (e.g parallel data) the pictures are supposed to have consecutive numbers. 55 | computeMaps(string("path_to_folder"), true); 56 | 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /src/mathfunctions.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Reflectance Maps 3 | * 4 | * Authors: Antoine TOISOUL LE CANN 5 | * 6 | * Copyright © 2016 Antoine TOISOUL LE CANN 7 | * All rights reserved 8 | * 9 | * 10 | * Reflectance Maps is free software: you can redistribute it and/or modify 11 | * 12 | * it under the terms of the GNU Lesser General Public License as published by 13 | * 14 | * the Free Software Foundation, either version 3 of the License, or 15 | * 16 | * (at your option) any later version. 17 | * 18 | * Reflectance Maps is distributed in the hope that it will be useful, 19 | * 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * 24 | * GNU Lesser General Public License for more details. 25 | * 26 | * You should have received a copy of the GNU Lesser General Public License 27 | * 28 | * along with this program. If not, see . 29 | */ 30 | 31 | /** 32 | * \file mathfunctions.cpp 33 | * \brief Implementation of mathematical functions. 34 | * \author Antoine Toisoul Le Cann 35 | * \date September, 1st, 2016 36 | * 37 | * Implementation of a several mmathematical functions. 38 | */ 39 | 40 | #include "mathfunctions.h" 41 | 42 | using namespace std; 43 | using namespace cv; 44 | 45 | /** 46 | * Clamp a number so it is in the range [inf ; sup]. 47 | * If numbersup, then the function returns sup. 49 | * If number is in the range [inf ; sup] then the function returns the number. 50 | * @param INPUT : value is a float which will be clamped. 51 | * @param INPUT : inf is the lower bound of the range. 52 | * @param INPUT : sup is the upper bound of the range. 53 | * @return The clamped value in the range [inf ; sup]. If sup < inf then the function returns the number. 54 | */ 55 | float clamp(float value, float inf, float sup) 56 | { 57 | float result = 0.0f; 58 | 59 | if(inf<=sup) 60 | { 61 | if(value < inf) 62 | result = inf; 63 | else if(value > sup) 64 | result = sup; 65 | else 66 | result = value; 67 | } 68 | else 69 | { 70 | cerr << "sup must be greater than inf !" << endl; 71 | return value; 72 | } 73 | 74 | return result; 75 | } 76 | 77 | /** 78 | * Normalizes a 3x1 vector stored in an OpenCV Mat. 79 | * @brief normalizeVector 80 | * @param vector 81 | */ 82 | void normalizeVector(Mat &vector) 83 | { 84 | float x = vector.at(0,0); 85 | float y = vector.at(1,0); 86 | float z = vector.at(2,0); 87 | float norm = sqrt(x*x+y*y+z*z); 88 | 89 | vector.at(0,0) = x/norm; 90 | vector.at(1,0) = y/norm; 91 | vector.at(2,0) = z/norm; 92 | 93 | } 94 | 95 | /** 96 | * Given a rotation axis and an angle defined by its cosine and sin, returns the corresponding rotation matrix. 97 | * @brief makeRotationMatrix 98 | * @param axis 99 | * @param sin 100 | * @param cos 101 | * @return 102 | */ 103 | Mat makeRotationMatrix(Point3f axis, float sin, float cos) 104 | { 105 | Mat rotationMatrix = Mat::zeros(3,3, CV_32FC1); 106 | 107 | //Q is the skew symmetric matrix 108 | Mat Q = Mat::zeros(3,3, CV_32FC1); 109 | 110 | Q.at(0,1) = -axis.z; 111 | Q.at(0,2) = axis.y; 112 | 113 | Q.at(1,0) = axis.z; 114 | Q.at(1,2) = -axis.x; 115 | 116 | Q.at(2,0) = axis.y; 117 | Q.at(2,1) = -axis.x; 118 | 119 | //Calculates the rotation matrix 120 | rotationMatrix += Mat::eye(3,3, CV_32FC1); 121 | rotationMatrix += sin*Q; 122 | rotationMatrix += (1.0-cos)*Q*Q; 123 | 124 | return rotationMatrix; 125 | } 126 | -------------------------------------------------------------------------------- /src/mathfunctions.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Reflectance Maps 3 | * 4 | * Authors: Antoine TOISOUL LE CANN 5 | * 6 | * Copyright © 2016 Antoine TOISOUL LE CANN 7 | * All rights reserved 8 | * 9 | * 10 | * Reflectance Maps is free software: you can redistribute it and/or modify 11 | * 12 | * it under the terms of the GNU Lesser General Public License as published by 13 | * 14 | * the Free Software Foundation, either version 3 of the License, or 15 | * 16 | * (at your option) any later version. 17 | * 18 | * Reflectance Maps is distributed in the hope that it will be useful, 19 | * 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * 24 | * GNU Lesser General Public License for more details. 25 | * 26 | * You should have received a copy of the GNU Lesser General Public License 27 | * 28 | * along with this program. If not, see . 29 | */ 30 | 31 | /** 32 | * \file mathfunctions.h 33 | * \brief Implementation of mathematical functions. 34 | * \author Antoine Toisoul Le Cann 35 | * \date September, 1st, 2016 36 | * 37 | * Implementation of a clamp function. 38 | */ 39 | 40 | #ifndef MATHFUNCTIONS 41 | #define MATHFUNCTIONS 42 | 43 | #define M_PI 3.14159265358979323846 44 | 45 | #include 46 | #include 47 | #include 48 | 49 | /*---- OpenCV ----*/ 50 | #include 51 | 52 | /** 53 | * Clamp a number so it is in the range [inf ; sup]. 54 | * If numbersup, then the function returns sup. 56 | * If number is in the range [inf ; sup] then the function returns the number. 57 | * @param INPUT : value is a float which will be clamped. 58 | * @param INPUT : inf is the lower bound of the range. 59 | * @param INPUT : sup is the upper bound of the range. 60 | * @return The clamped value in the range [inf ; sup]. If sup < inf then the function returns the number. 61 | */ 62 | float clamp(float value, float inf, float sup); 63 | 64 | /** 65 | * Normalizes a 3x1 vector stored in an OpenCV Mat. 66 | * @brief normalizeVector 67 | * @param vector 68 | */ 69 | void normalizeVector(cv::Mat &vector); 70 | 71 | /** 72 | * Given a rotation axis and an angle defined by its cosine and sin, returns the corresponding rotation matrix. 73 | * @brief makeRotationMatrix 74 | * @param axis 75 | * @param sin 76 | * @param cos 77 | * @return 78 | */ 79 | cv::Mat makeRotationMatrix(cv::Point3f axis, float sin, float cos); 80 | 81 | #endif // MATHFUNCTIONS 82 | 83 | -------------------------------------------------------------------------------- /src/reflectance.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Reflectance Maps 3 | * 4 | * Authors: Antoine TOISOUL LE CANN 5 | * 6 | * Copyright © 2016 Antoine TOISOUL LE CANN 7 | * All rights reserved 8 | * 9 | * 10 | * Reflectance Maps is free software: you can redistribute it and/or modify 11 | * 12 | * it under the terms of the GNU Lesser General Public License as published by 13 | * 14 | * the Free Software Foundation, either version 3 of the License, or 15 | * 16 | * (at your option) any later version. 17 | * 18 | * Reflectance Maps is distributed in the hope that it will be useful, 19 | * 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * 24 | * GNU Lesser General Public License for more details. 25 | * 26 | * You should have received a copy of the GNU Lesser General Public License 27 | * 28 | * along with this program. If not, see . 29 | */ 30 | 31 | /** 32 | * \file reflectance.cpp 33 | * \brief Implementation of functions related to the calculation of reflectance maps. 34 | * \author Antoine Toisoul Le Cann 35 | * \date September, 11th, 2016 36 | * 37 | * Implementation of functions related to the calculation of reflectance maps. 38 | */ 39 | 40 | #include "reflectance.h" 41 | 42 | using namespace std; 43 | using namespace cv; 44 | 45 | /** 46 | * Function to compute the reflectance maps given the path to the data folder and a bool that says if the 47 | * cross polarised data exists. 48 | * @brief computeMaps 49 | * @param pathToFolder 50 | * @param isCrossData 51 | */ 52 | void computeMaps(string pathToFolder, bool isCrossData) 53 | { 54 | 55 | //Load the mask object 56 | //Mask that represent the area where the calculations are done 57 | Mat mask = imread(pathToFolder + "/mask.JPG", CV_LOAD_IMAGE_COLOR); 58 | 59 | if(!mask.data) 60 | { 61 | cerr << "Could not load image : " << pathToFolder + "/mask.JPG" << endl; 62 | exit(-1); 63 | } 64 | else 65 | { 66 | mask.convertTo(mask, CV_32FC3); 67 | mask /= 255.0; 68 | } 69 | 70 | Mat parallelData[NUMBER_OF_GRADIENT_ILLUMINATION]; 71 | Mat crossData[NUMBER_OF_GRADIENT_ILLUMINATION]; 72 | 73 | ostringstream osstream; 74 | unsigned int imageNumberPar = 2855; 75 | unsigned int imageNumberCross = 2869; 76 | 77 | /*--Load images parallelPolarised ---*/ 78 | for(int i = 0 ; i> RParPicture >> GParPicture >> BParPicture >> checkerchartPar; 216 | 217 | ratioParR = atof(checkerchartPar.c_str())/atof(RParPicture.c_str()); 218 | ratioParG = atof(checkerchartPar.c_str())/atof(GParPicture.c_str()); 219 | ratioParB = atof(checkerchartPar.c_str())/atof(BParPicture.c_str()); 220 | 221 | cout << "Parallel " << ratioParR << " - " << ratioParG << " - "<< ratioParB << endl; 222 | 223 | int width = parallelData[0].cols; 224 | int height = parallelData[0].rows; 225 | 226 | /*--Scale parallel polarised values---*/ 227 | //White balancing with the checkerchart 228 | for(int k = 0 ; k(i,j).val[0] *= ratioParB; 236 | parallelData[k].at(i,j).val[1] *= ratioParG; 237 | parallelData[k].at(i,j).val[2] *= ratioParR; 238 | } 239 | } 240 | } 241 | 242 | //Scale down between 0 and 1 for the computation 243 | for(int k = 0 ; k> RParPicture >> GParPicture >> BParPicture >> checkerchartPar; 283 | 284 | ratioParR = atof(checkerchartPar.c_str())/atof(RParPicture.c_str()); 285 | ratioParG = atof(checkerchartPar.c_str())/atof(GParPicture.c_str()); 286 | ratioParB = atof(checkerchartPar.c_str())/atof(BParPicture.c_str()); 287 | 288 | checkerFile >> RCrossPicture >> GCrossPicture >> BCrossPicture >> checkerchartCross; 289 | 290 | ratioCrossR = atof(checkerchartCross.c_str())/atof(RCrossPicture.c_str()); 291 | ratioCrossG = atof(checkerchartCross.c_str())/atof(GCrossPicture.c_str()); 292 | ratioCrossB = atof(checkerchartCross.c_str())/atof(BCrossPicture.c_str()); 293 | 294 | cout << "Parallel " << ratioParR << " - " << ratioParG << " - "<< ratioParB << endl; 295 | cout << "Cross " << ratioCrossR << " - " << ratioCrossG << " - "<< ratioCrossB << endl; 296 | 297 | int width = parallelData[0].cols; 298 | int height = parallelData[0].rows; 299 | 300 | /*--Scale parallel polarised values---*/ 301 | //White balancing with the checkerchart 302 | for(int k = 0 ; k(i,j).val[0] *= ratioParB; 313 | parallelData[k].at(i,j).val[1] *= ratioParG; 314 | parallelData[k].at(i,j).val[2] *= ratioParR; 315 | 316 | crossData[k].at(i,j).val[0] *= ratioCrossB; 317 | crossData[k].at(i,j).val[1] *= ratioCrossG; 318 | crossData[k].at(i,j).val[2] *= ratioCrossR; 319 | } 320 | } 321 | } 322 | 323 | 324 | //After applying the checkerchart some pixels values might be above 1 325 | //Scale down between 0 and 1 for the computation 326 | for(int k = 0 ; k(i,j).val[1]-xGradient.at(i,j).val[1]; 414 | y = yGradient.at(i,j).val[1]-minusYGradient.at(i,j).val[1]; 415 | z = sqrt(1.0-x*x-y*y); 416 | norm = sqrt(x*x+y*y+z*z); 417 | 418 | //Normalise the reflection vector 419 | x /= norm; 420 | y /= norm; 421 | z /= norm; 422 | 423 | //(x,y,z) is the reflection vector 424 | //The normal is the hal vector V+R. V = (0,0,1) 425 | z += 1.0; 426 | 427 | //Normalise the normal 428 | norm = sqrt(x*x+y*y+z*z); 429 | 430 | x /= norm; 431 | y /= norm; 432 | z /= norm; 433 | 434 | normals.at(i,j).val[2] = x; 435 | normals.at(i,j).val[1] = y; 436 | normals.at(i,j).val[0] = z; 437 | 438 | } 439 | } 440 | 441 | alignAverageSurfaceNormal(normals, mask); 442 | 443 | //Color mapping 444 | for(int i = 0 ; i(i,j).val[2] = (normals.at(i,j).val[2]+1.0)/2.0; 450 | normals.at(i,j).val[1] = (normals.at(i,j).val[1]+1.0)/2.0; 451 | normals.at(i,j).val[0] = (normals.at(i,j).val[0]+1.0)/2.0; 452 | } 453 | } 454 | 455 | 456 | normals *= 255.0; 457 | normals.convertTo(normals, CV_8UC3); 458 | imwrite(pathToFolder + "/textures/normalMap.bmp", normals); 459 | } 460 | 461 | /** 462 | * Compute the specular normals given parallel and cross data. 463 | * Also requires a mask on which data is computed. 464 | * @brief computeNormals 465 | * @param parallelData 466 | * @param crossData 467 | * @param mask 468 | * @param pathToFolder 469 | */ 470 | void computeNormals(Mat parallelData[], const Mat &mask, string pathToFolder) 471 | { 472 | Mat xGradient, minusXGradient; 473 | Mat yGradient, minusYGradient;; 474 | Mat normals = Mat(parallelData[1].rows, parallelData[1].cols, CV_32FC3); 475 | 476 | xGradient = parallelData[1].clone(); 477 | minusXGradient = parallelData[2].clone(); 478 | 479 | yGradient = parallelData[3].clone(); 480 | minusYGradient = parallelData[4].clone(); 481 | 482 | float x = 0.0, y = 0.0, z = 0.0; 483 | float norm = 0.0; 484 | 485 | int width = parallelData[0].cols; 486 | int height = parallelData[0].rows; 487 | 488 | for(int i = 0 ; i(i,j).val[1]-xGradient.at(i,j).val[1]; 498 | y = yGradient.at(i,j).val[1]-minusYGradient.at(i,j).val[1]; 499 | z = sqrt(1.0-x*x-y*y); 500 | norm = sqrt(x*x+y*y+z*z); 501 | 502 | //Normalise the reflection vector 503 | x /= norm; 504 | y /= norm; 505 | z /= norm; 506 | 507 | //(x,y,z) is the reflection vector 508 | //The normal is the hal vector V+R. V = (0,0,1) 509 | z += 1.0; 510 | 511 | //Normalise the normal 512 | norm = sqrt(x*x+y*y+z*z); 513 | 514 | x /= norm; 515 | y /= norm; 516 | z /= norm; 517 | 518 | normals.at(i,j).val[2] = x; 519 | normals.at(i,j).val[1] = y; 520 | normals.at(i,j).val[0] = z; 521 | 522 | } 523 | } 524 | 525 | //Align the average surface normal with (0,0,1) (flat sample assumption) 526 | alignAverageSurfaceNormal(normals, mask); 527 | 528 | //Color mapping RGB = XYZ 529 | for(int i = 0 ; i(i,j).val[2] = (normals.at(i,j).val[2]+1.0)/2.0; 534 | normals.at(i,j).val[1] = (normals.at(i,j).val[1]+1.0)/2.0; 535 | normals.at(i,j).val[0] = (normals.at(i,j).val[0]+1.0)/2.0; 536 | } 537 | } 538 | 539 | 540 | normals *= 255.0; 541 | normals.convertTo(normals, CV_8UC3); 542 | //Save as BMP : no gamma! 543 | imwrite(pathToFolder + "/textures/normalMap.bmp", normals); 544 | } 545 | 546 | /** 547 | * Remove ambient illumination from a set of images. 548 | * @brief removeAmbientIllumination 549 | * @param images 550 | * @param numberOfImages 551 | * @param ambient 552 | */ 553 | void removeAmbientIllumination(Mat images[], int numberOfImages, const Mat &ambient) 554 | { 555 | for(int k = 0 ; k(i,j).val[2] >0.9) 585 | { 586 | if(isnan(normals.at(i,j).val[2]) || isnan(normals.at(i,j).val[1]) || isnan(normals.at(i,j).val[0])) 587 | { 588 | cout << "Nan Skip : " << i << " " << j << endl; 589 | } 590 | else 591 | { 592 | averageNormal.at(0,0) += normals.at(i,j).val[2]; 593 | averageNormal.at(1,0) += normals.at(i,j).val[1]; 594 | averageNormal.at(2,0) += normals.at(i,j).val[0]; 595 | numberOfNormalsAccounted += 1.0; 596 | } 597 | } 598 | } 599 | } 600 | 601 | 602 | averageNormal /= numberOfNormalsAccounted; 603 | normalizeVector(averageNormal); 604 | 605 | cout << "Average surface normal : " << averageNormal << endl; 606 | 607 | /*----Compute the rotation matrix---*/ 608 | //Align all the normals with normal with (0,0,1) 609 | Point3f zVector = Point3f(0.0, 0.0, 1.0); 610 | Point3f averageNormal3f = Point3f(averageNormal.at(0,0), averageNormal.at(1,0), averageNormal.at(2,0)); 611 | 612 | //Point3f axis = zVector.cross(averageNormal3f); 613 | Point3f axis = averageNormal3f.cross(zVector); 614 | 615 | float cos = zVector.dot(averageNormal3f); 616 | float sin = sqrt(axis.x*axis.x+axis.y*axis.y+axis.z*axis.z); 617 | 618 | Mat rotationMatrix = makeRotationMatrix(axis, sin, cos); 619 | 620 | /*----Align all the normals---*/ 621 | for(int i = 0 ; i(0,0) = normals.at(i,j).val[2]; 628 | currentNormal.at(1,0) = normals.at(i,j).val[1]; 629 | currentNormal.at(2,0) = normals.at(i,j).val[0]; 630 | 631 | currentNormal = rotationMatrix*currentNormal; 632 | 633 | normals.at(i,j).val[2] = currentNormal.at(0,0) ; 634 | normals.at(i,j).val[1] = currentNormal.at(1,0) ; 635 | normals.at(i,j).val[0] = currentNormal.at(2,0) ; 636 | } 637 | } 638 | } 639 | 640 | /** 641 | * Calculates the roughness using both cross and parallel polarised data. 642 | * @brief computeRoughness 643 | * @param parallelData 644 | * @param crossData 645 | * @param pathToFolder 646 | */ 647 | void computeRoughness(Mat parallelData[], Mat crossData[], string pathToFolder) 648 | { 649 | Mat xGradient, minusXGradient; 650 | Mat yGradient, minusYGradient;; 651 | 652 | //Remove the cross data from the parallel data for the L1 moments 653 | xGradient = parallelData[1].clone()-crossData[1].clone(); 654 | minusXGradient = parallelData[2].clone()-crossData[2].clone(); 655 | 656 | yGradient = parallelData[3].clone()-crossData[3].clone(); 657 | minusYGradient = parallelData[4].clone()-crossData[4].clone(); 658 | 659 | Mat horizontalGradient = minusXGradient-xGradient; 660 | Mat verticalGradient = yGradient-minusYGradient; 661 | 662 | //The general formula for the roughness is sigma^2 = L1^2/L0-L2/L0 with L1 and L2 the first and second order moments. 663 | //Compute it in the x direction and the y direction 664 | divide(horizontalGradient, parallelData[0]-crossData[0], horizontalGradient); 665 | divide(verticalGradient, parallelData[0]-crossData[0], verticalGradient); 666 | 667 | Mat secondOrderGradientX = parallelData[5]; 668 | Mat secondOrderGradientY = parallelData[6]; 669 | 670 | divide(secondOrderGradientX, parallelData[0]-crossData[0], secondOrderGradientX); 671 | divide(secondOrderGradientY, parallelData[0]-crossData[0], secondOrderGradientY); 672 | 673 | secondOrderGradientX -= horizontalGradient.mul(horizontalGradient); 674 | secondOrderGradientY -= verticalGradient.mul(verticalGradient); 675 | 676 | //Calculate the final roughness is sigma^2 = sqrt(sigma_x^4+sigma_y^4)/4 677 | Mat roughness = Mat::zeros(secondOrderGradientX.rows, secondOrderGradientX.cols, CV_32FC3); 678 | 679 | int height = parallelData[0].rows; 680 | int width = parallelData[0].cols; 681 | 682 | for(int i = 0 ; i(i,j).val[0] = sqrt(secondOrderGradientX.at(i,j).val[1]*secondOrderGradientX.at(i,j).val[1] 687 | +secondOrderGradientY.at(i,j).val[1]*secondOrderGradientY.at(i,j).val[1]); 688 | 689 | roughness.at(i,j).val[1] = sqrt(secondOrderGradientX.at(i,j).val[1]*secondOrderGradientX.at(i,j).val[1] 690 | +secondOrderGradientY.at(i,j).val[1]*secondOrderGradientY.at(i,j).val[1]); 691 | 692 | roughness.at(i,j).val[2] = sqrt(secondOrderGradientX.at(i,j).val[1]*secondOrderGradientX.at(i,j).val[1] 693 | +secondOrderGradientY.at(i,j).val[1]*secondOrderGradientY.at(i,j).val[1]); 694 | } 695 | } 696 | 697 | roughness /= 4.0; 698 | 699 | savePFM(roughness, pathToFolder + "/textures/roughness.pfm"); 700 | 701 | } 702 | 703 | /** 704 | * Calculates the roughness using only parallel polarised data. 705 | * @brief computeRoughness 706 | * @param parallelData 707 | * @param pathToFolder 708 | */ 709 | void computeRoughness(Mat parallelData[], string pathToFolder) 710 | { 711 | Mat xGradient, minusXGradient; 712 | Mat yGradient, minusYGradient;; 713 | 714 | xGradient = parallelData[1].clone(); 715 | minusXGradient = parallelData[2].clone(); 716 | 717 | yGradient = parallelData[3].clone(); 718 | minusYGradient = parallelData[4].clone(); 719 | 720 | //The general formula for the roughness is sigma^2 = L1^2/L0-L2/L0 with L1 and L2 the first and second order moments. 721 | //Compute it in the x direction and the y direction 722 | Mat horizontalGradient = minusXGradient-xGradient; 723 | Mat verticalGradient = yGradient-minusYGradient; 724 | 725 | divide(horizontalGradient, parallelData[0], horizontalGradient); 726 | divide(verticalGradient, parallelData[0], verticalGradient); 727 | 728 | Mat secondOrderGradientX = parallelData[5]; 729 | Mat secondOrderGradientY = parallelData[6]; 730 | 731 | divide(secondOrderGradientX, parallelData[0], secondOrderGradientX); 732 | divide(secondOrderGradientY, parallelData[0], secondOrderGradientY); 733 | 734 | secondOrderGradientX -= horizontalGradient.mul(horizontalGradient); 735 | secondOrderGradientY -= verticalGradient.mul(verticalGradient); 736 | 737 | //Calculate the final roughness is sigma^2 = sqrt(sigma_x^4+sigma_y^4)/4 738 | Mat roughness = Mat::zeros(secondOrderGradientX.rows, secondOrderGradientX.cols, CV_32FC3); 739 | 740 | int height = parallelData[0].rows; 741 | int width = parallelData[0].cols; 742 | 743 | for(int i = 0 ; i(i,j).val[0] = sqrt(sqrt(secondOrderGradientX.at(i,j).val[1]*secondOrderGradientX.at(i,j).val[1] 749 | +secondOrderGradientY.at(i,j).val[1]*secondOrderGradientY.at(i,j).val[1])); 750 | 751 | roughness.at(i,j).val[1] = sqrt(sqrt(secondOrderGradientX.at(i,j).val[1]*secondOrderGradientX.at(i,j).val[1] 752 | +secondOrderGradientY.at(i,j).val[1]*secondOrderGradientY.at(i,j).val[1])); 753 | 754 | roughness.at(i,j).val[2] = sqrt(sqrt(secondOrderGradientX.at(i,j).val[1]*secondOrderGradientX.at(i,j).val[1] 755 | +secondOrderGradientY.at(i,j).val[1]*secondOrderGradientY.at(i,j).val[1])); 756 | } 757 | } 758 | 759 | roughness /= 4.0; 760 | savePFM(roughness, pathToFolder + "/textures/roughness.pfm"); 761 | } 762 | -------------------------------------------------------------------------------- /src/reflectance.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Reflectance Maps 3 | * 4 | * Authors: Antoine TOISOUL LE CANN 5 | * 6 | * Copyright © 2016 Antoine TOISOUL LE CANN 7 | * All rights reserved 8 | * 9 | * 10 | * Reflectance Maps is free software: you can redistribute it and/or modify 11 | * 12 | * it under the terms of the GNU Lesser General Public License as published by 13 | * 14 | * the Free Software Foundation, either version 3 of the License, or 15 | * 16 | * (at your option) any later version. 17 | * 18 | * Reflectance Maps is distributed in the hope that it will be useful, 19 | * 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * 24 | * GNU Lesser General Public License for more details. 25 | * 26 | * You should have received a copy of the GNU Lesser General Public License 27 | * 28 | * along with this program. If not, see . 29 | */ 30 | 31 | /** 32 | * \file reflectance.h 33 | * \brief Implementation of functions related to the calculation of reflectance maps. 34 | * \author Antoine Toisoul Le Cann 35 | * \date September, 11th, 2016 36 | * 37 | * Implementation of functions related to the calculation of reflectance maps. 38 | */ 39 | 40 | #ifndef REFLECTANCE 41 | #define REFLECTANCE 42 | 43 | #define M_PI 3.14159265358979323846 44 | #define NUMBER_OF_GRADIENT_ILLUMINATION 7 45 | 46 | #include "imageprocessing.h" 47 | #include "mathfunctions.h" 48 | #include "PFMReadWrite.h" 49 | 50 | /*---- OpenCV ----*/ 51 | #include 52 | 53 | /*---- Standard library ----*/ 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | 60 | #include "mathfunctions.h" 61 | #include "imageprocessing.h" 62 | 63 | /** 64 | * Function to compute the reflectance maps given the path to the data folder and a bool that says if the 65 | * cross polarised data exists. 66 | * @brief computeMaps 67 | * @param pathToFolder 68 | * @param isCrossData 69 | */ 70 | void computeMaps(std::string pathToFolder, bool isCrossData); 71 | 72 | 73 | /** 74 | * Scale the value of parallel polarised data to the value of the checkerchart. 75 | * @brief checkerchartScaling 76 | * @param parallelData 77 | * @param mask 78 | * @param pathToFolder 79 | */ 80 | void checkerchartScaling(cv::Mat parallelData[], const cv::Mat &mask, std::string pathToFolder); 81 | 82 | /** 83 | * Scale the value of parallel and cross polarised data to the value of the checkerchart. 84 | * @brief checkerchartScaling 85 | * @param parallelData 86 | * @param crossData 87 | * @param mask 88 | * @param pathToFolder 89 | */ 90 | void checkerchartScaling(cv::Mat parallelData[], cv::Mat crossData[], const cv::Mat &mask, std::string pathToFolder); 91 | 92 | /** 93 | * Compute the diffuse and specular albedo given parallel and cross polarised data. 94 | * Also requires a mask on which data is computed. 95 | * @brief checkerchartScaling 96 | * @param parallelData 97 | * @param mask 98 | * @param pathToFolder 99 | */ 100 | void diffuseSpecularSeparation(cv::Mat parallelData[], cv::Mat crossData[], const cv::Mat &mask, std::string pathToFolder); 101 | 102 | /** 103 | * Compute the specular normals given parallel and cross data. 104 | * Also requires a mask on which data is computed. 105 | * The sample is assumed to be almost flat without big variations in the z component of the normal. 106 | * Therefore the measurements have been made without the zGradients. 107 | * @param parallelData 108 | * @param crossData 109 | * @param mask 110 | * @param pathToFolder 111 | */ 112 | void computeNormals(cv::Mat parallelData[], cv::Mat crossData[], const cv::Mat &mask, std::string pathToFolder); 113 | 114 | /** 115 | * Compute the specular normals given parallel and cross data. 116 | * Also requires a mask on which data is computed. 117 | * The sample is assumed to be almost flat without big variations in the z component of the normal. 118 | * Therefore the measurements have been made without the zGradients. 119 | * @brief computeNormals 120 | * @param parallelData 121 | * @param crossData 122 | * @param mask 123 | * @param pathToFolder 124 | */ 125 | void computeNormals(cv::Mat parallelData[], const cv::Mat &mask, std::string pathToFolder); 126 | 127 | /** 128 | * Remove ambient illumination from a set of images. 129 | * @brief removeAmbientIllumination 130 | * @param images 131 | * @param numberOfImages 132 | * @param ambient 133 | */ 134 | void removeAmbientIllumination(cv::Mat images[], int numberOfImages, const cv::Mat &ambient); 135 | 136 | /** 137 | * Rotates the normals so that the average surface normal is (0,0,1) 138 | * @brief alignAverageSurfaceNormal 139 | * @param normals 140 | * @param mask 141 | */ 142 | void alignAverageSurfaceNormal(cv::Mat &normals, const cv::Mat &mask); 143 | 144 | /** 145 | * Calculates the roughness using both cross and parallel polarised data. 146 | * @brief computeRoughness 147 | * @param parallelData 148 | * @param crossData 149 | * @param pathToFolder 150 | */ 151 | void computeRoughness(cv::Mat parallelData[], cv::Mat crossData[], std::string pathToFolder); 152 | 153 | /** 154 | * Calculates the roughness using only parallel polarised data. 155 | * @brief computeRoughness 156 | * @param parallelData 157 | * @param pathToFolder 158 | */ 159 | void computeRoughness(cv::Mat parallelData[], std::string pathToFolder); 160 | 161 | #endif // REFLECTANCE 162 | 163 | -------------------------------------------------------------------------------- /src/reflectance_maps.pro: -------------------------------------------------------------------------------- 1 | QT += core gui 2 | QT += opengl 3 | 4 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport 5 | 6 | TARGET = reflectance_maps 7 | TEMPLATE = app 8 | 9 | 10 | SOURCES += main.cpp \ 11 | PFMReadWrite.cpp \ 12 | reflectance.cpp \ 13 | imageprocessing.cpp \ 14 | mathfunctions.cpp 15 | 16 | 17 | 18 | 19 | HEADERS += \ 20 | PFMReadWrite.h \ 21 | reflectance.h \ 22 | imageprocessing.h \ 23 | mathfunctions.h 24 | 25 | ##################### OpenCV ############################## 26 | 27 | 28 | 29 | win32:{ 30 | CONFIG(debug, debug|release) 31 | { 32 | 33 | INCLUDEPATH += "C:\\OpenCV2411\\build\\include" 34 | 35 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_core2411.lib" 36 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_highgui2411.lib" 37 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_imgproc2411.lib" 38 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_features2d2411.lib" 39 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_calib3d2411.lib" 40 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_contrib2411.lib" 41 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_flann2411.lib" 42 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_gpu2411.lib" 43 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_legacy2411.lib" 44 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_ml2411.lib" 45 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_nonfree2411.lib" 46 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_objdetect2411.lib" 47 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_ocl2411.lib" 48 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_photo2411.lib" 49 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_stitching2411.lib" 50 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_superres2411.lib" 51 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_ts2411.lib" 52 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_video2411.lib" 53 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_videostab2411.lib" 54 | 55 | } 56 | CONFIG(release, debug|release) 57 | { 58 | INCLUDEPATH += "C:\\OpenCV2411\\build\\include" 59 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_core2411d.lib" 60 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_highgui2411d.lib" 61 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_imgproc2411d.lib" 62 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_features2d2411d.lib" 63 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_calib3d2411d.lib" 64 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_contrib2411d.lib" 65 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_flann2411d.lib" 66 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_gpu2411d.lib" 67 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_legacy2411d.lib" 68 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_ml2411d.lib" 69 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_nonfree2411d.lib" 70 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_objdetect2411d.lib" 71 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_ocl2411d.lib" 72 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_photo2411d.lib" 73 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_stitching2411d.lib" 74 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_superres2411d.lib" 75 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_ts2411d.lib" 76 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_video2411d.lib" 77 | LIBS += "C:\\OpenCV2411\\build\\x64\\vc12\\lib\\opencv_videostab2411d.lib" 78 | } 79 | } 80 | else:unix{ 81 | INCLUDEPATH += /usr/local/include/ 82 | LIBS += -L/usr/local/lib 83 | LIBS += -lopencv_core 84 | LIBS += -lopencv_imgproc 85 | LIBS += -lopencv_highgui 86 | LIBS += -lopencv_ml 87 | LIBS += -lopencv_video 88 | LIBS += -lopencv_features2d 89 | LIBS += -lopencv_calib3d 90 | LIBS += -lopencv_objdetect 91 | LIBS += -lopencv_contrib 92 | LIBS += -lopencv_legacy 93 | LIBS += -lopencv_flann 94 | } 95 | --------------------------------------------------------------------------------