Code viewer for World: Doodle Classifier
//---- normal P5 code -------------------------------------------------------
$.getScript("/uploads/atharva/prepareData.js")

$.getScript ( "https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js")
$.getScript("https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js")
$.getScript("https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.11.7")
//$.getScript("https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css")
//AB.loadCSS ( "https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" )




thehtml = '<h1>Doodle Classifier</h1> '
AB.msg(thehtml, 1);

thehtml = '<h2>Click the Train button to train the Model, then follow go to the below functions (Guess, Genrate, Clear)</h2> '+
    '<!<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"!>'
AB.msg(thehtml, 2);

thehtml = '<button class="train btn btn-primary btn-lg btn-block" id="train">train</button>';
AB.msg(thehtml, 4);

// 3 Training header
thehtml = '<span class="alert alert-primary" id="training-alert" role="alert">Training...</span>';
AB.msg(thehtml, 7);
thehtml = '<span class="alert1 alert-primary" id="Accuray-alert" role="alert"></span>';
AB.msg(thehtml, 8);
//   thehtml = '<textarea class="testpercentage"></textarea>'
//   AB.msg ( thehtml, 7 );

thehtml = '<h2>Draw a Bird,Flower, Rainbow or Cat  and click the Guess button.</h2> '
AB.msg(thehtml, 9);

// 5 Testing header
thehtml = '<button class="guess btn btn-primary" id="guess">guess</button>';
AB.msg(thehtml, 10);
thehtml = '<button class="btn btn btn-info" id="generate">Generate</button>';
AB.msg(thehtml, 12);

// 7 Demo header 
thehtml = '<button class="clear btn" id="clear">clear</button>';
AB.msg(thehtml, 14);


thehtml = '<span id="output" class="alert alert-success" role="alert"></span>';
AB.msg(thehtml, 16);
AB.newDiv('canvas')
AB.newDiv('Chart')

let charts = document.getElementById("Chart");
charts.innerHTML = `<canvas id="myChart" style="width:100%;max-width:600px"></canvas>`;




let dataObjectsArray = [];
let xValues = [];
let yValues = [];
// Contains the arrays of preloaded data
let dataPreload = [];

// Model
let model;

// Tensors
xs = {};
ys = {};
testing_xs = {};
testing_ys = {};

const numberOfEachDoodle = 1000;
const data_proportion = 0.8;

function preload() {
    console.log("Preloading data");
    let filename;
    for (let i = 0; i < doodleLabelList.length; i++) {
        filename = doodleLabelList[i].toLowerCase();
        dataPreload[i] = loadBytes("/uploads/atharva/" + filename + ".bin");
    }
    console.log(dataPreload);
    console.log("Done");
}

