Blockly.Msg["UGJ_DRAW_GRIDEYEDATA_TITLE"] = "赤外線アレイセンサ画像表示 %1 温度データ %2 温度範囲上限 %3 %4 温度範囲下限 %5 %6"; Blockly.Msg["UGJ_DRAW_GRIDEYEDATA_TOOLTIP"] = "AMG8833の温度データを、画像としてキャンバスに描画します。「着色」をチェックすると、温度範囲で設定されている色をつけて表示します。"; Blockly.Msg["UGJ_GRIDEYE_INIT_TITLE"] = "赤外線アレイセンサ(アドレス: %1 )を初期化"; Blockly.Msg["UGJ_GRIDEYE_INIT_TOOLTIP"] = "赤外線アレイセンサ AMG8833 の使用準備をします。"; Blockly.Msg["UGJ_GRIDEYE_THERMISTOR_TITLE"] = "赤外線アレイセンサ本体温度"; Blockly.Msg["UGJ_GRIDEYE_THERMISTOR_TOOLTIP"] = "AMG8833に内蔵されたサーミスタ(温度センサ)の値を取得します。"; Blockly.Msg["UGJ_GRIDEYE_READ_TITLE"] = "赤外線アレイセンサの値"; Blockly.Msg["UGJ_GRIDEYE_READ_TOOLTIP"] = "AMG8833から読み取った温度データを、8x8の配列で取得します。"; Blockly.Msg["UGJ_GRIDEYE_STOP_TITLE"] = "赤外線アレイセンサから切断"; Blockly.Msg["UGJ_GRIDEYE_STOP_TOOLTIP"] = "センサーとの接続を停止します。"; Blockly.Msg["UGJ_GRIDEYE_CANVAS_CREATE_TITLE"] = "赤外線アレイセンサデータ表示キャンバスを作成"; Blockly.Msg["UGJ_GRIDEYE_CANVAS_CREATE_TOOLTIP"] = "ディスプレイエリアにAMG8833データ表示用キャンバスを作成します。"; Blockly.Msg["UGJ_TEACHABLE_MACHINE_TITLE"] = "TensorFlow.jsによる画像分類器の準備"; Blockly.Msg["UGJ_TEACHABLE_MACHINE_TOOLTIP"] = "TensorFlow.jsにMobileNet, KNN Classifierを読み込んで、画像認識(分類)を行う準備をします。"; Blockly.Msg["UGJ_GRIDEYE_PREDICT_CLASS_TITLE"] = "赤外線アレイセンサの画像で推論を行う"; Blockly.Msg["UGJ_GRIDEYE_PREDICT_CLASS_TOOLTIP"] = "キャンバスに表示されたAMG8833の画像を元に画像分類の推論を行います。推論の結果として定義済みのラベルを返します。"; Blockly.Msg["UGJ_GRIDEYE_ADD_EXAMPLE_TITLE"] = "赤外線アレイセンサの画像にラベル %1 をつけてデータセットへ追加"; Blockly.Msg["UGJ_GRIDEYE_ADD_EXAMPLE_TOOLTIP"] = "キャンバスに表示されているAMG8833の画像にラベル(クラス名)をつけてデータセットへ追加します。"; Blockly.Msg["UGJ_TENSORSET_STRINGIFY_TITLE"] = "学習したクラスデータセットを文字列に変換"; Blockly.Msg["UGJ_TENSORSET_STRINGIFY_TOOLTIP"] = "学習したクラスデータセットを文字列に変換して保存します。"; Blockly.Msg["UGJ_TENSORSET_PARSE_TITLE"] = "クラスデータ文字列 %1 を画像分類器にセット"; Blockly.Msg["UGJ_TENSORSET_PARSE_TOOLTIP"] = "JSONテキストをパースして画像分類器に戻します。"; /******************* */ /** Init Grid-Eye ** */ /******************* */ var ugjGridEyeInitDefinition = { "type": "ugj_grideye_init", "message0": "%{BKY_UGJ_GRIDEYE_INIT_TITLE}", "args0": [ { "type": "field_dropdown", "name": "addr", "options": [ [ "0x68", "0x68" ], [ "0x69", "0x69" ] ] } ], "inputsInline": true, "previousStatement": null, "nextStatement": null, "tooltip": "%{BKY_UGJ_GRIDEYE_INIT_TOOLTIP}", "helpUrl": "", "style": "sensor_blocks" }; Blockly.Blocks['ugj_grideye_init'] = { init: function () { this.jsonInit(ugjGridEyeInitDefinition); } }; Blockly.JavaScript['ugj_grideye_init'] = function (block) { var dropdown_addr = block.getFieldValue('addr'); Blockly.JavaScript.provideFunction_( 'require_gpio', [`const _pi = require('@ocogeclub/` + elutil.gpio_backend + `');`] ); let modpath = elutil.path.join(elutil.blocks_sensors_dir, 'amg8833', 'AMG8833x.js'); Blockly.JavaScript.provideFunction_( 'require_amg8833', [`const _amg8833 = require('${modpath}');`] ); var code = `await _amg8833.init(${elutil.i2c_bus}, ${dropdown_addr}, window.addEventListener);\n`; return code;// }; /********************** */ /** Grid-Eye 本体温度 ** */ /********************** */ var ugjGridEyeThermistorDefinition = { "type": "ugj_grideye_thermistor", "message0": "%{BKY_UGJ_GRIDEYE_THERMISTOR_TITLE}", "output": "Number", "tooltip": "%{BKY_UGJ_GRIDEYE_THERMISTOR_TOOLTIP}", "helpUrl": "", "style": "sensor_blocks" }; Blockly.Blocks['ugj_grideye_thermistor'] = { init: function () { this.jsonInit(ugjGridEyeThermistorDefinition); } }; Blockly.JavaScript['ugj_grideye_thermistor'] = function (block) { var code = `await _amg8833.read_thermistor()`; return [code, Blockly.JavaScript.ORDER_NONE]; }; /**************************** */ /** Read Temperature Array ** */ /**************************** */ var ugjGridEyeReadDefinition = { "type": "ugj_grideye_read", "message0": "%{BKY_UGJ_GRIDEYE_READ_TITLE}", "inputsInline": true, "output": "Array", "tooltip": "%{BKY_UGJ_GRIDEYE_READ_TOOLTIP}", "helpUrl": "", "style": "sensor_blocks" }; Blockly.Blocks['ugj_grideye_read'] = { init: function () { this.jsonInit(ugjGridEyeReadDefinition); } }; Blockly.JavaScript['ugj_grideye_read'] = function (block) { var code = 'await _amg8833.read_temp_array()'; return [code, Blockly.JavaScript.ORDER_ATOMIC]; }; /******************* */ /** Stop Grid-Eye ** */ /******************* */ var ugjGridEyeStopDefinition = { "type": "ugj_grideye_stop", "message0": "%{BKY_UGJ_GRIDEYE_STOP_TITLE}", "inputsInline": true, "previousStatement": null, "nextStatement": null, "tooltip": "%{BKY_UGJ_GRIDEYE_STOP_TOOLTIP}", "helpUrl": "", "style": "sensor_blocks" }; Blockly.Blocks['ugj_grideye_stop'] = { init: function () { this.jsonInit(ugjGridEyeStopDefinition); } }; Blockly.JavaScript['ugj_grideye_stop'] = function (block) { var code = 'await _amg8833.stop();\n'; return code; }; /***************************** */ /** GridEye 表示キャンバス作成 ** */ /***************************** */ var ugjGridEyeCanvasCreateDefinition = { "type": "ugj_grideye_canvas_create", "message0": "%{BKY_UGJ_GRIDEYE_CANVAS_CREATE_TITLE}", "inputsInline": true, "previousStatement": null, "nextStatement": null, "tooltip": "%{BKY_UGJ_GRIDEYE_CANVAS_CREATE_TOOLTIP}", "helpUrl": "", "style": "multimedia_blocks" }; Blockly.Blocks['ugj_grideye_canvas_create'] = { init: function () { this.jsonInit(ugjGridEyeCanvasCreateDefinition); } }; Blockly.JavaScript['ugj_grideye_canvas_create'] = function (block) { var code = `let _grideye_canvas = document.createElement('canvas'); _grideye_canvas.setAttribute('width', 8); _grideye_canvas.setAttribute('height', 8); _grideye_canvas.className = 'subdisplay'; _grideye_canvas.style.width = '160px'; _grideye_canvas.style.height = '160px'; _grideye_canvas.id = 'subcanvas'; document.getElementById('display_area').appendChild(_grideye_canvas); _grideye_ctx = _grideye_canvas.getContext('2d'); _grideye_imgData = _grideye_ctx.createImageData(8, 8); `; return code; }; /********************************************** */ /** Draw IR Array Data to Image Data ** */ /********************************************** */ var ugjDrawGrideyedataDefinition = { "type": "ugj_draw_grideyedata", "message0": "%{BKY_UGJ_DRAW_GRIDEYEDATA_TITLE}", "args0": [ { "type": "input_dummy" }, { "type": "input_value", "name": "amg8833data", "check": "Array", "align": "RIGHT" }, { "type": "field_colour", "name": "color_high", "colour": "#ff0000" }, { "type": "input_value", "name": "temp_high", "check": "Number", "align": "RIGHT" }, { "type": "field_colour", "name": "color_low", "colour": "#3333ff" }, { "type": "input_value", "name": "temp_low", "check": "Number", "align": "RIGHT" } ], "inputsInline": false, "previousStatement": null, "nextStatement": null, "tooltip": "%{BKY_UGJ_DRAW_GRIDEYEDATA_TOOLTIP}", "helpUrl": "", "style": "multimedia_blocks" }; Blockly.Blocks['ugj_draw_grideyedata'] = { init: function () { this.jsonInit(ugjDrawGrideyedataDefinition); } }; Blockly.JavaScript['ugj_draw_grideyedata'] = function (block) { var value_amg8833data = Blockly.JavaScript.valueToCode(block, 'amg8833data', Blockly.JavaScript.ORDER_ATOMIC); var colour_color_high = block.getFieldValue('color_high'); var value_temp_high = Blockly.JavaScript.valueToCode(block, 'temp_high', Blockly.JavaScript.ORDER_ATOMIC); var colour_color_low = block.getFieldValue('color_low'); var value_temp_low = Blockly.JavaScript.valueToCode(block, 'temp_low', Blockly.JavaScript.ORDER_ATOMIC); var functionName = Blockly.JavaScript.provideFunction_( '_mapVal', ['const ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + ' = (val, inMin, inMax, outMin, outMax) => {', `return (val - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;`, '}' ] ); // 温度カラー let hr, hg, hb, lr, lg, lb; hr = '0x' + colour_color_high.slice(1, 3); hg = '0x' + colour_color_high.slice(3, 5); hb = '0x' + colour_color_high.slice(5, 7); lr = '0x' + colour_color_low.slice(1, 3); lg = '0x' + colour_color_low.slice(3, 5); lb = '0x' + colour_color_low.slice(5, 7); var code = ` const _color_range = [[${lr}, ${hr}], [${lg}, ${hg}], [${lb}, ${hb}]]; let _grideye_data = ${value_amg8833data};//読み取りブロックを入力に直接接続できるようにする for (let raw = 0; raw < _grideye_canvas.height; raw++) { for (let col = 0; col < _grideye_canvas.width; col++) { for (let rgb = 0; rgb < 3; rgb++) { let pixel = ${functionName}(_grideye_data[raw][col], ${value_temp_low}, ${value_temp_high}, _color_range[rgb][0], _color_range[rgb][1]); _grideye_imgData.data[((raw * _grideye_canvas.width * 4) + col * 4) + rgb] = pixel; } _grideye_imgData.data[((raw * _grideye_canvas.width * 4) + col * 4) + 3] = 0xff; } } _grideye_ctx.putImageData(_grideye_imgData, 0, 0); `; return code; }; /**************************** */ /** Teachable Machine を開始** */ /**************************** */ var ugjTeachableMachineDefinition = { "type": "ugj_teachable_machine", "message0": "%{BKY_UGJ_TEACHABLE_MACHINE_TITLE}", "inputsInline": true, "previousStatement": null, "nextStatement": null, "tooltip": "%{BKY_UGJ_TEACHABLE_MACHINE_TOOLTIP}", "helpUrl": "", "style": "multimedia_blocks" }; Blockly.Blocks['ugj_teachable_machine'] = { init: function () { this.jsonInit(ugjTeachableMachineDefinition); } }; Blockly.JavaScript['ugj_teachable_machine'] = function (block) { Blockly.JavaScript.provideFunction_( 'require_ts', [`const _tf = require('@tensorflow/tfjs');`] // 'require_ts', [`const _tf = require('@tensorflow/tfjs-node');`] ); Blockly.JavaScript.provideFunction_( 'require_wasm', [`const _wasm = require('@tensorflow/tfjs-backend-wasm');`] ); Blockly.JavaScript.provideFunction_( 'require_mobilenet', [`const _mobilenet = require('@tensorflow-models/mobilenet');`] ); Blockly.JavaScript.provideFunction_( 'require_knn', [`const _knnClassifier = require('@tensorflow-models/knn-classifier');`] ); var code = `await _tf.setBackend('wasm'); const _net = await _mobilenet.load({ version: 1, alpha: 0.25 }); // 高速・低精度 const _classifier = _knnClassifier.create(); `; return code; }; /************************* */ /** GridEye で推論を行う ** */ /************************* */ var ugjGridEyePredictClassDefinition = { "type": "ugj_grideye_predict_class", "message0": "%{BKY_UGJ_GRIDEYE_PREDICT_CLASS_TITLE}", "inputsInline": true, "output": "Number", "tooltip": "%{BKY_UGJ_GRIDEYE_PREDICT_CLASS_TOOLTIP}", "helpUrl": "", "style": "multimedia_blocks" }; Blockly.Blocks['ugj_grideye_predict_class'] = { init: function () { this.jsonInit(ugjGridEyePredictClassDefinition); } }; Blockly.JavaScript['ugj_grideye_predict_class'] = function (block) { var functionName = Blockly.JavaScript.provideFunction_( // left output にするための関数化 '_predictClass', [ `if (_confidence === undefined) var _confidence;`, `const ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_} = async (img, clsfr, mblnet) => {`, `if (clsfr.getNumClasses() > 0) {`, `const result = await clsfr.predictClass(mblnet.infer(img, 'conv_preds'));`, `_confidence = result.confidences[result.label];`, `return result.label;`, `}`, `else return 0;`, `}` ] ); var code = `await ${functionName}(_grideye_canvas, _classifier, _net)`; return [code, Blockly.JavaScript.ORDER_NONE]; }; /******************************************** */ /** ラベルをつけて Example をデータセットに追加 ** */ /******************************************** */ var ugjGridEyeAddExampleDefinition = { "type": "ugj_grideye_add_example", "message0": "%{BKY_UGJ_GRIDEYE_ADD_EXAMPLE_TITLE}", "args0": [ { "type": "input_value", "name": "class_id", "check": "Number" } ], "inputsInline": true, "previousStatement": null, "nextStatement": null, "tooltip": "%{BKY_UGJ_GRIDEYE_ADD_EXAMPLE_TOOLTIP}", "helpUrl": "", "style": "multimedia_blocks" }; Blockly.Blocks['ugj_grideye_add_example'] = { init: function () { this.jsonInit(ugjGridEyeAddExampleDefinition); } }; Blockly.JavaScript['ugj_grideye_add_example'] = function (block) { var value_class_id = Blockly.JavaScript.valueToCode(block, 'class_id', Blockly.JavaScript.ORDER_ATOMIC); var code = `_classifier.addExample (_net.infer(_grideye_canvas, true), ${value_class_id});`; return code; }; /*************************** */ /** 学習したクラスを文字列化 ** */ /*************************** */ var ugjTensorsetStringifyDefinition = { "type": "ugj_tensorset_stringify", "message0": "%{BKY_UGJ_TENSORSET_STRINGIFY_TITLE}", "output": null, "tooltip": "%{BKY_UGJ_TENSORSET_STRINGIFY_TOOLTIP}", "helpUrl": "", "style": "multimedia_blocks" }; Blockly.Blocks['ugj_tensorset_stringify'] = { init: function () { this.jsonInit(ugjTensorsetStringifyDefinition); } }; Blockly.JavaScript['ugj_tensorset_stringify'] = function (block) { Blockly.JavaScript.provideFunction_( 'require_tensorset', [`const _Tensorset = require('tensorset');`] ); var code = `await _Tensorset.stringify(_classifier.getClassifierDataset())`; return [code, Blockly.JavaScript.ORDER_NONE]; }; /***************************************** */ /** jsonをデータセットに戻して分類器にセット ** */ /***************************************** */ var ugjTensorsetParseDefinition = { "type": "ugj_tensorset_parse", "message0": "%{BKY_UGJ_TENSORSET_PARSE_TITLE}", "args0": [ { "type": "input_value", "name": "class_data_json", "check": "String" } ], "previousStatement": null, "nextStatement": null, "tooltip": "%{BKY_UGJ_TENSORSET_PARSE_TOOLTIP}", "helpUrl": "", "style": "multimedia_blocks" }; Blockly.Blocks['ugj_tensorset_parse'] = { init: function () { this.jsonInit(ugjTensorsetParseDefinition); } }; Blockly.JavaScript['ugj_tensorset_parse'] = function (block) { Blockly.JavaScript.provideFunction_( 'require_tensorset', [`const _Tensorset = require('tensorset');`] ); var value_class_data_json = Blockly.JavaScript.valueToCode(block, 'class_data_json', Blockly.JavaScript.ORDER_ATOMIC); var code = `try { let _class_dataset = _Tensorset.parse(${value_class_data_json}); _classifier.setClassifierDataset(_class_dataset); } catch (error) { alert('Could not load class dataset.'); } `; return code; };