From fe343c67ff2298128b30875def8a1d9c58ae0cf0 Mon Sep 17 00:00:00 2001 From: RXXXBNUer <1014042554@qq.com> Date: Sat, 4 Oct 2025 11:24:10 +0800 Subject: [PATCH] =?UTF-8?q?Pyodide=E9=87=8C=E7=9A=84Tensorflow=E7=9B=AE?= =?UTF-8?q?=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 可以跑通基本的训练、使用模型过程 --- .../python_pyodide/blocks/tensorflow.js | 230 +++++ boards/default_src/python_pyodide/export.js | 6 +- .../python_pyodide/generators/tensorflow.js | 97 +++ boards/default_src/python_pyodide/index.js | 10 +- .../modules/tensorflow-0.0.1-py3-none-any.whl | Bin 0 -> 2797 bytes .../origin/deps/0.62.21/python3/repodata.json | 11 + .../python_pyodide/others/loader.js | 325 ++++++- .../others/teachableMachine/App.vue | 25 - .../components/teachableModel/ModelArea.vue | 819 +++++++++--------- .../default_src/python_pyodide/template.xml | 122 +++ .../build/lib/tensorflow/__init__.py | 44 + .../build/lib/tensorflow/layers/__init__.py | 5 + .../build/lib/tensorflow/layers/activation.py | 37 + .../build/lib/tensorflow/layers/base.py | 0 .../build/lib/tensorflow/layers/core.py | 5 + .../dist/tensorflow-0.0.1-py3-none-any.whl | Bin 0 -> 2797 bytes .../tensorflow.egg-info/PKG-INFO | 10 + .../tensorflow.egg-info/SOURCES.txt | 10 + .../tensorflow.egg-info/dependency_links.txt | 1 + .../tensorflow.egg-info/top_level.txt | 1 + .../tensorflow-project/tensorflow/__init__.py | 14 + .../tensorflow/layers/__init__.py | 6 +- .../tensorflow/layers/core.py | 5 + common/media/tfmodel/group1-shard1of3.bin | Bin 0 -> 4194304 bytes common/media/tfmodel/group1-shard2of3.bin | Bin 0 -> 4194304 bytes common/media/tfmodel/group1-shard3of3.bin | Bin 0 -> 438664 bytes common/media/tfmodel/model.json | 1 + common/msg/blockly/en.js | 32 + common/msg/blockly/zh-hans.js | 31 + common/msg/blockly/zh-hant.js | 31 + 30 files changed, 1459 insertions(+), 419 deletions(-) create mode 100644 boards/default_src/python_pyodide/blocks/tensorflow.js create mode 100644 boards/default_src/python_pyodide/generators/tensorflow.js create mode 100644 boards/default_src/python_pyodide/origin/deps/0.62.21/python3/modules/tensorflow-0.0.1-py3-none-any.whl create mode 100644 boards/default_src/python_pyodide/whl/tensorflow-project/build/lib/tensorflow/__init__.py create mode 100644 boards/default_src/python_pyodide/whl/tensorflow-project/build/lib/tensorflow/layers/__init__.py create mode 100644 boards/default_src/python_pyodide/whl/tensorflow-project/build/lib/tensorflow/layers/activation.py create mode 100644 boards/default_src/python_pyodide/whl/tensorflow-project/build/lib/tensorflow/layers/base.py create mode 100644 boards/default_src/python_pyodide/whl/tensorflow-project/build/lib/tensorflow/layers/core.py create mode 100644 boards/default_src/python_pyodide/whl/tensorflow-project/dist/tensorflow-0.0.1-py3-none-any.whl create mode 100644 boards/default_src/python_pyodide/whl/tensorflow-project/tensorflow.egg-info/PKG-INFO create mode 100644 boards/default_src/python_pyodide/whl/tensorflow-project/tensorflow.egg-info/SOURCES.txt create mode 100644 boards/default_src/python_pyodide/whl/tensorflow-project/tensorflow.egg-info/dependency_links.txt create mode 100644 boards/default_src/python_pyodide/whl/tensorflow-project/tensorflow.egg-info/top_level.txt create mode 100644 boards/default_src/python_pyodide/whl/tensorflow-project/tensorflow/layers/core.py create mode 100644 common/media/tfmodel/group1-shard1of3.bin create mode 100644 common/media/tfmodel/group1-shard2of3.bin create mode 100644 common/media/tfmodel/group1-shard3of3.bin create mode 100644 common/media/tfmodel/model.json diff --git a/boards/default_src/python_pyodide/blocks/tensorflow.js b/boards/default_src/python_pyodide/blocks/tensorflow.js new file mode 100644 index 00000000..6ac54c7c --- /dev/null +++ b/boards/default_src/python_pyodide/blocks/tensorflow.js @@ -0,0 +1,230 @@ +import * as Blockly from 'blockly/core'; + +const TENSORFLOW_HUE = '#1216ab'; + +Blockly.Blocks.tensorflow_init_tensor = { + init: function () { + this.appendValueInput("VAR") + .setCheck(null) + .setAlign(Blockly.ALIGN_LEFT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_INIT_TENSOR); + this.setOutput(true, null); + this.setColour(TENSORFLOW_HUE); + this.setTooltip(''); + this.setHelpUrl(''); + } +}; + +Blockly.Blocks.tensorflow_sequential = { + init: function () { + this.appendDummyInput() + .setAlign(Blockly.ALIGN_LEFT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_SEQUENTIAL); + this.setOutput(true, null); + this.setColour(TENSORFLOW_HUE); + this.setTooltip(''); + this.setHelpUrl(''); + } +}; + +Blockly.Blocks.tensorflow_layers_dense = { + init: function () { + this.appendDummyInput() + .setAlign(Blockly.ALIGN_LEFT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_INIT_LAYERS_DENSE_LAYER); + this.appendValueInput("VAR1") + .setCheck(null) + .setAlign(Blockly.ALIGN_RIGHT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_OUTPUT_DIMENSION); + this.appendValueInput("VAR2") + .setCheck(null) + .setAlign(Blockly.ALIGN_RIGHT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_INPUT_SHAPE); + this.setOutput(true, null); + this.setColour(TENSORFLOW_HUE); + this.setTooltip(''); + this.setHelpUrl(''); + } +}; + +Blockly.Blocks.tensorflow_add = { + init: function () { + this.appendValueInput("VAR1") + .setCheck(null) + .setAlign(Blockly.ALIGN_LEFT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_MODEL); + this.appendValueInput("VAR2") + .setCheck(null) + .setAlign(Blockly.ALIGN_LEFT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_ADD_LAYER); + this.setInputsInline(true); + this.setPreviousStatement(true, null); + this.setNextStatement(true, null); + this.setColour(TENSORFLOW_HUE); + this.setTooltip(''); + this.setHelpUrl(''); + } +}; + +Blockly.Blocks.tensorflow_compile = { + init: function () { + this.appendValueInput("VAR1") + .setCheck(null) + .setAlign(Blockly.ALIGN_LEFT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_COMPILE_MODEL); + this.appendDummyInput() + .setAlign(Blockly.ALIGN_LEFT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_LOSS_FUNCTION_TYPE) + .appendField(new Blockly.FieldDropdown([ + [Blockly.Msg.MIXLY_TENSORFLOW_MEAN_SQUARED_ERROR, "meanSquaredError"] + ]), "VAR2"); + this.appendDummyInput() + .setAlign(Blockly.ALIGN_LEFT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_OPTIMIZER) + .appendField(new Blockly.FieldDropdown([ + [Blockly.Msg.MIXLY_TENSORFLOW_SGD, "sgd"] + ]), "VAR3"); + this.setInputsInline(true); + this.setPreviousStatement(true, null); + this.setNextStatement(true, null); + this.setColour(TENSORFLOW_HUE); + this.setTooltip(''); + this.setHelpUrl(''); + } +}; + +Blockly.Blocks.tensorflow_fit = { + init: function () { + this.appendValueInput("VAR1") + .setCheck(null) + .setAlign(Blockly.ALIGN_LEFT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_FIT_MODEL); + this.appendValueInput("VAR2") + .setCheck(null) + .setAlign(Blockly.ALIGN_RIGHT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_FIT_INPUT_DATA); + this.appendValueInput("VAR3") + .setCheck(null) + .setAlign(Blockly.ALIGN_RIGHT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_FIT_TARGET_DATA); + this.appendValueInput("VAR4") + .setCheck(null) + .setAlign(Blockly.ALIGN_RIGHT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_FIT_EPOCHS); + this.appendValueInput("VAR5") + .setCheck(null) + .setAlign(Blockly.ALIGN_RIGHT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_FIT_VERBOSE); + this.appendDummyInput() + .setAlign(Blockly.ALIGN_LEFT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_FIT_RETURN_HISTORY); + this.setInputsInline(false); + this.setOutput(true, null); + this.setColour(TENSORFLOW_HUE); + this.setTooltip(''); + this.setHelpUrl(''); + } +}; + +Blockly.Blocks.tensorflow_get_loss = { + init: function () { + this.appendValueInput("VAR") + .setCheck(null) + .setAlign(Blockly.ALIGN_LEFT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_GET_LOSS_FROM_HISTORY); + this.appendDummyInput() + .setAlign(Blockly.ALIGN_LEFT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_GET_LOSS_FROM_HISTORY_2); + this.setOutput(true, null); + this.setColour(TENSORFLOW_HUE); + this.setTooltip(''); + this.setHelpUrl(''); + } +}; + +Blockly.Blocks.tensorflow_predict = { + init: function () { + this.appendValueInput("VAR1") + .setCheck(null) + .setAlign(Blockly.ALIGN_LEFT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_PREDICT); + this.appendValueInput("VAR2") + .setCheck(null) + .setAlign(Blockly.ALIGN_RIGHT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_PREDICT_INPUT_DATA); + this.appendDummyInput() + .setAlign(Blockly.ALIGN_RIGHT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_PREDICT_RETURN_RESULT); + this.setInputsInline(false); + this.setOutput(true, null); + this.setColour(TENSORFLOW_HUE); + this.setTooltip(''); + this.setHelpUrl(''); + } +}; + +Blockly.Blocks.tensorflow_get_tensor_data = { + init: function () { + this.appendValueInput("VAR") + .setCheck(null) + .setAlign(Blockly.ALIGN_LEFT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_GET_TENSOR_DATA); + this.setOutput(true, null); + this.setColour(TENSORFLOW_HUE); + this.setTooltip(''); + this.setHelpUrl(''); + } +}; + +Blockly.Blocks.tensorflow_save_or_export_model = { + init: function () { + this.appendValueInput("NAME1") + .setCheck(null) + .setAlign(Blockly.ALIGN_LEFT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_MODEL); + this.appendValueInput("NAME2") + .setCheck(null) + .setAlign(Blockly.ALIGN_LEFT) + .appendField(new Blockly.FieldDropdown([ + [Blockly.Msg.MIXLY_TENSORFLOW_SAVE_MODEL, "save"], + [Blockly.Msg.MIXLY_TENSORFLOW_EXPORT_MODEL, "export"] + ]), "NAME") + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_SAVE_MODEL_NAME); + this.setInputsInline(true); + this.setPreviousStatement(true, null); + this.setNextStatement(true, null); + this.setColour(TENSORFLOW_HUE); + this.setTooltip(''); + this.setHelpUrl(''); + } +}; + +Blockly.Blocks.tensorflow_use_load_model = { + init: function () { + this.appendValueInput("NAME") + .setCheck(null) + .setAlign(Blockly.ALIGN_LEFT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_LOAD_MODEL) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_MODEL_NAME); + this.setInputsInline(true); + this.setOutput(true, null); + this.setColour(TENSORFLOW_HUE); + this.setTooltip(''); + this.setHelpUrl(''); + } +}; + +Blockly.Blocks.tensorflow_prepare_picture = { + init: function () { + this.appendValueInput("NAME") + .setCheck(null) + .setAlign(Blockly.ALIGN_LEFT) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_PREPARE_PICTURE_TO_TENSOR) + .appendField(Blockly.Msg.MIXLY_TENSORFLOW_PREPARE_PICTURE_READ_PICTURE); + this.setInputsInline(true); + this.setOutput(true, null); + this.setColour(TENSORFLOW_HUE); + this.setTooltip(''); + this.setHelpUrl(''); + } +}; \ No newline at end of file diff --git a/boards/default_src/python_pyodide/export.js b/boards/default_src/python_pyodide/export.js index 70d64194..ccf9bf50 100644 --- a/boards/default_src/python_pyodide/export.js +++ b/boards/default_src/python_pyodide/export.js @@ -1,7 +1,11 @@ import * as PythonPyodideSKLearnBlocks from './blocks/sklearn'; import * as PythonPyodideSKLearnGenerators from './generators/sklearn'; +import * as PythonTensorflowBlocks from './blocks/tensorflow'; +import * as PythonTensorflowGenerators from './generators/tensorflow'; export { PythonPyodideSKLearnBlocks, - PythonPyodideSKLearnGenerators + PythonPyodideSKLearnGenerators, + PythonTensorflowBlocks, + PythonTensorflowGenerators }; \ No newline at end of file diff --git a/boards/default_src/python_pyodide/generators/tensorflow.js b/boards/default_src/python_pyodide/generators/tensorflow.js new file mode 100644 index 00000000..404c3b7f --- /dev/null +++ b/boards/default_src/python_pyodide/generators/tensorflow.js @@ -0,0 +1,97 @@ +export const tensorflow_init_tensor = function (_, generator) { + var VALUE_INPUT_VAR = generator.valueToCode(this, "VAR", generator.ORDER_ATOMIC); + generator.definitions_['import_tensorflow'] = 'import tensorflow'; + var code = 'tensorflow.tensor(' + VALUE_INPUT_VAR + ')'; + return [code, generator.ORDER_ATOMIC]; +}; + +export const tensorflow_sequential = function (_, generator) { + generator.definitions_['import_tensorflow'] = 'import tensorflow'; + var code = 'tensorflow.sequential()'; + return [code, generator.ORDER_ATOMIC]; +}; + +export const tensorflow_layers_dense = function (_, generator) { + generator.definitions_['import_tensorflow'] = 'import tensorflow'; + var VALUE_INPUT_VAR1 = generator.valueToCode(this, "VAR1", generator.ORDER_ATOMIC); + var VALUE_INPUT_VAR2 = generator.valueToCode(this, "VAR2", generator.ORDER_ATOMIC); + var code = 'tensorflow.layers.dense(units = ' + VALUE_INPUT_VAR1 + ', input_shape = ' + VALUE_INPUT_VAR2 + ')'; + return [code, generator.ORDER_ATOMIC]; +}; + +export const tensorflow_add = function (_, generator) { + generator.definitions_['import_tensorflow'] = 'import tensorflow'; + var VALUE_INPUT_VAR1 = generator.valueToCode(this, "VAR1", generator.ORDER_ATOMIC); + var VALUE_INPUT_VAR2 = generator.valueToCode(this, "VAR2", generator.ORDER_ATOMIC); + var code = VALUE_INPUT_VAR1 + '.add(' + VALUE_INPUT_VAR2 + ')\n'; + return code; +}; + +export const tensorflow_compile = function (_, generator) { + generator.definitions_['import_tensorflow'] = 'import tensorflow'; + var VALUE_INPUT_VAR1 = generator.valueToCode(this, "VAR1", generator.ORDER_ATOMIC); + var VALUE_INPUT_VAR2 = this.getFieldValue("VAR2"); + var VALUE_INPUT_VAR3 = this.getFieldValue("VAR3"); + var code = VALUE_INPUT_VAR1 + '.compile(loss = "' + VALUE_INPUT_VAR2 + '", optimizer = "' + VALUE_INPUT_VAR3 + '")\n'; + return code; +} + +export const tensorflow_fit = function (_, generator) { + generator.definitions_['import_tensorflow'] = 'import tensorflow'; + var VALUE_INPUT_VAR1 = generator.valueToCode(this, "VAR1", generator.ORDER_ATOMIC); + var VALUE_INPUT_VAR2 = generator.valueToCode(this, "VAR2", generator.ORDER_ATOMIC); + var VALUE_INPUT_VAR3 = generator.valueToCode(this, "VAR3", generator.ORDER_ATOMIC); + var VALUE_INPUT_VAR4 = generator.valueToCode(this, "VAR4", generator.ORDER_ATOMIC); + var VALUE_INPUT_VAR5 = generator.valueToCode(this, "VAR5", generator.ORDER_ATOMIC); + var code = 'await ' + VALUE_INPUT_VAR1 + '.fit(' + VALUE_INPUT_VAR2 + ', ' + VALUE_INPUT_VAR3 + ', epochs=' + VALUE_INPUT_VAR4 + ', verbose=' + VALUE_INPUT_VAR5 + ')'; + return [code, generator.ORDER_ATOMIC]; +}; + +export const tensorflow_get_loss = function (_, generator) { + generator.definitions_['import_tensorflow'] = 'import tensorflow'; + var VALUE_INPUT_VAR = generator.valueToCode(this, "VAR", generator.ORDER_ATOMIC); + var code = VALUE_INPUT_VAR + '.history.loss'; + return [code, generator.ORDER_ATOMIC]; +}; + +export const tensorflow_predict = function (_, generator) { + generator.definitions_['import_tensorflow'] = 'import tensorflow'; + var VALUE_INPUT_VAR1 = generator.valueToCode(this, "VAR1", generator.ORDER_ATOMIC); + var VALUE_INPUT_VAR2 = generator.valueToCode(this, "VAR2", generator.ORDER_ATOMIC); + var code = VALUE_INPUT_VAR1 + '.predict(' + VALUE_INPUT_VAR2 + ')'; + return [code, generator.ORDER_ATOMIC]; +}; + +export const tensorflow_get_tensor_data = function (_, generator) { + generator.definitions_['import_tensorflow'] = 'import tensorflow'; + var VALUE_INPUT_VAR1 = generator.valueToCode(this, "VAR", generator.ORDER_ATOMIC); + var code = "(await " + VALUE_INPUT_VAR1 + ".data())"; + return [code, generator.ORDER_ATOMIC]; +}; + +export const tensorflow_save_or_export_model = function (_, generator) { + generator.definitions_['import_tensorflow'] = 'import tensorflow'; + + var VALUE_INPUT_NAME1 = generator.valueToCode(this, "NAME1", generator.ORDER_ATOMIC); + var FIELD_NAME = this.getFieldValue("NAME"); + var VALUE_INPUT_NAME2 = generator.valueToCode(this, "NAME2", generator.ORDER_ATOMIC).replace(/^'|'$/g, ''); + if (FIELD_NAME == "export") { + return `await ${VALUE_INPUT_NAME1}.save("downloads://${VALUE_INPUT_NAME2}")\n`; + } + return `await ${VALUE_INPUT_NAME1}.save("indexeddb://${VALUE_INPUT_NAME2}")\n`; +}; + +export const tensorflow_use_load_model = function (_, generator) { + generator.definitions_['import_tensorflow'] = 'import tensorflow'; + + var VALUE_INPUT_NAME = generator.valueToCode(this, "NAME", generator.ORDER_ATOMIC).replace(/^'|'$/g, ''); + return [`await tensorflow.load_model("${VALUE_INPUT_NAME}")`, generator.ORDER_ATOMIC]; +}; + +export const tensorflow_prepare_picture = function (_, generator) { + generator.definitions_['import_tensorflow'] = 'import tensorflow'; + generator.definitions_['import_numpy'] = 'import numpy'; + generator.definitions_['import_PIL'] = 'import PIL'; + var VALUE_INPUT_NAME = generator.valueToCode(this, "NAME", generator.ORDER_ATOMIC); + return [`(await tensorflow.prepare_qmyixtxi(tensorflow.tensor(numpy.array(PIL.Image.open(${VALUE_INPUT_NAME}).convert('RGB')))))`, generator.ORDER_ATOMIC]; +}; diff --git a/boards/default_src/python_pyodide/index.js b/boards/default_src/python_pyodide/index.js index 85c308b0..72f9bfd4 100644 --- a/boards/default_src/python_pyodide/index.js +++ b/boards/default_src/python_pyodide/index.js @@ -69,7 +69,9 @@ import { import { PythonPyodideSKLearnBlocks, - PythonPyodideSKLearnGenerators + PythonPyodideSKLearnGenerators, + PythonTensorflowBlocks, + PythonTensorflowGenerators } from './'; import './others/loader'; @@ -113,7 +115,8 @@ Object.assign( PythonMixpySKLearnBlocks, PythonMixpySystemBlocks, PythonMixpyTurtleBlocks, - PythonPyodideSKLearnBlocks + PythonPyodideSKLearnBlocks, + PythonTensorflowBlocks ); Object.assign( @@ -146,5 +149,6 @@ Object.assign( PythonMixpySKLearnGenerators, PythonMixpySystemGenerators, PythonMixpyTurtleGenerators, - PythonPyodideSKLearnGenerators + PythonPyodideSKLearnGenerators, + PythonTensorflowGenerators ); \ No newline at end of file diff --git a/boards/default_src/python_pyodide/origin/deps/0.62.21/python3/modules/tensorflow-0.0.1-py3-none-any.whl b/boards/default_src/python_pyodide/origin/deps/0.62.21/python3/modules/tensorflow-0.0.1-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..d26e3feda5606fc8743d01ecc40abf42f15cb086 GIT binary patch literal 2797 zcmaKu2{=^y8^_PsvXpdF6AIZ9n#B^5Y%?{tX*8CGQJG}aSTbT7Th>cSsOZ{dnqq8e zv7}q1kfIS4O0uOW>tIrZdvyE%XOf)rymOwJ=lnkNzQ6B#zVE?f1%;#m01yS%xNq2h z0iqLh0|EfuLIAK9{FUb7=|lBC;z9L=lSrDayc=FJ@BJ@(Y-#wbyk-^-RvRA|}d`0m@q zyOPZvG7p^Xwc(spfo5?bU%a>kNU3O^tWZXTc0XCqNv`NZis%}h!V?oI8I^;dkseod zWX+$>P6VGG90OT&;Xk4qGiBaB)Jke;DLY({{F0nBFy+lg_2iO!SLf!-P8QKFQ{ADie!ghNQ9fSIFT^g zETv=F#1;LS{*P<};iGuB=?X`ZXmC(q&j{hh&Ky;9&OW^l>z|!UbDM11cGqS{;;aY< zivPdHdeB)1Vq5KPET}C7j150)?BV3^;_bsPHSP&gyi##FP~T+A!3$zNNUgi*~i(`pSJ?69uS!S01p{3_#V0+f*f}8 zaRE0$kZ%v+ppt6Suvt%VLF#W8I#a#p7vf@(;#G*{Xy3m^7|2sSg3{iRTE?cL_Dqd4 z9xDEihtjgo&Q1A^j5p)+5+7T z;QJ89&Y|wn9s3F?^tx#}(?P!4|0U^mP{;sQu%^t9>c(XCyG;5-^LOF8`ToYy61ay%y9 z?8O<%CIX?bQm;%N-5z;`Y3XY?S%rJ-94+tee9KaH3u9G@!N3(I$faT(dTN^LQ4^E6 zo}7l)H}K-eq(<$j!?ojFZ+oGHx{JfCq5Jy2w`byt=-o#jiDP;-Rw9&aNd)CcZD=KN z)2_Z=Tk8}#`vjux?oZ1Da*70W1gysJ^a-q<;ebf&ZcCxAz+3^f`?=z@%&K%_$j^n- zKGY2!01Ms^hT_tN+hNe?J;D#ZizM85kk$A~Qd{z%EQvwW+*n*Bk*Ry=s$#u`XXoS& z6UGy{4&N6KZR#dMAG#+)hog>15C=})>Cd_ z6C50kU?Xo_8flHT*lUdndWLItGX;RInd$xg=$%@_=IYW3=l@AFv>?~nIWwoYzRh>G zY?}$|OF#V{*ZG|3R!fS&SgbQo7=FN2O$;4l-PK+#HOI*Q`&77q^i*M@e7Fx0?P*8Y zoSk1Xps_Dr!N4lMMB#I0{-`o*X4Bt()w+HqIv?=2j3nMBA83eHyJz`;E!}W#mGR_7 z-x!U3hF0fI7zWPd%9vudTp;s~T3q3gHm+7e>ny#Jg|$_87z)TWWWJJBG(J}stmmC~ z_2eO&_C6|OyUsO+QU2%c)!U=fw{?fP_uf|Qbad+q%t-ro(6WBCG|I4BFsWoBW1xEs z#b~F{Wk-hd$|>d3%}9cnm%~dK3)5Va0&R=C?xW;K9e;>Qj!IDeD6=<}Im0c$=yNe4 zO@2XdN1T$Ls%8(XpYIcM#B3r)3D1f~+U$6vWtMT%El1EYp4Hi3U0T4cNAG=Sz^&Qc zL<(0@A?mUll;nzaOPx^B3Mz z&Oc4~8H;yQjeue$; zXMW6L{>;M^E{gf#*8G^o9F&K#0bkR2+5FZ|7XlYL6A!pYcuC-QpIS&;=q5bchoxyh ndCNlTLPPl item.dataset.modelName === modelName)) { + return; + } + const modelItem = document.createElement('div'); + modelItem.className = 'model-item'; + modelItem.dataset.modelName = modelName; + modelItem.innerHTML = ` +
+ ${modelName} + +
+ `; + + // 绑定删除事件 + modelItem.querySelector('.delete-model').addEventListener('click', () => { + deleteModel(modelName); + }); + + modelsList.appendChild(modelItem); +} + +// 清空所有模型 +async function clearAllModels() { + try { + const modelInfos = await tf.io.listModels(); + const deletePromises = Object.keys(modelInfos) + .map(path => path.replace('indexeddb://', '')) + .map(modelName => tf.io.removeModel(`indexeddb://${modelName}`)); + + await Promise.all(deletePromises); + document.getElementById('imported-models').innerHTML = ''; + } catch (error) { + console.error('清空模型失败:', error); + alert('清空模型失败: ' + error.message); + } +} + +// 加载并显示所有模型 +async function loadAndDisplayAllModels() { + try { + const modelInfos = await tf.io.listModels(); + document.getElementById('imported-models').innerHTML = ''; + for (const [path] of Object.entries(modelInfos)) { + const modelName = path.replace('indexeddb://', ''); + displayModelItem(modelName); + } + } catch (error) { + console.error('加载模型列表失败:', error); + } +} + +async function createModal() { + const overlay = document.createElement('div'); + overlay.id = 'modalOverlay'; + Object.assign(overlay.style, { + display: 'none', + position: 'fixed', + top: '0', + left: '0', + width: '100%', + height: '100%', + backgroundColor: 'rgba(0,0,0,0.5)', + zIndex: '20011216', + pointerEvents: 'auto' + }); + const content = document.createElement('div'); + Object.assign(content.style, { + backgroundColor: 'white', + width: '60%', + maxHeight: '80%', + margin: '12vh auto', + padding: '20px 30px', + borderRadius: '12px' + }); + content.innerHTML = ` +

选择本地模型

+
+ + +
+ 导入模型名称: + + +
+
+
+ ❌ 模型结构描述文件(model.json) + +
未选择
+
+
+ ❌ 权重文件(model.weights.bin) +
0 个已选择
+
+
+
+
+
+

已导入模型

+
+ + +
+
+
+
加载中...
+
+
+
+ +
+
+ `; + overlay.appendChild(content); + document.body.appendChild(overlay); + + content.querySelector('.close-btn').addEventListener('click', closeModal); + overlay.addEventListener('click', (e) => { + if (e.target === overlay) closeModal(); + }); + // 获取DOM元素 + const modelUpload = document.getElementById('model-upload'); + const modelHandle = document.getElementById('model-handle'); + const outputDiv = document.getElementById('output'); + + let jsonFile = null; + let weightFiles = []; + + modelUpload.addEventListener('change', async (event) => { + const files = event.target.files; + + // 获取状态元素 + const jsonStatus = document.getElementById('json-status'); + const weightsStatus = document.getElementById('weights-status'); + + // 重置状态显示(保持完整文件名描述) + jsonStatus.querySelector('span').textContent = '❌ 模型结构描述文件(model.json)'; + jsonStatus.querySelector('div').textContent = '未选择'; + weightsStatus.querySelector('span').textContent = '❌ 权重文件(model.weights.bin)'; + weightsStatus.querySelector('div').textContent = '0 个已选择'; + + // 分离 JSON 和权重文件 + weightFiles = []; + for (let i = 0; i < files.length; i++) { + if (files[i].name.endsWith('.json')) { + jsonFile = files[i]; + } else { + weightFiles.push(files[i]); + } + } + + if (!jsonFile) { + alert('未找到 model.json 文件'); + return; + } + + outputDiv.innerHTML = '正在处理上传的模型文件...'; + + if (jsonFile) { + jsonStatus.querySelector('span').textContent = '✅ 模型结构描述文件(model.json)'; + jsonStatus.querySelector('div').textContent = '已选择'; + const modelName = jsonFile.name.replace('.json', ''); + document.getElementById('model-name').value = modelName; + } + + if (weightFiles.length > 0) { + weightsStatus.querySelector('span').textContent = '✅ 权重文件(model.weights.bin)'; + weightsStatus.querySelector('div').textContent = `${weightFiles.length} 个已选择`; + } + }); + + modelHandle.addEventListener('click', async () => { + try { + const modelNameInput = document.getElementById('model-name'); + const modelName = modelNameInput.value || 'mixly-model'; + + const model = await tf.loadLayersModel( + tf.io.browserFiles([jsonFile, ...weightFiles]) + ); + await model.save(`indexeddb://${modelName}`); + loadAndDisplayAllModels(); + outputDiv.innerHTML = `模型已成功保存为 ${modelName}!`; + } catch (error) { + outputDiv.innerHTML = `保存模型出错: ${error.message}`; + console.error(error); + } + }) + + content.querySelector('#refresh-models').addEventListener('click', loadAndDisplayAllModels); + content.querySelector('#clear-models').addEventListener('click', async () => { + if (confirm('确定要删除所有模型吗?此操作不可恢复!')) { + await clearAllModels(); + } + }); +} + +createModal(); + +await loadAndDisplayAllModels(); + +function openModal() { + loadAndDisplayAllModels(); + document.getElementById('modalOverlay').style.display = 'block'; +} + +const workspace = Blockly.getMainWorkspace(); +workspace.registerButtonCallback('handleModels', function () { + openModal(); +}); + + +async function prepare_qmyixtxi(imgTensor) { + let net = null; + + if (window.featureExtractor) { + net = window.featureExtractor; + } else { + net = await tf.loadGraphModel("../common/media/tfmodel/model.json"); + window.featureExtractor = net; + } + const preprocessedImg = imgTensor + .resizeBilinear([224, 224]) + .toFloat() + .div(tf.scalar(127.5)) + .sub(tf.scalar(1)) + .expandDims(0); + + const features = featureExtractor.predict(preprocessedImg); + + let activation = features; + return activation; +} +window.prepare_qmyixtxi = prepare_qmyixtxi; diff --git a/boards/default_src/python_pyodide/others/teachableMachine/App.vue b/boards/default_src/python_pyodide/others/teachableMachine/App.vue index d5208002..2ae86a68 100644 --- a/boards/default_src/python_pyodide/others/teachableMachine/App.vue +++ b/boards/default_src/python_pyodide/others/teachableMachine/App.vue @@ -8,31 +8,6 @@ import teachableModel from './components/teachableModel.vue'; // import 'element-plus/theme-chalk/el-notification.css'; // import './styles/index.scss'; - -const userInfo = reactive({ - is_Login: false, - username: '', - password: '', -}) - -const apiurl = ref('http://127.0.0.1:5174') -provide('apiurl', apiurl.value) - -provide('userInfo', userInfo) -onMounted(() => { - // 获取当前的LS里的已经登录的信息 - if ( - localStorage.getItem('myAIplatformUsername') - && localStorage.getItem('myAIplatformPassword') - ) { - userInfo.is_Login = true - userInfo.username = localStorage.getItem('myAIplatformUsername') - userInfo.password = localStorage.getItem('myAIplatformPassword') - } - else { - userInfo.is_Login = false - } -})