├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── Cargo.toml ├── Dockerfile ├── README-ZH.md ├── README.md ├── node ├── cat.png ├── grace_hopper.jpg ├── imagenet_slim_labels.txt ├── mobilenet_v2_1.4_224_frozen.pb ├── package.json ├── public │ ├── grace_hopper.jpg │ └── index.html ├── server.js └── test.js └── src └── lib.rs /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------------------------------------- 2 | # Copyright (c) Microsoft Corporation. All rights reserved. 3 | # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. 4 | #------------------------------------------------------------------------------------------------------------- 5 | 6 | FROM rust:1 7 | 8 | # This Dockerfile adds a non-root user with sudo access. Use the "remoteUser" 9 | # property in devcontainer.json to use it. On Linux, the container user's GID/UIDs 10 | # will be updated to match your local UID/GID (when using the dockerFile property). 11 | # See https://aka.ms/vscode-remote/containers/non-root-user for details. 12 | ARG USERNAME=vscode 13 | ARG USER_UID=1000 14 | ARG USER_GID=$USER_UID 15 | 16 | # Avoid warnings by switching to noninteractive 17 | ENV DEBIAN_FRONTEND=noninteractive 18 | 19 | # Configure apt and install packages 20 | RUN apt-get update \ 21 | && apt-get -y install --no-install-recommends apt-utils dialog 2>&1 \ 22 | # 23 | # Verify git, needed tools installed 24 | && apt-get -y install git openssh-client less iproute2 procps lsb-release \ 25 | # 26 | # Install lldb, vadimcn.vscode-lldb VSCode extension dependencies 27 | && apt-get install -y lldb python3-minimal libpython3.7 \ 28 | # 29 | # Install Rust components 30 | && rustup update 2>&1 \ 31 | && rustup component add rls rust-analysis rust-src rustfmt clippy 2>&1 \ 32 | # 33 | # Create a non-root user to use if preferred - see https://aka.ms/vscode-remote/containers/non-root-user. 34 | && groupadd --gid $USER_GID $USERNAME \ 35 | && useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME \ 36 | # [Optional] Add sudo support for the non-root user 37 | && apt-get install -y sudo \ 38 | && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME\ 39 | && chmod 0440 /etc/sudoers.d/$USERNAME \ 40 | # 41 | # Install node.js and npm 42 | # 43 | && apt-get install -y nodejs npm 2>&1 \ 44 | # 45 | # Install ssvmup 46 | # 47 | && apt-get install -y build-essential curl wget git vim libboost-all-dev \ 48 | && npm install -g ssvmup --unsafe-perm \ 49 | && npm install ssvm \ 50 | # 51 | # Clean up 52 | && apt-get autoremove -y \ 53 | && apt-get clean -y \ 54 | && rm -rf /var/lib/apt/lists/* 55 | 56 | # Switch back to dialog for any ad-hoc use of apt-get 57 | ENV DEBIAN_FRONTEND=dialog 58 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Second State Rust Node.js", 3 | "dockerFile": "Dockerfile", 4 | 5 | // Set *default* container specific settings.json values on container create. 6 | "settings": { 7 | "terminal.integrated.shell.linux": "/bin/bash", 8 | "lldb.executable": "/usr/bin/lldb" 9 | }, 10 | 11 | // Add the IDs of extensions you want installed when the container is created. 12 | "extensions": [ 13 | "rust-lang.rust", 14 | "bungcip.better-toml", 15 | "vadimcn.vscode-lldb", 16 | "dbaeumer.vscode-eslint" 17 | ], 18 | 19 | "forwardPorts": [3000] 20 | 21 | // Use 'postCreateCommand' to run commands after the container is created. 22 | // "postCreateCommand": "yarn install", 23 | 24 | // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root. 25 | // "remoteUser": "node" 26 | } 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | target 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | # nodejs 13 | node_modules 14 | build 15 | npm-debug.log 16 | package-lock.json 17 | 18 | # ssvm 19 | pkg 20 | 21 | # local computer 22 | .env 23 | .DS_Store 24 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "program": "${workspaceFolder}/node/app.js", 12 | "cwd": "${workspaceFolder}/node", 13 | "preLaunchTask": "ssvmup build" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.workingDirectories": [ 3 | { 4 | "directory": "./node", 5 | "changeProcessCWD": true 6 | } 7 | ], 8 | "search.exclude": { 9 | "**/target": true 10 | }, 11 | "lldb.verboseLogging": true 12 | } 13 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [{ 4 | "label": "ssvmup build", 5 | "type": "shell", 6 | "command": "ssvmup build", 7 | "problemMatcher": [] 8 | }] 9 | } 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "csdn-ai-demo" 3 | version = "0.1.0" 4 | authors = ["Michael Yuan ", "Mathieu Poumeyrol "] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "csdn_ai_demo_lib" 9 | path = "src/lib.rs" 10 | crate-type =["cdylib"] 11 | 12 | [dependencies] 13 | wasm-bindgen = "=0.2.61" 14 | serde = { version = "^1.0.59", features = ["derive"] } 15 | serde_json = "^1.0.51" 16 | image = { version = "0.23.0", default-features = false, features = ["jpeg", "png", "gif"] } 17 | tract-tensorflow = "0.6.3" 18 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | ARG DEBIAN_FRONTEND=noninteractive 3 | ENV RUSTUP_HOME=/usr/local/rustup \ 4 | CARGO_HOME=/usr/local/cargo \ 5 | PATH=/usr/local/cargo/bin:$PATH 6 | RUN apt-get update \ 7 | && apt-get install -y tzdata \ 8 | && apt-get -y upgrade && apt-get install -y build-essential curl wget git vim libboost-all-dev 9 | RUN curl -sL https://deb.nodesource.com/setup_14.x | bash \ 10 | && apt-get install -y nodejs \ 11 | && npm install -y -g ssvmup --unsafe-perm \ 12 | && npm install -y ssvm \ 13 | && npm install express express-fileupload 14 | RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y -------------------------------------------------------------------------------- /README-ZH.md: -------------------------------------------------------------------------------- 1 | # Tensorflow 图像识别案例 2 | 3 |

