├── .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 |
--------------------------------------------------------------------------------