/*
MIT License

Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/* This file is generated by nnir2openvx.py on 2018-11-12T08:03:54.930527-08:00 */

#include "caffeModels.h"
#include <vx_ext_amd.h>
#include <vx_amd_nn.h>
#include <iostream>
#include <sstream>
#include <vector>
#include <stdio.h>
#include <string.h>
#include <string>
#include <inttypes.h>
#include <chrono>
#include <unistd.h>
#include <math.h>
#include <immintrin.h>
#include<fstream>

#if ENABLE_OPENCV
#include <opencv2/opencv.hpp>
#include <opencv/cv.h>
#include <opencv/highgui.h>
#define CVUI_IMPLEMENTATION
#include "cvui.h"
using namespace cv;
#endif

#define MIVisionX_LEGEND "MIVisionX Image Classification"

unsigned char colors[20][3]={
                                {0,255,0},
                                {0, 0,255},
                                {255, 0,0},
                                { 250, 150, 70},
                                {102,102,156},
                                {190,153,153},
                                { 0,  0,   0},
                                {250,170, 30},
                                {220,220,  0},
                                {0, 255, 0},
                                {152,251,152},
                                { 135,206,250},
                                {220, 20, 60},
                                {255,  0,  0},
                                {  0,  0,255},
                                {  0,  0, 70},
                                {  0, 60,100},
                                {  0, 80,100},
                                {  0,  0,230},
                                {119, 11, 32}
                            };

std::string classificationModels[20] = {
    "InceptionV4",
    "Resnet50",
    "VGG16",
    "GoogleNet",
    "Resnet101",
    "Resnet152",
    "VGG19",
    "Unclassified",
    "Unclassified",
    "Unclassified",
    "Unclassified",
    "Unclassified",
    "Unclassified",
    "Unclassified",
    "Unclassified",
    "Unclassified",
    "Unclassified",
    "Unclassified",
    "Unclassified",
    "Unclassified"
};

// probability track bar
const int threshold_slider_max = 100;
int threshold_slider;
double thresholdValue = 0.5;
void threshold_on_trackbar( int, void* ){
    thresholdValue = (double) threshold_slider/threshold_slider_max ;
    return;
}

bool runInception = false, runResnet50 = false, runVgg16 = false, runGooglenet = false, runResnet101 = false, runResnet152 = false, runVgg19 = false;
float inceptionV4Time_g, resnet50Time_g, vgg16Time_g, googlenetTime_g, resnet101Time_g, resnet152Time_g, vgg19Time_g;

void createLegendImage()
{
    // create display legend image
    int fontFace = CV_FONT_HERSHEY_DUPLEX;
    double fontScale = 0.75;
    int thickness = 1.3;
    cv::Size legendGeometry = cv::Size(625, (10 * 40) + 40);
    Mat legend = Mat::zeros(legendGeometry,CV_8UC3);
    Rect roi = Rect(0,0,625,(10 * 40) + 40);
    legend(roi).setTo(cv::Scalar(128,128,128));
    int l = 0, model = 0;
    int red, green, blue;
    std::string className;
    std::string bufferName;
    char buffer [50];

    // add headers
    bufferName = "MIVisionX Image Classification";
    putText(legend, bufferName, Point(20, (l * 40) + 30), fontFace, 1.2, cv::Scalar(66,13,9), thickness,5);
    l++;
    bufferName = "Model";
    putText(legend, bufferName, Point(100, (l * 40) + 30), fontFace, 1, Scalar::all(0), thickness,5);
    bufferName = "ms/frame";
    putText(legend, bufferName, Point(300, (l * 40) + 30), fontFace, 1, Scalar::all(0), thickness,5);
    bufferName = "Color";
    putText(legend, bufferName, Point(525, (l * 40) + 30), fontFace, 1, Scalar::all(0), thickness,5);
    l++;
    
    // add legend items
    thickness = 1;    
    red = (colors[model][2]); green = (colors[model][1]) ; blue = (colors[model][0]) ;
    className = classificationModels[model];
    sprintf (buffer, " %.2f ", inceptionV4Time_g );
    cvui::checkbox(legend, 30, (l * 40) + 15,"", &runInception);
    putText(legend, className, Point(80, (l * 40) + 30), fontFace, fontScale, Scalar::all(0), thickness,3);
    putText(legend, buffer, Point(320, (l * 40) + 30), fontFace, fontScale, Scalar::all(0), thickness,3);
    rectangle(legend, Point(550, (l * 40)) , Point(575, (l * 40) + 40), Scalar(red,green,blue),-1);
    l++; model++;
    red = (colors[model][2]); green = (colors[model][1]) ; blue = (colors[model][0]) ;
    className = classificationModels[model];
    sprintf (buffer, " %.2f ", resnet50Time_g );
    cvui::checkbox(legend, 30, (l * 40) + 15,"", &runResnet50);
    putText(legend, className, Point(80, (l * 40) + 30), fontFace, fontScale, Scalar::all(0), thickness,3);
    putText(legend, buffer, Point(320, (l * 40) + 30), fontFace, fontScale, Scalar::all(0), thickness,3);
    rectangle(legend, Point(550, (l * 40)) , Point(575, (l * 40) + 40), Scalar(red,green,blue),-1);
    l++; model++;
    red = (colors[model][2]); green = (colors[model][1]) ; blue = (colors[model][0]) ;
    className = classificationModels[model];
    sprintf (buffer, " %.2f ", vgg16Time_g );
    cvui::checkbox(legend, 30, (l * 40) + 15,"", &runVgg16);
    putText(legend, className, Point(80, (l * 40) + 30), fontFace, fontScale, Scalar::all(0), thickness,3);
    putText(legend, buffer, Point(320, (l * 40) + 30), fontFace, fontScale, Scalar::all(0), thickness,3);
    rectangle(legend, Point(550, (l * 40)) , Point(575, (l * 40) + 40), Scalar(red,green,blue),-1);
    l++; model++;
    red = (colors[model][2]); green = (colors[model][1]) ; blue = (colors[model][0]) ;
    className = classificationModels[model];
    sprintf (buffer, " %.2f ", googlenetTime_g );
    cvui::checkbox(legend, 30, (l * 40) + 15,"", &runGooglenet);
    putText(legend, className, Point(80, (l * 40) + 30), fontFace, fontScale, Scalar::all(0), thickness,3);
    putText(legend, buffer, Point(320, (l * 40) + 30), fontFace, fontScale, Scalar::all(0), thickness,3);
    rectangle(legend, Point(550, (l * 40)) , Point(575, (l * 40) + 40), Scalar(red,green,blue),-1);
    l++; model++;
    red = (colors[model][2]); green = (colors[model][1]) ; blue = (colors[model][0]) ;
    className = classificationModels[model];
    sprintf (buffer, " %.2f ", resnet101Time_g );
    cvui::checkbox(legend, 30, (l * 40) + 15,"", &runResnet101);
    putText(legend, className, Point(80, (l * 40) + 30), fontFace, fontScale, Scalar::all(0), thickness,3);
    putText(legend, buffer, Point(320, (l * 40) + 30), fontFace, fontScale, Scalar::all(0), thickness,3);
    rectangle(legend, Point(550, (l * 40)) , Point(575, (l * 40) + 40), Scalar(red,green,blue),-1);
    l++; model++;
    red = (colors[model][2]); green = (colors[model][1]) ; blue = (colors[model][0]) ;
    className = classificationModels[model];
    sprintf (buffer, " %.2f ", resnet152Time_g );
    cvui::checkbox(legend, 30, (l * 40) + 15,"", &runResnet152);
    putText(legend, className, Point(80, (l * 40) + 30), fontFace, fontScale, Scalar::all(0), thickness,3);
    putText(legend, buffer, Point(320, (l * 40) + 30), fontFace, fontScale, Scalar::all(0), thickness,3);
    rectangle(legend, Point(550, (l * 40)) , Point(575, (l * 40) + 40), Scalar(red,green,blue),-1);
    l++; model++;
    red = (colors[model][2]); green = (colors[model][1]) ; blue = (colors[model][0]) ;
    className = classificationModels[model];
    sprintf (buffer, " %.2f ", vgg19Time_g );
    cvui::checkbox(legend, 30, (l * 40) + 15,"", &runVgg19);
    putText(legend, className, Point(80, (l * 40) + 30), fontFace, fontScale, Scalar::all(0), thickness,3);
    putText(legend, buffer, Point(320, (l * 40) + 30), fontFace, fontScale, Scalar::all(0), thickness,3);
    rectangle(legend, Point(550, (l * 40)) , Point(575, (l * 40) + 40), Scalar(red,green,blue),-1);
    l++; model++;

    cvui::trackbar(legend, 100, (l * 40)+10, 450, &threshold_slider, 0, 100);

    cvui::update();
    cv::imshow(MIVisionX_LEGEND, legend);

    thresholdValue = (double) threshold_slider/threshold_slider_max ;
}

