Contents

%function gammaValue = calibDisplay(mode, gammaIn)
%       function gammaValue = calibDisplay(mode, gammaIn)
% Display luminances, collect photometer readings, and derive gamma.
%
% In mode 1 (default), user is prompted to enter photometer readings for
% displays of increasing luminance. Data must then be saved.
% Luminances are displayed in a large figure window, which should leave
% room at the bottom of the screen for the last lines of the command window.
%
% In mode 2 (following 1 automatically), user is prompted for an existing
% calibration file if mode 1 was skipped. Values are then plotted, and fit
% with a gamma function. An inverse gamma function and the resulting
% linearized output are also graphed. Corrected and uncorrected demo
% graylevel ramps are also displayed.
%
% mode 1 can be run a second time with gammaIn (default ==1) set to the
% value computed in the first instance, just to test whether this gamma does
% indeed produce a linear output. Be careful not to overwrite the existing
% data though!
%
% Elliot Freeman 25.10.05
%--------------------------------------------------------------------------

default arguments

if ~exist('mode'), mode = 1; end            % 1: calibration sequence; 2: plot and fit data (load calibration file first!)
if ~exist('gammaIn'), gammaIn = 1; end      % set this to 1 for calibration, or use it to test the linearity of a given gamma correction

cIn = [0:16:240 255]'; 	                    % range of CLUT entry values
cIn = (( cIn/255 ) .^ ( 1/gammaIn )) *255;	% CLUT output (with gamma correction for testing whether this gives a linear output)
nlevels = length(cIn);
cmapLIN = repmat(linspace(0,1,256),3,1)';   % linear RGB look-up-table

----------------------------------- MODE 1 ------------------------------

calibration sequence

Type in photometer reading and hit return. Enter negative to quit.

if mode==1
    figure(1); set(1, 'menubar', 'none');
    scrsz = get(0,'screensize');
    set(1, 'position', scrsz + [0 0 0 150]);    % show figure window for calibration leaving bottom lines of command window visible
    imC = ones(scrsz(4),scrsz(3));
    fprintf('For each gray level, enter the photometer reading at the prompt. \nEnter negative value to quit.\n');

    for i=1:nlevels
        lum=[];
        while isempty(lum)
            c=cIn(i);
            figure(1);
            image(imC .* cIn(i)); colormap(cmapLIN); axis off;
            set(gca, 'position', [0 0 1 1]);
            inputStr = sprintf('Greylevel = %d\t Luminance = ', c);
            lum = input(inputStr);
            if ~isnumeric(lum), lum = []; end
            if lum <0, close all; error('Aborted'); end;
        end
        lOut(i) = lum;         % record photometer reading of display output
    end
    close(1);
end

load calibration data if not already in memory

if ~exist('lOut'), uiopen('*.mat'); end

process display output subtracting black-level offset

lumout = lOut' - lOut(1);                       % display output in cdm-2
lummax = max(lumout);
lumpc = lumout./repmat(lummax,nlevels,1);   % display output as percentage of maximum luminance

Fit the gamma function

define inline function to calculate sum of square residuals of gamma-corrected data relative to desired linear output; then use leastsquares to find gamma value that minimises this residual.

in = inline('sum( ((lum .^ (1/g)) - lin ) .^2 )', 'g', 'lum', 'lin');
gammaValue = fminbnd(in,1,10,[],lumpc,cIn/255);

save results or hit cancel to continue without saving

if mode == 1
    [FILENAME, PATHNAME, FILTERINDEX] = uiputfile('*.mat', 'Save calibration file as...');
    calName = fullfile(PATHNAME,FILENAME);
    if ~isequal(FILENAME, 0)
        dateStr = date;
        note = input('Please describe your display\nE.g. \hitachi mc7515; contrast 100% brightness 50%\n', 's');
        save(calName, 'lOut', 'note', 'dateStr', 'cIn', 'gammaValue');
        fprintf('\nSaved as %s', calName);
    end
end

----------------------------------- MODE 2 ------------------------------

Plot Results

Display Characteristic: Photometer reading as function of grey-level colour value (blue) Also shows goodness of fit of gamma function (red)

figure(2);
p(1) = subplot(1,3,1);
hold on;
title('Display characteristic');
plot(cIn, lOut);                                                        % plot photometer readings
plot(cIn, lOut(1) + ((cIn/255).^gammaValue) * lOut(end), 'r--');        % plot gamma fit
xlabel('uncorrected input value');
ylabel('output luminance');
legend('display output', ['fit with gamma= ' num2str(gammaValue)]);
set(gca,'ylim', [0 lummax]);
hold off;

Show how the inverse gamma function transforms linear grey-level values to

new non-linear values. New = Old ^ (1/gammaValue) <= this equation performs the transformation

p(2) = subplot(1,3,2);
plot(cIn, lOut(1) + (cIn/255) .^ (1/gammaValue) * 255); % plot corrected against uncorrected gray-level values
title('Inverse gamma');
xlabel('uncorrected input value');
ylabel('corrected input value');
set(gca,'ytick', cIn(1:4:end), 'xlim', [0 255], 'ylim', [0 255]);
legend('y = x\^(1/gamma)')

Show how these new non-linear input values compensate for the non-linear display characteristics,

resulting in a linear range of output luminances.

p(3) = subplot(1,3,3);
cOut =  lumpc.^(1/gammaValue) * lummax;
plot(cIn, cOut + lOut(1));      % plot luminance as a function of corrected values
disp([cIn cOut])                % output old and new values to command line
title('Linearized');
xlabel('corrected input value');
ylabel('output luminance');
set(gca,'ylim', [0 lummax]);
set(p,'xtick', cIn(1:4:end), 'xlim', [0 255]);

Demonstrate the difference between uncorrected and corrected images

Uncorrected

figure(3)
subplot(2,1,1);
imUncorr = repmat((0:255),256,1);   % input consists of 256 values from 0 to 1 (black to white)
image(imUncorr); axis off;           % Notice that this image is dominated by blacks and dark grays
colormap(cmapLIN)
title('Uncorrected');

% Corrected
subplot(2,1,2);
imCorr = ((imUncorr/255) .^ (1/gammaValue)) * 255;    % non-linear transformation of input values to produce linear output luminance
image(imCorr); axis off;
colormap(cmapLIN)
image(imCorr); axis off;                        % Notice that all gray levels are now more fairly represented
title('Corrected');
set([2 3], 'menubar', 'none');