Przeglądaj źródła

only for ONNX. tflite is still not working

Dr-Swopt 2 tygodni temu
rodzic
commit
d02d415981

+ 2 - 2
frontend/angular.json

@@ -43,8 +43,8 @@
               "budgets": [
                 {
                   "type": "initial",
-                  "maximumWarning": "500kB",
-                  "maximumError": "1MB"
+                  "maximumWarning": "2MB",
+                  "maximumError": "4MB"
                 },
                 {
                   "type": "anyComponentStyle",

+ 413 - 37
frontend/package-lock.json

@@ -14,6 +14,7 @@
         "@angular/forms": "^20.0.0",
         "@angular/platform-browser": "^20.0.0",
         "@angular/router": "^20.0.0",
+        "@tensorflow/tfjs": "^4.22.0",
         "@tensorflow/tfjs-tflite": "^0.0.1-alpha.10",
         "onnxruntime-web": "^1.24.3",
         "rxjs": "~7.8.0",
@@ -3570,6 +3571,28 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/@tensorflow/tfjs": {
+      "version": "4.22.0",
+      "resolved": "https://registry.npmjs.org/@tensorflow/tfjs/-/tfjs-4.22.0.tgz",
+      "integrity": "sha512-0TrIrXs6/b7FLhLVNmfh8Sah6JgjBPH4mZ8JGb7NU6WW+cx00qK5BcAZxw7NCzxj6N8MRAIfHq+oNbPUNG5VAg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@tensorflow/tfjs-backend-cpu": "4.22.0",
+        "@tensorflow/tfjs-backend-webgl": "4.22.0",
+        "@tensorflow/tfjs-converter": "4.22.0",
+        "@tensorflow/tfjs-core": "4.22.0",
+        "@tensorflow/tfjs-data": "4.22.0",
+        "@tensorflow/tfjs-layers": "4.22.0",
+        "argparse": "^1.0.10",
+        "chalk": "^4.1.0",
+        "core-js": "3.29.1",
+        "regenerator-runtime": "^0.13.5",
+        "yargs": "^16.0.3"
+      },
+      "bin": {
+        "tfjs-custom-module": "dist/tools/custom_module/cli.js"
+      }
+    },
     "node_modules/@tensorflow/tfjs-core": {
       "version": "4.9.0",
       "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-4.9.0.tgz",
@@ -3602,6 +3625,239 @@
         "@tensorflow/tfjs-core": "4.9.0"
       }
     },
