%%  Separace statickeho a dynamickeho obsahu ve videu
%   Pristup 1: Separace pozadi pomoci prumeru/medianu
%   Autor: Vadym Serediuk

%% Inicializace
clc
clear

% Vyber statistiky:
statsCase = 2;          
    % 1: prumer (mean); 
    % 2: median.
    
% Vyber vstupniho videa:
inputVideoCase = 6;
switch inputVideoCase
    case 1, inputVideo = 'test';
    case 2, inputVideo = 'escalator';
    case 3, inputVideo = 'rowing';
    case 4, inputVideo = 'shop';
    case 5, inputVideo = 'traffic';
    case 6, inputVideo = 'pedestrian';
    case 7, inputVideo = 'pedestrian_noisy';
end
inputVideoFolder = 'input_video';
inputVideoFull = [inputVideo, '.avi'];

videoOutputTrigger = false;         % <- ulozeni jednotlivych videi ve formatu AVI
videoCompilationTrigger = true;     % <- ulozeni kompilaci 4 videi ve formatu AVI
videoStructTrigger = true;          % <- ulozeni matic videi do MAT souboru (pro dalsi zpracovani)
logOutputTrigger = true;            % <- vytvoreni log souboru s detaily vypoctu

outputFolder =  'output_STAT';
outputA =       'STAT_A.avi';
outputL =       'STAT_L.avi';
outputS =       'STAT_S.avi';
outputM =       'STAT_M.avi';
outputComp =    'STAT.avi';
outputStruct =  'STAT.mat';
outputLog =     'STAT_log.txt';

fprintf('Start.\n')
tStart = tic;

%% Import videa
if inputVideoCase <= 5 % nahravani AVI souboru
    [ A, vidWidth, vidHeight ] = importVideoToColumn( ...
        [inputVideoFolder, '\', inputVideoFull] );
else % nahravani simulovaneho videa z MAT souboru
    load([inputVideoFolder, '\pedestrian.mat']);
    switch inputVideoCase
        case 6, A = video.arrays.A * 255;
        case 7, A = video.arrays.A_noise * 255;
    end
    Lorig = video.arrays.L * 255; % L originalni
    Sorig = video.arrays.S * 255; % S originalni
    
    vidWidth = video.parameters.width;
    vidHeight = video.parameters.height;
end

[pixelCount, frameCount] = size(A);

%% Vypocet
% Systematicky vyber nekolika malo snimku z videa
ImageStack = zeros( pixelCount, 0 );
ImageBG = zeros( pixelCount, 1 );
N = 5; % kazdy N-ty snimek bude pouzit pro statistiku

for i = 1 : frameCount 
    if mod( i, N ) == 0
        ImageStack( :, end+1 ) = A(:,i);
    end
end

% Separace pozadi
% Pro kazdy pixel  provest prumerovani (resp. median) vypranych snimku.
for i = 1 : pixelCount      % height
    switch statsCase
        case 1, ImageBG(i,1) = mean( ImageStack(i, :) );
        case 2, ImageBG(i,1) = median( ImageStack(i, :) );
    end    
end

% Vytvoreni L slozky videa ze snimku
L = zeros(size(A));
for i = 1 : size(L, 2)
    L(:, i) = ImageBG(:);
end

% Vytvoreni S slozky videa
S = A - L;

% Maska
M = createMask(S);

% Normalizace a vyrovnani jasu matice S (pro graficky vystup)
Str = shiftBrightness (S);

%% Hodnost L a ridkost S, M
rank_L = rank(L);
sparsity_S = length(find(S == 0)) / length(S(:));
sparsity_M = length(find(M == 0)) / length(M(:));

fprintf('rank(L) = %2.0f (rank(A) = %2.0f)\n', rank_L, min(pixelCount, frameCount))
fprintf('sparsity(S) = %2.4f\n', sparsity_S)
fprintf('sparsity(M) = %2.4f\n', sparsity_M)

%% Signal-to-noise Ratio (pouze pro simulovana data)
if inputVideoCase == 6 || inputVideoCase == 7
    SNR_L = 20 * log10( norm(Lorig, 'fro') / norm(Lorig - L, 'fro'));
    
    fprintf('SNR(L) = %2.4f dB\n', SNR_L)
end

%% Ulozeni videi
if videoOutputTrigger == true
    makeVideoFromColumn( A, [outputFolder, '\', inputVideo, '_', outputA] , vidWidth, vidHeight );
    makeVideoFromColumn( L, [outputFolder, '\', inputVideo, '_', outputL] , vidWidth, vidHeight );
    makeVideoFromColumn( Str, [outputFolder, '\', inputVideo, '_', outputS] , vidWidth, vidHeight );
    makeVideoFromColumn( M, [outputFolder, '\', inputVideo, '_', outputM] , vidWidth, vidHeight );
end

if videoCompilationTrigger == true
    makeCompilation( A, L, Str, M, [outputFolder, '\', inputVideo, '_', outputComp] , vidWidth, vidHeight, 1 );
end

%% Ulozeni promennych do MAT souboru
if videoStructTrigger == true
    video = struct;
    video.arrays = struct;
    video.parameters = struct;
    
    video.arrays.A = A;
    video.arrays.L = L;
    video.arrays.S = S;
    video.arrays.Str = Str;
    video.arrays.M = M;
    
    video.parameters.width = vidWidth;
    video.parameters.height = vidHeight;
    video.parameters.frames = frameCount;
    
    save([outputFolder, '\', inputVideo, '_', outputStruct], 'video');
end

%% Log soubor
totalElapsedTime = toc( tStart );

if logOutputTrigger == true
    % inicializace
    fid = fopen( [outputFolder, '\', inputVideo, '_', outputLog], 'w' );
    fprintf( fid, 'Start.\r\n' );
    
    % import videa
    fprintf( fid, 'Imported video:\r\n');
    fprintf( fid, '\tName: \t\t%s \r\n', inputVideo );
    fprintf( fid, '\tVideo size: \t%1.0fx%1.0f px \r\n', vidWidth, vidHeight );
    fprintf( fid, '\tFrame count:\t%1.0f \r\n', frameCount );
    fprintf( fid, '\tMatrix size:\t%1.0fx%1.0f \r\n', pixelCount, frameCount );
    
    % parametry
    fprintf(fid, 'Parameters, constants:\r\n' );
    fprintf(fid, '\tMethod \t: ');
        switch statsCase
            case 1, fprintf( fid, ' Mean \r\n');
            case 2, fprintf( fid, ' Median \r\n');
        end
    
    % vypocet
    fprintf( fid, 'Computation ... \r\n' );
    fprintf( fid, '\t# of stacked images = %1.0f (of %1.0f frames)\r\n', size(ImageStack,2), frameCount );

    fprintf(fid, '\trank(L) = %2.0f (rank(A) = %2.0f)\r\n', rank_L, min(pixelCount, frameCount));
    fprintf(fid, '\tsparsity(S) = %2.4f\r\n', sparsity_S);
    fprintf(fid, '\tsparsity(M) = %2.4f\r\n', sparsity_M);
    if inputVideoCase == 6 || inputVideoCase == 7  
        fprintf(fid, '\tSNR(L) = %2.4f dB\r\n', SNR_L);
    end
    
    % finished
    fprintf(fid, 'Finished! Total elapsed time is %2.2f seconds.', ...
        totalElapsedTime);
    fclose all;
end

%% Konec
fprintf( 'Konec! Celkovy cas %1.2f s.\n', totalElapsedTime )

