% Always close everything beforehand
close all;
clear all;

% 1. Understand the Image Basis and Render them
% Files like "light_pca.mat" contain a pre-computed image basis. Load one of them and perform a simple visualization.
% Here, we use the pre-provided "light_pca.mat" as an example.
% You will be expected to load your own computed image basis in Exercise 5.

% Load
load("light_pca.mat");

% Render
figure(1);
set(gcf,'Visible','on')
T = size(Y,2);
for t = 1:T
    It = renderim(Y(:,t),B,imsize);     % Consists of operation to integrate basis B with coefficients Y
    
    % For custom normalized PCA, you might need to add back the mean image
    % during the reconstruction.
    if exist("Imean")
        It = It + Imean;
    end

    figure(1);
    imshow(It);
    drawnow;
end

%
% 2. Densely Generate New Y_new Values with a Piecewise Interpolation
% The video rendered above looked a bit choppy. That's because we have images with illumination from only 12 angles between -120 degrees and 120 degrees. We would like to create a smoother looking video.
% To do that, we will synthesize images with illumination from angles between the angles we already have. Recall that above we generated each image as a linear combination of the image basis vectors.
% \begin{array}{l}
% \mathbf{I}=\mathbf{By}\\
% \mathbf{I}=\left\lbrack \begin{array}{cccc}
% {\mathbf{b}}_1  & {\mathbf{b}}_2  & \ldotp \ldotp \ldotp  & {\mathbf{b}}_{\mathbf{n}}
% \end{array}\right\rbrack \left\lbrack \begin{array}{c}
% y_1 \\
% y_2 \\
% \vdots \\
% y_n
% \end{array}\right\rbrack
% \end{array}
% where \mathbf{I} is the image, {\mathbf{b}}_1 ,{\mathbf{b}}_2 ,\ldotp \ldotp \ldotp {\mathbf{b}}_{\mathbf{n}} are basis vectors, and y_1 ,y_2 ,\ldotp \ldotp \ldotp y_n are the coefficents used to construct \mathbf{I} as a linear combination of the basis vectors.
% We can denote the image corresponding to an illumination angle of \theta \;degrees as {\mathbf{I}}_{\theta \;}. For example, we will call the image with illumination at 120 degrees {\mathbf{I}}_{120}. All images are constructed using the same basis \mathbf{B}, but with different coefficents {\mathbf{y}}_{\theta \;}. We can represent each image in our dataset the following way:
% {\mathbf{I}}_{-120} ={\textrm{By}}_{-120} , {\mathbf{I}}_{-95} ={\textrm{By}}_{-95} , {\mathbf{I}}_{-70} ={\textrm{By}}_{-70} ,     . . .     , {\mathbf{I}}_{70} ={\textrm{By}}_{70} , {\mathbf{I}}_{95} ={\textrm{By}}_{95} , {\mathbf{I}}_{120} ={\mathbf{By}}_{120}
%
% To generate all the images at once, we arrange the {\mathbf{y}}_{\theta \;}vectors as columns of a matrix \mathbf{Y}.
% \left\lbrack \begin{array}{cccc}
% {\mathbf{I}}_{\theta {\;}_1 }  & {\mathbf{I}}_{\theta_2 }  & \cdots  & {\mathbf{I}}_{\theta_m }
% \end{array}\right\rbrack =\left\lbrack \begin{array}{cccc}
% {\mathbf{b}}_1  & {\mathbf{b}}_2  & \ldotp \ldotp \ldotp  & {\mathbf{b}}_{\mathbf{n}}
% \end{array}\right\rbrack \left\lbrack \begin{array}{cccc}
% {\mathbf{y}}_{\theta {\;}_1 }  & {\mathbf{y}}_{\theta_2 }  & \ldotp \ldotp \ldotp  & {\mathbf{y}}_{\theta {\;}_m }
% \end{array}\right\rbrack
% Equipped with our basis \mathbf{B}, we should be able to generate images corresponding to any illumination angle \theta \in \left(-120,120\right) if we find the right coefficients {\mathbf{y}}_{\theta \;}. Our strategy will then be to fit a function {\mathbf{y}}_{\theta \;} =\mathbf{f}\left(\theta \right) using the interpolation techniques we learned in the previous lab.
%
% We will take the following steps:
% Choose a set of angles from which we want the illumination. These angles should remain between -120 and 120 degrees and should be more densely distributed than the angles corresponding to the images in our dataset.
% Fit {\mathbf{y}}_{\theta \;} =\mathbf{f}\left(\theta \right) using either piecewise linear interpolation or polynomial interpolation. Note that {\mathbf{y}}_{\theta \;}is a vector of coefficients and theta is a scalar.
% Render images {\mathbf{I}}_{\theta \;}as {\mathbf{I}}_{\theta \;} =\textrm{Bf}\left(\theta \right).
%
% 2.1 Construct a New Densely Sampled X_new
% X is a vector that contains the angles of illumination corresponding to each picture in our dataset.
% Create a vector X_new that uniformly samples angles between -120 and 120 at at higher density than X. In the following steps you will synthesize images with illumination from these new angles.
% num_frames will control how densely X_new is sampled.
% You should be modify this number as you see fit.
%

