%% Deep Learning Code Generation on Intel Targets for Different Batch Sizes
% 
% This example shows how to use the |codegen| command to generate code for an image 
% classification application that uses deep learning on Intel&reg; processors. 
% The generated code uses the Intel Math Kernel Library for 
% Deep Neural Networks (MKL-DNN). This example consists of two parts: 
%
% * The first part shows how to generate a MEX function that accepts a batch 
% of images as input. 
% * The second part shows how to generate an executable that accepts a batch 
% of images as input.
%
% Copyright 2020 The MathWorks, Inc.
%% Prerequisites
% * Intel processor with support for Intel Advanced Vector Extensions 2
% (Intel AVX2) instructions
% * Intel Math Kernel Library for Deep Neural Networks (MKL-DNN) 
% * Environment variables for the compilers and libraries. For information 
% on the supported versions of compilers, see 
% <https://www.mathworks.com/support/requirements/supported-compilers.html- Supported Compilers>. 
% For setting up the environment variables, see 
% <docid:coder_ug#mw_99a4acec-aced-453c-aadd-a18e83133574 Prerequisites>.
%
%
% This example is supported on Linux(R) , Windows(R) and Mac(R) platforms and not
% supported for MATLAB Online.
%% Download input video File
%
% Download a sample video file.
%
%     if ~exist('./object_class.avi', 'file')
%         url = 'https://www.mathworks.com/supportfiles/gpucoder/media/object_class.avi.zip';
%         websave('object_class.avi.zip',url);
%         unzip('object_class.avi.zip');
%     end
%
%% Define the |resnet_predict| Function
% 
% This example uses the DAG network ResNet-50 to show image classification
% on Intel desktops. A pretrained ResNet-50 model for MATLAB is available as part of the
% support package Deep Learning Toolbox Model for ResNet-50 Network. 
% 
% The |resnet_predict| function loads the ResNet-50 network into a persistent
% network object and then performs prediction on the input. Subsequent calls
% to the function reuse the persistent network object.
%
type resnet_predict
%% Generate MEX for |resnet_predict|
%
% To generate a MEX function for the |resnet_predict| function, use
% |codegen| with a deep learning configuration object for the MKL-DNN
% library.
% Attach the deep learning configuration object to the MEX code
% generation configuration object that you pass to |codegen|. 
% Run the |codegen| command and specify the input as a 4D matrix of size 
% [224,224,3,|batchSize|]. This value corresponds to the input layer size of 
% the ResNet-50 network.
%
    batchSize = 5;
    cfg = coder.config('mex');
    cfg.TargetLang = 'C++';
    cfg.DeepLearningConfig = coder.DeepLearningConfig('mkldnn');
    codegen -config cfg resnet_predict -args {ones(224,224,3,batchSize,'single')} -report
 
%% Perform Prediction on a Batch of Images
%
% Presuming the Object_class.avi video file is already downloaded.
% Create the videoReader object and read five frames using videoReader read
% function.Since batchSize is set to 5 read 5 images .Resize the batch of 
% input images  to size needed by resnet50 size expected by ResNet50 network. 
% 
%     videoReader = VideoReader('Object_class.avi');
%     imBatch = read(videoReader,[1 5]);
%     imBatch = imresize(imBatch, [224,224]);
% 
%%
% Call the generated |resnet_predict_mex| function which 
% outputs classification results for the inputs that you provide.
%
%     predict_scores = resnet_predict_mex(single(imBatch));
%
%%
% Get top 5 probability scores and their labels for each image in the
% batch.
%
%     [val,indx] = sort(transpose(predict_scores), 'descend');
%     scores = val(1:5,:)*100;
%     net = resnet50;
%     classnames = net.Layers(end).ClassNames;
%     for i = 1:batchSize
%         labels = classnames(indx(1:5,i));
%         disp(['Top 5 predictions on image, ', num2str(i)]);
%         for j=1:5
%             disp([labels{j},' ',num2str(scores(j,i), '%2.2f'),'%'])
%         end
%     end
%
%%
% For predictions on the first image, map the top five prediction scores to
% words in the |synset| dictionary.
%
%     fid = fopen('synsetWords.txt');
%     synsetOut = textscan(fid,'%s', 'delimiter', '\n');
%     synsetOut = synsetOut{1};
%     fclose(fid);
%     [val,indx] = sort(transpose(predict_scores), 'descend');
%     scores = val(1:5,1)*100;
%     top5labels = synsetOut(indx(1:5,1)); 
%
%%
% Display the top five classification labels on the image.
%
%     outputImage = zeros(224,400,3, 'uint8');
%     for k = 1:3
%         outputImage(:,177:end,k) = imBatch(:,:,k,1);
%     end
% 
%     scol = 1;
%     srow = 1;
%     outputImage = insertText(outputImage, [scol, srow], 'Classification with ResNet-50', 'TextColor', 'w','FontSize',20, 'BoxColor', 'black');
%     srow = srow + 30;
%     for k = 1:5
%         outputImage = insertText(outputImage, [scol, srow], [top5labels{k},' ',num2str(scores(k), '%2.2f'),'%'], 'TextColor', 'w','FontSize',15, 'BoxColor', 'black');
%         srow = srow + 25;
%     end
%
%     imshow(outputImage);
%%
% 
% <<../peppers_mkldnnex_output_c.png>>
%


%%
% Clear the persistent network object from memory.
clear mex;
%% Define the |resnet_predict_exe| Entry-Point Function
% 
% To generate an executable from MATLAB code, define a new entry-point
% function |resnet_predict_exe|. This function is similar to the previous
% entry-point function |resent_predict| but, in addition, includes code for preprocessing and
% postprocessing. The API that |resnet_predict_exe| uses is platform
% independent. This function accepts a video and the batch size as input 
% arguments. These arguments are compile-time constants.
%
type resnet_predict_exe

