// Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import $ from 'jquery' import 'jquery.cookie'; import 'bootstrap' import FileSaver from 'file-saver'; // Number of classes to classify const NUM_CLASSES = 8; String.prototype.sprintf = function() { let str = this + ''; const args = Array.prototype.slice.call(arguments); let ph = true; if (str.indexOf('%s', 0) != -1) { ph = false; } if (args.length === 1) { if (ph) { return str.replace(/%1$s/g, args[0]); } else { return str.replace(/%s/g, args[0]); } } else { for (let i=0; i 0) { lang = vars['lang']; } if (lang == 'ja') { return LOCALIZED_TEXT['ja'][key].sprintf(arg); } else if (lang == 'zh_cn') { return LOCALIZED_TEXT['zh_cn'][key].sprintf(arg); }else { return LOCALIZED_TEXT['en'][key].sprintf(arg); } } static getUrlVars() { let vars = [], max = 0, hash = "", array = ""; const url = window.location.search; hash = url.slice(1).split('&'); max = hash.length; for (let i = 0; i < max; i++) { array = hash[i].split('='); vars.push(array[0]); vars[array[0]] = array[1]; } return vars; } } class Main { constructor(){ // Initiate variables this.infoTexts = []; this.training = -1; // -1 when no class is being trained this.videoPlaying = false; this.connId = undefined; this.wss_url = $.cookie('wss_url') || "wss://ml2scratch-helper.glitch.me" this.video = $('video')[0]; this.isTouchDevice = 'ontouchstart' in document.documentElement; this.knnClassifier = ml5.KNNClassifier(); this.featureExtractor = ml5.featureExtractor('MobileNet', () => { this.start(); }); let params = new URLSearchParams(window.location.search); if (params.get('conn_id')) { this.connId = params.get('conn_id'); } else { this.connId = Math.floor(Math.random(100000000) * 100000000) } $('#conn-id').val(this.connId); this.connect(this.connId); // Create cards. This needs to be run at the first place. for(let i=0;i { this.clearAll(); return false; }); $('#learning .edit-label-menu').each((i, el) => { $(el).on('click', ()=> { this.editLabel(i); return false; }); }); $('#learning .clear-menu').each((i, el) => { $(el).on('click', ()=> { this.clear(i); return false; }); }); $('#download-button').on('click', ()=> { this.download(); return false; }); $('#conn-id').on('click', (e)=> { $(e.target).select(); }) // fileを選択したら名前を表示する $('[data-file]').each(function(index, el) { $(el).on('change', function(e) { let filename = $(e.currentTarget).val().split('\\').pop() let element = $(e.currentTarget).closest('.input-file').find('[data-file-name]')[0] element.innerText = filename; $(e.currentTarget).closest('.input-file').addClass('has-file') }); }); $("#upload-files").change(()=>{ this.upload(); }); $('.card-block').each((i, el) => { $(el).on('click', ()=> { $('#trained-images .images').hide(); $('#trained-images .images').eq(i).show(); $("#trained-images .training-id").html(i); }); }); // Create training buttons and info texts for(let i=0;i{ if (this.isTouchDevice == false) { this.training = i; } }); button.mouseup(()=>{ if (this.isTouchDevice == false) { this.training = -1; } }); button.on('touchstart', ()=>{ if (this.isTouchDevice) { this.training = i; } }); button.on('touchend', ()=>{ if (this.isTouchDevice) { this.training = -1; } }); $('#learning .card-block .input').eq(i).on("blur", ()=>{ let label = $('#learning .card-block .card-block__label').eq(i); let input = $('#learning .card-block .input').eq(i); label.removeClass("none"); input.addClass("none"); label.html(input.val() || i); }) } $('#wss_url').val(this.wss_url); $('#wss_url').on('blur', (e)=> { this.wss_url = $(e.target).val(); $.cookie('wss_url', this.wss_url, { expires: 90 }); }); $('#connect-button').on('click', (e)=> { let connId = $('#conn-id').val(); this.connect(connId); return false; }); $("#scratch-link").attr('href', 'https://champierre.github.io/scratch/'); // Setup webcam navigator.mediaDevices.getUserMedia({video: true, audio: false}) .then((stream) => { this.video.srcObject = stream; this.video.addEventListener('playing', ()=> this.videoPlaying = true); this.video.addEventListener('paused', ()=> this.videoPlaying = false); }) $(window).on('beforeunload', function() { if (location.href != "http://localhost:9966/dist/") { return 'ページから離れようとしていますが、よろしいですか?'; } }); } start() { if (this.timer) { this.stop(); } this.video.play(); this.timer = requestAnimationFrame(this.animate.bind(this)); } stop() { this.video.pause(); cancelAnimationFrame(this.timer); } classify() { const numLabels = this.knnClassifier.getNumLabels(); if (numLabels == 0) return; const features = this.featureExtractor.infer(this.video); this.knnClassifier.classify(features, (err, result) => { if (err) { console.error(err); } else { if(this.ws && this.ws.readyState === WebSocket.OPEN){ let label = $('#learning .card-block .card-block__label').eq(result.classIndex).html(); this.ws.send(JSON.stringify({action: 'predict', conn_id: this.connId, value: result.classIndex, label: label})); } this.updateProgress(result.confidences); } }); } animate() { if(this.videoPlaying){ this.classify(); // Train class if one of the buttons is held down if(this.training != -1){ const features = this.featureExtractor.infer(this.video); this.knnClassifier.addExample(features, String(this.training)); this.updateCounts(); } } this.timer = requestAnimationFrame(this.animate.bind(this)); } connect(connId) { this.ws = new WebSocket(`${this.wss_url}/ml`); this.connId = connId; } download() { const fileName = String(Date.now()); this.knnClassifier.save(fileName); } upload() { const files = document.getElementById('upload-files').files; if (files.length <= 0) { return false; } const fr = new FileReader(); fr.onload = (e) => { const data = JSON.parse(e.target.result); this.knnClassifier.load(data, () => { this.updateCounts(); }); } fr.onloadend = (e) => { document.getElementById('upload-files').value = ""; } fr.readAsText(files.item(0)); } editLabel(i) { let label = $('.card-block .card-block__label').eq(i); let input = $('.card-block .input').eq(i); label.addClass('none'); input.removeClass('none'); input.val(label.html()); } clear(i) { this.knnClassifier.clearLabel(String(i)); this.updateCounts(); } clearAll() { this.knnClassifier.clearAllLabels() this.updateCounts(); } updateProgress(confidences) { let html = ''; let labels = $('.card-block .card-block__label'); $.each(confidences, function(i, confidence) { let label = ""; if (confidence > 0) { label = labels.eq(i).html(); } html += `
${label}
`; }); $('.progress').html(html); } addCard() { const html = `
x 0
`; $('#learning .card-block-container').append(html); } updateCounts() { const counts = this.knnClassifier.getCountByLabel(); for(let i=0;i { new Main(); new I18n(); });