% Expect to vary
num_frames = 4*size(X,2);

%

% ########################################
% WRITE YOUR CODE HERE TO
% Densely sample X_new based on X
% Make sure when you construct your X_new, information
% originally in X is preserved
% e.g. For lighting: 
% X_new(1) = -120; X(1) = -120;
% X_new(size(X_new, 2)) = 120; X(13) = 120;



% ########################################

%
% 2.2 Piecewise Interpolation
% Given the original angles of illumination X, the corresponding coefficients Y, and your densely sampled X_new, perform piecewise interpolation and generate the new coefficients Y_new. Y_new will contain interpolated values of Y and can be used to synthesize images from new illumination angles as linear combinations of the vectors in our image basis B.
%

% Note that you need to take transpose of X, Y, X_new for interp1 to accept
Y_new = interp1(X', Y', X_new','linear')';

%
% 2.3 Render the New Image Sequence
%

% Render the new basis
figure(1);
clf(1);
set(gcf,'Visible','on')
T = size(Y_new,2);
for t = 1:T
    It = renderim(Y_new(:,t),B,imsize);     % Consists of operation to integrate basis B with coefficients Y
    
    % For custom normalized PCA, you might need to add back the mean image
    % during the reconstruction.
    if exist("Imean")
        It = It + Imean;
    end
    figure(1);
    imshow(It);
    drawnow;
end

%
% 3. Polynomial Interpolation
% Repeat the previous experiments using polynomial approximation / interpolation.
% You can use your your polynomial interpolation function from lab 2: Y_new=polynomial_interp(X,Y,X_new,n) which fits a polynomial of degree n to Y, and returns the values of that polynomial for each element in the vector X_new.
% Alternatively, you can also use MATLAB's built-in functions "polyfit" and "polyval".
%

% Note: Previously loaded X, Y and X_new can still be used in this step
% ########################################
% WRITE YOUR CODE HERE TO
% Perform polynomial interpolation / Approximation with various degrees


% ########################################

%
% 4. Experiment with Omission of Image Basis Vectors
% Observe the quality of reconstructed images when you omit some of the image basis vectors during reconstruction. Recall that columns of Y contain coefficients with which the image basis vectors in B are multiplied. Thus we can omit some image basis vectors in our reconstruction by setting rows of Y to zero.
%

% ########################################
% WRITE YOUR CODE HERE TO
% Omit image basis vectors in B and observe the quality of reconstruction

% ########################################

%
% 5. Experiment with Leaving Out Original Data
% From the original Y, pick one (or more) t-values, and omit the sets of coefficients Y(:, t) corresponding to image t.
% Re-compute the interpolated coefficients Y_new and be sure to now include the t's corresponding to the ommited values.
% Does the interpolation correctly recover the Y? (ie how close is Y_new(:,t) to the dropped Y(:,t)?) Render the image at t and compare with the original. How close do they look?
%

% ########################################
% WRITE YOUR CODE HERE TO
% Omit the t-values and redo interpolation


% ########################################