function [fit_out,x,fitComment] = ... FitGamma(values_in,measurements,values_out,fitType) % [fit_out,x,fitComment] = ... % FitGamma(values_in,measurements,values_out,[fitType]) % % Fit a gamma function. This essentially a driver function. % It has two main purposes. % % First it tries several different % underlying parametric forms for the fit and chooses the best % for a particular data set. % % Second, it does the bookkeeping for fitting each column of % input measurements. (Each of the underlying fit functions expects % only vector input.) % % To a large extent, the interface to the underlying fit functions % (e.g. FitGammaPow, FitGammaSig, ...) is uniform. However, this routine % does have to know a little bit about initial value dimension and choice. % We have tried to localize this information in the initialization routines % (e.g. InitialXPow, InitialXSig, ...) as much as possible, but some % caution is advised. % % Optional argument fitType allows you to force the return of a particular % parametric form. Currently: % fitType == 1: Power function % fitType == 2: Extended power function % fitType == 3: Sigmoid % fitType == 4: Weibull % fitType == 5: Modified polynomial % fitType == 6: Linear interpolation % fitType == 7: Cubic spline % % All fit types are in a form such that the fit is forced through the % origin for 0 input. This is because our convention is that gamma % correction happens after subtraction of the ambient light. % % NOTE: FitGammaPow (and perhaps other subroutines) uses CONSTR, which is part of the % Mathworks Optimization Toolbox. % % Also see FitGammaDemo. % 10/3/93 dhb Removed polynomial fit from list tried with fitType == 0. % Added Weibull function fit % 3/15/94 dhb, jms Added linear interpolation. % 7/18/94 dhb Added cubic spline interpolation. % 8/7/00 dhb Fix bug. Spline was calling linear interpolation. Thanks to % Chien-Chung Chen for notifying us of this bug. % 11/14/06 dhb Modify how default type is set. Handle passed empty matrix. % 3/07/10 dhb Cosmetic to make m-lint happier, including some "|" -> "||" % 5/26/10 dhb Allow values_in to be either a single column or a matrix with same number of columns as values_out. % 6/5/10 dhb Fix a lot of MATLAB lint warnings. % dhb Fix error reporting to actually take mean across devices. % dhb Rewrite how mean is taken for evaluation of best fit. I think this was done right. % 11/07/10 dhb Print out fit exponents when gamma fit by a simple power function. % Get sizes [nDevices] = size(measurements,2); [nOut] = size(values_out,1); % If input comes as a single column, then replicate it to % match number of devices if (size(values_in,2) == 1) values_in = repmat(values_in,1,nDevices); end % Set up number of fit types nFitTypes = 5; errv = zeros(nFitTypes,nDevices); % Handle force fittting if (nargin < 4 || isempty(fitType)) fitType = 0; end if ~ismember(fitType, 0:7) error('Unsupported fitType requested. Only 0 to 7 are supported.'); end if ismember(fitType, [0, 1, 2, 3, 4]) && IsOctave v = version; if str2num(v(1)) < 6 error('For use with Octave, you need at least Octave version 6.'); end try % Try loading the optim package with the optimization functions: pkg load optim; catch error('For use with Octave, you must install the ''optim'' package from Octave Forge. See ''help pkg''.'); end % Got optim package loaded. Does it support fmincon()? if ~exist('fmincon') error('For use with Octave, you need at least version 1.6.0 of the ''optim'' package from Octave Forge.'); end end % Fit with simple power function through origin if (fitType == 0 || fitType == 1 || fitType == 2) disp('Fitting with simple power function'); fit_out1 = zeros(nOut,nDevices); [nParams] = size(InitialXPow,1); x1 = zeros(nParams,nDevices); for i = 1:nDevices x0 = InitialXPow; [fit_out1(:,i),x1(:,i),errv(1,i)] = ... FitGammaPow(values_in(:,i),measurements(:,i),values_out,x0); fprintf('Exponent for device %d is %g\n',i,x1(:,i)); end fprintf('Simple power function fit, RMSE: %g\n',mean(errv(1,i))); end % Fit with extended power function. Use power function % fit to drive parameters. InitialXExtP can take a two % vector as input. This defines the parameters of a good fitting % simple power function. if (fitType == 0 || fitType == 2) disp('Fitting with extended power function'); fit_out2 = zeros(nOut,nDevices); [nParams] = size(InitialXExtP,1); x2 = zeros(nParams,nDevices); for i = 1:nDevices x0 = InitialXExtP(x1(:,i)); [fit_out2(:,i),x2(:,i),errv(2,i)] = ... FitGammaExtP(values_in(:,i),measurements(:,i),values_out,x0); end fprintf('Extended power function fit, RMSE: %g\n',mean(errv(2,i))); end % Fit with a sigmoidal shape. This works well for % the dimmer packs controlling lights. InitialXSig can take % a two vector as input. This defines roughly the input for % half maximum and the maximum output value. if (fitType == 0 || fitType == 3) disp('Fitting with sigmoidal function'); fit_out3 = zeros(nOut,nDevices); [nParams] = size(InitialXSig,1); x3 = zeros(nParams,nDevices); for i = 1:nDevices maxVals = max(values_in(:,i)); x0 = InitialXSig(maxVals'/2); [fit_out3(:,i),x3(:,i),errv(3,i)] = ... FitGammaSig(values_in(:,i),measurements(:,i),values_out,x0); end fprintf('Sigmoidal fit, RMSE: %g\n',mean(errv(3,i))); end % Fit with Weibull if (fitType == 0 || fitType == 4) disp('Fitting with Weibull function'); fit_out4 = zeros(nOut,nDevices); [nParams] = size(InitialXWeib(values_in(:,1),measurements(:,1)),1); x4 = zeros(nParams,nDevices); for i = 1:nDevices x0 = InitialXWeib(values_in(:,i),measurements(:,i)); [fit_out4(:,i),x4(:,i),errv(4,i)] = ... FitGammaWeib(values_in(:,i),measurements(:,i),values_out,x0); end fprintf('Weibull function fit, RMSE: %g\n',mean(errv(4,i))); end % Fit with polynomial. InitalXPoly is used mostly for consistency % with other calling forms, since FitGammaPoly computes an analytic % fit to start the search. But it serves to implicitly defines the % order of the polynomial. if (fitType == 0 || fitType == 5) disp('Fitting with polynomial'); fit_out5 = zeros(nOut,nDevices); [order5] = size(InitialXPoly,1); x5 = zeros(order5,nDevices); for i = 1:nDevices [fit_out5(:,i),x5(:,i),errv(5,i)] = ... FitGammaPoly(values_in(:,i),measurements(:,i),values_out); end fprintf('Polynomial fit, order %g, RMSE: %g\n',order5,mean(errv(5,i))); end % Linear interpolation. Variable x is bogus here, but % we fill it in to keep the accountants upstream happy. if (fitType == 6) disp('Fitting with linear interpolation'); fit_out6 = zeros(nOut,nDevices); for i = 1:nDevices [fit_out6(:,i)] = ... FitGammaLin(values_in(:,i),measurements(:,i),values_out); end x6 = []; end % Cubic spline. Variable x is bogus here, but % we fill it in to keep the accountants upstream happy. if (fitType == 7) disp('Fitting with cubic spline'); fit_out7 = zeros(nOut,nDevices); for i = 1:nDevices [fit_out7(:,i)] = ... FitGammaSpline(values_in(:,i),measurements(:,i),values_out); end x7 = []; end % If we are not forcing a fit type, find best fit. % Don't check linear interpolation, as it has zero error always. % Currently we take the minimum mean error over all devices. % In principle, could use best fit type for each device. But % that would make the interface tricky. if (fitType == 0) meanErr = mean(errv,2); [minErr,bestFit] = min(meanErr); fitType = bestFit; end if (fitType == 1) fit_out = fit_out1; x = x1; fitComment = (sprintf('Simple power function fit, RMSE: %g',... mean(errv(1,:)))); elseif (fitType == 2) fit_out = fit_out2; x = x2; fitComment = (sprintf('Extended power function fit, RMSE: %g',... mean(errv(2,:)))); elseif (fitType == 3) fit_out = fit_out3; x = x3; fitComment = (sprintf('Sigmoidal fit, RMSE: %g',... mean(errv(3,:)))); elseif (fitType == 4) fit_out = fit_out4; x = x4; fitComment = (sprintf('Weibull fit, RMSE: %g',... mean(errv(4,:)))); elseif (fitType == 5) fit_out = fit_out5; x = x5; fitComment = (sprintf('Polynomial fit, RMSE: %g',... mean(errv(5,:)))); elseif (fitType == 6) fit_out = fit_out6; x = x6; fitComment = (sprintf('Linear interpolation fit')); elseif (fitType == 7) fit_out = fit_out7; x = x7; fitComment = (sprintf('Cubic spline fit')); end % Check that fit is non-decreasing if (CheckMonotonic(fit_out) == 0) disp('Warning, fit is not non-decreasing'); end