% LFHistEqualize - histogram-based contrast adjustment
% LF = LFHistEqualize( LF )
% [LF, LowerBound, UpperBound] = LFHistEqualize( LF, Cutoff_percent )
% LF = LFHistEqualize( LF, , LowerBound, UpperBound )
% LF = LFHistEqualize(LF, Cutoff_percent, , , Precision)
% This function performs contrast adjustment on images based on a histogram of intensity. Colour
% images are handled by building a histogram of the `value' channel in the HSV colour space.
% Saturation points are computed based on a desired percentage of saturated white / black pixels.
% All parameters except LF are optional. Pass an empty array "" to omit a parameter.
% LF : a light field or image to adjust. Integer formats are automatically converted to floating
% point. The default precision is 'single', though this is controllable via the optional
% Precision parameter. The function can handle most input dimensionalities as well as
% colour and monochrome inputs. For example, a colour 2D image of size [Nl,Nk,3], a mono 2D
% image of size [Nl,Nk], a colour 4D image of size [Nj,Ni,Nl,Nk,3], and a mono 4D image of
% size [Nj,Ni,Nl,Nk] are all valid.
% Including a weight channel will result in zero-weight pixels being ignored. Weight should
% be included as a fourth colour channel -- e.g. an image of size [Nl,Nk,4].
% Optional inputs:
% Cutoff_percent : controls how much of the histogram gets saturated on both the high
% and lower ends. Higher values result in more saturated black and
% white pixels. The default value is 0.1%.
% LowerBound, UpperBound : bypass the histogram computation and instead directly saturate the
% input image at the prescribed limits. If no bounds are provided, they
% are computed from the histogram.
% Precision : 'single' or 'double'
% LF : the contrast-adjusted image, in floating point values scaled between
% 0 and 1.
% LowerBound, UpperBound : the saturation points used, on the intensity scale of the input after
% converting to the requested floating point precision. These are
% useful in applying the same contrast adjustment to a number of light
% fields, by saving and passing these bounds on subsequent function
% LF = LFDecodeLytroImage('Images/IMG_0002');
% LF = LFHistEqualize(LF);
% LFDispMousePan(LF, 2)
% Run from the top level of the light field samples will decode IMG_0002, apply histogram
% equalization, then display the result in a shifting-perspective display with x2 magnification.
% LFUtilProcessWhiteImages must be run before decoding will work.
% See also: LFUtilDecodeLytroFolder, LFColourCorrect
% Part of LF Toolbox v0.2 released 27-May-2013
% Copyright (c) 2013, Donald G. Dansereau
function [LF, LowerBound, UpperBound] = ...
LFHistEqualize(LF, Cutoff_percent, LowerBound, UpperBound, Precision)
Cutoff_percent = LFDefaultVal( 'Cutoff_percent', 0.1 );
Precision = LFDefaultVal( 'Precision', 'single' );
%---Make sure LF is floating point, as required by hist function---
if( ~strcmp(class(LF), Precision) )
LF = cast(mat2gray(LF), Precision);
%---Flatten to a single list of N x NChans entries---
LFSize = size(LF);
NDims = numel(LFSize);
NChans = LFSize(end);
LF = reshape(LF, [prod(LFSize(1:NDims-1)), NChans]);
%---Strip out and use the weight channel if it's present---
if( NChans == 4 )
% Weight channel found, strip it out and use it
LFW = LF(:,4);
LF = LF(:,1:3);
ValidIdx = find(LFW ~= 0);
LFW = ;
ValidIdx = ':';
%---Convert colour images to HSV, and operate on value only---
if( size(LF,2) == 3 )
LF_hsv = rgb2hsv( LF );
LF = LF_hsv(:,3); % operate on value channel only
LF_hsv = ;
%---Compute bounds from histogram---
if( ~exist('LowerBound','var') || ~exist('UpperBound','var') || ...
isempty(LowerBound) || isempty(UpperBound))
[ha,xa] = hist(LF(ValidIdx), 2^16);
ha = cumsum(ha);
ha = ha ./ max(ha(:));
Cutoff = Cutoff_percent / 100;
LowerBound = xa(find(ha >= Cutoff, 1, 'first'));
UpperBound = xa(find(ha <= 1-Cutoff, 1, 'last'));
%---Apply bounds and rescale to 0-1 range---
LF = max(LowerBound, min(UpperBound, LF));
LF = (LF - LowerBound) ./ (UpperBound-LowerBound);
%---Rebuild hsv, then rgb colour light field---
if( ~isempty(LF_hsv) )
LF_hsv(:,3) = LF;
LF = hsv2rgb(LF_hsv);
%---Return the weight channel---
if( ~isempty(LFW) )
LF(:,4) = LFW;
LF = reshape(LF, [LFSize(1:NDims-1),NChans]);