#define ERROR_CHECK_OBJECT(obj) { vx_status status = vxGetStatus((vx_reference)(obj)); if(status != VX_SUCCESS) { vxAddLogEntry((vx_reference)context, status     , "ERROR: failed with status = (%d) at " __FILE__ "#%d\n", status, __LINE__); return status; } }
#define ERROR_CHECK_STATUS(call) { vx_status status = (call); if(status != VX_SUCCESS) { printf("ERROR: failed with status = (%d) at " __FILE__ "#%d\n", status, __LINE__); return -1; } }

static void VX_CALLBACK log_callback(vx_context context, vx_reference ref, vx_status status, const vx_char string[])
{
    size_t len = strlen(string);
    if (len > 0) {
        printf("%s", string);
        if (string[len - 1] != '\n')
            printf("\n");
        fflush(stdout);
    }
}

inline int64_t clockCounter()
{
    return std::chrono::high_resolution_clock::now().time_since_epoch().count();
}

inline int64_t clockFrequency()
{
    return std::chrono::high_resolution_clock::period::den / std::chrono::high_resolution_clock::period::num;
}

static void show_usage()
{ 
    printf(
            "\n"
            "Usage: ./classifier \n"
            "--inception  <inceptionV4-weights.bin>  [optional]\n"
            "--resnet50   <resnet50-weights.bin> [optional]\n"
            "--vgg16      <vgg16-weights.bin> [optional]\n"
            "--googlenet  <googlenet-weights.bin> [optional]\n" 
            "--resnet101  <resnet101-weights.bin> [optional]\n"
            "--resnet152  <resnet152-weights.bin> [optional]\n"
            "--vgg19      <vgg19-weights.bin> [optional]\n"
            "--label      <label text> [required] \n"
            "--video <video file>/--capture <0>[required]  \n"
            "\n"
        );
}


