├── HfPol.m ├── License.md ├── README.md ├── comparison ├── LambertianSFP.m ├── Propagation.m └── lsqintegration.m ├── demo.m ├── sampleData.mat └── utils ├── PolarisationImage.m ├── TRSfit.m ├── boundaryPrior.m ├── findLight.m ├── pad.m ├── rho_diffuse.m └── rho_spec.m /HfPol.m: -------------------------------------------------------------------------------- 1 | function [ height ] = HfPol( theta,diffuse,phi,l,mask,verbose,spec,azi,weight ) 2 | %HFPS Height from polarisation 3 | % Inputs: 4 | % theta - zenith angle from polarisation 5 | % diffuse - diffuse image (i.e. unpolarised appearance) 6 | % phi - phase angle 7 | % (the above are all matrices size rows*cols) 8 | % l - 3D column vector containing point light source 9 | % mask - rows*cols binary foreground mask 10 | % verbose - if true, it will print out some details on the LS system 11 | % spec - a binary spec mask (subset of mask) 12 | % azi - priors on the azimuth, e.g. from a convexity prior 13 | % weight - per-pixel weight for the azimuth prior 14 | % 15 | % Ouputs: 16 | % height - rows*cols estimated height map 17 | % 18 | % William Smith 2016 19 | 20 | % Todo: tidy up parameters so just pass an options parameter containing all 21 | % optional params, with sensible defaults 22 | 23 | % Note: this is a parameter - need to move this to options later 24 | % Smoothing weight. lambda=0 => no smoothing. 25 | lambda=0.1; 26 | 27 | if nargin<6 28 | verbose=true; 29 | end 30 | 31 | rows = size(mask,1); 32 | cols = size(mask,2); 33 | 34 | f = cos(theta); 35 | 36 | % Pad to avoid boundary problems 37 | f = pad(f); 38 | diffuse = pad(diffuse); 39 | mask = pad(mask); 40 | phi = pad(phi); 41 | theta = pad(theta); 42 | if nargin>=7 43 | spec = pad(spec); 44 | else 45 | spec = zeros(size(mask)); 46 | end 47 | 48 | if nargin==9 49 | weight = pad(weight); 50 | azi = pad(azi); 51 | end 52 | 53 | rows = rows+2; 54 | cols = cols+2; 55 | 56 | % Compute halfway vector 57 | H = (l+[0 0 1]')./norm(l+[0 0 1]'); 58 | Hp = -H(1)./H(3); 59 | Hq = -H(2)./H(3); 60 | 61 | % Build lookup table relating x,y coordinate of valid pixels to index 62 | % position in vectorised representation 63 | count = 0; 64 | indices = zeros(size(mask)); 65 | for row=1:rows 66 | for col=1:cols 67 | if mask(row,col) 68 | count=count+1; 69 | indices(row,col)=count; 70 | end 71 | end 72 | end 73 | 74 | % Create mask for 4-neighbours for Laplacian smoothing 75 | h = [0 1 0; 1 0 1; 0 1 0]; 76 | mask4n = imfilter(1.*mask,h,'conv')==4; 77 | 78 | % The number of usable pixels 79 | npix = sum(mask(:)); 80 | 81 | if verbose 82 | disp(['Using ' num2str(npix) ' pixels']); 83 | end 84 | 85 | % Preallocate maximum required space 86 | % This would be if all valid pixels had equations for all 8 neighbours for 87 | % all possible pairs of images - it will be less in practice 88 | i = zeros(npix*8*2+sum(mask4n(:))*4,1); 89 | j = zeros(npix*8*2+sum(mask4n(:))*4,1); 90 | s = zeros(npix*8*2+sum(mask4n(:))*4,1); 91 | 92 | % Right hand side of linear system: 93 | d = zeros(npix*2+sum(mask4n(:)),1); 94 | 95 | NumEq = 0; % number of rows in matrix 96 | k=0; % total number of non-zero entries in matrix 97 | 98 | for row=1:rows 99 | for col=1:cols 100 | if mask(row,col) 101 | if mask4n(row,col) && lambda~=0 102 | % Add Laplacian smoothing term 103 | NumEq=NumEq+1; 104 | d(NumEq)=0; 105 | k=k+1; 106 | i(k)=NumEq; j(k)=indices(row,col); s(k)=-4*lambda; 107 | % Edge neighbours 108 | k=k+1; 109 | i(k)=NumEq; j(k)=indices(row,col-1); s(k)=1*lambda; 110 | k=k+1; 111 | i(k)=NumEq; j(k)=indices(row,col+1); s(k)=1*lambda; 112 | k=k+1; 113 | i(k)=NumEq; j(k)=indices(row-1,col); s(k)=1*lambda; 114 | k=k+1; 115 | i(k)=NumEq; j(k)=indices(row+1,col); s(k)=1*lambda; 116 | end 117 | if nargin==9 118 | if spec(row,col) 119 | maxeq = 5; 120 | else 121 | maxeq = 4; 122 | end 123 | else 124 | if spec(row,col) 125 | maxeq = 3; 126 | else 127 | maxeq = 2; 128 | end 129 | end 130 | for eq=1:maxeq 131 | 132 | % Start by computing p and q weights and RHSs of eqs 133 | if spec(row,col) 134 | if eq==1 % Half vector constraint 135 | RHS = Hp; 136 | xval = 1; 137 | yval = 0; 138 | elseif eq==2 % Half vector constraint 139 | RHS = Hq; 140 | xval = 0; 141 | yval = 1; 142 | elseif eq==3 % Phase angle constraint 143 | RHS = 0; 144 | xval = cos(phi(row,col)); 145 | yval = -sin(phi(row,col)); 146 | elseif eq==4 % Azimuth prior from boundary (weighted) 147 | RHS = -weight(row,col)*(sin(azi(row,col))*sin(theta(row,col))); 148 | xval = weight(row,col)*cos(theta(row,col)); 149 | yval = 0; 150 | elseif eq==5 % Azimuth prior from boundary (weighted) 151 | RHS = -weight(row,col)*(cos(azi(row,col))*sin(theta(row,col))); 152 | xval = 0; 153 | yval = weight(row,col)*cos(theta(row,col)); 154 | end 155 | else 156 | if eq==1 % Phase angle constraint 157 | RHS = 0; 158 | xval = cos(phi(row,col)); 159 | yval = -sin(phi(row,col)); 160 | elseif eq==2 % Ratio between DOP and Lambertian constraint 161 | RHS = diffuse(row,col)/f(row,col) - l(3); 162 | xval = -l(1); 163 | yval = -l(2); 164 | elseif eq==3 % Azimuth prior from boundary (weighted) 165 | RHS = -weight(row,col)*(sin(azi(row,col))*sin(theta(row,col))); 166 | xval = weight(row,col)*cos(theta(row,col)); 167 | yval = 0; 168 | elseif eq==4 % Azimuth prior from boundary (weighted) 169 | RHS = -weight(row,col)*(cos(azi(row,col))*sin(theta(row,col))); 170 | xval = 0; 171 | yval = weight(row,col)*cos(theta(row,col)); 172 | end 173 | end 174 | 175 | 176 | % Now decide which combination of neighbours are present 177 | % This determines which version of the numerical 178 | % approximation to the surface gradients will be used 179 | 180 | if mask(row,col-1) && mask(row,col+1) 181 | % Both X neighbours present 182 | if mask(row-1,col) 183 | if mask(row+1,col) 184 | if mask(row-1,col-1) && mask(row-1,col+1) && mask(row+1,col-1) && mask(row+1,col+1) 185 | % All 8 neighbours present 186 | NumEq=NumEq+1; 187 | d(NumEq)=RHS; 188 | % Edge neighbours 189 | k=k+1; 190 | i(k)=NumEq; j(k)=indices(row,col-1); s(k)=-(4/12)*xval; 191 | k=k+1; 192 | i(k)=NumEq; j(k)=indices(row,col+1); s(k)=(4/12)*xval; 193 | k=k+1; 194 | i(k)=NumEq; j(k)=indices(row-1,col); s(k)=(4/12)*yval; 195 | k=k+1; 196 | i(k)=NumEq; j(k)=indices(row+1,col); s(k)=-(4/12)*yval; 197 | % Corner neighbours 198 | k=k+1; 199 | i(k)=NumEq; j(k)=indices(row-1,col-1); s(k)=-(1/12)*xval+(1/12)*yval; 200 | k=k+1; 201 | i(k)=NumEq; j(k)=indices(row-1,col+1); s(k)=(1/12)*xval+(1/12)*yval; 202 | k=k+1; 203 | i(k)=NumEq; j(k)=indices(row+1,col-1); s(k)=-(1/12)*xval-(1/12)*yval; 204 | k=k+1; 205 | i(k)=NumEq; j(k)=indices(row+1,col+1); s(k)=(1/12)*xval-(1/12)*yval; 206 | else 207 | % All 4 neighbours present 208 | NumEq=NumEq+1; 209 | d(NumEq)=RHS; 210 | k=k+1; 211 | i(k)=NumEq; j(k)=indices(row,col-1); s(k)=-xval/2; 212 | k=k+1; 213 | i(k)=NumEq; j(k)=indices(row,col+1); s(k)=xval/2; 214 | k=k+1; 215 | i(k)=NumEq; j(k)=indices(row-1,col); s(k)=yval/2; 216 | k=k+1; 217 | i(k)=NumEq; j(k)=indices(row+1,col); s(k)=-yval/2; 218 | end 219 | else 220 | % Both X, only forward in Y 221 | NumEq=NumEq+1; 222 | d(NumEq)=RHS; 223 | k=k+1; 224 | i(k)=NumEq; j(k)=indices(row,col-1); s(k)=-xval/2; 225 | k=k+1; 226 | i(k)=NumEq; j(k)=indices(row,col+1); s(k)=xval/2; 227 | k=k+1; 228 | i(k)=NumEq; j(k)=indices(row-1,col); s(k)=yval; 229 | k=k+1; 230 | i(k)=NumEq; j(k)=indices(row,col); s(k)=-yval; 231 | end 232 | elseif mask(row+1,col) 233 | % Both X, only backward in Y 234 | NumEq=NumEq+1; 235 | d(NumEq)=RHS; 236 | k=k+1; 237 | i(k)=NumEq; j(k)=indices(row,col-1); s(k)=-xval/2; 238 | k=k+1; 239 | i(k)=NumEq; j(k)=indices(row,col+1); s(k)=xval/2; 240 | k=k+1; 241 | i(k)=NumEq; j(k)=indices(row+1,col); s(k)=-yval; 242 | k=k+1; 243 | i(k)=NumEq; j(k)=indices(row,col); s(k)=yval; 244 | end 245 | elseif mask(row,col-1) 246 | % Only backward in X 247 | if mask(row-1,col) 248 | if mask(row+1,col) 249 | % Backward in X, both in Y 250 | NumEq=NumEq+1; 251 | d(NumEq)=RHS; 252 | k=k+1; 253 | i(k)=NumEq; j(k)=indices(row,col-1); s(k)=-xval; 254 | k=k+1; 255 | i(k)=NumEq; j(k)=indices(row,col); s(k)=xval; 256 | k=k+1; 257 | i(k)=NumEq; j(k)=indices(row-1,col); s(k)=yval/2; 258 | k=k+1; 259 | i(k)=NumEq; j(k)=indices(row+1,col); s(k)=-yval/2; 260 | else 261 | % Backward in X, only forward in Y 262 | NumEq=NumEq+1; 263 | d(NumEq)=RHS; 264 | k=k+1; 265 | i(k)=NumEq; j(k)=indices(row,col-1); s(k)=-xval; 266 | k=k+1; 267 | i(k)=NumEq; j(k)=indices(row,col); s(k)=xval-yval; 268 | k=k+1; 269 | i(k)=NumEq; j(k)=indices(row-1,col); s(k)=yval; 270 | end 271 | elseif mask(row+1,col) 272 | % Backward in X, only backward in Y 273 | NumEq=NumEq+1; 274 | d(NumEq)=RHS; 275 | k=k+1; 276 | i(k)=NumEq; j(k)=indices(row,col-1); s(k)=-xval; 277 | k=k+1; 278 | i(k)=NumEq; j(k)=indices(row,col); s(k)=xval+yval; 279 | k=k+1; 280 | i(k)=NumEq; j(k)=indices(row+1,col); s(k)=-yval; 281 | end 282 | elseif mask(row,col+1) 283 | % Only forward in X 284 | if mask(row-1,col) 285 | if mask(row+1,col) 286 | % Forward in X, both in Y 287 | NumEq=NumEq+1; 288 | d(NumEq)=RHS; 289 | k=k+1; 290 | i(k)=NumEq; j(k)=indices(row,col+1); s(k)=xval; 291 | k=k+1; 292 | i(k)=NumEq; j(k)=indices(row,col); s(k)=-xval; 293 | k=k+1; 294 | i(k)=NumEq; j(k)=indices(row-1,col); s(k)=yval/2; 295 | k=k+1; 296 | i(k)=NumEq; j(k)=indices(row+1,col); s(k)=-yval/2; 297 | else 298 | % Forward in X, only forward in Y 299 | NumEq=NumEq+1; 300 | d(NumEq)=RHS; 301 | k=k+1; 302 | i(k)=NumEq; j(k)=indices(row,col+1); s(k)=xval; 303 | k=k+1; 304 | i(k)=NumEq; j(k)=indices(row,col); s(k)=-xval-yval; 305 | k=k+1; 306 | i(k)=NumEq; j(k)=indices(row-1,col); s(k)=yval; 307 | end 308 | elseif mask(row+1,col) 309 | % Forward in X, only backward in Y 310 | NumEq=NumEq+1; 311 | d(NumEq)=RHS; 312 | k=k+1; 313 | i(k)=NumEq; j(k)=indices(row,col+1); s(k)=xval; 314 | k=k+1; 315 | i(k)=NumEq; j(k)=indices(row,col); s(k)=-xval+yval; 316 | k=k+1; 317 | i(k)=NumEq; j(k)=indices(row+1,col); s(k)=-yval; 318 | end 319 | end 320 | end 321 | end 322 | % Finished with a pixel 323 | end 324 | end 325 | 326 | if verbose 327 | disp(['System contains ' num2str(NumEq) ' linear equations with ' num2str(k) ' non-zero entries in C']); 328 | end 329 | 330 | i=i(1:k,1); 331 | j=j(1:k,1); 332 | s=s(1:k,1); 333 | d=d(1:NumEq,1); 334 | 335 | % Fix one pixel's height to zero to resolve unknown constant of integration 336 | i(k+1)=NumEq+1; 337 | j(k+1)=1; 338 | s(k+1)=1; 339 | d(NumEq+1)=0; 340 | 341 | if sum(isnan(d))>0 342 | warning('d contains NaNs - output will be all NaN'); 343 | end 344 | 345 | % Build matrix +1 (for constraint on pixel 1) 346 | C = sparse(i,j,s,NumEq+1,npix); 347 | if verbose 348 | tic 349 | end 350 | z = C\d; 351 | if verbose 352 | toc 353 | end 354 | 355 | % Copy vector of height values back into appropriate pixel positions 356 | height=zeros(size(mask)).*NaN; 357 | count=0; 358 | for row=1:rows 359 | for col=1:cols 360 | if mask(row,col) 361 | count=count+1; 362 | height(row,col)=z(count); 363 | end 364 | end 365 | end 366 | 367 | % Unpad 368 | height = height(2:rows-1,2:cols-1); 369 | 370 | end 371 | 372 | -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Linear depth estimation from an uncalibrated, monocular polarisation image 2 | 3 | This is a Matlab implementation of our ECCV 2016 paper "Linear depth estimation from an uncalibrated, monocular polarisation image". It also includes an implementation of polarimetric image decomposition (linear and nonlinear optimisation), two comparison shape-from-polarisation methods, a simple least squares surface integration method (which supports a foreground mask) and a basic method for pixel-wise specular labelling. 4 | 5 | Note: I am in the process of cleaning up the code and adding to the repository. I will update the list of what has been uploaded as I go along. Content included so far: 6 | 7 | 1. Comparison methods 8 | 2. Least squares integrator 9 | 3. Polarimetric image decomposition 10 | 4. Diffuse polarisation model (degree of polarisation to zenith angle) 11 | 5. Light source estimation 12 | 6. Height from polarisation 13 | 14 | Still to add: 15 | 16 | 1. Specular model, specular labelling 17 | 2. Boundary prior (computing boundary azimuth and weight) 18 | 3. Sample datasets 19 | 4. Code for generating synthetic datasets and evaluating 20 | 21 | I will add documentation and demo scripts as I upload the code. 22 | 23 | ## Polarimetric image decomposition 24 | 25 | The first thing you need to do is convert your captured image into a 3-channel polarisation image. The function that does this is PolarisationImage.m. Inputs are: 26 | 27 | 1. images - 3D array containing captured images of size rows by cols by nimages 28 | 2. angles - vector of length nimages containing polariser angles (I use a coordinate system where the polariser angle is measured from the upward vertical axis, increasing in a clockwise direction if viewed looking into the camera lens) 29 | 3. (optional) mask - binary foreground mask of size rows by cols 30 | 4. (optional) method - either 'linear' or 'nonlinear', default: linear 31 | 32 | It returns rho (degree of polarisation), phi (phase angle) and Iun (unpolarised intensity). 33 | 34 | Sample call: 35 | 36 | ```matlab 37 | [ rho,phi,Iun ] = PolarisationImages( images,angles,mask,'linear' ); 38 | figure; imagesc(rho); colorbar 39 | figure; imagesc(phi); colorbar 40 | figure; imshow(Iun) 41 | ``` 42 | 43 | ## Convert degree of polarisation to zenith angle 44 | 45 | For the time being, I only include the diffuse polarisation model. So to convert the degree of polarisation (rho) to a zenith angle, you simply do: 46 | 47 | ```matlab 48 | theta = rho_diffuse(rho,n); 49 | ``` 50 | 51 | where n is the index of refraction (we use n=1.5 in the paper). 52 | 53 | ## Light source estimation 54 | 55 | If you do not know your light source direction (or if you know it, but don't know the light source intensity/uniform albedo) then the next thing you need to do is estimate it. This is done using the findLight function. It can estimate point source or spherical harmonic order 1 or 2 lighting. A basic call for point source lighting where you don't know the direction would be: 56 | 57 | ```matlab 58 | [ l,T,B ] = findLight( theta,phi,diffuse,mask,3 ); 59 | ``` 60 | 61 | Alternatively, if you know the direction is [1 0 1] but need to scale to account for unknown intensity/albedo, then do: 62 | 63 | ```matlab 64 | l = [1 0 1]'./norm([1 0 1]); 65 | [ l,T,B ] = findLight( theta,phi,diffuse,mask,3,l ); 66 | ``` 67 | 68 | For other options and spherical harmonic lighting, see the documentation. 69 | 70 | ## Computing depth 71 | 72 | Finally, we are ready to compute depth. Again, this is the most basic call assuming only diffuse pixels: 73 | 74 | ```matlab 75 | [ height ] = HfPol( theta,diffuse,phi,l,mask ); 76 | ``` 77 | 78 | To display the height map, you can do, for example: 79 | 80 | ```matlab 81 | figure; surf(height'); axis equal 82 | ``` 83 | -------------------------------------------------------------------------------- /comparison/LambertianSFP.m: -------------------------------------------------------------------------------- 1 | function [ N,height ] = LambertianSFP( rho,phi,mask,n,s,albedo,Iun ) 2 | %LAMBERTIANSFP Shape-from-polarisation with Lambertian model 3 | % Inputs: 4 | % rho - rows by cols matrix of DOP values 5 | % phi - rows by cols matrix of phase angles 6 | % mask - rows by cols binary foreground mask 7 | % n - refractive index 8 | % s - light source direction 9 | % albedo - diffuse albedo (can be scalar or per-pixel) 10 | % Iun - unpolarised intensity 11 | % 12 | % Outputs: 13 | % N - rows by cols by 3 matrix containing surface normals 14 | % height - height map obtained by integrating N using lsqintegration 15 | % 16 | % This is a re-implementation of the technique used in: 17 | % 18 | % Mahmoud, A.H., El-Melegy, M.T., Farag, A.A.: Direct method for shape 19 | % recovery from polarization and shading. In: Proc. ICIP. (2012) 1769?1772 20 | % 21 | % William Smith 22 | % 2016 23 | 24 | % Invert degree of diffuse polarisation expression to compute zenith angle 25 | temp = ((2.*rho + 2.*n.^2.*rho - 2.*n.^2 + n.^4 + rho.^2 + 4.*n.^2*rho.^2 - n.^4.*rho.^2 - 4.*n.^3.*rho.*(-(rho - 1).*(rho + 1)).^(1/2) + 1)./(n.^4.*rho.^2 + 2.*n.^4.*rho + n.^4 + 6.*n.^2.*rho.^2 + 4.*n.^2.*rho - 2.*n.^2 + rho.^2 + 2.*rho + 1)).^(1/2); 26 | temp = min(real(temp),1); 27 | theta = acos(temp); 28 | Iun((Iun./albedo)>1)=albedo; 29 | Iun = Iun./albedo; 30 | 31 | % NOTE: In the paper, they find the intersection of the Lambertian 32 | % constraint and the DOP constraint in terms of angles, I do it in terms of 33 | % unit vectors. Once the two unit vectors have been found, the one that is 34 | % closest to one of the ambiguous polarisation normals is chosen and the 35 | % final result is the average of the chosen one and the closest 36 | % polarisation normal 37 | 38 | % Two possible ambiguous polarisation normals: 39 | N1(:,:,1)=sin(phi).*sin(theta); 40 | N1(:,:,2)=cos(phi).*sin(theta); 41 | N1(:,:,3)=cos(theta); 42 | N2(:,:,1)=sin(phi+pi).*sin(theta); 43 | N2(:,:,2)=cos(phi+pi).*sin(theta); 44 | N2(:,:,3)=cos(theta); 45 | 46 | N = NaN(size(N1)); 47 | for row=1:size(mask,1) 48 | for col=1:size(mask,2) 49 | if mask(row,col) 50 | a = s(1); 51 | b = s(2); 52 | c = s(3)*cos(theta(row,col))-Iun(row,col); 53 | d = -sin(theta(row,col))^2; 54 | ny1 = -(b*c + a*(- d*a^2 - d*b^2 - c^2)^(1/2))/(a^2 + b^2); 55 | ny2 = -(b*c - a*(- d*a^2 - d*b^2 - c^2)^(1/2))/(a^2 + b^2); 56 | %if a~=0 57 | % nx1 = -(c - (b*(b*c + a*(- d*a^2 - d*b^2 - c^2)^(1/2)))/(a^2 + b^2))/a; 58 | % nx2 = -(c - (b*(b*c - a*(- d*a^2 - d*b^2 - c^2)^(1/2)))/(a^2 + b^2))/a; 59 | %else 60 | % Expression for nx1 and nx2 give Inf in this case so 61 | % compute from known norm of [nx ny] 62 | nx1 = sqrt(sin(theta(row,col))^2-ny1^2); 63 | nx2 = -sqrt(sin(theta(row,col))^2-ny1^2); 64 | %end 65 | % These are the two possible normals satisfying the Lambertian 66 | % + polarisation DOP constraint (intersection of two planes and 67 | % the unit norm constraint): 68 | n1 = [nx1 ny1 cos(theta(row,col))]'; 69 | n2 = [nx2 ny2 cos(theta(row,col))]'; 70 | n1best = max( dot(n1,squeeze(N1(row,col,:))), ... 71 | dot(n1,squeeze(N2(row,col,:))) ); 72 | n2best = max( dot(n2,squeeze(N1(row,col,:))), ... 73 | dot(n2,squeeze(N2(row,col,:))) ); 74 | if n1best>n2best 75 | na = n1; 76 | else 77 | na = n2; 78 | end 79 | if dot(na,squeeze(N1(row,col,:)))>dot(na,squeeze(N2(row,col,:))) 80 | nb = squeeze(N1(row,col,:)); 81 | else 82 | nb = squeeze(N2(row,col,:)); 83 | end 84 | n = na+nb; 85 | n(1:2) = n(1:2)./norm(n(1:2)); 86 | n(1:2) = n(1:2).*sin(theta(row,col)); 87 | n(3) = cos(theta(row,col)); 88 | N(row,col,:)=real(n); 89 | end 90 | end 91 | end 92 | 93 | % Integrate normals into height map 94 | height = lsqintegration( N,mask,false,[] ); 95 | 96 | end 97 | 98 | -------------------------------------------------------------------------------- /comparison/Propagation.m: -------------------------------------------------------------------------------- 1 | function [ N,height ] = Propagation( rho,phi,mask,n ) 2 | %PROPAGATION Shape-from-polarisation by boundary propagation 3 | % Inputs: 4 | % rho - rows by cols matrix of DOP values 5 | % phi - rows by cols matrix of phase angles 6 | % mask - rows by cols binary foreground mask 7 | % n - refractive index 8 | % 9 | % Outputs: 10 | % N - rows by cols by 3 matrix containing surface normals 11 | % height - height map obtained by integrating N using lsqintegration 12 | % 13 | % This is essentially a re-implementation of the technique used in: 14 | % 15 | % Miyazaki, D., Tan, R.T., Hara, K. and Ikeuchi, K. Polarization-based 16 | % inverse rendering from a single view. In Proc. ICCV, pp. 982-987, 2003. 17 | % 18 | % and 19 | % 20 | % G. A. Atkinson and E. R. Hancock. Recovery of Surface Orientation from 21 | % Diffuse Polarization. IEEE Transactions on Image Processing 15:1653-1664, 22 | % June 2006. 23 | % 24 | % It disambiguates polarisation normals beginning at the boundary. Boundary 25 | % normals are disambiguated to minimise angular error to the outward facing 26 | % boundary normal. Normals are then propagated into the interior in 27 | % descending order of zenith angle, with the disambiguation being chosen 28 | % that maximises smoothness with respect to already-disambiguated normals. 29 | % 30 | % William Smith 31 | % 2016 32 | 33 | % Invert degree of diffuse polarisation expression to compute zenith angle 34 | temp = ((2.*rho + 2.*n.^2.*rho - 2.*n.^2 + n.^4 + rho.^2 + 4.*n.^2*rho.^2 - n.^4.*rho.^2 - 4.*n.^3.*rho.*(-(rho - 1).*(rho + 1)).^(1/2) + 1)./(n.^4.*rho.^2 + 2.*n.^4.*rho + n.^4 + 6.*n.^2.*rho.^2 + 4.*n.^2.*rho - 2.*n.^2 + rho.^2 + 2.*rho + 1)).^(1/2); 35 | temp = min(real(temp),1); 36 | theta = acos(temp); 37 | 38 | % Pad twice to avoid boundary problems (a 7x7 neighbourhood around pixels 39 | % is considered for smoothness) 40 | mask = pad(pad(mask)); 41 | theta = pad(pad(theta)); 42 | phi = pad(pad(phi)); 43 | [rows,cols]=size(mask); 44 | 45 | N = zeros(rows,cols,3).*NaN; 46 | 47 | B = bwboundaries(mask,8,'noholes'); 48 | boundary=mask~=mask; 49 | % This keeps track of which pixels have been disambiguated: 50 | available_estimates=mask~=mask; 51 | 52 | % Fill in boundary normals with disambiguation that is closest to outward 53 | % facing normal 54 | B{1}(end+1,:)=B{1}(1,:); 55 | for i=1:size(B{1},1)-1 56 | r = B{1}(i,1); 57 | c = B{1}(i,2); 58 | available_estimates(r,c)=true; 59 | boundary(r,c)=true; 60 | azi = atan2(B{1}(i+1,2)-B{1}(i,2),B{1}(i+1,1)-B{1}(i,1)); 61 | n1 = [sin(phi(r,c))*sin(theta(r,c)); cos(phi(r,c))*sin(theta(r,c)); cos(theta(r,c))]; 62 | n2 = [sin(phi(r,c)+pi)*sin(theta(r,c)); cos(phi(r,c)+pi)*sin(theta(r,c)); cos(theta(r,c))]; 63 | nb = [cos(azi)*sin(theta(r,c)); sin(azi)*sin(theta(r,c)); cos(theta(r,c))]; 64 | if dot(n1,nb)>dot(n2,nb) 65 | N(r,c,:)=n1; 66 | else 67 | N(r,c,:)=n2; 68 | end 69 | end 70 | interior = mask & ~boundary; 71 | 72 | % Sort zenith angles of interior pixels into descending order 73 | [~,idx]=sort(theta(interior),1,'descend'); 74 | [col,row]=meshgrid(1:cols,1:rows); 75 | 76 | % At each iteration, we choose the unprocessed pixel with smallest zenith 77 | % angle that has at least one neighbour 78 | while ~isempty(idx) 79 | flag = false; 80 | selected = 1; 81 | r = row(interior); 82 | c = col(interior); 83 | % Consider pixels in ranked-theta order to find first one with at least 84 | % one neighbour (over 7x7 neighbourhood) 85 | while flag==false 86 | neighbourhood = []; 87 | for i=-3:3 88 | for j=-3:3 89 | if (i~=0) || (j~=0) 90 | if available_estimates(r(idx(selected))+i,c(idx(selected))+j) 91 | neighbourhood = [neighbourhood; r(idx(selected))+i c(idx(selected))+j]; 92 | end 93 | end 94 | end 95 | end 96 | if ~isempty(neighbourhood) % We need at least one neighbour to test smoothness 97 | flag=true; 98 | else 99 | selected=selected+1; 100 | end 101 | end 102 | % We now have a pixel with at least one neighbour 103 | r=r(idx(selected)); 104 | c=c(idx(selected)); 105 | % Select the normals from the neighbourhood 106 | Ns=[]; 107 | for i=1:size(neighbourhood,1) 108 | Ns=[Ns; N(neighbourhood(i,1),neighbourhood(i,2),1) N(neighbourhood(i,1),neighbourhood(i,2),2) N(neighbourhood(i,1),neighbourhood(i,2),3)]; 109 | end 110 | % Compute which local solution has smaller mean angular deviation from 111 | % its neighbours, our definition of smoothness 112 | n1 = [sin(phi(r,c))*sin(theta(r,c)); cos(phi(r,c))*sin(theta(r,c)); cos(theta(r,c))]; 113 | n2 = [sin(phi(r,c)+pi)*sin(theta(r,c)); cos(phi(r,c)+pi)*sin(theta(r,c)); cos(theta(r,c))]; 114 | if mean(acos(Ns*n1)) no smoothing. 68 | lambda=0.2; 69 | 70 | % The number of usable pixels 71 | npix = sum(mask(:)); 72 | 73 | if verbose 74 | disp(['Using ' num2str(npix) ' pixels']); 75 | end 76 | 77 | % Preallocate maximum required space 78 | % This would be if all valid pixels had equations for all 8 neighbours for 79 | % all possible pairs of images - it will be less in practice 80 | i = zeros(npix*20*2,1); 81 | j = zeros(npix*20*2,1); 82 | s = zeros(npix*20*2,1); 83 | 84 | % Right hand side of linear system: 85 | d = zeros(npix*2,1); 86 | 87 | NumEq = 0; % number of rows in matrix 88 | k=0; % total number of non-zero entries in matrix 89 | 90 | for row=1:rows 91 | for col=1:cols 92 | if mask(row,col) 93 | if mask4n(row,col) && lambda~=0 94 | % Add Laplacian smoothing term 95 | NumEq=NumEq+1; 96 | d(NumEq)=0; 97 | k=k+1; 98 | i(k)=NumEq; j(k)=indices(row,col); s(k)=-4*lambda; 99 | % Edge neighbours 100 | k=k+1; 101 | i(k)=NumEq; j(k)=indices(row,col-1); s(k)=1*lambda; 102 | k=k+1; 103 | i(k)=NumEq; j(k)=indices(row,col+1); s(k)=1*lambda; 104 | k=k+1; 105 | i(k)=NumEq; j(k)=indices(row-1,col); s(k)=1*lambda; 106 | k=k+1; 107 | i(k)=NumEq; j(k)=indices(row+1,col); s(k)=1*lambda; 108 | end 109 | % X equations 110 | % if maskSGx(row,col) && 1==0 111 | % NumEq=NumEq+1; 112 | % d(NumEq)=P(row,col); 113 | % for a=1:5 114 | % for b=1:5 115 | % if SGx(a,b)~=0 116 | % k=k+1; 117 | % i(k)=NumEq; j(k)=indices(row+a-3,col+b-3); s(k)=SGx(a,b); 118 | % end 119 | % end 120 | % end 121 | % elseif 122 | if maskSCDx(row,col) 123 | NumEq=NumEq+1; 124 | d(NumEq)=P(row,col); 125 | for a=1:3 126 | for b=1:3 127 | if SCDx(a,b)~=0 128 | k=k+1; 129 | i(k)=NumEq; j(k)=indices(row+a-2,col+b-2); s(k)=SCDx(a,b); 130 | end 131 | end 132 | end 133 | elseif mask(row,col-1) 134 | if mask(row,col+1) 135 | % Central difference 136 | NumEq=NumEq+1; 137 | d(NumEq)=P(row,col); 138 | k=k+1; 139 | i(k)=NumEq; j(k)=indices(row,col+1); s(k)=0.5; 140 | k=k+1; 141 | i(k)=NumEq; j(k)=indices(row,col-1); s(k)=-0.5; 142 | else 143 | % Backward difference 144 | NumEq=NumEq+1; 145 | d(NumEq)=P(row,col); 146 | k=k+1; 147 | i(k)=NumEq; j(k)=indices(row,col); s(k)=1; 148 | k=k+1; 149 | i(k)=NumEq; j(k)=indices(row,col-1); s(k)=-1; 150 | end 151 | elseif mask(row,col+1) 152 | % Forward difference 153 | NumEq=NumEq+1; 154 | d(NumEq)=P(row,col); 155 | k=k+1; 156 | i(k)=NumEq; j(k)=indices(row,col); s(k)=-1; 157 | k=k+1; 158 | i(k)=NumEq; j(k)=indices(row,col+1); s(k)=1; 159 | end 160 | 161 | % Y equations 162 | % if maskSGy(row,col) && 1==0 163 | % NumEq=NumEq+1; 164 | % d(NumEq)=Q(row,col); 165 | % for a=1:5 166 | % for b=1:5 167 | % if SGy(a,b)~=0 168 | % k=k+1; 169 | % i(k)=NumEq; j(k)=indices(row+a-3,col+b-3); s(k)=SGy(a,b); 170 | % end 171 | % end 172 | % end 173 | % elseif 174 | if maskSCDy(row,col) 175 | NumEq=NumEq+1; 176 | d(NumEq)=Q(row,col); 177 | for a=1:3 178 | for b=1:3 179 | if SCDy(a,b)~=0 180 | k=k+1; 181 | i(k)=NumEq; j(k)=indices(row+a-2,col+b-2); s(k)=SCDy(a,b); 182 | end 183 | end 184 | end 185 | elseif mask(row-1,col) 186 | if mask(row+1,col) 187 | % Central difference 188 | NumEq=NumEq+1; 189 | d(NumEq)=Q(row,col); 190 | k=k+1; 191 | i(k)=NumEq; j(k)=indices(row-1,col); s(k)=0.5; 192 | k=k+1; 193 | i(k)=NumEq; j(k)=indices(row+1,col); s(k)=-0.5; 194 | else 195 | % Backward difference 196 | NumEq=NumEq+1; 197 | d(NumEq)=Q(row,col); 198 | k=k+1; 199 | i(k)=NumEq; j(k)=indices(row,col); s(k)=-1; 200 | k=k+1; 201 | i(k)=NumEq; j(k)=indices(row-1,col); s(k)=1; 202 | end 203 | elseif mask(row+1,col) 204 | % Forward difference 205 | NumEq=NumEq+1; 206 | d(NumEq)=Q(row,col); 207 | k=k+1; 208 | i(k)=NumEq; j(k)=indices(row,col); s(k)=1; 209 | k=k+1; 210 | i(k)=NumEq; j(k)=indices(row+1,col); s(k)=-1; 211 | end 212 | end 213 | end 214 | end 215 | 216 | if verbose 217 | disp(['System contains ' num2str(NumEq) ' linear equations with ' num2str(k) ' non-zero entries in C']); 218 | end 219 | 220 | i=i(1:k,1); 221 | j=j(1:k,1); 222 | s=s(1:k,1); 223 | d=d(1:NumEq,1); 224 | 225 | i(k+1)=NumEq+1; 226 | j(k+1)=1; 227 | s(k+1)=1; 228 | d(NumEq+1)=0; 229 | 230 | % Build matrix +1 (for constraint on pixel 1) 231 | C = sparse(i,j,s,NumEq+1,npix); 232 | if verbose 233 | tic 234 | end 235 | %sum(isnan(C(:))) 236 | %sum(isnan(d(:))) 237 | %[c,A] = qr(C,d,0); 238 | %sum(isnan(A(:))) 239 | %sum(isnan(c(:))) 240 | 241 | %z = A\c; 242 | z = C\d; 243 | if verbose 244 | toc 245 | end 246 | 247 | 248 | % Copy vector of height values back into appropriate pixel positions 249 | height=zeros(size(mask)).*NaN; 250 | count=0; 251 | for row=1:rows 252 | for col=1:cols 253 | if mask(row,col) 254 | count=count+1; 255 | height(row,col)=z(count); 256 | end 257 | end 258 | end 259 | 260 | % Unpad 261 | height = height(2:rows-1,2:cols-1); 262 | 263 | end 264 | 265 | -------------------------------------------------------------------------------- /demo.m: -------------------------------------------------------------------------------- 1 | % Load raw images, mask and specular mask 2 | load sampleData.mat 3 | 4 | % Estimate polarisation image from captured images 5 | [ rho_est,phi_est,Iun_est ] = PolarisationImage( images,angles,mask,'linear' ); 6 | 7 | % Assume refractive index = 1.5 8 | n = 1.5; 9 | 10 | % Estimate light source direction from diffuse pixels (note that you might 11 | % get a convex/concave flip) 12 | %[ s,T,B ] = findLight( theta_est,phi_est,Iun_est,mask&~spec,3 ); 13 | % Or use known direction and estimate albedo 14 | s = [2 0 7]'; 15 | [ s,T,B ] = findLight( theta_est,phi_est,Iun_est,mask&~spec,3,s ); 16 | 17 | % Compute angles, taking into account different model for specular pixels 18 | theta_est_combined = rho_diffuse(rho_est,n); 19 | theta_s = rho_spec(rho_est(spec),n); 20 | theta_est_combined(spec)=theta_s; 21 | phi_est_combined = phi_est; 22 | phi_est_combined(spec)=mod(phi_est(spec)+pi/2,pi); 23 | 24 | % Compute boundary prior azimuth angles and weight 25 | [ azi,Bdist ] = boundaryPrior( mask ); 26 | 27 | % Run linear height from polarisation 28 | [ height ] = HfPol( theta_est_combined,min(1,Iun_est),phi_est_combined,s,mask,false,spec ); 29 | 30 | % Visualise 31 | figure; 32 | surf(height,'EdgeColor','none','FaceColor',[0 0 1],'FaceLighting','gouraud','AmbientStrength',0,'DiffuseStrength',1); axis equal; light -------------------------------------------------------------------------------- /sampleData.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waps101/depth-from-polarisation/9d0e32d631240df40c8aa9e6d8fbae769b071433/sampleData.mat -------------------------------------------------------------------------------- /utils/PolarisationImage.m: -------------------------------------------------------------------------------- 1 | function [ rho,phi,Iun ] = PolarisationImage( images,angles,mask,method ) 2 | %POLARISATIONIMAGE Decompose polarimetric images to polarisation image 3 | % Inputs: 4 | % images - rows by cols by nimages matrix of input images 5 | % angles - vector of polariser angles, length nimages 6 | % mask - binary foreground mask (true for foreground) 7 | % method - either 'linear' or 'nonlinear' 8 | % Outputs: 9 | % rho - rows by cols matrix containing degree of polarisation 10 | % phi - rows by cols matrix containing phase angle 11 | % Iun - rows by cols matrix containing unpolarised intensity 12 | % 13 | % William Smith 2016 14 | 15 | if nargin<4 16 | % Default to linear method 17 | method='linear' 18 | end 19 | 20 | [rows,cols,nimages] = size(images); 21 | 22 | if nargin>2 23 | % A mask has been provided so select only foreground pixels 24 | for i=1:nimages 25 | im = images(:,:,i); 26 | I(:,i)=im(mask); 27 | end 28 | else 29 | % No mask - use all pixels 30 | I = reshape(images,rows*cols,nimages); 31 | end 32 | 33 | if strcmp(method,'nonlinear') 34 | % For each pixel solve a nonlinear optimisation problem 35 | % Warning - slow (displays percentage progress) 36 | Iun = zeros(size(I,1),1); 37 | rho = zeros(size(I,1),1); 38 | phi = zeros(size(I,1),1); 39 | 40 | disp(' '); 41 | for i=1:size(I,1) 42 | [ Iun(i,1),rho(i,1),phi(i,1) ] = TRSfit( angles,I(i,:) ); 43 | fprintf('\b\b\b\b\b\b%05.2f%%',i/size(I,1)*100); 44 | end 45 | phi = mod(phi,pi); 46 | 47 | elseif strcmp(method,'linear') 48 | % Fast linear method - this seems to produce almost identical results 49 | % to the nonlinear method but is much faster 50 | A = [ones(nimages,1) cos(2.*angles') sin(2.*angles')]; 51 | x = A\(I'); 52 | x = x'; 53 | Imax = x(:,1)+sqrt(x(:,2).^2+x(:,3).^2); 54 | Imin = x(:,1)-sqrt(x(:,2).^2+x(:,3).^2); 55 | Iun = (Imin+Imax)./2; 56 | rho = (Imax-Imin)./(Imax+Imin); 57 | phi = 0.5*atan2(x(:,3),x(:,2)); 58 | phi = mod(phi,pi); 59 | end 60 | 61 | if nargin>2 62 | % We have a mask so need to reshape the estimated quantities to the 63 | % masked pixels only 64 | phi2 = zeros(rows,cols); 65 | phi2(mask) = phi; 66 | phi = phi2; 67 | rho2 = zeros(rows,cols); 68 | rho2(mask) = rho; 69 | rho = rho2; 70 | Iun2 = zeros(rows,cols); 71 | Iun2(mask) = Iun; 72 | Iun = Iun2; 73 | else 74 | phi = reshape(phi,rows,cols); 75 | rho = reshape(rho,rows,cols); 76 | Iun = reshape(Iun,rows,cols); 77 | end 78 | 79 | end 80 | 81 | -------------------------------------------------------------------------------- /utils/TRSfit.m: -------------------------------------------------------------------------------- 1 | function [ Iun,rho,phi ] = TRSfit( angles,I ) 2 | %TRSFIT Nonlinear least squares optimisation to fit sinusoid 3 | % Inputs: 4 | % angles - vector of polarising filter angles 5 | % I - vector of measured intensities 6 | % Outputs: 7 | % Iun, rho, phi - scalar values containing polarisation image params 8 | % 9 | % William Smith 2016 10 | 11 | % Initialise 12 | b0(1)=mean(I); 13 | b0(2)=sqrt(mean((I-b0(1)).^2)).*sqrt(2); 14 | b0(3)=0; 15 | 16 | options = optimoptions('lsqnonlin', 'Algorithm','trust-region-reflective','Display','off'); 17 | b=lsqnonlin(@(b) TRSfitobj(b,angles,I),b0,[0 0 -pi],[inf inf pi],options); 18 | 19 | if b(3)<0 20 | b(3)=b(3)+pi; 21 | end 22 | Iun = b(1); 23 | Imax = Iun+b(2); 24 | Imin = Iun-b(2); 25 | rho = (Imax-Imin)/(Imax+Imin); 26 | phi = b(3); 27 | 28 | end 29 | 30 | function errs = TRSfitobj(b,angles,I) 31 | 32 | I2 = b(1)+b(2).*cos(2.*angles-2*b(3)); 33 | errs = I-I2; 34 | 35 | end -------------------------------------------------------------------------------- /utils/boundaryPrior.m: -------------------------------------------------------------------------------- 1 | function [ azi,Bdist ] = boundaryPrior( mask,weight ) 2 | %BOUNDARYPRIOR Use foreground mask to compute convexity prior 3 | % Input: 4 | % mask - rows by cols binary foreground mask 5 | % weight - determines how fast the per-pixel weight falls off from the 6 | % boundary (default: 5) 7 | % Output: 8 | % azi - rows by cols azimuth angle prior 9 | % Bdist - weight associated with each azimuth estimate 10 | 11 | if nargin<2 12 | weight=5; 13 | end 14 | 15 | % Find boundary of mask 16 | B = bwperim(mask); 17 | [bcol,brow]=find(B); 18 | [row,col]=meshgrid(1:size(mask,2),1:size(mask,1)); 19 | % Compute distance to closest point on boundary 20 | [~,D]=knnsearch([brow bcol],[row(mask) col(mask)]); 21 | Bdist = zeros(size(mask)).*NaN; 22 | % Transform to range 0..1 23 | Bdist(mask)=-D; 24 | Bdist(mask) = Bdist(mask)-min(Bdist(mask)); 25 | Bdist = Bdist./max(Bdist(mask)); 26 | Bdist = Bdist.^weight; 27 | mask2 = mask; 28 | azi = zeros(size(mask)); 29 | % Repeatedly erode boundary to propagate into interior 30 | while sum(mask2(:))>0 31 | % Find boundary of current mask 32 | B = bwboundaries(mask2,8); 33 | B{1}(end+1,:)=B{1}(1,:); 34 | for i=1:size(B{1},1)-1 35 | azi(B{1}(i,1),B{1}(i,2))=atan2(B{1}(i+1,2)-B{1}(i,2),B{1}(i+1,1)-B{1}(i,1)); 36 | end 37 | % Remove boundary pixels from mask, i.e. erode mask 38 | mask2(bwperim(mask2,8))=0; 39 | end 40 | % Transform azimuth angles to vectors 41 | dx = cos(azi); 42 | dy = sin(azi); 43 | dx(~mask)=0; 44 | dy(~mask)=0; 45 | % Smooth vector field 46 | g=fspecial('Gaussian',[9 9],2); 47 | dx = imfilter(dx,g); 48 | dy = imfilter(dy,g); 49 | % Transform back to azimuth angle 50 | azi = atan2(dy,dx); 51 | azi = mod(-azi+pi-pi/2,2*pi); 52 | 53 | end 54 | 55 | -------------------------------------------------------------------------------- /utils/findLight.m: -------------------------------------------------------------------------------- 1 | function [ l,T,B ] = findLight( theta,phi,diffuse,mask,ldim,l ) 2 | %FINDLIGHT Estimate illumination from polarisation data 3 | % Inputs: 4 | % theta - zenith angle estimates from degree of polarisation 5 | % phi - phase angle from polarisation image 6 | % diffuse - unpolarised intensity 7 | % mask - binary foreground mask 8 | % ldim - Set to 3, 4 or 9 depending on whether solving for point 9 | % source, order 1 or order 2 spherical harmonic illumination 10 | % l - if ldim=3 and the direction of l is known (but not the 11 | % intensity) then pass in the known direction and only the 12 | % intensity will be optimised 13 | % Outputs: 14 | % l - 3D, 4D or 9D lighting coefficient vector 15 | % T - Ambiguous transformation, T*l is also a solution 16 | % B - The basis that is consistent with l, B*T is also a possible 17 | % basis 18 | % 19 | % William Smith 2016 20 | 21 | if nargin<5 22 | ldim = 3; 23 | end 24 | 25 | %% Set up some required values 26 | 27 | i = diffuse(mask); 28 | theta = theta(mask); 29 | phi = phi(mask); 30 | N(:,1)=sin(phi).*sin(theta); 31 | N(:,2)=cos(phi).*sin(theta); 32 | N(:,3)=cos(theta); 33 | 34 | %% Set up appropriate (ambiguous) basis vectors and transformation matrices depending on dimensionality of lighting 35 | 36 | if ldim==3 37 | B = N; 38 | T = [-1 0 0; 0 -1 0; 0 0 1]; 39 | elseif ldim==4 40 | B = N; 41 | B(:,4) = 1; 42 | T = diag([-1 -1 1 1]); 43 | elseif ldim==9 44 | B = [ones(size(N,1),1) N 3.*N(:,3).^2-1 N(:,1).*N(:,2) N(:,1).*N(:,3) N(:,2).*N(:,3) N(:,1).^2-N(:,2).^2]; 45 | T = diag([1 -1 -1 1 1 1 -1 -1 1]); 46 | end 47 | 48 | %% Initialise optimisation 49 | 50 | converged = false; 51 | tau = 1e-9; 52 | niter = 0; 53 | maxiter = 100; 54 | 55 | if nargin==6 56 | % In this case, we already know the light source direction and just 57 | % want to optimise for the intensity 58 | while ~converged 59 | idx = (B*l-i).^2>(B*T*l-i).^2; 60 | P = B; 61 | P(idx,:)=P(idx,:)*T; 62 | lnew=l.*((P*l)\i); 63 | if norm(l-lnew)maxiter 69 | break 70 | end 71 | end 72 | else 73 | % Random initialisation 74 | l = randn(ldim,1); 75 | if ldim==4 76 | % This is treating the order 1 model as ambient plus point source 77 | % and therefore assuming ambient should be positive 78 | l(4)=abs(l(4)); 79 | end 80 | 81 | %% Perform alternating assignment/optimisation 82 | 83 | while ~converged 84 | % indicator function 85 | idx = (B*l-i).^2>(B*T*l-i).^2; 86 | P = B; 87 | P(idx,:)=P(idx,:)*T; 88 | if ldim==3 89 | % Unconstrained linear least squares 90 | lnew = P\i; 91 | elseif ldim==4 92 | % Order 0 term is constrained to be positive 93 | lnew = lsqlin(P,i,[],[],[],[],[-inf -inf -inf 0],[inf inf inf inf]); 94 | elseif ldim==9 95 | lnew = P\i; 96 | end 97 | if norm(l-lnew)maxiter 103 | break 104 | end 105 | end 106 | end 107 | B = P; 108 | 109 | disp(['Number of iterations = ' num2str(niter)]); 110 | disp(['Residual = ' num2str(norm(P*l-i).^2)]); 111 | 112 | end 113 | 114 | -------------------------------------------------------------------------------- /utils/pad.m: -------------------------------------------------------------------------------- 1 | function [ X2 ] = pad( X ) 2 | %PAD Pad a matrix with an additional row/column at each edge 3 | 4 | [rows,cols]=size(X); 5 | X2 = zeros(rows+2,cols+2); 6 | X2(2:rows+1,2:cols+1)=X(:,:); 7 | if islogical(X) 8 | X2 = logical(X2); 9 | end 10 | 11 | end 12 | 13 | -------------------------------------------------------------------------------- /utils/rho_diffuse.m: -------------------------------------------------------------------------------- 1 | function [ theta ] = rho_diffuse( rho,n ) 2 | %RHO_DIFFUSE Compute zenith angle from DOP for diffuse pixels 3 | % Uses closed form inversion of DOP expression 4 | % Inputs: 5 | % rho - matrix or vector containing degree of polarisation values 6 | % n - scalar containing index of refraction 7 | % Output: 8 | % theta - zenith angle estimates (same size as rho) 9 | % 10 | % William Smith 2016 11 | 12 | temp = ((2.*rho + 2.*n.^2.*rho - 2.*n.^2 + n.^4 + rho.^2 + 4.*n.^2*rho.^2 - n.^4.*rho.^2 - 4.*n.^3.*rho.*(-(rho - 1).*(rho + 1)).^(1/2) + 1)./(n.^4.*rho.^2 + 2.*n.^4.*rho + n.^4 + 6.*n.^2.*rho.^2 + 4.*n.^2.*rho - 2.*n.^2 + rho.^2 + 2.*rho + 1)).^(1/2); 13 | % To avoid complex result in case of numerical issues: 14 | temp = min(real(temp),1); 15 | theta = acos(temp); 16 | 17 | end 18 | 19 | -------------------------------------------------------------------------------- /utils/rho_spec.m: -------------------------------------------------------------------------------- 1 | function [ theta_s ] = rho_spec( rho,n ) 2 | %RHO_S Compute zenith angle from degree of polarisation for specular pix 3 | % There is no closed form inversion for rho_s so this functions uses a 4 | % look up table and interpolates 5 | 6 | theta = 0:0.01:pi/2; 7 | 8 | rho_s = (2.*sin(theta).^2.*cos(theta).*sqrt(n.^2-sin(theta).^2))./(n.^2-sin(theta).^2-n.^2.*sin(theta).^2+2.*sin(theta).^4); 9 | %figure; plot(theta,rho_s) 10 | maxpos = find(rho_s==max(rho_s)); 11 | 12 | theta = theta(1:maxpos); 13 | rho_s = rho_s(1:maxpos); 14 | 15 | theta_s = interp1(rho_s,theta,rho); 16 | 17 | end 18 | 19 | --------------------------------------------------------------------------------