+    "node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-backend-cpu": {
+      "version": "4.22.0",
+      "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-cpu/-/tfjs-backend-cpu-4.22.0.tgz",
+      "integrity": "sha512-1u0FmuLGuRAi8D2c3cocHTASGXOmHc/4OvoVDENJayjYkS119fcTcQf4iHrtLthWyDIPy3JiPhRrZQC9EwnhLw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/seedrandom": "^2.4.28",
+        "seedrandom": "^3.0.5"
+      },
+      "engines": {
+        "yarn": ">= 1.3.2"
+      },
+      "peerDependencies": {
+        "@tensorflow/tfjs-core": "4.22.0"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-backend-webgl": {
+      "version": "4.22.0",
+      "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-webgl/-/tfjs-backend-webgl-4.22.0.tgz",
+      "integrity": "sha512-H535XtZWnWgNwSzv538czjVlbJebDl5QTMOth4RXr2p/kJ1qSIXE0vZvEtO+5EC9b00SvhplECny2yDewQb/Yg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@tensorflow/tfjs-backend-cpu": "4.22.0",
+        "@types/offscreencanvas": "~2019.3.0",
+        "@types/seedrandom": "^2.4.28",
+        "seedrandom": "^3.0.5"
+      },
+      "engines": {
+        "yarn": ">= 1.3.2"
+      },
+      "peerDependencies": {
+        "@tensorflow/tfjs-core": "4.22.0"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-backend-webgl/node_modules/@types/offscreencanvas": {
+      "version": "2019.3.0",
+      "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.3.0.tgz",
+      "integrity": "sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q==",
+      "license": "MIT"
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-converter": {
+      "version": "4.22.0",
+      "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-4.22.0.tgz",
+      "integrity": "sha512-PT43MGlnzIo+YfbsjM79Lxk9lOq6uUwZuCc8rrp0hfpLjF6Jv8jS84u2jFb+WpUeuF4K33ZDNx8CjiYrGQ2trQ==",
+      "license": "Apache-2.0",
+      "peerDependencies": {
+        "@tensorflow/tfjs-core": "4.22.0"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-core": {
+      "version": "4.22.0",
+      "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-4.22.0.tgz",
+      "integrity": "sha512-LEkOyzbknKFoWUwfkr59vSB68DMJ4cjwwHgicXN0DUi3a0Vh1Er3JQqCI1Hl86GGZQvY8ezVrtDIvqR1ZFW55A==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/long": "^4.0.1",
+        "@types/offscreencanvas": "~2019.7.0",
+        "@types/seedrandom": "^2.4.28",
+        "@webgpu/types": "0.1.38",
+        "long": "4.0.0",
+        "node-fetch": "~2.6.1",
+        "seedrandom": "^3.0.5"
+      },
+      "engines": {
+        "yarn": ">= 1.3.2"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-data": {
+      "version": "4.22.0",
+      "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-data/-/tfjs-data-4.22.0.tgz",
+      "integrity": "sha512-dYmF3LihQIGvtgJrt382hSRH4S0QuAp2w1hXJI2+kOaEqo5HnUPG0k5KA6va+S1yUhx7UBToUKCBHeLHFQRV4w==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/node-fetch": "^2.1.2",
+        "node-fetch": "~2.6.1",
+        "string_decoder": "^1.3.0"
+      },
+      "peerDependencies": {
+        "@tensorflow/tfjs-core": "4.22.0",
+        "seedrandom": "^3.0.5"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/@tensorflow/tfjs-layers": {
+      "version": "4.22.0",
+      "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-layers/-/tfjs-layers-4.22.0.tgz",
+      "integrity": "sha512-lybPj4ZNj9iIAPUj7a8ZW1hg8KQGfqWLlCZDi9eM/oNKCCAgchiyzx8OrYoWmRrB+AM6VNEeIT+2gZKg5ReihA==",
+      "license": "Apache-2.0 AND MIT",
+      "peerDependencies": {
+        "@tensorflow/tfjs-core": "4.22.0"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/@webgpu/types": {
+      "version": "0.1.38",
+      "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.38.tgz",
+      "integrity": "sha512-7LrhVKz2PRh+DD7+S+PVaFd5HxaWQvoMqBbsV9fNJO1pjUs1P8bM2vQVNfk+3URTqbuTI7gkXi0rfsN0IadoBA==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/cliui": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.0",
+        "wrap-ansi": "^7.0.0"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "license": "MIT"
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/yargs": {
+      "version": "16.2.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+      "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+      "license": "MIT",
+      "dependencies": {
+        "cliui": "^7.0.2",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.0",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^20.2.2"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@tensorflow/tfjs/node_modules/yargs-parser": {
+      "version": "20.2.9",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+      "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/@tufjs/canonical-json": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz",
@@ -3693,8 +3949,7 @@
       "version": "4.0.2",
       "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
       "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==",
-      "license": "MIT",
-      "peer": true
+      "license": "MIT"
     },
     "node_modules/@types/node": {
       "version": "25.5.2",
@@ -3705,19 +3960,27 @@
         "undici-types": "~7.18.0"
       }
     },
+    "node_modules/@types/node-fetch": {
+      "version": "2.6.13",
+      "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz",
+      "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "form-data": "^4.0.4"
+      }
+    },
     "node_modules/@types/offscreencanvas": {
       "version": "2019.7.3",
       "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz",
       "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==",
-      "license": "MIT",
-      "peer": true
+      "license": "MIT"
     },
     "node_modules/@types/seedrandom": {
       "version": "2.4.34",
       "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-2.4.34.tgz",
       "integrity": "sha512-ytDiArvrn/3Xk6/vtylys5tlY6eo7Ane0hvcx++TKo6RxQXuVfW0AF/oeWqAj9dN29SyhtawuXstgmPlwNcv/A==",
-      "license": "MIT",
-      "peer": true
+      "license": "MIT"
     },
     "node_modules/@types/webgl-ext": {
       "version": "0.0.30",
@@ -3927,6 +4190,21 @@
         "url": "https://github.com/sponsors/jonschlinkert"
       }
     },
+    "node_modules/argparse": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+      "license": "MIT",
+      "dependencies": {
+        "sprintf-js": "~1.0.2"
+      }
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+      "license": "MIT"
+    },
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -4190,7 +4468,6 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
       "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "es-errors": "^1.3.0",
@@ -4377,7 +4654,6 @@
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
       "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "color-name": "~1.1.4"
@@ -4390,7 +4666,6 @@
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
       "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-      "dev": true,
       "license": "MIT"
     },
     "node_modules/colorette": {
@@ -4400,6 +4675,18 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
     "node_modules/concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -4543,6 +4830,17 @@
         "node": ">=6.6.0"
       }
     },
