% Script common_map.m
%
% Script to estimate the data mapping to combine subjective data sets
% within one resolution (e.g., QCIF), for the VQEG MM data. A linear fit
% will determine the map, where the common scores for a given test are
% used for the independent variable x and the average of the common scores
% over all tests is used for the dependent variable y.
%
% Import subjective data manually from XLS files (using the File -> Import
% Data menus in the main MATLAB window). When prompted, accept the default
% variable names for the data (data). The XLS files are sorted by test,
% scene, and then HRC, except that the common sources are at the end of
% each test. This routine assumes that the XLS file has been modified
% to only contain test, scene, hrc, meanMOS, stdDevMOS, and NumVotes in
% that order, and that the test has been converted to a number (i.e., no
% letters). This allows Matlab to import the data properly into a data
% matrix.
%
n = 102; % total number of clips per test, including originals
nc = 3; % number of common clips at end of each test

% =1 to include original clips in mapping, =0 to discard
% Should discard for DMOS but could keep for MOS
keep_originals = 1;

% Determine the number of tests
ntests = size(data,1)/n;

% Loop through tests, if the subjective scores for all common video clips
% in the test are valid, then that test will be included in the data map.
% The first column of data is assumed to have the test number.
test_num = [];
common = [];
for i = 1 : ntests
    this_test = data(1 + n * (i - 1),1);
    this_common = data(1 + n * i - nc : n * i, :);
    if (~keep_originals)
        this_common = this_common(this_common(:,3) ~= 0,4); % pick off mos only
    else
        this_common = this_common(:, 4);
    end
    % Make sure that all mos scores are valid before using this set
    if (~isnan(this_common)) % There are no NaN for MOS in common set
        test_num = [test_num this_test];
        common = [common this_common];
    end
end

common_mean = mean(common,2);
valid_tests = size(test_num,2);

% Fit and plot
porder = 1; % polynomial order
map_fits = []; % holds the polynomial fit
map_corrs = []; % holds the correlation coefficents between individual test and mean test
x = 1:0.1:5;
for i = 1 : ntests
    this_fit = polyfit(common(:,i),common_mean,porder)';
    map_fits = [map_fits this_fit];
    y = polyval(this_fit,x);
    r = corrcoef(common(:,i),common_mean);
    map_corrs = [map_corrs r(1,2)];
    fprintf('Test Number = %i, correlation = %f\n',test_num(i), r(1,2))
    plot(common(:,i),common_mean, '.')
    grid on
    xlabel('Individual Test');
    ylabel('Mean Test');
    hold on
    plot(x,y,'r');
    hold off
    pause
end

% Apply the common map to the entire subjective data set
% For a linear fit, the stdev scales as the gain term of the fit.
scaled_mos = []; % all arrays of dimension n x valid_tests
unscaled_mos = [];
scaled_std = [];
unscaled_std = [];
num_viewers = [];
for i = 1 : ntests
    exclude_orig = find(data(:,1)==test_num(i) & data(:,3)~=0);
    include_orig = find(data(:,1)==test_num(i));
    % This std scaling is only valid for linear fits, approximate otherwise
    if (~keep_originals)
        scaled_mos(:,i) = polyval(map_fits(:,i),data(exclude_orig, 4));
        unscaled_mos(:,i) = data(exclude_orig, 4);
        scaled_std(:,i) = sum(map_fits(1:porder,i))*data(exclude_orig, 5);
        unscaled_std(:,i) = data(exclude_orig, 5);
        num_viewers(:,i) = data(exclude_orig, 6);
    else
        scaled_mos(:,i) = polyval(map_fits(:,i),data(include_orig, 4));
        unscaled_mos(:,i) = data(include_orig, 4);
        scaled_std(:,i) = sum(map_fits(1:porder,i))*data(include_orig, 5);
        unscaled_std(:,i) = data(include_orig, 5);
        num_viewers(:,i) = data(include_orig, 6);
    end
end
