.
This website contains supplementary code and data for the paper On Optimal, Minimal BRDF Sampling for Reflectance Acquisition to appear in Siggraph Asia 2015 Technical Papers.
Code covers python scripts for reconstructing BRDFs and determining optimum sampling directions, and the data includes our results for optimum sampling directions, precomputed principal components for your own reconstructions, and a small collection of reconstructed BRDFs in the MERL format.You are welcome to freely use any of the code and data provided on this website, but we kindly ask that you cite our paper. Please see citation.
Download supplementary material (pdf)
Python code has been made available for others to run their own optimizations and reconstructions. The code is arranged in a set of files:
In addition, example code is available for learning data statistics, optimizing sampling directions, and reconstructing data. The contents of these examples are shown below.
Download source files (Python)
In this example we simply load the MERL database, map it using our proposed "Log-Relative" mapping, and perform PCA. For performance, we precompute a mask map and a cosine map, used in other examples.
The MERL BRDF database can be downloaded here.
import numpy as np import os.path as path from os import listdir from trainingFunctions import * from merlFunctions import * from coordinateFunctions import * MERLDir = "MERLDir/" OutputDir = "data/" #Parse filenames materials = [brdfFile for i,brdfFile in enumerate(listdir(MERLDir)) \ if (path.isfile(MERLDir+brdfFile) and path.splitext(brdfFile)[1] == ".binary")] #Fill observation array obs = np.zeros((90*90*180, 3*len(materials)),'float32') #Add each color channel as a single observation for i in range(0,len(materials)): mat = readMERLBRDF("%s/%s"%(MERLDir,materials[i])) obs[:,3*i] = np.reshape(mat[:,:,:,0],(-1)) obs[:,3*i+1] = np.reshape(mat[:,:,:,1],(-1)) obs[:,3*i+2] = np.reshape(mat[:,:,:,2],(-1)) #Pre-compute maskMap (VERY SLOW if horizonCheck=True) maskMap = ComputeMaskMap(obs, horizonCheck=False) #Pre-compute cosine-map (VERY SLOW! - do once and store, or download!) cosMap = ComputeCosMap(maskMap) #Perform PCA on data (scaledPCs,relativeOffset,median) = LearnMapping(obs,maskMap,cosMap) #Save data np.save('%s/MaskMap'%OutputDir,maskMap) np.save('%s/CosineMap'%OutputDir,cosMap) np.save('%s/ScaledEigenvectors'%OutputDir,scaledPCs) np.save('%s/Median'%OutputDir,median) np.save('%s/RelativeOffset'%OutputDir, relativeOffset)
In this example we load the learned principal components etc. from training, and find the optimal up to 3 sampling directions
import numpy as np from optimizationFunctions import * #Load precomputed data dataDir = "data/" maskMap = np.load('%s/MaskMap.npy'%dataDir) #Indicating valid regions in MERL BRDFs Q = np.load('%s/ScaledEigenvectors.npy'%dataDir) #Scaled eigenvectors, learned from trainingdata #Find up to 3 optimal sampling directions of Vs: (pointHierachi, C) = FewToMany(Q, maskMap, 3) #Print points: (in MERL coordinates) for (i,pointSet) in enumerate(pointHierachi): print "#Optimum directions, n=%d:\n%s\n"%(i+1,pointSet)
Output from example:
#Optimum directions, n=1: [[ 6 0 66]] #Optimum directions, n=2: [[ 2 12 47] [158 37 4]] #Optimum directions, n=3: [[ 11 12 3] [154 69 15] [143 28 78]]
Here we have 5 RGB BRDF observations captured from 5 unique directions. Using the training data we reconstruct the full BRDF and store it in the MERL format
import numpy as np from merlFunctions import * from coordinateFunctions import * from reconstructionFunctions import * #BRDF observations (5 RGB values) obs = np.array([[0.09394814, 0.01236500, 0.00221087], [0.09005638, 0.00315711, 0.00270478], [1.38033974, 1.21132099, 1.19253075], [0.97795460, 0.85147798, 0.84648135], [0.10845871, 0.05911538, 0.05381590]]) #Coordinates for observations (phi_d, theta_h, theta_d) coords = np.array([[36.2, 1.4, 4.0 ], [86.5, 76.7, 13.1], [85.5, 7.6, 78.9], [144.8, 2.5, 73.8], [80.4, 12.9, 51.6]]) #Convert to BRDF coordinates MERLCoords = RusinkToMERL(np.deg2rad(coords)) #Convert to IDs (i.e. rows-ids in the PC matrix) MERLIds = MERLToID(MERLCoords) #Load precomputed data dataDir = "data/" maskMap = np.load('%s/MaskMap.npy'%dataDir) #Indicating valid regions in MERL BRDFs median = np.load('%s/Median.npy'%dataDir) #Median, learned from trainingdata cosMap = np.load('%s/CosineMap.npy'%dataDir) #Precomputed cosine-term for all BRDF locations (ids) relativeOffset = np.load('%s/RelativeOffset.npy'%dataDir) #Offset, learned from trainingdata Q = np.load('%s/ScaledEigenvectors.npy'%dataDir) #Scaled eigenvectors, learned from trainingdata #Reconstruct BRDF recon = ReconstructBRDF(obs, MERLIds, maskMap, Q, median, relativeOffset, cosMap, eta=40) #Save reconstruction as MERL .binary saveMERLBRDF("reconstruction.binary",recon)
Rendering of reconstructed BRDF. (Note that code for rendering is not available here. See PBRT.)
Direction-lists are stored in the Numpy text format (.nptxt). To load the lists into python, simply do:
import numpy as np directions = np.loadtxt("path/to/file.nptxt")Directions are stored in n x 3 arrays, with n being the number of directions and the columns representing the 3 Rusinkiewicz coordinates in degrees (PhiD, ThetaH, ThetaD):
# phiD thetaH thetaD [deg] 24.13 01.63 40.45 52.29 29.02 21.24Below direction lists can be downloaded for two different BRDF sampling approaches:
We here supply precomputed data for users who are not interrested in acquiring the MERL database and performning principal component analysis etc. themselves.
The data is packed in a rar-container and includes:
Download precomputed data (Python)
The following files are reconstructed BRDFs. The reconstructions are stored in the MERL BRDF format (.binary, see MERL Database).
If you use the code or measurements, please, reference the following paper:
BibTeX: @article {Nielsen2015, author = {Jannik Boll Nielsen and Henrik Wann Jensen and Ravi Ramamoorthi}, title = {On Optimal, Minimal BRDF Sampling for Reflectance Acquisition}, journal = {ACM Transactions on Graphics (TOG)}, year = {2015}, month = {November}, volume = {34}, number = {6}, pages = {186:1-186:11}, doi = {10.1145/2816795.2818085} }