+    "node_modules/core-js": {
+      "version": "3.29.1",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.1.tgz",
+      "integrity": "sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/core-js"
+      }
+    },
     "node_modules/cors": {
       "version": "2.8.6",
       "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
@@ -4641,6 +4939,15 @@
         }
       }
     },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/depd": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -4756,7 +5063,6 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
       "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "call-bind-apply-helpers": "^1.0.1",
@@ -4940,7 +5246,6 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
       "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">= 0.4"
@@ -4950,7 +5255,6 @@
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
       "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">= 0.4"
@@ -4960,7 +5264,6 @@
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
       "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "es-errors": "^1.3.0"
@@ -4969,6 +5272,21 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/es-set-tostringtag": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+      "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.6",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/esbuild": {
       "version": "0.25.9",
       "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
@@ -5015,7 +5333,6 @@
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
       "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=6"
@@ -5256,6 +5573,43 @@
         }
       }
     },
+    "node_modules/form-data": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+      "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "es-set-tostringtag": "^2.1.0",
+        "hasown": "^2.0.2",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/form-data/node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/form-data/node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
     "node_modules/forwarded": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -5330,7 +5684,6 @@
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
       "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
-      "dev": true,
       "license": "MIT",
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
@@ -5350,7 +5703,6 @@
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
       "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
-      "dev": true,
       "license": "ISC",
       "engines": {
         "node": "6.* || 8.* || >= 10.*"
@@ -5373,7 +5725,6 @@
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
       "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "call-bind-apply-helpers": "^1.0.2",
@@ -5398,7 +5749,6 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
       "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "dunder-proto": "^1.0.1",
@@ -5454,7 +5804,6 @@
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
       "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">= 0.4"
@@ -5480,7 +5829,6 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
       "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -5490,7 +5838,6 @@
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
       "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">= 0.4"
@@ -5503,7 +5850,6 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
       "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "has-symbols": "^1.0.3"
@@ -5519,7 +5865,6 @@
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
       "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "function-bind": "^1.1.2"
@@ -6793,8 +7138,7 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
       "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
-      "license": "Apache-2.0",
-      "peer": true
+      "license": "Apache-2.0"
     },
     "node_modules/lru-cache": {
       "version": "5.1.1",
@@ -6870,7 +7214,6 @@
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
       "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">= 0.4"
@@ -7224,7 +7567,6 @@
       "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz",
       "integrity": "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==",
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "whatwg-url": "^5.0.0"
       },
@@ -8035,11 +8377,16 @@
       "dev": true,
       "license": "Apache-2.0"
     },
+    "node_modules/regenerator-runtime": {
+      "version": "0.13.11",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+      "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
+      "license": "MIT"
+    },
     "node_modules/require-directory": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
       "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=0.10.0"
@@ -8205,6 +8552,26 @@
         "tslib": "^2.1.0"
       }
     },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
     "node_modules/safe-regex-test": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
@@ -8255,8 +8622,7 @@
       "version": "3.0.5",
       "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz",
       "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==",
-      "license": "MIT",
-      "peer": true
+      "license": "MIT"
     },
     "node_modules/semver": {
       "version": "7.7.2",
@@ -8670,6 +9036,12 @@
       "dev": true,
       "license": "CC0-1.0"
     },
+    "node_modules/sprintf-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+      "license": "BSD-3-Clause"
+    },
     "node_modules/ssri": {
       "version": "13.0.1",
       "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.1.tgz",
@@ -8721,6 +9093,15 @@
         "node": ">=8.0"
       }
     },
+    "node_modules/string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
     "node_modules/string-width": {
       "version": "7.2.0",
       "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
@@ -8759,7 +9140,6 @@
       "version": "7.2.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
       "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "has-flag": "^4.0.0"
@@ -8862,8 +9242,7 @@
       "version": "0.0.3",
       "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
       "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
-      "license": "MIT",
-      "peer": true
+      "license": "MIT"
     },
     "node_modules/tslib": {
       "version": "2.8.1",
@@ -9157,15 +9536,13 @@
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
       "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
-      "license": "BSD-2-Clause",
-      "peer": true
+      "license": "BSD-2-Clause"
     },
     "node_modules/whatwg-url": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
       "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "tr46": "~0.0.3",
         "webidl-conversions": "^3.0.0"
@@ -9306,7 +9683,6 @@
       "version": "5.0.8",
       "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
       "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
