% This program will train visual detection tool (ANN) 
% and will recognize different vehicle starting faults
% 8/8/2024
% Coded in MATLAB R2024b
% Place file under MATLAB folder NIS/Visualfaults

%Remove previous settings
clear; close all; 

%Set environment
dataFolder = "./NIS/Visualfaults";
[files,labels] = getmyfilesmp4(dataFolder);

idx = 1;
filename = files(idx);
      
c = filename{1};
video = readVideo(c);
size(video)
labels(idx)
numFrames = size(video,4);

%Make feature vectors
netCNN = googlenet;
inputSize = netCNN.Layers(1).InputSize(1:2);
layerName = "pool5-7x7_s1";

numFiles = numel(files);
sequences = cell(numFiles,1);

%Modify video file for processing
for i = 1:numFiles
    fprintf("Reading file %d of %d...\n", i, numFiles)
    b = files(i);
    c = b{:}
    video = readVideo(c);
    video = centerCrop(video,inputSize);
    sequences{i,1} = activations(netCNN,video,layerName,'OutputAs','columns');
end

%Show updated video without time and date
figure
for i = 1:numFrames
    frame = video(:,:,:,i);
    imshow(frame/255);
    drawnow
end

%Make setting for training
numObservations = numel(sequences);
idx = randperm(numObservations);
N = floor(0.9 * numObservations);

idxTrain = idx(1:N);
sequencesTrain = sequences(idxTrain);
labelsTrain = labels(idxTrain);

idxValidation = idx(N+1:end);
sequencesValidation = sequences(idxValidation);
labelsValidation = labels(idxValidation);

% Check sequences
numObservationsTrain = numel(sequencesTrain);
sequenceLengths = zeros(1,numObservationsTrain);

for i = 1:numObservationsTrain
    sequence = sequencesTrain{i};
    sequenceLengths(i) = size(sequence,2);
end

%For efficiency remove sequence more than 400
maxLength = 400;
idx = sequenceLengths > maxLength;
sequencesTrain(idx) = [];
labelsTrain(idx) = [];

%Make LSTM network
numFeatures = size(sequencesTrain{1},1);
numClasses = numel(categories(labelsTrain));

layers = [
    sequenceInputLayer(numFeatures,'Name','sequence')
    bilstmLayer(2000,'OutputMode','last','Name','bilstm')
    dropoutLayer(0.5,'Name','drop')
    fullyConnectedLayer(numClasses,'Name','fc')
    softmaxLayer('Name','softmax')
    classificationLayer('Name','classification')];

% Training options
miniBatchSize = 2;
numObservations = numel(sequencesTrain);
numIterationsPerEpoch = floor(numObservations / miniBatchSize);

options = trainingOptions('adam', ...
    'MiniBatchSize',miniBatchSize, ...
    'InitialLearnRate',1e-4, ...
    'GradientThreshold',2, ...
    'Shuffle','every-epoch', ...
    'ValidationData',{sequencesValidation,labelsValidation}, ...
    'ValidationFrequency',numIterationsPerEpoch, ...
    'Plots','training-progress', ...
    'Verbose',false);

[netLSTM,info] = trainNetwork(sequencesTrain,labelsTrain,layers,options);

% Check accuracy
YPred = classify(netLSTM,sequencesValidation,'MiniBatchSize',miniBatchSize);
YValidation = labelsValidation;
accuracy = mean(YPred == YValidation);

%Save trained visual tool
save VisMotionFaultNet.mat 

%Reading in video data for processing
function video = readVideo(filename)    
    vr = VideoReader(filename);
    H = vr.Height;
    W = vr.Width;
    C = 3;
    
    numFrames = floor(vr.Duration * vr.FrameRate);
    video = zeros(H,W,C,numFrames);
    
    i = 0;
    while hasFrame(vr)
        i = i + 1;
        video(:,:,:,i) = readFrame(vr);
    end
    
    if size(video,4) > i
        video(:,:,:,i+1:end) = [];
    end
end

%Remove the date and time on the bottom side of the videos
function videoResized = centerCrop(video,inputSize)
    videoResized = imresize(video,inputSize(1:2));

end
