From 2527630e50d566ebbe20851aac17397a43303b68 Mon Sep 17 00:00:00 2001
From: Brian Wandell <wandell@stanford.edu>
Date: Sat, 12 Aug 2017 17:11:24 -0700
Subject: [PATCH] Quality of life routines related to cloud computing, and more
 generally.

---
 ...rMultipleObj.m => remodelerCloudExample.m} |   2 +-
 ...modeller.m => remodelerPBRTCloudExample.m} |   2 +-
 ExampleScenes/GcloudRenderer/s_cloudExample.m |  39 ++++---
 Utilities/rtbCamerasInit.m                    | 106 ++++++++++++++++++
 Utilities/rtbCamerasPlace.m                   |  43 +++++++
 Utilities/rtbHintsInit.m                      |  74 ++++++++++++
 6 files changed, 247 insertions(+), 19 deletions(-)
 rename ExampleScenes/GcloudRenderer/{MexximpRemodellerMultipleObj.m => remodelerCloudExample.m} (95%)
 rename ExampleScenes/GcloudRenderer/{PBRTRemodeller.m => remodelerPBRTCloudExample.m} (98%)
 create mode 100644 Utilities/rtbCamerasInit.m
 create mode 100644 Utilities/rtbCamerasPlace.m
 create mode 100644 Utilities/rtbHintsInit.m

diff --git a/ExampleScenes/GcloudRenderer/MexximpRemodellerMultipleObj.m b/ExampleScenes/GcloudRenderer/remodelerCloudExample.m
similarity index 95%
rename from ExampleScenes/GcloudRenderer/MexximpRemodellerMultipleObj.m
rename to ExampleScenes/GcloudRenderer/remodelerCloudExample.m
index c30ec5c..6ac4457 100644
--- a/ExampleScenes/GcloudRenderer/MexximpRemodellerMultipleObj.m
+++ b/ExampleScenes/GcloudRenderer/remodelerCloudExample.m
@@ -1,4 +1,4 @@
-function [ scene, mappings ] = MexximpRemodellerMultipleObj( scene, mappings, names, conditionValues, conditionNumber )
+function [ scene, mappings ] = remodelerCloudExample( scene, mappings, names, conditionValues, conditionNumber )
 % Remodeler used usually in remodelPerConditionAfterFunction
 %
 %
diff --git a/ExampleScenes/GcloudRenderer/PBRTRemodeller.m b/ExampleScenes/GcloudRenderer/remodelerPBRTCloudExample.m
similarity index 98%
rename from ExampleScenes/GcloudRenderer/PBRTRemodeller.m
rename to ExampleScenes/GcloudRenderer/remodelerPBRTCloudExample.m
index 349317d..cd9b0ea 100644
--- a/ExampleScenes/GcloudRenderer/PBRTRemodeller.m
+++ b/ExampleScenes/GcloudRenderer/remodelerPBRTCloudExample.m
@@ -1,4 +1,4 @@
-function [ nativeScene ] = PBRTRemodeller( parentScene, nativeScene, mappings, names, conditionValues, conditionNumbers )
+function [ nativeScene ] = remodelerPBRTCloudExample( parentScene, nativeScene, mappings, names, conditionValues, conditionNumbers )
 % Attaches PBRT-specific constructs to the PBRT scene
 %
 %  
diff --git a/ExampleScenes/GcloudRenderer/s_cloudExample.m b/ExampleScenes/GcloudRenderer/s_cloudExample.m
index 4ce6205..77ca474 100644
--- a/ExampleScenes/GcloudRenderer/s_cloudExample.m
+++ b/ExampleScenes/GcloudRenderer/s_cloudExample.m
@@ -1,4 +1,18 @@
-%% Compare lens renderings of different cars
+%% Illustrates google cloud platform usage
+%
+%  Reads in a small object (millenial falcon).
+%  Sets up a camera array
+%  Places the object with respect to the cameras
+%  Calls batch render on the cloud
+%
+% Uses - rtbCloudInit, rtbCloudUpload, rtbCloudDownload
+% Uses - rtbHintsInit, rtbCamerasInit, rtbCamerasPlace
+% Uses - local remodelers.  More discussion here.
+%
+% For this to run, you must have set up a google cloud account, have appropriate
+% permissions, and have kubectl installed.  Instructions for this should be on
+% the RenderToolbox4 web-site.  Soon, we hope.  Now a bunch of it is on the
+% NN_Camera_Generalization web site.
 %
 % BW, Henryk Blasinski, SCIEN Stanford, 2017
 