4 | 5 | 6 | 7 |

8 | 9 | 在这个例子中,我们演示了如何在 Node.js 中实现高性能的 AI 推理。 计算密集型的 tensorflow 代码是用 Rust 编写的,并在 WebAssembly 中执行。 使用图像识别的面向用户的应用程序是用 JavaScript 编写的,并运行在 Node.js 中。 10 | 11 | ![AI as a service](https://camo.githubusercontent.com/7ee97417b1f6fd51660ba64ec64a7dc70f51e851/68747470733a2f2f626c6f672e7365636f6e6473746174652e696f2f696d616765732f414961617325323033307365636f6e64732e676966) 12 | 13 | > 你可以在 VSCode 或者 VS Codespaces 中 [fork](https://github.com/second-state/csdn-ai-demo/fork) 并打开这个 git 库。 使用 VS Codespaces,您可以在浏览器中编写代码、编译、运行和调试应用程序,而无需安装任何软件。 参见 https://github.com/second-state/ssvm-nodejs-starter/ 14 | 15 | ## 设置 16 | 17 | ``` 18 | $ sudo apt-get update 19 | $ sudo apt-get -y upgrade 20 | $ sudo apt install build-essential curl wget git vim libboost-all-dev 21 | 22 | $ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 23 | $ source $HOME/.cargo/env 24 | 25 | $ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash 26 | $ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" 27 | $ [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" 28 | 29 | $ nvm install v10.19.0 30 | $ nvm use v10.19.0 31 | 32 | $ npm install -g ssvmup # Append --unsafe-perm if permission denied 33 | $ npm install ssvm 34 | ``` 35 | 36 | ## 创建新项目 37 | 38 | ``` 39 | $ cargo new --lib tensorflow 40 | $ cd tensorflow 41 | ``` 42 | 43 | ## 修改cargo config 文件 44 | 45 | The [Cargo.toml](Cargo.toml) 文件显示依赖项. 46 | 47 | * The `wasm-bindgen` crate is required for invoking Rust functions from JavaScript. 48 | * The `serde` and `serde_json` crates allow us to work with JSON strings to represent complex data types. 49 | * The `images` crate only enables features that are compatible with WebAssembly. 50 | 51 | ## 写 Rust 代码 52 | 53 | [src/lib.rs](src/lib.rs) 文件包含 Rust 函数从文件中读取 tensorflow 模型,读取并调整图像大小,然后根据图像运行模型来识别图像主题。 结果作为 JSON 数组返回,其中包含识别对象的 ImageNet 类别 ID 和此预测的置信度。[了解更多](https://github.com/tensorflow/models/tree/master/research/slim/nets/mobilenet) 有关这个案例。 54 | 55 | ## 创建 WASM bytecode 56 | 57 | ``` 58 | $ ssvmup build 59 | ``` 60 | 61 | ## 创建一个node app 62 | 63 | [node/app.js](node/app.js) app 展示了如何从 JavaScript 中调用 Rust 函数。它使用了一个[预先训练的 tensorflow 模型](https://storage.googleapis.com/mobilenet_v2/checkpoints/mobilenet_v2_1.4_224.tgz) 来识别两个图像。 64 | 65 | ## 测试 66 | 67 | ``` 68 | $ node app.js 69 | ``` 70 | 71 | 第一个任务是识别计算机科学家格蕾丝 · 霍珀的图像,识别这个图像需要0.9秒。 72 | 73 | 74 | ``` 75 | Model: "mobilenet_v2_1.4_224_frozen.pb" 76 | Image: "grace_hopper.jpg" 77 | Inference: 131.783ms Model loaded 78 | Inference: 367.625ms Plan loaded 79 | Inference: 391.095ms Image loaded 80 | Inference: 427.137ms Image resized 81 | Inference: 1322.184ms Model applied 82 | Inference: 1322.637ms 83 | Detected object id 654 with probability 0.3256046 84 | ``` 85 | 86 | Category ID `654` 可以在 [imagenet_slim_labels.txt](imagenet_slim_labels.txt) 找到。 行数 `654`. 87 | 88 | ``` 89 | 654 军装 90 | ``` 91 | 92 | 第二个任务是识别一张猫的图像,识别这张图像需要0.8秒。 93 | 94 | ``` 95 | Model: "mobilenet_v2_1.4_224_frozen.pb" 96 | Image: "cat.png" 97 | Inference: 86.587ms Model loaded 98 | Inference: 314.308ms Plan loaded 99 | Inference: 842.836ms Image loaded 100 | Inference: 1166.115ms Image resized 101 | Inference: 2014.337ms Model applied 102 | Inference: 2014.602ms 103 | Detected object id 284 with probability 0.27039126 104 | ``` 105 | 106 | Category ID `284` 可以在这里找到 [imagenet_slim_labels.txt](imagenet_slim_labels.txt). 行数 `284`. 107 | 108 | ``` 109 | 284 虎猫 110 | ``` 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The tensorflow image recognition example 2 | 3 | [中文](README-ZH.md) 4 | 5 |

6 | 7 | 8 | 9 |

10 | 11 | In this example, we demonstrate how to do high performance AI inference in Node.js. The computationally intensive tensorflow code is written in Rust and executed in WebAssembly. The user-facing application that uses image recognition is written in JavaScript and runs in Node.js. 12 | 13 | ![wasm Rust AI](https://blog.secondstate.io/images/AIaas%2030seconds.gif) 14 | 15 | > Check out the [high-res screencast](https://youtu.be/Ce2am-ugQhg). 16 | 17 | ## Set up the build and runtime environment 18 | 19 | [Read more](https://www.secondstate.io/articles/setup-rust-nodejs/) about how to set up the environment. 20 | 21 | ### Docker 22 | 23 | ```bash 24 | # build the docker image 25 | $ docker build -t ssvm-nodejs-ai:v1 . 26 | 27 | # run the docker container in interactive shell 28 | $ docker run -p 8080:8080 --rm -it -v $(pwd):/app ssvm-nodejs-ai:v1 29 | (docker) $ cd /app 30 | ``` 31 | 32 | ### Ubuntu 20.04 TLS 33 | 34 | ``` 35 | $ sudo apt-get update 36 | $ sudo apt-get -y upgrade 37 | $ sudo apt install build-essential curl wget git vim libboost-all-dev 38 | 39 | $ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 40 | $ source $HOME/.cargo/env 41 | 42 | $ curl -sL https://deb.nodesource.com/setup_14.x | bash 43 | $ sudo apt-get install -y nodejs 44 | 45 | $ npm install -g ssvmup # Append --unsafe-perm if permission denied 46 | $ npm install ssvm 47 | 48 | $ npm install express express-fileupload 49 | ``` 50 | 51 | ## The cargo config file 52 | 53 | The [Cargo.toml](Cargo.toml) file shows the dependencies. 54 | 55 | * The `wasm-bindgen` crate is required for invoking Rust functions from JavaScript. 56 | * The `serde` and `serde_json` crates allow us to work with JSON strings to represent complex data types. 57 | * The `images` crate only enables features that are compatible with WebAssembly. 58 | 59 | ## Rust function 60 | 61 | The [src/lib.rs](src/lib.rs) file contains Rust functions to read the tensorflow model from a file, read and resize an image, and then run the model against the image to recognize the image subject. The result is returned as a JSON array containing the the ImageNet category ID for the recognized object, and the confidence level for this prediction. [Learn more](https://github.com/tensorflow/models/tree/master/research/slim/nets/mobilenet) about this example. 62 | 63 | ## Build the WASM bytecode 64 | 65 | ``` 66 | $ ssvmup build 67 | ``` 68 | 69 | ## Node.js app 70 | 71 | The [node/test.js](node/test.js) app shows how to call the Rust functions from JavaScript. It uses a [pre-trained tensorflow model](https://storage.googleapis.com/mobilenet_v2/checkpoints/mobilenet_v2_1.4_224.tgz) to recognize two images. 72 | 73 | ## Test 74 | 75 | ``` 76 | $ cd node 77 | $ node test.js 78 | ``` 79 | 80 | The first task is to recognize an image of computer scientist Grace Hopper. It takes 0.9s to recognize this image. 81 | 82 | ``` 83 | Model: "mobilenet_v2_1.4_224_frozen.pb" 84 | Image: "grace_hopper.jpg" 85 | Inference: 131.783ms Model loaded 86 | Inference: 367.625ms Plan loaded 87 | Inference: 391.095ms Image loaded 88 | Inference: 427.137ms Image resized 89 | Inference: 1322.184ms Model applied 90 | Inference: 1322.637ms 91 | Detected object id 654 with probability 0.3256046 92 | ``` 93 | 94 | Category ID `654` can be found in the [imagenet_slim_labels.txt](imagenet_slim_labels.txt). Line `654`. 95 | 96 | ``` 97 | 654 military uniform 98 | ``` 99 | 100 | The second task is to recognize an image of a cat. It takes 0.8s to recognize this image. 101 | 102 | ``` 103 | Model: "mobilenet_v2_1.4_224_frozen.pb" 104 | Image: "cat.png" 105 | Inference: 86.587ms Model loaded 106 | Inference: 314.308ms Plan loaded 107 | Inference: 842.836ms Image loaded 108 | Inference: 1166.115ms Image resized 109 | Inference: 2014.337ms Model applied 110 | Inference: 2014.602ms 111 | Detected object id 284 with probability 0.27039126 112 | ``` 113 | 114 | Category ID `284` can be found in the [imagenet_slim_labels.txt](imagenet_slim_labels.txt). Line `284`. 115 | 116 | ``` 117 | 284 tiger cat 118 | ``` 119 | 120 | ## Web service 121 | 122 | Start the Node.js application in a web server. 123 | 124 | ``` 125 | $ cd node 126 | $ node server.js 127 | ``` 128 | 129 | Then, go to http://ip-addr:8080/ and upload an image for recognition! 130 | -------------------------------------------------------------------------------- /node/cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/second-state/rust-wasm-ai-demo/3a1f9b0677d5f12ad77b4090710e4c8a953c16be/node/cat.png -------------------------------------------------------------------------------- /node/grace_hopper.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/second-state/rust-wasm-ai-demo/3a1f9b0677d5f12ad77b4090710e4c8a953c16be/node/grace_hopper.jpg -------------------------------------------------------------------------------- /node/imagenet_slim_labels.txt: -------------------------------------------------------------------------------- 1 | dummy 2 | tench 3 | goldfish 4 | great white shark 5 | tiger shark 6 | hammerhead 7 | electric ray 8 | stingray 9 | cock 10 | hen 11 | ostrich 12 | brambling 13 | goldfinch 14 | house finch 15 | junco 16 | indigo bunting 17 | robin 18 | bulbul 19 | jay 20 | magpie 21 | chickadee 22 | water ouzel 23 | kite 24 | bald eagle 25 | vulture 26 | great grey owl 27 | European fire salamander 28 | common newt 29 | eft 30 | spotted salamander 31 | axolotl 32 | bullfrog 33 | tree frog 34 | tailed frog 35 | loggerhead 36 | leatherback turtle 37 | mud turtle 38 | terrapin 39 | box turtle 40 | banded gecko 41 | common iguana 42 | American chameleon 43 | whiptail 44 | agama 45 | frilled lizard 46 | alligator lizard 47 | Gila monster 48 | green lizard 49 | African chameleon 50 | Komodo dragon 51 | African crocodile 52 | American alligator 53 | triceratops 54 | thunder snake 55 | ringneck snake 56 | hognose snake 57 | green snake 58 | king snake 59 | garter snake 60 | water snake 61 | vine snake 62 | night snake 63 | boa constrictor 64 | rock python 65 | Indian cobra 66 | green mamba 67 | sea snake 68 | horned viper 69 | diamondback 70 | sidewinder 71 | trilobite 72 | harvestman 73 | scorpion 74 | black and gold garden spider 75 | barn spider 76 | garden spider 77 | black widow 78 | tarantula 79 | wolf spider 80 | tick 81 | centipede 82 | black grouse 83 | ptarmigan 84 | ruffed grouse 85 | prairie chicken 86 | peacock 87 | quail 88 | partridge 89 | African grey 90 | macaw 91 | sulphur-crested cockatoo 92 | lorikeet 93 | coucal 94 | bee eater 95 | hornbill 96 | hummingbird 97 | jacamar 98 | toucan 99 | drake 100 | red-breasted merganser 101 | goose 102 | black swan 103 | tusker 104 | echidna 105 | platypus 106 | wallaby 107 | koala 108 | wombat 109 | jellyfish 110 | sea anemone 111 | brain coral 112 | flatworm 113 | nematode 114 | conch 115 | snail 116 | slug 117 | sea slug 118 | chiton 119 | chambered nautilus 120 | Dungeness crab 121 | rock crab 122 | fiddler crab 123 | king crab 124 | American lobster 125 | spiny lobster 126 | crayfish 127 | hermit crab 128 | isopod 129 | white stork 130 | black stork 131 | spoonbill 132 | flamingo 133 | little blue heron 134 | American egret 135 | bittern 136 | crane 137 | limpkin 138 | European gallinule 139 | American coot 140 | bustard 141 | ruddy turnstone 142 | red-backed sandpiper 143 | redshank 144 | dowitcher 145 | oystercatcher 146 | pelican 147 | king penguin 148 | albatross 149 | grey whale 150 | killer whale 151 | dugong 152 | sea lion 153 | Chihuahua 154 | Japanese spaniel 155 | Maltese dog 156 | Pekinese 157 | Shih-Tzu 158 | Blenheim spaniel 159 | papillon 160 | toy terrier 161 | Rhodesian ridgeback 162 | Afghan hound 163 | basset 164 | beagle 165 | bloodhound 166 | bluetick 167 | black-and-tan coonhound 168 | Walker hound 169 | English foxhound 170 | redbone 171 | borzoi 172 | Irish wolfhound 173 | Italian greyhound 174 | whippet 175 | Ibizan hound 176 | Norwegian elkhound 177 | otterhound 178 | Saluki 179 | Scottish deerhound 180 | Weimaraner 181 | Staffordshire bullterrier 182 | American Staffordshire terrier 183 | Bedlington terrier 184 | Border terrier 185 | Kerry blue terrier 186 | Irish terrier 187 | Norfolk terrier 188 | Norwich terrier 189 | Yorkshire terrier 190 | wire-haired fox terrier 191 | Lakeland terrier 192 | Sealyham terrier 193 | Airedale 194 | cairn 195 | Australian terrier 196 | Dandie Dinmont 197 | Boston bull 198 | miniature schnauzer 199 | giant schnauzer 200 | standard schnauzer 201 | Scotch terrier 202 | Tibetan terrier 203 | silky terrier 204 | soft-coated wheaten terrier 205 | West Highland white terrier 206 | Lhasa 207 | flat-coated retriever 208 | curly-coated retriever 209 | golden retriever 210 | Labrador retriever 211 | Chesapeake Bay retriever 212 | German short-haired pointer 213 | vizsla 214 | English setter 215 | Irish setter 216 | Gordon setter 217 | Brittany spaniel 218 | clumber 219 | English springer 220 | Welsh springer spaniel 221 | cocker spaniel 222 | Sussex spaniel 223 | Irish water spaniel 224 | kuvasz 225 | schipperke 226 | groenendael 227 | malinois 228 | briard 229 | kelpie 230 | komondor 231 | Old English sheepdog 232 | Shetland sheepdog 233 | collie 234 | Border collie 235 | Bouvier des Flandres 236 | Rottweiler 237 | German shepherd 238 | Doberman 239 | miniature pinscher 240 | Greater Swiss Mountain dog 241 | Bernese mountain dog 242 | Appenzeller 243 | EntleBucher 244 | boxer 245 | bull mastiff 246 | Tibetan mastiff 247 | French bulldog 248 | Great Dane 249 | Saint Bernard 250 | Eskimo dog 251 | malamute 252 | Siberian husky 253 | dalmatian 254 | affenpinscher 255 | basenji 256 | pug 257 | Leonberg 258 | Newfoundland 259 | Great Pyrenees 260 | Samoyed 261 | Pomeranian 262 | chow 263 | keeshond 264 | Brabancon griffon 265 | Pembroke 266 | Cardigan 267 | toy poodle 268 | miniature poodle 269 | standard poodle 270 | Mexican hairless 271 | timber wolf 272 | white wolf 273 | red wolf 274 | coyote 275 | dingo 276 | dhole 277 | African hunting dog 278 | hyena 279 | red fox 280 | kit fox 281 | Arctic fox 282 | grey fox 283 | tabby 284 | tiger cat 285 | Persian cat 286 | Siamese cat 287 | Egyptian cat 288 | cougar 289 | lynx 290 | leopard 291 | snow leopard 292 | jaguar 293 | lion 294 | tiger 295 | cheetah 296 | brown bear 297 | American black bear 298 | ice bear 299 | sloth bear 300 | mongoose 301 | meerkat 302 | tiger beetle 303 | ladybug 304 | ground beetle 305 | long-horned beetle 306 | leaf beetle 307 | dung beetle 308 | rhinoceros beetle 309 | weevil 310 | fly 311 | bee 312 | ant 313 | grasshopper 314 | cricket 315 | walking stick 316 | cockroach 317 | mantis 318 | cicada 319 | leafhopper 320 | lacewing 321 | dragonfly 322 | damselfly 323 | admiral 324 | ringlet 325 | monarch 326 | cabbage butterfly 327 | sulphur butterfly 328 | lycaenid 329 | starfish 330 | sea urchin 331 | sea cucumber 332 | wood rabbit 333 | hare 334 | Angora 335 | hamster 336 | porcupine 337 | fox squirrel 338 | marmot 339 | beaver 340 | guinea pig 341 | sorrel 342 | zebra 343 | hog 344 | wild boar 345 | warthog 346 | hippopotamus 347 | ox 348 | water buffalo 349 | bison 350 | ram 351 | bighorn 352 | ibex 353 | hartebeest 354 | impala 355 | gazelle 356 | Arabian camel 357 | llama 358 | weasel 359 | mink 360 | polecat 361 | black-footed ferret 362 | otter 363 | skunk 364 | badger 365 | armadillo 366 | three-toed sloth 367 | orangutan 368 | gorilla 369 | chimpanzee 370 | gibbon 371 | siamang 372 | guenon 373 | patas 374 | baboon 375 | macaque 376 | langur 377 | colobus 378 | proboscis monkey 379 | marmoset 380 | capuchin 381 | howler monkey 382 | titi 383 | spider monkey 384 | squirrel monkey 385 | Madagascar cat 386 | indri 387 | Indian elephant 388 | African elephant 389 | lesser panda 390 | giant panda 391 | barracouta 392 | eel 393 | coho 394 | rock beauty 395 | anemone fish 396 | sturgeon 397 | gar 398 | lionfish 399 | puffer 400 | abacus 401 | abaya 402 | academic gown 403 | accordion 404 | acoustic guitar 405 | aircraft carrier 406 | airliner 407 | airship 408 | altar 409 | ambulance 410 | amphibian 411 | analog clock 412 | apiary 413 | apron 414 | ashcan 415 | assault rifle 416 | backpack 417 | bakery 418 | balance beam 419 | balloon 420 | ballpoint 421 | Band Aid 422 | banjo 423 | bannister 424 | barbell 425 | barber chair 426 | barbershop 427 | barn 428 | barometer 429 | barrel 430 | barrow 431 | baseball 432 | basketball 433 | bassinet 434 | bassoon 435 | bathing cap 436 | bath towel 437 | bathtub 438 | beach wagon 439 | beacon 440 | beaker 441 | bearskin 442 | beer bottle 443 | beer glass 444 | bell cote 445 | bib 446 | bicycle-built-for-two 447 | bikini 448 | binder 449 | binoculars 450 | birdhouse 451 | boathouse 452 | bobsled 453 | bolo tie 454 | bonnet 455 | bookcase 456 | bookshop 457 | bottlecap 458 | bow 459 | bow tie 460 | brass 461 | brassiere 462 | breakwater 463 | breastplate 464 | broom 465 | bucket 466 | buckle 467 | bulletproof vest 468 | bullet train 469 | butcher shop 470 | cab 471 | caldron 472 | candle 473 | cannon 474 | canoe 475 | can opener 476 | cardigan 477 | car mirror 478 | carousel 479 | carpenter's kit 480 | carton 481 | car wheel 482 | cash machine 483 | cassette 484 | cassette player 485 | castle 486 | catamaran 487 | CD player 488 | cello 489 | cellular telephone 490 | chain 491 | chainlink fence 492 | chain mail 493 | chain saw 494 | chest 495 | chiffonier 496 | chime 497 | china cabinet 498 | Christmas stocking 499 | church 500 | cinema 501 | cleaver 502 | cliff dwelling 503 | cloak 504 | clog 505 | cocktail shaker 506 | coffee mug 507 | coffeepot 508 | coil 509 | combination lock 510 | computer keyboard 511 | confectionery 512 | container ship 513 | convertible 514 | corkscrew 515 | cornet 516 | cowboy boot 517 | cowboy hat 518 | cradle 519 | crane 520 | crash helmet 521 | crate 522 | crib 523 | Crock Pot 524 | croquet ball 525 | crutch 526 | cuirass 527 | dam 528 | desk 529 | desktop computer 530 | dial telephone 531 | diaper 532 | digital clock 533 | digital watch 534 | dining table 535 | dishrag 536 | dishwasher 537 | disk brake 538 | dock 539 | dogsled 540 | dome 541 | doormat 542 | drilling platform 543 | drum 544 | drumstick 545 | dumbbell 546 | Dutch oven 547 | electric fan 548 | electric guitar 549 | electric locomotive 550 | entertainment center 551 | envelope 552 | espresso maker 553 | face powder 554 | feather boa 555 | file 556 | fireboat 557 | fire engine 558 | fire screen 559 | flagpole 560 | flute 561 | folding chair 562 | football helmet 563 | forklift 564 | fountain 565 | fountain pen 566 | four-poster 567 | freight car 568 | French horn 569 | frying pan 570 | fur coat 571 | garbage truck 572 | gasmask 573 | gas pump 574 | goblet 575 | go-kart 576 | golf ball 577 | golfcart 578 | gondola 579 | gong 580 | gown 581 | grand piano 582 | greenhouse 583 | grille 584 | grocery store 585 | guillotine 586 | hair slide 587 | hair spray 588 | half track 589 | hammer 590 | hamper 591 | hand blower 592 | hand-held computer 593 | handkerchief 594 | hard disc 595 | harmonica 596 | harp 597 | harvester 598 | hatchet 599 | holster 600 | home theater 601 | honeycomb 602 | hook 603 | hoopskirt 604 | horizontal bar 605 | horse cart 606 | hourglass 607 | iPod 608 | iron 609 | jack-o'-lantern 610 | jean 611 | jeep 612 | jersey 613 | jigsaw puzzle 614 | jinrikisha 615 | joystick 616 | kimono 617 | knee pad 618 | knot 619 | lab coat 620 | ladle 621 | lampshade 622 | laptop 623 | lawn mower 624 | lens cap 625 | letter opener 626 | library 627 | lifeboat 628 | lighter 629 | limousine 630 | liner 631 | lipstick 632 | Loafer 633 | lotion 634 | loudspeaker 635 | loupe 636 | lumbermill 637 | magnetic compass 638 | mailbag 639 | mailbox 640 | maillot 641 | maillot 642 | manhole cover 643 | maraca 644 | marimba 645 | mask 646 | matchstick 647 | maypole 648 | maze 649 | measuring cup 650 | medicine chest 651 | megalith 652 | microphone 653 | microwave 654 | military uniform 655 | milk can 656 | minibus 657 | miniskirt 658 | minivan 659 | missile 660 | mitten 661 | mixing bowl 662 | mobile home 663 | Model T 664 | modem 665 | monastery 666 | monitor 667 | moped 668 | mortar 669 | mortarboard 670 | mosque 671 | mosquito net 672 | motor scooter 673 | mountain bike 674 | mountain tent 675 | mouse 676 | mousetrap 677 | moving van 678 | muzzle 679 | nail 680 | neck brace 681 | necklace 682 | nipple 683 | notebook 684 | obelisk 685 | oboe 686 | ocarina 687 | odometer 688 | oil filter 689 | organ 690 | oscilloscope 691 | overskirt 692 | oxcart 693 | oxygen mask 694 | packet 695 | paddle 696 | paddlewheel 697 | padlock 698 | paintbrush 699 | pajama 700 | palace 701 | panpipe 702 | paper towel 703 | parachute 704 | parallel bars 705 | park bench 706 | parking meter 707 | passenger car 708 | patio 709 | pay-phone 710 | pedestal 711 | pencil box 712 | pencil sharpener 713 | perfume 714 | Petri dish 715 | photocopier 716 | pick 717 | pickelhaube 718 | picket fence 719 | pickup 720 | pier 721 | piggy bank 722 | pill bottle 723 | pillow 724 | ping-pong ball 725 | pinwheel 726 | pirate 727 | pitcher 728 | plane 729 | planetarium 730 | plastic bag 731 | plate rack 732 | plow 733 | plunger 734 | Polaroid camera 735 | pole 736 | police van 737 | poncho 738 | pool table 739 | pop bottle 740 | pot 741 | potter's wheel 742 | power drill 743 | prayer rug 744 | printer 745 | prison 746 | projectile 747 | projector 748 | puck 749 | punching bag 750 | purse 751 | quill 752 | quilt 753 | racer 754 | racket 755 | radiator 756 | radio 757 | radio telescope 758 | rain barrel 759 | recreational vehicle 760 | reel 761 | reflex camera 762 | refrigerator 763 | remote control 764 | restaurant 765 | revolver 766 | rifle 767 | rocking chair 768 | rotisserie 769 | rubber eraser 770 | rugby ball 771 | rule 772 | running shoe 773 | safe 774 | safety pin 775 | saltshaker 776 | sandal 777 | sarong 778 | sax 779 | scabbard 780 | scale 781 | school bus 782 | schooner 783 | scoreboard 784 | screen 785 | screw 786 | screwdriver 787 | seat belt 788 | sewing machine 789 | shield 790 | shoe shop 791 | shoji 792 | shopping basket 793 | shopping cart 794 | shovel 795 | shower cap 796 | shower curtain 797 | ski 798 | ski mask 799 | sleeping bag 800 | slide rule 801 | sliding door 802 | slot 803 | snorkel 804 | snowmobile 805 | snowplow 806 | soap dispenser 807 | soccer ball 808 | sock 809 | solar dish 810 | sombrero 811 | soup bowl 812 | space bar 813 | space heater 814 | space shuttle 815 | spatula 816 | speedboat 817 | spider web 818 | spindle 819 | sports car 820 | spotlight 821 | stage 822 | steam locomotive 823 | steel arch bridge 824 | steel drum 825 | stethoscope 826 | stole 827 | stone wall 828 | stopwatch 829 | stove 830 | strainer 831 | streetcar 832 | stretcher 833 | studio couch 834 | stupa 835 | submarine 836 | suit 837 | sundial 838 | sunglass 839 | sunglasses 840 | sunscreen 841 | suspension bridge 842 | swab 843 | sweatshirt 844 | swimming trunks 845 | swing 846 | switch 847 | syringe 848 | table lamp 849 | tank 850 | tape player 851 | teapot 852 | teddy 853 | television 854 | tennis ball 855 | thatch 856 | theater curtain 857 | thimble 858 | thresher 859 | throne 860 | tile roof 861 | toaster 862 | tobacco shop 863 | toilet seat 864 | torch 865 | totem pole 866 | tow truck 867 | toyshop 868 | tractor 869 | trailer truck 870 | tray 871 | trench coat 872 | tricycle 873 | trimaran 874 | tripod 875 | triumphal arch 876 | trolleybus 877 | trombone 878 | tub 879 | turnstile 880 | typewriter keyboard 881 | umbrella 882 | unicycle 883 | upright 884 | vacuum 885 | vase 886 | vault 887 | velvet 888 | vending machine 889 | vestment 890 | viaduct 891 | violin 892 | volleyball 893 | waffle iron 894 | wall clock 895 | wallet 896 | wardrobe 897 | warplane 898 | washbasin 899 | washer 900 | water bottle 901 | water jug 902 | water tower 903 | whiskey jug 904 | whistle 905 | wig 906 | window screen 907 | window shade 908 | Windsor tie 909 | wine bottle 910 | wing 911 | wok 912 | wooden spoon 913 | wool 914 | worm fence 915 | wreck 916 | yawl 917 | yurt 918 | web site 919 | comic book 920 | crossword puzzle 921 | street sign 922 | traffic light 923 | book jacket 924 | menu 925 | plate 926 | guacamole 927 | consomme 928 | hot pot 929 | trifle 930 | ice cream 931 | ice lolly 932 | French loaf 933 | bagel 934 | pretzel 935 | cheeseburger 936 | hotdog 937 | mashed potato 938 | head cabbage 939 | broccoli 940 | cauliflower 941 | zucchini 942 | spaghetti squash 943 | acorn squash 944 | butternut squash 945 | cucumber 946 | artichoke 947 | bell pepper 948 | cardoon 949 | mushroom 950 | Granny Smith 951 | strawberry 952 | orange 953 | lemon 954 | fig 955 | pineapple 956 | banana 957 | jackfruit 958 | custard apple 959 | pomegranate 960 | hay 961 | carbonara 962 | chocolate sauce 963 | dough 964 | meat loaf 965 | pizza 966 | potpie 967 | burrito 968 | red wine 969 | espresso 970 | cup 971 | eggnog 972 | alp 973 | bubble 974 | cliff 975 | coral reef 976 | geyser 977 | lakeside 978 | promontory 979 | sandbar 980 | seashore 981 | valley 982 | volcano 983 | ballplayer 984 | groom 985 | scuba diver 986 | rapeseed 987 | daisy 988 | yellow lady's slipper 989 | corn 990 | acorn 991 | hip 992 | buckeye 993 | coral fungus 994 | agaric 995 | gyromitra 996 | stinkhorn 997 | earthstar 998 | hen-of-the-woods 999 | bolete 1000 | ear 1001 | toilet tissue 1002 | -------------------------------------------------------------------------------- /node/mobilenet_v2_1.4_224_frozen.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/second-state/rust-wasm-ai-demo/3a1f9b0677d5f12ad77b4090710e4c8a953c16be/node/mobilenet_v2_1.4_224_frozen.pb -------------------------------------------------------------------------------- /node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "dependencies": { 7 | "express": "^4.17.1", 8 | "express-fileupload": "^1.1.7-alpha.4" 9 | }, 10 | "devDependencies": {}, 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1", 13 | "start": "node server.js" 14 | }, 15 | "keywords": [], 16 | "author": "", 17 | "license": "ISC" 18 | } 19 | -------------------------------------------------------------------------------- /node/public/grace_hopper.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/second-state/rust-wasm-ai-demo/3a1f9b0677d5f12ad77b4090710e4c8a953c16be/node/public/grace_hopper.jpg -------------------------------------------------------------------------------- /node/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Demo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 29 | 30 | 31 | 32 |
33 | 34 |

What's on the image?

35 |

Image recognition as a service powered by Rust, Tensorflow, and Node.js. How it works

36 | 37 |
38 |
39 | 40 | 41 |
42 | 43 | 44 |
45 | 46 |
47 |

48 |
49 | 50 |
51 | 52 | 53 | -------------------------------------------------------------------------------- /node/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const fileUpload = require('express-fileupload'); 3 | const { infer } = require('../pkg/csdn_ai_demo_lib.js'); 4 | 5 | const fs = require('fs'); 6 | var data_model = fs.readFileSync('mobilenet_v2_1.4_224_frozen.pb'); 7 | 8 | var labels = []; 9 | fs.readFileSync('imagenet_slim_labels.txt', 'utf-8') 10 | .split(/\r?\n/) 11 | .forEach(function (line) { 12 | labels.push(line); 13 | }); 14 | 15 | const app = express(); 16 | const host = '0.0.0.0'; 17 | const port = 8080; 18 | app.use(express.static(__dirname + '/public')); 19 | app.use(fileUpload()); 20 | // app.use(express.urlencoded({ extended: false })); 21 | 22 | app.get('/', (req, res) => res.redirect('/index.html')); 23 | 24 | app.post('/infer', function (req, res) { 25 | if (!req.files || Object.keys(req.files).length === 0) { 26 | return res.status(400).send('No files were uploaded.'); 27 | } 28 | console.log( 29 | 'Received ' + 30 | req.files.image_file.name + 31 | ' with size: ' + 32 | req.files.image_file.size 33 | ); 34 | 35 | let image_file = req.files.image_file; 36 | console.time(image_file.name); 37 | var result = JSON.parse(infer(data_model, image_file.data, 224, 224)); 38 | console.timeEnd(image_file.name); 39 | 40 | var confidence = 'low'; 41 | if (result[0] > 0.75) { 42 | confidence = 'very high'; 43 | } else if (result[0] > 0.5) { 44 | confidence = 'high'; 45 | } else if (result[0] > 0.2) { 46 | confidence = 'medium'; 47 | } 48 | res.send( 49 | 'Detected ' + 50 | labels[result[1] - 1] + 51 | ' with ' + 52 | confidence + 53 | ' confidence.' 54 | ); 55 | }); 56 | 57 | app.listen(port, host, () => 58 | console.log(`Listening at http://${host}:${port}`) 59 | ); 60 | -------------------------------------------------------------------------------- /node/test.js: -------------------------------------------------------------------------------- 1 | const { infer } = require('../pkg/csdn_ai_demo_lib.js'); 2 | 3 | const fs = require('fs'); 4 | var data_model = fs.readFileSync("mobilenet_v2_1.4_224_frozen.pb"); 5 | var data_img_cat = fs.readFileSync("cat.png"); 6 | var data_img_hopper = fs.readFileSync("grace_hopper.jpg"); 7 | 8 | var result = JSON.parse( infer(data_model, data_img_hopper, 224, 224) ); 9 | console.log("Detected object id " + result[1] + " with probability " + result[0]); 10 | 11 | var result = JSON.parse( infer(data_model, data_img_cat, 224, 224) ); 12 | console.log("Detected object id " + result[1] + " with probability " + result[0]); -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use wasm_bindgen::prelude::*; 2 | use tract_tensorflow::prelude::*; 3 | use std::io::Cursor; 4 | 5 | #[wasm_bindgen] 6 | pub fn infer(model_data: &[u8], image_data: &[u8], image_height: i32, image_width: i32) -> String { 7 | let res: (f32, u32) = infer_impl (model_data, image_data, image_height as usize, image_width as usize).unwrap(); 8 | return serde_json::to_string(&res).unwrap(); 9 | } 10 | 11 | fn infer_impl (model_data: &[u8], image_data: &[u8], image_height: usize, image_width: usize) -> TractResult<(f32, u32)> { 12 | // load the model 13 | let mut model_data_mut = Cursor::new(model_data); 14 | let mut model = tract_tensorflow::tensorflow().model_for_read(&mut model_data_mut)?; 15 | 16 | // specify input type and shape 17 | model.set_input_fact(0, InferenceFact::dt_shape(f32::datum_type(), tvec!(1, image_height, image_width, 3)))?; 18 | // optimize the model and get an execution plan 19 | let model = model.into_optimized()?; 20 | let plan = SimplePlan::new(&model)?; 21 | 22 | // open image, resize it and make a Tensor out of it 23 | let image = image::load_from_memory(image_data).unwrap().to_rgb(); 24 | let resized = image::imageops::resize(&image, image_height as u32, image_width as u32, ::image::imageops::FilterType::Triangle); 25 | let image: Tensor = tract_ndarray::Array4::from_shape_fn((1, image_height, image_width, 3), |(_, y, x, c)| { 26 | resized[(x as _, y as _)][c] as f32 / 255.0 27 | }) 28 | .into(); 29 | 30 | // run the plan on the input 31 | let result = plan.run(tvec!(image))?; 32 | 33 | // find and display the max value with its index 34 | let best = result[0] 35 | .to_array_view::()? 36 | .iter() 37 | .cloned() 38 | .zip(1..) 39 | .max_by(|a, b| a.0.partial_cmp(&b.0).unwrap()); 40 | match best { 41 | Some(t) => Ok(t), 42 | None => Ok((0.0, 0)), 43 | } 44 | } --------------------------------------------------------------------------------