// 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);
}