// Setup function of p5.js (called after preload)
async function setup() {
    let canvas = createCanvas(280, 280);
    canvas.parent("canvas");
    background(0);
    initializeData();
    console.log("Creating training tensors");

    let rTensors = prepareData(training_data, training_labels);
    xs = rTensors[0];
    ys = rTensors[1];
    console.log("Done");
    console.log("Creating testing tensors");
    // TODO: Something less cancerous
    rTensors = prepareData(testing_data, testing_labels);
    testing_xs = rTensors[0];
    testing_ys = rTensors[1];
    // Log progress
    console.log("Done");
    // Log progress
    console.log("Creating model");
    // Let's build the model
    model = buildModel();
    // Log progress
    console.log("Done");

    let trainButton = select("#train");
    let trainingAlert = document.getElementById("training-alert");
    trainingAlert.style.display = "none";
    let accurayalert = document.getElementById("Accuray-alert");
    accurayalert.style.display = "none";

    trainButton.mousePressed(
        // Log progress
        () => {
            trainingAlert.style.display = "inline";
            accurayalert.style.display = "inline";

            console.log("Training model");

            // Let's train the model (this .then(() => thingy is an application of the
            // new ES6 functionnality combined with the js promises).
            train(trainingAlert, accurayalert).then(async () => {
                // Log progress
                console.log("Done");
                trainingAlert.style.display = "none";
                trainingAlert.className = "btn btn-disabled";
            });
        }
    );


    let guessButton = select("#guess");

    guessButton.mousePressed(function() {
        let inputs = [];
        let inputImage = [];
        let img = get();
        img.resize(28, 28);
        img.loadPixels();
        // Convert black in white drawings to white in black drawings(training doodles are white on black)
        for (let i = 0; i < dataLength; i++) {
            let alpha = img.pixels[i * 4];
            // normalize the pixels
            inputs[i] = alpha / 255.0;
        }
        // We need to create a 2D array with this pixel because the model has been
        // trained with 2D tensors.
        inputImage[0] = inputs;

        // convert array to tensor
        let tensorToPredict = tf.tensor2d(inputImage);
        console.log(tensorToPredict);
        //  predict the doodle
        let guess = model.predict(tensorToPredict);
        let argMax = guess.argMax(1);
        let classifiedLabel = argMax.dataSync()[0];

        let classifiedDoodleLabel = doodleLabelList[classifiedLabel];
        const output = select("#output");
        output.html(classifiedDoodleLabel + "!!!");
        console.log("Guessed: " + classifiedDoodleLabel);
    });

    let clearButton = select("#clear");
    clearButton.mousePressed(function() {
        background(0);
        const output = select("#output");
        output.html("");
    });

    //////////////Genrating a Random image from dataset that is cat,flower,bird,rainbow
    let generateButton = select("#generate");

    generateButton.mousePressed(function() {
        background(0);
        const output = select("#output");
        output.html("");
        let randomIndex = floor(
            random(
                numberOfEachDoodle * doodleLabelList.length * (1 - data_proportion)
            )
        );
        let offset = randomIndex * dataLength;
        let doodlePixels = testing_xs
            .dataSync()
            .subarray(offset, offset + dataLength);
        let otherOffset = randomIndex * doodleLabelList.length;
        let labelsResult = testing_ys
            .dataSync()
            .subarray(otherOffset, otherOffset + doodleLabelList.length);
        let doodleIndex;
        for (let i = 0; i < labelsResult.length; i++) {
            if (labelsResult[i] === 1) {
                doodleIndex = i;
            }
        }
        console.log(doodleLabelList[doodleIndex]);
        let img = createImage(28, 28);
        img.loadPixels();
        for (let i = 0; i < dataLength; i++) {
            let val = doodlePixels[i] * 255;
            img.pixels[i * 4 + 0] = val;
            img.pixels[i * 4 + 1] = val;
            img.pixels[i * 4 + 2] = val;
            img.pixels[i * 4 + 3] = 255;
        }
        img.updatePixels();
        img.resize(280, 280);
        image(img, 0, 0);
    });
}

function draw() {
    strokeWeight(15);
    stroke(255);
    if (mouseIsPressed) {
        line(pmouseX, pmouseY, mouseX, mouseY);
    }
}


//////////////////////////////////////  dataobject
class DataObject {
    constructor(label) {
        this.label = label;
        this.totalData = [];
        this.trainingData = [];
        this.testingData = [];
        this.trainingLabels = [];
        this.testingLabels = [];
        this.data_proportion = 0.8;
        this.numberOfEachDoodle = 1000;
        this.bytesArrayLength = 784;
        this.numberOfDoodles = 1000;
    }

    get trainingData() {
        return this._trainingData;
    }
    get testingData() {
        return this._testingData;
    }
    get trainingLabels() {
        return this._trainingLabels;
    }
    get testingLabels() {
        return this._testingLabels;
    }

    set trainingData(data) {
        this._trainingData = data;
    }

    set testingData(data) {
        this._testingData = data;
    }

    set trainingLabels(data) {
        this._trainingLabels = data;
    }

    set testingLabels(data) {
        this._testingLabels = data;
    }

    loadBytesData() {
        let index = doodleLabelList.indexOf(this.label);
        let bytesObject = dataPreload[index];
        // bytesObject.bytes is the actual bytes array
        this.totalData = bytesObject.bytes;
    }

