diff --git a/BatchRenderer/AssimpStrategy/BasicMappings/rtbLoadJsonMappings.m b/BatchRenderer/AssimpStrategy/BasicMappings/rtbLoadJsonMappings.m index f6160b3b57d0ae8c9d141629789fe03e3f9e0e3c..a651b9037ee16f359eaf2b74f028144769ab3455 100644 --- a/BatchRenderer/AssimpStrategy/BasicMappings/rtbLoadJsonMappings.m +++ b/BatchRenderer/AssimpStrategy/BasicMappings/rtbLoadJsonMappings.m @@ -29,9 +29,4 @@ else originalMappings = {}; end -if ~iscell(originalMappings) - error('parseJsonMappings:invalidJson', ... - 'Could not load mappings cell from JSON <%s>\n', fileName); -end - mappings = rtbValidateMappings(originalMappings); diff --git a/ExampleScenes/LuminanceScaling/LuminanceScaling.blend b/ExampleScenes/LuminanceScaling/LuminanceScaling.blend new file mode 100644 index 0000000000000000000000000000000000000000..81a9271046846b5198ba68ee5212ebcf81ca7667 Binary files /dev/null and b/ExampleScenes/LuminanceScaling/LuminanceScaling.blend differ diff --git a/ExampleScenes/LuminanceScaling/LuminanceScaling.blend1 b/ExampleScenes/LuminanceScaling/LuminanceScaling.blend1 new file mode 100644 index 0000000000000000000000000000000000000000..e7801a33ecbda96daf15a1bbe22ed75246b88656 Binary files /dev/null and b/ExampleScenes/LuminanceScaling/LuminanceScaling.blend1 differ diff --git a/ExampleScenes/LuminanceScaling/rtbMakeLuminanceScaling.m b/ExampleScenes/LuminanceScaling/rtbMakeLuminanceScaling.m new file mode 100644 index 0000000000000000000000000000000000000000..88b7f22afa6cd73b72dd7784faa6025b065bbc92 --- /dev/null +++ b/ExampleScenes/LuminanceScaling/rtbMakeLuminanceScaling.m @@ -0,0 +1,97 @@ +%%% RenderToolbox4 Copyright (c) 2012-2016 The RenderToolbox Team{1}. +%%% About Us://github.com/RenderToolbox/RenderToolbox4/wiki/About-Us +%%% RenderToolbox4 is released under the MIT License. See LICENSE file. +% +%% Render the LuminanceScaling scene. +clear; + +%% Choose example file. +parentSceneFile = 'LuminanceScaling.blend'; + +%% Choose batch renderer options. +hints.imageWidth = 320; +hints.imageHeight = 240; +hints.fov = deg2rad(36); +hints.recipeName = 'rtbMakeLuminanceScaling'; +hints.renderer = 'Mitsuba'; + +recipeFolder = rtbWorkingFolder('hints', hints); + + +%% Mappings to turn spheres into area lights. +m{1}.broadType = 'meshes'; +m{1}.name = 'LeftSphere'; +m{1}.operation = 'blessAsAreaLight'; +m{1}.properties = rtbMappingProperty( ... + 'name', 'intensity', ... + 'valueType', 'spectrum', ... + 'value', '300:0 800:(intensity)'); + +m{2}.broadType = 'meshes'; +m{2}.name = 'RightSphere'; +m{2}.operation = 'blessAsAreaLight'; +m{2}.properties = rtbMappingProperty( ... + 'name', 'intensity', ... + 'valueType', 'spectrum', ... + 'value', '300:100 800:0'); + +m{3}.broadType = 'materials'; +m{3}.specificType = 'matte'; +m{3}.name = 'Backdrop'; +m{3}.operation = 'update'; +m{3}.properties = rtbMappingProperty( ... + 'name', 'diffuseReflectance', ... + 'valueType', 'spectrum', ... + 'value', 'mccBabel-1.spd'); + +mappingsFile = fullfile(recipeFolder, 'LuminanceScalingMappings.json'); +rtbWriteJson(m, 'fileName', mappingsFile); + + +%% Conditions to vary the intensity of one light. +names = {'intensity'}; +intensities = {1, 10, 100, 1000}'; +conditionsFile = fullfile(recipeFolder, 'LuminanceScalingConditions.txt'); +rtbWriteConditionsFile(conditionsFile, names, intensities); + + +%% Render. +nativeSceneFiles = rtbMakeSceneFiles(parentSceneFile, ... + 'mappingsFile', mappingsFile, ... + 'conditionsFile', conditionsFile, ... + 'hints', hints); +radianceDataFiles = rtbBatchRender(nativeSceneFiles, 'hints', hints); + + +%% Choose scaling and tone mapping based on the last rendering. +calibrationRendering = load(radianceDataFiles{end}); + +% convert to sRGB with scaling by max luminance +% obtain the XYZ image and scaling facotr used internally +[~, XYZImage, ~, scaleFactor] = rtbMultispectralToSRGB( ... + calibrationRendering.multispectralImage, ... + calibrationRendering.S, ... + 'isScale', true); + +% choose a tone mapping threshold from the returned XYZ image +luminance = XYZImage(:,:,2); +toneMapThreshold = 100 * mean(luminance(:)); + + +%% Convert each rendering to sRGB with constant scaling and tone mapping. +nRenderings = numel(radianceDataFiles); +nRows = 2; +nColumns = ceil(nRenderings / nRows); +for rr = 1:nRenderings + rendering = load(radianceDataFiles{rr}); + + sRGBImage = rtbMultispectralToSRGB( ... + rendering.multispectralImage, ... + rendering.S, ... + 'scaleFactor', scaleFactor, ... + 'toneMapThreshold', toneMapThreshold); + + subplot(nRows, nColumns, rr); + imshow(uint8(sRGBImage)); + title(sprintf('left %d, right 100', intensities{rr})) +end diff --git a/Utilities/rtbMultispectralToSRGB.m b/Utilities/rtbMultispectralToSRGB.m index ba367e678e3ac6a2637e5cc0711487174ea8a5ea..b4e52a9e1eed06b737b93ba19723860a6ddd9c1d 100644 --- a/Utilities/rtbMultispectralToSRGB.m +++ b/Utilities/rtbMultispectralToSRGB.m @@ -1,4 +1,4 @@ -function [sRGBImage, XYZImage, rawRGBImage] = rtbMultispectralToSRGB(multispectralImage, S, varargin) +function [sRGBImage, XYZImage, rawRGBImage, scaleFactor] = rtbMultispectralToSRGB(multispectralImage, S, varargin) % Convert multi-spectral image data to XYZ and sRGB. % % sRGBImage = rtbMultispectralToSRGB(multispectralImage, S) @@ -14,14 +14,29 @@ function [sRGBImage, XYZImage, rawRGBImage] = rtbMultispectralToSRGB(multispectr % this factor times the mean luminance. The default is 0, don't truncate % luminances. % +% sRGBImage = rtbMultispectralToSRGB( ... 'toneMapThreshold', toneMapThreshold) +% specifies a simple tone mapping threshold. Truncates lumininces above +% the given toneMapThreshold. The default is 0, don't truncate luminances. +% +% If toneMapFactor and toneMapThreshold are both supplied, toneMapThreshold +% is used and toneMapFactor is ignored. +% % sRGBImage = rtbMultispectralToSRGB( ... 'isScale', isScale) % specifies whether to scale the gamma-corrected image to the display % maximum (true) or not (false). The default is false, don't scale the % image. % +% sRGBImage = rtbMultispectralToSRGB( ... 'scaleFactor', scaleFactor) +% specifies a constant to scale the sRGB image. The default is 0, don't +% scale the image. +% +% If isScale and scaleFactor are both supplied, scaleFactor +% is used and toneMapFactor is isScale. +% % Returns a gamma-corrected sRGB image of size [height width 3]. Also % returns the intermediate XYZ image and the uncorrected RGB image, which -% have the same size. +% have the same size. Also returns the scale factor that was used to +% scale the sRGB image, if any. % %%% RenderToolbox4 Copyright (c) 2012-2016 The RenderToolbox Team. %%% About Us://github.com/RenderToolbox/RenderToolbox4/wiki/About-Us @@ -31,12 +46,16 @@ parser = inputParser(); parser.addRequired('multispectralImage', @isnumeric); parser.addRequired('S', @isnumeric); parser.addParameter('toneMapFactor', 0, @isnumeric); +parser.addParameter('toneMapThreshold', 0, @isnumeric); parser.addParameter('isScale', false, @islogical); +parser.addParameter('scaleFactor', 0, @isnumeric); parser.parse(multispectralImage, S, varargin{:}); multispectralImage = parser.Results.multispectralImage; S = parser.Results.S; toneMapFactor = parser.Results.toneMapFactor; +toneMapThreshold = parser.Results.toneMapThreshold; isScale = parser.Results.isScale; +scaleFactor = parser.Results.scaleFactor; % convert to CIE XYZ image using CIE 1931 standard weighting functions % 683 converts watt-valued spectra to lumen-valued luminances (Y-values) @@ -49,6 +68,8 @@ XYZImage = rtbMultispectralToSensorImage(multispectralImage, S, ... % convert to sRGB with a very simple tone mapping algorithm that truncates % luminance above a factor times the mean luminance -[sRGBImage, rawRGBImage] = rtbXYZToSRGB(XYZImage, ... +[sRGBImage, rawRGBImage, scaleFactor] = rtbXYZToSRGB(XYZImage, ... 'toneMapFactor', toneMapFactor, ... - 'isScale', isScale); + 'toneMapThreshold', toneMapThreshold, ... + 'isScale', isScale, ... + 'scaleFactor', scaleFactor); diff --git a/Utilities/rtbXYZToSRGB.m b/Utilities/rtbXYZToSRGB.m index 445ac77076b8f4ac1103770d6edd5adbeefa53a1..fb01b640817292a2403af5cfd56f5f206d59f905 100644 --- a/Utilities/rtbXYZToSRGB.m +++ b/Utilities/rtbXYZToSRGB.m @@ -10,14 +10,23 @@ function [gammaImage, rawImage, scaleFactor] = rtbXYZToSRGB(image, varargin) % for simple tone mapping -- luminance will be trncated above this factor % times the mean luminance. The default is 0, don't do this tone mapping. % -% rtbXYZToSRGB( ... 'toneMapMax', toneMapMax) specifies a threshold for an -% even simpler tone mapping -- truncate luminance above this value. The -% default is 0, don't do this tone mapping. +% sRGBImage = rtbXYZToSRGB( ... 'toneMapThreshold', toneMapThreshold) +% specifies a simple tone mapping threshold. Truncates lumininces above +% the given toneMapThreshold. The default is 0, don't truncate luminances. +% +% If toneMapFactor and toneMapThreshold are both supplied, toneMapThreshold +% is used and toneMapFactor is ignored. % % rtbXYZToSRGB( ... 'isScale', isScale) specifies whether to scale the % gamma-corrected image. If isScale is logical and true, the image will be -% scaled by its maximum. If isScale is a number, the image will be scaled -% by this number. The default is false, don't do any scaling. +% scaled by its maximum. The default is false, don't do any scaling. +% +% sRGBImage = rtbXYZToSRGB( ... 'scaleFactor', scaleFactor) +% specifies a constant to scale the sRGB image. The default is 0, don't +% scale the image. +% +% If isScale and scaleFactor are both supplied, scaleFactor +% is used and toneMapFactor is isScale. % % Returns a matrix of size [height width n] with gamma corrected sRGB color % data. Also returns a matrix of the same size with uncorrected sRGB color @@ -32,13 +41,15 @@ function [gammaImage, rawImage, scaleFactor] = rtbXYZToSRGB(image, varargin) parser = inputParser(); parser.addRequired('image', @isnumeric); parser.addParameter('toneMapFactor', 0, @isnumeric); -parser.addParameter('toneMapMax', 0, @isnumeric); -parser.addParameter('isScale', false); +parser.addParameter('toneMapThreshold', 0, @isnumeric); +parser.addParameter('isScale', false, @islogical); +parser.addParameter('scaleFactor', 0, @isnumeric); parser.parse(image, varargin{:}); image = parser.Results.image; toneMapFactor = parser.Results.toneMapFactor; -toneMapMax = parser.Results.toneMapMax; +toneMapThreshold = parser.Results.toneMapThreshold; isScale = parser.Results.isScale; +scaleFactor = parser.Results.scaleFactor; %% Convert XYZ to sRGB % @@ -53,30 +64,25 @@ isScale = parser.Results.isScale; [XYZCalFormat,m,n] = ImageToCalFormat(image); % Tone map. This is a very simple algorithm that truncates -% luminance above a factor times the mean luminance. -if (toneMapFactor > 0) +% luminance above threshold. +if (toneMapThreshold > 0) + XYZCalFormat = BasicToneMapCalFormat(XYZCalFormat, toneMapThreshold); +elseif (toneMapFactor > 0) meanLuminance = mean(XYZCalFormat(2,:)); maxLum = toneMapFactor * meanLuminance; XYZCalFormat = BasicToneMapCalFormat(XYZCalFormat, maxLum); end -% Tone map again. This is an even simpler algorithm -% that truncates luminance above a fixed value. -if (toneMapMax > 0) - XYZCalFormat = BasicToneMapCalFormat(XYZCalFormat, toneMapMax); -end - % Convert to sRGB % may allow code to scale input max to output max. SRGBPrimaryCalFormat = XYZToSRGBPrimary(XYZCalFormat); -if islogical(isScale) - scaleFactor = 1/max(SRGBPrimaryCalFormat(:)); - SRGBCalFormat = SRGBGammaCorrect(SRGBPrimaryCalFormat, isScale); -else - scaleFactor = isScale; +if scaleFactor > 0 SRGBPrimaryCalFormat = SRGBPrimaryCalFormat .* scaleFactor; SRGBCalFormat = SRGBGammaCorrect(SRGBPrimaryCalFormat, false); +elseif islogical(isScale) + scaleFactor = 1/max(SRGBPrimaryCalFormat(:)); + SRGBCalFormat = SRGBGammaCorrect(SRGBPrimaryCalFormat, isScale); end % Back to image plane format