├── .eslintrc ├── .gitignore ├── Dockerfile ├── LICENSE ├── Procfile ├── README.md ├── package-lock.json ├── package.json └── src ├── api ├── facets.js ├── index.js └── object_detectors │ └── ObjectDetectors.js ├── config.json ├── db.js ├── index.js ├── lib └── util.js ├── middleware └── index.js └── models └── facets.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "es6": true 5 | }, 6 | 7 | "parserOptions": { 8 | "ecmaVersion": 8 9 | } 10 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /logs 3 | /npm-debug.log 4 | /node_modules 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.4 2 | 3 | # File Author / Maintainer 4 | LABEL authors="Zouhir Chahoud " 5 | 6 | # Update & install required packages 7 | RUN apk add --update nodejs bash git 8 | 9 | # Install app dependencies 10 | COPY package.json /www/package.json 11 | RUN cd /www; npm install 12 | 13 | # Copy app source 14 | COPY . /www 15 | 16 | # Set work directory to /www 17 | WORKDIR /www 18 | 19 | # set your port 20 | ENV PORT 8080 21 | 22 | # expose the port to outside world 23 | EXPOSE 8080 24 | 25 | # start command as per package.json 26 | CMD ["npm", "start"] 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jason Miller 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: npm start 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Project Details 2 | This is API project on overflowjs.com over object detection in image with tensorflow.js 3 | Got ahead install and run project 4 | Make sure you have latest node version tfjs-node do some issues while installing latest releases 5 | 6 | Frontend part of code is - https://github.com/overflowjs-com/image_object_detction_react_ui 7 | 8 | Do read the blog to know more - https://overflowjs.com/posts/Image-Object-Detection-Using-TensorFlowjs.html 9 | 10 | # Install dependencies 11 | npm install 12 | 13 | # Start development live-reload server 14 | PORT=4000 npm run dev 15 | 16 | # Start production server: 17 | PORT=4000 npm start 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-es6-rest-api", 3 | "version": "0.3.0", 4 | "description": "Starter project for an ES6 RESTful Express API", 5 | "main": "dist", 6 | "scripts": { 7 | "dev": "nodemon -w src --exec \"babel-node src --presets es2015,stage-0\"", 8 | "build": "babel src -s -D -d dist --presets es2015,stage-0", 9 | "start": "node dist", 10 | "prestart": "npm run -s build", 11 | "test": "eslint src" 12 | }, 13 | "eslintConfig": { 14 | "extends": "eslint:recommended", 15 | "parserOptions": { 16 | "ecmaVersion": 7, 17 | "sourceType": "module" 18 | }, 19 | "env": { 20 | "node": true 21 | }, 22 | "rules": { 23 | "no-console": 0, 24 | "no-unused-vars": 1 25 | } 26 | }, 27 | "author": "Rakesh Bhatt ", 28 | "license": "MIT", 29 | "dependencies": { 30 | "@tensorflow-models/coco-ssd": "^2.0.0", 31 | "@tensorflow-models/mobilenet": "^2.0.3", 32 | "@tensorflow/tfjs-node": "^1.2.7", 33 | "base64-to-uint8array": "^1.0.0", 34 | "body-parser": "^1.13.3", 35 | "compression": "^1.5.2", 36 | "cors": "^2.7.1", 37 | "express": "^4.13.3", 38 | "jpeg-js": "^0.3.6", 39 | "morgan": "^1.8.0", 40 | "resource-router-middleware": "^0.6.0" 41 | }, 42 | "devDependencies": { 43 | "babel-cli": "^6.9.0", 44 | "babel-core": "^6.9.0", 45 | "babel-preset-es2015": "^6.9.0", 46 | "babel-preset-stage-0": "^6.5.0", 47 | "eslint": "^3.1.1", 48 | "nodemon": "^1.9.2" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/api/facets.js: -------------------------------------------------------------------------------- 1 | import resource from 'resource-router-middleware'; 2 | import facets from '../models/facets'; 3 | 4 | export default ({ config, db }) => resource({ 5 | 6 | /** Property name to store preloaded entity on `request`. */ 7 | id : 'facet', 8 | 9 | /** For requests with an `id`, you can auto-load the entity. 10 | * Errors terminate the request, success sets `req[id] = data`. 11 | */ 12 | load(req, id, callback) { 13 | let facet = facets.find( facet => facet.id===id ), 14 | err = facet ? null : 'Not found'; 15 | callback(err, facet); 16 | }, 17 | 18 | /** GET / - List all entities */ 19 | index({ params }, res) { 20 | res.json(facets); 21 | }, 22 | 23 | /** POST / - Create a new entity */ 24 | create({ body }, res) { 25 | body.id = facets.length.toString(36); 26 | facets.push(body); 27 | res.json(body); 28 | }, 29 | 30 | /** GET /:id - Return a given entity */ 31 | read({ facet }, res) { 32 | res.json(facet); 33 | }, 34 | 35 | /** PUT /:id - Update a given entity */ 36 | update({ facet, body }, res) { 37 | for (let key in body) { 38 | if (key!=='id') { 39 | facet[key] = body[key]; 40 | } 41 | } 42 | res.sendStatus(204); 43 | }, 44 | 45 | /** DELETE /:id - Delete a given entity */ 46 | delete({ facet }, res) { 47 | facets.splice(facets.indexOf(facet), 1); 48 | res.sendStatus(204); 49 | } 50 | }); 51 | -------------------------------------------------------------------------------- /src/api/index.js: -------------------------------------------------------------------------------- 1 | import { version } from '../../package.json'; 2 | import { Router } from 'express'; 3 | import facets from './facets'; 4 | 5 | import ObjectDetectors from './object_detectors/ObjectDetectors'; 6 | 7 | const tf = require('@tensorflow/tfjs-node'); 8 | 9 | export default ({ config, db }) => { 10 | let api = Router(); 11 | 12 | // mount the facets resource 13 | api.use('/facets', facets({ config, db })); 14 | 15 | // perhaps expose some API metadata at the root 16 | api.get('/', (req, res) => { 17 | res.json({ version }); 18 | }); 19 | 20 | // perhaps expose some API metadata at the root 21 | api.post('/detect_image_objects', async (req, res) => { 22 | 23 | const data = req.body.data; 24 | const type = req.body.type; 25 | 26 | const objectDetect = new ObjectDetectors(data, type); 27 | const results = await objectDetect.process(); 28 | 29 | res.json(results); 30 | }); 31 | 32 | return api; 33 | } 34 | -------------------------------------------------------------------------------- /src/api/object_detectors/ObjectDetectors.js: -------------------------------------------------------------------------------- 1 | const tf = require('@tensorflow/tfjs-node'); 2 | 3 | const cocossd = require('@tensorflow-models/coco-ssd'); 4 | const mobilenet = require('@tensorflow-models/mobilenet'); 5 | 6 | import toUint8Array from 'base64-to-uint8array'; 7 | 8 | 9 | export default class ObjectDetectors { 10 | 11 | constructor(image, type) { 12 | 13 | this.inputImage = image; 14 | this.type = type; 15 | } 16 | 17 | async loadCocoSsdModal() { 18 | const modal = await cocossd.load({ 19 | base: 'mobilenet_v2' 20 | }) 21 | return modal; 22 | } 23 | 24 | async loadMobileNetModal() { 25 | const modal = await mobilenet.load({ 26 | version: 1, 27 | alpha: 0.25 | .50 | .75 | 1.0, 28 | }) 29 | return modal; 30 | } 31 | 32 | getTensor3dObject(numOfChannels) { 33 | 34 | const imageData = this.inputImage.replace('data:image/jpeg;base64','') 35 | .replace('data:image/png;base64',''); 36 | 37 | const imageArray = toUint8Array(imageData); 38 | 39 | const tensor3d = tf.node.decodeJpeg( imageArray, numOfChannels ); 40 | 41 | return tensor3d; 42 | } 43 | 44 | async process() { 45 | 46 | let predictions = null; 47 | const tensor3D = this.getTensor3dObject(3); 48 | 49 | if(this.type === "imagenet") { 50 | 51 | const model = await this.loadMobileNetModal(); 52 | predictions = await model.classify(tensor3D); 53 | 54 | } else { 55 | 56 | const model = await this.loadCocoSsdModal(); 57 | predictions = await model.detect(tensor3D); 58 | } 59 | 60 | tensor3D.dispose(); 61 | 62 | return {data: predictions, type: this.type}; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": 4000, 3 | "bodyLimit": "10000kb", 4 | "corsHeaders": ["Link"] 5 | } 6 | -------------------------------------------------------------------------------- /src/db.js: -------------------------------------------------------------------------------- 1 | export default callback => { 2 | // connect to a database if needed, then pass it to `callback`: 3 | callback(); 4 | } 5 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import http from 'http'; 2 | import express from 'express'; 3 | import cors from 'cors'; 4 | import morgan from 'morgan'; 5 | import bodyParser from 'body-parser'; 6 | import initializeDb from './db'; 7 | import middleware from './middleware'; 8 | import api from './api'; 9 | import config from './config.json'; 10 | 11 | let app = express(); 12 | app.server = http.createServer(app); 13 | 14 | // logger 15 | app.use(morgan('dev')); 16 | 17 | // 3rd party middleware 18 | app.use(cors({ 19 | exposedHeaders: config.corsHeaders 20 | })); 21 | 22 | app.use(bodyParser.json({ 23 | limit : config.bodyLimit 24 | })); 25 | 26 | // connect to db 27 | initializeDb( db => { 28 | 29 | // internal middleware 30 | app.use(middleware({ config, db })); 31 | 32 | // api router 33 | app.use('/api', api({ config, db })); 34 | 35 | app.server.listen(process.env.PORT || config.port, () => { 36 | console.log(`Started on port ${app.server.address().port}`); 37 | }); 38 | }); 39 | 40 | export default app; 41 | -------------------------------------------------------------------------------- /src/lib/util.js: -------------------------------------------------------------------------------- 1 | 2 | /** Creates a callback that proxies node callback style arguments to an Express Response object. 3 | * @param {express.Response} res Express HTTP Response 4 | * @param {number} [status=200] Status code to send on success 5 | * 6 | * @example 7 | * list(req, res) { 8 | * collection.find({}, toRes(res)); 9 | * } 10 | */ 11 | export function toRes(res, status=200) { 12 | return (err, thing) => { 13 | if (err) return res.status(500).send(err); 14 | 15 | if (thing && typeof thing.toObject==='function') { 16 | thing = thing.toObject(); 17 | } 18 | res.status(status).json(thing); 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /src/middleware/index.js: -------------------------------------------------------------------------------- 1 | import { Router } from 'express'; 2 | 3 | export default ({ config, db }) => { 4 | let routes = Router(); 5 | 6 | // add middleware here 7 | 8 | return routes; 9 | } 10 | -------------------------------------------------------------------------------- /src/models/facets.js: -------------------------------------------------------------------------------- 1 | // our example model is just an Array 2 | const facets = []; 3 | export default facets; 4 | --------------------------------------------------------------------------------