@@ -19,15 +33,18 @@ gcloud = true;
 % you could use this:
 %   zone   = 'us-west1-b';
 % and then set a 'zone' parameter below.
+p.addParameter('remodelerAfter', @MexximpRemodellerMultipleObj,@(x)(isequal(class(x),'function_handle')));
+p.addParameter('remodelerConvertAfter', @PBRTRemodeller,@(x)(isequal(class(x),'function_handle')));
 
 % Should have a validity check.  Surprising that we have the tokenPath early in
 % the ordering within this nnHintsInit routine
 % Small image size for debugging.
-hints = nnHintsInit('imageWidth',160,'imageHeight',120,...
+hints = rtbHintsInit('imageWidth',160,'imageHeight',120,...
     'recipeName','cloud-example',...
     'tokenPath',tokenPath,...
     'gcloud',gcloud,...
-    'mexximpRemodeler', @MexximpRemodellerMultipleObj);
+    'remodelerConvertAfter',@remodelerPBRTCloudExample,...
+    'remodelerAfter', @remodelerCloudExample);
 
 %% Open the GCP
 rtbCloudInit(hints);
@@ -37,7 +54,7 @@ sceneFile = which('millenium-falcon.obj');
 
 % Camera set to be 50 meters from an object distance
 % This could be an array of cameras.