int main(int argc, const char ** argv)
{
    // check command-line usage   
    std::string binaryFilename_inception_str = "empty";
    std::string binaryFilename_resnet_str = "empty";
    std::string binaryFilename_vgg_str = "empty";
    std::string binaryFilename_googlenet_str = "empty";
    std::string binaryFilename_resnet101_str = "empty";
    std::string binaryFilename_resnet152_str = "empty";
    std::string binaryFilename_vgg19_str = "empty";

    std::string videoFile = "empty";
    std::string labelFileName = "empty";
    std::string labelText[1000];
    int captureID = -1;
    bool captureFromVideo = false;
    int status = 0;
    int parameter = 0;

    for(int arg = 1; arg < argc; arg++)
    {
        if (!strcasecmp(argv[arg], "--help") || !strcasecmp(argv[arg], "--H") || !strcasecmp(argv[arg], "--h"))
        {
            show_usage();
            exit(status);
        }

        else if (!strcasecmp(argv[arg], "--inception"))
        {
            if ((arg + 1) == argc)
            {
                printf("\n\nERROR: missing inception ONNX .model file location on command-line (see help for details)\n\n\n");
                show_usage();
                status = -1;
                exit(status);
            }
            arg++;
            binaryFilename_inception_str = (argv[arg]);
            parameter++;
        }

        else if (!strcasecmp(argv[arg], "--resnet50"))
        {
            if ((arg + 1) == argc)
            {
                printf("\n\nERROR: missing resnet50 ONNX .model file location on command-line (see help for details)\n\n\n");
                show_usage();
                status = -1;
                exit(status);
            }
            arg++;
            binaryFilename_resnet_str = (argv[arg]);
            parameter++;
        }

        else if (!strcasecmp(argv[arg], "--vgg16"))
        {
            if ((arg + 1) == argc)
            {
                printf("\n\nERROR: missing vgg16 ONNX .model file location on command-line (see help for details)\n\n\n");
                show_usage();
                status = -1;
                exit(status);
            }
            arg++;
            binaryFilename_vgg_str = (argv[arg]);
            parameter++;
        }

        else if (!strcasecmp(argv[arg], "--googlenet"))
        {
            if ((arg + 1) == argc)
            {
                printf("\n\nERROR: missing googlenet ONNX .model file location on command-line (see help for details)\n\n\n");
                show_usage();
                status = -1;
                exit(status);
            }
            arg++;
            binaryFilename_googlenet_str = (argv[arg]);
            parameter++;
        }

        else if (!strcasecmp(argv[arg], "--resnet101"))
        {
            if ((arg + 1) == argc)
            {
                printf("\n\nERROR: missing resnet101 ONNX .model file location on command-line (see help for details)\n\n\n");
                show_usage();
                status = -1;
                exit(status);
            }
            arg++;
            binaryFilename_resnet101_str = (argv[arg]);
            parameter++;
        }

        else if (!strcasecmp(argv[arg], "--resnet152"))
        {
            if ((arg + 1) == argc)
            {
                printf("\n\nERROR: missing resnet152 ONNX .model file location on command-line (see help for details)\n\n\n");
                show_usage();
                status = -1;
                exit(status);
            }
            arg++;
            binaryFilename_resnet152_str = (argv[arg]);
            parameter++;
        }

        else if (!strcasecmp(argv[arg], "--vgg19"))
        {
            if ((arg + 1) == argc)
            {
                printf("\n\nERROR: missing vgg19 ONNX .model file location on command-line (see help for details)\n\n\n");
                show_usage();
                status = -1;
                exit(status);
            }
            arg++;
            binaryFilename_vgg19_str = (argv[arg]);
            parameter++;
        }

        else if (!strcasecmp(argv[arg], "--label") || !strcasecmp(argv[arg], "--L") || !strcasecmp(argv[arg], "--l"))
        {
            if ((arg + 1) == argc)
            {
                printf("\n\nERROR: missing label.txt file on command-line (see help for details)\n\n\n");
                show_usage();
                status = -1;
                exit(status);
            }
            arg++;
            labelFileName = (argv[arg]);
            std::string line;
	        std::ifstream out(labelFileName);
	        int lineNum = 0;
	        while(getline(out, line)) {
	            labelText[lineNum] = line;
	            lineNum++;
	        }
	        out.close();
	        parameter++;
        }

        else if (!strcasecmp(argv[arg], "--video") || !strcasecmp(argv[arg], "--V") || !strcasecmp(argv[arg], "--v"))
        {
            if ((arg + 1) == argc)
            {
                printf("\n\nERROR: missing video file on command-line (see help for details)\n\n\n");
                show_usage();
                status = -1;
                exit(status);
            }
            arg++;
            videoFile = (argv[arg]);
        captureFromVideo = true;
            parameter++;
        }

        else if (!strcasecmp(argv[arg], "--capture") || !strcasecmp(argv[arg], "--C") || !strcasecmp(argv[arg], "--c"))
        {
            if ((arg + 1) == argc)
            {
                printf("\n\nERROR: missing camera source on command-line (see help for details)\n\n\n");
                show_usage();
                status = -1;
                exit(status);
            }
            arg++;
            captureID = atoi(argv[arg]);
            parameter++;
        }
    }


    if (parameter < 3)
    {
        printf("\nERROR: missing parameters in command-line.\n");
        show_usage();
        status = -1;
        exit(status);
    }

    // create context, input, output, and graph
    vxRegisterLogCallback(NULL, log_callback, vx_false_e);
    vx_context context = vxCreateContext();
    status = vxGetStatus((vx_reference)context);
    if(status) {
        printf("ERROR: vxCreateContext() failed\n");
        return -1;
    }
    vxRegisterLogCallback(context, log_callback, vx_false_e);

    // load vx_nn kernels
    ERROR_CHECK_STATUS(vxLoadKernels(context, "vx_nn"));

    // creation of graphs
    vx_graph graph_inception = vxCreateGraph(context);
    status = vxGetStatus((vx_reference)graph_inception);
    if(status) {
        printf("ERROR: vxCreateGraph(...) failed (%d)\n", status);
        return -1;
    }
    vx_graph graph_resnet = vxCreateGraph(context);
    status = vxGetStatus((vx_reference)graph_resnet);
    if(status) {
        printf("ERROR: vxCreateGraph(...) failed (%d)\n", status);
        return -1;
    }
    vx_graph graph_vgg = vxCreateGraph(context);
    status = vxGetStatus((vx_reference)graph_vgg);
    if(status) {
        printf("ERROR: vxCreateGraph(...) failed (%d)\n", status);
        return -1;
    }
    vx_graph graph_googlenet = vxCreateGraph(context);
    status = vxGetStatus((vx_reference)graph_googlenet);
    if(status) {
        printf("ERROR: vxCreateGraph(...) failed (%d)\n", status);
        return -1;
    }
    vx_graph graph_resnet101 = vxCreateGraph(context);
    status = vxGetStatus((vx_reference)graph_resnet101);
    if(status) {
        printf("ERROR: vxCreateGraph(...) failed (%d)\n", status);
        return -1;
    }
    vx_graph graph_resnet152 = vxCreateGraph(context);
    status = vxGetStatus((vx_reference)graph_resnet152);
    if(status) {
        printf("ERROR: vxCreateGraph(...) failed (%d)\n", status);
        return -1;
    }
    vx_graph graph_vgg19 = vxCreateGraph(context);
    status = vxGetStatus((vx_reference)graph_vgg19);
    if(status) {
        printf("ERROR: vxCreateGraph(...) failed (%d)\n", status);
        return -1;
    }

    // create and initialize input tensor data
    vx_size dims_data_299x299[4] = { 299, 299, 3, 1 };
    vx_size dims_data_224x224[4] = { 224, 224, 3, 1 };

    // create data for different sizes
    vx_tensor data_299x299 = vxCreateTensor(context, 4, dims_data_299x299, VX_TYPE_FLOAT32, 0);
    if(vxGetStatus((vx_reference)data_299x299)) {
        printf("ERROR: vxCreateTensor() failed for data\n");
        return -1;
    }
    vx_tensor data_224x224 = vxCreateTensor(context, 4, dims_data_224x224, VX_TYPE_FLOAT32, 0);
    if(vxGetStatus((vx_reference)data_224x224)) {
        printf("ERROR: vxCreateTensor() failed for data\n");
        return -1;
    }

    // create output tensor prob
    vx_size dims_prob[4] = { 1, 1, 1000, 1 };
    vx_tensor prob_inception = vxCreateTensor(context, 4, dims_prob, VX_TYPE_FLOAT32, 0);
    if(vxGetStatus((vx_reference)prob_inception)) {
        printf("ERROR: vxCreateTensor() failed for prob\n");
        return -1;
    }
    vx_tensor prob_resnet = vxCreateTensor(context, 4, dims_prob, VX_TYPE_FLOAT32, 0);
    if(vxGetStatus((vx_reference)prob_resnet)) {
        printf("ERROR: vxCreateTensor() failed for prob\n");
        return -1;
    }
    vx_tensor prob_vgg = vxCreateTensor(context, 4, dims_prob, VX_TYPE_FLOAT32, 0);
    if(vxGetStatus((vx_reference)prob_vgg)) {
        printf("ERROR: vxCreateTensor() failed for prob\n");
        return -1;
    }
    vx_tensor prob_googlenet = vxCreateTensor(context, 4, dims_prob, VX_TYPE_FLOAT32, 0);
    if(vxGetStatus((vx_reference)prob_googlenet)) {
        printf("ERROR: vxCreateTensor() failed for prob\n");
        return -1;
    }
    vx_tensor prob_resnet101 = vxCreateTensor(context, 4, dims_prob, VX_TYPE_FLOAT32, 0);
    if(vxGetStatus((vx_reference)prob_resnet101)) {
        printf("ERROR: vxCreateTensor() failed for prob\n");
        return -1;
    }
    vx_tensor prob_resnet152 = vxCreateTensor(context, 4, dims_prob, VX_TYPE_FLOAT32, 0);
    if(vxGetStatus((vx_reference)prob_resnet152)) {
        printf("ERROR: vxCreateTensor() failed for prob\n");
        return -1;
    }
    vx_tensor prob_vgg19 = vxCreateTensor(context, 4, dims_prob, VX_TYPE_FLOAT32, 0);
    if(vxGetStatus((vx_reference)prob_vgg19)) {
        printf("ERROR: vxCreateTensor() failed for prob\n");
        return -1;
    }

    // build graph using annmodule
    int64_t freq = clockFrequency(), t0, t1;
    char binaryFilename_inception[1024], binaryFilename_resnet[1024], binaryFilename_resnet101[1024], binaryFilename_vgg[1024], binaryFilename_googlenet[1024], binaryFilename_resnet152[1024], binaryFilename_vgg19[1024];
    strcpy(binaryFilename_inception, binaryFilename_inception_str.c_str());
    strcpy(binaryFilename_resnet, binaryFilename_resnet_str.c_str());
    strcpy(binaryFilename_vgg, binaryFilename_vgg_str.c_str());
    strcpy(binaryFilename_googlenet, binaryFilename_googlenet_str.c_str());
    strcpy(binaryFilename_resnet101, binaryFilename_resnet101_str.c_str());
    strcpy(binaryFilename_resnet152, binaryFilename_resnet152_str.c_str());
    strcpy(binaryFilename_vgg19, binaryFilename_vgg19_str.c_str());
    t0 = clockCounter();

    if(binaryFilename_inception_str != "empty"){
    	status = annAddToGraph_inception(graph_inception, data_299x299, prob_inception, binaryFilename_inception);
	    if(status) {
	        printf("ERROR: inception annAddToGraph() failed (%d)\n", status);
	        return -1;
	    }
	    status = vxVerifyGraph(graph_inception);
	    if(status) {
	        printf("ERROR: inception vxVerifyGraph(...) failed (%d)\n", status);
	        return -1;
    	}
        runInception = true;
    }
   
    if(binaryFilename_resnet_str != "empty"){
    	status = annAddToGraph_resnet(graph_resnet, data_224x224, prob_resnet, binaryFilename_resnet);
	    if(status) {
	        printf("ERROR: resnet annAddToGraph() failed (%d)\n", status);
	        return -1;
	    }
	    status = vxVerifyGraph(graph_resnet);
	    if(status) {
	        printf("ERROR: resnet vxVerifyGraph(...) failed (%d)\n", status);
	        return -1;
	    }
        runResnet50 = true;
    }
    
    if(binaryFilename_vgg_str != "empty"){
    	status = annAddToGraph_vgg(graph_vgg, data_224x224, prob_vgg, binaryFilename_vgg);
	    if(status) {
	        printf("ERROR: vgg annAddToGraph() failed (%d)\n", status);
	        return -1;
	    }
	    status = vxVerifyGraph(graph_vgg);
	    if(status) {
	        printf("ERROR: vgg vxVerifyGraph(...) failed (%d)\n", status);
	        return -1;
	    }
        runVgg16 = true;
    }
    
    if(binaryFilename_googlenet_str != "empty"){
    	status = annAddToGraph_googleNet(graph_googlenet, data_224x224, prob_googlenet, binaryFilename_googlenet);
	    if(status) {
	        printf("ERROR: googlenet annAddToGraph() failed (%d)\n", status);
	        return -1;
	    }
	    status = vxVerifyGraph(graph_googlenet);
	    if(status) {
	        printf("ERROR: googlenet vxVerifyGraph(...) failed (%d)\n", status);
	        return -1;
	    }
        runGooglenet = true;
    }
    

    if(binaryFilename_resnet101_str != "empty"){
    	status = annAddToGraph_resnet101(graph_resnet101, data_224x224, prob_resnet101, binaryFilename_resnet101);
	    if(status) {
	        printf("ERROR: resnet101 annAddToGraph() failed (%d)\n", status);
	        return -1;
	    }
	    status = vxVerifyGraph(graph_resnet101);
	    if(status) {
	        printf("ERROR: resnet101 vxVerifyGraph(...) failed (%d)\n", status);
	        return -1;
	    }
        runResnet101 = true;
    }
   
    if(binaryFilename_resnet152_str != "empty"){
    	status = annAddToGraph_resnet152(graph_resnet152, data_224x224, prob_resnet152, binaryFilename_resnet152);
	    if(status) {
	        printf("ERROR: resnet152 annAddToGraph() failed (%d)\n", status);
	        return -1;
	    }
	    status = vxVerifyGraph(graph_resnet152);
	    if(status) {
	        printf("ERROR: resnet`52 vxVerifyGraph(...) failed (%d)\n", status);
	        return -1;
	    }
        runResnet152 = true;
    }
    
    if(binaryFilename_vgg19_str != "empty"){
    	status = annAddToGraph_vgg19(graph_vgg19, data_224x224, prob_vgg19, binaryFilename_vgg19);
	    if(status) {
	        printf("ERROR: vgg19 annAddToGraph() failed (%d)\n", status);
	        return -1;
	    }
	    status = vxVerifyGraph(graph_vgg);
	    if(status) {
	        printf("ERROR: vgg19 vxVerifyGraph(...) failed (%d)\n", status);
	        return -1;
	    }
        runVgg19 = true;
    }
    

    t1 = clockCounter();
    printf("OK: graph initialization with annAddToGraph() took %.3f msec\n", (float)(t1-t0)*1000.0f/(float)freq);

    t0 = clockCounter();
    status = vxProcessGraph(graph_inception);
    if(status != VX_SUCCESS) {
        printf("ERROR: vxProcessGraph() failed (%d)\n", status);
        return -1;
    }
    status = vxProcessGraph(graph_resnet);
    if(status != VX_SUCCESS) {
        printf("ERROR: vxProcessGraph() failed (%d)\n", status);
        return -1;
    }
    status = vxProcessGraph(graph_vgg);
    if(status != VX_SUCCESS) {
        printf("ERROR: vxProcessGraph() failed (%d)\n", status);
        return -1;
    }
    status = vxProcessGraph(graph_googlenet);
    if(status != VX_SUCCESS) {
        printf("ERROR: vxProcessGraph() failed (%d)\n", status);
        return -1;
    }
    status = vxProcessGraph(graph_resnet101);
    if(status != VX_SUCCESS) {
        printf("ERROR: vxProcessGraph() failed (%d)\n", status);
        return -1;
    }
    status = vxProcessGraph(graph_resnet152);
    if(status != VX_SUCCESS) {
        printf("ERROR: vxProcessGraph() failed (%d)\n", status);
        return -1;
    }
    status = vxProcessGraph(graph_vgg19);
    if(status != VX_SUCCESS) {
        printf("ERROR: vxProcessGraph() failed (%d)\n", status);
        return -1;
    }
    t1 = clockCounter();
    printf("OK: vxProcessGraph() took %.3f msec (1st iteration)\n", (float)(t1-t0)*1000.0f/(float)freq);

    int N = 100;
    float inceptionV4Time, resnet50Time, vgg16Time, googlenetTime, resnet101Time, resnet152Time, vgg19Time;

    if(binaryFilename_inception_str != "empty"){
        t0 = clockCounter();
        for(int i = 0; i < N; i++) {
            status = vxProcessGraph(graph_inception);
            if(status != VX_SUCCESS)
                break;
        }
        t1 = clockCounter();
        inceptionV4Time = (float)(t1-t0)*1000.0f/(float)freq/(float)N;
        printf("OK: inceptionV4 took %.3f msec (average over %d iterations)\n", (float)(t1-t0)*1000.0f/(float)freq/(float)N, N);
    }

    if(binaryFilename_resnet_str != "empty"){
        t0 = clockCounter();
        for(int i = 0; i < N; i++) {
            status = vxProcessGraph(graph_resnet);
            if(status != VX_SUCCESS)
                break;
        }
        t1 = clockCounter();
        resnet50Time = (float)(t1-t0)*1000.0f/(float)freq/(float)N;
        printf("OK: resnet50 took %.3f msec (average over %d iterations)\n", (float)(t1-t0)*1000.0f/(float)freq/(float)N, N);
    }
    if(binaryFilename_vgg_str != "empty"){
        t0 = clockCounter();
        for(int i = 0; i < N; i++) {
            status = vxProcessGraph(graph_vgg);
            if(status != VX_SUCCESS)
                break;
        }
        t1 = clockCounter();
        vgg16Time = (float)(t1-t0)*1000.0f/(float)freq/(float)N;
        printf("OK: vgg16 took %.3f msec (average over %d iterations)\n", (float)(t1-t0)*1000.0f/(float)freq/(float)N, N);
    }
    if(binaryFilename_googlenet_str != "empty"){
        t0 = clockCounter();
        for(int i = 0; i < N; i++) {
            status = vxProcessGraph(graph_googlenet);
            if(status != VX_SUCCESS)
                break;
        }
        t1 = clockCounter();
        googlenetTime = (float)(t1-t0)*1000.0f/(float)freq/(float)N;
        printf("OK: googlenet took %.3f msec (average over %d iterations)\n", (float)(t1-t0)*1000.0f/(float)freq/(float)N, N);
    }
    if(binaryFilename_resnet101_str != "empty"){
        t0 = clockCounter();
        for(int i = 0; i < N; i++) {
            status = vxProcessGraph(graph_resnet101);
            if(status != VX_SUCCESS)
                break;
        }
        t1 = clockCounter();
        resnet101Time = (float)(t1-t0)*1000.0f/(float)freq/(float)N;
        printf("OK: resnet101 took %.3f msec (average over %d iterations)\n", (float)(t1-t0)*1000.0f/(float)freq/(float)N, N);
    }
    if(binaryFilename_resnet152_str != "empty"){
        t0 = clockCounter();
        for(int i = 0; i < N; i++) {
            status = vxProcessGraph(graph_resnet152);
            if(status != VX_SUCCESS)
                break;
        }
        t1 = clockCounter();
        resnet152Time = (float)(t1-t0)*1000.0f/(float)freq/(float)N;
        printf("OK: resnet152 took %.3f msec (average over %d iterations)\n", (float)(t1-t0)*1000.0f/(float)freq/(float)N, N);
    }
    if(binaryFilename_vgg19_str != "empty"){
        t0 = clockCounter();
        for(int i = 0; i < N; i++) {
            status = vxProcessGraph(graph_vgg19);
            if(status != VX_SUCCESS)
                break;
        }
        t1 = clockCounter();
        vgg19Time = (float)(t1-t0)*1000.0f/(float)freq/(float)N;
        printf("OK: vgg19 took %.3f msec (average over %d iterations)\n", (float)(t1-t0)*1000.0f/(float)freq/(float)N, N);
    }
    /***** OPENCV Additions *****/

    // create display windows
    cv::namedWindow(MIVisionX_LEGEND);
    cvui::init(MIVisionX_LEGEND);
    cv::namedWindow("MIVisionX Image Classification - LIVE", cv::WINDOW_GUI_EXPANDED);

    //create a probability track bar
    threshold_slider = 50;
    //cv::createTrackbar("Probability Threshold", MIVisionX_LEGEND, &threshold_slider, threshold_slider_max, threshold_on_trackbar);

    // create display legend image
    inceptionV4Time_g = inceptionV4Time; resnet50Time_g = resnet50Time;
    vgg16Time_g = vgg16Time; resnet101Time_g =  resnet101Time;
    googlenetTime_g = googlenetTime; resnet152Time_g = resnet152Time; vgg19Time_g = vgg19Time;
    createLegendImage();

    // define variables for run
    cv::Mat frame;
    int total_size = 1000;
    int outputImgWidth = 1080, outputImgHeight = 720;
    float threshold = 0.01;
    cv::Size output_geometry = cv::Size(outputImgWidth, outputImgHeight);
    Mat inputDisplay, outputDisplay;  

    cv::Mat inputFrame_299x299, inputFrame_224x224;
    int fontFace = CV_FONT_HERSHEY_DUPLEX;
    double fontScale = 1;
    int thickness = 1.5;
    float *outputBuffer[7];
    for(int models = 0; models < 7; models++){
        outputBuffer[models] = new float[total_size];
    }

    int loopSeg = 1;

    while(argc && loopSeg)
    {
        VideoCapture cap;
        if (captureFromVideo) {
            cap.open(videoFile);
            if(!cap.isOpened()) {
                std::cout << "Unable to open the video: " << videoFile << std::endl;
                return 0;
            }
        }
        else {
            cap.open(captureID);
            if(!cap.isOpened()) {
                std::cout << "Unable to open the camera feed: " << captureID << std::endl;
                return 0;
            }
        }
        int frameCount = 0;
        float msFrame = 0, fpsAvg = 0, frameMsecs = 0;
        for(;;)
        {
            msFrame = 0;
            // capture image frame
            t0 = clockCounter();
            cap >> frame;
            if( frame.empty() ) break; // end of video stream
            t1 = clockCounter();
            msFrame += (float)(t1-t0)*1000.0f/(float)freq;
            //printf("\n\nLIVE: OpenCV Frame Capture Time -- %.3f msec\n", (float)(t1-t0)*1000.0f/(float)freq);

            // preprocess image frame
            t0 = clockCounter();
            if(runInception){
                cv::resize(frame, inputFrame_299x299, cv::Size(299,299));
            }
            cv::resize(frame, inputFrame_224x224, cv::Size(224,224));
            t1 = clockCounter();
            msFrame += (float)(t1-t0)*1000.0f/(float)freq;
            //printf("LIVE: OpenCV Frame Resize Time -- %.3f msec\n", (float)(t1-t0)*1000.0f/(float)freq);

            // Copy Image frame into the input tensor
            t0 = clockCounter();
            vx_enum usage = VX_WRITE_ONLY;
            vx_enum data_type = VX_TYPE_FLOAT32;
            vx_size num_of_dims = 4, dims[4] = { 1, 1, 1, 1 }, stride[4];
            vx_map_id map_id;
            float * ptr;
            vx_size count;
            // copy - 299x299
            if(runInception)
            {
                vxQueryTensor(data_299x299, VX_TENSOR_DATA_TYPE, &data_type, sizeof(data_type));
                vxQueryTensor(data_299x299, VX_TENSOR_NUMBER_OF_DIMS, &num_of_dims, sizeof(num_of_dims));
                vxQueryTensor(data_299x299, VX_TENSOR_DIMS, &dims, sizeof(dims[0])*num_of_dims);
                if(data_type != VX_TYPE_FLOAT32) {
                    std::cerr << "ERROR: copyTensor() supports only VX_TYPE_FLOAT32: invalid for " <<  std::endl;
                    return -1;
                }
                count = dims[0] * dims[1] * dims[2] * dims[3];
                vx_status status = vxMapTensorPatch(data_299x299, num_of_dims, nullptr, nullptr, &map_id, stride, (void **)&ptr, usage, VX_MEMORY_TYPE_HOST, 0);
                if(status) {
                    std::cerr << "ERROR: vxMapTensorPatch() failed for " <<  std::endl;
                    return -1;
                }
                Mat srcImg;
                for(size_t n = 0; n < dims[3]; n++) {
                    srcImg = inputFrame_299x299;
                    for(vx_size y = 0; y < dims[1]; y++) {
                        unsigned char * src = srcImg.data + y*dims[0]*3;
                        float * dstR = ptr + ((n * stride[3] + y * stride[1]) >> 2);
                        float * dstG = dstR + (stride[2] >> 2);
                        float * dstB = dstG + (stride[2] >> 2);
                        for(vx_size x = 0; x < dims[0]; x++, src += 3) {
                            *dstR++ = (src[2] * 0.007843137) - 1;
                            *dstG++ = (src[1] * 0.007843137) - 1;
                            *dstB++ = (src[0] * 0.007843137) - 1;
                        }
                    }
                }
                status = vxUnmapTensorPatch(data_299x299, map_id);
                if(status) {
                    std::cerr << "ERROR: vxUnmapTensorPatch() failed for " <<  std::endl;
                    return -1;
                }
            }
            // copy - 224x224
            if(runResnet50 || runVgg16 || runGooglenet || runResnet101 || runResnet152 || runVgg19)
            {
                vxQueryTensor(data_224x224, VX_TENSOR_DATA_TYPE, &data_type, sizeof(data_type));
                vxQueryTensor(data_224x224, VX_TENSOR_NUMBER_OF_DIMS, &num_of_dims, sizeof(num_of_dims));
                vxQueryTensor(data_224x224, VX_TENSOR_DIMS, &dims, sizeof(dims[0])*num_of_dims);
                if(data_type != VX_TYPE_FLOAT32) {
                    std::cerr << "ERROR: copyTensor() supports only VX_TYPE_FLOAT32: invalid for " <<  std::endl;
                    return -1;
                }
                count = dims[0] * dims[1] * dims[2] * dims[3];
                vx_status status = vxMapTensorPatch(data_224x224, num_of_dims, nullptr, nullptr, &map_id, stride, (void **)&ptr, usage, VX_MEMORY_TYPE_HOST, 0);
                if(status) {
                    std::cerr << "ERROR: vxMapTensorPatch() failed for " <<  std::endl;
                    return -1;
                }
                Mat srcImg;
                for(size_t n = 0; n < dims[3]; n++) {
                    srcImg = inputFrame_224x224;
                    for(vx_size y = 0; y < dims[1]; y++) {
                        unsigned char * src = srcImg.data + y*dims[0]*3;
                        float * dstR = ptr + ((n * stride[3] + y * stride[1]) >> 2);
                        float * dstG = dstR + (stride[2] >> 2);
                        float * dstB = dstG + (stride[2] >> 2);
                        for(vx_size x = 0; x < dims[0]; x++, src += 3) {
                            *dstR++ = src[2];
                            *dstG++ = src[1];
                            *dstB++ = src[0];
                        }
                    }
                }
                status = vxUnmapTensorPatch(data_224x224, map_id);
                if(status) {
                    std::cerr << "ERROR: vxUnmapTensorPatch() failed for " <<  std::endl;
                    return -1;
                }
            }
            t1 = clockCounter();
            msFrame += (float)(t1-t0)*1000.0f/(float)freq;
            //printf("LIVE: Convert Image to Tensor Time -- %.3f msec\n", (float)(t1-t0)*1000.0f/(float)freq);
   
            // process graph for the input           
            if(runInception)
            {
                t0 = clockCounter();
                status = vxProcessGraph(graph_inception);
                if(status != VX_SUCCESS) break;
                t1 = clockCounter();
                inceptionV4Time_g = (float)(t1-t0)*1000.0f/(float)freq;
                msFrame += (float)(t1-t0)*1000.0f/(float)freq;
                //printf("LIVE: Process InceptionV4 Classification Time -- %.3f msec\n", (float)(t1-t0)*1000.0f/(float)freq);
            }
            if(runResnet50)
            {
                t0 = clockCounter();
                status = vxProcessGraph(graph_resnet);
                if(status != VX_SUCCESS) break;
                t1 = clockCounter();
                resnet50Time_g = (float)(t1-t0)*1000.0f/(float)freq;
                msFrame += (float)(t1-t0)*1000.0f/(float)freq;
                //printf("LIVE: Process Resnet50 Classification Time -- %.3f msec\n", (float)(t1-t0)*1000.0f/(float)freq);
            }
            if(runVgg16)
            {
                t0 = clockCounter();
                status = vxProcessGraph(graph_vgg);
                if(status != VX_SUCCESS) break;
                t1 = clockCounter();
                vgg16Time_g = (float)(t1-t0)*1000.0f/(float)freq;
                msFrame += (float)(t1-t0)*1000.0f/(float)freq;
                //printf("LIVE: Process VGG16 Classification Time -- %.3f msec\n", (float)(t1-t0)*1000.0f/(float)freq);
            }
            if(runGooglenet)
            {
                t0 = clockCounter();
                status = vxProcessGraph(graph_googlenet);
                if(status != VX_SUCCESS) break;
                t1 = clockCounter();
                googlenetTime_g = (float)(t1-t0)*1000.0f/(float)freq;
                msFrame += (float)(t1-t0)*1000.0f/(float)freq;
                //printf("LIVE: Process VGG16 Classification Time -- %.3f msec\n", (float)(t1-t0)*1000.0f/(float)freq);
            }
            if(runResnet101)
            {
                t0 = clockCounter();
                status = vxProcessGraph(graph_resnet101);
                if(status != VX_SUCCESS) break;
                t1 = clockCounter();
                resnet101Time_g = (float)(t1-t0)*1000.0f/(float)freq;
                msFrame += (float)(t1-t0)*1000.0f/(float)freq;
                //printf("LIVE: Process Resnet101 Classification Time -- %.3f msec\n", (float)(t1-t0)*1000.0f/(float)freq);
            }
            if(runResnet152)
            {
                t0 = clockCounter();
                status = vxProcessGraph(graph_resnet152);
                if(status != VX_SUCCESS) break;
                t1 = clockCounter();
                resnet152Time_g = (float)(t1-t0)*1000.0f/(float)freq;
                msFrame += (float)(t1-t0)*1000.0f/(float)freq;
                //printf("LIVE: Process Resnet152 Classification Time -- %.3f msec\n", (float)(t1-t0)*1000.0f/(float)freq);
            }
            if(runVgg19)
            {
                t0 = clockCounter();
                status = vxProcessGraph(graph_vgg19);
                if(status != VX_SUCCESS) break;
                t1 = clockCounter();
                vgg19Time_g = (float)(t1-t0)*1000.0f/(float)freq;
                msFrame += (float)(t1-t0)*1000.0f/(float)freq;
                //printf("LIVE: Process VGG16 Classification Time -- %.3f msec\n", (float)(t1-t0)*1000.0f/(float)freq);
            }

            // copy output data into local buffer
            t0 = clockCounter();
            usage = VX_READ_ONLY;
            // inception copy
            if(runInception)
            {
                vxQueryTensor(prob_inception, VX_TENSOR_DATA_TYPE, &data_type, sizeof(data_type));
                vxQueryTensor(prob_inception, VX_TENSOR_NUMBER_OF_DIMS, &num_of_dims, sizeof(num_of_dims));
                vxQueryTensor(prob_inception, VX_TENSOR_DIMS, &dims, sizeof(dims[0])*num_of_dims);
                if(data_type != VX_TYPE_FLOAT32) {
                    std::cerr << "ERROR: copyTensor() supports only VX_TYPE_FLOAT32: invalid for "  << std::endl;
                    return -1;
                }
                count = dims[0] * dims[1] * dims[2] * dims[3];
                status = vxMapTensorPatch(prob_inception, num_of_dims, nullptr, nullptr, &map_id, stride, (void **)&ptr, usage, VX_MEMORY_TYPE_HOST, 0);
                if(status) {
                    std::cerr << "ERROR: vxMapTensorPatch() failed for "  << std::endl;
                    return -1;
                }
                memcpy(outputBuffer[0], ptr, (count*sizeof(float)));
                status = vxUnmapTensorPatch(prob_inception, map_id);
                if(status) {
                    std::cerr << "ERROR: vxUnmapTensorPatch() failed for "  << std::endl;
                    return -1;
                }
            }
            // resnet copy
            if(runResnet50)
            {
                vxQueryTensor(prob_resnet, VX_TENSOR_DATA_TYPE, &data_type, sizeof(data_type));
                vxQueryTensor(prob_resnet, VX_TENSOR_NUMBER_OF_DIMS, &num_of_dims, sizeof(num_of_dims));
                vxQueryTensor(prob_resnet, VX_TENSOR_DIMS, &dims, sizeof(dims[0])*num_of_dims);
                if(data_type != VX_TYPE_FLOAT32) {
                    std::cerr << "ERROR: copyTensor() supports only VX_TYPE_FLOAT32: invalid for "  << std::endl;
                    return -1;
                }
                count = dims[0] * dims[1] * dims[2] * dims[3];
                status = vxMapTensorPatch(prob_resnet, num_of_dims, nullptr, nullptr, &map_id, stride, (void **)&ptr, usage, VX_MEMORY_TYPE_HOST, 0);
                if(status) {
                    std::cerr << "ERROR: vxMapTensorPatch() failed for "  << std::endl;
                    return -1;
                }
                memcpy(outputBuffer[1], ptr, (count*sizeof(float)));
                status = vxUnmapTensorPatch(prob_resnet, map_id);
                if(status) {
                    std::cerr << "ERROR: vxUnmapTensorPatch() failed for "  << std::endl;
                    return -1;
                }
            }
            // vgg copy
            if(runVgg16)
            {
                vxQueryTensor(prob_vgg, VX_TENSOR_DATA_TYPE, &data_type, sizeof(data_type));
                vxQueryTensor(prob_vgg, VX_TENSOR_NUMBER_OF_DIMS, &num_of_dims, sizeof(num_of_dims));
                vxQueryTensor(prob_vgg, VX_TENSOR_DIMS, &dims, sizeof(dims[0])*num_of_dims);
                if(data_type != VX_TYPE_FLOAT32) {
                    std::cerr << "ERROR: copyTensor() supports only VX_TYPE_FLOAT32: invalid for "  << std::endl;
                    return -1;
                }
                count = dims[0] * dims[1] * dims[2] * dims[3];
                status = vxMapTensorPatch(prob_vgg, num_of_dims, nullptr, nullptr, &map_id, stride, (void **)&ptr, usage, VX_MEMORY_TYPE_HOST, 0);
                if(status) {
                    std::cerr << "ERROR: vxMapTensorPatch() failed for "  << std::endl;
                    return -1;
                }
                memcpy(outputBuffer[2], ptr, (count*sizeof(float)));
                status = vxUnmapTensorPatch(prob_vgg, map_id);
                if(status) {
                    std::cerr << "ERROR: vxUnmapTensorPatch() failed for "  << std::endl;
                    return -1;
                }
            }
            // googlenet copy
            if(runGooglenet)
            {
                vxQueryTensor(prob_googlenet, VX_TENSOR_DATA_TYPE, &data_type, sizeof(data_type));
                vxQueryTensor(prob_googlenet, VX_TENSOR_NUMBER_OF_DIMS, &num_of_dims, sizeof(num_of_dims));
                vxQueryTensor(prob_googlenet, VX_TENSOR_DIMS, &dims, sizeof(dims[0])*num_of_dims);
                if(data_type != VX_TYPE_FLOAT32) {
                    std::cerr << "ERROR: copyTensor() supports only VX_TYPE_FLOAT32: invalid for "  << std::endl;
                    return -1;
                }
                count = dims[0] * dims[1] * dims[2] * dims[3];
                status = vxMapTensorPatch(prob_googlenet, num_of_dims, nullptr, nullptr, &map_id, stride, (void **)&ptr, usage, VX_MEMORY_TYPE_HOST, 0);
                if(status) {
                    std::cerr << "ERROR: vxMapTensorPatch() failed for "  << std::endl;
                    return -1;
                }
                memcpy(outputBuffer[3], ptr, (count*sizeof(float)));
                status = vxUnmapTensorPatch(prob_googlenet, map_id);
                if(status) {
                    std::cerr << "ERROR: vxUnmapTensorPatch() failed for "  << std::endl;
                    return -1;
                }
            }
            // resnet 101 copy
            if(runResnet101)
            {
                vxQueryTensor(prob_resnet101, VX_TENSOR_DATA_TYPE, &data_type, sizeof(data_type));
                vxQueryTensor(prob_resnet101, VX_TENSOR_NUMBER_OF_DIMS, &num_of_dims, sizeof(num_of_dims));
                vxQueryTensor(prob_resnet101, VX_TENSOR_DIMS, &dims, sizeof(dims[0])*num_of_dims);
                if(data_type != VX_TYPE_FLOAT32) {
                    std::cerr << "ERROR: copyTensor() supports only VX_TYPE_FLOAT32: invalid for "  << std::endl;
                    return -1;
                }
                count = dims[0] * dims[1] * dims[2] * dims[3];
                status = vxMapTensorPatch(prob_resnet101, num_of_dims, nullptr, nullptr, &map_id, stride, (void **)&ptr, usage, VX_MEMORY_TYPE_HOST, 0);
                if(status) {
                    std::cerr << "ERROR: vxMapTensorPatch() failed for "  << std::endl;
                    return -1;
                }
                memcpy(outputBuffer[4], ptr, (count*sizeof(float)));
                status = vxUnmapTensorPatch(prob_resnet101, map_id);
                if(status) {
                    std::cerr << "ERROR: vxUnmapTensorPatch() failed for "  << std::endl;
                    return -1;
                }
            }
            // resnet 152 copy
            if(runResnet152)
            {
                vxQueryTensor(prob_resnet152, VX_TENSOR_DATA_TYPE, &data_type, sizeof(data_type));
                vxQueryTensor(prob_resnet152, VX_TENSOR_NUMBER_OF_DIMS, &num_of_dims, sizeof(num_of_dims));
                vxQueryTensor(prob_resnet152, VX_TENSOR_DIMS, &dims, sizeof(dims[0])*num_of_dims);
                if(data_type != VX_TYPE_FLOAT32) {
                    std::cerr << "ERROR: copyTensor() supports only VX_TYPE_FLOAT32: invalid for "  << std::endl;
                    return -1;
                }
                count = dims[0] * dims[1] * dims[2] * dims[3];
                status = vxMapTensorPatch(prob_resnet152, num_of_dims, nullptr, nullptr, &map_id, stride, (void **)&ptr, usage, VX_MEMORY_TYPE_HOST, 0);
                if(status) {
                    std::cerr << "ERROR: vxMapTensorPatch() failed for "  << std::endl;
                    return -1;
                }
                memcpy(outputBuffer[5], ptr, (count*sizeof(float)));
                status = vxUnmapTensorPatch(prob_resnet152, map_id);
                if(status) {
                    std::cerr << "ERROR: vxUnmapTensorPatch() failed for "  << std::endl;
                    return -1;
                }
            }
            // vgg copy
            if(runVgg19)
            {
                vxQueryTensor(prob_vgg19, VX_TENSOR_DATA_TYPE, &data_type, sizeof(data_type));
                vxQueryTensor(prob_vgg19, VX_TENSOR_NUMBER_OF_DIMS, &num_of_dims, sizeof(num_of_dims));
                vxQueryTensor(prob_vgg19, VX_TENSOR_DIMS, &dims, sizeof(dims[0])*num_of_dims);
                if(data_type != VX_TYPE_FLOAT32) {
                    std::cerr << "ERROR: copyTensor() supports only VX_TYPE_FLOAT32: invalid for "  << std::endl;
                    return -1;
                }
                count = dims[0] * dims[1] * dims[2] * dims[3];
                status = vxMapTensorPatch(prob_vgg19, num_of_dims, nullptr, nullptr, &map_id, stride, (void **)&ptr, usage, VX_MEMORY_TYPE_HOST, 0);
                if(status) {
                    std::cerr << "ERROR: vxMapTensorPatch() failed for "  << std::endl;
                    return -1;
                }
                memcpy(outputBuffer[6], ptr, (count*sizeof(float)));
                status = vxUnmapTensorPatch(prob_vgg19, map_id);
                if(status) {
                    std::cerr << "ERROR: vxUnmapTensorPatch() failed for "  << std::endl;
                    return -1;
                }
            }
            t1 = clockCounter();
            msFrame += (float)(t1-t0)*1000.0f/(float)freq;
            //printf("LIVE: Copy probability Output Time -- %.3f msec\n", (float)(t1-t0)*1000.0f/(float)freq);

            // process probabilty
            t0 = clockCounter();
            threshold = (float)thresholdValue;
            const int N = 1000;
            int inceptionID, resnetID, vggID, googlenetID, resnet101ID, resnet152ID, vgg19ID;
            if(runInception)
            {
                inceptionID = std::distance(outputBuffer[0], std::max_element(outputBuffer[0], outputBuffer[0] + N));
            }
            if(runResnet50)
            {
                resnetID = std::distance(outputBuffer[1], std::max_element(outputBuffer[1], outputBuffer[1] + N));
            }
            if(runVgg16)
            {
                vggID = std::distance(outputBuffer[2], std::max_element(outputBuffer[2], outputBuffer[2] + N));
            }
            if(runGooglenet)
            {
                googlenetID = std::distance(outputBuffer[3], std::max_element(outputBuffer[3], outputBuffer[3] + N));
            }
            if(runResnet101)
            {
                resnet101ID = std::distance(outputBuffer[4], std::max_element(outputBuffer[4], outputBuffer[4] + N));
            }
            if(runResnet152)
            {
                resnet152ID = std::distance(outputBuffer[5], std::max_element(outputBuffer[5], outputBuffer[5] + N));
            }
            if(runVgg19)
            {
                vgg19ID = std::distance(outputBuffer[6], std::max_element(outputBuffer[6], outputBuffer[6] + N));
            }
            t1 = clockCounter();
            msFrame += (float)(t1-t0)*1000.0f/(float)freq;
            //printf("LIVE: Get Classification ID Time -- %.3f msec\n", (float)(t1-t0)*1000.0f/(float)freq);

            // Write Output on Image
            t0 = clockCounter();
            cv::resize(frame, outputDisplay, cv::Size(outputImgWidth,outputImgHeight));
            int l = 1;
            std::string modelName1 = "InceptionV4 - ";
            std::string modelName2 = "Resnet50 - ";
            std::string modelName3 = "VGG16 - ";
            std::string modelName4 = "GoogleNet - ";
            std::string modelName5 = "Resnet101 - ";
            std::string modelName6 = "Resnet152 - ";
            std::string modelName7 = "VGG19 - ";
            std::string inceptionText = "Unclassified", resnetText = "Unclassified", vggText = "Unclassified", googlenetText = "Unclassified";
            std::string resnet101Text = "Unclassified", resnet152Text = "Unclassified", vgg19Text = "Unclassified";
            if(outputBuffer[0][inceptionID] >= threshold){ inceptionText = labelText[inceptionID]; }
            if(outputBuffer[1][resnetID] >= threshold){ resnetText = labelText[resnetID]; }
            if(outputBuffer[2][vggID] >= threshold){ vggText = labelText[vggID]; }
            if(outputBuffer[3][googlenetID] >= threshold){ googlenetText = labelText[googlenetID]; }
            if(outputBuffer[4][resnet101ID] >= threshold){ resnet101Text = labelText[resnet101ID]; }
            if(outputBuffer[5][resnet152ID] >= threshold){ resnet152Text = labelText[resnet152ID]; }
            if(outputBuffer[6][vgg19ID] >= threshold){ vgg19Text = labelText[vgg19ID]; }
            modelName1 = modelName1 + inceptionText;
            modelName2 = modelName2 + resnetText;
            modelName3 = modelName3 + vggText;
            modelName4 = modelName4 + googlenetText;
            modelName5 = modelName5 + resnet101Text;
            modelName6 = modelName6 + resnet152Text;
            modelName7 = modelName7 + vgg19Text;
            int red, green, blue;
            if(runInception && binaryFilename_inception_str != "empty")
            {
                red = (colors[0][2]); green = (colors[0][1]); blue = (colors[0][0]) ;
                putText(outputDisplay, modelName1, Point(20, (l * 40) + 30), fontFace, fontScale, Scalar(red,green,blue), thickness,8);
                l++;
            }
            if(runResnet50 && binaryFilename_resnet_str != "empty")
            {
                red = (colors[1][2]); green = (colors[1][1]); blue = (colors[1][0]) ;
                putText(outputDisplay, modelName2, Point(20, (l * 40) + 30), fontFace, fontScale, Scalar(red,green,blue), thickness,8);
                l++;
            }
            if(runVgg16 && binaryFilename_vgg_str != "empty")
            {
                red = (colors[2][2]); green = (colors[2][1]); blue = (colors[2][0]) ;
                putText(outputDisplay, modelName3, Point(20, (l * 40) + 30), fontFace, fontScale, Scalar(red,green,blue), thickness,8);
                l++;
            }
            if(runGooglenet && binaryFilename_googlenet_str != "empty")
            {
                red = (colors[3][2]); green = (colors[3][1]); blue = (colors[3][0]) ;
                putText(outputDisplay, modelName4, Point(20, (l * 40) + 30), fontFace, fontScale, Scalar(red,green,blue), thickness,8);
                l++;
            }
            if(runResnet101 && binaryFilename_resnet101_str != "empty")
            {
                red = (colors[4][2]); green = (colors[4][1]); blue = (colors[4][0]) ;
                putText(outputDisplay, modelName5, Point(20, (l * 40) + 30), fontFace, fontScale, Scalar(red,green,blue), thickness,8);
                l++;
            }
            if(runResnet152 && binaryFilename_resnet152_str != "empty")
            {
                red = (colors[5][2]); green = (colors[5][1]); blue = (colors[5][0]) ;
                putText(outputDisplay, modelName6, Point(20, (l * 40) + 30), fontFace, fontScale, Scalar(red,green,blue), thickness,8);
                l++;
            }
            if(runVgg19 && binaryFilename_vgg19_str != "empty")
            {
                red = (colors[6][2]); green = (colors[6][1]); blue = (colors[6][0]) ;
                putText(outputDisplay, modelName7, Point(20, (l * 40) + 30), fontFace, fontScale, Scalar(red,green,blue), thickness,8);
                l++;
            }
            t1 = clockCounter();
            msFrame += (float)(t1-t0)*1000.0f/(float)freq;
            //printf("LIVE: Resize and write on Output Image Time -- %.3f msec\n", (float)(t1-t0)*1000.0f/(float)freq);
   
            // display img time
            t0 = clockCounter();
            cv::imshow("MIVisionX Image Classification - LIVE", outputDisplay);
            createLegendImage();
            t1 = clockCounter();
            msFrame += (float)(t1-t0)*1000.0f/(float)freq;
            //printf("LIVE: Output Image Display Time -- %.3f msec\n", (float)(t1-t0)*1000.0f/(float)freq);

            // calculate FPS
            //printf("LIVE: msec for frame -- %.3f msec\n", (float)msFrame);
            frameMsecs += msFrame;
            if(frameCount && frameCount%10 == 0){
                printf("FPS LIVE: Avg FPS -- %d\n", (int)((ceil)(1000/(frameMsecs/10))));
                frameMsecs = 0;
            }

            // wait to close live inference application
            if( waitKey(2) == 27 ){ loopSeg = 0; break; } // stop capturing by pressing ESC
            else if( waitKey(2) == 82 ){ break; } // for restart pressing R

            frameCount++;
        }
    }

    // release resources
    for(int models = 0; models < 7; models++){
        delete outputBuffer[models];
    }
    // release input data
    ERROR_CHECK_STATUS(vxReleaseTensor(&data_299x299));
    ERROR_CHECK_STATUS(vxReleaseTensor(&data_224x224));

    // release output data
    ERROR_CHECK_STATUS(vxReleaseTensor(&prob_inception));
    ERROR_CHECK_STATUS(vxReleaseTensor(&prob_resnet));
    ERROR_CHECK_STATUS(vxReleaseTensor(&prob_vgg));
    ERROR_CHECK_STATUS(vxReleaseTensor(&prob_googlenet));
    ERROR_CHECK_STATUS(vxReleaseTensor(&prob_resnet101));
    ERROR_CHECK_STATUS(vxReleaseTensor(&prob_resnet152));
    ERROR_CHECK_STATUS(vxReleaseTensor(&prob_vgg19));

    // release graphs
    ERROR_CHECK_STATUS(vxReleaseGraph(&graph_inception));
    ERROR_CHECK_STATUS(vxReleaseGraph(&graph_resnet));
    ERROR_CHECK_STATUS(vxReleaseGraph(&graph_vgg));
    ERROR_CHECK_STATUS(vxReleaseGraph(&graph_googlenet));
    ERROR_CHECK_STATUS(vxReleaseGraph(&graph_resnet101));
    ERROR_CHECK_STATUS(vxReleaseGraph(&graph_resnet152));
    ERROR_CHECK_STATUS(vxReleaseGraph(&graph_vgg19));

    // release context
    ERROR_CHECK_STATUS(vxReleaseContext(&context));

    printf("OK: successful\n");

    return 0;
}
