├── .eslintignore ├── .eslintrc ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ ├── codecov.yml │ ├── codeql-analysis.yml │ ├── lib-test.yml │ ├── njsscan-analysis.yml │ ├── node.js.yml │ └── playground-tests.yml ├── .gitignore ├── .npmignore ├── .replit ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── babel.config.js ├── deploy.sh ├── example.ts ├── index.ts ├── jest.config.js ├── lib ├── build.sh ├── download_models.py ├── download_models.sh ├── install.sh ├── requirements.txt └── tfjs │ └── web_model │ ├── group1-shard1of6.bin │ ├── group1-shard2of6.bin │ ├── group1-shard3of6.bin │ ├── group1-shard4of6.bin │ ├── group1-shard5of6.bin │ ├── group1-shard6of6.bin │ └── model.json ├── media └── sample.gif ├── package-lock.json ├── package.json ├── playground ├── classifications │ └── hotdog.png ├── docker-compose.yml ├── server │ ├── .babelrc │ ├── Dockerfile │ ├── __tests__ │ │ ├── fish.jpg │ │ └── server.test.js │ ├── index.js │ ├── package-lock.json │ ├── package.json │ └── server.js └── ui │ ├── .browserslistrc │ ├── .editorconfig │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── angular.json │ ├── e2e │ ├── protractor.conf.js │ ├── src │ │ ├── app.e2e-spec.ts │ │ └── app.po.ts │ └── tsconfig.json │ ├── karma.conf.js │ ├── nginx.conf │ ├── package.json │ ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.scss │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── header │ │ │ ├── header.component.html │ │ │ ├── header.component.scss │ │ │ ├── header.component.spec.ts │ │ │ └── header.component.ts │ │ ├── imageuploader │ │ │ ├── imageuploader.component.html │ │ │ ├── imageuploader.component.scss │ │ │ ├── imageuploader.component.spec.ts │ │ │ └── imageuploader.component.ts │ │ ├── interfces │ │ │ └── index.ts │ │ ├── languages-api.service.spec.ts │ │ ├── languages-api.service.ts │ │ ├── note │ │ │ ├── note.component.html │ │ │ ├── note.component.scss │ │ │ ├── note.component.spec.ts │ │ │ └── note.component.ts │ │ ├── primeng-imports.ts │ │ ├── results │ │ │ ├── results.component.html │ │ │ ├── results.component.scss │ │ │ ├── results.component.spec.ts │ │ │ └── results.component.ts │ │ ├── single-result │ │ │ ├── single-result.component.html │ │ │ ├── single-result.component.scss │ │ │ ├── single-result.component.spec.ts │ │ │ └── single-result.component.ts │ │ └── uploader │ │ │ ├── uploader.component.html │ │ │ ├── uploader.component.scss │ │ │ ├── uploader.component.spec.ts │ │ │ └── uploader.component.ts │ ├── assets │ │ └── .gitkeep │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── proxy.conf.json │ ├── styles.scss │ └── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.spec.json │ └── tslint.json ├── samples ├── car.jpg ├── fish.jpg ├── gun.jpg └── panda.jpg ├── src ├── EfficientNetCheckPoint.ts ├── EfficientNetCheckPointFactory.ts ├── EfficientNetLanguageProvider.ts ├── EfficientNetResult.ts ├── EfficientnetModel.ts ├── ModelResourcesProvider.ts └── misc │ ├── ar.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── he.json │ ├── ru.json │ └── zh.json ├── tester ├── package.json ├── samples │ ├── car.jpg │ ├── fish.jpg │ ├── gun.jpg │ └── panda.jpg └── tests │ ├── language-provider.test.js │ └── model.test.js ├── tests ├── examples │ ├── fish.gif │ ├── fish.heic │ ├── fish.png │ ├── fish.svg │ └── fish.webp ├── language-provider.test.js └── model.test.js └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | web 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": ["@typescript-eslint", "import", "jest", "prettier"], 5 | "extends": [ 6 | "eslint:recommended", 7 | "plugin:@typescript-eslint/eslint-recommended", 8 | "plugin:@typescript-eslint/recommended", 9 | "plugin:prettier/recommended" 10 | ], 11 | "rules": { 12 | "prettier/prettier": "error", 13 | "import/no-unresolved": "error", 14 | "@typescript-eslint/no-non-null-assertion": "off" 15 | }, 16 | "env": { 17 | "jest/globals": true, 18 | "node": true, 19 | "browser": true 20 | }, 21 | "settings": { 22 | "import/parsers": { 23 | "@typescript-eslint/parser": [".ts", ".tsx"] 24 | }, 25 | "import/resolver": { 26 | "typescript": { 27 | "alwaysTryTypes": true 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | custom: ['https://paypal.me/tedgi'] 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.github/workflows/codecov.yml: -------------------------------------------------------------------------------- 1 | name: CodeCov - Running Code Coverage 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [16.x] 13 | 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v2 17 | with: 18 | fetch-depth: 2 19 | 20 | - name: Set up Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v1 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | 25 | - name: Install dependencies 26 | run: npm install 27 | 28 | - name: Run the tests 29 | run: npm test 30 | 31 | - name: Upload coverage to Codecov 32 | uses: codecov/codecov-action@v3 33 | with: 34 | token: ${{ secrets.CODECOV_TOKEN }} 35 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL - Analysis" 13 | 14 | on: 15 | push: 16 | branches: [ main, pr ] 17 | pull_request: 18 | branches: [ main ] 19 | 20 | jobs: 21 | analyze: 22 | name: Analyze 23 | runs-on: ubuntu-latest 24 | 25 | strategy: 26 | fail-fast: false 27 | matrix: 28 | language: [ 'javascript', 'python' ] 29 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 30 | # Learn more: 31 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 32 | 33 | steps: 34 | - name: Checkout repository 35 | uses: actions/checkout@v2 36 | 37 | # Initializes the CodeQL tools for scanning. 38 | - name: Initialize CodeQL 39 | uses: github/codeql-action/init@v1 40 | with: 41 | languages: ${{ matrix.language }} 42 | # If you wish to specify custom queries, you can do so here or in a config file. 43 | # By default, queries listed here will override any specified in a config file. 44 | # Prefix the list here with "+" to use these queries and those in the config file. 45 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 46 | 47 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 48 | # If this step fails, then you should remove it and run the build manually (see below) 49 | - name: Autobuild 50 | uses: github/codeql-action/autobuild@v1 51 | 52 | # ℹ️ Command-line programs to run using the OS shell. 53 | # 📚 https://git.io/JvXDl 54 | 55 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 56 | # and modify them (or add more) to build your code if your project 57 | # uses a compiled language 58 | 59 | #- run: | 60 | # make bootstrap 61 | # make release 62 | 63 | - name: Perform CodeQL Analysis 64 | uses: github/codeql-action/analyze@v1 65 | -------------------------------------------------------------------------------- /.github/workflows/lib-test.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: LIB Tests 5 | 6 | on: 7 | push: 8 | branches: [main] 9 | pull_request: 10 | branches: [main] 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | node-version: [14.x, 16.x, 18.x] 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v1 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | - run: npm install 25 | - run: npm run build --if-present 26 | - run: npm run test 27 | -------------------------------------------------------------------------------- /.github/workflows/njsscan-analysis.yml: -------------------------------------------------------------------------------- 1 | # This workflow integrates njsscan with GitHub's Code Scanning feature 2 | # nodejsscan is a static security code scanner that finds insecure code patterns in your Node.js applications 3 | 4 | name: NodeJS Scan - Static Application Testing 5 | 6 | on: 7 | push: 8 | branches: [ main ] 9 | pull_request: 10 | branches: [ main ] 11 | 12 | jobs: 13 | njsscan: 14 | runs-on: ubuntu-latest 15 | name: njsscan code scanning 16 | steps: 17 | - name: Checkout the code 18 | uses: actions/checkout@v2 19 | - name: nodejsscan scan 20 | id: njsscan 21 | uses: ajinabraham/njsscan-action@master 22 | with: 23 | args: '. --sarif --output results.sarif || true' 24 | - name: Upload njsscan report 25 | uses: github/codeql-action/upload-sarif@v1 26 | with: 27 | sarif_file: results.sarif 28 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Package Tests 5 | 6 | on: 7 | push: 8 | branches: [ main ] 9 | pull_request: 10 | branches: [ main ] 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | strategy: 17 | matrix: 18 | node-version: [10.x, 12.x, 14.x, 16.x, 18.x] 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Use Node.js ${{ matrix.node-version }} 23 | uses: actions/setup-node@v1 24 | with: 25 | node-version: ${{ matrix.node-version }} 26 | - run: npm install 27 | - run: npm run build --if-present 28 | -------------------------------------------------------------------------------- /.github/workflows/playground-tests.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Playground Tests 5 | 6 | on: 7 | push: 8 | branches: [main] 9 | pull_request: 10 | branches: [main] 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | strategy: 17 | matrix: 18 | node-version: [14.x, 16.x, 18.x] 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Use Node.js ${{ matrix.node-version }} 23 | uses: actions/setup-node@v1 24 | with: 25 | node-version: ${{ matrix.node-version }} 26 | - run: npm run test:playground 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib/model 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | lerna-debug.log* 10 | .idea 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 13 | 14 | # Runtime data 15 | pids 16 | *.pid 17 | *.seed 18 | *.pid.lock 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | *.lcov 26 | 27 | # nyc test coverage 28 | .nyc_output 29 | 30 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 31 | .grunt 32 | 33 | # Bower dependency directory (https://bower.io/) 34 | bower_components 35 | 36 | # node-waf configuration 37 | .lock-wscript 38 | 39 | # Compiled binary addons (https://nodejs.org/api/addons.html) 40 | build/Release 41 | 42 | # Dependency directories 43 | node_modules/ 44 | jspm_packages/ 45 | 46 | # Snowpack dependency directory (https://snowpack.dev/) 47 | web_modules/ 48 | 49 | # TypeScript cache 50 | *.tsbuildinfo 51 | 52 | # Optional npm cache directory 53 | .npm 54 | 55 | # Optional eslint cache 56 | .eslintcache 57 | 58 | # Microbundle cache 59 | .rpt2_cache/ 60 | .rts2_cache_cjs/ 61 | .rts2_cache_es/ 62 | .rts2_cache_umd/ 63 | 64 | # Optional REPL history 65 | .node_repl_history 66 | 67 | # Output of 'npm pack' 68 | *.tgz 69 | 70 | # Yarn Integrity file 71 | .yarn-integrity 72 | 73 | # dotenv environment variables file 74 | .env 75 | .env.test 76 | 77 | # parcel-bundler cache (https://parceljs.org/) 78 | .cache 79 | .parcel-cache 80 | 81 | # Next.js build output 82 | .next 83 | out 84 | 85 | # Nuxt.js build / generate output 86 | .nuxt 87 | dist 88 | 89 | # Gatsby files 90 | .cache/ 91 | # Comment in the public line in if your project uses Gatsby and not Next.js 92 | # https://nextjs.org/blog/next-9-1#public-directory-support 93 | # public 94 | 95 | # vuepress build output 96 | .vuepress/dist 97 | 98 | # Serverless directories 99 | .serverless/ 100 | 101 | # FuseBox cache 102 | .fusebox/ 103 | 104 | # DynamoDB Local files 105 | .dynamodb/ 106 | 107 | # TernJS port file 108 | .tern-port 109 | 110 | # Stores VSCode versions used for testing VSCode extensions 111 | .vscode-test 112 | 113 | # yarn v2 114 | .yarn/cache 115 | .yarn/unplugged 116 | .yarn/build-state.yml 117 | .yarn/install-state.gz 118 | .pnp.* 119 | .vscode -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | lib/ 2 | .idea 3 | example.ts 4 | samples 5 | tests 6 | coverage/* 7 | .coveralls.yml 8 | .travis.yml 9 | .github/* 10 | .replit 11 | *.config.* 12 | playground 13 | misc/s* 14 | web 15 | .eslintrc 16 | .eslintignore 17 | LICENSE 18 | CODE_OF_CONDUCT.md 19 | tester 20 | .vscode 21 | media 22 | *.tar -------------------------------------------------------------------------------- /.replit: -------------------------------------------------------------------------------- 1 | language = "nodejs" 2 | run = "npm run example" 3 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at naor.tedgi@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a code of conduct, please follow it in all your interactions with the project. 7 | 8 | ## Pull Request Process 9 | 10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 11 | build. 12 | 2. Update the README.md with details of changes to the interface, this includes new environment 13 | variables, exposed ports, useful file locations and container parameters. 14 | 3. Increase the version numbers in any examples files and the README.md to the new version that this 15 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). 16 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 17 | do not have permission to do that, you may request the second reviewer to merge it for you. 18 | 19 | ## Code of Conduct 20 | 21 | ### Our Pledge 22 | 23 | In the interest of fostering an open and welcoming environment, we as 24 | contributors and maintainers pledge to making participation in our project and 25 | our community a harassment-free experience for everyone, regardless of age, body 26 | size, disability, ethnicity, gender identity and expression, level of experience, 27 | nationality, personal appearance, race, religion, or sexual identity and 28 | orientation. 29 | 30 | ### Our Standards 31 | 32 | Examples of behavior that contributes to creating a positive environment 33 | include: 34 | 35 | * Using welcoming and inclusive language 36 | * Being respectful of differing viewpoints and experiences 37 | * Gracefully accepting constructive criticism 38 | * Focusing on what is best for the community 39 | * Showing empathy towards other community members 40 | 41 | Examples of unacceptable behavior by participants include: 42 | 43 | * The use of sexualized language or imagery and unwelcome sexual attention or 44 | advances 45 | * Trolling, insulting/derogatory comments, and personal or political attacks 46 | * Public or private harassment 47 | * Publishing others' private information, such as a physical or electronic 48 | address, without explicit permission 49 | * Other conduct which could reasonably be considered inappropriate in a 50 | professional setting 51 | 52 | ### Our Responsibilities 53 | 54 | Project maintainers are responsible for clarifying the standards of acceptable 55 | behavior and are expected to take appropriate and fair corrective action in 56 | response to any instances of unacceptable behavior. 57 | 58 | Project maintainers have the right and responsibility to remove, edit, or 59 | reject comments, commits, code, wiki edits, issues, and other contributions 60 | that are not aligned to this Code of Conduct, or to ban temporarily or 61 | permanently any contributor for other behaviors that they deem inappropriate, 62 | threatening, offensive, or harmful. 63 | 64 | ### Scope 65 | 66 | This Code of Conduct applies both within project spaces and in public spaces 67 | when an individual is representing the project or its community. Examples of 68 | representing a project or community include using an official project e-mail 69 | address, posting via an official social media account, or acting as an appointed 70 | representative at an online or offline event. Representation of a project may be 71 | further defined and clarified by project maintainers. 72 | 73 | ### Enforcement 74 | 75 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 76 | reported by contacting the project team at [INSERT EMAIL ADDRESS]. All 77 | complaints will be reviewed and investigated and will result in a response that 78 | is deemed necessary and appropriate to the circumstances. The project team is 79 | obligated to maintain confidentiality with regard to the reporter of an incident. 80 | Further details of specific enforcement policies may be posted separately. 81 | 82 | Project maintainers who do not follow or enforce the Code of Conduct in good 83 | faith may face temporary or permanent repercussions as determined by other 84 | members of the project's leadership. 85 | 86 | ### Attribution 87 | 88 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 89 | available at [http://contributor-covenant.org/version/1/4][version] 90 | 91 | [homepage]: http://contributor-covenant.org 92 | [version]: http://contributor-covenant.org/version/1/4/ 93 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2010-2020 Google LLC. http://angularjs.org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TensorflowJS EfficientNet 2 | 3 | ![npm](https://img.shields.io/npm/v/node-efficientnet) ![Node.js CI](https://github.com/ntedgi/node-efficientnet/workflows/Node.js%20CI/badge.svg?branch=main) [![codecov](https://codecov.io/gh/ntedgi/node-efficientnet/branch/main/graph/badge.svg?token=gkNPxmyry3)](https://codecov.io/gh/ntedgi/node-efficientnet) 4 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/09917d9ddf9c42648eb60d7d917f5026)](https://www.codacy.com/gh/ntedgi/node-efficientnet/dashboard?utm_source=github.com&utm_medium=referral&utm_content=ntedgi/node-efficientnet&utm_campaign=Badge_Grade) 5 | [![Run on Repl.it](https://repl.it/badge/github/ntedgi/node-efficientnet)](https://repl.it/github/ntedgi/node-efficientnet) 6 | License 7 | Downloads 8 | Downloads 9 | [![Gitter](https://badges.gitter.im/node-efficientnet/community.svg)](https://gitter.im/node-efficientnet/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 10 | 11 | 12 | 13 | This repository contains a tensorflowJs implementation of **EfficientNet**, 14 | an object detection model trained on [ImageNet](http://www.image-net.org/) and can detect [1000 different objects](https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt). 15 | 16 | EfficientNet a lightweight convolutional neural network architecture achieving the [state-of-the-art accuracy with an order of magnitude fewer parameters and FLOPS](https://arxiv.org/abs/1905.11946), on both ImageNet and 17 | five other commonly used transfer learning datasets. 18 | 19 | The codebase is heavily inspired by the [TensorFlow implementation](https://github.com/tensorflow/tpu/tree/master/models/official/efficientnet). 20 | 21 | ![Alt Text](https://raw.githubusercontent.com/ntedgi/node-efficientnet/main/media/sample.gif) 22 | 23 | # 24 | 25 | ## 👏 Supporters 26 | 27 | ### ↳ Stargazers 28 | 29 | [![Stargazers repo roster for @ntedgi/node-efficientnet](https://reporoster.com/stars/ntedgi/node-efficientnet)](https://github.com/ntedgi/node-efficientnet/stargazers) 30 | 31 | ### ↳ Forkers 32 | 33 | [![Forkers repo roster for @ntedgi/node-efficientnet](https://reporoster.com/forks/ntedgi/node-efficientnet)](https://github.com/ntedgi/node-efficientnet/network/members) 34 | 35 | ## Multilingual status 36 | 37 | | locale | status | translate by 👑 | 38 | |:-------:|:--------------------------:|:------------------------------------------:| 39 | | `en` | ✅ | | 40 | | `zh` | ✅ | [@luoye-fe](https://github.com/luoye-fe) | 41 | | `es` | ✅ | [@h383r](https://github.com/h383r) | 42 | | `ar` | ✅ | [@lamamyf](https://github.com/lamamyf) | 43 | | `ru` | ✅ | [@Abhighyaa](https://github.com/Abhighyaa) | 44 | | `he` | ✅ | [@jhonDoe15](https://github.com/jhonDoe15) | 45 | | `fr` | ✅ | [@burmanp](https://github.com/burmanp) | 46 | | `other` | ⏩ (need help, PR welcome ) | | 47 | 48 | ## Table of Contents 49 | 50 | 1. [Just Want to Play With The Model](#how-i-run-this-project-locally-) 51 | 2. [Installation](#installation) 52 | 3. [API](#api) 53 | 4. [Examples](#examples) 54 | 5. [Usage](#usgae) 55 | 6. [About EfficientNet Models](#about-efficientnet-models) 56 | 7. [Models](#models) 57 | 8. [Multilingual status](#multilingual-status) 58 | 59 | ## How I Run This Project Locally ? 60 | 61 | - clone this repository 62 | - Just Want to Play ? 63 | - At the root project go to playground directory, Run: `docker-compose up` 64 | - Navigate to http://localhost:8080 65 | 66 | ## Usage: 67 | 68 | EfficientNet has 8 different model checkpoints each checkpoint as different input layer resolution 69 | for larger input layer resolution, the greater the accuracy and the running time is slower. 70 | 71 | for example lets take this images: 72 | 73 | 74 | 75 | 78 | 94 | 95 | 96 | 99 | 115 | 116 | 117 | 120 | 136 | 137 | 138 | 141 | 157 | 158 | 159 | 160 |
76 | 77 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 |
ModelPrediction
EfficientNetB0('Giant panda',83.607) , ( 'Skunk',11.61) , ('hog',4.772)
EfficientNetB7('Giant panda',90.406) , ( 'American black bear',7.07) , ('Badger',2.5192)
93 |
97 | 98 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 |
ModelPrediction
EfficientNetB3('goldfish, Carassius auratus',82.5) , ( 'starfish, sea star',9.26) , ('corn'',7.33)
EfficientNetB7('goldfish, Carassius auratus',97.5) , ( 'starfish, sea star',1.46) , ('corn'',0.93)
114 |
118 | 119 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 |
ModelPrediction
EfficientNetB0('Sports Car',88.02) , ( 'racing car',6.647) , ('car wheel',5.32)
EfficientNetB7('Sports Car',87.68) , ( 'convertible'',7.831) , ('car wheel',4.485)
135 |
139 | 140 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 |
ModelPrediction
EfficientNetB0('revolver',85.52) , ( 'assault rifle',9.85) , ('rifle',4.6197)
EfficientNetB7('revolver',88.13) , ( 'rifle',8.29) , ('assault rifle',3.56)
156 |
161 | 162 | # 163 | 164 | ## Installation 165 | 166 | ```node 167 | npm i --save node-efficientnet 168 | ``` 169 | 170 | ## API 171 | 172 | ### `EfficientNetCheckPointFactory.create(checkPoint: EfficientNetCheckPoint, options?: EfficientNetCheckPointFactoryOptions): Promise` 173 | 174 | Example: to create an efficientnet model you need to pass `EfficientNetCheckPoint` 175 | (available checkpoint [B0..B7]) each one of them represent different model 176 | 177 | ```javascript 178 | const { 179 | EfficientNetCheckPointFactory, 180 | EfficientNetCheckPoint, 181 | } = require("node-efficientnet"); 182 | 183 | const model = await EfficientNetCheckPointFactory.create( 184 | EfficientNetCheckPoint.B7 185 | ); 186 | 187 | const path2image = "..."; 188 | 189 | const topResults = 5; 190 | 191 | const result = await model.inference(path2image, { 192 | topK: topResults, 193 | locale: "zh", 194 | }); 195 | ``` 196 | 197 | Of course, you can use local model file to speed up loading 198 | 199 | You can download model file from [efficientnet-tensorflowjs-binaries](https://github.com/ntedgi/efficientnet-tensorflowjs-binaries), please keep the directory structure consistent, just like: 200 | 201 | ``` 202 | local_model 203 | └── B0 204 | ├── group1-shard1of6.bin 205 | ├── group1-shard2of6.bin 206 | ├── group1-shard3of6.bin 207 | ├── group1-shard4of6.bin 208 | ├── group1-shard5of6.bin 209 | ├── group1-shard6of6.bin 210 | └── model.json 211 | ``` 212 | 213 | ```javascript 214 | const path = require("path"); 215 | const { 216 | EfficientNetCheckPointFactory, 217 | EfficientNetCheckPoint, 218 | } = require("node-efficientnet"); 219 | 220 | const model = await EfficientNetCheckPointFactory.create( 221 | EfficientNetCheckPoint.B7, 222 | { 223 | localModelRootDirectory: path.join(__dirname, "local_model"), 224 | } 225 | ); 226 | 227 | const path2image = "..."; 228 | 229 | const topResults = 5; 230 | 231 | const result = await model.inference(path2image, { 232 | topK: topResults, 233 | locale: "zh", 234 | }); 235 | ``` 236 | 237 | # 238 | 239 | ## Examples 240 | 241 | download files from remote and predict using model 242 | 243 | ```js 244 | const fs = require("fs"); 245 | const nodeFetch = require("node-fetch"); 246 | 247 | const { 248 | EfficientNetCheckPointFactory, 249 | EfficientNetCheckPoint, 250 | } = require("node-efficientnet"); 251 | 252 | const images = ["car.jpg", "panda.jpg"]; 253 | const imageDir = "./samples"; 254 | const imageDirRemoteUri = 255 | "https://raw.githubusercontent.com/ntedgi/node-EfficientNet/main/samples"; 256 | 257 | if (!fs.existsSync(imageDir)) { 258 | fs.mkdirSync(imageDir); 259 | } 260 | 261 | async function download(image, cb) { 262 | const response = await nodeFetch.default(`${imageDirRemoteUri}/${image}`); 263 | const buffer = await response.buffer(); 264 | fs.writeFile(`${imageDir}/${image}`, buffer, cb); 265 | } 266 | 267 | EfficientNetCheckPointFactory.create(EfficientNetCheckPoint.B2) 268 | .then((model) => { 269 | images.forEach(async (image) => { 270 | await download(image, () => { 271 | model.inference(`${imageDir}/${image}`).then((result) => { 272 | console.log(result.result); 273 | }); 274 | }); 275 | }); 276 | }) 277 | .catch((e) => { 278 | console.error(e); 279 | }); 280 | ``` 281 | 282 | output : 283 | 284 | ```js 285 | [ 286 | { label: "sports car, sport car", precision: 88.02440940394301 }, 287 | { 288 | label: "racer, race car, racing car", 289 | precision: 6.647441678387659, 290 | }, 291 | { label: "car wheel", precision: 5.3281489176693295 }, 292 | ][ 293 | ({ 294 | label: "giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca", 295 | precision: 83.60747593436018, 296 | }, 297 | { label: "skunk, poleca", precision: 11.61300759424677 }, 298 | { 299 | label: "hog, pig, grunter, squealer, Sus scrofa", 300 | precision: 4.779516471393051, 301 | }) 302 | ]; 303 | ``` 304 | 305 | # 306 | 307 | ## About EfficientNet Models 308 | 309 | EfficientNets rely on AutoML and compound scaling to achieve superior performance without compromising resource efficiency. The [AutoML Mobile framework](https://ai.googleblog.com/2018/08/mnasnet-towards-automating-design-of.html) has helped develop a mobile-size baseline network, **EfficientNet-B0**, which is then improved by the compound scaling method to obtain EfficientNet-B1 to B7. 310 | 311 | 312 | 313 | 316 | 319 | 320 |
314 | 315 | 317 | 318 |
321 | 322 | # 323 | 324 | EfficientNets achieve state-of-the-art accuracy on ImageNet with an order of magnitude better efficiency: 325 | 326 | - In high-accuracy regime, EfficientNet-B7 achieves the state-of-the-art 84.4% top-1 / 97.1% top-5 accuracy on ImageNet with 66M parameters and 37B FLOPS. At the same time, the model is 8.4x smaller and 6.1x faster on CPU inference than the former leader, [Gpipe](https://arxiv.org/abs/1811.06965). 327 | 328 | - In middle-accuracy regime, EfficientNet-B1 is 7.6x smaller and 5.7x faster on CPU inference than [ResNet-152](https://arxiv.org/abs/1512.03385), with similar ImageNet accuracy. 329 | 330 | - Compared to the widely used [ResNet-50](https://arxiv.org/abs/1512.03385), EfficientNet-B4 improves the top-1 accuracy from 76.3% of ResNet-50 to 82.6% (+6.3%), under similar FLOPS constraints. 331 | 332 | # 333 | 334 | ## Models 335 | 336 | The performance of each model variant using the pre-trained weights converted from checkpoints provided by the authors is as follows: 337 | 338 | | Architecture | @top1\* Imagenet | @top1\* Noisy-Student | 339 | | -------------- | :--------------: | :-------------------: | 340 | | EfficientNetB0 | 0.772 | 0.788 | 341 | | EfficientNetB1 | 0.791 | 0.815 | 342 | | EfficientNetB2 | 0.802 | 0.824 | 343 | | EfficientNetB3 | 0.816 | 0.841 | 344 | | EfficientNetB4 | 0.830 | 0.853 | 345 | | EfficientNetB5 | 0.837 | 0.861 | 346 | | EfficientNetB6 | 0.841 | 0.864 | 347 | | EfficientNetB7 | 0.844 | 0.869 | 348 | 349 | **\*** - topK accuracy score for converted models (imagenet `val` set) 350 | 351 | --- 352 | 353 | ```ts 354 | if (this.repo.isAwesome || this.repo.isHelpful) { 355 | Star(this.repo); 356 | } 357 | ``` 358 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | // babel.config.js 2 | module.exports = { 3 | presets: [ 4 | ['@babel/preset-env', {targets: {node: 'current'}}], 5 | '@babel/preset-typescript', 6 | ], 7 | "env": { 8 | "test": { 9 | "presets": [["@babel/preset-env"], '@babel/preset-typescript'] 10 | } 11 | }, 12 | "plugins": ["@babel/proposal-class-properties"] 13 | }; 14 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -x 4 | cd playground 5 | docker-compose up --build -d -------------------------------------------------------------------------------- /example.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import * as nodeFetch from "node-fetch"; 3 | 4 | import { 5 | EfficientNetCheckPointFactory, 6 | EfficientNetCheckPoint, 7 | EfficientNetModel, 8 | EfficientNetResult, 9 | EfficientNetLableLanguage, 10 | EfficientNetLanguageProvider 11 | } from "./index"; 12 | 13 | const images = ["car.jpg", "panda.jpg", "fish.jpg"]; 14 | const imageDir = "./samples"; 15 | const imageDirRemoteUri = 16 | "https://raw.githubusercontent.com/ntedgi/node-EfficientNet/main/samples"; 17 | 18 | if (!fs.existsSync(imageDir)) fs.mkdirSync(imageDir); 19 | 20 | async function download(image: string, cb: fs.NoParamCallback) { 21 | const response = await nodeFetch.default(`${imageDirRemoteUri}/${image}`); 22 | const buffer = await response.buffer(); 23 | fs.writeFile(`${imageDir}/${image}`, buffer, cb); 24 | } 25 | 26 | //Default language results (English) 27 | EfficientNetCheckPointFactory.create(EfficientNetCheckPoint.B0) 28 | .then((model: EfficientNetModel) => { 29 | images.forEach(async (image) => { 30 | await download(image, () => { 31 | model 32 | .inference(`${imageDir}/${image}`, { topK: 3 }) 33 | .then((result: EfficientNetResult) => { 34 | console.log("Result in English : \n -------------------"); 35 | console.log(result.result); 36 | }); 37 | }); 38 | }); 39 | }) 40 | .catch((e: Error) => { 41 | console.error(e); 42 | }); 43 | 44 | //Not default language results (Spanish) 45 | EfficientNetCheckPointFactory.create(EfficientNetCheckPoint.B0) 46 | .then(async (model: EfficientNetModel) => { 47 | 48 | const labelLanguage = EfficientNetLableLanguage.SPANISH; 49 | const languageProvider = new EfficientNetLanguageProvider(labelLanguage); 50 | await languageProvider.load(); 51 | 52 | images.forEach(async (image) => { 53 | await download(image, () => { 54 | model 55 | .inference(`${imageDir}/${image}`, { topK: 3 }, languageProvider) 56 | .then((result: EfficientNetResult) => { 57 | console.log("Result in Spanish : \n -------------------"); 58 | console.log(result.result); 59 | }); 60 | }); 61 | }); 62 | }) 63 | .catch((e: Error) => { 64 | console.error(e); 65 | }); 66 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import EfficientNetCheckPointFactory from "./src/EfficientNetCheckPointFactory"; 2 | import EfficientNetModel from "./src/EfficientnetModel"; 3 | import EfficientNetResult from "./src/EfficientNetResult"; 4 | import { EfficientNetCheckPoint } from "./src/EfficientNetCheckPoint"; 5 | import { 6 | EfficientNetLabelLanguage, 7 | EfficientNetLanguageProvider, 8 | } from "./src/EfficientNetLanguageProvider"; 9 | export { 10 | EfficientNetModel, 11 | EfficientNetResult, 12 | EfficientNetCheckPoint, 13 | EfficientNetLabelLanguage, 14 | EfficientNetLanguageProvider, 15 | EfficientNetCheckPointFactory, 16 | }; 17 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | verbose: true, 3 | testTimeout: 1000000, 4 | transform: { 5 | "^.+\\.ts$": "babel-jest", 6 | }, 7 | modulePathIgnorePatterns: ["/playground/"], 8 | coverageReporters: ["text", "cobertura"], 9 | }; 10 | -------------------------------------------------------------------------------- /lib/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -x 4 | tensorflowjs_converter --input_format=tf_saved_model --output_format=tfjs_graph_model --signature_name=serving_default --saved_model_tags=serve efficientnet_b7_classification_1 ./models/B7 5 | -------------------------------------------------------------------------------- /lib/download_models.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import numpy as np 3 | from skimage.io import imread 4 | sys.path.append('..') 5 | 6 | from keras.applications.imagenet_utils import decode_predictions 7 | from efficientnet.keras import EfficientNetB0 8 | from efficientnet.keras import center_crop_and_resize, preprocess_input 9 | model = EfficientNetB0(weights='imagenet') 10 | 11 | image = imread('../misc/panda.jpg') 12 | image_size = model.input_shape[1] 13 | x = center_crop_and_resize(image, image_size=image_size) 14 | # x = preprocess_input(x) 15 | x = np.expand_dims(x, 0) 16 | 17 | y = model.predict(x) 18 | print(decode_predictions(y)) 19 | 20 | #https://storage.googleapis.com/keras-applications/efficientnetb0.h5 21 | -------------------------------------------------------------------------------- /lib/download_models.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -x 4 | curl https://tfhub.dev/tensorflow/efficientnet/b0/classification/1?tf-hub-format=compressed --output b0.tgz 5 | curl https://tfhub.dev/tensorflow/efficientnet/b1/classification/1?tf-hub-format=compressed --output b1.tgz 6 | curl https://tfhub.dev/tensorflow/efficientnet/b2/classification/1?tf-hub-format=compressed --output b2.tgz 7 | curl https://tfhub.dev/tensorflow/efficientnet/b3/classification/1?tf-hub-format=compressed --output b3.tgz 8 | curl https://tfhub.dev/tensorflow/efficientnet/b4/classification/1?tf-hub-format=compressed --output b4.tgz 9 | curl https://tfhub.dev/tensorflow/efficientnet/b5/classification/1?tf-hub-format=compressed --output b5.tgz 10 | curl https://tfhub.dev/tensorflow/efficientnet/b6/classification/1?tf-hub-format=compressed --output b6.tgz 11 | curl https://tfhub.dev/tensorfl ow/efficientnet/b7/classification/1?tf-hub-format=compressed --output b1.tgz 12 | 13 | -------------------------------------------------------------------------------- /lib/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -x 4 | pip3 install virtualenv 5 | virtualenv -p python3 model 6 | model/bin/pip3 install -r requirements.txt -------------------------------------------------------------------------------- /lib/requirements.txt: -------------------------------------------------------------------------------- 1 | tensorflowjs==2.8.1 2 | keras==2.4.3 3 | efficientnet==1.1.1 4 | -------------------------------------------------------------------------------- /lib/tfjs/web_model/group1-shard1of6.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/lib/tfjs/web_model/group1-shard1of6.bin -------------------------------------------------------------------------------- /lib/tfjs/web_model/group1-shard2of6.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/lib/tfjs/web_model/group1-shard2of6.bin -------------------------------------------------------------------------------- /lib/tfjs/web_model/group1-shard3of6.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/lib/tfjs/web_model/group1-shard3of6.bin -------------------------------------------------------------------------------- /lib/tfjs/web_model/group1-shard4of6.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/lib/tfjs/web_model/group1-shard4of6.bin -------------------------------------------------------------------------------- /lib/tfjs/web_model/group1-shard5of6.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/lib/tfjs/web_model/group1-shard5of6.bin -------------------------------------------------------------------------------- /lib/tfjs/web_model/group1-shard6of6.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/lib/tfjs/web_model/group1-shard6of6.bin -------------------------------------------------------------------------------- /media/sample.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/media/sample.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-efficientnet", 3 | "version": "2.1.0", 4 | "description": "Implementation of efficientNet model in nodejs", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "prepublish": "npm run build", 9 | "build": "rm -rf ./dist && tsc && cp -r src/misc ./dist/src/misc", 10 | "example": "ts-node example.ts", 11 | "coveralls": "cat ./coverage/lcov.info | coveralls", 12 | "test": "npm run test:prapare && npm run test:unit", 13 | "lint": "eslint . --fix --ext .ts && npm run lint:md", 14 | "lint:md": "remark .", 15 | "test:unit": "jest --clearCache && jest --coverage", 16 | "test:prapare": "npm link && cd tester && npm link ${npm_package_name}", 17 | "test:playground": "npm i && npm run pack && cd playground/server && npm i && npm test && cd ../../ && npm run remove:pack", 18 | "prepare:pack": "npm run build && npm pack", 19 | "apply:pack": "cd playground/server && npm i file:../../${npm_package_name}-${npm_package_version}.tgz", 20 | "pack": "npm run prepare:pack && npm run apply:pack", 21 | "remove:pack": "rm ${npm_package_name}-${npm_package_version}.tgz && cd playground/server && npm uninstall ${npm_package_name} && npm i ${npm_package_name}", 22 | "deploy:playground": "sh deploy.sh" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "git+https://github.com/ntedgi/node-efficientnet.git" 27 | }, 28 | "author": "Naor Tedgi (Naor.tedgi@gmail.com)", 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/ntedgi/node-efficientnet/issues" 32 | }, 33 | "homepage": "https://github.com/ntedgi/node-efficientnet#readme", 34 | "dependencies": { 35 | "@tensorflow/tfjs-core": "^3.3.0", 36 | "@tensorflow/tfjs-node-gpu": "^3.3.0", 37 | "@types/cli-progress": "^3.8.0", 38 | "cli-progress": "^3.8.2", 39 | "jimp": "^0.16.1", 40 | "node-fetch": "^3.2.10", 41 | "ts-node": "^9.1.1" 42 | }, 43 | "devDependencies": { 44 | "@babel/core": "^7.12.10", 45 | "@babel/preset-env": "^7.12.11", 46 | "@babel/preset-typescript": "^7.12.7", 47 | "@types/jest": "^26.0.19", 48 | "@typescript-eslint/eslint-plugin": "^4.11.1", 49 | "@typescript-eslint/parser": "^4.11.1", 50 | "babel-jest": "^29.1.2", 51 | "babel-plugin-transform-class-properties": "^6.24.1", 52 | "coveralls": "^3.1.0", 53 | "eslint": "^7.16.0", 54 | "eslint-config-prettier": "^8.5.0", 55 | "eslint-import-resolver-typescript": "^3.5.2", 56 | "eslint-plugin-import": "^2.22.1", 57 | "eslint-plugin-jest": "^24.1.3", 58 | "eslint-plugin-prettier": "^3.3.0", 59 | "jest": "^26.6.3", 60 | "prettier": "^2.2.1", 61 | "regenerator-runtime": "^0.13.7", 62 | "remark-cli": "^9.0.0", 63 | "remark-lint": "^8.0.0", 64 | "remark-lint-emphasis-marker": "^3.1.1", 65 | "remark-lint-strong-marker": "^2.0.1", 66 | "remark-preset-lint-recommended": "^6.1.2", 67 | "shelljs": "^0.8.4", 68 | "typescript": "^4.1.3" 69 | }, 70 | "jest": { 71 | "transform": {} 72 | }, 73 | "remarkConfig": { 74 | "settings": { 75 | "emphasis": "*", 76 | "strong": "*" 77 | }, 78 | "plugins": [ 79 | "remark-preset-lint-recommended", 80 | "remark-lint-emphasis-marker", 81 | "remark-lint-strong-marker", 82 | "remark-stringify" 83 | ] 84 | }, 85 | "keywords": [ 86 | "tensorflow", 87 | "tfjs", 88 | "efficientnet", 89 | "imagenet", 90 | "object detection", 91 | "dnn" 92 | ] 93 | } 94 | -------------------------------------------------------------------------------- /playground/classifications/hotdog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/playground/classifications/hotdog.png -------------------------------------------------------------------------------- /playground/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.2' 2 | services: 3 | node-server: 4 | build: 5 | context: ./server 6 | dockerfile: Dockerfile 7 | ports: 8 | - "3000:3000" 9 | environment: 10 | - INSTANCE=node-server 11 | volumes: 12 | - ./:/server 13 | nginx: 14 | build: 15 | context: ./ui 16 | dockerfile: Dockerfile 17 | depends_on: 18 | - node-server 19 | ports: 20 | - "8080:80" 21 | links: 22 | - node-server:node-server 23 | restart: always 24 | -------------------------------------------------------------------------------- /playground/server/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env" 4 | ] 5 | } -------------------------------------------------------------------------------- /playground/server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14.16.1-buster 2 | EXPOSE 3000 3 | WORKDIR /home/app 4 | COPY . . 5 | 6 | RUN rm -rf node_modules \ 7 | && npm ci --quiet --no-progress 8 | 9 | CMD ["npm","start"] 10 | -------------------------------------------------------------------------------- /playground/server/__tests__/fish.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/playground/server/__tests__/fish.jpg -------------------------------------------------------------------------------- /playground/server/__tests__/server.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | import request from "supertest"; 3 | import { createServer } from "../server.js"; 4 | import { join } from "path"; 5 | import { EfficientNetLabelLanguage } from "node-efficientnet"; 6 | 7 | let app = null; 8 | beforeAll(async (done) => { 9 | app = await createServer(); 10 | done(); 11 | }, 60000); 12 | 13 | describe("Get Endpoints", () => { 14 | it("/api/languages should return all existing languages", async (done) => { 15 | jest.setTimeout(1000000); 16 | const res = await request(app) 17 | .get("/api/languages") 18 | .expect(200) 19 | .then((output, err) => { 20 | if (output) { 21 | //Expected languages 22 | const languagesEnumKeys = Object.keys(EfficientNetLabelLanguage); 23 | const languagesAmount = languagesEnumKeys.length / 2; 24 | const languagesArr = languagesEnumKeys.slice(languagesAmount); 25 | 26 | const expectedLanguagesArr = languagesArr 27 | .map((language) => language.toLowerCase()) 28 | .map((item) => item.charAt(0).toUpperCase() + item.slice(1)); 29 | 30 | //Actual languages 31 | const { _body: actualLanguagesArr } = output; 32 | 33 | expect(expectedLanguagesArr).toEqual(actualLanguagesArr); 34 | done(); 35 | } else { 36 | done(err); 37 | } 38 | }) 39 | .catch((err) => { 40 | done(err); 41 | }); 42 | }); 43 | }); 44 | 45 | describe("Post Endpoints", () => { 46 | it("should predict simple gold fish", (done) => { 47 | jest.setTimeout(100000); 48 | const filePath = join(__dirname, "fish.jpg"); 49 | request(app) 50 | .post("/api/upload/english") 51 | .attach("file", filePath) 52 | .expect(200) 53 | .then((output, err) => { 54 | const expectedLabel = "goldfish, Carassius auratus"; 55 | if (output) { 56 | const { result } = output.body; 57 | expect(result[0].label).toEqual(expectedLabel); 58 | done(); 59 | } else { 60 | done(err); 61 | } 62 | }) 63 | .catch((err) => { 64 | done(err); 65 | }); 66 | }); 67 | it("should predict simple gold fish in spanish", (done) => { 68 | jest.setTimeout(100000); 69 | const filePath = join(__dirname, "fish.jpg"); 70 | request(app) 71 | .post("/api/upload/spanish") 72 | .attach("file", filePath) 73 | .expect(200) 74 | .then((output, err) => { 75 | const expectedLabel = "pez dorado, Carassius auratus"; 76 | if (output) { 77 | const { result } = output.body; 78 | expect(result[0].label).toEqual(expectedLabel); 79 | done(); 80 | } else { 81 | done(err); 82 | } 83 | }) 84 | .catch((err) => { 85 | done(err); 86 | }); 87 | }); 88 | it("sanity test server/version", (done) => { 89 | request(app) 90 | .get("/api/version/") 91 | .expect(200) 92 | .then((response) => { 93 | const version = response.body; 94 | done(); 95 | }) 96 | .catch((err) => { 97 | done(err); 98 | }); 99 | }); 100 | }); 101 | 102 | -------------------------------------------------------------------------------- /playground/server/index.js: -------------------------------------------------------------------------------- 1 | import { createServer } from "./server.js"; 2 | const port = 3000; 3 | createServer() 4 | .then((app) => { 5 | app.listen(port, () => { 6 | console.log("Server is running on PORT", port); 7 | }); 8 | }) 9 | .catch((err) => { 10 | console.log(err); 11 | }); 12 | -------------------------------------------------------------------------------- /playground/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon index.js", 8 | "test": "jest --clearCache && jest --detectOpenHandles " 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.17.1", 14 | "express-body-parser-error-handler": "^1.0.3", 15 | "express-formidable": "^1.2.0", 16 | "multer": "^1.4.2", 17 | "node-efficientnet": "file:../../node-efficientnet-2.0.4.tgz", 18 | "node-fetch": "^2.6.1" 19 | }, 20 | "devDependencies": { 21 | "@babel/plugin-transform-modules-commonjs": "^7.13.8", 22 | "@babel/preset-env": "^7.13.12", 23 | "babel-jest": "^26.6.3", 24 | "jest": "^26.6.3", 25 | "nodemon": "^2.0.7", 26 | "supertest": "^6.3.0" 27 | }, 28 | "type": "module", 29 | "jest": { 30 | "testEnvironment": "node", 31 | "coveragePathIgnorePatterns": [ 32 | "/node_modules/" 33 | ], 34 | "transform": { 35 | "^.+\\.jsx?$": "babel-jest" 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /playground/server/server.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 2 | /* eslint-disable no-undef */ 3 | import express, { Router } from "express"; 4 | import formidable from "express-formidable"; 5 | import bodyParserErrorHandler from "express-body-parser-error-handler"; 6 | 7 | const { urlencoded, json } = express; 8 | 9 | import { 10 | EfficientNetCheckPointFactory, 11 | EfficientNetCheckPoint, 12 | EfficientNetLabelLanguage, 13 | EfficientNetLanguageProvider, 14 | } from "node-efficientnet"; 15 | 16 | const safeGet = (fn, fallBack) => { 17 | try { 18 | return fn(); 19 | } catch (e) { 20 | return fallBack; 21 | } 22 | }; 23 | 24 | const loggerMiddleware = (serverName) => (req, _res, next) => { 25 | console.info(`${serverName} | ${req.url} ${req.method} -- ${new Date()}`); 26 | next(); 27 | }; 28 | 29 | const initServer = (model, serverName = "back-end") => { 30 | const app = express(); 31 | const router = Router(); 32 | app.use(loggerMiddleware(serverName)); 33 | 34 | router.post("/api/upload/:language", async (req, res) => { 35 | try { 36 | console.log(`/api/upload/ ^ language=>${req.params.language}`); 37 | const filePath = safeGet(() => req.files.file.path, null); 38 | if (!filePath) { 39 | res.status(400); 40 | res.send({ error: "should pass file to inference" }); 41 | } else { 42 | const language = safeGet(() => req.params.language, null); 43 | if (!language) { 44 | res.status(400); 45 | res.send({ error: "should pass file to inference" }); 46 | } else { 47 | const formattedLanguage = language.toUpperCase(); 48 | const labelLanguage = EfficientNetLabelLanguage[formattedLanguage]; 49 | const languageProvider = new EfficientNetLanguageProvider( 50 | labelLanguage 51 | ); 52 | await languageProvider.load(); 53 | const result = await model.inference( 54 | filePath, 55 | null, 56 | languageProvider 57 | ); 58 | res.send(result); 59 | } 60 | } 61 | } catch (err) { 62 | console.error(err); 63 | res.status(500).send("Something went wrong"); 64 | } 65 | }); 66 | 67 | router.get("/api/languages", async (req, res) => { 68 | try { 69 | const languagesEnumKeys = Object.keys(EfficientNetLabelLanguage); 70 | const languagesAmount = languagesEnumKeys.length / 2; 71 | const languagesArr = languagesEnumKeys.slice(languagesAmount); 72 | 73 | const formattedLanguagesArr = languagesArr 74 | .map((language) => language.toLowerCase()) 75 | .map((item) => item.charAt(0).toUpperCase() + item.slice(1)); 76 | 77 | res.send(formattedLanguagesArr); 78 | } catch (err) { 79 | console.error(err); 80 | res.status(500).send("Something went wrong"); 81 | } 82 | }); 83 | 84 | router.get("/api/version", async (req, res) => { 85 | res.send({ version: "1.0" }); 86 | }); 87 | 88 | app.use(urlencoded({ extended: true })); 89 | app.use(json()); 90 | app.use(formidable()); 91 | app.use(bodyParserErrorHandler()); 92 | app.use(router); 93 | return app; 94 | }; 95 | 96 | const createServer = async () => { 97 | const model = await EfficientNetCheckPointFactory.create( 98 | EfficientNetCheckPoint.B7 99 | ); 100 | console.log("model loaded starting server."); 101 | return initServer(model); 102 | }; 103 | 104 | export { createServer }; 105 | -------------------------------------------------------------------------------- /playground/ui/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # For the full list of supported browsers by the Angular framework, please see: 6 | # https://angular.io/guide/browser-support 7 | 8 | # You can see what browsers were selected by your queries by running: 9 | # npx browserslist 10 | 11 | last 1 Chrome version 12 | last 1 Firefox version 13 | last 2 Edge major versions 14 | last 2 Safari major versions 15 | last 2 iOS major versions 16 | Firefox ESR 17 | not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. 18 | -------------------------------------------------------------------------------- /playground/ui/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /playground/ui/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events*.json 15 | speed-measure-plugin*.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.angular/cache 36 | /.sass-cache 37 | /connect.lock 38 | /coverage 39 | /libpeerconnection.log 40 | npm-debug.log 41 | yarn-error.log 42 | testem.log 43 | /typings 44 | 45 | # System Files 46 | .DS_Store 47 | Thumbs.db 48 | -------------------------------------------------------------------------------- /playground/ui/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14.16.1-buster as node-efficientnet-client 2 | WORKDIR /app 3 | COPY package*.json ./ 4 | RUN npm install 5 | COPY . . 6 | RUN npm run build 7 | 8 | FROM nginx 9 | RUN rm -rf /usr/share/nginx/html/* 10 | COPY ./nginx.conf /etc/nginx/nginx.conf 11 | COPY --from=node-efficientnet-client /app/dist/playground /usr/share/nginx/html 12 | EXPOSE 80 13 | -------------------------------------------------------------------------------- /playground/ui/README.md: -------------------------------------------------------------------------------- 1 | # Playground 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 11.0.6. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. 28 | -------------------------------------------------------------------------------- /playground/ui/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "playground": { 7 | "projectType": "application", 8 | "schematics": { 9 | "@schematics/angular:component": { 10 | "style": "scss" 11 | } 12 | }, 13 | "root": "", 14 | "sourceRoot": "src", 15 | "prefix": "app", 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:browser", 19 | "options": { 20 | "outputPath": "dist/playground", 21 | "index": "src/index.html", 22 | "main": "src/main.ts", 23 | "polyfills": "src/polyfills.ts", 24 | "tsConfig": "tsconfig.app.json", 25 | "assets": [ 26 | "src/favicon.ico", 27 | "src/assets" 28 | ], 29 | "styles": [ 30 | "src/styles.scss", 31 | "node_modules/primeng/resources/themes/bootstrap4-light-blue/theme.css", 32 | "node_modules/primeng/resources/primeng.min.css", 33 | "node_modules/primeicons/primeicons.css" 34 | ], 35 | "scripts": [], 36 | "vendorChunk": true, 37 | "extractLicenses": false, 38 | "buildOptimizer": false, 39 | "sourceMap": true, 40 | "optimization": false, 41 | "namedChunks": true 42 | }, 43 | "configurations": { 44 | "production": { 45 | "fileReplacements": [ 46 | { 47 | "replace": "src/environments/environment.ts", 48 | "with": "src/environments/environment.prod.ts" 49 | } 50 | ], 51 | "optimization": true, 52 | "outputHashing": "all", 53 | "sourceMap": false, 54 | "namedChunks": false, 55 | "extractLicenses": true, 56 | "vendorChunk": false, 57 | "buildOptimizer": true, 58 | "budgets": [ 59 | { 60 | "type": "initial", 61 | "maximumWarning": "2mb", 62 | "maximumError": "5mb" 63 | }, 64 | { 65 | "type": "anyComponentStyle", 66 | "maximumWarning": "6kb", 67 | "maximumError": "10kb" 68 | } 69 | ] 70 | } 71 | }, 72 | "defaultConfiguration": "" 73 | }, 74 | "serve": { 75 | "builder": "@angular-devkit/build-angular:dev-server", 76 | "options": { 77 | "browserTarget": "playground:build", 78 | "proxyConfig": "src/proxy.conf.json" 79 | }, 80 | "configurations": { 81 | "production": { 82 | "browserTarget": "playground:build:production" 83 | } 84 | } 85 | }, 86 | "extract-i18n": { 87 | "builder": "@angular-devkit/build-angular:extract-i18n", 88 | "options": { 89 | "browserTarget": "playground:build" 90 | } 91 | }, 92 | "test": { 93 | "builder": "@angular-devkit/build-angular:karma", 94 | "options": { 95 | "main": "src/test.ts", 96 | "polyfills": "src/polyfills.ts", 97 | "tsConfig": "tsconfig.spec.json", 98 | "karmaConfig": "karma.conf.js", 99 | "assets": [ 100 | "src/favicon.ico", 101 | "src/assets" 102 | ], 103 | "styles": [ 104 | "src/styles.scss", 105 | "node_modules/primeng/resources/themes/bootstrap4-light-blue/theme.css", 106 | "node_modules/primeng/resources/primeng.min.css", 107 | "node_modules/primeicons/primeicons.css" 108 | ], 109 | "scripts": [] 110 | } 111 | }, 112 | "e2e": { 113 | "builder": "@angular-devkit/build-angular:protractor", 114 | "options": { 115 | "protractorConfig": "e2e/protractor.conf.js", 116 | "devServerTarget": "playground:serve" 117 | }, 118 | "configurations": { 119 | "production": { 120 | "devServerTarget": "playground:serve:production" 121 | } 122 | } 123 | } 124 | } 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /playground/ui/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | browserName: 'chrome' 17 | }, 18 | directConnect: true, 19 | SELENIUM_PROMISE_MANAGER: false, 20 | baseUrl: 'http://localhost:4200/', 21 | framework: 'jasmine', 22 | jasmineNodeOpts: { 23 | showColors: true, 24 | defaultTimeoutInterval: 30000, 25 | print: function() {} 26 | }, 27 | onPrepare() { 28 | require('ts-node').register({ 29 | project: require('path').join(__dirname, './tsconfig.json') 30 | }); 31 | jasmine.getEnv().addReporter(new SpecReporter({ 32 | spec: { 33 | displayStacktrace: StacktraceOption.PRETTY 34 | } 35 | })); 36 | } 37 | }; -------------------------------------------------------------------------------- /playground/ui/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', async () => { 12 | await page.navigateTo(); 13 | expect(await page.getTitleText()).toEqual('playground app is running!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain( 20 | jasmine.objectContaining({ 21 | level: logging.Level.SEVERE, 22 | } as logging.Entry) 23 | ); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /playground/ui/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | async navigateTo(): Promise { 5 | return browser.get(browser.baseUrl); 6 | } 7 | 8 | async getTitleText(): Promise { 9 | return element(by.css('app-root .content span')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /playground/ui/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../out-tsc/e2e", 6 | "module": "commonjs", 7 | "target": "es2018", 8 | "types": [ 9 | "jasmine", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /playground/ui/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | jasmine: { 17 | // you can add configuration options for Jasmine here 18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html 19 | // for example, you can disable the random execution with `random: false` 20 | // or set a specific seed with `seed: 4321` 21 | }, 22 | clearContext: false // leave Jasmine Spec Runner output visible in browser 23 | }, 24 | jasmineHtmlReporter: { 25 | suppressAll: true // removes the duplicated traces 26 | }, 27 | coverageReporter: { 28 | dir: require('path').join(__dirname, './coverage/playground'), 29 | subdir: '.', 30 | reporters: [ 31 | { type: 'html' }, 32 | { type: 'text-summary' } 33 | ] 34 | }, 35 | reporters: ['progress', 'kjhtml'], 36 | port: 9876, 37 | colors: true, 38 | logLevel: config.LOG_INFO, 39 | autoWatch: true, 40 | browsers: ['Chrome'], 41 | singleRun: false, 42 | restartOnFileChange: true 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /playground/ui/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | 3 | events { worker_connections 1024; } 4 | 5 | http { 6 | include /etc/nginx/mime.types; 7 | 8 | upstream node-app { 9 | server node-server:3000 weight=1 max_fails=3 fail_timeout=30s; 10 | } 11 | 12 | server { 13 | listen 80; 14 | root /usr/share/nginx/html; 15 | 16 | location /api/upload { 17 | proxy_pass http://node-app; 18 | } 19 | 20 | location / { 21 | root /usr/share/nginx/html; 22 | index index.html index.htm; 23 | try_files $uri $uri/ /index.html; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /playground/ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playground", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "^14.2.3", 15 | "@angular/cdk": "^14.2.2", 16 | "@angular/common": "^14.2.3", 17 | "@angular/compiler": "^14.2.3", 18 | "@angular/core": "^14.2.3", 19 | "@angular/forms": "^14.2.3", 20 | "@angular/platform-browser": "^14.2.3", 21 | "@angular/platform-browser-dynamic": "^14.2.3", 22 | "@angular/router": "^14.2.3", 23 | "github-fork-ribbon-css": "^0.2.3", 24 | "primeicons": "^4.1.0", 25 | "primeng": "^11.1.0-rc.1", 26 | "rxjs": "~6.6.0", 27 | "tslib": "^2.0.0", 28 | "zone.js": "~0.11.4" 29 | }, 30 | "devDependencies": { 31 | "@angular-devkit/build-angular": "^14.2.3", 32 | "@angular/cli": "^14.2.3", 33 | "@angular/compiler-cli": "^14.2.3", 34 | "@types/jasmine": "~3.6.0", 35 | "@types/node": "^12.11.1", 36 | "codelyzer": "^6.0.0", 37 | "jasmine-core": "~3.6.0", 38 | "jasmine-spec-reporter": "~5.0.0", 39 | "karma": "~6.4.1", 40 | "karma-chrome-launcher": "~3.1.0", 41 | "karma-coverage": "~2.0.3", 42 | "karma-jasmine": "~4.0.0", 43 | "karma-jasmine-html-reporter": "^1.5.0", 44 | "protractor": "~7.0.0", 45 | "ts-node": "~8.3.0", 46 | "tslint": "~6.1.0", 47 | "typescript": "~4.6.4" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /playground/ui/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { UploaderComponent } from './uploader/uploader.component'; 4 | 5 | const routes: Routes = [{ path: '', component: UploaderComponent }]; 6 | 7 | @NgModule({ 8 | imports: [RouterModule.forRoot(routes)], 9 | exports: [RouterModule], 10 | }) 11 | export class AppRoutingModule {} 12 | -------------------------------------------------------------------------------- /playground/ui/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | Fork me on GitHub 4 | 5 |
6 | -------------------------------------------------------------------------------- /playground/ui/src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/playground/ui/src/app/app.component.scss -------------------------------------------------------------------------------- /playground/ui/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async () => { 7 | await TestBed.configureTestingModule({ 8 | imports: [RouterTestingModule], 9 | declarations: [AppComponent], 10 | }).compileComponents(); 11 | }); 12 | 13 | it('should create the app', () => { 14 | const fixture = TestBed.createComponent(AppComponent); 15 | const app = fixture.componentInstance; 16 | expect(app).toBeTruthy(); 17 | }); 18 | 19 | it(`should have as title 'playground'`, () => { 20 | const fixture = TestBed.createComponent(AppComponent); 21 | const app = fixture.componentInstance; 22 | expect(app.title).toEqual('node-efficientnet playground'); 23 | }); 24 | 25 | it('should render title', () => { 26 | const fixture = TestBed.createComponent(AppComponent); 27 | fixture.detectChanges(); 28 | const compiled = fixture.nativeElement; 29 | expect(compiled.querySelector('.github-fork-ribbon').textContent).toContain( 30 | 'Fork me on GitHub' 31 | ); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /playground/ui/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { PrimeNGConfig } from 'primeng/api'; 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | templateUrl: './app.component.html', 7 | styleUrls: ['./app.component.scss'], 8 | }) 9 | export class AppComponent { 10 | title = 'node-efficientnet playground'; 11 | constructor(private primengConfig: PrimeNGConfig) {} 12 | ngOnInit() { 13 | this.primengConfig.ripple = true; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /playground/ui/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule , CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; 3 | import { PrimeNgModuleLoaders } from './primeng-imports'; 4 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 5 | import { AppRoutingModule } from './app-routing.module'; 6 | import { AppComponent } from './app.component'; 7 | import { UploaderComponent } from './uploader/uploader.component'; 8 | import { HeaderComponent } from './header/header.component'; 9 | import { ImageuploaderComponent } from './imageuploader/imageuploader.component'; 10 | import { HttpClientModule } from '@angular/common/http'; 11 | import { NoteComponent } from './note/note.component'; 12 | import { ResultsComponent } from './results/results.component'; 13 | import { SingleResultComponent } from './single-result/single-result.component'; 14 | import { FormsModule } from '@angular/forms'; 15 | 16 | 17 | @NgModule({ 18 | declarations: [ 19 | AppComponent, 20 | UploaderComponent, 21 | HeaderComponent, 22 | ImageuploaderComponent, 23 | NoteComponent, 24 | ResultsComponent, 25 | SingleResultComponent, 26 | ], 27 | schemas: [CUSTOM_ELEMENTS_SCHEMA], 28 | imports: [ 29 | HttpClientModule, 30 | BrowserModule, 31 | AppRoutingModule, 32 | PrimeNgModuleLoaders, 33 | BrowserAnimationsModule, 34 | FormsModule 35 | ], 36 | providers: [], 37 | bootstrap: [AppComponent], 38 | }) 39 | export class AppModule {} 40 | -------------------------------------------------------------------------------- /playground/ui/src/app/header/header.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Try node-efficientnet

3 |

Use the application below to return image annotations for your image file. Click the Choose button to upload image.

4 |
    5 |
  • Maximum file size is 4MB.
  • 6 |
  • Your browser must have JavaScript enabled.
  • 7 |
8 |
9 | 10 | -------------------------------------------------------------------------------- /playground/ui/src/app/header/header.component.scss: -------------------------------------------------------------------------------- 1 | [header-container]{ 2 | width:80vh; 3 | padding-left:40px; 4 | padding-right:40px; 5 | } 6 | 7 | [header]{ 8 | font-size: 28px; 9 | } 10 | -------------------------------------------------------------------------------- /playground/ui/src/app/header/header.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { HeaderComponent } from './header.component'; 4 | 5 | describe('HeaderComponent', () => { 6 | let component: HeaderComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [HeaderComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(HeaderComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /playground/ui/src/app/header/header.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-header', 5 | templateUrl: './header.component.html', 6 | styleUrls: ['./header.component.scss'], 7 | }) 8 | export class HeaderComponent implements OnInit { 9 | constructor() {} 10 | 11 | ngOnInit(): void {} 12 | } 13 | -------------------------------------------------------------------------------- /playground/ui/src/app/imageuploader/imageuploader.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | Results language : 4 | 5 |
6 |
7 | 9 | 10 |
    11 |
  • {{file.name}} - {{file.size}} bytes
  • 12 |
13 |
14 |
15 |
16 | -------------------------------------------------------------------------------- /playground/ui/src/app/imageuploader/imageuploader.component.scss: -------------------------------------------------------------------------------- 1 | [languages-container]{ 2 | width:80vh; 3 | padding-left:40px; 4 | padding-right:40px; 5 | padding-top:40px; 6 | font-family: 'Roboto', sans-serif; 7 | } 8 | [upload-image-wrapper]{ 9 | padding: 40px; 10 | } 11 | -------------------------------------------------------------------------------- /playground/ui/src/app/imageuploader/imageuploader.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ImageuploaderComponent } from './imageuploader.component'; 4 | 5 | describe('ImageuploaderComponent', () => { 6 | let component: ImageuploaderComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ImageuploaderComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ImageuploaderComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /playground/ui/src/app/imageuploader/imageuploader.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit, Output, EventEmitter, ViewChild} from '@angular/core'; 2 | import {MessageService, SelectItem} from 'primeng/api'; 3 | import {HttpClient} from '@angular/common/http'; 4 | import Prediction from '../interfces'; 5 | import {LanguagesApiService} from '../languages-api.service' 6 | 7 | @Component({ 8 | selector: 'app-imageuploader', 9 | templateUrl: './imageuploader.component.html', 10 | styleUrls: ['./imageuploader.component.scss'], 11 | providers: [MessageService, HttpClient], 12 | }) 13 | export class ImageuploaderComponent implements OnInit { 14 | constructor(private messageService: MessageService, private languagesApi: LanguagesApiService) { 15 | } 16 | 17 | languages: SelectItem[]; 18 | selectedLanguage: SelectItem; 19 | selectedLanguageName: string; 20 | 21 | uploadedFiles: any[] = []; 22 | 23 | @Output() updateImage = new EventEmitter(); 24 | @Output() updateClassification = new EventEmitter(); 25 | @Output() isLoading = new EventEmitter(); 26 | @Output("resetFields") resetFields: EventEmitter = new EventEmitter(); 27 | 28 | ngOnInit(): void { 29 | this.languagesApi.getLanguages().subscribe((data) => { 30 | const languagesArray = Object.values(data); 31 | this.languages = languagesArray.map(language => ({label: language, value: null})); 32 | this.selectedLanguage = this.languages[0]; 33 | this.selectedLanguageName = this.getSelectedLanguageName(); 34 | }); 35 | } 36 | 37 | onChange(event) { 38 | this.selectedLanguageName = this.getSelectedLanguageName(); 39 | //Reset component file field 40 | this.uploadedFiles=[]; 41 | //Reset parent fields 42 | this.resetFields.emit(); 43 | } 44 | 45 | onUpload(event) { 46 | const result = event.originalEvent.body.result as Prediction[]; 47 | this.updateClassification.emit(result); 48 | this.uploadedFiles = [event.files[event.files.length - 1]]; 49 | this.messageService.add({ 50 | severity: 'info', 51 | summary: 'File Uploaded', 52 | detail: '', 53 | }); 54 | this.isLoading.emit(true); 55 | } 56 | 57 | onSend(event) { 58 | this.isLoading.emit(false); 59 | this.uploadedFiles = []; 60 | this.updateImage.emit(event.formData.get('file')); 61 | } 62 | getSelectedLanguageName(){ 63 | return this.selectedLanguage.label.toLowerCase(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /playground/ui/src/app/interfces/index.ts: -------------------------------------------------------------------------------- 1 | export default interface Prediction { 2 | label: string; 3 | precision: number; 4 | } 5 | -------------------------------------------------------------------------------- /playground/ui/src/app/languages-api.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { LanguagesApiService } from './languages-api.service'; 4 | 5 | describe('LanguagesApiService', () => { 6 | let service: LanguagesApiService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(LanguagesApiService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /playground/ui/src/app/languages-api.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | 4 | 5 | @Injectable({ 6 | providedIn: 'root' 7 | }) 8 | export class LanguagesApiService { 9 | 10 | 11 | constructor(private http:HttpClient) { } 12 | 13 | getLanguages(){ 14 | return this.http.get('/api/languages'); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /playground/ui/src/app/note/note.component.html: -------------------------------------------------------------------------------- 1 |
2 |
    3 |
  • Note: Efficientnet API offers 8 checkpoints for object detection , each checkpoint change image resolution and prediction execution time 4 | this demo uses checkpoint B7 5 |
  • 6 |
7 |
8 | -------------------------------------------------------------------------------- /playground/ui/src/app/note/note.component.scss: -------------------------------------------------------------------------------- 1 | [note-container]{ 2 | background-color: #E1F4FE; 3 | padding: 20px; 4 | font-family: 'Roboto', sans-serif; 5 | } 6 | 7 | ul { 8 | list-style: none; 9 | padding: 0; 10 | } 11 | li { 12 | padding-left: 1.3em; 13 | } 14 | li:before { 15 | content: "\f005"; /* FontAwesome Unicode */ 16 | font-family: FontAwesome; 17 | display: inline-block; 18 | margin-left: -1.3em; /* same as padding-left set on li */ 19 | width: 1.6em; /* same as padding-left set on li */ 20 | } 21 | -------------------------------------------------------------------------------- /playground/ui/src/app/note/note.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { NoteComponent } from './note.component'; 4 | 5 | describe('NoteComponent', () => { 6 | let component: NoteComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [NoteComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(NoteComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /playground/ui/src/app/note/note.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-note', 5 | templateUrl: './note.component.html', 6 | styleUrls: ['./note.component.scss'], 7 | }) 8 | export class NoteComponent implements OnInit { 9 | constructor() {} 10 | 11 | ngOnInit(): void {} 12 | } 13 | -------------------------------------------------------------------------------- /playground/ui/src/app/primeng-imports.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { ButtonModule } from 'primeng/button'; 3 | import { PanelModule } from 'primeng/panel'; 4 | import { TabViewModule } from 'primeng/tabview'; 5 | import { InputTextModule } from 'primeng/inputtext'; 6 | import { ToastModule } from 'primeng/toast'; 7 | import { RippleModule } from 'primeng/ripple'; 8 | import { FileUploadModule } from 'primeng/fileupload'; 9 | import { DividerModule } from 'primeng/divider'; 10 | import { ProgressBarModule } from 'primeng/progressbar'; 11 | import { SkeletonModule } from 'primeng/skeleton'; 12 | import {DropdownModule} from 'primeng/dropdown'; 13 | 14 | 15 | @NgModule({ 16 | exports: [ 17 | ButtonModule, 18 | ProgressBarModule, 19 | PanelModule, 20 | TabViewModule, 21 | InputTextModule, 22 | ToastModule, 23 | RippleModule, 24 | FileUploadModule, 25 | DividerModule, 26 | SkeletonModule, 27 | DropdownModule 28 | ], 29 | }) 30 | export class PrimeNgModuleLoaders {} 31 | -------------------------------------------------------------------------------- /playground/ui/src/app/results/results.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 |
6 | 7 |
8 |
9 |
10 | 11 |
12 |
13 |
14 |
15 | 16 |
17 |
18 |
19 | 20 | 21 |
22 | -------------------------------------------------------------------------------- /playground/ui/src/app/results/results.component.scss: -------------------------------------------------------------------------------- 1 | [ results-container]{ 2 | display: flex; 3 | padding-right: 40px; 4 | padding-left: 40px; 5 | padding-bottom: 20px; 6 | justify-content: space-between; 7 | max-height: 300px; 8 | } 9 | [flex-container]{ 10 | min-height: 200px; 11 | padding: 20px; 12 | flex: 1; 13 | box-shadow: var(--devsite-page-box-shadow,0 1px 2px 0 rgba(60,64,67,.3),0 1px 3px 1px rgba(60,64,67,.15)); 14 | } 15 | 16 | [results]{ 17 | 18 | } 19 | [image-contianer]{ 20 | img { 21 | max-width: 100%; 22 | max-height: 100%; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /playground/ui/src/app/results/results.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ResultsComponent } from './results.component'; 4 | 5 | describe('ResultsComponent', () => { 6 | let component: ResultsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ResultsComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(ResultsComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /playground/ui/src/app/results/results.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnInit, OnChanges } from '@angular/core'; 2 | import Prediction from '../interfces'; 3 | 4 | @Component({ 5 | selector: 'app-results', 6 | templateUrl: './results.component.html', 7 | styleUrls: ['./results.component.scss'], 8 | }) 9 | export class ResultsComponent implements OnInit, OnChanges { 10 | @Input() image2Display: string | ArrayBuffer; 11 | @Input() loading: boolean; 12 | @Input() classifications: Prediction[]; 13 | 14 | constructor() {} 15 | 16 | show(): boolean { 17 | if (this.loading === undefined) { 18 | return false; 19 | } 20 | return this.loading; 21 | } 22 | 23 | ngOnChanges() {} 24 | 25 | ngOnInit(): void {} 26 | } 27 | -------------------------------------------------------------------------------- /playground/ui/src/app/single-result/single-result.component.html: -------------------------------------------------------------------------------- 1 |
2 |

{{acc}}

3 | 4 |
5 |
6 | 7 |
8 | -------------------------------------------------------------------------------- /playground/ui/src/app/single-result/single-result.component.scss: -------------------------------------------------------------------------------- 1 | [placeholder] { 2 | padding: 24px; 3 | min-height: 10px; 4 | } 5 | -------------------------------------------------------------------------------- /playground/ui/src/app/single-result/single-result.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SingleResultComponent } from './single-result.component'; 4 | 5 | describe('SingleResultComponent', () => { 6 | let component: SingleResultComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [SingleResultComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(SingleResultComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /playground/ui/src/app/single-result/single-result.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-single-result', 5 | templateUrl: './single-result.component.html', 6 | styleUrls: ['./single-result.component.scss'], 7 | }) 8 | export class SingleResultComponent implements OnInit { 9 | @Input() acc: number; 10 | @Input() title: string; 11 | @Input() loading: boolean; 12 | 13 | constructor() {} 14 | 15 | show(): boolean { 16 | return this.loading; 17 | } 18 | 19 | ngOnInit(): void {} 20 | } 21 | -------------------------------------------------------------------------------- /playground/ui/src/app/uploader/uploader.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 6 | 7 |
8 | -------------------------------------------------------------------------------- /playground/ui/src/app/uploader/uploader.component.scss: -------------------------------------------------------------------------------- 1 | [upload-page]{ 2 | background-color: white; 3 | width: 60vw; 4 | max-width: 1024px; 5 | min-width: 900px; 6 | box-shadow: var(--devsite-page-box-shadow,0 1px 2px 0 rgba(60,64,67,.3),0 1px 3px 1px rgba(60,64,67,.15)); 7 | display: flex; 8 | flex-direction: column; 9 | } 10 | -------------------------------------------------------------------------------- /playground/ui/src/app/uploader/uploader.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { UploaderComponent } from './uploader.component'; 4 | 5 | describe('UploaderComponent', () => { 6 | let component: UploaderComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [UploaderComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(UploaderComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /playground/ui/src/app/uploader/uploader.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import Prediction from '../interfces'; 3 | 4 | @Component({ 5 | selector: 'app-uploader', 6 | templateUrl: './uploader.component.html', 7 | styleUrls: ['./uploader.component.scss'], 8 | }) 9 | export class UploaderComponent implements OnInit { 10 | constructor() {} 11 | 12 | image2Display: string | ArrayBuffer; 13 | classifications: Prediction[]; 14 | loading: boolean; 15 | 16 | updateImage(imgPath: File) { 17 | const reader = new FileReader(); 18 | reader.readAsDataURL(imgPath); 19 | reader.onload = (_event) => { 20 | this.image2Display = reader.result; 21 | }; 22 | } 23 | isLoading(loading: boolean) { 24 | this.loading = loading; 25 | } 26 | 27 | updateClassification(result: Prediction[]) { 28 | console.log('updateClassification'); 29 | console.log(result); 30 | this.classifications = result; 31 | } 32 | 33 | resetFields(){ 34 | this.image2Display=null; 35 | this.classifications=null; 36 | this.loading=false; 37 | } 38 | 39 | ngOnInit(): void {} 40 | } 41 | -------------------------------------------------------------------------------- /playground/ui/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/playground/ui/src/assets/.gitkeep -------------------------------------------------------------------------------- /playground/ui/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | }; 4 | -------------------------------------------------------------------------------- /playground/ui/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /playground/ui/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/playground/ui/src/favicon.ico -------------------------------------------------------------------------------- /playground/ui/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Node Efficientnet Playground 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /playground/ui/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic() 12 | .bootstrapModule(AppModule) 13 | .catch((err) => console.error(err)); 14 | -------------------------------------------------------------------------------- /playground/ui/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** 22 | * By default, zone.js will patch all possible macroTask and DomEvents 23 | * user can disable parts of macroTask/DomEvents patch by setting following flags 24 | * because those flags need to be set before `zone.js` being loaded, and webpack 25 | * will put import in the top of bundle, so user need to create a separate file 26 | * in this directory (for example: zone-flags.ts), and put the following flags 27 | * into that file, and then add the following code before importing zone.js. 28 | * import './zone-flags'; 29 | * 30 | * The flags allowed in zone-flags.ts are listed here. 31 | * 32 | * The following flags will work for all browsers. 33 | * 34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 37 | * 38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 40 | * 41 | * (window as any).__Zone_enable_cross_context_check = true; 42 | * 43 | */ 44 | 45 | /*************************************************************************************************** 46 | * Zone JS is required by default for Angular itself. 47 | */ 48 | import 'zone.js'; // Included with Angular CLI. 49 | 50 | /*************************************************************************************************** 51 | * APPLICATION IMPORTS 52 | */ 53 | -------------------------------------------------------------------------------- /playground/ui/src/proxy.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "/api/upload/*": { 3 | "target": "http://localhost:3000", 4 | "secure": false, 5 | "logLevel": "debug" 6 | }, 7 | "/api/languages": { 8 | "target": "http://localhost:3000", 9 | "secure": false, 10 | "logLevel": "debug" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /playground/ui/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | [window] { 3 | position: absolute; 4 | left: 0; 5 | right: 0; 6 | top: 0; 7 | bottom: 0; 8 | width: 100%; 9 | min-height: 100%; 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | background-color: #E7EAED; 14 | } 15 | [text]{ 16 | font-family: 'Roboto', sans-serif; 17 | } 18 | -------------------------------------------------------------------------------- /playground/ui/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting, 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: { 11 | context( 12 | path: string, 13 | deep?: boolean, 14 | filter?: RegExp 15 | ): { 16 | keys(): string[]; 17 | (id: string): T; 18 | }; 19 | }; 20 | 21 | // First, initialize the Angular testing environment. 22 | getTestBed().initTestEnvironment( 23 | BrowserDynamicTestingModule, 24 | platformBrowserDynamicTesting(), { 25 | teardown: { destroyAfterEach: false } 26 | } 27 | ); 28 | // Then we find all the tests. 29 | const context = require.context('./', true, /\.spec\.ts$/); 30 | // And load the modules. 31 | context.keys().map(context); 32 | -------------------------------------------------------------------------------- /playground/ui/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts", 10 | "src/polyfills.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /playground/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "sourceMap": true, 8 | "declaration": false, 9 | "downlevelIteration": true, 10 | "experimentalDecorators": true, 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2020", 14 | "module": "es2020", 15 | "lib": [ 16 | "es2018", 17 | "dom" 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /playground/ui/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /playground/ui/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rulesDirectory": [ 4 | "codelyzer" 5 | ], 6 | "rules": { 7 | "align": { 8 | "options": [ 9 | "parameters", 10 | "statements" 11 | ] 12 | }, 13 | "array-type": false, 14 | "arrow-return-shorthand": true, 15 | "curly": true, 16 | "deprecation": { 17 | "severity": "warning" 18 | }, 19 | "eofline": true, 20 | "import-blacklist": [ 21 | true, 22 | "rxjs/Rx" 23 | ], 24 | "import-spacing": true, 25 | "indent": { 26 | "options": [ 27 | "spaces" 28 | ] 29 | }, 30 | "max-classes-per-file": false, 31 | "max-line-length": [ 32 | true, 33 | 140 34 | ], 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-console": [ 47 | true, 48 | "debug", 49 | "info", 50 | "time", 51 | "timeEnd", 52 | "trace" 53 | ], 54 | "no-empty": false, 55 | "no-inferrable-types": [ 56 | true, 57 | "ignore-params" 58 | ], 59 | "no-non-null-assertion": true, 60 | "no-redundant-jsdoc": true, 61 | "no-switch-case-fall-through": true, 62 | "no-var-requires": false, 63 | "object-literal-key-quotes": [ 64 | true, 65 | "as-needed" 66 | ], 67 | "quotemark": [ 68 | true, 69 | "single" 70 | ], 71 | "semicolon": { 72 | "options": [ 73 | "always" 74 | ] 75 | }, 76 | "space-before-function-paren": { 77 | "options": { 78 | "anonymous": "never", 79 | "asyncArrow": "always", 80 | "constructor": "never", 81 | "method": "never", 82 | "named": "never" 83 | } 84 | }, 85 | "typedef": [ 86 | true, 87 | "call-signature" 88 | ], 89 | "typedef-whitespace": { 90 | "options": [ 91 | { 92 | "call-signature": "nospace", 93 | "index-signature": "nospace", 94 | "parameter": "nospace", 95 | "property-declaration": "nospace", 96 | "variable-declaration": "nospace" 97 | }, 98 | { 99 | "call-signature": "onespace", 100 | "index-signature": "onespace", 101 | "parameter": "onespace", 102 | "property-declaration": "onespace", 103 | "variable-declaration": "onespace" 104 | } 105 | ] 106 | }, 107 | "variable-name": { 108 | "options": [ 109 | "ban-keywords", 110 | "check-format", 111 | "allow-pascal-case" 112 | ] 113 | }, 114 | "whitespace": { 115 | "options": [ 116 | "check-branch", 117 | "check-decl", 118 | "check-operator", 119 | "check-separator", 120 | "check-type", 121 | "check-typecast" 122 | ] 123 | }, 124 | "component-class-suffix": true, 125 | "contextual-lifecycle": true, 126 | "directive-class-suffix": true, 127 | "no-conflicting-lifecycle": true, 128 | "no-host-metadata-property": true, 129 | "no-input-rename": true, 130 | "no-inputs-metadata-property": true, 131 | "no-output-native": true, 132 | "no-output-on-prefix": true, 133 | "no-output-rename": true, 134 | "no-outputs-metadata-property": true, 135 | "template-banana-in-box": true, 136 | "template-no-negated-async": true, 137 | "use-lifecycle-interface": true, 138 | "use-pipe-transform-interface": true, 139 | "directive-selector": [ 140 | true, 141 | "attribute", 142 | "app", 143 | "camelCase" 144 | ], 145 | "component-selector": [ 146 | true, 147 | "element", 148 | "app", 149 | "kebab-case" 150 | ] 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /samples/car.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/samples/car.jpg -------------------------------------------------------------------------------- /samples/fish.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/samples/fish.jpg -------------------------------------------------------------------------------- /samples/gun.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/samples/gun.jpg -------------------------------------------------------------------------------- /samples/panda.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/samples/panda.jpg -------------------------------------------------------------------------------- /src/EfficientNetCheckPoint.ts: -------------------------------------------------------------------------------- 1 | export enum EfficientNetCheckPoint { 2 | B0, 3 | B1, 4 | B2, 5 | B3, 6 | B4, 7 | B5, 8 | B6, 9 | B7, 10 | } 11 | -------------------------------------------------------------------------------- /src/EfficientNetCheckPointFactory.ts: -------------------------------------------------------------------------------- 1 | import * as tf from "@tensorflow/tfjs-node-gpu"; 2 | import { io } from "@tensorflow/tfjs-core"; 3 | 4 | import EfficientNetModel from "./EfficientnetModel"; 5 | import { EfficientNetCheckPoint } from "./EfficientNetCheckPoint"; 6 | import { EfficientNetLabelLanguage } from "./EfficientNetLanguageProvider"; 7 | const defaultModelsUrl = 8 | "https://raw.githubusercontent.com/ntedgi/efficientnet-tensorflowjs-binaries/main/models/B"; 9 | const modelFileName = "model.json"; 10 | const inputLayerImageSize = [224, 240, 260, 300, 380, 456, 528, 600]; 11 | 12 | interface EfficientNetCheckPointFactoryOptions { 13 | localModelRootDirectory?: string; 14 | locale?: EfficientNetLabelLanguage; 15 | } 16 | 17 | export default class EfficientNetCheckPointFactory { 18 | static async create( 19 | checkPoint: EfficientNetCheckPoint, 20 | options?: EfficientNetCheckPointFactoryOptions 21 | ): Promise { 22 | const { localModelRootDirectory, locale } = options || {}; 23 | let modelPath: 24 | | string 25 | | io.IOHandler = `${defaultModelsUrl}${checkPoint}/${modelFileName}`; 26 | 27 | if (localModelRootDirectory) { 28 | modelPath = tf.io.fileSystem( 29 | `${localModelRootDirectory}/B${checkPoint}/${modelFileName}` 30 | ); 31 | } 32 | 33 | const model = new EfficientNetModel( 34 | modelPath, 35 | inputLayerImageSize[checkPoint], 36 | locale 37 | ); 38 | await model.load(); 39 | return model; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/EfficientNetLanguageProvider.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import * as path from "path"; 3 | 4 | export enum EfficientNetLabelLanguage { 5 | ENGLISH, 6 | CHINESE, 7 | SPANISH, 8 | ARABIC, 9 | FRENCH, 10 | RUSSIAN, 11 | HEBREW, 12 | } 13 | export class EfficientNetLanguageProvider { 14 | private filePath = "misc/en.json"; 15 | private labelsMap = null; 16 | 17 | constructor(language: EfficientNetLabelLanguage | undefined) { 18 | let fileName = null; 19 | if (language) { 20 | language as EfficientNetLabelLanguage; 21 | switch (+language) { 22 | case EfficientNetLabelLanguage.CHINESE: 23 | fileName = "zh"; 24 | break; 25 | case EfficientNetLabelLanguage.ENGLISH: 26 | fileName = "en"; 27 | break; 28 | case EfficientNetLabelLanguage.SPANISH: 29 | fileName = "es"; 30 | break; 31 | case EfficientNetLabelLanguage.RUSSIAN: 32 | fileName = "ru"; 33 | break; 34 | case EfficientNetLabelLanguage.ARABIC: 35 | fileName = "ar"; 36 | break; 37 | case EfficientNetLabelLanguage.HEBREW: 38 | fileName = "he"; 39 | break; 40 | case EfficientNetLabelLanguage.FRENCH: 41 | fileName = "fr"; 42 | break; 43 | } 44 | } 45 | this.filePath = fileName ? `misc/${fileName}.json` : this.filePath; 46 | } 47 | 48 | async load(): Promise { 49 | const jsonFile = path.join(__dirname, this.filePath); 50 | const translationFile = await fs.readFileSync(jsonFile, "utf8"); 51 | this.labelsMap = JSON.parse(translationFile); 52 | } 53 | 54 | get(value: number): string | undefined { 55 | return this.labelsMap?.[value]; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/EfficientNetResult.ts: -------------------------------------------------------------------------------- 1 | import { EfficientNetLanguageProvider } from "./EfficientNetLanguageProvider"; 2 | interface Prediction { 3 | label: string; 4 | precision: number; 5 | } 6 | 7 | export default class EfficientNetResult { 8 | result: Prediction[] = []; 9 | 10 | constructor( 11 | values: Float32Array, 12 | topK: number, 13 | languageProvider: EfficientNetLanguageProvider 14 | ) { 15 | const arr = Array.from(values); 16 | const topValues = values 17 | .sort((a: number, b: number) => b - a) 18 | .slice(0, topK); 19 | const indexes = topValues.map((e: number) => arr.indexOf(e)); 20 | const sum = topValues.reduce((a: number, b: number) => { 21 | return a + b; 22 | }, 0); 23 | indexes.forEach((value: number, index: number) => { 24 | this.result.push({ 25 | label: languageProvider.get(value), 26 | precision: (topValues[index] / sum) * 100, 27 | } as Prediction); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/EfficientnetModel.ts: -------------------------------------------------------------------------------- 1 | import * as tf from "@tensorflow/tfjs-node-gpu"; 2 | import * as Jimp from "jimp"; 3 | import * as cliProgress from "cli-progress"; 4 | import { io } from "@tensorflow/tfjs-core"; 5 | import { 6 | EfficientNetLabelLanguage, 7 | EfficientNetLanguageProvider, 8 | } from "./EfficientNetLanguageProvider"; 9 | import EfficientNetResult from "./EfficientNetResult"; 10 | 11 | const NUM_OF_CHANNELS = 3; 12 | 13 | interface EfficientNetModelInferenceOptions { 14 | topK?: number; 15 | } 16 | 17 | export default class EfficientNetModel { 18 | modelPath: string | io.IOHandler; 19 | imageSize: number; 20 | model: tf.GraphModel | undefined; 21 | languageProvider: EfficientNetLanguageProvider; 22 | constructor( 23 | modelPath: string | io.IOHandler, 24 | imageSize: number, 25 | local: EfficientNetLabelLanguage | undefined 26 | ) { 27 | this.modelPath = modelPath; 28 | this.imageSize = imageSize; 29 | this.languageProvider = new EfficientNetLanguageProvider(local); 30 | } 31 | 32 | async load(): Promise { 33 | await this.languageProvider.load(); 34 | 35 | const bar = new cliProgress.SingleBar( 36 | {}, 37 | cliProgress.Presets.shades_classic 38 | ); 39 | bar.start(100, 0); 40 | const model = await tf.loadGraphModel(this.modelPath, { 41 | onProgress: (p) => { 42 | bar.update(p * 100); 43 | }, 44 | }); 45 | bar.stop(); 46 | this.model = model; 47 | } 48 | 49 | private async createTensor(image: Jimp): Promise { 50 | const values = new Float32Array( 51 | this.imageSize * this.imageSize * NUM_OF_CHANNELS 52 | ); 53 | let i = 0; 54 | image.scan( 55 | 0, 56 | 0, 57 | image.bitmap.width, 58 | image.bitmap.height, 59 | (x: number, y: number) => { 60 | const pixel = Jimp.intToRGBA(image.getPixelColor(x, y)); 61 | pixel.r = ((pixel.r - 1) / 127.0) >> 0; 62 | pixel.g = ((pixel.g - 1) / 127.0) >> 0; 63 | pixel.b = ((pixel.b - 1) / 127.0) >> 0; 64 | values[i * NUM_OF_CHANNELS + 0] = pixel.r; 65 | values[i * NUM_OF_CHANNELS + 1] = pixel.g; 66 | values[i * NUM_OF_CHANNELS + 2] = pixel.b; 67 | i++; 68 | } 69 | ); 70 | const outShape: [number, number, number] = [ 71 | this.imageSize, 72 | this.imageSize, 73 | NUM_OF_CHANNELS, 74 | ]; 75 | let imageTensor = tf.tensor3d(values, outShape, "float32"); 76 | imageTensor = imageTensor.expandDims(0); 77 | return imageTensor; 78 | } 79 | 80 | private async cropAndResize(image: Jimp): Promise { 81 | const width = image.bitmap.width; 82 | const height = image.bitmap.height; 83 | const cropPadding = 32; 84 | const paddedCenterCropSize = 85 | ((this.imageSize / (this.imageSize + cropPadding)) * 86 | Math.min(height, width)) >> 87 | 0; 88 | const offsetHeight = ((height - paddedCenterCropSize + 1) / 2) >> 0; 89 | const offsetWidth = (((width - paddedCenterCropSize + 1) / 2) >> 0) + 1; 90 | 91 | await image.crop( 92 | offsetWidth, 93 | offsetHeight, 94 | paddedCenterCropSize, 95 | paddedCenterCropSize 96 | ); 97 | await image.resize(this.imageSize, this.imageSize, Jimp.RESIZE_BICUBIC); 98 | return image; 99 | } 100 | 101 | private async predict( 102 | tensor: tf.Tensor3D, 103 | topK: number, 104 | overrideLanguageProvider?: EfficientNetLanguageProvider 105 | ): Promise { 106 | const objectArray = this.model!.predict(tensor) as tf.Tensor; 107 | const values = objectArray.dataSync() as Float32Array; 108 | const languageProvider = overrideLanguageProvider 109 | ? overrideLanguageProvider 110 | : this.languageProvider; 111 | return new EfficientNetResult(values, topK, languageProvider); 112 | } 113 | 114 | async inference( 115 | imgPath: string | Buffer, 116 | options?: EfficientNetModelInferenceOptions, 117 | overrideLanguageProvider?: EfficientNetLanguageProvider 118 | ): Promise { 119 | const { topK = NUM_OF_CHANNELS } = options || {}; 120 | // @ts-ignore 121 | let image = await Jimp.read(imgPath); 122 | image = await this.cropAndResize(image); 123 | const tensor = await this.createTensor(image); 124 | return this.predict(tensor, topK, overrideLanguageProvider); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/ModelResourcesProvider.ts: -------------------------------------------------------------------------------- 1 | import { EfficientNetCheckPoint } from "./EfficientNetCheckPoint"; 2 | import * as fs from "fs"; 3 | import * as nodeFetch from "node-fetch"; 4 | 5 | const workspaceDir = "./workspace"; 6 | 7 | export default class ModelResourcesProvider { 8 | private static downloadUri = (checkPoint: EfficientNetCheckPoint) => 9 | `https://tfhub.dev/tensorflow/efficientnet/b${checkPoint}/classification/1?tf-hub-format=compressed`; 10 | 11 | private static async download(url: string, outputFilePath: string) { 12 | const response = await nodeFetch.default(url); 13 | const buffer = await response.buffer(); 14 | await fs.writeFileSync(outputFilePath, buffer); 15 | } 16 | 17 | static async get(checkPoint: EfficientNetCheckPoint): Promise { 18 | const modelDir = `${workspaceDir}/B${checkPoint}/model.tgz`; 19 | if (!fs.existsSync(workspaceDir)) { 20 | fs.mkdirSync(workspaceDir); 21 | await this.download(this.downloadUri(checkPoint), modelDir); 22 | } 23 | return ""; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/misc/he.json: -------------------------------------------------------------------------------- 1 | { 2 | "0": "טנץ', דג טנקה", 3 | "1": "דג זהב,קָרַס זָהָב, Carassius auratus", 4 | "2": "כריש לבן גדול, כריש לבן, כריש אוכל אדם, Carcharodon carcharias", 5 | "3": "כריש נמר, Galeocerdo cuvieri", 6 | "4": "פטישן, כריש פטיש, Sphyrnidae", 7 | "5": "חשמלנאים, Torpediniformes", 8 | "6": "טחניים, סטינגריי, Stingray", 9 | "7": "תרנגול זכרי", 10 | "8": "תרנגולת", 11 | "9": "יען, יען מצוי", 12 | "10": "פרוש הררי, Fringilla montifringilla", 13 | "11": "חוחית, Carduelis carduelis", 14 | "12": "חוחית בית, Carpodacus mexicanus", 15 | "13": "יונקו, junco", 16 | "14": "כחלית, Indigo bunting", 17 | "15": "רובין, אדום חזה אמריקאי , Turdus migratorius", 18 | "16": "בולבוליים", 19 | "17": "עורבני", 20 | "18": "עקעקים, Magpie", 21 | "19": "צ'יקדי, Chickadee", 22 | "20": "אמודאיים, Dippers", 23 | "21": "עפיפון", 24 | "22": "עיטם לבן-ראש, Haliaeetus leucocephalus", 25 | "23": "נשר", 26 | "24": "לילית אפורה, Strix nebulosa", 27 | "25": "סלמנדרה אש", 28 | "26": "לטאה מסוג Triturus vulgaris", 29 | "27": "eft", 30 | "28": "סלמנדרה מנומרת, Ambystoma maculatum", 31 | "29": "axolotl, Ambystoma mexicanum", 32 | "30": "צפרדע השור, bullfrog", 33 | "31": "צפרדע עצים, treefrog", 34 | "32": "tailed frog, bell toad, ribbed toad, tailed toad, Ascaphus trui", 35 | "33": "צב ים חום", 36 | "34": "צב ים גלדי, Dermochelys coriacea", 37 | "35": "צב בוץ, mud turtle", 38 | "36": "טראפין היהלום", 39 | "37": "צב סגיר", 40 | "38": "עַפְעַפִּית אֲמֶרִיקָה", 41 | "39": "איגואנה ירוקה", 42 | "40": "American chameleon, anole, Anolis carolinensis", 43 | "41": "לטאת טגו", 44 | "42": "חרדון", 45 | "43": "חרדון הצווארון, Chlamydosaurus kingi", 46 | "44": "לטאה תנין, alligator lizard", 47 | "45": "הלודרמה מרירית, Heloderma suspectum", 48 | "46": "לְטָאָה אִזְמָרַגְדִּית, Lacerta viridis", 49 | "47": "זיקית אפריקאית, Chamaeleo chamaeleon", 50 | "48": "דרקון קומודו, לטאת קומודו, לטאת דרקון, Varanus komodoensis", 51 | "49": "תנין אפריקאי, תנין נילוס, Crocodylus niloticus", 52 | "50": "אליגטור אמריקני, Alligator mississipiensis", 53 | "51": "טריצרטופס", 54 | "52": "נחש רעם, נחש תולעת, Carphophis amoenus", 55 | "53": "נחש טבעת, נחש קצוץ, ring snake", 56 | "54": "נפוח נפוח, צפע חול, sand viper", 57 | "55": "נחש מים, grass snake", 58 | "56": "נחש מלך", 59 | "57": "נחשי בירית", 60 | "58": "נחשי בירית, water snake", 61 | "59": "נחש-שוטני ירוק", 62 | "60": "נחש לילה, Hypsiglena torquata", 63 | "61": "רב חנק מצוי", 64 | "62": "פיתון סלעים אפריקני", 65 | "63": "קוברה הודית, נג'ה נג'ה", 66 | "64": "ממבה ירוקה מזרחית", 67 | "65": "פתני הים", 68 | "66": "צפע קרניים, צפע, צפע חול, צפע קרני, Cerastes cornutus", 69 | "67": "עכסן גב יהלום מערבי, Crotalus adamanteus", 70 | "68": "עכסן עכני, Crotalus cerastes", 71 | "69": "טרילוביטים", 72 | "70": "ארך-רגליים קרני, פלנגיום אופיליו, Phalangium opilio", 73 | "71": "עקרב", 74 | "72": "עכביש גן שחור וזהב, Argiope aurantia", 75 | "73": "עכביש אסם, Araneus cavaticus", 76 | "74": "עכביש גן, Aranea diademata", 77 | "75": "אלמנה שחורה, Latrodectus mactans", 78 | "76": "טרנטולה", 79 | "77": "עכביש זאב, עכביש ציד", 80 | "78": "קרציה", 81 | "79": "מרבה רגליים", 82 | "80": "שכווי שחור", 83 | "81": "שכווי הצפון", 84 | "82": "שכווי הצווארון", 85 | "83": "שכווי ערבות ענק, שכווי הערבה", 86 | "84": "טווס", 87 | "85": "שליו", 88 | "86": "חוגלה", 89 | "87": "אפרור אפריקני, Psittacus erithacus", 90 | "88": "מקאו", 91 | "89": "קקדו צהוב-ציצית, Kakatoe galerita, Cacatua galerita", 92 | "90": "לורים", 93 | "91": "קוקל, coucal", 94 | "92": "שרקרקיים", 95 | "93": "קלאוניים", 96 | "94": "קוליבריים", 97 | "95": "יקמראים", 98 | "96": "טוקניים", 99 | "97": "ציפור דרייק", 100 | "98": "מרגון בינוני", 101 | "99": "אווז", 102 | "100": "ברבור שחור, Cygnus atratus", 103 | "101": "tusker", 104 | "102": "echidna, דוב נמלים קוצני, דוב נמלים", 105 | "103": "פלטיפוס, פלטיפוס ברווז, Ornithorhynchus anatinus", 106 | "104": "וואלבי, Western brush wallaby", 107 | "105": "קואלה, דוב קואלה, Phascolarctos cinereus", 108 | "106": "וומבט", 109 | "107": "מדוזה", 110 | "108": "שושנת ים", 111 | "109": "אלמוגים מוח", 112 | "110": "תולעת שטוחה, platyhelminth", 113 | "111": "נמטודה, תולעים נימיות", 114 | "112": "קונכייה", 115 | "113": "חילזון", 116 | "114": "חשופיות", 117 | "115": "חשופיות ים", 118 | "116": "chiton, coat-of-mail shell, sea cradle, polyplacophora", 119 | "117": "Nautilus pompilius, nautilus", 120 | "118": "סרטן דנג'נס", 121 | "119": "rock crab, Cancer irroratus", 122 | "120": "סרטן כנר", 123 | "121": "סרטן מלך, סרטן אלסקה, סרטן מלך אלסקה, סרטן מלך אלסקה, Paralithodes camtschatica", 124 | "122": "לובסטר אמריקאי, Homarus americanus", 125 | "123": "סרטן נהרות", 126 | "124": "סרטן נהרות, קראודדי", 127 | "125": "סרטן נזיר", 128 | "126": "שווה-רגלאים, איזופוד", 129 | "127": "חסידה לבנה, Ciconia ciconia", 130 | "128": "חסידה שחורה, Ciconia nigra", 131 | "129": "כפן", 132 | "130": "פלמינגו", 133 | "131": "אנפה כחולה קטנה, Egretta caerulea", 134 | "132": "אנפה אמריקאית, אנפה לבנה גדולה, אגרטה אלבוס", 135 | "133": "אנפיות", 136 | "134": "עגורן", 137 | "135": "ארמוס, Aramus pictus", 138 | "136": "גלינול אירופי, Porphyrio porphyrio", 139 | "137": "אגמייה אמריקאית", 140 | "138": "חובתיים", 141 | "139": "ארנריה אדמונית, Arenaria interpres", 142 | "140": "חופית אלפינית, Erolia alpina", 143 | "141": "redshank, ביצנית לבנת-כנף", 144 | "142": "חרטומנית", 145 | "143": "שלצדף אמריקני, שלצדף החוף", 146 | "144": "שקנאי", 147 | "145": "פינגווין מלכותי, Aptenodytes patagonica", 148 | "146": "אלבטרוס, mollymawk", 149 | "147": "לווייתן אפור, Eschrichtius gibbosus, Eschrichtius robustus", 150 | "148": "לוויתן קטלן, אורקה, אורקינוס אורקה", 151 | "149": "תחש המשכן", 152 | "150": "אריה ים", 153 | "151": "צ'יוואווה", 154 | "152": "ספנייל יפני, צ'ין יפני", 155 | "153": "כלב מלטזי, טרייר מלטזי, מלטזי", 156 | "154": "פקינז, פקינז, פקה", 157 | "155": "שי טסו", 158 | "156": "קינג צ'ארלס ספניאל", 159 | "157": "כלב פפיון", 160 | "158": "טוי רוסי", 161 | "159": "רידג'בק רודזי", 162 | "160": "כלב אפגני", 163 | "161": "באסט האונד", 164 | "162": "ביגל", 165 | "163": "loodhound, כלב דם", 166 | "164": "בלוטיק קונהאונד", 167 | "165": "קונהאונד שחור וחום", 168 | "166": "Walker hound", 169 | "167": "פוקסהאונד אנגלי", 170 | "168": "Redbone Coonhound", 171 | "169": "בורזוי, כלב זאב רוסי", 172 | "170": "וולפהאונד אירי, כלב זאב אירי", 173 | "171": "כלב גרייהאונד איטלקי", 174 | "172": "ויפט", 175 | "173": "איביזן האונד", 176 | "174": "כלב נורווגי, אלקהאונד נורבגי", 177 | "175": "אוטרהאונד", 178 | "176": "סלוקי, כלב צבי", 179 | "177": "דירהאונד סקוטי, כלב צבי", 180 | "178": "ויימרנר", 181 | "179": "סטפורדשייר בולטרייר, סטפורדשייר בולטרייר", 182 | "180": "סטאפורדשייר טרייר אמריקאי", 183 | "181": "בדלינגטון טרייר", 184 | "182": "בורדר טרייר", 185 | "183": "קרי כחול טרייר", 186 | "184": "טרייר אירי", 187 | "185": "נורפולק טרייר", 188 | "186": "נוריץ' טרייר", 189 | "187": "יורקשייר טרייר", 190 | "188": "פוקס טרייר מסומר שיער", 191 | "189": "טרייר לייקלנד", 192 | "190": "טרייר סיליהאם, סיליהאם", 193 | "191": "איירדייל, טרייר איירדייל", 194 | "192": "קאירן טרייר", 195 | "193": "טרייר אוסטרלי", 196 | "194": "דנדי דינמונט טרייר", 197 | "195": "בוסטון בול, טרייר בוסטון", 198 | "196": "שנאוצר זעיר", 199 | "197": "שנאוצר ענק", 200 | "198": "שנאוצר בינוני", 201 | "199": "סקוטיש טרייר", 202 | "200": "טרייר טיבטי, chrysanthemum dog", 203 | "201": "טרייר סילקי אוסטרלי", 204 | "202": "טרייר חיטה רך-פרווה", 205 | "203": "וסט היילנד וייט טרייר", 206 | "204": "כלב להאסה אפסו", 207 | "205": "רטריבר חלק-שיער", 208 | "206": "רטריבר מתולתל", 209 | "207": "גולדן רטריבר", 210 | "208": "לברדור רטריבר", 211 | "209": "צ'ספיק ביי רטריבר", 212 | "210": "פוינטר גרמני קצר שיער", 213 | "211": "vizsla, ויסלה", 214 | "212": "סטר אנגלי", 215 | "213": "סטר אירי", 216 | "214": "גורדון סטר", 217 | "215": "ספנייל בריטני", 218 | "216": "קלמבר, ספנייל קלמבר", 219 | "217": "ספרינגר אנגלי, ספרינגר ספנייל אנגלי", 220 | "218": "ספרינגר ספנייל וולשי", 221 | "219": "קוקר ספנייל, קוקר ספנייל אנגלי, קוקר", 222 | "220": "ספנייל סאסקס", 223 | "221": "ספנייל מים אירי", 224 | "222": "קוואז", 225 | "223": "סכיפרקה", 226 | "224": "גרוננדל", 227 | "225": "מלינואה, רועה בלגי", 228 | "226": "בריארד", 229 | "227": "קלפי, קלפי אוסטראלי", 230 | "228": "קומונדור", 231 | "229": "כלב רועים אנגלי ישן, בובטייל", 232 | "230": "כלב רועים שלטי", 233 | "231": "קולי", 234 | "232": "בורדר קולי", 235 | "233": "בוביה דה פלאנדר", 236 | "234": "רוטוויילר", 237 | "235": "רועה גרמני, כלב רועה גרמני, כלב משטרה גרמני, אלזס", 238 | "236": "דוברמן, פינצ'ר דוברמן", 239 | "237": "פינשר מיניאטורי", 240 | "238": "כלב ההרים השוויצרי ענק", 241 | "239": "ברנר זננהונד", 242 | "240": "אפנצלר זננהונד", 243 | "241": "אנטלבוכר זננהונד", 244 | "242": "בוקסר", 245 | "243": "בול מסטיף", 246 | "244": "מסטיף טיבטי", 247 | "245": "בולדוג צרפתי", 248 | "246": "דני ענק", 249 | "247": "סנט ברנרד, סנט ברנרד", 250 | "248": "אסקימו אמריקאי", 251 | "249": "אלסקן מלמוט", 252 | "250": "האסקי סיבירי", 253 | "251": "דלמטי, כלב מאמן, כלב כרכרה", 254 | "252": "אפנפינצ'ר", 255 | "253": "בסנג'י", 256 | "254": "פאג, פאג-כלב", 257 | "255": "לאונברג", 258 | "256": "כלב ניופאונדלנד, ניופאונדלנד", 259 | "257": "כלב הרים פירנאי", 260 | "258": "סמויד", 261 | "259": "פומרניאן", 262 | "260": "צ'או צ'או", 263 | "261": "קייסהונד", 264 | "262": "ברבנקון גריפון", 265 | "263": "ולש קורגי", 266 | "264": "קרדיגן, קרדיגן וולשי קורגי", 267 | "265": "פודל צעצוע", 268 | "266": "פודל מיניאטורי", 269 | "267": "פודל רגיל", 270 | "268": "כלב מקסיקני חסר שיער", 271 | "269": "זאב מזרחי", 272 | "270": "זאב טונדרה אלסקי", 273 | "271": "זאב אדמוני", 274 | "272": "זאב ערבות", 275 | "273": "Canis dingo ,כלב דינגו ", 276 | "274": "דול מצוי", 277 | "275": "זאב טלוא", 278 | "276": "צבוע, צבוע", 279 | "277": "שועל מצוי", 280 | "278": "שועל מקרוטיס", 281 | "279": "שועל שלג", 282 | "280": "שועל אפור מצוי", 283 | "281": "טאבי, חתול טאבי", 284 | "282": "חתול נמר", 285 | "283": "חתול פרסי", 286 | "284": "חתול סיאמי, סיאמי", 287 | "285": "חתול מצרי", 288 | "286": "קוגר, פומה, פומת ההרים", 289 | "287": "catamount ,לינקס", 290 | "288": "נמר, פנתרה פרדוס", 291 | "289": "נמר שלג", 292 | "290": "יגואר", 293 | "291": "אריה, מלך החיות, פנתרה ליאו", 294 | "292": "נמר, פנתרה טיגריס", 295 | "293": "צ'יטה, ברדלס", 296 | "294": "דוב חום", 297 | "295": "דוב שחור אמריקאי", 298 | "296": "דוב קוטב", 299 | "297": "דוב שפתני", 300 | "298": "נמייה", 301 | "299": "סוריקטה", 302 | "300": "חיפושית נמר, גדיתים", 303 | "301": "פרת משה רבנו", 304 | "302": "חיפושית טחונה, רצניתיים", 305 | "303": "חיפושית ארוכת קרן, יקרוניתיים", 306 | "304": "חיפושית עלים, עליתיים", 307 | "305": "חיפושית זבל", 308 | "306": "חיפושית קרנף", 309 | "307": "חדקונית", 310 | "308": "לעוף", 311 | "309": "דבורה", 312 | "310": "נמלה", 313 | "311": "חגב=", 314 | "312": "קריקט", 315 | "313": "חרק מקל", 316 | "314": "ג'וק, מקק", 317 | "315": "גמל שלמה", 318 | "316": "ציקדה", 319 | "317": "חגב leafhopper", 320 | "318": "זבוב lacewing", 321 | "319": "שפירית", 322 | "320": "שפרירית", 323 | "321": "אדמירל", 324 | "322": "פרפר צלצול", 325 | "323": "מונרך, פרפר מונרך, פרפר חלב, דנאוס מקלעת", 326 | "324": "פרפר כרוב", 327 | "325": "פרפר גופרית, פרפר גופרית", 328 | "326": "פרפר כחליליים", 329 | "327": "כוכב ים, כוכב ים", 330 | "328": "קיפוד ים", 331 | "329": "מלפפון ים", 332 | "330": "ארנב cottontail", 333 | "331": "ארנבת", 334 | "332": "ארנב אנגורה", 335 | "333": "אוגר", 336 | "334": "דורבן, קיפוד", 337 | "335": "סנאי ניגר", 338 | "336": "מרמיטה", 339 | "337": "בונה", 340 | "338": "שרקן", 341 | "339": "סוס", 342 | "340": "זברה", 343 | "341": "חזיר בר hog", 344 | "342": "חזיר בר", 345 | "343": "חזיר יבלות", 346 | "344": "היפופוטם, היפופוטם, סוס יאור, היפופוטם אמפיביוס", 347 | "345": "שור", 348 | "346": "תאו מים", 349 | "347": "ביזון", 350 | "348": "ראם", 351 | "349": "כבש גדול-קרניים", 352 | "350": "יעל האלפים", 353 | "351": "בובאל איילי", 354 | "352": "אימפלה", 355 | "353": "צבי", 356 | "354": "גמל ערבי, דרומדרי, קמלוס דרומדריוס", 357 | "355": "לאמה", 358 | "356": "סמור", 359 | "357": "מינק", 360 | "358": "חמוס מבאיש", 361 | "359": "חמוס שחור רגל", 362 | "360": "לוטרה", 363 | "361": "בואש", 364 | "362": "גירית", 365 | "363": "ארמדיל", 366 | "364": "עצלן חיוור-גרון", 367 | "365": "אורנגאוטן, אורנגאוטנג", 368 | "366": "גורילה", 369 | "367": "שימפנזה", 370 | "368": "גיבון לבן-יד", 371 | "369": "קוף סיאמנג", 372 | "370": "גנון, קוף גנון", 373 | "371": "פטאס, קוף הוסאר, אריתרוסבוס פאטאס", 374 | "372": "בבון", 375 | "373": "macaque, מקוק", 376 | "374": "לנגור", 377 | "375": "קולובוס, קוף קולובוס", 378 | "376": "קוף חוטמני", 379 | "377": "פרמיט מרמוסט", 380 | "378": "קפוצ'ין לבן-ראש", 381 | "379": "קוף שאגן", 382 | "380": "titi, titi monkey", 383 | "381": "קוף עכביש שחור", 384 | "382": "קוף סנאי מצוי", 385 | "383": "חתול מדגסקר, למור טבעת זנב", 386 | "384": "אינדרי", 387 | "385": "פיל הודי, פיל אסייתי", 388 | "386": "פיל סוואנה אפריקני", 389 | "387": "פנדה אדומה", 390 | "388": "פנדה ענקית", 391 | "389": "אספירנה ברקודה", 392 | "390": "צלופח", 393 | "391": "טרכון רב-מגנים", 394 | "392": "rock beauty, Holocanthus tricolor דג מסוג", 395 | "393": "שושנונים, דג ליצן", 396 | "394": "חדקן", 397 | "395": "שיפודן מובהק", 398 | "396": "זהרון", 399 | "397": "דג נפוח", 400 | "398": "חשבונייה", 401 | "399": "לבוש אביה", 402 | "400": "חלוק אקדמי, חלוק שופט", 403 | "401": "אקורדיון", 404 | "402": "גיטרה אקוסטית", 405 | "403": "נושאת מטוסים, נושאת מטוסים, נושאת מטוסים תקיפה", 406 | "404": "מטוס נוסעים", 407 | "405": "ספינת אוויר, ניתנת לניהול", 408 | "406": "מזבח", 409 | "407": "אמבולנס", 410 | "408": "רכב אמפיבי", 411 | "409": "שעון אנלוגי", 412 | "410": "כוורת, בית דבורים", 413 | "411": "סינר", 414 | "412": "פח אשפה", 415 | "413": "רובה סער, אקדח סער", 416 | "414": "תרמיל", 417 | "415": "מאפייה", 418 | "416": "חמור התעמלות", 419 | "417": "בלון", 420 | "418": "עט כדור", 421 | "419": "פלסטר", 422 | "420": "בנג'ו", 423 | "421": "מעקה", 424 | "422": "משקולת", 425 | "423": "כיסא ספר", 426 | "424": "מספרה", 427 | "425": "אסם", 428 | "426": "ברומטר", 429 | "427": "חבית, חבית", 430 | "428": "מריצה", 431 | "429": "בייסבול", 432 | "430": "כדורסל", 433 | "431": "כיסנית", 434 | "432": "כלי נגינה בסון", 435 | "433": "כובע רחצה, כובע שחייה", 436 | "434": "מגבת רחצה", 437 | "435": "אמבטיה, אמבטיה, אמבטיה, אמבטיה", 438 | "436": "עגלת חוף, סטיישן, עגלה, מכונית אחוזה, עגלה חוף, סטיישן, עגלה", 439 | "437": "משואה, מגדלור, אור משואה, פארוס", 440 | "438": "כוס מדעית", 441 | "439": "כובע פרוותי של שומרים אנגלים", 442 | "440": "בקבוק בירה", 443 | "441": "כוס בירה", 444 | "442": "מיטת פעמון, מיטת פעמון", 445 | "443": "סינר תינוקות", 446 | "444": "אופניים שנבנו לשניים", 447 | "445": "ביקיני,בגד ים דו חלקי", 448 | "446": "קלסר, קלסר טבעת", 449 | "447": "משקפת, משקפי שטח, משקפי אופרה", 450 | "448": "בית ציפורים", 451 | "449": "בית סירות", 452 | "450": "מזחלת", 453 | "451": "עניבת קאובוי", 454 | "452": "מצנפת", 455 | "453": "ארון ספרים", 456 | "454": "חנות ספרים, חנות ספרים, דוכן ספרים", 457 | "455": "פקק בקבוק", 458 | "456": "קשת", 459 | "457": "עניבת פרפר, עניבת פרפר, עניבת פרפר", 460 | "458": "פליז, פלאק", 461 | "459": "חזייה, חזייה, בנדו", 462 | "460": "שובר גלים, חומת ים", 463 | "461": "חושן, אגיס, אגיס", 464 | "462": "מטאטא", 465 | "463": "דלי, דלי", 466 | "464": "אבזם", 467 | "465": "אפוד חסין כדורים", 468 | "466": "רכבת מהירה", 469 | "467": "קצבייה, שוק בשר", 470 | "468": "מונית, פריצה, מונית, מונית", 471 | "469": "קלחת, קלחת", 472 | "470": "נר, אור שעווה", 473 | "471": "תותח", 474 | "472": "קאנו", 475 | "473": "פותחן קופסאות שימורים", 476 | "474": "קרדיגן", 477 | "475": "מראת לרכב", 478 | "476": "קרוסלה, קרוסלה, קרוסלה, סיבוב, מערבולת", 479 | "477": "ערכת נגר, ערכת כלים", 480 | "478": "קרטון", 481 | "479": "גלגל מכונית", 482 | "480": "כספומט, מנפק מזומנים", 483 | "481": "קסטה", 484 | "482": "נגן קסטות", 485 | "483": "טירה", 486 | "484": "קטמרן", 487 | "485": "נגן תקליטורים", 488 | "486": "צ'לו", 489 | "487": "טלפון סלולרי, טלפון נייד", 490 | "488": "שרשרת", 491 | "489": "גדר שרשרת", 492 | "490": "דואר שרשרת", 493 | "491": "מסור שרשרת, מסור שרשרת", 494 | "492": "תיבה", 495 | "493": "שידה", 496 | "494": "פעמון, גונג", 497 | "495": "ארון חרסינה, ארון חרסינה", 498 | "496": "גרב חג המולד", 499 | "497": "כנסייה, בניין כנסייה", 500 | "498": "קולנוע, קולנוע, קולנוע, בית קולנוע, ארמון תמונות", 501 | "499": "סכין קבצים", 502 | "500": "משכן צוק", 503 | "501": "גלימה", 504 | "502": "נעל עץ הולנדית", 505 | "503": "שייקר קוקטיילים", 506 | "504": "ספל קפה", 507 | "505": "קומקום קפה", 508 | "506": "סליל, ספירלה", 509 | "507": "מנעול שילוב תוים", 510 | "508": "מקלדת מחשב, לוח מקשים", 511 | "509": "חנות ממתקים, קונדיטוריה", 512 | "510": "ספינת מכולות", 513 | "511": "מכונית קביורלה", 514 | "512": "חולץ פקקים, בורג בקבוק", 515 | "513": "קורנט, קרן, חצוצרה, חצוצרה", 516 | "514": "מגף בוקרים", 517 | "515": "כובע בוקרים", 518 | "516": "עריסה", 519 | "517": "עגורן", 520 | "518": "קסדת התרסקות", 521 | "519": "ארגז", 522 | "520": "עריסה, מיטת תינוק", 523 | "521": "סיר חרס", 524 | "522": "כדור קרוקט", 525 | "523": "קב", 526 | "524": "קסדת קיראס", 527 | "525": "סכר", 528 | "526": "שולחן עבודה", 529 | "527": "מחשב שולחני", 530 | "528": "טלפון חיוג", 531 | "529": "חיתול", 532 | "530": "שעון דיגיטלי", 533 | "531": "שעון יד דיגיטלי", 534 | "532": "שולחן אוכל, לוח", 535 | "533": "מטלית כלים", 536 | "534": "מדיח כלים, מדיח כלים, מכונת מדיח", 537 | "535": "בלם דיסק, בלם דיסק", 538 | "536": "מזח, רציף, מתקן עגינה", 539 | "537": "מזחלת כלבים, מזחלת כלבים, מזחלת כלבים", 540 | "538": "מבנה כיפתי", 541 | "539": "שטיח לדלת, מחצלת קבלת פנים", 542 | "540": "פלטפורמת קידוח, אסדה ימית", 543 | "541": "תוף, ממברנופון, טימפן", 544 | "542": "מקל תיפוף", 545 | "543": "משקולת", 546 | "544": "תנור הולנדי", 547 | "545": "מאוורר חשמלי, מפוח", 548 | "546": "גיטרה חשמלית", 549 | "547": "קטר חשמלי", 550 | "548": "מרכז בידור", 551 | "549": "מעטפה", 552 | "550": "מכונת אספרסו", 553 | "551": "פודרה", 554 | "552": "צעיף נוצה", 555 | "553": "ארונית תיוק", 556 | "554": "סירת כיבוי אש", 557 | "555": "כבאית", 558 | "556": "מסך אש", 559 | "557": "עמוד דגל", 560 | "558": "חליל, חליל רוחבי", 561 | "559": "כיסא מתקפל", 562 | "560": "קסדת כדורגל", 563 | "561": "מלגזה", 564 | "562": "מזרקה", 565 | "563": "עט נובע", 566 | "564": "מתקן מעל מיטה מיוחדת", 567 | "565": "מכונית משא", 568 | "566": "צופר, קרן", 569 | "567": "מחבת", 570 | "568": "מעיל פרווה", 571 | "569": "משאית זבל, עגלת אבק", 572 | "570": "מסיכת גז, מכונת הנשמה, קסדת גז", 573 | "571": "משאבת דלק, משאבת בנזין", 574 | "572": "גביע", 575 | "573": "קארטינג", 576 | "574": "כדור גולף", 577 | "575": "עגלת גולף, עגלת גולף", 578 | "576": "גונדולה", 579 | "577": "גונג, טם-טאם", 580 | "578": "שמלה", 581 | "579": "פסנתר כנף", 582 | "580": "חממה", 583 | "581": "גריל", 584 | "582": "חנות מכולת, מכולת, שוק מזון, שוק", 585 | "583": "גיליוטינה", 586 | "584": "קליפ לשיער", 587 | "585": "ספריי לשיער", 588 | "586": "חצי רצועה", 589 | "587": "פטיש", 590 | "588": "מקשה", 591 | "589": "מפוח ידני, מייבש שיער, מייבש שיער, מייבש שיער", 592 | "590": "מחשב כף יד, מיקרו מחשב כף יד", 593 | "591": "מטפחת, ממחטה", 594 | "592": "דיסק קשיח, דיסק קשיח, דיסק קבוע", 595 | "593": "מפוחית", 596 | "594": "נבל", 597 | "595": "קוצר, קוצר", 598 | "596": "גרזן", 599 | "597": "נרתיק", 600 | "598": "קולנוע ביתי, קולנוע ביתי", 601 | "599": "חלת דבש", 602 | "600": "וו, טופר", 603 | "601": "חצאית חישוקים, קרינולינה", 604 | "602": "מתקן מתח", 605 | "603": "עגלת סוסים, עגלת סוסים", 606 | "604": "שעון חול", 607 | "605": "אייפוד", 608 | "606": "מגהץ", 609 | "607": "ג'ק-או-לנטרן", 610 | "608": "ג'ינס, ג'ינס כחול, ג'ינס", 611 | "609": "ג'יפ, לנדרובר", 612 | "610": "ג'רזי, חולצת טריקו, חולצת טי", 613 | "611": "פאזל", 614 | "612": "ג'ינריקישה, ריקשה, ריקשה", 615 | "613": "ג'ויסטיק", 616 | "614": "קימונו", 617 | "615": "מגן ברכיים", 618 | "616": "קשר", 619 | "617": "מעיל מעבדה, מעיל מעבדה", 620 | "618": "מצקת", 621 | "619": "אהיל, אהיל", 622 | "620": "מחשב נייד, מחשב נייד", 623 | "621": "מכסחת דשא, מכסחת", 624 | "622": "כובע עדשה, כיסוי עדשה", 625 | "623": "פותחן מכתבים, סכין נייר, סכין נייר", 626 | "624": "ספרייה", 627 | "625": "סירת הצלה", 628 | "626": "מצית, אור, מצת, מצת", 629 | "627": "לימוזינה, לימוזינה", 630 | "628": "אנית, אוניית אוקיינוס", 631 | "629": "שפתון, אודם שפתיים", 632 | "630": "כפכף", 633 | "631": "תחליב", 634 | "632": "רמקול, רמקול, יחידת רמקול, מערכת רמקול, מערכת רמקול", 635 | "633": "זכוכית מגדלת של תכשיטנים", 636 | "634": "טחנת עצים, מנסרה", 637 | "635": "מצפן מגנטי", 638 | "636": "שקית דואר, שקית דואר", 639 | "637": "תיבת דואר, תיבת מכתבים", 640 | "638": "בגד ים חתיכה אחת", 641 | "639": "maillot, חליפת טנק", 642 | "640": "מכסה ביוב", 643 | "641": "מאראקס", 644 | "642": "מרימבה, קסילופון", 645 | "643": "מסכה", 646 | "644": "גפרור", 647 | "645": "קוטב מאי", 648 | "646": "מבוך, מבוך", 649 | "647": "כוס מדידה", 650 | "648": "תיבת תרופות, ארון תרופות", 651 | "649": "מגלית, megalith", 652 | "650": "מיקרופון, מיקרופון", 653 | "651": "מיקרוגל, מיקרוגל", 654 | "652": "מדים צבאיים", 655 | "653": "פחית חלב", 656 | "654": "מיניבוס", 657 | "655": "מיני חצאית, מיני", 658 | "656": "מיניוואן", 659 | "657": "טיל", 660 | "658": "כפפה", 661 | "659": "קערת ערבוב", 662 | "660": "בית נייד", 663 | "661": "פורד דגם T", 664 | "662": "מודם", 665 | "663": "מנזר", 666 | "664": "מוניטור", 667 | "665": "טוסטוס", 668 | "666": "מרגמה", 669 | "667": "לוח מרגמה", 670 | "668": "מסגד", 671 | "669": "רשת יתושים", 672 | "670": "קטנוע, קטנוע", 673 | "671": "אופני הרים, אופני שטח, אופני שטח", 674 | "672": "אוהל הרים", 675 | "673": "עכבר, עכבר מחשב", 676 | "674": "מלכודת עכברים", 677 | "675": "טנדר נוסע", 678 | "676": "זמם", 679 | "677": "מסמר", 680 | "678": "סד צוואר", 681 | "679": "שרשרת", 682 | "680": "פטמה", 683 | "681": "מחשב נייד", 684 | "682": "אובליסק", 685 | "683": "אבוב", 686 | "684": "בטטה", 687 | "685": "מד מרחק", 688 | "686": "מסנן שמן", 689 | "687": "עוגב, עוגב מקטרת", 690 | "688": "אוסילוסקופ", 691 | "689": "חצאית על", 692 | "690": "עגלת שוורים", 693 | "691": "מסכת חמצן", 694 | "692": "חבילה", 695 | "693": "משוט, משוט בסירה", 696 | "694": "גלגל ההנעה, גלגל ההנעה", 697 | "695": "מנעול", 698 | "696": "מכחול", 699 | "697": "פיג'מה", 700 | "698": "ארמון", 701 | "699": "חליל פאן", 702 | "700": "מגבת נייר", 703 | "701": "מצנח, מצנח", 704 | "702": "ברים מקבילים, ברים", 705 | "703": "ספסל בפארק", 706 | "704": "מד חניה", 707 | "705": "מכונית נוסעים, אוטובוס, כרכרה", 708 | "706": "פטיו, מרפסת", 709 | "707": "טלפון ציבורי, תחנת תשלום", 710 | "708": "כן, בסיס, דוכן", 711 | "709": "קלמר, קלמר", 712 | "710": "מחדד עפרונות", 713 | "711": "בושם, תמצית", 714 | "712": "צלחת פטרי", 715 | "713": "מכונת צילום", 716 | "714": "מפרט גיטרה", 717 | "715": "קסדת פיקלהאובה", 718 | "716": "גדר כלונס, מחווירה", 719 | "717": "טנדר", 720 | "718": "מזח", 721 | "719": "בנק חזירון, בנק פרוטה", 722 | "720": "בקבוק גלולות", 723 | "721": "כרית", 724 | "722": "כדור פינג-פונג", 725 | "723": "שבשבת", 726 | "724": "פיראט, ספינת פיראטים", 727 | "725": "כד, קנקן", 728 | "726": "מקצועה", 729 | "727": "פלנטריום", 730 | "728": "שקית פלסטיק", 731 | "729": "מתלה לצלחות", 732 | "730": "מחרשה", 733 | "731": "בוכנה", 734 | "732": "מצלמת פולארויד, מצלמת פולארויד לנד", 735 | "733": "עמוד", 736 | "734": "ניידת משטרה", 737 | "735": "פונצ'ו", 738 | "736": "שולחן ביליארד, שולחן ביליארד, שולחן סנוקר", 739 | "737": "בקבוק סודה", 740 | "738": "עציץ, עציץ", 741 | "739": "גלגל קדרים", 742 | "740": "מקדחה כוח", 743 | "741": "שטיח תפילה, מחצלת תפילה", 744 | "742": "מדפסת", 745 | "743": "כלא, בית כלא", 746 | "744": "טיל", 747 | "745": "מקרן", 748 | "746": "פאק הוקי", 749 | "747": "שק אגרוף", 750 | "748": "ארנק", 751 | "749": "נוצה, עט נוצה", 752 | "750": "שמיכה", 753 | "751": "מכונית מירוץ", 754 | "752": "מחבט, מחבט", 755 | "753": "רדיאטור", 756 | "754": "רדיו, אלחוטי", 757 | "755": "צלחת רדיו", 758 | "756": "חבית גשם", 759 | "757": "רכב פנאי, קרוואנים, קרוואנים", 760 | "758": "סליל", 761 | "759": "מצלמת רפלקס", 762 | "760": "מקרר, קופסת קרח", 763 | "761": "שלט רחוק, שלט", 764 | "762": "מסעדה, בית אוכל, מקום אוכל, מסעדה", 765 | "763": "אקדח תופי", 766 | "764": "רובה", 767 | "765": "כיסא נדנדה, נדנדה", 768 | "766": "רוטיסרי", 769 | "767": "מחק גומי, גומי, מחק עיפרון", 770 | "768": "כדור רוגבי", 771 | "769": "סרגל", 772 | "770": "נעל ריצה", 773 | "771": "כספת", 774 | "772": "סיכת ביטחון", 775 | "773": "מלחייה, מלחייה", 776 | "774": "סנדל", 777 | "775": "סרונג, sarong", 778 | "776": "סקסופון, סקסופון", 779 | "777": "נדן", 780 | "778": "קנה מידה, מכונת שקילה", 781 | "779": "אוטובוס בית ספר", 782 | "780": "סירת סקונר", 783 | "781": "לוח תוצאות", 784 | "782": "מסך, טלויזיית CRT", 785 | "783": "בורג", 786 | "784": "מברג", 787 | "785": "חגורת בטיחות, חגורת בטיחות", 788 | "786": "מכונת תפירה", 789 | "787": "אבזם", 790 | "788": "חנות נעליים, חנות נעליים, חנות נעליים", 791 | "789": "שוג'י", 792 | "790": "סל קניות", 793 | "791": "עגלת קניות", 794 | "792": "את", 795 | "793": "כובע מקלחת", 796 | "794": "וילון מקלחת", 797 | "795": "סקי", 798 | "796": "מסיכת סקי", 799 | "797": "שק שינה", 800 | "798": "סרגל חישוב", 801 | "799": "דלת הזזה", 802 | "800": "חריץ, שודד חד זרוע", 803 | "801": "שנורקל", 804 | "802": "אופנוע שלג", 805 | "803": "מפלסת שלג", 806 | "804": "מתקן סבון", 807 | "805": "כדור כדורגל", 808 | "806": "גרב", 809 | "807": "צלחת שמש, קולט שמש, תנור סולארי", 810 | "808": "סומבררו", 811 | "809": "קערת מרק", 812 | "810": "מקש רווח", 813 | "811": "גוף חימום", 814 | "812": "מעבורת חלל", 815 | "813": "מרית", 816 | "814": "סירת מנוע מהירה", 817 | "815": "קורי עכביש, קורי עכביש", 818 | "816": "פלך", 819 | "817": "מכונית ספורט, מכונית ספורט", 820 | "818": "זרקור", 821 | "819": "במה", 822 | "820": "קטר קיטור", 823 | "821": "גשר קשת פלדה", 824 | "822": "תוף פלדה", 825 | "823": "סטטוסקופ", 826 | "824": "שָׁל, רְדִיד, צָעִיף", 827 | "825": "קיר אבן", 828 | "826": "שעון עצר, שעון עצר", 829 | "827": "כיריים", 830 | "828": "מסננת", 831 | "829": "קרונית, חשמלית, חשמלית, עגלה, קרונית עגלה", 832 | "830": "אלונקה", 833 | "831": "ספת סטודיו, מיטת יום", 834 | "832": "סטופה", 835 | "833": "צוללת", 836 | "834": "חליפה, חליפת בגדים", 837 | "835": "משקפי שמש", 838 | "836": "משקפי שמש", 839 | "837": "משקפי שמש, משקפיים כהים, גוונים", 840 | "838": "קרם הגנה, קרם הגנה, חוסם שמש", 841 | "839": "גשר תלוי", 842 | "840": "ספוגית, ספוג, מגב", 843 | "841": "סווטשירט", 844 | "842": "בגדי שחייה, בגדי רחצה", 845 | "843": "נדנדה", 846 | "844": "מתג, מתג חשמלי, מתג חשמלי", 847 | "845": "מזרק", 848 | "846": "מנורת שולחן", 849 | "847": "טנק, טנק צבאי, רכב קרבי משוריין, רכב קרבי משוריין", 850 | "848": "נגן קלטות", 851 | "849": "קומקום", 852 | "850": "טדי, דובון", 853 | "851": "טלוויזיה, מערכת טלוויזיה", 854 | "852": "כדור טניס", 855 | "853": "סכך, גג סכך", 856 | "854": "וילון תיאטרון, וילון תיאטרון", 857 | "855": "אצבעון", 858 | "856": "גורן, גורן, מכונת דיש", 859 | "857": "כס המלכות", 860 | "858": "גג רעפים", 861 | "859": "טוסטר", 862 | "860": "חנות טבק, חנות טבק, חנות טבק", 863 | "861": "מושב שירותים", 864 | "862": "לפיד", 865 | "863": "עמוד טוטם", 866 | "864": "גרר, מכונית גרר", 867 | "865": "חנות צעצועים", 868 | "866": "טרקטור", 869 | "867": "משאית נגרר, נגרר לטרקטור, מתקן הובלה", 870 | "868": "מגש", 871 | "869": "מעיל גשם", 872 | "870": "תלת אופן, תלת אופניים, מנוע מנוע", 873 | "871": "טרימרן", 874 | "872": "חצובה", 875 | "873": "שער הניצחון", 876 | "874": "טרוליבוס", 877 | "875": "טרומבון", 878 | "876": "אמבט, מיכל", 879 | "877": "קרוסלה", 880 | "878": "מקלדת מכונת כתיבה", 881 | "879": "מטריה", 882 | "880": "חד אופן, חד אופן", 883 | "881": "פסנתר זקוף", 884 | "882": "שואב אבק", 885 | "883": "אגרטל", 886 | "884": "כספת", 887 | "885": "קטיפה", 888 | "886": "מכונה אוטומטית", 889 | "887": "חלוק מיוחד", 890 | "888": "ויאדוקט", 891 | "889": "כינור", 892 | "890": "כדורעף", 893 | "891": "טוסטר וופלים", 894 | "892": "שעון קיר", 895 | "893": "ארנק, פנקס", 896 | "894": "ארון בגדים, ארון", 897 | "895": "מטוס קרב, מטוס צבאי", 898 | "896": "כיור", 899 | "897": "מכונת כביסה, מכונת כביסה אוטומטית, מכונת כביסה", 900 | "898": "בקבוק מים", 901 | "899": "כד מים", 902 | "900": "מגדל מים", 903 | "901": "כד וויסקי", 904 | "902": "שריקה", 905 | "903": "פאה", 906 | "904": "רשת", 907 | "905": "וילון", 908 | "906": "עניבת וינדזור", 909 | "907": "בקבוק יין", 910 | "908": "כנף", 911 | "909": "ווק", 912 | "910": "כף עץ", 913 | "911": "צמר", 914 | "912": "גדר עצים", 915 | "913": "הריסות", 916 | "914": "סירת מפרש yawl", 917 | "915": "יורט", 918 | "916": "אתר אינטרנט, אתר אינטרנט, אתר אינטרנט, אתר", 919 | "917": "ספר קומיקס", 920 | "918": "תשבץ, תשבץ", 921 | "919": "תמרור רחוב", 922 | "920": "רמזור, איתות תנועה, רמזור", 923 | "921": "כריכת ספרים", 924 | "922": "תפריט", 925 | "923": "צלחת", 926 | "924": "גוואקמולי", 927 | "925": "קונסומה", 928 | "926": "הוטפוט", 929 | "927": "טרייפל", 930 | "928": "גלידה", 931 | "929": "סוכרייה על מקל", 932 | "930": "בגט", 933 | "931": "בייגל, בייגל", 934 | "932": "בייגלה", 935 | "933": "צ'יזבורגר", 936 | "934": "נקניקייה, נקניקייה, נקניקייה אדומה", 937 | "935": "פירה", 938 | "936": "כרוב הבר", 939 | "937": "ברוקולי", 940 | "938": "כרובית", 941 | "939": "קישוא, קישואים", 942 | "940": "דלעת ספגטי", 943 | "941": "דלעת ערמונים", 944 | "942": "דלעת", 945 | "943": "מלפפון", 946 | "944": "ארטישוק", 947 | "945": "פלפל, גמבה", 948 | "946": "קנרס הגבעול", 949 | "947": "פטריה", 950 | "948": "תפוח גרני סמית'", 951 | "949": "תות", 952 | "950": "תפוז", 953 | "951": "לימון", 954 | "952": "תאנה", 955 | "953": "אננס, אנאנס", 956 | "954": "בננה", 957 | "955": "פרי הג'ק", 958 | "956": "אנונה קשקשית", 959 | "957": "רימון", 960 | "958": "חציר", 961 | "959": "קרבונרה", 962 | "960": "רוטב שוקולד, סירופ שוקולד", 963 | "961": "בצק", 964 | "962": "קציץ בשר", 965 | "963": "פיצה", 966 | "964": "סוג של פאי", 967 | "965": "בוריטו", 968 | "966": "יין אדום", 969 | "967": "אספרסו", 970 | "968": "כוס", 971 | "969": "ליקר ביצים", 972 | "970": "alp", 973 | "971": "בועה", 974 | "972": "צוק", 975 | "973": "שונית אלמוגים", 976 | "974": "גייזר", 977 | "975": "שפת האגם, שפת האגם", 978 | "976": "צוק, לשון יבשה, ראש, חזית", 979 | "977": "שרטון", 980 | "978": "חוף ים, חוף", 981 | "979": "עמק, עמק", 982 | "980": "הר געש", 983 | "981": "שחקן כדור, שחקן בייסבול", 984 | "982": "חתן, חתן", 985 | "983": "צוללן", 986 | "984": "לפתית", 987 | "985": "חיננית", 988 | "986": "פרח נעלי גברת צהובה, נעלי בית צהובות, Cypripedium calceolus, Cypripedium parviflorum", 989 | "987": "תירס", 990 | "988": "בלוט", 991 | "989": "יפ, ורדרד, ורדרד", 992 | "990": "באקי, ערמון סוס, קונקר", 993 | "991": "פטריית אלמוגים", 994 | "992": "אגריק", 995 | "993": "gyromitra", 996 | "994": "קרן סירחון, פטריית נבלות", 997 | "995": "earthstar ,סוג של פטרייה", 998 | "996": "פטריית מאיטקה, תרנגולת היער", 999 | "997": "bolete פטריית", 1000 | "998": "אוזן, ספייק, קפיטול", 1001 | "999": "טישו לשירותים, נייר טואלט, טישו לאמבטיה" 1002 | } -------------------------------------------------------------------------------- /src/misc/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "0": "丁鲷", 3 | "1": "金鱼", 4 | "2": "大白鲨", 5 | "3": "虎鲨", 6 | "4": "锤头鲨", 7 | "5": "电鳐", 8 | "6": "黄貂鱼", 9 | "7": "公鸡", 10 | "8": "母鸡", 11 | "9": "鸵鸟", 12 | "10": "燕雀", 13 | "11": "金翅雀", 14 | "12": "家朱雀", 15 | "13": "灯芯草雀", 16 | "14": "靛蓝雀,靛蓝鸟", 17 | "15": "蓝鹀", 18 | "16": "夜莺", 19 | "17": "松鸦", 20 | "18": "喜鹊", 21 | "19": "山雀", 22 | "20": "河鸟", 23 | "21": "鸢(猛禽)", 24 | "22": "秃头鹰", 25 | "23": "秃鹫", 26 | "24": "大灰猫头鹰", 27 | "25": "欧洲火蝾螈", 28 | "26": "普通蝾螈", 29 | "27": "水蜥", 30 | "28": "斑点蝾螈", 31 | "29": "蝾螈,泥狗", 32 | "30": "牛蛙", 33 | "31": "树蛙", 34 | "32": "尾蛙,铃蟾蜍,肋蟾蜍,尾蟾蜍", 35 | "33": "红海龟", 36 | "34": "皮革龟", 37 | "35": "泥龟", 38 | "36": "淡水龟", 39 | "37": "箱龟", 40 | "38": "带状壁虎", 41 | "39": "普通鬣蜥", 42 | "40": "美国变色龙", 43 | "41": "鞭尾蜥蜴", 44 | "42": "飞龙科蜥蜴", 45 | "43": "褶边蜥蜴", 46 | "44": "鳄鱼蜥蜴", 47 | "45": "毒蜥", 48 | "46": "绿蜥蜴", 49 | "47": "非洲变色龙", 50 | "48": "科莫多蜥蜴", 51 | "49": "非洲鳄,尼罗河鳄鱼", 52 | "50": "美国鳄鱼,鳄鱼", 53 | "51": "三角龙", 54 | "52": "雷蛇,蠕虫蛇", 55 | "53": "环蛇,环颈蛇", 56 | "54": "希腊蛇", 57 | "55": "绿蛇,草蛇", 58 | "56": "国王蛇", 59 | "57": "袜带蛇,草蛇", 60 | "58": "水蛇", 61 | "59": "藤蛇", 62 | "60": "夜蛇", 63 | "61": "大蟒蛇", 64 | "62": "岩石蟒蛇,岩蛇,蟒蛇", 65 | "63": "印度眼镜蛇", 66 | "64": "绿曼巴", 67 | "65": "海蛇", 68 | "66": "角腹蛇", 69 | "67": "菱纹响尾蛇", 70 | "68": "角响尾蛇", 71 | "69": "三叶虫", 72 | "70": "盲蜘蛛", 73 | "71": "蝎子", 74 | "72": "黑金花园蜘蛛", 75 | "73": "谷仓蜘蛛", 76 | "74": "花园蜘蛛", 77 | "75": "黑寡妇蜘蛛", 78 | "76": "狼蛛", 79 | "77": "狼蜘蛛,狩猎蜘蛛", 80 | "78": "壁虱", 81 | "79": "蜈蚣", 82 | "80": "黑松鸡", 83 | "81": "松鸡,雷鸟", 84 | "82": "披肩鸡,披肩榛鸡", 85 | "83": "草原鸡,草原松鸡", 86 | "84": "孔雀", 87 | "85": "鹌鹑", 88 | "86": "鹧鸪", 89 | "87": "非洲灰鹦鹉", 90 | "88": "金刚鹦鹉", 91 | "89": "硫冠鹦鹉", 92 | "90": "短尾鹦鹉", 93 | "91": "褐翅鸦鹃", 94 | "92": "蜜蜂", 95 | "93": "犀鸟", 96 | "94": "蜂鸟", 97 | "95": "鹟䴕", 98 | "96": "犀鸟", 99 | "97": "野鸭", 100 | "98": "红胸秋沙鸭", 101 | "99": "鹅", 102 | "100": "黑天鹅", 103 | "101": "大象", 104 | "102": "针鼹鼠", 105 | "103": "鸭嘴兽", 106 | "104": "沙袋鼠", 107 | "105": "考拉,考拉熊", 108 | "106": "袋熊", 109 | "107": "水母", 110 | "108": "海葵", 111 | "109": "脑珊瑚", 112 | "110": "扁形虫扁虫", 113 | "111": "线虫,蛔虫", 114 | "112": "海螺", 115 | "113": "蜗牛", 116 | "114": "鼻涕虫", 117 | "115": "海参", 118 | "116": "石鳖", 119 | "117": "鹦鹉螺", 120 | "118": "珍宝蟹", 121 | "119": "石蟹", 122 | "120": "招潮蟹", 123 | "121": "帝王蟹,阿拉斯加蟹,阿拉斯加帝王蟹", 124 | "122": "美国龙虾,缅因州龙虾", 125 | "123": "大螯虾", 126 | "124": "小龙虾", 127 | "125": "寄居蟹", 128 | "126": "等足目动物(明虾和螃蟹近亲)", 129 | "127": "白鹳", 130 | "128": "黑鹳", 131 | "129": "鹭", 132 | "130": "火烈鸟", 133 | "131": "小蓝鹭", 134 | "132": "美国鹭,大白鹭", 135 | "133": "麻鸦", 136 | "134": "鹤", 137 | "135": "秧鹤", 138 | "136": "欧洲水鸡,紫水鸡", 139 | "137": "沼泽泥母鸡,水母鸡", 140 | "138": "鸨", 141 | "139": "红翻石鹬", 142 | "140": "红背鹬,黑腹滨鹬", 143 | "141": "红脚鹬", 144 | "142": "半蹼鹬", 145 | "143": "蛎鹬", 146 | "144": "鹈鹕", 147 | "145": "国王企鹅", 148 | "146": "信天翁,大海鸟", 149 | "147": "灰鲸", 150 | "148": "杀人鲸,逆戟鲸,虎鲸", 151 | "149": "海牛", 152 | "150": "海狮", 153 | "151": "奇瓦瓦", 154 | "152": "日本猎犬", 155 | "153": "马尔济斯犬", 156 | "154": "狮子狗", 157 | "155": "西施犬", 158 | "156": "布莱尼姆猎犬", 159 | "157": "巴比狗", 160 | "158": "玩具犬", 161 | "159": "罗得西亚长背猎狗", 162 | "160": "阿富汗猎犬", 163 | "161": "猎犬", 164 | "162": "比格犬,猎兔犬", 165 | "163": "侦探犬", 166 | "164": "蓝色快狗", 167 | "165": "黑褐猎浣熊犬", 168 | "166": "沃克猎犬", 169 | "167": "英国猎狐犬", 170 | "168": "美洲赤狗", 171 | "169": "俄罗斯猎狼犬", 172 | "170": "爱尔兰猎狼犬", 173 | "171": "意大利灰狗", 174 | "172": "惠比特犬", 175 | "173": "依比沙猎犬", 176 | "174": "挪威猎犬", 177 | "175": "奥达猎犬,水獭猎犬", 178 | "176": "沙克犬,瞪羚猎犬", 179 | "177": "苏格兰猎鹿犬,猎鹿犬", 180 | "178": "威玛猎犬", 181 | "179": "斯塔福德郡牛头梗,斯塔福德郡斗牛梗", 182 | "180": "美国斯塔福德郡梗,美国比特斗牛梗,斗牛梗", 183 | "181": "贝德灵顿梗", 184 | "182": "边境梗", 185 | "183": "凯丽蓝梗", 186 | "184": "爱尔兰梗", 187 | "185": "诺福克梗", 188 | "186": "诺维奇梗", 189 | "187": "约克郡梗", 190 | "188": "刚毛猎狐梗", 191 | "189": "莱克兰梗", 192 | "190": "锡利哈姆梗", 193 | "191": "艾尔谷犬", 194 | "192": "凯恩梗", 195 | "193": "澳大利亚梗", 196 | "194": "丹迪丁蒙梗", 197 | "195": "波士顿梗", 198 | "196": "迷你雪纳瑞犬", 199 | "197": "巨型雪纳瑞犬", 200 | "198": "标准雪纳瑞犬", 201 | "199": "苏格兰梗", 202 | "200": "西藏梗,菊花狗", 203 | "201": "丝毛梗", 204 | "202": "软毛麦色梗", 205 | "203": "西高地白梗", 206 | "204": "拉萨阿普索犬", 207 | "205": "平毛寻回犬", 208 | "206": "卷毛寻回犬", 209 | "207": "金毛猎犬", 210 | "208": "拉布拉多猎犬", 211 | "209": "乞沙比克猎犬", 212 | "210": "德国短毛猎犬", 213 | "211": "维兹拉犬", 214 | "212": "英国谍犬", 215 | "213": "爱尔兰雪达犬,红色猎犬", 216 | "214": "戈登雪达犬", 217 | "215": "布列塔尼犬猎犬", 218 | "216": "黄毛,黄毛猎犬", 219 | "217": "英国史宾格犬", 220 | "218": "威尔士史宾格犬", 221 | "219": "可卡犬,英国可卡犬", 222 | "220": "萨塞克斯猎犬", 223 | "221": "爱尔兰水猎犬", 224 | "222": "哥威斯犬", 225 | "223": "舒柏奇犬", 226 | "224": "比利时牧羊犬", 227 | "225": "马里努阿犬", 228 | "226": "伯瑞犬", 229 | "227": "凯尔皮犬", 230 | "228": "匈牙利牧羊犬", 231 | "229": "老英国牧羊犬", 232 | "230": "喜乐蒂牧羊犬", 233 | "231": "牧羊犬", 234 | "232": "边境牧羊犬", 235 | "233": "法兰德斯牧牛狗", 236 | "234": "罗特韦尔犬", 237 | "235": "德国牧羊犬,德国警犬,阿尔萨斯", 238 | "236": "多伯曼犬,杜宾犬", 239 | "237": "迷你杜宾犬", 240 | "238": "大瑞士山地犬", 241 | "239": "伯恩山犬", 242 | "240": "Appenzeller狗", 243 | "241": "EntleBucher狗", 244 | "242": "拳师狗", 245 | "243": "斗牛獒", 246 | "244": "藏獒", 247 | "245": "法国斗牛犬", 248 | "246": "大丹犬", 249 | "247": "圣伯纳德狗", 250 | "248": "爱斯基摩犬,哈士奇", 251 | "249": "雪橇犬,阿拉斯加爱斯基摩狗", 252 | "250": "哈士奇", 253 | "251": "达尔马提亚,教练车狗", 254 | "252": "狮毛狗", 255 | "253": "巴辛吉狗", 256 | "254": "哈巴狗,狮子狗", 257 | "255": "莱昂贝格狗", 258 | "256": "纽芬兰岛狗", 259 | "257": "大白熊犬", 260 | "258": "萨摩耶犬", 261 | "259": "博美犬", 262 | "260": "松狮,松狮", 263 | "261": "荷兰卷尾狮毛狗", 264 | "262": "布鲁塞尔格林芬犬", 265 | "263": "彭布洛克威尔士科基犬", 266 | "264": "威尔士柯基犬", 267 | "265": "玩具贵宾犬", 268 | "266": "迷你贵宾犬", 269 | "267": "标准贵宾犬", 270 | "268": "墨西哥无毛犬", 271 | "269": "灰狼", 272 | "270": "白狼,北极狼", 273 | "271": "红太狼,鬃狼,犬犬鲁弗斯", 274 | "272": "狼,草原狼,刷狼,郊狼", 275 | "273": "澳洲野狗,澳大利亚野犬", 276 | "274": "豺", 277 | "275": "非洲猎犬,土狼犬", 278 | "276": "鬣狗", 279 | "277": "红狐狸", 280 | "278": "沙狐", 281 | "279": "北极狐狸,白狐狸", 282 | "280": "灰狐狸", 283 | "281": "虎斑猫", 284 | "282": "山猫,虎猫", 285 | "283": "波斯猫", 286 | "284": "暹罗暹罗猫,", 287 | "285": "埃及猫", 288 | "286": "美洲狮,美洲豹", 289 | "287": "猞猁,山猫", 290 | "288": "豹子", 291 | "289": "雪豹", 292 | "290": "美洲虎", 293 | "291": "狮子", 294 | "292": "老虎", 295 | "293": "猎豹", 296 | "294": "棕熊", 297 | "295": "美洲黑熊", 298 | "296": "冰熊,北极熊", 299 | "297": "懒熊", 300 | "298": "猫鼬", 301 | "299": "猫鼬,海猫", 302 | "300": "虎甲虫", 303 | "301": "瓢虫", 304 | "302": "土鳖虫", 305 | "303": "天牛", 306 | "304": "龟甲虫", 307 | "305": "粪甲虫", 308 | "306": "犀牛甲虫", 309 | "307": "象甲", 310 | "308": "苍蝇", 311 | "309": "蜜蜂", 312 | "310": "蚂蚁", 313 | "311": "蚱蜢", 314 | "312": "蟋蟀", 315 | "313": "竹节虫", 316 | "314": "蟑螂", 317 | "315": "螳螂", 318 | "316": "蝉", 319 | "317": "叶蝉", 320 | "318": "草蜻蛉", 321 | "319": "蜻蜓", 322 | "320": "豆娘,蜻蛉", 323 | "321": "优红蛱蝶", 324 | "322": "小环蝴蝶", 325 | "323": "君主蝴蝶,大斑蝶", 326 | "324": "菜粉蝶", 327 | "325": "白蝴蝶", 328 | "326": "灰蝶", 329 | "327": "海星", 330 | "328": "海胆", 331 | "329": "海参,海黄瓜", 332 | "330": "野兔", 333 | "331": "兔", 334 | "332": "安哥拉兔", 335 | "333": "仓鼠", 336 | "334": "刺猬,豪猪,", 337 | "335": "黑松鼠", 338 | "336": "土拨鼠", 339 | "337": "海狸", 340 | "338": "豚鼠,豚鼠", 341 | "339": "栗色马", 342 | "340": "斑马", 343 | "341": "猪", 344 | "342": "野猪", 345 | "343": "疣猪", 346 | "344": "河马", 347 | "345": "牛", 348 | "346": "水牛,亚洲水牛", 349 | "347": "野牛", 350 | "348": "公羊", 351 | "349": "大角羊,洛矶山大角羊", 352 | "350": "山羊", 353 | "351": "狷羚", 354 | "352": "黑斑羚", 355 | "353": "瞪羚", 356 | "354": "阿拉伯单峰骆驼,骆驼", 357 | "355": "骆驼", 358 | "356": "黄鼠狼", 359 | "357": "水貂", 360 | "358": "臭猫", 361 | "359": "黑足鼬", 362 | "360": "水獭", 363 | "361": "臭鼬,木猫", 364 | "362": "獾", 365 | "363": "犰狳", 366 | "364": "树懒", 367 | "365": "猩猩,婆罗洲猩猩", 368 | "366": "大猩猩", 369 | "367": "黑猩猩", 370 | "368": "长臂猿", 371 | "369": "合趾猿长臂猿,合趾猿", 372 | "370": "长尾猴", 373 | "371": "赤猴", 374 | "372": "狒狒", 375 | "373": "恒河猴,猕猴", 376 | "374": "白头叶猴", 377 | "375": "疣猴", 378 | "376": "长鼻猴", 379 | "377": "狨(美洲产小型长尾猴)", 380 | "378": "卷尾猴", 381 | "379": "吼猴", 382 | "380": "伶猴", 383 | "381": "蜘蛛猴", 384 | "382": "松鼠猴", 385 | "383": "马达加斯加环尾狐猴,鼠狐猴", 386 | "384": "大狐猴,马达加斯加大狐猴", 387 | "385": "印度大象,亚洲象", 388 | "386": "非洲象,非洲象", 389 | "387": "小熊猫", 390 | "388": "大熊猫", 391 | "389": "杖鱼", 392 | "390": "鳗鱼", 393 | "391": "银鲑,银鲑鱼", 394 | "392": "三色刺蝶鱼", 395 | "393": "海葵鱼", 396 | "394": "鲟鱼", 397 | "395": "雀鳝", 398 | "396": "狮子鱼", 399 | "397": "河豚", 400 | "398": "算盘", 401 | "399": "长袍", 402 | "400": "学位袍", 403 | "401": "手风琴", 404 | "402": "原声吉他", 405 | "403": "航空母舰", 406 | "404": "客机", 407 | "405": "飞艇", 408 | "406": "祭坛", 409 | "407": "救护车", 410 | "408": "水陆两用车", 411 | "409": "模拟时钟", 412 | "410": "蜂房", 413 | "411": "围裙", 414 | "412": "垃圾桶", 415 | "413": "攻击步枪,枪", 416 | "414": "背包", 417 | "415": "面包店,面包铺,", 418 | "416": "平衡木", 419 | "417": "热气球", 420 | "418": "圆珠笔", 421 | "419": "创可贴", 422 | "420": "班卓琴", 423 | "421": "栏杆,楼梯扶手", 424 | "422": "杠铃", 425 | "423": "理发师的椅子", 426 | "424": "理发店", 427 | "425": "牲口棚", 428 | "426": "晴雨表", 429 | "427": "圆筒", 430 | "428": "园地小车,手推车", 431 | "429": "棒球", 432 | "430": "篮球", 433 | "431": "婴儿床", 434 | "432": "巴松管,低音管", 435 | "433": "游泳帽", 436 | "434": "沐浴毛巾", 437 | "435": "浴缸,澡盆", 438 | "436": "沙滩车,旅行车", 439 | "437": "灯塔", 440 | "438": "高脚杯", 441 | "439": "熊皮高帽", 442 | "440": "啤酒瓶", 443 | "441": "啤酒杯 ", 444 | "442": "钟塔", 445 | "443": "(小儿用的)围嘴", 446 | "444": "串联自行车,", 447 | "445": "比基尼", 448 | "446": "装订册", 449 | "447": "双筒望远镜", 450 | "448": "鸟舍", 451 | "449": "船库", 452 | "450": "雪橇", 453 | "451": "饰扣式领带", 454 | "452": "阔边女帽", 455 | "453": "书橱", 456 | "454": "书店,书摊", 457 | "455": "瓶盖", 458 | "456": "弓箭", 459 | "457": "蝴蝶结领结", 460 | "458": "铜制牌位", 461 | "459": "奶罩", 462 | "460": "防波堤,海堤", 463 | "461": "铠甲", 464 | "462": "扫帚", 465 | "463": "桶", 466 | "464": "扣环", 467 | "465": "防弹背心", 468 | "466": "动车,子弹头列车", 469 | "467": "肉铺,肉菜市场", 470 | "468": "出租车", 471 | "469": "大锅", 472 | "470": "蜡烛", 473 | "471": "大炮", 474 | "472": "独木舟", 475 | "473": "开瓶器,开罐器", 476 | "474": "开衫", 477 | "475": "车镜", 478 | "476": "旋转木马", 479 | "477": "木匠的工具包,工具包", 480 | "478": "纸箱", 481 | "479": "车轮", 482 | "480": "取款机,自动取款机", 483 | "481": "盒式录音带", 484 | "482": "卡带播放器", 485 | "483": "城堡", 486 | "484": "双体船", 487 | "485": "CD播放器", 488 | "486": "大提琴", 489 | "487": "移动电话,手机", 490 | "488": "铁链", 491 | "489": "围栏", 492 | "490": "链甲", 493 | "491": "电锯,油锯", 494 | "492": "箱子", 495 | "493": "衣柜,洗脸台", 496 | "494": "编钟,钟,锣", 497 | "495": "中国橱柜", 498 | "496": "圣诞袜", 499 | "497": "教堂,教堂建筑", 500 | "498": "电影院,剧场", 501 | "499": "切肉刀,菜刀", 502 | "500": "悬崖屋", 503 | "501": "斗篷", 504 | "502": "木屐,木鞋", 505 | "503": "鸡尾酒调酒器", 506 | "504": "咖啡杯", 507 | "505": "咖啡壶", 508 | "506": "螺旋结构(楼梯)", 509 | "507": "组合锁", 510 | "508": "电脑键盘,键盘", 511 | "509": "糖果,糖果店", 512 | "510": "集装箱船", 513 | "511": "敞篷车", 514 | "512": "开瓶器,瓶螺杆", 515 | "513": "短号,喇叭", 516 | "514": "牛仔靴", 517 | "515": "牛仔帽", 518 | "516": "摇篮", 519 | "517": "起重机", 520 | "518": "头盔", 521 | "519": "板条箱", 522 | "520": "小儿床", 523 | "521": "砂锅", 524 | "522": "槌球", 525 | "523": "拐杖", 526 | "524": "胸甲", 527 | "525": "大坝,堤防", 528 | "526": "书桌", 529 | "527": "台式电脑", 530 | "528": "有线电话", 531 | "529": "尿布湿", 532 | "530": "数字时钟", 533 | "531": "数字手表", 534 | "532": "餐桌板", 535 | "533": "抹布", 536 | "534": "洗碗机,洗碟机", 537 | "535": "盘式制动器", 538 | "536": "码头,船坞,码头设施", 539 | "537": "狗拉雪橇", 540 | "538": "圆顶", 541 | "539": "门垫,垫子", 542 | "540": "钻井平台,海上钻井", 543 | "541": "鼓,乐器,鼓膜", 544 | "542": "鼓槌", 545 | "543": "哑铃", 546 | "544": "荷兰烤箱", 547 | "545": "电风扇,鼓风机", 548 | "546": "电吉他", 549 | "547": "电力机车", 550 | "548": "电视,电视柜", 551 | "549": "信封", 552 | "550": "浓缩咖啡机", 553 | "551": "扑面粉", 554 | "552": "女用长围巾", 555 | "553": "文件,文件柜,档案柜", 556 | "554": "消防船", 557 | "555": "消防车", 558 | "556": "火炉栏", 559 | "557": "旗杆", 560 | "558": "长笛", 561 | "559": "折叠椅", 562 | "560": "橄榄球头盔", 563 | "561": "叉车", 564 | "562": "喷泉", 565 | "563": "钢笔", 566 | "564": "有四根帷柱的床", 567 | "565": "运货车厢", 568 | "566": "圆号,喇叭", 569 | "567": "煎锅", 570 | "568": "裘皮大衣", 571 | "569": "垃圾车", 572 | "570": "防毒面具,呼吸器", 573 | "571": "汽油泵", 574 | "572": "高脚杯", 575 | "573": "卡丁车", 576 | "574": "高尔夫球", 577 | "575": "高尔夫球车", 578 | "576": "狭长小船", 579 | "577": "锣", 580 | "578": "礼服", 581 | "579": "钢琴", 582 | "580": "温室,苗圃", 583 | "581": "散热器格栅", 584 | "582": "杂货店,食品市场", 585 | "583": "断头台", 586 | "584": "小发夹", 587 | "585": "头发喷雾", 588 | "586": "半履带装甲车", 589 | "587": "锤子", 590 | "588": "大篮子", 591 | "589": "手摇鼓风机,吹风机", 592 | "590": "手提电脑", 593 | "591": "手帕", 594 | "592": "硬盘", 595 | "593": "口琴,口风琴", 596 | "594": "竖琴", 597 | "595": "收割机", 598 | "596": "斧头", 599 | "597": "手枪皮套", 600 | "598": "家庭影院", 601 | "599": "蜂窝", 602 | "600": "钩爪", 603 | "601": "衬裙", 604 | "602": "单杠", 605 | "603": "马车", 606 | "604": "沙漏", 607 | "605": "手机,iPad", 608 | "606": "熨斗", 609 | "607": "南瓜灯笼", 610 | "608": "牛仔裤,蓝色牛仔裤", 611 | "609": "吉普车", 612 | "610": "运动衫,T恤", 613 | "611": "拼图", 614 | "612": "人力车", 615 | "613": "操纵杆", 616 | "614": "和服", 617 | "615": "护膝", 618 | "616": "蝴蝶结", 619 | "617": "大褂,实验室外套", 620 | "618": "长柄勺", 621 | "619": "灯罩", 622 | "620": "笔记本电脑", 623 | "621": "割草机", 624 | "622": "镜头盖", 625 | "623": "开信刀,裁纸刀", 626 | "624": "图书馆", 627 | "625": "救生艇", 628 | "626": "点火器,打火机", 629 | "627": "豪华轿车", 630 | "628": "远洋班轮", 631 | "629": "唇膏,口红", 632 | "630": "平底便鞋", 633 | "631": "洗剂", 634 | "632": "扬声器", 635 | "633": "放大镜", 636 | "634": "锯木厂", 637 | "635": "磁罗盘", 638 | "636": "邮袋", 639 | "637": "信箱", 640 | "638": "女游泳衣", 641 | "639": "有肩带浴衣", 642 | "640": "窨井盖", 643 | "641": "沙球(一种打击乐器)", 644 | "642": "马林巴木琴", 645 | "643": "面膜", 646 | "644": "火柴", 647 | "645": "花柱", 648 | "646": "迷宫", 649 | "647": "量杯", 650 | "648": "药箱", 651 | "649": "巨石,巨石结构", 652 | "650": "麦克风", 653 | "651": "微波炉", 654 | "652": "军装", 655 | "653": "奶桶", 656 | "654": "迷你巴士", 657 | "655": "迷你裙", 658 | "656": "面包车", 659 | "657": "导弹", 660 | "658": "连指手套", 661 | "659": "搅拌钵", 662 | "660": "活动房屋(由汽车拖拉的)", 663 | "661": "T型发动机小汽车", 664 | "662": "调制解调器", 665 | "663": "修道院", 666 | "664": "显示器", 667 | "665": "电瓶车", 668 | "666": "砂浆", 669 | "667": "学士", 670 | "668": "清真寺", 671 | "669": "蚊帐", 672 | "670": "摩托车", 673 | "671": "山地自行车", 674 | "672": "登山帐", 675 | "673": "鼠标,电脑鼠标", 676 | "674": "捕鼠器", 677 | "675": "搬家车", 678 | "676": "口套", 679 | "677": "钉子", 680 | "678": "颈托", 681 | "679": "项链", 682 | "680": "乳头(瓶)", 683 | "681": "笔记本,笔记本电脑", 684 | "682": "方尖碑", 685 | "683": "双簧管", 686 | "684": "陶笛,卵形笛", 687 | "685": "里程表", 688 | "686": "滤油器", 689 | "687": "风琴,管风琴", 690 | "688": "示波器", 691 | "689": "罩裙", 692 | "690": "牛车", 693 | "691": "氧气面罩", 694 | "692": "包装", 695 | "693": "船桨", 696 | "694": "明轮,桨轮", 697 | "695": "挂锁,扣锁", 698 | "696": "画笔", 699 | "697": "睡衣", 700 | "698": "宫殿", 701 | "699": "排箫,鸣管", 702 | "700": "纸巾", 703 | "701": "降落伞", 704 | "702": "双杠", 705 | "703": "公园长椅", 706 | "704": "停车收费表,停车计时器", 707 | "705": "客车,教练车", 708 | "706": "露台,阳台", 709 | "707": "付费电话", 710 | "708": "基座,基脚", 711 | "709": "铅笔盒", 712 | "710": "卷笔刀", 713 | "711": "香水(瓶)", 714 | "712": "培养皿", 715 | "713": "复印机", 716 | "714": "拨弦片,拨子", 717 | "715": "尖顶头盔", 718 | "716": "栅栏,栅栏", 719 | "717": "皮卡,皮卡车", 720 | "718": "桥墩", 721 | "719": "存钱罐", 722 | "720": "药瓶", 723 | "721": "枕头", 724 | "722": "乒乓球", 725 | "723": "风车", 726 | "724": "海盗船", 727 | "725": "水罐", 728 | "726": "木工刨", 729 | "727": "天文馆", 730 | "728": "塑料袋", 731 | "729": "板架", 732 | "730": "犁型铲雪机", 733 | "731": "手压皮碗泵", 734 | "732": "宝丽来相机", 735 | "733": "电线杆", 736 | "734": "警车,巡逻车", 737 | "735": "雨披", 738 | "736": "台球桌", 739 | "737": "充气饮料瓶", 740 | "738": "花盆", 741 | "739": "陶工旋盘", 742 | "740": "电钻", 743 | "741": "祈祷垫,地毯", 744 | "742": "打印机", 745 | "743": "监狱", 746 | "744": "炮弹,导弹", 747 | "745": "投影仪", 748 | "746": "冰球", 749 | "747": "沙包,吊球", 750 | "748": "钱包", 751 | "749": "羽管笔", 752 | "750": "被子", 753 | "751": "赛车", 754 | "752": "球拍", 755 | "753": "散热器", 756 | "754": "收音机", 757 | "755": "射电望远镜,无线电反射器", 758 | "756": "雨桶", 759 | "757": "休闲车,房车", 760 | "758": "卷轴,卷筒", 761 | "759": "反射式照相机", 762 | "760": "冰箱,冰柜", 763 | "761": "遥控器", 764 | "762": "餐厅,饮食店,食堂", 765 | "763": "左轮手枪", 766 | "764": "步枪", 767 | "765": "摇椅", 768 | "766": "电转烤肉架", 769 | "767": "橡皮", 770 | "768": "橄榄球", 771 | "769": "直尺", 772 | "770": "跑步鞋", 773 | "771": "保险柜", 774 | "772": "安全别针", 775 | "773": "盐瓶(调味用)", 776 | "774": "凉鞋", 777 | "775": "纱笼,围裙", 778 | "776": "萨克斯管", 779 | "777": "剑鞘", 780 | "778": "秤,称重机", 781 | "779": "校车", 782 | "780": "帆船", 783 | "781": "记分牌", 784 | "782": "屏幕", 785 | "783": "螺丝", 786 | "784": "螺丝刀", 787 | "785": "安全带", 788 | "786": "缝纫机", 789 | "787": "盾牌,盾牌", 790 | "788": "皮鞋店,鞋店", 791 | "789": "障子", 792 | "790": "购物篮", 793 | "791": "购物车", 794 | "792": "铁锹", 795 | "793": "浴帽", 796 | "794": "浴帘", 797 | "795": "滑雪板", 798 | "796": "滑雪面罩", 799 | "797": "睡袋", 800 | "798": "滑尺", 801 | "799": "滑动门", 802 | "800": "角子老虎机", 803 | "801": "潜水通气管", 804 | "802": "雪橇", 805 | "803": "扫雪机,扫雪机", 806 | "804": "皂液器", 807 | "805": "足球", 808 | "806": "袜子", 809 | "807": "碟式太阳能,太阳能集热器,太阳能炉", 810 | "808": "宽边帽", 811 | "809": "汤碗", 812 | "810": "空格键", 813 | "811": "空间加热器", 814 | "812": "航天飞机", 815 | "813": "铲(搅拌或涂敷用的)", 816 | "814": "快艇", 817 | "815": "蜘蛛网", 818 | "816": "纺锤,纱锭", 819 | "817": "跑车", 820 | "818": "聚光灯", 821 | "819": "舞台", 822 | "820": "蒸汽机车", 823 | "821": "钢拱桥", 824 | "822": "钢滚筒", 825 | "823": "听诊器", 826 | "824": "女用披肩", 827 | "825": "石头墙", 828 | "826": "秒表", 829 | "827": "火炉", 830 | "828": "过滤器", 831 | "829": "有轨电车,电车", 832 | "830": "担架", 833 | "831": "沙发床", 834 | "832": "佛塔", 835 | "833": "潜艇,潜水艇", 836 | "834": "套装,衣服", 837 | "835": "日晷", 838 | "836": "太阳镜", 839 | "837": "太阳镜,墨镜", 840 | "838": "防晒霜,防晒剂", 841 | "839": "悬索桥", 842 | "840": "拖把", 843 | "841": "运动衫", 844 | "842": "游泳裤", 845 | "843": "秋千", 846 | "844": "开关,电器开关", 847 | "845": "注射器", 848 | "846": "台灯", 849 | "847": "坦克,装甲战车,装甲战斗车辆", 850 | "848": "磁带播放器", 851 | "849": "茶壶", 852 | "850": "泰迪,泰迪熊", 853 | "851": "电视", 854 | "852": "网球", 855 | "853": "茅草,茅草屋顶", 856 | "854": "幕布,剧院的帷幕", 857 | "855": "顶针", 858 | "856": "脱粒机", 859 | "857": "宝座", 860 | "858": "瓦屋顶", 861 | "859": "烤面包机", 862 | "860": "烟草店,烟草", 863 | "861": "马桶", 864 | "862": "火炬", 865 | "863": "图腾柱", 866 | "864": "拖车,牵引车,清障车", 867 | "865": "玩具店", 868 | "866": "拖拉机", 869 | "867": "拖车,铰接式卡车", 870 | "868": "托盘", 871 | "869": "风衣", 872 | "870": "三轮车", 873 | "871": "三体船", 874 | "872": "三脚架", 875 | "873": "凯旋门", 876 | "874": "无轨电车", 877 | "875": "长号", 878 | "876": "浴盆,浴缸", 879 | "877": "旋转式栅门", 880 | "878": "打字机键盘", 881 | "879": "伞", 882 | "880": "独轮车", 883 | "881": "直立式钢琴", 884 | "882": "真空吸尘器", 885 | "883": "花瓶", 886 | "884": "拱顶", 887 | "885": "天鹅绒", 888 | "886": "自动售货机", 889 | "887": "祭服", 890 | "888": "高架桥", 891 | "889": "小提琴,小提琴", 892 | "890": "排球", 893 | "891": "松饼机", 894 | "892": "挂钟", 895 | "893": "钱包,皮夹", 896 | "894": "衣柜,壁橱", 897 | "895": "军用飞机", 898 | "896": "洗脸盆,洗手盆", 899 | "897": "洗衣机,自动洗衣机", 900 | "898": "水瓶", 901 | "899": "水壶", 902 | "900": "水塔", 903 | "901": "威士忌壶", 904 | "902": "哨子", 905 | "903": "假发", 906 | "904": "纱窗", 907 | "905": "百叶窗", 908 | "906": "温莎领带", 909 | "907": "葡萄酒瓶", 910 | "908": "飞机翅膀,飞机", 911 | "909": "炒菜锅", 912 | "910": "木制的勺子", 913 | "911": "毛织品,羊绒", 914 | "912": "栅栏,围栏", 915 | "913": "沉船", 916 | "914": "双桅船", 917 | "915": "蒙古包", 918 | "916": "网站,互联网网站", 919 | "917": "漫画", 920 | "918": "纵横字谜", 921 | "919": "路标", 922 | "920": "交通信号灯", 923 | "921": "防尘罩,书皮", 924 | "922": "菜单", 925 | "923": "盘子", 926 | "924": "鳄梨酱", 927 | "925": "清汤", 928 | "926": "罐焖土豆烧肉", 929 | "927": "蛋糕", 930 | "928": "冰淇淋", 931 | "929": "雪糕,冰棍,冰棒", 932 | "930": "法式面包", 933 | "931": "百吉饼", 934 | "932": "椒盐脆饼", 935 | "933": "芝士汉堡", 936 | "934": "热狗", 937 | "935": "土豆泥", 938 | "936": "结球甘蓝", 939 | "937": "西兰花", 940 | "938": "菜花", 941 | "939": "绿皮密生西葫芦", 942 | "940": "西葫芦", 943 | "941": "小青南瓜", 944 | "942": "南瓜", 945 | "943": "黄瓜", 946 | "944": "朝鲜蓟", 947 | "945": "甜椒", 948 | "946": "刺棘蓟", 949 | "947": "蘑菇", 950 | "948": "绿苹果", 951 | "949": "草莓", 952 | "950": "橘子", 953 | "951": "柠檬", 954 | "952": "无花果", 955 | "953": "菠萝", 956 | "954": "香蕉", 957 | "955": "菠萝蜜", 958 | "956": "蛋奶冻苹果", 959 | "957": "石榴", 960 | "958": "干草", 961 | "959": "烤面条加干酪沙司", 962 | "960": "巧克力酱,巧克力糖浆", 963 | "961": "面团", 964 | "962": "瑞士肉包,肉饼", 965 | "963": "披萨,披萨饼", 966 | "964": "馅饼", 967 | "965": "卷饼", 968 | "966": "红葡萄酒", 969 | "967": "意大利浓咖啡", 970 | "968": "杯子", 971 | "969": "蛋酒", 972 | "970": "高山", 973 | "971": "泡泡", 974 | "972": "悬崖", 975 | "973": "珊瑚礁", 976 | "974": "间歇泉", 977 | "975": "湖边,湖岸", 978 | "976": "海角", 979 | "977": "沙洲,沙坝", 980 | "978": "海滨,海岸", 981 | "979": "峡谷", 982 | "980": "火山", 983 | "981": "棒球,棒球运动员", 984 | "982": "新郎", 985 | "983": "潜水员", 986 | "984": "油菜", 987 | "985": "雏菊", 988 | "986": "杓兰", 989 | "987": "玉米", 990 | "988": "橡子", 991 | "989": "玫瑰果", 992 | "990": "七叶树果实", 993 | "991": "珊瑚菌", 994 | "992": "木耳", 995 | "993": "鹿花菌", 996 | "994": "鬼笔菌", 997 | "995": "地星(菌类)", 998 | "996": "多叶奇果菌", 999 | "997": "牛肝菌", 1000 | "998": "玉米穗", 1001 | "999": "卫生纸" 1002 | } -------------------------------------------------------------------------------- /tester/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-efficientnet-tester", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest --clearCache" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "jest": "^26.6.3", 13 | "shelljs": "^0.8.4" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tester/samples/car.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/tester/samples/car.jpg -------------------------------------------------------------------------------- /tester/samples/fish.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/tester/samples/fish.jpg -------------------------------------------------------------------------------- /tester/samples/gun.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/tester/samples/gun.jpg -------------------------------------------------------------------------------- /tester/samples/panda.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/tester/samples/panda.jpg -------------------------------------------------------------------------------- /tester/tests/language-provider.test.js: -------------------------------------------------------------------------------- 1 | const { 2 | EfficientNetLabelLanguage, 3 | EfficientNetLanguageProvider, 4 | } = require("node-efficientnet"); 5 | 6 | test("EfficientNetLanguageProvider - check english translation file", (done) => { 7 | const englishProvider = new EfficientNetLanguageProvider( 8 | EfficientNetLabelLanguage.ENGLISH 9 | ); 10 | englishProvider 11 | .load() 12 | .then(() => { 13 | const result = englishProvider.get(0); 14 | expect(result).toBeDefined(); 15 | expect(result).toEqual("tench, Tinca tinca"); 16 | done(); 17 | }) 18 | .catch((error) => done(error)); 19 | }); 20 | 21 | test("EfficientNetLanguageProvider - check chinese translation file", (done) => { 22 | const chineseProvider = new EfficientNetLanguageProvider( 23 | EfficientNetLabelLanguage.CHINESE 24 | ); 25 | chineseProvider 26 | .load() 27 | .then(() => { 28 | const result = chineseProvider.get(0); 29 | expect(result).toBeDefined(); 30 | expect(result).toEqual("丁鲷"); 31 | done(); 32 | }) 33 | .catch((error) => done(error)); 34 | }); 35 | 36 | test("EfficientNetLanguageProvider - check russian translation file", (done) => { 37 | const russianLanguageProvider = new EfficientNetLanguageProvider( 38 | EfficientNetLabelLanguage.RUSSIAN 39 | ); 40 | russianLanguageProvider 41 | .load() 42 | .then(() => { 43 | const result = russianLanguageProvider.get(0); 44 | expect(result).toBeDefined(); 45 | expect(result).toEqual("линь, Тинка-тинка"); 46 | done(); 47 | }) 48 | .catch((error) => done(error)); 49 | }); 50 | 51 | test("EfficientNetLanguageProvider - check spanish translation file", (done) => { 52 | const spanishProvider = new EfficientNetLanguageProvider( 53 | EfficientNetLabelLanguage.SPANISH 54 | ); 55 | spanishProvider 56 | .load() 57 | .then(() => { 58 | const result = spanishProvider.get(0); 59 | expect(result).toBeDefined(); 60 | expect(result).toEqual("tenca, Tinca tinca"); 61 | done(); 62 | }) 63 | .catch((error) => done(error)); 64 | }); 65 | -------------------------------------------------------------------------------- /tester/tests/model.test.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const shelljs = require("shelljs"); 3 | 4 | const { 5 | EfficientNetCheckPoint, 6 | EfficientNetCheckPointFactory, 7 | EfficientNetLabelLanguage, 8 | } = require("node-efficientnet"); 9 | 10 | const rootDir = path.join(__dirname, "../../lib/tfjs/web_model"); 11 | 12 | beforeAll((done) => { 13 | shelljs.mkdir("-p", path.join(rootDir, "B0")); 14 | shelljs.cp("-r", path.join(rootDir, "*.json"), path.join(rootDir, "B0")); 15 | shelljs.cp("-r", path.join(rootDir, "*.bin"), path.join(rootDir, "B0")); 16 | done(); 17 | }); 18 | 19 | afterAll((done) => { 20 | shelljs.rm("-rf", path.join(rootDir, "B0")); 21 | done(); 22 | }); 23 | 24 | test("EfficientNetCheckPointFactory - create checkpoint B0 without throwing exception", (done) => { 25 | EfficientNetCheckPointFactory.create(EfficientNetCheckPoint.B0, { 26 | localModelRootDirectory: rootDir, 27 | }) 28 | .then((model) => { 29 | expect(model).toBeDefined(); 30 | expect(model).toHaveProperty("modelPath"); 31 | expect(model).toHaveProperty("imageSize"); 32 | expect(model).toHaveProperty("model"); 33 | done(); 34 | }) 35 | .catch((error) => done(error)); 36 | }); 37 | 38 | test("EfficientNetCheckPointFactory - checkpoint B0 should predict panda at top precision without throwing exception", (done) => { 39 | EfficientNetCheckPointFactory.create(EfficientNetCheckPoint.B0, { 40 | localModelRootDirectory: rootDir, 41 | }) 42 | .then(async (model) => { 43 | expect(model).toBeDefined(); 44 | const image = "samples/panda.jpg"; 45 | model.inference(image).then((predictions) => { 46 | expect(predictions.result[0].label).toEqual( 47 | "giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca" 48 | ); 49 | done(); 50 | }); 51 | }) 52 | .catch((error) => done(error)); 53 | }); 54 | 55 | test("EfficientNetCheckPointFactory - checkpoint B0 should predict car at top precision without throwing exception", (done) => { 56 | EfficientNetCheckPointFactory.create(EfficientNetCheckPoint.B0, { 57 | localModelRootDirectory: rootDir, 58 | }) 59 | .then(async (model) => { 60 | expect(model).toBeDefined(); 61 | const image = "samples/car.jpg"; 62 | model.inference(image).then((predictions) => { 63 | expect(predictions.result[0].label).toEqual("sports car, sport car"); 64 | done(); 65 | }); 66 | }) 67 | .catch((error) => done(error)); 68 | }); 69 | 70 | test("EfficientNetCheckPointFactory - checkpoint B0 should return top 5 answers", (done) => { 71 | EfficientNetCheckPointFactory.create(EfficientNetCheckPoint.B0, { 72 | localModelRootDirectory: rootDir, 73 | }) 74 | .then(async (model) => { 75 | expect(model).toBeDefined(); 76 | const image = "samples/car.jpg"; 77 | model 78 | .inference(image, { 79 | topK: 5, 80 | }) 81 | .then((predictions) => { 82 | expect(predictions.result[0].label).toEqual("sports car, sport car"); 83 | expect(predictions.result.length).toEqual(5); 84 | done(); 85 | }); 86 | }) 87 | .catch((error) => done(error)); 88 | }); 89 | 90 | test("EfficientNetCheckPointFactory - load model from local file", (done) => { 91 | EfficientNetCheckPointFactory.create(EfficientNetCheckPoint.B0, { 92 | localModelRootDirectory: rootDir, 93 | }) 94 | .then(async (model) => { 95 | expect(model).toBeDefined(); 96 | const image = "samples/car.jpg"; 97 | model.inference(image).then((predictions) => { 98 | expect(predictions.result[0].label).toEqual("sports car, sport car"); 99 | done(); 100 | }); 101 | }) 102 | .catch((error) => { 103 | done(error); 104 | }); 105 | }); 106 | 107 | test("EfficientNetCheckPointFactory - load model from remote default", (done) => { 108 | EfficientNetCheckPointFactory.create(EfficientNetCheckPoint.B0) 109 | .then(async (model) => { 110 | expect(model).toBeDefined(); 111 | const image = "samples/car.jpg"; 112 | model.inference(image).then((predictions) => { 113 | expect(predictions.result[0].label).toEqual("sports car, sport car"); 114 | done(); 115 | }); 116 | }) 117 | .catch((error) => { 118 | done(error); 119 | }); 120 | }); 121 | 122 | test("EfficientNetModel - use different locale chinese", (done) => { 123 | EfficientNetCheckPointFactory.create(EfficientNetCheckPoint.B0, { 124 | locale: EfficientNetLabelLanguage.CHINESE, 125 | }) 126 | .then(async (model) => { 127 | expect(model).toBeDefined(); 128 | const image = "samples/car.jpg"; 129 | model.inference(image).then((localeZhResult) => { 130 | expect(localeZhResult.result[0].label).toEqual("跑车"); 131 | done(); 132 | }); 133 | }) 134 | .catch((error) => { 135 | done(error); 136 | }); 137 | }); 138 | 139 | test("EfficientNetModel - use different locale english", (done) => { 140 | EfficientNetCheckPointFactory.create(EfficientNetCheckPoint.B0, { 141 | locale: EfficientNetLabelLanguage.ENGLISH, 142 | }) 143 | .then(async (model) => { 144 | expect(model).toBeDefined(); 145 | const image = "samples/car.jpg"; 146 | model.inference(image).then((localeEnResult) => { 147 | expect(localeEnResult.result[0].label).toEqual("sports car, sport car"); 148 | done(); 149 | }); 150 | }) 151 | .catch((error) => { 152 | done(error); 153 | }); 154 | }); 155 | -------------------------------------------------------------------------------- /tests/examples/fish.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/tests/examples/fish.gif -------------------------------------------------------------------------------- /tests/examples/fish.heic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/tests/examples/fish.heic -------------------------------------------------------------------------------- /tests/examples/fish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/tests/examples/fish.png -------------------------------------------------------------------------------- /tests/examples/fish.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntedgi/node-efficientnet/d30b27fb650ff752877f0bc64fc60699a335ae07/tests/examples/fish.webp -------------------------------------------------------------------------------- /tests/language-provider.test.js: -------------------------------------------------------------------------------- 1 | const { 2 | EfficientNetLabelLanguage, 3 | EfficientNetLanguageProvider, 4 | } = require("../index"); 5 | 6 | test("EfficientNetLanguageProvider - check english translation file", (done) => { 7 | const englishProvider = new EfficientNetLanguageProvider( 8 | EfficientNetLabelLanguage.ENGLISH 9 | ); 10 | englishProvider 11 | .load() 12 | .then(() => { 13 | const result = englishProvider.get(0); 14 | expect(result).toBeDefined(); 15 | expect(result).toEqual("tench, Tinca tinca"); 16 | done(); 17 | }) 18 | .catch((error) => done(error)); 19 | }); 20 | 21 | test("EfficientNetLanguageProvider - check chinese translation file", (done) => { 22 | const chineseProvider = new EfficientNetLanguageProvider( 23 | EfficientNetLabelLanguage.CHINESE 24 | ); 25 | chineseProvider 26 | .load() 27 | .then(() => { 28 | const result = chineseProvider.get(0); 29 | expect(result).toBeDefined(); 30 | expect(result).toEqual("丁鲷"); 31 | done(); 32 | }) 33 | .catch((error) => done(error)); 34 | }); 35 | 36 | test("EfficientNetLanguageProvider - check spanish translation file", (done) => { 37 | const spanishProvider = new EfficientNetLanguageProvider( 38 | EfficientNetLabelLanguage.SPANISH 39 | ); 40 | spanishProvider 41 | .load() 42 | .then(() => { 43 | const result = spanishProvider.get(0); 44 | expect(result).toBeDefined(); 45 | expect(result).toEqual("tenca, Tinca tinca"); 46 | done(); 47 | }) 48 | .catch((error) => done(error)); 49 | }); 50 | 51 | test("EfficientNetLanguageProvider - check russian translation file", (done) => { 52 | const russianLanguageProvider = new EfficientNetLanguageProvider( 53 | EfficientNetLabelLanguage.RUSSIAN 54 | ); 55 | 56 | russianLanguageProvider 57 | .load() 58 | .then(() => { 59 | const result = russianLanguageProvider.get(0); 60 | expect(result).toBeDefined(); 61 | expect(result).toEqual("линь, Тинка-тинка"); 62 | done(); 63 | }) 64 | .catch((error) => done(error)); 65 | }); 66 | 67 | test("EfficientNetLanguageProvider - check arabic translation file", (done) => { 68 | const arabicProvider = new EfficientNetLanguageProvider( 69 | EfficientNetLabelLanguage.ARABIC 70 | ); 71 | 72 | arabicProvider 73 | .load() 74 | .then(() => { 75 | const result = arabicProvider.get(0); 76 | expect(result).toBeDefined(); 77 | expect(result).toEqual("تنش ، تينكا تينكا"); 78 | done(); 79 | }) 80 | .catch((error) => done(error)); 81 | }); 82 | 83 | test("EfficientNetLanguageProvider - check hebrew translation file", (done) => { 84 | const hebrewProvider = new EfficientNetLanguageProvider( 85 | EfficientNetLabelLanguage.HEBREW 86 | ); 87 | 88 | hebrewProvider 89 | .load() 90 | .then(() => { 91 | const result = hebrewProvider.get(0); 92 | expect(result).toBeDefined(); 93 | expect(result).toEqual("טנץ', דג טנקה"); 94 | done(); 95 | }) 96 | .catch((error) => done(error)); 97 | }); 98 | 99 | test("EfficientNetLanguageProvider - check hebrew translation file with special punctuation", (done) => { 100 | 101 | const hebrewProvider = new EfficientNetLanguageProvider( 102 | EfficientNetLabelLanguage.HEBREW 103 | ); 104 | 105 | hebrewProvider 106 | .load() 107 | .then(() => { 108 | const result = hebrewProvider.get(38); 109 | expect(result).toBeDefined(); 110 | expect(result).toEqual("עַפְעַפִּית אֲמֶרִיקָה"); 111 | done() 112 | }); 113 | }); 114 | 115 | test("EfficientNetLanguageProvider - check french translation file", (done) => { 116 | const frenchProvider = new EfficientNetLanguageProvider( 117 | EfficientNetLabelLanguage.FRENCH 118 | ); 119 | 120 | frenchProvider 121 | .load() 122 | .then(() => { 123 | const result = frenchProvider.get(0); 124 | expect(result).toBeDefined(); 125 | expect(result).toEqual("tanche, Tinca tinca"); 126 | done(); 127 | }) 128 | .catch((error) => done(error)); 129 | }); 130 | -------------------------------------------------------------------------------- /tests/model.test.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const shelljs = require("shelljs"); 3 | 4 | const { 5 | EfficientNetCheckPoint, 6 | EfficientNetCheckPointFactory, 7 | EfficientNetLabelLanguage, 8 | } = require("../index"); 9 | 10 | const rootDir = path.join(__dirname, "../lib/tfjs/web_model"); 11 | 12 | beforeAll((done) => { 13 | shelljs.mkdir("-p", path.join(rootDir, "B0")); 14 | shelljs.cp("-r", path.join(rootDir, "*.json"), path.join(rootDir, "B0")); 15 | shelljs.cp("-r", path.join(rootDir, "*.bin"), path.join(rootDir, "B0")); 16 | done(); 17 | }); 18 | 19 | afterAll((done) => { 20 | shelljs.rm("-rf", path.join(rootDir, "B0")); 21 | done(); 22 | }); 23 | 24 | test("EfficientNetCheckPointFactory - create checkpoint B0 without throwing exception", (done) => { 25 | EfficientNetCheckPointFactory.create(EfficientNetCheckPoint.B0, { 26 | localModelRootDirectory: rootDir, 27 | }) 28 | .then((model) => { 29 | expect(model).toBeDefined(); 30 | expect(model).toHaveProperty("modelPath"); 31 | expect(model).toHaveProperty("imageSize"); 32 | expect(model).toHaveProperty("model"); 33 | done(); 34 | }) 35 | .catch((error) => done(error)); 36 | }); 37 | 38 | test("EfficientNetCheckPointFactory - checkpoint B0 should predict panda at top precision without throwing exception", (done) => { 39 | EfficientNetCheckPointFactory.create(EfficientNetCheckPoint.B0, { 40 | localModelRootDirectory: rootDir, 41 | }) 42 | .then(async (model) => { 43 | expect(model).toBeDefined(); 44 | const image = "samples/panda.jpg"; 45 | model.inference(image).then((predictions) => { 46 | expect(predictions.result[0].label).toEqual( 47 | "giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca" 48 | ); 49 | done(); 50 | }); 51 | }) 52 | .catch((error) => done(error)); 53 | }); 54 | 55 | test("EfficientNetCheckPointFactory - checkpoint B0 should predict car at top precision without throwing exception", (done) => { 56 | EfficientNetCheckPointFactory.create(EfficientNetCheckPoint.B0, { 57 | localModelRootDirectory: rootDir, 58 | }) 59 | .then(async (model) => { 60 | expect(model).toBeDefined(); 61 | const image = "samples/car.jpg"; 62 | model.inference(image).then((predictions) => { 63 | expect(predictions.result[0].label).toEqual("sports car, sport car"); 64 | done(); 65 | }); 66 | }) 67 | .catch((error) => done(error)); 68 | }); 69 | 70 | test("EfficientNetCheckPointFactory - checkpoint B0 should return top 5 answers", (done) => { 71 | EfficientNetCheckPointFactory.create(EfficientNetCheckPoint.B0, { 72 | localModelRootDirectory: rootDir, 73 | }) 74 | .then(async (model) => { 75 | expect(model).toBeDefined(); 76 | const image = "samples/car.jpg"; 77 | model 78 | .inference(image, { 79 | topK: 5, 80 | }) 81 | .then((predictions) => { 82 | expect(predictions.result[0].label).toEqual("sports car, sport car"); 83 | expect(predictions.result.length).toEqual(5); 84 | done(); 85 | }); 86 | }) 87 | .catch((error) => done(error)); 88 | }); 89 | 90 | test("EfficientNetCheckPointFactory - load model from local file", (done) => { 91 | EfficientNetCheckPointFactory.create(EfficientNetCheckPoint.B0, { 92 | localModelRootDirectory: rootDir, 93 | }) 94 | .then(async (model) => { 95 | expect(model).toBeDefined(); 96 | const image = "samples/car.jpg"; 97 | model.inference(image).then((predictions) => { 98 | expect(predictions.result[0].label).toEqual("sports car, sport car"); 99 | done(); 100 | }); 101 | }) 102 | .catch((error) => { 103 | done(error); 104 | }); 105 | }); 106 | 107 | test("EfficientNetCheckPointFactory - load model from remote default", (done) => { 108 | EfficientNetCheckPointFactory.create(EfficientNetCheckPoint.B0) 109 | .then(async (model) => { 110 | expect(model).toBeDefined(); 111 | const image = "samples/car.jpg"; 112 | model.inference(image).then((predictions) => { 113 | expect(predictions.result[0].label).toEqual("sports car, sport car"); 114 | done(); 115 | }); 116 | }) 117 | .catch((error) => { 118 | done(error); 119 | }); 120 | }); 121 | 122 | test("EfficientNetModel - use different locale chinese", (done) => { 123 | EfficientNetCheckPointFactory.create(EfficientNetCheckPoint.B0, { 124 | locale: EfficientNetLabelLanguage.CHINESE, 125 | }) 126 | .then(async (model) => { 127 | expect(model).toBeDefined(); 128 | const image = "samples/car.jpg"; 129 | model.inference(image).then((localeZhResult) => { 130 | expect(localeZhResult.result[0].label).toEqual("跑车"); 131 | done(); 132 | }); 133 | }) 134 | .catch((error) => { 135 | done(error); 136 | }); 137 | }); 138 | 139 | test("EfficientNetModel - use different locale english", (done) => { 140 | EfficientNetCheckPointFactory.create(EfficientNetCheckPoint.B0, { 141 | locale: EfficientNetLabelLanguage.ENGLISH, 142 | }) 143 | .then(async (model) => { 144 | expect(model).toBeDefined(); 145 | const image = "samples/car.jpg"; 146 | model.inference(image).then((localeEnResult) => { 147 | expect(localeEnResult.result[0].label).toEqual("sports car, sport car"); 148 | done(); 149 | }); 150 | }) 151 | .catch((error) => { 152 | done(error); 153 | }); 154 | }); 155 | 156 | test("EfficientNetModel - use different locale hebrew", async () => { 157 | const model = await EfficientNetCheckPointFactory.create( 158 | EfficientNetCheckPoint.B0, 159 | { 160 | locale: EfficientNetLabelLanguage.HEBREW, 161 | } 162 | ); 163 | try { 164 | expect(model).toBeDefined(); 165 | const image = "samples/car.jpg"; 166 | model.inference(image).then((localeEnResult) => { 167 | expect(localeEnResult.result[0].label).toEqual( 168 | "מכונית ספורט, מכונית ספורט" 169 | ); 170 | }); 171 | } catch (error) { 172 | throw error; 173 | } 174 | }); 175 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "outDir": "./dist", 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "types": [], 10 | "typeRoots": [ 11 | "./node_modules", 12 | "./node_modules/@types" 13 | ], 14 | "resolveJsonModule": true, 15 | "moduleResolution": "node" 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | "example.ts", 20 | "playground" 21 | ] 22 | } 23 | --------------------------------------------------------------------------------