diff --git a/backend/eng.traineddata b/backend/eng.traineddata new file mode 100644 index 0000000..6d11002 Binary files /dev/null and b/backend/eng.traineddata differ diff --git a/backend/package-lock.json b/backend/package-lock.json index 354d682..624e3b2 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -12,7 +12,8 @@ "@prisma/client": "^5.6.0", "cors": "^2.8.5", "dotenv": "^16.3.1", - "express": "^4.18.2" + "express": "^4.18.2", + "tesseract.js": "^5.0.4" }, "devDependencies": { "@types/chai": "^4.3.10", @@ -315,6 +316,11 @@ "node": ">=8" } }, + "node_modules/bmp-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", + "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==" + }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -1096,6 +1102,11 @@ "node": ">=0.10.0" } }, + "node_modules/idb-keyval": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz", + "integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==" + }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", @@ -1137,6 +1148,11 @@ "node": ">=8" } }, + "node_modules/is-electron": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.2.tgz", + "integrity": "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==" + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1197,6 +1213,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==" + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -1463,6 +1484,25 @@ "node": ">= 0.6" } }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/nodemon": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz", @@ -1566,6 +1606,14 @@ "wrappy": "1" } }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "bin": { + "opencollective-postinstall": "index.js" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -1739,6 +1787,11 @@ "node": ">=8.10.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==" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -2009,6 +2062,29 @@ "node": ">=4" } }, + "node_modules/tesseract.js": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/tesseract.js/-/tesseract.js-5.0.4.tgz", + "integrity": "sha512-GCIoSQMZlvTP2AaHrjUOH29/oyO7ZyHVe+BhTexEcO7/nDClRVDRjl2sYJLOWSSNbTDrm5q2m1+gfaf3lUrZ5Q==", + "hasInstallScript": true, + "dependencies": { + "bmp-js": "^0.1.0", + "idb-keyval": "^6.2.0", + "is-electron": "^2.2.2", + "is-url": "^1.2.4", + "node-fetch": "^2.6.9", + "opencollective-postinstall": "^2.0.3", + "regenerator-runtime": "^0.13.3", + "tesseract.js-core": "^5.0.0", + "wasm-feature-detect": "^1.2.11", + "zlibjs": "^0.3.1" + } + }, + "node_modules/tesseract.js-core": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-5.0.0.tgz", + "integrity": "sha512-lJur5LzjinW5VYMKlVNnBU2JPLpO+A9VqAYBeuV+ZgH0hKvsnm+536Yyp+/zRTBdLe7D6Kok0FN9g+TE4J8qGA==" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2041,6 +2117,11 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -2111,6 +2192,25 @@ "node": ">= 0.8" } }, + "node_modules/wasm-feature-detect": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.6.1.tgz", + "integrity": "sha512-R1i9ED8UlLu/foILNB1ck9XS63vdtqU/tP1MCugVekETp/ySCrBZRk5I/zI67cI1wlQYeSonNm1PLjDHZDNg6g==" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "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==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/workerpool": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", @@ -2208,6 +2308,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zlibjs": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz", + "integrity": "sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==", + "engines": { + "node": "*" + } } } } diff --git a/backend/package.json b/backend/package.json index 61e1f14..fc36644 100644 --- a/backend/package.json +++ b/backend/package.json @@ -17,7 +17,8 @@ "@prisma/client": "^5.6.0", "cors": "^2.8.5", "dotenv": "^16.3.1", - "express": "^4.18.2" + "express": "^4.18.2", + "tesseract.js": "^5.0.4" }, "devDependencies": { "@types/chai": "^4.3.10", diff --git a/backend/src/controllers/scanner.controller.ts b/backend/src/controllers/scanner.controller.ts new file mode 100644 index 0000000..9cf8f9e --- /dev/null +++ b/backend/src/controllers/scanner.controller.ts @@ -0,0 +1,30 @@ +import { Request, Response, NextFunction, response } from "express"; +import ScannerService from "../services/scanner.service.js"; + +class ScannerController { + constructor(private service: ScannerService) { + this.service = service; + } + postMaterials = async (//must send image as tag "image" and value base64 string. Returns JSON with the percentage and material of each material found on the tag + req: Request, + res: Response, + next: NextFunction + ): Promise> | void> => { + try { + console.log("postMaterials called"); + let materials = await (async () => { + var str; + str = await this.service.getMaterials(await this.service.getTextFromImage(req.body.image)); + // console.log("Fuck: ", await str); + return str; + })(); + console.log("materials: ",materials); + return res.status(201).json(JSON.parse(materials)); + } catch (e) { + console.log("Error: ", e); + next(e); + } + }; +} + +export default ScannerController; \ No newline at end of file diff --git a/backend/src/index.ts b/backend/src/index.ts index 81ed20c..facd85b 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -5,14 +5,17 @@ import errorHandler from "./middlewares/error.middleware.js"; import { PrismaClient } from "@prisma/client"; import algorithmRouter from "./routes/algorithm.routes.js"; import productRouter from "./routes/product.routes.js"; +import scannerRouter from "./routes/scanner.routes.js"; export const app: Express = express(); export const prisma = new PrismaClient(); -app.use(express.json()); +app.use(express.json({ limit: '50mb' })); app.use(cors()); +app.use("/api/v1/scanner", scannerRouter); + app.use("/api/v1/algorithm", algorithmRouter); app.use("/api/v1/products", productRouter); diff --git a/backend/src/routes/scanner.routes.ts b/backend/src/routes/scanner.routes.ts new file mode 100644 index 0000000..430159b --- /dev/null +++ b/backend/src/routes/scanner.routes.ts @@ -0,0 +1,11 @@ +import { Router } from "express"; +import ScannerController from "../controllers/scanner.controller.js"; +import ScannerService from "../services/scanner.service.js"; + +const scannerRouter = Router(); + +const scannerController = new ScannerController(new ScannerService()); + +scannerRouter.post("/", scannerController.postMaterials); + +export default scannerRouter; \ No newline at end of file diff --git a/backend/src/services/scanner.service.ts b/backend/src/services/scanner.service.ts new file mode 100644 index 0000000..a6c54ee --- /dev/null +++ b/backend/src/services/scanner.service.ts @@ -0,0 +1,51 @@ +import Tesseract from 'tesseract.js'; //Javascript OCR + +type Tag = { + material: string; + percentage: string; +} + +class ScannerService{ + + getTextFromImage = async(imagePath: string): Promise => { + console.log("getTextFromImage called"); + // return "20% Cotton 40% Shit 40% Rizz"; + return new Promise((resolve, reject) => { + console.log("Test1"); + Tesseract.recognize(imagePath,'eng',{ + logger: info => console.log(info), // optional logger function + }) + .then(({ data: { text } }) => { + console.log("Test2"); + resolve(text); + console.log("text: ",text); + return text; + }) + .catch((error) => { + console.log("Test2.5"); + reject(error); + }); + }); + } + + getMaterials = async(txt: string): Promise => { + console.log("getMaterials called"); + const regex = /(100|\d{1,2})% *(\b\w+\b)/g; + let m; + let scannedTags: Tag[] = []; + do { + m = regex.exec(txt); + if (m){ + scannedTags.push({ + material: m[2], + percentage: m[1] + }); + } + }while (m); + console.log(JSON.stringify(scannedTags)); + return JSON.stringify(scannedTags); + } +} + + +export default ScannerService; \ No newline at end of file