Image classification using Tensorflow.js
This is an image training and classificator using Tensorflow.js for the Machine Learning, and Vue.js for the interface.
by katth
HTML
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/@tensorflow/tfjs"></script>
<script src="https://unpkg.com/@tensorflow-models/mobilenet"></script>
<script src="https://unpkg.com/@tensorflow-models/knn-classifier"></script>
<div id="app">
<h1>Model status: {{ statusMessage }}</h1>
<!-- Computer webcam -->
<video id="player" controls autoplay muted
width="224" height="224"></video>
<form v-on:submit.prevent
action="">
<!-- New class Registrator -->
<label for="newClassInput"
>Register an unexistent class: </label>
<input type="text"
placeholder="Example: 'apple'" id="newClassInput">
<!-- registerButton -->
<button type="button" id="btnAddClass"
@click="registerNewClass()">Register new class</button>
<!-- Selector of class that will be trained -->
<label for="classSelector"
>Choose the class that will be trained:</label>
<select name="classSelector" id="classSelector">
<option v-for="(c, index) in foodClasses" :value="c.name"
v-bind:key="c.id" v-bind:id="'option' + index">{{ c.name }}</option>
</select>
<button type="button" @click="addExample()">Train class</button>
</form>
<hr>
<div class="classificationContainer">
<div class="info">
<h2>Classification</h2>
<p>Labels: {{ label }}</p>
<p>Confidence: {{ confidence }}</p>
</div>
</div>
</div>
CSS
* {
box-sizing: border-box;
}
html, body {
margin: 0;
}
#app {
width: 100%;
padding-right: 16px;
padding-left: 16px;
}
button, input {
display: block;
}
.classificationContainer {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: auto;
grid-column-gap: 16px;
grid-row-gap: 16px;
}
.previewContainer img {
width: 60px;
height: 60px;
}
#image {
max-width: 100%;
height: auto;
margin-top: 16px;
margin-bottom: 16px;
}
@media screen and (min-width: 768px) {
.classificationContainer {
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr;
}
}
JavaScript
// Vue.js
let app = new Vue({
el: '#app',
data: {
net: null,
classifier: null,
previewURLS: [],
imageDisplayed: "",
imagesSources: [],
label: '',
confidence: '',
statusMessage: "",
foodClasses: [],
cameraOn: false
},
methods: {
/* previewImage(event) {
// Mostra um preview das imagens que o usuário selecionou para fazer upload
let fileList = event.srcElement.files;
const file = fileList[0];
if (!file.type.startsWith('image/')) {
throw new Error(`File "${file.name}" is not an image!`);
}
this.previewURLS.push(URL.createObjectURL(file));
}, */
registerNewClass() {
let newClass = {};
const newClassInput = document.getElementById('newClassInput');
if (newClassInput.value !== '') {
newClass.name = newClassInput.value,
newClass.id = this.numberOfClasses;
this.foodClasses.push(newClass);
newClassInput.value = '';
}
},
// Add the class to the model
async addExample() {
const selector = document.getElementById('classSelector');
const player = document.getElementById('player');
// Get the intermediate activation of MobileNet 'conv_preds' and pass that
// to the KNN classifier.
const activation = this.net.infer(player, 'conv_preds');
// Pass the intermediate activation to the classifier
this.classifier.addExample(activation, selector.selectedIndex);
console.log("Class '" + selector.value + "' added to the model");
while (true) {
if (this.classifier.getNumClasses() > 0) {
const activation = this.net.infer(player, 'conv_preds');
const result = await...