-cameras = nnGenCameras('type',{'pinhole'},...
+cameras = rtbCamerasInit('type',{'pinhole'},...
     'mode',{'radiance'},...
     'distance',50);
 
@@ -60,18 +77,6 @@ rtbWriteSpectrumFile(wave,d65,fullfile(resourceFolder,'D65.spd'));
 
 %% Build the scene
 
-% dragonFile = which('Dragon.blend');
-% dragonScene = mexximpCleanImport(dragonFile,...
-%     'ignoreRootTransform',true,...
-%     'flipUVs',true,...
-%     'imagemagicImage','hblasins/imagemagic-docker',...
-%     'toReplace',{'jpg','png','tga'},...
-%     'options','-gamma 0.45',...
-%     'targetFormat','exr',...
-%     'makeLeftHanded',true,...
-%     'flipWindingOrder',true,...
-%     'workingFolder',resourceFolder);
-
 % Import the millenial faclon, which is small
 mfScene = mexximpCleanImport(sceneFile,...
     'ignoreRootTransform',true,...
@@ -100,7 +105,7 @@ objectArrangements = {objects(1), objects(2)};
 % lookAt and film distance variables.  Other slots are copied from the camera
 % object itself.  The placedCameras combine the different object arrangements
 % and cameras. The output is placedCameras{nCameras}(nArrangements).
-placedCameras = nnPlaceCameras(cameras,objectArrangements);
+placedCameras = rtbCamerasPlace(cameras,objectArrangements);
 
 %% Make values used for the Conditions file.
 %
diff --git a/Utilities/rtbCamerasInit.m b/Utilities/rtbCamerasInit.m
new file mode 100644
index 0000000..76101d8
--- /dev/null
+++ b/Utilities/rtbCamerasInit.m
@@ -0,0 +1,106 @@
+function [ camera ] = rtbCamerasInit( varargin )
+% Generate an array of cameras with various parameters for rendering
+%
+% Used in conjunction with object placement for multiple scenes.  
+%
+% See also:  s_cloudExample.m
+%
+% HB SCIEN STANFORD< 2017
+
+%%
+p = inputParser;
+p.addOptional('type',{'pinhole'});
+p.addOptional('lens',{'dgauss.22deg.6.0mm'});
+p.addOptional('mode',{'radiance'});
+p.addOptional('pixelSamples',128);
+p.addOptional('distance',10);
+p.addOptional('orientation',0);
+p.addOptional('height',-1.5);
+p.addOptional('PTR',{[0,0,0]});
+p.addOptional('defocus',0);
+p.addOptional('diffraction',{'false'});
+p.addOptional('chromaticAberration',{'false'});
+p.addOptional('fNumber',2.8);
+p.addOptional('filmDiagonal',1/6.4*25.4);
+p.addOptional('microlens',{[0,0]});
+p.addOptional('lookAtObject',1);
+
+p.parse(varargin{:});
+inputs = p.Results;
+
+%% Checks
+assert(length(inputs.type)==length(inputs.lens) || length(inputs.type)==1 || length(inputs.lens)==1);
+assert(length(inputs.diffraction)==length(inputs.chromaticAberration) || length(inputs.diffraction)==1 || length(inputs.chromaticAberration)==1);
+assert(length(inputs.microlens)==length(inputs.lens) || length(inputs.microlens) == 1 || length(inputs.lens)==1);
+
+%% Loop
+
+cntr = 1;
+for a=1:max([length(inputs.type), length(inputs.lens), length(inputs.microlens)])
+for b=1:length(inputs.pixelSamples)
+for c=1:length(inputs.distance)
+for d=1:length(inputs.orientation)
+for e=1:length(inputs.height)
+for f=1:length(inputs.PTR)
+for g=1:length(inputs.defocus)
+for h=1:max([length(inputs.diffraction), length(inputs.chromaticAberration)])
+for i=1:length(inputs.fNumber)
+for j=1:length(inputs.filmDiagonal)
+for k=1:length(inputs.mode)
+    for l=1:length(inputs.lookAtObject)
+    
+    if length(inputs.type) == 1
+        camera(cntr).type = inputs.type{1};
+    else
+        camera(cntr).type = inputs.type{a};
+    end
+    if length(inputs.lens) == 1
+        camera(cntr).lens = inputs.lens{1};
+    else
+        camera(cntr).lens = inputs.lens{a};
+    end
+    if length(inputs.microlens) == 1
+        camera(cntr).microlens = inputs.microlens{1};
+    else
+        camera(cntr).microlens = inputs.microlens{a};
+    end
+    camera(cntr).mode = inputs.mode{k};
+    camera(cntr).pixelSamples = inputs.pixelSamples(b);
+    camera(cntr).fNumber = inputs.fNumber(i);
+    camera(cntr).filmDiagonal = inputs.filmDiagonal(j);
+    camera(cntr).distance = inputs.distance(c);
+    camera(cntr).orientation = inputs.orientation(d);
+    camera(cntr).height = inputs.height(e);
+    camera(cntr).PTR = inputs.PTR{f};
+    camera(cntr).defocus = inputs.defocus(g);
+    
+    if length(inputs.diffraction) == 1
+        camera(cntr).diffraction = inputs.diffraction{1};
+    else
+        camera(cntr).diffraction = inputs.diffraction{h};
+    end
+    if length(inputs.chromaticAberration) == 1
+        camera(cntr).chromaticAberration = inputs.chromaticAberration{1};
+    else
+        camera(cntr).chromaticAberration = inputs.chromaticAberration{h};
+    end
+    camera(cntr).lookAtObject = inputs.lookAtObject(l);
+    
+    cntr = cntr+1;
+    end
+end
+end
+end
+end
+end
+end
+end
+end
+end
+end
+
+
+
+
+end
+
diff --git a/Utilities/rtbCamerasPlace.m b/Utilities/rtbCamerasPlace.m
new file mode 100644
index 0000000..90d6012
--- /dev/null
+++ b/Utilities/rtbCamerasPlace.m
@@ -0,0 +1,43 @@
+function [ cameras ] = nnPlaceCameras( cameras, objects )
+
+global lensDir
+
+nArrangements = length(objects);
+cameras = repmat({cameras},[1, nArrangements]);
+
+for a=1:nArrangements
+    for i=1:length(cameras{a})
+        lookAtObject = cameras{a}(i).lookAtObject;
+        objPosition = objects{a}(lookAtObject).position;
+        
+        cx = cameras{a}(i).distance*sind(cameras{a}(i).orientation) + objPosition(1);
+        cy = cameras{a}(i).distance*cosd(cameras{a}(i).orientation) + objPosition(2);
+        
+        cameras{a}(i).position = [cx, cy, cameras{a}(i).height];
+        cameras{a}(i).lookAt = objPosition;
+        cameras{a}(i).lookAt(3) = cameras{a}(i).height;
+        
+        lensFile = fullfile(lensDir,sprintf('%s.dat',cameras{a}(i).lens));
+        if strcmp(cameras{a}(i).type,'pinhole')
+            cameras{a}(i).filmDistance = effectiveFocalLength(lensFile);
+        else
+            cameras{a}(i).filmDistance = focusLens(lensFile,cameras{a}(i).distance);
+            if cameras{a}(i).defocus ~= 0
+                lens = lensC('fileName',lensFile);
+                focalLength = lens.focalLength;
+                
+                sensorInFocus = 1/(1/(focalLength/1000) - 1/cameras{a}(i).distance);
+                sensorOutOfFocus = 1/(1/(focalLength/1000) + cameras{a}(i).defocus - 1/cameras{a}(i).distance);
+                
+                delta = (sensorOutOfFocus - sensorInFocus)*1000;
+                
+                cameras{a}(i).filmDistance = cameras{a}(i).filmDistance + delta;
+            end
+        end
+        
+    end
+end
+
+
+end
+
diff --git a/Utilities/rtbHintsInit.m b/Utilities/rtbHintsInit.m
new file mode 100644
index 0000000..e855803
--- /dev/null
+++ b/Utilities/rtbHintsInit.m
@@ -0,0 +1,74 @@
+function hints = rtbHintsInit(varargin)
+% Initialize the rendering hints with the defaults
+%
+% 
+% We should set up input parameters for this
+%
+% BW/HB SCIEN Stanford, 2017
+
+%%
+p = inputParser;
+
+% Some can be reset.
+p.addParameter('imageWidth',640,@isnumeric);
+p.addParameter('imageHeight',480,@isnumeric);
+p.addParameter('recipeName',tempname,@ischar);
+p.addParameter('tokenPath','',@(x)(exist(x,'file')));
+p.addParameter('remodelerAfter', @MexximpRemodellerMultipleObj,@(x)(isequal(class(x),'function_handle')));
+p.addParameter('remodelerConvertAfter', @PBRTRemodeller,@(x)(isequal(class(x),'function_handle')));
+
+% The Wandell lab's resources are set up to scale reasonably on this zone, but
+% not on others.  You can check this from the gcloud web site that shows we are
+% good to scale to 800 cores there.  https://console.cloud.google.com/ 
+%    IAM | Quotas
+p.addParameter('zone','us-central1-a',@ischar);
+
+p.addParameter('gcloud',false,@islogical);
+
+% Local docker image
+p.addParameter('dockerImage','',@ischar);
+
+p.parse(varargin{:});
+
+%%
+hints.imageWidth  =  p.Results.imageWidth;
+hints.imageHeight = p.Results.imageHeight;
+hints.recipeName  = p.Results.recipeName;  % Name of the directory output
+if p.Results.gcloud
+    hints.renderer = 'PBRTCloud';          % We're only using PBRT right now
+else
+    hints.renderer = 'PBRT';               % We're only using PBRT right now
+end
+hints.copyResources = 1;                   % Is this a logical?? (BW)
+hints.isParallel = false;
+
+% Change the docker container
+hints.tokenPath = p.Results.tokenPath; 
+hints.batchRenderStrategy = RtbAssimpStrategy(hints);
+
+hints.batchRenderStrategy.remodelPerConditionAfterFunction = p.Results.remodelerAfter;
+hints.batchRenderStrategy.converter = RtbAssimpPBRTConverter(hints);
+hints.batchRenderStrategy.converter.remodelAfterMappingsFunction = p.Results.remodelerConvertAfter;
+hints.batchRenderStrategy.converter.rewriteMeshData = false;
+
+if p.Results.gcloud
+    % Google cloud run
+    % Odd that is has to be earlier
+    %  hints.tokenPath = p.Results.tokenPath;
+    hints.batchRenderStrategy.renderer = RtbPBRTCloudRenderer(hints);
+    if isempty(p.Results.dockerImage)
+        dockerImage = 'gcr.io/primal-surfer-140120/pbrt-v2-spectral-gcloud';
+    end
+    hints.batchRenderStrategy.renderer.pbrt.dockerImage = dockerImage;
+    hints.batchRenderStrategy.renderer.cloudFolder = fullfile('gs://primal-surfer-140120.appspot.com',hints.recipeName);
+    hints.batchRenderStrategy.renderer.zone = p.Results.zone;
+else
+    % Local run
+    hints.batchRenderStrategy.renderer = RtbPBRTRenderer(hints);
+    if isempty(p.Results.dockerImage)
+        dockerImage = 'vistalab/pbrt-v2-spectral';
+    end
+    hints.batchRenderStrategy.renderer.pbrt.dockerImage = dockerImage;
+end
+
+end
\ No newline at end of file
-- 
GitLab