mirror of
https://github.com/ocogeclub/ocoge.git
synced 2024-11-21 23:29:48 +00:00
462 lines
17 KiB
JavaScript
462 lines
17 KiB
JavaScript
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 ** */
|
||
/******************* */
|
||
Blockly.defineBlocksWithJsonArray([{
|
||
"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"
|
||
}]);
|
||
javascript.javascriptGenerator.forBlock['ugj_grideye_init'] = function (block, generator) {
|
||
var dropdown_addr = block.getFieldValue('addr');
|
||
Blockly.JavaScript.provideFunction_(
|
||
'require_amg8833', [`const _amg8833 = require('@ocoge/amg8833');`]
|
||
);
|
||
var code = `await _amg8833.init(_rg, ${apptool.i2c_bus}, ${dropdown_addr});\n`;
|
||
return code;//
|
||
};
|
||
/********************** */
|
||
/** Grid-Eye 本体温度 ** */
|
||
/********************** */
|
||
Blockly.defineBlocksWithJsonArray([{
|
||
"type": "ugj_grideye_thermistor",
|
||
"message0": "%{BKY_UGJ_GRIDEYE_THERMISTOR_TITLE}",
|
||
"output": "Number",
|
||
"tooltip": "%{BKY_UGJ_GRIDEYE_THERMISTOR_TOOLTIP}",
|
||
"helpUrl": "",
|
||
"style": "sensor_blocks"
|
||
}]);
|
||
javascript.javascriptGenerator.forBlock['ugj_grideye_thermistor'] = function (block, generator) {
|
||
var code = `await _amg8833.read_thermistor()`;
|
||
return [code, Blockly.JavaScript.ORDER_NONE];
|
||
};
|
||
|
||
/**************************** */
|
||
/** Read Temperature Array ** */
|
||
/**************************** */
|
||
Blockly.defineBlocksWithJsonArray([{
|
||
"type": "ugj_grideye_read",
|
||
"message0": "%{BKY_UGJ_GRIDEYE_READ_TITLE}",
|
||
"inputsInline": true,
|
||
"output": "Array",
|
||
"tooltip": "%{BKY_UGJ_GRIDEYE_READ_TOOLTIP}",
|
||
"helpUrl": "",
|
||
"style": "sensor_blocks"
|
||
}]);
|
||
javascript.javascriptGenerator.forBlock['ugj_grideye_read'] = function (block, generator) {
|
||
var code = 'await _amg8833.read_temp_array()';
|
||
return [code, Blockly.JavaScript.ORDER_ATOMIC];
|
||
};
|
||
/******************* */
|
||
/** Stop Grid-Eye ** */
|
||
/******************* */
|
||
Blockly.defineBlocksWithJsonArray([{
|
||
"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"
|
||
}]);
|
||
javascript.javascriptGenerator.forBlock['ugj_grideye_stop'] = function (block, generator) {
|
||
var code = 'await _amg8833.stop();\n';
|
||
return code;
|
||
};
|
||
|
||
|
||
/***************************** */
|
||
/** GridEye 表示キャンバス作成 ** */
|
||
/***************************** */
|
||
Blockly.defineBlocksWithJsonArray([{
|
||
"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"
|
||
}]);
|
||
javascript.javascriptGenerator.forBlock['ugj_grideye_canvas_create'] = function (block, generator) {
|
||
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', {willReadFrequently: true});
|
||
_grideye_imgData = _grideye_ctx.createImageData(8, 8);
|
||
`;
|
||
return code;
|
||
};
|
||
/********************************************** */
|
||
/** Draw IR Array Data to Image Data ** */
|
||
/********************************************** */
|
||
|
||
Blockly.defineBlocksWithJsonArray([{
|
||
"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"
|
||
}]);
|
||
javascript.javascriptGenerator.forBlock['ugj_draw_grideyedata'] = function (block, generator) {
|
||
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 を開始** */
|
||
/**************************** */
|
||
Blockly.defineBlocksWithJsonArray([{
|
||
"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"
|
||
}]);
|
||
javascript.javascriptGenerator.forBlock['ugj_teachable_machine'] = function (block, generator) {
|
||
Blockly.JavaScript.provideFunction_(
|
||
'require_ts', [`const _tf = require('@tensorflow/tfjs-node');`]
|
||
);
|
||
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 = ` const _net = await _mobilenet.load({ version: 1, alpha: 0.25 }); // 高速・低精度
|
||
const _classifier = _knnClassifier.create();
|
||
`;
|
||
return code;
|
||
};
|
||
/************************* */
|
||
/** GridEye で推論を行う ** */
|
||
/************************* */
|
||
Blockly.defineBlocksWithJsonArray([{
|
||
"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"
|
||
}]);
|
||
javascript.javascriptGenerator.forBlock['ugj_grideye_predict_class'] = function (block, generator) {
|
||
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 をデータセットに追加 ** */
|
||
/******************************************** */
|
||
Blockly.defineBlocksWithJsonArray([{
|
||
"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"
|
||
}]);
|
||
javascript.javascriptGenerator.forBlock['ugj_grideye_add_example'] = function (block, generator) {
|
||
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;
|
||
};
|
||
/*************************** */
|
||
/** 学習したクラスを文字列化 ** */
|
||
/*************************** */
|
||
Blockly.defineBlocksWithJsonArray([{
|
||
"type": "ugj_tensorset_stringify",
|
||
"message0": "%{BKY_UGJ_TENSORSET_STRINGIFY_TITLE}",
|
||
"output": null,
|
||
"tooltip": "%{BKY_UGJ_TENSORSET_STRINGIFY_TOOLTIP}",
|
||
"helpUrl": "",
|
||
"style": "multimedia_blocks"
|
||
}]);
|
||
javascript.javascriptGenerator.forBlock['ugj_tensorset_stringify'] = function (block, generator) {
|
||
var code = `JSON.stringify( Object.entries(_classifier.getClassifierDataset()).map(([label, data])=>[label, Array.from(data.dataSync()), data.shape]) )`;
|
||
return [code, Blockly.JavaScript.ORDER_NONE];
|
||
};
|
||
/***************************************** */
|
||
/** jsonをデータセットに戻して分類器にセット ** */
|
||
/***************************************** */
|
||
Blockly.defineBlocksWithJsonArray([{
|
||
"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"
|
||
}]);
|
||
javascript.javascriptGenerator.forBlock['ugj_tensorset_parse'] = function (block, generator) {
|
||
var value_class_data_json = Blockly.JavaScript.valueToCode(block, 'class_data_json', Blockly.JavaScript.ORDER_ATOMIC);
|
||
var code = `_classifier.setClassifierDataset( Object.fromEntries( JSON.parse(${value_class_data_json}).map(([label, data, shape])=>[label, _tf.tensor(data, shape)]) ) );`;
|
||
return code;
|
||
};
|
||
|
||
// 温度ブロック(ツールボックス上非表示)
|
||
Blockly.defineBlocksWithJsonArray([{
|
||
"type": "ugj_temp",
|
||
"message0": "%1",
|
||
"args0": [
|
||
{
|
||
"type": "field_slider",
|
||
"name": "temp",
|
||
"value": 15,
|
||
"min": 0,
|
||
"max": 40,
|
||
"precision": 1
|
||
}
|
||
],
|
||
"inputsInline": true,
|
||
"output": "Number",
|
||
"tooltip": "",
|
||
"helpUrl": "",
|
||
"style": "math_blocks"
|
||
}]);
|
||
javascript.javascriptGenerator.forBlock['ugj_temp'] = function (block, generator) {
|
||
var number_temp = block.getFieldValue('temp');
|
||
var code = `${number_temp}`;
|
||
return [code, Blockly.JavaScript.ORDER_NONE];
|
||
};
|
||
|
||
|
||
flyout_contents = flyout_contents.concat([
|
||
{
|
||
"kind": "label",
|
||
"text": "赤外線アレイセンサー(サーマルカメラ)AMG8833",
|
||
"web-line": "4.0",
|
||
"web-line-width": "200"
|
||
},
|
||
{
|
||
"kind": "block",
|
||
"type": "ugj_grideye_init",
|
||
"fields": {
|
||
"addr": "0x69"
|
||
}
|
||
},
|
||
{
|
||
"kind": "block",
|
||
"type": "ugj_grideye_thermistor"
|
||
},
|
||
{
|
||
"kind": "block",
|
||
"type": "ugj_grideye_read"
|
||
},
|
||
{
|
||
"kind": "block",
|
||
"type": "ugj_grideye_stop"
|
||
},
|
||
{
|
||
"kind": "block",
|
||
"type": "ugj_grideye_canvas_create"
|
||
},
|
||
{
|
||
"kind": "block",
|
||
"type": "ugj_draw_grideyedata",
|
||
"fields": {
|
||
"color_high": "#ff0000",
|
||
"color_low": "#3333ff"
|
||
},
|
||
"inputs": {
|
||
"temp_high": {
|
||
"shadow": {
|
||
"type": "ugj_temp",
|
||
"fields": {
|
||
"temp": "28"
|
||
}
|
||
}
|
||
},
|
||
"temp_low": {
|
||
"shadow": {
|
||
"type": "ugj_temp",
|
||
"fields": {
|
||
"temp": "15"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"kind": "block",
|
||
"type": "ugj_teachable_machine"
|
||
},
|
||
{
|
||
"kind": "block",
|
||
"type": "ugj_grideye_predict_class"
|
||
},
|
||
{
|
||
"kind": "block",
|
||
"type": "ugj_grideye_add_example",
|
||
"inputs": {
|
||
"class_id": {
|
||
"shadow": {
|
||
"type": "math_number",
|
||
"fields": {
|
||
"NUM": "0"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"kind": "block",
|
||
"type": "ugj_tensorset_stringify"
|
||
},
|
||
{
|
||
"kind": "block",
|
||
"type": "ugj_tensorset_parse"
|
||
}
|
||
]); |