// Analyse numérique : TP n°2
// Groupe : C06
// ~ INGOUFF Christian, TYNDAL Pierre-Alexandre
//
// Compression d'images par FFT et SVD

// Usage :
//  main; -> Interface console
//  ou
//  1. img = getImage(fichier);
//  2. test = doFFT(img, pourcentage); ou test = doSVD(img, pourcentage);
//  3. getFromFFT(test, size(img)); ou getFromSVD(test, size(img));

// Partie 0 : Importation d'images

// Fonction : getImage
// Description : Importation d'images à partir d'un fichier texte
// Paramètres :
//      fichier : Nom du fichier à importer
// Retourne :
//      Matrice contenant l'image décrite dans le fichier spécifié
//      (dans le format hereinafter décrit en tant que "format img")
function y = getImage(fichier)
    a = read(fichier,-1,640);
    
    // Début : partie graphique
    ap = uint8(255*a);
    figure(1, "Figure_name", "Image originale");
    clf(1);
    ShowImage(ap,'Image originale');
    // Fin : partie graphique
    
    y = a; // Retour de l'image de dimensions m * n
endfunction

// Partie 1 : FFT

// Fonction : doFFT
// Description : Compression d'une image par application de la transformée de Fourier (FFT)
// Paramètres :
//      img : Matrice contenant l'image à compresser (format img)
//      pourcentage : Pourcentage de compression
// Retourne :
//      Vecteur contenant pourcentage% des valeurs obtenues par la FFT
function res = doFFT(img, pourcentage)    
    tab = matrix(img,1,length(img));
    A = fft(tab, -1);
    A = A(1:length(A)/2); // On ne considère que la première moitié des valeurs (symétrie)
    
    // Début : partie graphique
    Amod = abs(A);
    x = 2:length(Amod);
    figure(2, "Figure_name", "FFT : Spectre de l''image");
    clf(2);
    plot(x,Amod(2:length(Amod)));
    // Fin : partie graphique
    
    res = A(1:length(A)*(pourcentage/100)); // Retour de pourcentage% des premières valeurs
endfunction

// Fonction : getFromFFT
// Description : Reconstitution d'une image à partir d'un vecteur de valeurs
// Paramètres : 
//      part_A : Vecteur de valeurs obtenues auparavant grâce à la FFT (doFFT)
//      ini_size : dimensions de l'image, en couple (size(img))
// Retour :
//      Matrice représentant l'image reconstituée (en format img)
function res = getFromFFT(part_A, ini_size)
    A_cmp = zeros(1, (ini_size(1)*ini_size(2)));
    A_cmp(1:length(part_A)) = part_A;
    
    // Symétrie : reconstruction de la seconde moitié par le vecteur donné
    // (la seconde moitié étant le conjugué de la 1ère en symétrie)
    A_cmp((ini_size(1)*ini_size(2))-length(part_A)+2:(ini_size(1)*ini_size(2))) = conj(part_A($:-1:2));
    
    B = fft(A_cmp, 1);
    Bmod = abs(B);
    tab = matrix(Bmod, ini_size(1), ini_size(2));
    
    // Début : partie graphique
    ap = uint8(255*tab);
    figure(3, "Figure_name", "Image reconstruite à partir de FFT");
    clf(3);
    ShowImage(ap,'Image reconstruite à partir de FFT');
    // Fin : partie graphique
    
    res = tab; // Retour de l'image reconstituée à partir de part_A
endfunction

// Partie 2 : SVD

// Fonction : doSVD
// Description : Compression d'une image en utilisant la décomposition en valeurs singulières
// Paramètres :
//      img : Matrice contenant l'image à compresser (format img)
//      pourcentage : Pourcentage de compression
// Retourne :
//      Matrice contenant dans l'ordre des colonnes :
//          1. pourcentage% * min(m, n) des valeurs singulières
//          2. La matrice carrée unitaire pour la hauteur (transposée)
//          3. La matrice carrée unitaire pour la longueur (transposée)
function res = doSVD(img, pourcentage)
    [U, S, V] = svd(img);
    S = diag(S); // On récupère les min(m, n) valeurs singulières (extraction valeurs diagonales de S)
    
    // Début : partie graphique
    figure(4, "Figure_name", "Représentation des valeurs singulières");
    clf(4);
    x = 1:length(S);
    plot(x,S);
    // Fin : partie graphique
    
    // Réduction à pourcentage% de valeurs
    nb_val = length(S) * (pourcentage/100);
    U = U(:, 1:nb_val);
    S = S(1:nb_val);
    V = V(:, 1:nb_val);
    
    res = [S,U',V'];
endfunction

// Fonction : getFromSVD
// Description : Reconstitution d'une image compressée à l'aide de la SVD (doSVD)
// Paramètres :
//      part_svd : Matrice obtenue à l'aide de doSVD
//      ini_size : dimensions de l'image, en couple (size(img))
// Retourne :
//      Matrice représentant l'image reconstituée (en format img)
function res = getFromSVD(part_svd, ini_size)
    // Extraction des matrices U, S, V
    S = diag(part_svd(:, 1));
    U = part_svd(:, 2:ini_size(1)+1)';
    V = part_svd(:, ini_size(1)+2:size(part_svd,2));
    tab = U * S * V;
        
    // Début : partie graphique
    ap = uint8(255*tab);
    figure(5, "Figure_name", "Image reconstruite à partir de SVD");
    clf(5);
    ShowImage(ap,'Image reconstruite à partir de SVD');
    // Fin : partie graphique
    
    res = tab;
endfunction

// Partie 3 : Interface console

function res = pickFunction(choice)
    if (choice == 1) | (choice == 2) then
        disp(ls());
        disp("Spécifier l''image à compresser");
        fichier = input("Fichier : ", "s");
        img = getImage(fichier);
        disp("Image importée.");
        disp("Choisir le pourcentage de compression :");
        pourcentage = input("Pourcentage : ");
    end
    
    select choice,
    case 0 then res = 0;,
    case 1 then
        disp("Compression de l''image avec FFT...");
        tmp_FFT = doFFT(img, pourcentage);
        disp("Compression terminée.");
        disp("Nombre de valeurs stockées : ");
        disp(length(tmp_FFT));
        disp("(Sur le nombre initial de pixels de l''image :)");
        disp(length(img));
        disp("Décompression de l''image...");
        img_FFT = getFromFFT(tmp_FFT, length(img));
        res = 0;
    ,
    case 2 then
        disp("Compression de l''image avec SVD...");
        tmp_SVD = doSVD(img, pourcentage);
        disp("Compression terminée.");
        disp("Nombre de valeurs stockées (valeurs singulières et matrices carrées unitaires) : ");
        disp(length(tmp_SVD));
        disp("(Sur le nombre initial de pixels de l''image :)");
        disp(length(img));
        disp("Décompression de l''image...");
        img_SVD = getFromSVD(tmp_SVD, size(img));
        res = 0;
    ,
    else disp("Veuillez entrer un chiffre entre 0 et 2."); res = 1;,
    end
endfunction

// Fonction : main
// Description : Fonction maîtresse du programme
// Retourne :
//      0 si le programme quitte normalement, autre chose sinon
function res = main()
    res = 1;
	while (res > 0)
		disp("Veuillez entrer l''entier correspondant à la fonction :");
		disp("0. Quitter le programme");
		disp("1. Compression d''une image avec FFT");
		disp("2. Compression d''une image avec SVD");
		choice = input("Réponse : ");
		res = pickFunction(choice);
	end
    disp("Fin du programme.");
endfunction
