Skip to content

Commit 08c57d7

Browse files
Sasha Illarionovdsmilkov
authored andcommitted
Add DeepLab (#229)
This PR adds the DeepLab model together with a demo. ~~There are several issues with the implementation at the moment.~~ Here is the todo list: - [x] **Fix the grayscale-only segmentation maps** ~~The culprit might be [here](https://github.com/tensorflow/tfjs-models/blob/5fa0787a968799f0a614c663f83afb09b7e8f2cb/deeplab/src/utils.ts#L86).~~ **UPDATE 05/06**: The problem was resolved by constructing a single buffer for the translated segmentation map, instead of building the tensor by stacking three separate channel buffers (see [here](218383a)). I do not quite understand why this solves the problem. - [x] **Show performance stats in the demo** - [x] **Add loader for improved UX** - [x] **~~Fix~~ ~~Disable~~ Reserve `tf` to make mangling work** The code uses ES6 features, so I have picked [`rollup-plugin-terser`](https://github.com/TrySound/rollup-plugin-terser) instead of uglify, which breaks on the compilation of the esm module. ~~Terser, however, is pestered with bugs when mangling is enabled: the demo fails to recognise the minified `tf.tidy` function (the error is, say, `t.tidy is not a function`). I might as well revert to uglify after refactoring the code.~~ **UPDATE 06/06**: ~~The problem persists even after playing with the mangling options, with the model missing `predict` method in the debugging mode. I have disabled mangling altogether and reported the issue (terser/terser#364 **UPDATE 29/06**: Mangling works well when `tf` is reserved. See the awesome [explanation](terser/terser#364 (comment)) by @fabiosantoscode why this might be the case. - [x] **Improve docs** - [x] **Add tests** - [x] **Explore quantisation** **UPDATE 07/06**: The models are quantized by default. - [x] Add Cityscapes and ADE20K Labelling Schemes - [x] Change the gif demo to show all models with updated color mappings Please let me know if I am missing anything. cc: @manrajgrover
1 parent fd09e50 commit 08c57d7

29 files changed

+9667
-1
lines changed

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ and can be used as building blocks in other apps.
2727
<!-- Images -->
2828
<!-- ** MobileNet -->
2929
<tr>
30-
<td rowspan="8"><b>Images</b></td>
30+
<td rowspan="10"><b>Images</b></td>
3131
<td rowspan="2"><b><a style="white-space:nowrap; display:inline-block;" href="./mobilenet"><div style='vertical-align:middle; display:inline;'>MobileNet</div></a></b></td>
3232
<td><a href=""></a></td>
3333
<td rowspan="2">Classify images with labels from the <a href="http://www.image-net.org/">ImageNet database</a>.</td>
@@ -65,6 +65,16 @@ and can be used as building blocks in other apps.
6565
</tr>
6666
<tr>
6767
<td><a href="./body-pix/demos/index.html">source</a></td>
68+
</tr>
69+
<!-- ** DeepLab -->
70+
<tr>
71+
<td rowspan="2"><b><a style="white-space:nowrap; display:inline-block;" href="./deeplab"><div style='vertical-align:middle; display:inline;'>DeepLab v3</div></a></b></td>
72+
<td><a href=""></a></td>
73+
<td rowspan="2">Semantic segmentation</td>
74+
<td rowspan="2"><code>npm i @tensorflow-models/deeplab</code></td>
75+
</tr>
76+
<tr>
77+
<td><a href="./deeplab/demo/index.html">source</a></td>
6878
</tr>
6979
<!-- * Audio -->
7080
<!-- ** Speech Commands -->

deeplab/.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.6.8

deeplab/README.md

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
# Semantic Segmentation in the Browser: DeepLab v3 Model
2+
3+
## This model is a work-in-progress and has not been released yet. We will update this README when the model is released and usable
4+
5+
This package contains a standalone implementation of the DeepLab inference pipeline, as well as a [demo](./demo), for running semantic segmentation using TensorFlow.js.
6+
7+
![DeepLab Demo](./docs/deeplab-demo.gif)
8+
9+
## Usage
10+
11+
In the first step of semantic segmentation, an image is fed through a pre-trained model [based](https://github.com/tensorflow/models/blob/master/research/deeplab/g3doc/model_zoo.md) on MobileNet-v2. Three types of pre-trained weights are available, trained on [Pascal](http://host.robots.ox.ac.uk/pascal/VOC/voc2012/index.html), [Cityscapes](https://www.cityscapes-dataset.com) and [ADE20K](https://groups.csail.mit.edu/vision/datasets/ADE20K/) datasets.
12+
13+
To get started, pick the model name from `pascal`, `cityscapes` and `ade20k`, and decide whether you want your model quantized to 1 or 2 bytes (set the `quantizationBytes` option to 4 if you want to disable quantization). Then, initialize the model as follows:
14+
15+
```typescript
16+
import * as tf from '@tensorflow-models/tfjs';
17+
import * as deeplab from '@tensorflow-models/deeplab';
18+
const loadModel = async () => {
19+
const modelName = 'pascal'; // set to your preferred model, out of `pascal`,
20+
// `cityscapes` and `ade20k`
21+
const quantizationBytes = 2; // either 1, 2 or 4
22+
return await deeplab.load({base: modelName, quantizationBytes});
23+
};
24+
25+
const input = tf.zeros([227, 500, 3]);
26+
// ...
27+
28+
loadModel()
29+
.then((model) => model.segment(input))
30+
.then(
31+
({legend}) =>
32+
console.log(`The predicted classes are ${JSON.stringify(legend)}`));
33+
```
34+
35+
By default, calling `load` initalizes the PASCAL variant of the model quantized to 2 bytes.
36+
37+
If you would rather load custom weights, you can pass the URL in the config instead:
38+
39+
```typescript
40+
import * as deeplab from '@tensorflow-models/deeplab';
41+
const loadModel = async () => {
42+
// #TODO(tfjs): Replace this URL after you host the model
43+
const url = 'https://storage.googleapis.com/gsoc-tfjs/models/deeplab/quantized/1/pascal/model.json';
44+
return await deeplab.load({modelUrl: url});
45+
};
46+
loadModel().then(() => console.log(`Loaded the model successfully!`));
47+
```
48+
49+
This will initialize and return the `SemanticSegmentation` model.
50+
51+
You can set the `base` attribute in the argument to `pascal`, `cityscapes` or `ade20k` to use the corresponding colormap and labelling scheme. Otherwise, you would have to provide those yourself during segmentation.
52+
53+
If you require more careful control over the initialization and behavior of the model (e.g. you want to use your own labelling scheme and colormap), use the `SemanticSegmentation` class, passing a pre-loaded `GraphModel` in the constructor:
54+
55+
```typescript
56+
import * as tfconv from '@tensorflow/tfjs-converter';
57+
import * as deeplab from '@tensorflow-models/deeplab';
58+
const loadModel = async () => {
59+
const base = 'pascal'; // set to your preferred model, out of `pascal`,
60+
// `cityscapes` and `ade20k`
61+
const quantizationBytes = 2; // either 1, 2 or 4
62+
// use the getURL utility function to get the URL to the pre-trained weights
63+
const modelUrl = deeplab.getURL(base, quantizationBytes);
64+
const rawModel = await tfconv.loadGraphModel(modelUrl);
65+
const modelName = 'pascal'; // set to your preferred model, out of `pascal`,
66+
// `cityscapes` and `ade20k`
67+
return new deeplab.SemanticSegmentation(rawModel);
68+
};
69+
loadModel().then(() => console.log(`Loaded the model successfully!`));
70+
```
71+
72+
Use `getColormap(base)` and `getLabels(base)` utility function to fetch the default colormap and labelling scheme.
73+
74+
```typescript
75+
import {getLabels, getColormap} from '@tensorflow-models/deeplab';
76+
const model = 'ade20k';
77+
const colormap = getColormap(model);
78+
const labels = getLabels(model);
79+
```
80+
81+
### Segmenting an Image
82+
83+
The `segment` method of the `SemanticSegmentation` object covers most use cases.
84+
85+
Each model recognises a different set of object classes in an image:
86+
87+
- [PASCAL](./deeplab/src/config.ts#L60)
88+
- [CityScapes](./deeplab/src/config.ts#L66)
89+
- [ADE20K](./deeplab/src/config.ts#L72)
90+
91+
#### `model.segment(image, config?)` inputs
92+
93+
- **image** :: `ImageData | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement | tf.Tensor3D`;
94+
95+
The image to segment
96+
97+
- **config.canvas** (optional) :: `HTMLCanvasElement`
98+
99+
Pass an optional canvas element as `canvas` to draw the output
100+
101+
- **config.colormap** (optional) :: `[number, number, number][]`
102+
103+
The array of RGB colors corresponding to labels
104+
105+
- **config.labels** (optional) :: `string[]`
106+
107+
The array of names corresponding to labels
108+
109+
By [default](./src/index.ts#L81), `colormap` and `labels` are set according to the `base` model attribute passed during initialization.
110+
111+
#### `model.segment(image, config?)` outputs
112+
113+
The output is a promise of a `DeepLabOutput` object, with four attributes:
114+
115+
- **legend** :: `{ [name: string]: [number, number, number] }`
116+
117+
The legend is a dictionary of objects recognized in the image and their colors in RGB format.
118+
119+
- **height** :: `number`
120+
121+
The height of the returned segmentation map
122+
123+
- **width** :: `number`
124+
125+
The width of the returned segmentation map
126+
127+
- **segmentationMap** :: `Uint8ClampedArray`
128+
129+
The colored segmentation map as `Uint8ClampedArray` which can be [fed](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas) into `ImageData` and mapped to a canvas.
130+
131+
#### `model.segment(image, config?)` example
132+
133+
```typescript
134+
const classify = async (image) => {
135+
return await model.segment(image);
136+
}
137+
```
138+
139+
**Note**: *For more granular control, consider `predict` and `toSegmentationImage` methods described below.*
140+
141+
### Producing a Semantic Segmentation Map
142+
143+
To segment an arbitrary image and generate a two-dimensional tensor with class labels assigned to each cell of the grid overlayed on the image (with the maximum number of cells on the side fixed to 513), use the `predict` method of the `SemanticSegmentation` object.
144+
145+
#### `model.predict(image)` input
146+
147+
- **image** :: `ImageData | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement | tf.Tensor3D`;
148+
149+
The image to segment
150+
151+
#### `model.predict(image)` output
152+
153+
- **rawSegmentationMap** :: `tf.Tensor2D`
154+
155+
The segmentation map of the image
156+
157+
#### `model.predict(image)` example
158+
159+
```javascript
160+
const getSemanticSegmentationMap = (image) => {
161+
return model.predict(image)
162+
}
163+
```
164+
165+
### Translating a Segmentation Map into the Color-Labelled Image
166+
167+
To transform the segmentation map into a coloured image, use the `toSegmentationImage` method.
168+
169+
#### `toSegmentationImage(colormap, labels, segmentationMap, canvas?)` inputs
170+
171+
- **colormap** :: `[number, number, number][]`
172+
173+
The array of RGB colors corresponding to labels
174+
175+
- **labels** :: `string[]`
176+
177+
The array of names corresponding to labels
178+
179+
- **segmentationMap** :: `tf.Tensor2D`
180+
181+
The segmentation map of the image
182+
183+
- **canvas** (optional) :: `HTMLCanvasElement`
184+
185+
Pass an optional canvas element as `canvas` to draw the output
186+
187+
#### `toSegmentationImage(colormap, labels, segmentationMap, canvas?)` outputs
188+
189+
A promise resolving to the `SegmentationData` object that contains two attributes:
190+
191+
- **legend** :: `{ [name: string]: [number, number, number] }`
192+
193+
The legend is a dictionary of objects recognized in the image and their colors.
194+
195+
- **segmentationMap** :: `Uint8ClampedArray`
196+
197+
The colored segmentation map as `Uint8ClampedArray` which can be [fed](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas) into `ImageData` and mapped to a canvas.
198+
199+
#### `toSegmentationImage(colormap, labels, segmentationMap, canvas?)` example
200+
201+
```javascript
202+
const base = 'pascal';
203+
const translateSegmentationMap = async (segmentationMap) => {
204+
return await toSegmentationImage(
205+
getColormap(base), getLabels(base), segmentationMap)
206+
}
207+
```
208+
209+
## Contributing to the Demo
210+
211+
Please see the demo [documentation](./demo/README.md).
212+
213+
## Technical Details
214+
215+
This model is based on the TensorFlow [implementation](https://github.com/tensorflow/models/tree/master/research/deeplab) of DeepLab v3. You might want to inspect the [conversion script](./scripts/convert_deeplab.sh), or download original pre-trained weights [here](https://github.com/tensorflow/models/blob/master/research/deeplab/g3doc/model_zoo.md). To convert the weights locally, run the script as follows, replacing `dist` with the target directory:
216+
217+
```bash
218+
./scripts/convert_deeplab.sh --target_dir ./scripts/dist
219+
```
220+
221+
Run the usage helper to learn more about the options:
222+
223+
```bash
224+
./scripts/convert_deeplab.sh -h
225+
```

deeplab/demo/.babelrc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"presets": [
3+
[
4+
"@babel/preset-env",
5+
{
6+
"esmodules": false,
7+
"targets": {
8+
"browsers": [
9+
"> 3%"
10+
]
11+
}
12+
}
13+
]
14+
],
15+
"plugins": [
16+
"@babel/plugin-transform-runtime"
17+
]
18+
}

deeplab/demo/README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# DeepLab Demo
2+
3+
This demo allows you to try out semantic segmentation on a couple of preset images using different base models.
4+
5+
## Setup
6+
7+
Change the directory to the `demo` folder:
8+
9+
```sh
10+
cd deeplab/demo
11+
```
12+
13+
Install dependencies:
14+
15+
```sh
16+
yarn
17+
```
18+
19+
Launch the development server watching the files for changes.
20+
21+
```sh
22+
yarn watch
23+
```
24+
25+
**Warning**: *Running the Cityscapes model in the demo is resource-intensive and might crash your browser.*
26+
27+
## Development
28+
29+
If you are developing the model locally and want to test the changes in the demo, proceed as follows:
30+
31+
### Change the directory to the `deeplab` folder
32+
33+
```sh
34+
cd deeplab
35+
```
36+
37+
### Install dependencies
38+
39+
```sh
40+
yarn
41+
```
42+
43+
### Publish a local copy of deeplab
44+
45+
```sh
46+
yarn publish-local
47+
```
48+
49+
### Change into the demo directory (`deeplab/demo`) and install dependencies
50+
51+
```sh
52+
cd demo
53+
yarn
54+
```
55+
56+
### Link the package published from the publish step above
57+
58+
```sh
59+
yarn link-local
60+
```
61+
62+
### Start the dev demo server
63+
64+
```sh
65+
yarn watch
66+
```
67+
68+
**Note**: *To get future updates from the `deeplab` source code, just run `yarn publish-local` in the `deeplab` folder again.*

deeplab/demo/package.json

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"name": "deeplab-demo",
3+
"version": "0.0.1",
4+
"description": "",
5+
"main": "src/index.js",
6+
"license": "Apache-2.0",
7+
"private": true,
8+
"engines": {
9+
"node": ">=8.9.0"
10+
},
11+
"dependencies": {
12+
"@tensorflow-models/deeplab": "link:.yalc/@tensorflow-models/deeplab",
13+
"@tensorflow/tfjs-converter": "1.2.5",
14+
"@tensorflow/tfjs-core": "1.2.5",
15+
"bulma": "^0.7.5"
16+
},
17+
"scripts": {
18+
"watch": "cross-env NODE_ENV=development parcel src/index.html --no-hmr",
19+
"build": "cross-env NODE_ENV=production parcel build src/index.html --no-minify --public-url ./",
20+
"lint": "eslint .",
21+
"link-local": "yalc add --link @tensorflow-models/deeplab"
22+
},
23+
"devDependencies": {
24+
"@babel/core": "^7.0.0-0",
25+
"@babel/plugin-transform-runtime": "^7.5.5",
26+
"@babel/preset-env": "^7.5.5",
27+
"clang-format": "^1.2.4",
28+
"cross-env": "^5.2.0",
29+
"eslint": "^6.1.0",
30+
"eslint-config-google": "^0.13.0",
31+
"parcel-bundler": "~1.12.3",
32+
"ts-node": "^8.3.0",
33+
"yalc": "~1.0.0-pre.32"
34+
},
35+
"eslintConfig": {
36+
"extends": "google",
37+
"rules": {
38+
"require-jsdoc": 0,
39+
"valid-jsdoc": 0
40+
},
41+
"env": {
42+
"es6": true
43+
},
44+
"parserOptions": {
45+
"ecmaVersion": 8,
46+
"sourceType": "module"
47+
}
48+
},
49+
"eslintIgnore": [
50+
"dist/"
51+
]
52+
}

deeplab/demo/src/examples/ade20k.jpg

19.6 KB
Loading
66 KB
Loading

deeplab/demo/src/examples/pascal.jpg

90.6 KB
Loading

0 commit comments

Comments
 (0)