    splitData() {
        console.log(this.numberOfDoodles);
        for (let i = 0; i < this.numberOfDoodles; i++) {
            // keeping track of index
            let offset = i * this.bytesArrayLength;

            // threshold for test/train data split
            let threshold = floor(this.data_proportion * this.numberOfDoodles);
            if (i < threshold) {
                // 1 - 800
                this.trainingData[i] = this.totalData.subarray(
                    offset,
                    offset + this.bytesArrayLength
                );
                this.trainingLabels[i] = this.label;
            } else {
                // 1 - 200
                this.testingData[i - threshold] = this.totalData.subarray(
                    offset,
                    offset + this.bytesArrayLength
                );
                this.testingLabels[i - threshold] = this.label;
            }
        }
    }
}




//////// training and testing
function buildModel() {
    let tempModel = tf.sequential();
    
    

    const hiddenLayer0_Units = 512;
    const hiddenLayer1_Units = 256;
    const hiddenLayers_Activation = "sigmoid";
    
    
 
    
   /* const hiddenLayer0 = tf.layers.dense({
        units: hiddenLayer0_Units,
        inputShape: dataLength,
        activation: hiddenLayers_Activation,
    });*/

    const hiddenLayer1 = tf.layers.dense({
        units: hiddenLayer1_Units,
        inputShape: dataLength,
        activation: hiddenLayers_Activation,
    });

    const hiddenLayer2_Units = 128;
    const hiddenLayer2 = tf.layers.dense({
        units: hiddenLayer2_Units,
        activation: hiddenLayers_Activation,
    });

    const hiddenLayer3_Units = 64;
    const hiddenLayer3 = tf.layers.dense({
        units: hiddenLayer3_Units,
        activation: hiddenLayers_Activation,
    });


    
    //output layer
    const outputLayer_Activation = "softmax";
    const output = tf.layers.dense({
        units: doodleLabelList.length,
        activation: outputLayer_Activation,
    });
    
    //tempModel.add(hiddenLayer0);
    tempModel.add(hiddenLayer1);
    tempModel.add(hiddenLayer2);
    tempModel.add(hiddenLayer3);
    tempModel.add(output);

    const model_LearningRate = 0.5;
    const model_Optimizer = tf.train.adagrad(model_LearningRate);
    const model_Loss =  "meanSquaredError";
    tempModel.compile({
        optimizer: model_Optimizer,
        loss: model_Loss,
        metrics: ["accuracy"],
        debug: true
    });
    //tempModel.compile(optimizer=tf.train.adagrad(model_LearningRate), loss=tf.keras.losses.CategoricalCrossentropy(), metrics = ["accuracy"]);
    return tempModel;
}

async function train(alert, alert1) {
    const training_DoShuffle = true;
    const training_ValidationSplit = 0.2;
    const training_BatchSize = 16;
    const training_NumEpochs = 50;
    await model.fit(xs, ys, {
        shuffle: training_DoShuffle,
        validationSplit: training_ValidationSplit,
        batchSize: training_BatchSize,
        epochs: training_NumEpochs,
        callbacks: {
            onEpochEnd: (epochs, logs) => {
                console.log("Epoch: " + (epochs + 1));
                console.log("Loss: " + logs.loss);
                console.log("Accuracy: " + logs.acc.toFixed(2));
                alert.innerHTML = `Training ${epochs * 2}% done...`;
                alert1.innerHTML = `Accuracy: ${100 * logs.acc.toFixed(2)}`;
                var xVal = parseInt(epochs + 1);
                xValues.push(xVal);
                var yVal = parseInt(100 * logs.acc.toFixed(2));
                yValues.push(yVal);
            },
        },
    });
    plotchat(xValues,yValues)
}

function plotchat(xValues,yValues){
                new Chart("myChart", {
                  type: "line",
                  data: {
                    labels: xValues,
                    datasets: [{
                      fill: false,
                      lineTension: 0,
                      backgroundColor: "rgba(0,0,255,1.0)",
                      borderColor: "rgba(0,0,255,0.1)",
                      data: yValues
                    }]
                  },
                  options: {
                    legend: {display: false,label: "Accuracy" },
                    scales: {
                      yAxes: [{ticks: {min: 0, max:100}}],
                    },
                    title: {
                      display: true,
                      text: "Accuracy Chart",
                      fontSize: 16
                    }
                  }
                });
                //console.log((xValues));
                //console.log((yValues));
}