%% Structure of the |resnet_predict_exe| Function
%
% The function |resnet_predict_exe| contains four subsections that perform these actions: 
%
% * Read the classification labels from supplied input text file
% * Read the input batch of images and resize them as needed by the network
% * Run inference on input image batch
% * Overlay the results on the images
%
% For more information each of these steps, see the subsequent sections. 
%
%% The |readImageClassLabels| Function
%
% This function accepts the |synsetWords.txt| file as an input argument. It reads the
% classification labels and populates a cell array.
%
%
%         function synsetOut = readImageClassLabels(classLabelsFile)
%         % Read the classification label names from the file 
%         %
%         % Inputs : 
%         % classLabelsFile - supplied by user
%         %
%         % Outputs : 
%         % synsetOut       - cell array filled with 1000 image class labels
% 
%             synsetOut = cell(1000,1);
%             fid = fopen(classLabelsFile);
%             for i = 1:1000
%                 synsetOut{i} = fgetl(fid);
%             end
%             fclose(fid);
%         end
%
%% The |readImageInputBatch| Function
%
% This function reads and resizes the images from the video input file that
% is passed to the function as an input argument.
% It reads the specified input images and resizes them 
% to 224x224x3 which is the size the resnet50 network expects.
%
%         function reSizedImagesBatch = readImageInputBatch(videoReader,batchSize,i)
%         % Read and resize batch of frames as specified by input argument%
%         %
%         % Inputs : 
%         % videoReader - Object used for reading the images from video file
%         % batchSize   - Number of images in batch to process. Supplied by user
%         % i           - index to track frames read from video file
%         %
%         % Outputs : 
%         % reSizedImagesBatch - Batch of images resized to 224x224x3xbatchsize
% 
%             img = read(videoReader,[i (i+batchSize-1)]);
%             reSizedImagesBatch = coder.nullcopy(ones(224,224,3,batchSize,'like',img));
%             resizeTo  = coder.const([224,224]);
%             reSizedImagesBatch(:,:,:,:) = imresize(img,resizeTo);
%         end
%
%% The |mynet.predict| Function
%
% This function accepts the resized batch of images as input and returns the
% prediction results.
%
%        % run predict on resized input images %
%        predict_scores = mynet.predict(reSizedImagesBatch);
%
%% The |overlayResultsOnImages| Function
%
% This function accepts the prediction results and sorts them in descending order. 
% It overlays these results on the input images and displays them.
%
%         function overlayResultsOnImages(predict_scores,synsetOut,reSizedImagesBatch,batchSize,depVideoPlayer)
%         % Read and resize batch of frames as specified by input argument%
%         %
%         % Inputs : 
%         % predict_scores  - classification results for given network
%         % synsetOut       - cell array filled with 1000 image class labels
%         % reSizedImagesBatch - Batch of images resized to 224x224x3xbatchsize
%         % batchSize       - Number of images in batch to process. Supplied by user
%         % depVideoPlayer  - Object for displaying results
%         %
%         % Outputs : 
%         % Predicted results overlayed on input images
% 
%             % sort the predicted scores  %
%             [val,indx] = sort(transpose(predict_scores), 'descend');
% 
%             for j = 1:batchSize
%                 scores = val(1:5,j)*100;
%                 outputImage = zeros(224,400,3, 'uint8');
%                 for k = 1:3
%                     outputImage(:,177:end,k) = reSizedImagesBatch(:,:,k,j);
%                 end
% 
%                 % Overlay the results on image %
%                 scol = 1;
%                 srow = 1;
%                 outputImage = insertText(outputImage, [scol, srow], 'Classification with ResNet-50', 'TextColor', [255 255 255],'FontSize',20, 'BoxColor', [0 0 0]);
%                 srow = srow + 30;
%                 for k = 1:5
%                     scoreStr = sprintf('%2.2f',scores(k));
%                     outputImage = insertText(outputImage, [scol, srow], [synsetOut{indx(k,j)},' ',scoreStr,'%'], 'TextColor', [255 255 255],'FontSize',15, 'BoxColor', [0 0 0]);
%                     srow = srow + 25;
%                 end
% 
%                 depVideoPlayer(outputImage);
%             end
%         end
%
%% Build and Run Executable
%
% Create a code configuration object for generating an executable. Attach a deeplearning
% configuarion object to it. Set the |batchSize| and
% |inputVideoFile| variables. 
%
% If you do not intend to create a custom C++ main function and use the generated 
% example C++ main instead, set the |GenerateExampleMain| parameter to |'GenerateCodeAndCompile'|.
% Also, disable cfg.EnableOpenMP to make sure there
% are no openmp library dependencies when you run your executable from the desktop terminal. 
%
%         cfg = coder.config('exe');
%         cfg.TargetLang = 'C++';
%         cfg.DeepLearningConfig = coder.DeepLearningConfig('mkldnn');
%         batchSize = 5;
%         inputVideoFile = 'object_class.avi';
%         cfg.GenerateExampleMain = 'GenerateCodeAndCompile';
%         cfg.EnableOpenMP = 0;
%
% Run the |codegen| command to build the executable. Run the generated
% executable resnet_predict_exe either at the MATLAB command line or 
% at the desktop terminal.
%
%         codegen -config cfg resnet_predict_exe -args {coder.Constant(inputVideoFile), coder.Constant(batchSize)} -report 
%         system('./resnet_predict_exe')


%%
% 
% <<../peppers_mkldnnex_output_c.png>>
%