-      "dev": true,
       "license": "ISC",
       "engines": {
         "node": ">=10"

+ 1 - 0
frontend/package.json

@@ -26,6 +26,7 @@
     "@angular/forms": "^20.0.0",
     "@angular/platform-browser": "^20.0.0",
     "@angular/router": "^20.0.0",
+    "@tensorflow/tfjs": "^4.22.0",
     "@tensorflow/tfjs-tflite": "^0.0.1-alpha.10",
     "onnxruntime-web": "^1.24.3",
     "rxjs": "~7.8.0",

+ 7 - 5
frontend/src/app/components/analyzer/analyzer.component.ts

@@ -96,11 +96,13 @@ export class AnalyzerComponent implements OnInit {
       const imageData = await this.imageProcessor.processImage(this.selectedFile);
 
       // 3. Run Inference locally
-      const outputTensor = await this.localInference.runInference(imageData);
-      const rawData = outputTensor.data as Float32Array;
+      const rawData = await this.localInference.runInference(imageData);
+
+      if (!rawData) {
+        throw new Error("Inference failed to return data");
+      }
 
       // 4. Decode Tensor -> Detections
-      // PASS img.width and img.height here
       const detections = this.localInference.parseDetections(
         rawData, 
         this.confidence, 
@@ -181,11 +183,11 @@ export class AnalyzerComponent implements OnInit {
       this.results.detections.forEach((det: any) => {
         const [x1, y1, x2, y2] = det.box.map((v: number) => v * scale);
         
-        ctx.strokeStyle = det.class === 'Ripe' ? '#00A651' : (det.is_health_alert ? '#DC3545' : '#FFD700');
+        ctx.strokeStyle = det.color;
         ctx.lineWidth = 3;
         ctx.strokeRect(x1, y1, x2 - x1, y2 - y1);
 
-        ctx.fillStyle = ctx.strokeStyle;
+        ctx.fillStyle = det.color;
         ctx.font = '14px Outfit';
         const label = `${det.class} ${Math.round(det.confidence * 100)}%`;
         const textWidth = ctx.measureText(label).width;

+ 5 - 3
frontend/src/app/components/history/history.component.html

@@ -47,9 +47,11 @@
               <div *ngFor="let det of record.detections" 
                    class="box-overlay"
                    [ngStyle]="getBoxStyles(det.box, record.dimensions)"
-                   [class.ripe]="det.class === 'Ripe'"
-                   [class.alert]="det.is_health_alert">
-                <span class="box-label">{{ det.class }} {{ (det.confidence * 100).toFixed(0) }}%</span>
+                   [style.border-color]="det.color"
+                   [style.background-color]="det.color + '33'">
+                <span class="box-label" [style.background-color]="det.color">
+                  {{ det.class }} {{ (det.confidence * 100).toFixed(0) }}%
+                </span>
               </div>
             </div>
           </div>

+ 59 - 31
frontend/src/app/services/local-inference.service.ts

@@ -1,39 +1,69 @@
 import { Injectable } from '@angular/core';
 import * as ort from 'onnxruntime-web';
+import * as tflite from '@tensorflow/tfjs-tflite/dist/tf-tflite.fesm.js';
 
 @Injectable({ providedIn: 'root' })
 export class LocalInferenceService {
-  private session: ort.InferenceSession | null = null;
+  private onnxSession: ort.InferenceSession | null = null;
+  private tfliteModel: any | null = null;
+
+  // Standardized Industrial Color Scheme
+  private readonly GRADE_COLORS: { [key: string]: string } = {
+    'Empty_Bunch': '#6C757D', // Gray
+    'Underripe': '#F9A825',   // Amber
+    'Abnormal': '#DC3545',    // Red
+    'Ripe': '#00A651',        // Green
+    'Unripe': '#9E9D24',      // Lime
+    'Overripe': '#5D4037'     // Brown
+  };
 
   async loadModel(modelPath: string) {
-    // Explicitly set the WASM path
-    ort.env.wasm.wasmPaths = '/assets/wasm/';
-    
-    this.session = await ort.InferenceSession.create(modelPath, {
-      executionProviders: ['wasm'], // Start with WASM for stability
-      graphOptimizationLevel: 'all'
-    });
-    console.log('Model loaded successfully');
+    if (modelPath.endsWith('.onnx')) {
+      // Explicitly set the WASM path for ONNX
+      ort.env.wasm.wasmPaths = '/assets/wasm/';
+      
+      this.onnxSession = await ort.InferenceSession.create(modelPath, {
+        executionProviders: ['wasm'], // Start with WASM for stability
+        graphOptimizationLevel: 'all'
+      });
+      this.tfliteModel = null;
+      console.log('ONNX Model loaded successfully');
+    } else {
+      // CRITICAL: Set this BEFORE loading the model
+      tflite.setWasmPath('/assets/tflite-wasm/'); 
+      this.tfliteModel = await tflite.loadTFLiteModel(modelPath);
+      this.onnxSession = null;
+      console.log('TFLite Model loaded successfully');
+    }
   }
 
-  async runInference(input: Float32Array): Promise<ort.Tensor> {
-    if (!this.session) throw new Error('Model not loaded');
-
-    // Create the tensor: [BatchSize, Channels, Height, Width]
-    const tensor = new ort.Tensor('float32', input, [1, 3, 640, 640]);
-    
-    // Execute the model logic
-    const feeds: any = {};
-    feeds[this.session.inputNames[0]] = tensor;
-    const output = await this.session.run(feeds);
-    
-    return output[this.session.outputNames[0]];
+  async runInference(input: Float32Array): Promise<any> {
+    if (this.onnxSession) {
+      // Create the tensor: [BatchSize, Channels, Height, Width]
+      const tensor = new ort.Tensor('float32', input, [1, 3, 640, 640]);
+      
+      // Execute the model logic
+      const feeds: any = {};
+      feeds[this.onnxSession.inputNames[0]] = tensor;
+      const output = await this.onnxSession.run(feeds);
+      
+      return output[this.onnxSession.outputNames[0]].data;
+    } else if (this.tfliteModel) {
+      // TFLite expects [1, 3, 640, 640] usually in this PoC export
+      const output = await this.tfliteModel.predict(input);
+      return output.data();
+    }
+    throw new Error('No model loaded');
   }
 
-  parseDetections(tensorData: Float32Array, threshold: number, imgWidth: number, imgHeight: number): any[] {
-    const detections = [];
-    const numBoxes = 300; // Based on your dims [1, 300, 6]
+  parseDetections(rawData: any, threshold: number, imgWidth: number, imgHeight: number): any[] {
+    if (!rawData) return [];
     
+    // Ensure we are working with a Float32Array
+    const tensorData = rawData instanceof Float32Array ? rawData : new Float32Array(rawData);
+    const detections = [];
+    const numBoxes = tensorData.length / 6; // Dynamically calculate based on data size
+
     for (let i = 0; i < numBoxes; i++) {
         const offset = i * 6;
         const confidence = tensorData[offset + 4];
@@ -43,17 +73,15 @@ export class LocalInferenceService {
             const className = this.getClassName(classId);
 
             detections.push({
-                // Coordinate Scaling: Convert 0.0-1.0 normalized values to absolute pixels
-                // This matches your Python logic: abs_x1 = x1 * orig_w
                 box: [
-                    tensorData[offset + 0] * imgWidth,  // x1
-                    tensorData[offset + 1] * imgHeight, // y1
-                    tensorData[offset + 2] * imgWidth,  // x2
-                    tensorData[offset + 3] * imgHeight  // y2
+                    tensorData[offset + 0] * imgWidth,
+                    tensorData[offset + 1] * imgHeight,
+                    tensorData[offset + 2] * imgWidth,
+                    tensorData[offset + 3] * imgHeight
                 ],
                 confidence: confidence,
                 class: className,
-                // Health Alert: Abnormal (2) and Empty_Bunch (0) per data.yaml
+                color: this.GRADE_COLORS[className] || '#000000',
                 is_health_alert: className === 'Abnormal' || className === 'Empty_Bunch'
             });
         }

+ 13 - 0
frontend/src/app/services/tflite.d.ts

@@ -0,0 +1,13 @@
+declare module '@tensorflow/tfjs-tflite/dist/tf-tflite.fesm.js' {
+  export function setWasmPath(path: string): void;
+  export function loadTFLiteModel(path: string | ArrayBuffer): Promise<any>;
+}
+
+declare module '@tensorflow/tfjs-tflite' {
+  export function setWasmPath(path: string): void;
+  export function loadTFLiteModel(path: string | ArrayBuffer): Promise<any>;
+}
+
+declare module '@tensorflow/tfjs-tfliteSource' {
+  export * from '@tensorflow/tfjs-tflite';
+}

BIN
frontend/src/assets/tflite-wasm/tflite_web_api_cc.wasm


BIN
frontend/src/assets/tflite-wasm/tflite_web_api_cc_simd.wasm


BIN
frontend/src/assets/tflite-wasm/tflite_web_api_cc_simd_threaded.wasm


BIN
frontend/src/assets/tflite-wasm/tflite_web_api_cc_threaded.wasm