%% Image Category Classification Using Bag of Features
% Table of Contents
% Load Image Dataset
% Prepare Training and Validation Image Sets
% Create a Visual Vocabulary and Train an Image Category Classifier
% Evaluate Classifier Performance
% Try the Newly Trained Classifier on Test Images
%
%
%% Load Image Dataset
% Load the image collection using an imageDatastore to help you manage the data. Because imageDatastore operates on image file locations, and therefore does not load all the images into memory, it is safe to use on large image collections.

% DATASET
dataset = 'MerchData';

% PATHS
base_dir = '/Users/grossi/Google Drive/Informazione_Multimediale/';
dataset_dir = [base_dir 'data/' dataset]
imds = imageDatastore(dataset_dir,'IncludeSubfolders',true,'LabelSource','foldernames');

% You can easily inspect the number of images per category as well as category labels as shown below:

tbl = countEachLabel(imds)

% Note that the labels were derived from directory names used to construct the ImageDatastore, but can be customized by manually setting the Labels property of the ImageDatastore object. Next, display a few of the images to get a sense of the type of images being used.

figure
montage(imds.Files(1:16:end))

% Note that for the bag of features approach to be effective, the majority of the object must be visible in the image.
%
%% Prepare Training and Validation Image Sets
% Separate the sets into training and validation data. Pick 60% of images from each set for the training data and the remainder, 40%, for the validation data. Randomize the split to avoid biasing the results.

[trainingSet, validationSet] = splitEachLabel(imds, 0.6, 'randomize');

% The above call returns two imageDatastore objects ready for training and validation tasks.
%
%% Create a Visual Vocabulary and Train an Image Category Classifier
% Bag of words is a technique adapted to computer vision from the world of natural language processing. Since images do not actually contain discrete words, we first construct a "vocabulary" of  SURF features representative of each image category.
% This is accomplished with a single call to bagOfFeatures function, which:
% extracts SURF features from all images in all image categories
% constructs the visual vocabulary by reducing the number of features through quantization of feature space using K-means clustering

bag = bagOfFeatures(trainingSet);

%
% Additionally, the bagOfFeatures object provides an encode method for counting the visual word occurrences in an image. It produced a histogram that becomes a new and reduced representation of an image.

img = readimage(imds, 2);
featureVector = encode(bag, img);

% Plot the histogram of visual word occurrences
figure
bar(featureVector)
title('Visual word occurrences')
xlabel('Visual word index')
ylabel('Frequency of occurrence')

%
% This histogram forms a basis for training a classifier and for the actual image classification. In essence, it encodes an image into a feature vector.
% Encoded training images from each category are fed into a classifier training process invoked by the trainImageCategoryClassifier function. Note that this function relies on the multiclass linear SVM classifier from the Statistics and Machine Learning Toolbox™.

categoryClassifier = trainImageCategoryClassifier(trainingSet, bag)

% The above function utilizes the encode method of the input bag object to formulate feature vectors representing each image category from the  trainingSet.
%
%% Evaluate Classifier Performance
% Now that we have a trained classifier, categoryClassifier, let's evaluate it. As a sanity check, let's first test it with the training set, which should produce near perfect confusion matrix, i.e. ones on the diagonal.

confMatrix = evaluate(categoryClassifier, trainingSet);

%
% Next, let's evaluate the classifier on the validationSet, which was not used during the training. By default, the evaluate function returns the confusion matrix, which is a good initial indicator of how well the classifier is performing.

confMatrix = evaluate(categoryClassifier, validationSet);

% Compute average accuracy
mean(diag(confMatrix))

% You can tune bagOfFeatures hyperparameters and continue evaluating the trained classifier until you are satisfied with the results. Additional statistics can be derived using the rest of arguments returned by the evaluate function. See help for imageCategoryClassifier/evaluate.
%
%% Try the Newly Trained Classifier on Test Images
% You can now apply the newly trained classifier to categorize new images.

img = imread(fullfile('MerchData','MathWorks Cap','Hat_0.jpg'));
figure
imshow(img)
[labelIdx, scores] = predict(categoryClassifier, img);

% Display the string label
categoryClassifier.Labels(labelIdx)

% Copyright 2014-2019 The MathWorks, Inc.