Code viewer for World: A-Z character recognizer

// For this I decided to make my own instead of port or build on another code base

// link for python code for neural net training https://colab.research.google.com/drive/1hPdGIQm1UCjRe9ImYwQwpjx-Jm12MkZK?usp=sharing

// link for dataset used https://www.kaggle.com/datasets/sachinpatel21/az-handwritten-alphabets-in-csv-format

// This code uses tensorflow js library to import the model trained in python (keras) and use it on the web to make predictions
// I have uploaded the tf.min.js library and the trained model files on AB


// following is the architecture of the model I build in keras library in python
/*

Model: "sequential_1"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d_3 (Conv2D)           (None, 28, 28, 32)        320       
                                                                 
 max_pooling2d_2 (MaxPooling  (None, 14, 14, 32)       0         
 2D)                                                             
                                                                 
 conv2d_4 (Conv2D)           (None, 14, 14, 64)        18496     
                                                                 
 max_pooling2d_3 (MaxPooling  (None, 7, 7, 64)         0         
 2D)                                                             
                                                                 
 flatten_1 (Flatten)         (None, 3136)              0         
                                                                 
 dense_2 (Dense)             (None, 100)               313700    
                                                                 
 dense_3 (Dense)             (None, 26)                2626      
                                                                 
=================================================================
Total params: 335,142
Trainable params: 335,142
Non-trainable params: 0

*/

let model;
let correctPreds = 0;
let wrongPreds = 0;
let totalPreds = 0;

function getRandomAlphabet(){
    return "abcdefghijklmnopqrstuvwxyz".toUpperCase()[Math.floor(Math.random()*26)];
}

let currentChar = getRandomAlphabet();

function setup() {
    
	createCanvas(400, 400);
	background(0,0,0);
	
	// loading the tensorflow js script
	 $.getScript ( "/uploads/prashantk047/tf.min.js", function(){
	     
	     // loading the model from the uploded file model.json this function also make the request for the
	     // associated weights file group1-shard1of1.bin which is also uploaded on AB
	     tf.loadLayersModel("/uploads/prashantk047/model.json").then(model_ => {
	        model = model_;
	        model.summary();
	     });
	 });
	 
	 
	 // making the markup for interaction and prediction
    
    let thehtml = "Press the button to predict after drawing on the canvas<br>  " +
        " <button onclick='predict()' class='normbutton' >Predict</button> <br> ";
    AB.msg ( thehtml, 3 );
  
    thehtml = "Press to Clear the canvas " +
        " <button onclick='clean()' class='normbutton' >Clear</button> <br> ";
    AB.msg ( thehtml, 4 );
    
    thehtml = `Predicted Character : NULL`
    AB.msg ( thehtml, 5 );
    
    thehtml = "<h2>This section is to check the accuracy of the predictions</h2>";
    AB.msg( thehtml, 8);
    
    thehtml = `Draw the Alphabet <p style="color: blue; font-weight: bold; font-size:30px">${currentChar}</p> `;
    AB.msg( thehtml, 9);
    
    thehtml = `
    <table style="width:100%; border: 3px solid green; margin: 1rem 0">
  <tr>
    <th  style="text-align:center">Correct Guesses</th>
    <th  style="text-align:center">Wrong Guesses</th>
  </tr>
  <tr>
    <td  style="text-align:center">${correctPreds}</td>
    <td  style="text-align:center">${wrongPreds}</td>
  </tr>
</table>

    `;
    AB.msg( thehtml, 10);

     //---------------------------------------------------
	
}

function predict(){
    totalPreds++;
	        
    let labels = "abcdefghijklmnopqrstuvwxyz".toUpperCase();
    
    // this model expects an input of type tensor and of dimension nullx28x28x1 where the
    // first is null because there can be multiple images for prediction
    // here we will be prediction one 1 character at a time
    // so the input dimension will be 1x28x28x1
    
	// this takes the canvas created by p5 js and converts it to a matrix from pixels
	let image = tf.browser.fromPixels(document.getElementById('defaultCanvas0')) 
        .resizeNearestNeighbor([28, 28]) // converting the matrix to 28x28 as the model expects the image to be of this size
        .mean(2) // as the image from canvas has 3 color channels, this gets rid of the channel dimension
        .expandDims(2) // this add an extra dimension to make it 28x28x1
        .expandDims() // adds one more dimension to match the input size of 1 1x28x28x1
        .toFloat().div(255); // normalize the values from 0-255 to 0-1
	        
	let pred = model.predict(image).as1D()
		
	let argMax = pred.argMax().dataSync()[0];
	let finalPred = labels[argMax];
	
	if( finalPred == currentChar){
	    correctPreds++;
	}
	else {
	    wrongPreds++;
	}
	AB.msg ( `Predicted Character : <br/> <p style="color:red; font-size: 30px; font-weight:bold">${finalPred}</p> ${finalPred == currentChar?'Correct!!':'Wrong'}`, 5 );
	
	currentChar = getRandomAlphabet();
	
	thehtml = `Draw the Alphabet <p style="color: blue; font-weight: bold; font-size:30px">${currentChar}</p> `;
    AB.msg( thehtml, 9);
    
    thehtml = `
    <table style="width:100%; border: 3px solid green; margin: 1rem 0">
  <tr>
    <th  style="text-align:center">Correct Guesses</th>
    <th  style="text-align:center">Wrong Guesses</th>
  </tr>
  <tr>
    <td  style="text-align:center">${correctPreds}</td>
    <td  style="text-align:center">${wrongPreds}</td>
  </tr>
</table>

    `;
    AB.msg( thehtml, 10);
    
    thehtml = `Accuracy: ${parseFloat(correctPreds*100/totalPreds).toFixed(2)}%`
    AB.msg( thehtml, 11);

	
    
	console.log(labels[argMax]);
		    
}

function clean(){
    clear();
    background(0,0,0);
}

function mouseDragged() {
    noStroke();
    fill(255,255,255);
	ellipse(mouseX, mouseY, 25);
}