-- M module extracted from ITU-T J.249 (01/2010)
function [data] = st_collapse(request, raw_data, varargin)
% ST_COLLAPSE
% Compute the requested spatial or temporal collapsing function.
% SYNTAX
% [data] = st_collapse(request, raw_data)
% [data] = st_collapse(...,'PropertyName',...);
% DESCRIPTION
% Compute the requested spatial or temporal collapsing function on the
% FIRST dimension of the array or matrix, 'raw_data', and return the
% results in 'data'. The available precentile functions are:
% 'mean', 'std', 'rms', 'min', 'max', '10%', '25%', '50%', '90%',
% 'above90%', 'above95%', 'above99%', 'above90%tail', 'above95%tail', 'above98%tail', 'above99%tail',
% 'below5%', 'below10%', 'below1%', 'below1%tail, 'below2%tail', 'below5%tail', 'below10%tail'
% 'below25%', 'above75%', 'below2%', 'above98%', 'below50%tail'
% 'between25%50%'
% [ The meanings of the above are as defined in "Video Quality
% Measurement Techniques" NTIA Technical Report 02-392. ]
% and 'minkowski(P,R)'.
% [ minkowski = mean(abs(raw_data).^P).^(1/R) ]
% Where 'P' and 'R' are replaced with the actual values to be
% used. For example, 'minkowski(1.8,2.8)' or 'minkowski(6,7)'
%
% The following optional parameters are also available. All of these
% options are mutually exclusive.
%
% 'MacroBlock', row, col, time,
% Apply the requested function to macro blocks, of size (row,col,time).
% If the region does not evenly divide, center spatially, and
% abut with the end of the raw data temporally.
% 'OverlapMacroBlock', row, col, time,
% Apply the requested function to macro blocks, of size (row,col,time).
% Unlike option 'MacroBlock', blocks overlap rather than
% abutting. Thus, returned data will be smaller only by
% (row-1) rows,(col-1) columns and (time-1) in time.
% 'SlideMacroBlock', row, col, time,
% Apply the requested function to macro blocks, of size (row,col,time).
% Blocks will overlap in time and abut spatially.
% '3D', Apply the requested function simultaneously to all
% dimensions. Thus, convert all of the data into a 1D array and
% apply the colllapsing function to that 1D array.
% '1D', Apply the requested function to only the first
% dimensions. For example, the variable 'raw_data' should be 2D
% (spatial,temporal) or 1D (temporal). This is the default
% behavior.
%
%
% WARNING: Unless 'MacroBlock' or '3D' arguments are selected, the
% requested function will be applied to the first dimension only!
%
collapse_3d = 0;
collapse_macroblock = 0;
collapse_overlap_macroblock = 0;
collapse_slide_macroblock = 0;
mb_row = 1;
mb_col = 1;
mb_time = 1;
cnt = 1;
while cnt <= nargin-2,
switch lower(varargin{cnt}),
case { 'macroblock' },
collapse_macroblock = 1;
mb_row = varargin{cnt+1};
mb_col = varargin{cnt+2};
mb_time = varargin{cnt+3};
cnt = cnt + 4;
case { 'overlapmacroblock' },
collapse_overlap_macroblock = 1;
mb_row = varargin{cnt+1};
mb_col = varargin{cnt+2};
mb_time = varargin{cnt+3};
cnt = cnt + 4;
case { 'slidemacroblock' },
collapse_slide_macroblock = 1;
mb_row = varargin{cnt+1};
mb_col = varargin{cnt+2};
mb_time = varargin{cnt+3};
cnt = cnt + 4;
case { '3d' },
collapse_3d = 1;
cnt = cnt + 1;
case { '1d' },
collapse_3d = 0;
cnt = cnt + 1;
otherwise
error('Function st_collapse, optional argument "%s" not recognized', varargin{cnt});
end
if cnt <= nargin-2,
error('Optional arguments are mutually exclusive. Choose one only.');
end
end
% Overlapping Macroblock Case. Recurse to calculate.
if collapse_overlap_macroblock,
[row_raw,col_raw,time_raw] = size(raw_data);
data = zeros(row_raw-mb_row+1, col_raw-mb_col+1, time_raw-mb_time+1);
[row,col,time] = size(data);
for cnt1 = 1:mb_row,
temp = floor((row_raw - cnt1 + 1) / mb_row) * mb_row;
rng1 = cnt1:(cnt1-1+temp);
if length(rng1) < mb_row,
continue;
end
for cnt2 = 1:mb_col,
temp = floor((col_raw - cnt2 + 1) / mb_col) * mb_col;
rng2 = cnt2:(cnt2-1+temp);
if length(rng2) < mb_col,
continue;
end
for cnt3 = 1:mb_time,
temp = floor((time_raw - cnt3 + 1) / mb_time) * mb_time;
rng3 = cnt3:(cnt3-1+temp);
if length(rng3) < mb_time,
continue;
end
data(cnt1:mb_row:row, cnt2:mb_col:col, cnt3:mb_time:time) = ...
st_collapse(request, raw_data(rng1,rng2,rng3), 'macroblock', ...
mb_row,mb_col,mb_time);
end
end
end
return;
end
% Slide Macroblock Case. Recurse to calculate.
if collapse_slide_macroblock,
[row_raw,col_raw,time_raw] = size(raw_data);
row = floor(row_raw / mb_row);
col = floor(col_raw / mb_col);
time = time_raw - mb_time + 1;
data = zeros(row,col,time);
for cnt = 1:mb_time,
temp = floor((time_raw - cnt + 1) / mb_time) * mb_time;
rng = cnt:(temp+cnt-1);
if length(rng) < mb_time,
continue;
end
data(:,:, cnt:mb_time:time) = ...
st_collapse(request, raw_data(:,:,rng), 'macroblock', ...
mb_row,mb_col,mb_time);
end
return;
end
% if wanting to collapse a 3D structure all at once, reshape into an array.
if collapse_3d,
[r,c,t] = size(raw_data);
raw_data = reshape(raw_data, r*c*t,1);
end
% reshape for macroblocks.
if collapse_macroblock,
[r,c,t] = size(raw_data);
% error check.
if floor(r / mb_row) == 0 | floor(c / mb_col) == 0 | floor(t / mb_time) == 0,
error('st_collapse, macroblock too large.');
elseif mb_row == 1 & mb_col == 1 & mb_time == 1,
error('st_collapse, macro-block size must be greater than one.');
end
% figure out how to center the macroblocks
[r,c,t] = size(raw_data);
r_start = floor( mod(r,mb_row) / 2 ) + 1;
r_stop = r_start + floor(r / mb_row)*mb_row - 1;
c_start = floor( mod(c,mb_col) / 2 ) + 1;
c_stop = c_start + floor(c / mb_col)*mb_col - 1;
t_start = mod(t,mb_time) + 1;
t_stop = t;
% copy over macro-block data.
raw_data = raw_data(r_start:r_stop,c_start:c_stop,t_start:t_stop);
% reorganize first dimension
[r,c,t] = size(raw_data);
raw_data = reshape(raw_data, mb_row, r / mb_row, c, t);
raw_data = permute(raw_data, [1 3 4 2]);
raw_data = reshape(raw_data, mb_row * mb_col, c / mb_col, t, r / mb_row);
raw_data = permute(raw_data, [1 3 4 2]);
raw_data = reshape(raw_data, mb_row * mb_col * mb_time, t / mb_time, r / mb_row, c / mb_col);
raw_data = permute(raw_data, [1 3 4 2]);
end
% error checking.
[r,c,t] = size(raw_data);
if t > 1 & ~collapse_macroblock,
error('Function st_collapse requires 2D or 1D arrays, only. See help information warning.');
end
% Apply requested function.
above = 0;
below = 0;
tail = 0;
if r == 1,
% Special case. ST-collapse over a singleton dimension. This is
% always either the same as the input or zero.
if strcmp(request,'std') | length(findstr(request,'tail')) > 0,
data = 0 * raw_data;
else
data = raw_data;
end
% handle the usual cases.
elseif strcmp(request,'mean'),
data = mean(raw_data);
elseif strcmp(request,'std'),
data = std(raw_data);
elseif strcmp(request,'rms'),
data = sqrt(mean(raw_data.^2));
elseif strcmp(request,'min'),
data = min(raw_data);
elseif strcmp(request,'max'),
data = max(raw_data);
elseif strncmp(request, 'minkowski(', 10),
[mink n] = sscanf(request(11:length(request)), '%f,%f');
if n ~= 2,
error('Cannot parse minkowski P and R values in string "%s"', request(10:length(request)));
end
data = mean(abs(raw_data).^mink(1)).^(1.0/mink(2));
elseif strcmp(request,'between25%50%'),
percentile1 = 0.25;
percentile2 = 0.50;
% if 1D but wrong direction vector, transpose it.
if ndims(raw_data) == 2 & r == 1,
raw_data = raw_data';
end
% compute percentile functions
[rows,cols] = size(raw_data);
want1 = 1 + round((rows-1) * percentile1);
want2 = 1 + round((rows-1) * percentile2);
temp = sort(raw_data, 1);
data = mean(temp(want1:want2,:,:,:),1);
else
if strcmp(request,'10%'),
percentile = 0.10;
elseif strcmp(request,'25%'),
percentile = 0.25;
elseif strcmp(request,'50%'),
percentile = 0.50;
elseif strcmp(request,'75%'),
percentile = 0.75;
elseif strcmp(request,'90%'),
percentile = 0.90;
elseif strcmp(request,'above95%'),
percentile = 0.95;
above = 1;
elseif strcmp(request,'below5%'),
percentile = 0.05;
below = 1;
elseif strcmp(request,'above99%'),
percentile = 0.99;
above = 1;
elseif strcmp(request,'below1%'),
percentile = 0.01;
below = 1;
elseif strcmp(request,'above98%'),
percentile = 0.98;
above = 1;
elseif strcmp(request,'below2%'),
percentile = 0.02;
below = 1;
elseif strcmp(request,'above90%'),
percentile = 0.90;
above = 1;
elseif strcmp(request,'below10%'),
percentile = 0.10;
below = 1;
elseif strcmp(request,'above75%'),
percentile = 0.75;
above = 1;
elseif strcmp(request,'below25%'),
percentile = 0.25;
below = 1;
elseif strcmp(request,'above95%tail'),
percentile = 0.95;
above = 1;
tail = 1;
elseif strcmp(request,'below5%tail'),
percentile = 0.05;
below = 1;
tail = 1;
elseif strcmp(request,'below50%tail'),
percentile = 0.50;
below = 1;
tail = 1;
elseif strcmp(request,'below2%tail'),
percentile = 0.02;
below = 1;
tail = 1;
elseif strcmp(request,'above98%tail'),
percentile = 0.98;
above = 1;
tail = 1;
elseif strcmp(request,'above99%tail'),
percentile = 0.99;
above = 1;
tail = 1;
elseif strcmp(request,'below1%tail'),
percentile = 0.01;
below = 1;
tail = 1;
elseif strcmp(request,'above90%tail'),
percentile = 0.90;
above = 1;
tail = 1;
elseif strcmp(request,'below10%tail'),
percentile = 0.10;
below = 1;
tail = 1;
else
error('ERROR: percentile function "%s" not recognized by function compute_percentile', request);
end
% if 1D but wrong direction vector, transpose it.
if ndims(raw_data) == 2 & r == 1,
raw_data = raw_data';
end
% compute percentile functions
[rows,cols] = size(raw_data);
want = 1 + round((rows-1) * percentile);
%fprintf('r=%d, c=%d, percentile %f, want=%d\n', rows, cols, percentile, want);
temp = sort(raw_data, 1);
if ~below & ~above & ~tail,
data = temp(want,:,:,:);
elseif above & ~tail,
data = mean(temp(want:rows,:,:,:),1);
elseif below & ~tail,
data = mean(temp(1:want,:,:,:),1);
elseif above & tail,
if want == rows,
% special case, can't do tail.
data = temp(want,:,:,:) * 0;
else
data = mean(temp(want:rows,:,:,:),1) - temp(want,:,:,:);
end
elseif below & tail,
if want == 1,
% special case, can't do tail.
data = temp(want,:,:,:) * 0;
else
data = temp(want,:,:,:) - mean(temp(1:want,:,:,:),1);
end
end;
end
% get rid of extra dimension.
[a,b,c,d] = size(data);
data = reshape(data,b,c,d);