├── site
├── dfu.js
├── dfuse.js
├── firmware.bin
├── tflite.js
├── index.html
└── models.js
├── .gitignore
├── .gitmodules
├── tflite
├── BUILD
├── queue.h
├── interpreter.cc
└── libusb.cc
├── CONTRIBUTING.md
├── docker
└── Dockerfile
├── scripts
└── linux_device_rules.sh
├── WORKSPACE
├── README.md
├── Makefile
└── LICENSE
/site/dfu.js:
--------------------------------------------------------------------------------
1 | ../third_party/webdfu/dfu-util/dfu.js
--------------------------------------------------------------------------------
/site/dfuse.js:
--------------------------------------------------------------------------------
1 | ../third_party/webdfu/dfu-util/dfuse.js
--------------------------------------------------------------------------------
/site/firmware.bin:
--------------------------------------------------------------------------------
1 | ../third_party/libedgetpu/driver/usb/apex_latest_single_ep.bin
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bazel-*
2 | .distdir
3 | *.zip
4 | site/interpreter.*
5 | site/models/*.tflite
6 | third_party/dfu-util
7 |
8 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "third_party/libedgetpu"]
2 | path = third_party/libedgetpu
3 | url = https://github.com/google-coral/libedgetpu.git
4 | [submodule "third_party/webdfu"]
5 | path = third_party/webdfu
6 | url = https://github.com/devanlai/webdfu.git
7 |
--------------------------------------------------------------------------------
/tflite/BUILD:
--------------------------------------------------------------------------------
1 | load("@emsdk//emscripten_toolchain:wasm_rules.bzl", "wasm_cc_binary")
2 |
3 | cc_binary(
4 | name = "interpreter",
5 | srcs = ["interpreter.cc", "libusb.cc", "queue.h"],
6 | deps = [
7 | "@libedgetpu//tflite/public:edgetpu_c",
8 | "@libedgetpu//tflite/public:oss_edgetpu_direct_usb",
9 | "@org_tensorflow//tensorflow/lite:framework",
10 | "@org_tensorflow//tensorflow/lite/kernels:builtin_ops", # BuiltinOpResolver
11 | ]
12 | )
13 |
14 | wasm_cc_binary(
15 | name = "interpreter-wasm",
16 | cc_target = ":interpreter",
17 | )
18 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | ## Contributor License Agreement
4 |
5 | Contributions to this project must be accompanied by a Contributor License
6 | Agreement (CLA). You (or your employer) retain the copyright to your
7 | contribution; this simply gives us permission to use and redistribute your
8 | contributions as part of the project. Head over to
9 | to see your current agreements on file or
10 | to sign a new one.
11 |
12 | You generally only need to submit a CLA once, so if you've already submitted one
13 | (even if it was for a different project), you probably don't need to do it
14 | again.
15 |
16 | ## Code reviews
17 |
18 | All submissions, including submissions by project members, require review. We
19 | use GitHub pull requests for this purpose. Consult
20 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
21 | information on using pull requests.
22 |
23 | ## Community Guidelines
24 |
25 | This project follows
26 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/).
27 |
--------------------------------------------------------------------------------
/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | # Copyright 2019 Google LLC
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # https://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | ARG IMAGE
15 | FROM ${IMAGE}
16 |
17 | RUN apt-get update && apt-get install -y \
18 | sudo \
19 | curl \
20 | wget \
21 | vim \
22 | zip \
23 | unzip \
24 | make \
25 | git \
26 | python3-all \
27 | python3-numpy \
28 | build-essential \
29 | libusb-1.0-0-dev
30 |
31 | RUN ln -s /usr/bin/python3 /usr/bin/python
32 |
33 | ARG BAZEL_VERSION=4.0.0
34 | RUN wget -O /bazel https://github.com/bazelbuild/bazel/releases/download/${BAZEL_VERSION}/bazel-${BAZEL_VERSION}-installer-linux-x86_64.sh && \
35 | bash /bazel && \
36 | rm -f /bazel
37 |
--------------------------------------------------------------------------------
/scripts/linux_device_rules.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Copyright 2019 Google LLC
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # https://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | set -e
17 |
18 | readonly RULES_FILE=/etc/udev/rules.d/99-edgetpu-accelerator.rules
19 |
20 | if [[ ! -x "$(command -v udevadm)" ]]; then
21 | echo "Your system does not support device rules"
22 | exit 1
23 | fi
24 |
25 | if [[ "${EUID}" != 0 ]]; then
26 | echo "Please use sudo to run as root"
27 | exit 1
28 | fi
29 |
30 | if [[ "$1" == "install" ]]; then
31 | cat << EOF > "${RULES_FILE}"
32 | SUBSYSTEM=="usb",ATTRS{idVendor}=="1a6e",ATTRS{idProduct}=="089a",GROUP="plugdev"
33 | SUBSYSTEM=="usb",ATTRS{idVendor}=="18d1",ATTRS{idProduct}=="9302",GROUP="plugdev"
34 | EOF
35 | udevadm control --reload-rules && udevadm trigger
36 | cat "${RULES_FILE}"
37 | elif [[ "$1" == "uninstall" ]]; then
38 | rm -f "${RULES_FILE}"
39 | udevadm control --reload-rules && udevadm trigger
40 | else
41 | echo "$0 install|uninstall"
42 | exit 1
43 | fi
44 |
45 |
--------------------------------------------------------------------------------
/tflite/queue.h:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 | #ifndef TFLITE_QUEUE_H_
15 | #define TFLITE_QUEUE_H_
16 |
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 |
23 | template
24 | class Queue {
25 | public:
26 | Queue() {}
27 |
28 | void Push(T t) {
29 | {
30 | std::lock_guard lock(m_);
31 | q_.push(t);
32 | }
33 | cv_.notify_one();
34 | }
35 |
36 | std::optional Pop(int timeout_ms) {
37 | std::unique_lock lock(m_);
38 | if (!cv_.wait_for(lock,
39 | std::chrono::milliseconds(timeout_ms),
40 | [this]{ return !q_.empty(); }))
41 | return std::nullopt;
42 |
43 | auto t = q_.front();
44 | q_.pop();
45 | return t;
46 | }
47 |
48 | private:
49 | std::mutex m_;
50 | std::condition_variable cv_;
51 | std::queue q_;
52 | };
53 |
54 | #endif // TFLITE_QUEUE_H_
55 |
--------------------------------------------------------------------------------
/WORKSPACE:
--------------------------------------------------------------------------------
1 | # Copyright 2019 Google LLC
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # https://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | workspace(name = "coral-web")
15 |
16 | # Configure libedgetpu and downstream libraries (TF and Crosstool).
17 | new_local_repository(
18 | name = "libedgetpu",
19 | path = "third_party/libedgetpu",
20 | build_file = "third_party/libedgetpu/BUILD"
21 | )
22 |
23 | TENSORFLOW_COMMIT = "ee728bcc18abef50b968c4b4cb06523f64abf7ac"
24 | TENSORFLOW_SHA256 = "d429e78426a08ddfe121514aa132b3e90051055e3a379676ade8058a266b726e"
25 |
26 | load("@libedgetpu//:workspace.bzl", "libedgetpu_dependencies")
27 | libedgetpu_dependencies(TENSORFLOW_COMMIT, TENSORFLOW_SHA256)
28 |
29 | load("@org_tensorflow//tensorflow:workspace3.bzl", "tf_workspace3")
30 | tf_workspace3()
31 |
32 | load("@org_tensorflow//tensorflow:workspace2.bzl", "tf_workspace2")
33 | tf_workspace2()
34 |
35 | load("@org_tensorflow//tensorflow:workspace1.bzl", "tf_workspace1")
36 | tf_workspace1()
37 |
38 | load("@org_tensorflow//tensorflow:workspace0.bzl", "tf_workspace0")
39 | tf_workspace0()
40 |
41 | # Emscripten
42 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
43 |
44 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
45 | http_archive(
46 | name = "emsdk",
47 | strip_prefix = "emsdk-c1589b55641787d55d53e883852035beea9aec3f/bazel",
48 | url = "https://github.com/emscripten-core/emsdk/archive/c1589b55641787d55d53e883852035beea9aec3f.tar.gz",
49 | sha256 = "7a58a9996b113d3e0675df30b5f17e28aa47de2e684a844f05394fe2f6f12e8e",
50 | )
51 |
52 | load("@emsdk//:deps.bzl", emsdk_deps = "deps")
53 | emsdk_deps()
54 |
55 | load("@emsdk//:emscripten_deps.bzl", emsdk_emscripten_deps = "emscripten_deps")
56 | emsdk_emscripten_deps()
57 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WebCoral demo
2 |
3 | This is a project to demonstrate how to use the
4 | [Coral USB Accelerator](https://coral.ai/products/accelerator/) from the Chrome
5 | browser with [WebUSB](https://wicg.github.io/webusb/).
6 |
7 | Currently, this demo is verified to work only on Linux and macOS.
8 |
9 | ## Web Server Setup
10 |
11 | Clone this repo with submodules:
12 | ```
13 | git clone --recurse-submodules https://github.com/google-coral/webcoral
14 | ```
15 |
16 | Switch directory:
17 | ```
18 | cd webcoral
19 | ```
20 |
21 | Download `.tflite` model files to `site/models/`:
22 | ```
23 | make download
24 | ```
25 |
26 | Build WASM files inside a Docker container:
27 | ```
28 | DOCKER_SHELL_COMMAND="make COMPILATION_MODE=opt wasm" make docker-shell
29 | ```
30 |
31 | Run local web server using python:
32 | ```
33 | make server
34 | ```
35 |
36 | Server is listening on port `8000`.
37 |
38 | ## System Setup
39 |
40 | On **macOS**, you don't need to install anything else.
41 |
42 | On **Linux**, you need to install device rules to make the Coral USB Accelerator
43 | visible in Chrome. You probably already have them installed if you are using
44 | Coral products. If not, this repo has a bash script to install the device rules:
45 | ```
46 | scripts/linux_device_rules.sh install
47 | ```
48 | and corresponding uninstall command if needed:
49 | ```
50 | scripts/linux_device_rules.sh uninstall
51 | ```
52 |
53 | ## Device Setup
54 |
55 | To use the USB Accelerator from the web browser, you need to update the USB
56 | Accelerator's firmware as follows. The firmware is usually automatically flashed
57 | by [libedgetpu](https://github.com/google-coral/libedgetpu) library when using
58 | C++ or Python programs, but that’s not the case from the browser.
59 |
60 | If you run `lsusb` command right after plugging USB device in, you'll see:
61 | ```
62 | Bus 001 Device 008: ID 1a6e:089a Global Unichip Corp.
63 | ```
64 |
65 | This means firmware is not flashed yet. It is possible to flash firmware
66 | directly from Chrome or using command line.
67 |
68 | To flash firmware from command line:
69 | ```
70 | make reset
71 | ```
72 |
73 | To flash from Chrome browser, point it to http://localhost:8000/ and press
74 | `Flash Device Firmware` button. WARNING: this only works on Linux now.
75 | There is an [issue](https://crbug.com/1189418) on macOS. It is already fixed and
76 | the fix will be available in Chrome 91.
77 |
78 | Either way, you should now verify it's flashed by again running `lsusb` and you
79 | should see:
80 | ```
81 | Bus 001 Device 009: ID 18d1:9302 Google Inc.
82 | ```
83 | which means that device is ready to use.
84 |
85 |
86 | ## Demo
87 |
88 | Open Chrome at http://localhost:8000/. Choose the model you want to run and
89 | press the **Initialize Interpreter** button. Selecting an Edge TPU model then
90 | requires you to select the USB Accelerator in the dialog; selecting a CPU model
91 | will be ready immediately. At this moment, you can run inference on any local
92 | image file by pressing the **Choose Image File** button.
93 |
94 | 
95 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Copyright 2019 Google LLC
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # https://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | MAKEFILE_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST))))
15 | TEST_DATA_URL := https://github.com/google-coral/edgetpu/raw/master/test_data
16 |
17 | .PHONY: wasm download zip server reset clean
18 |
19 | COMPILATION_MODE ?= dbg
20 | ifeq ($(filter $(COMPILATION_MODE),opt dbg),)
21 | $(error COMPILATION_MODE must be opt or dbg)
22 | endif
23 |
24 | ifneq (,$(wildcard /.dockerenv))
25 | BAZEL_OPTIONS += --output_base=/output/base --output_user_root=/output/user_root
26 | endif
27 |
28 | wasm:
29 | bazel $(BAZEL_OPTIONS) build \
30 | --distdir=$(MAKEFILE_DIR)/.distdir \
31 | --verbose_failures \
32 | --sandbox_debug \
33 | --subcommands \
34 | --experimental_repo_remote_exec \
35 | --compilation_mode=$(COMPILATION_MODE) \
36 | --define darwinn_portable=1 \
37 | --action_env PYTHON_BIN_PATH=$(shell which python3) \
38 | --features=use_pthreads \
39 | --linkopt=-sPTHREAD_POOL_SIZE=8 \
40 | --linkopt=-sINITIAL_MEMORY=67108864 \
41 | --linkopt="-sEXTRA_EXPORTED_RUNTIME_METHODS=['cwrap']" \
42 | --linkopt=-sASYNCIFY \
43 | --linkopt=-sASYNCIFY_STACK_SIZE=16384 \
44 | --linkopt="-sASYNCIFY_IMPORTS=['emscripten_receive_on_main_thread_js','emscripten_asm_const_int_sync_on_main_thread']" \
45 | //tflite:interpreter-wasm && \
46 | cp -f "$(MAKEFILE_DIR)/bazel-bin/tflite/interpreter-wasm/interpreter.data" \
47 | "$(MAKEFILE_DIR)/bazel-bin/tflite/interpreter-wasm/interpreter.js" \
48 | "$(MAKEFILE_DIR)/bazel-bin/tflite/interpreter-wasm/interpreter.wasm" \
49 | "$(MAKEFILE_DIR)/bazel-bin/tflite/interpreter-wasm/interpreter.worker.js" \
50 | "$(MAKEFILE_DIR)/site"
51 |
52 | %.tflite:
53 | mkdir -p $(dir $@) && cd $(dir $@) && wget "$(TEST_DATA_URL)/$(notdir $@)"
54 |
55 | download: site/models/mobilenet_v1_1.0_224_quant.tflite \
56 | site/models/mobilenet_v1_1.0_224_quant_edgetpu.tflite \
57 | site/models/ssd_mobilenet_v2_coco_quant_postprocess.tflite \
58 | site/models/ssd_mobilenet_v2_coco_quant_postprocess_edgetpu.tflite \
59 | site/models/ssd_mobilenet_v2_face_quant_postprocess.tflite \
60 | site/models/ssd_mobilenet_v2_face_quant_postprocess_edgetpu.tflite
61 |
62 | third_party/dfu-util/src/dfu-util:
63 | git -C third_party clone git://git.code.sf.net/p/dfu-util/dfu-util || true
64 | cd third_party/dfu-util && ./autogen.sh && ./configure && make
65 |
66 | reset: third_party/dfu-util/src/dfu-util
67 | third_party/dfu-util/src/dfu-util \
68 | -D third_party/libedgetpu/driver/usb/apex_latest_single_ep.bin \
69 | -d 1a6e:089a -R || true
70 |
71 | zip:
72 | zip -r site.zip site
73 |
74 | server:
75 | cd "$(MAKEFILE_DIR)/site" && python3 -m http.server
76 |
77 | clean:
78 | rm -f $(MAKEFILE_DIR)/site/interpreter.* \
79 | $(MAKEFILE_DIR)/site/models/*.tflite
80 |
81 | ################################################################################
82 | # Docker commands
83 | ################################################################################
84 | DOCKER_CONTEXT_DIR := $(MAKEFILE_DIR)/docker
85 | DOCKER_WORKSPACE := $(MAKEFILE_DIR)
86 | DOCKER_CONTAINER_WORKSPACE := /workspace
87 | DOCKER_CPUS ?= k8 armv7a armv6 aarch64
88 | DOCKER_TARGETS ?=
89 | DOCKER_IMAGE ?= debian:buster
90 | DOCKER_TAG_BASE ?= webcoral
91 | DOCKER_TAG := "$(DOCKER_TAG_BASE)-$(subst :,-,$(DOCKER_IMAGE))"
92 | DOCKER_SHELL_COMMAND ?=
93 |
94 | DOCKER_MAKE_COMMAND := \
95 | for cpu in $(DOCKER_CPUS); do \
96 | make CPU=\$${cpu} -C $(DOCKER_CONTAINER_WORKSPACE) $(DOCKER_TARGETS) || exit 1; \
97 | done
98 |
99 | define docker_run_command
100 | chmod a+w /; \
101 | groupadd --gid $(shell id -g) $(shell id -g -n); \
102 | useradd -m -e '' -s /bin/bash --gid $(shell id -g) --uid $(shell id -u) $(shell id -u -n); \
103 | echo '$(shell id -u -n) ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers; \
104 | su $(shell id -u -n) $(if $(1),-c '$(1)',)
105 | endef
106 |
107 | docker-image:
108 | docker build $(DOCKER_IMAGE_OPTIONS) -t $(DOCKER_TAG) \
109 | --build-arg IMAGE=$(DOCKER_IMAGE) $(DOCKER_CONTEXT_DIR)
110 |
111 | docker-shell: docker-image
112 | docker run --rm -i --tty --workdir $(DOCKER_CONTAINER_WORKSPACE) \
113 | --tmpfs /output:rw,exec,nr_inodes=0,size=20G \
114 | -v $(DOCKER_WORKSPACE):$(DOCKER_CONTAINER_WORKSPACE) \
115 | $(DOCKER_TAG) /bin/bash -c "$(call docker_run_command,$(DOCKER_SHELL_COMMAND))"
116 |
117 | docker-build: docker-image
118 | docker run --rm -i $(shell tty -s && echo --tty) \
119 | -v $(DOCKER_WORKSPACE):$(DOCKER_CONTAINER_WORKSPACE) \
120 | $(DOCKER_TAG) /bin/bash -c "$(call docker_run_command,$(DOCKER_MAKE_COMMAND))"
121 |
--------------------------------------------------------------------------------
/site/tflite.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2021 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 | const tflite = {};
18 |
19 | (function() {
20 | 'use strict';
21 |
22 | let nextId = 0;
23 |
24 | function rgbaArrayToRgbArray(rgbaArray) {
25 | const rgbArray = new Uint8Array(new ArrayBuffer(3 * rgbaArray.length / 4));
26 | for (let i = 0, j = 0; i < rgbaArray.length; i += 4, j += 3) {
27 | rgbArray[j + 0] = rgbaArray[i + 0];
28 | rgbArray[j + 1] = rgbaArray[i + 1];
29 | rgbArray[j + 2] = rgbaArray[i + 2];
30 | }
31 | return rgbArray;
32 | }
33 |
34 | function callbackKey(id) {
35 | return 'interpreter_' + id;
36 | }
37 |
38 | tflite.setRgbInput = function(interpreter, rgbArray, index=0) {
39 | const shape = interpreter.inputShape(index);
40 | if (rgbArray.length != shape.reduce((a, b) => a * b))
41 | throw new Error('Invalid input array size');
42 |
43 | writeArrayToMemory(rgbArray, interpreter.inputBuffer(index));
44 | }
45 |
46 | tflite.setRgbaInput = function(interpreter, rgbaArray) {
47 | tflite.setRgbInput(interpreter, rgbaArrayToRgbArray(rgbaArray));
48 | }
49 |
50 | tflite.getClassificationOutput = function(interpreter, index=0) {
51 | const count = interpreter.outputShape(index).reduce((a, b) => a * b);
52 | const scoresPtr = interpreter.outputBuffer(index) / Module.HEAPU8.BYTES_PER_ELEMENT;
53 | const scores = Module.HEAPU8.slice(scoresPtr, scoresPtr + count);
54 | return scores.indexOf(Math.max(...scores));
55 | }
56 |
57 | tflite.getDetectionOutput = function(interpreter, threshold=0.0) {
58 | const bboxesPtr = interpreter.outputBuffer(0) / Module.HEAPF32.BYTES_PER_ELEMENT;
59 | const idsPtr = interpreter.outputBuffer(1) / Module.HEAPF32.BYTES_PER_ELEMENT;
60 | const scoresPtr = interpreter.outputBuffer(2) / Module.HEAPF32.BYTES_PER_ELEMENT;
61 | const countPtr = interpreter.outputBuffer(3) / Module.HEAPF32.BYTES_PER_ELEMENT;
62 |
63 | const count = Math.round(Module.HEAPF32[countPtr]);
64 | const bboxes = Module.HEAPF32.slice(bboxesPtr, bboxesPtr + 4 * count);
65 | const ids = Module.HEAPF32.slice(idsPtr, idsPtr + count);
66 | const scores = Module.HEAPF32.slice(scoresPtr, scoresPtr + count);
67 |
68 | const objects = [];
69 | for (let i = 0; i < count; ++i) {
70 | if (scores[i] < threshold)
71 | break;
72 |
73 | objects.push({
74 | 'id': ids[i],
75 | 'score': scores[i],
76 | 'bbox' : {
77 | 'ymin': Math.max(0.0, bboxes[4 * i]),
78 | 'xmin': Math.max(0.0, bboxes[4 * i + 1]),
79 | 'ymax': Math.min(1.0, bboxes[4 * i + 2]),
80 | 'xmax': Math.min(1.0, bboxes[4 * i + 3]),
81 | },
82 | });
83 | }
84 | return objects;
85 | }
86 |
87 | tflite.Interpreter = function() {
88 | this.interpreter_create = Module.cwrap('interpreter_create', 'number', ['number'], { async: true });
89 | this.interpreter_destroy = Module.cwrap('interpreter_destroy', null, ['number']);
90 |
91 | this.interpreter_num_inputs = Module.cwrap('interpreter_num_inputs', 'number', ['number']);
92 | this.interpreter_input_buffer = Module.cwrap('interpreter_input_buffer', 'number', ['number', 'number']);
93 | this.interpreter_num_input_dims = Module.cwrap('interpreter_num_input_dims', 'number', ['number', 'number']);
94 | this.interpreter_input_dim = Module.cwrap('interpreter_input_dim', 'number', ['number', 'number', 'number']);
95 |
96 | this.interpreter_num_outputs = Module.cwrap('interpreter_num_outputs', 'number', ['number']);
97 | this.interpreter_output_buffer = Module.cwrap('interpreter_output_buffer', 'number', ['number', 'number']);
98 | this.interpreter_num_output_dims = Module.cwrap('interpreter_num_output_dims', 'number', ['number', 'number']);
99 | this.interpreter_output_dim = Module.cwrap('interpreter_output_dim', 'number', ['number', 'number', 'number']);
100 |
101 | this.interpreter_invoke_async = Module.cwrap('interpreter_invoke_async', null, ['number', 'number']);
102 |
103 | this.id = nextId++;
104 |
105 | Module['invokeDone'] = function(id, result) {
106 | const key = callbackKey(id);
107 | const callback = Module[key];
108 | if (callback) callback(result);
109 | delete Module[key];
110 | };
111 | }
112 |
113 | tflite.Interpreter.prototype.createFromBuffer = async function(buffer) {
114 | const model = new Uint8Array(buffer);
115 | const modelBufferSize = model.length * model.BYTES_PER_ELEMENT;
116 | const modelBufferPtr = Module._malloc(modelBufferSize);
117 | Module.HEAPU8.set(model, modelBufferPtr);
118 | this.interpreter = await this.interpreter_create(modelBufferPtr, modelBufferSize, 0);
119 |
120 | if (this.interpreter == null)
121 | return false;
122 |
123 | this.input_shapes = [];
124 | this.input_buffers = [];
125 | const num_inputs = this.interpreter_num_inputs(this.interpreter);
126 | for (let ti = 0; ti < num_inputs; ++ti) {
127 | const shape = [];
128 | const dims = this.interpreter_num_input_dims(this.interpreter, ti);
129 | for (let i = 0; i < dims; ++i)
130 | shape.push(this.interpreter_input_dim(this.interpreter, ti, i));
131 | this.input_shapes.push(shape);
132 |
133 | const buffer = this.interpreter_input_buffer(this.interpreter, ti);
134 | this.input_buffers.push(buffer);
135 | }
136 |
137 | this.output_shapes = [];
138 | this.output_buffers = [];
139 | const num_outputs = this.interpreter_num_outputs(this.interpreter);
140 | for (let ti = 0; ti < num_outputs; ++ti) {
141 | const shape = [];
142 | const dims = this.interpreter_num_output_dims(this.interpreter, ti);
143 | for (let i = 0; i < dims; ++i)
144 | shape.push(this.interpreter_output_dim(this.interpreter, ti, i));
145 | this.output_shapes.push(shape);
146 |
147 | const buffer = this.interpreter_output_buffer(this.interpreter, ti);
148 | this.output_buffers.push(buffer);
149 | }
150 |
151 | return true;
152 | }
153 |
154 | tflite.Interpreter.prototype.destroy = function() {
155 | this.interpreter_destroy(this.interpreter);
156 | }
157 |
158 | tflite.Interpreter.prototype.numInputs = function() {
159 | return this.input_shapes.length;
160 | }
161 |
162 | tflite.Interpreter.prototype.inputBuffer = function(index) {
163 | return this.input_buffers[index];
164 | }
165 |
166 | tflite.Interpreter.prototype.inputShape = function(index) {
167 | return this.input_shapes[index];
168 | }
169 |
170 | tflite.Interpreter.prototype.numOutputs = function() {
171 | return this.output_shapes.length;
172 | }
173 |
174 | tflite.Interpreter.prototype.outputBuffer = function(index) {
175 | return this.output_buffers[index];
176 | }
177 |
178 | tflite.Interpreter.prototype.outputShape = function(index) {
179 | return this.output_shapes[index];
180 | }
181 |
182 | tflite.Interpreter.prototype.invoke = function() {
183 | const self = this;
184 | return new Promise((resolve, reject) => {
185 | Module[callbackKey(self.id)] = result => {
186 | (result ? resolve : reject)();
187 | };
188 | self.interpreter_invoke_async(self.interpreter, self.id);
189 | });
190 | }
191 | })();
192 |
--------------------------------------------------------------------------------
/tflite/interpreter.cc:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | #include
22 |
23 | #include "tflite/public/edgetpu_c.h"
24 |
25 | #include "tensorflow/lite/model_builder.h"
26 | #include "tensorflow/lite/interpreter.h"
27 | #include "tensorflow/lite/interpreter_builder.h"
28 | #include "tensorflow/lite/kernels/register.h" // BuiltinOpResolver
29 |
30 | #include "tflite/queue.h"
31 |
32 | namespace {
33 |
34 | constexpr char kEdgeTpuCustomOp[] = "edgetpu-custom-op";
35 | constexpr int kExit = -1;
36 |
37 | using DelegatePtr = std::unique_ptr;
39 |
40 | bool HasCustomOp(const tflite::FlatBufferModel& model, const char* name) {
41 | const auto* opcodes = model->operator_codes();
42 | if (!opcodes) return false;
43 |
44 | for (const auto* opcode : *opcodes) {
45 | auto* custom_op = opcode->custom_code();
46 | if (custom_op && std::strcmp(name, custom_op->c_str()) == 0) return true;
47 | }
48 |
49 | return false;
50 | }
51 |
52 | class Interpreter {
53 | public:
54 | Interpreter(): thread_([this]() {
55 | while (true) {
56 | if (auto cmd = queue_.Pop(250/*ms*/)) {
57 | if (cmd.value() == kExit) break;
58 | auto result = Invoke();
59 | MAIN_THREAD_ASYNC_EM_ASM({Module['invokeDone']($0, $1);},
60 | cmd.value(), result);
61 | }
62 | }
63 | }) {}
64 |
65 | ~Interpreter() {
66 | queue_.Push(kExit);
67 | thread_.join();
68 | }
69 |
70 | public:
71 | bool Init(const char* filename, int verbosity) {
72 | auto model = tflite::FlatBufferModel::BuildFromFile(filename);
73 | if (!model) {
74 | std::cerr << "[ERROR] Cannot load model" << std::endl;
75 | return false;
76 | }
77 | return Init(std::move(model), verbosity);
78 | }
79 |
80 | bool Init(const char* model_buffer, size_t model_buffer_size, int verbosity) {
81 | auto model = tflite::FlatBufferModel::BuildFromBuffer(model_buffer,
82 | model_buffer_size);
83 | if (!model) {
84 | std::cerr << "[ERROR] Cannot load model" << std::endl;
85 | return false;
86 | }
87 | return Init(std::move(model), verbosity);
88 | }
89 |
90 | bool Init(std::unique_ptr model, int verbosity) {
91 | // Model
92 | model_ = std::move(model);
93 |
94 | tflite::ops::builtin::BuiltinOpResolver resolver;
95 | if (tflite::InterpreterBuilder(*model_, resolver)(&interpreter_) != kTfLiteOk) {
96 | std::cerr << "[ERROR] Cannot create interpreter" << std::endl;
97 | return false;
98 | }
99 |
100 | if (HasCustomOp(*model_, kEdgeTpuCustomOp)) {
101 | edgetpu_verbosity(verbosity);
102 |
103 | size_t num_devices;
104 | std::unique_ptr
105 | devices(edgetpu_list_devices(&num_devices), &edgetpu_free_devices);
106 | if (num_devices < 1) {
107 | std::cerr << "[ERROR] Edge TPU is not connected" << std::endl;
108 | return false;
109 | }
110 |
111 | auto& device = devices.get()[0];
112 | edgetpu_option option = {"Usb.AlwaysDfu", "False"};
113 | DelegatePtr delegate(edgetpu_create_delegate(device.type, device.path,
114 | &option, 1),
115 | edgetpu_free_delegate);
116 | if (interpreter_->ModifyGraphWithDelegate(std::move(delegate)) != kTfLiteOk) {
117 | std::cerr << "[ERROR] Cannot apply EdgeTPU delegate" << std::endl;
118 | return false;
119 | }
120 | }
121 |
122 | if (interpreter_->AllocateTensors() != kTfLiteOk) {
123 | std::cerr << "[ERROR] Cannot allocated tensors" << std::endl;
124 | return false;
125 | }
126 |
127 | return true;
128 | }
129 |
130 | public:
131 | size_t NumInputs() const {
132 | return interpreter_->inputs().size();
133 | }
134 |
135 | void* InputBuffer(size_t tensor_index) const {
136 | return interpreter_->input_tensor(tensor_index)->data.data;
137 | }
138 |
139 | const size_t NumInputDims(size_t tensor_index) const {
140 | return interpreter_->input_tensor(tensor_index)->dims->size;
141 | }
142 |
143 | const size_t InputDim(size_t tensor_index, size_t dim) const {
144 | return interpreter_->input_tensor(tensor_index)->dims->data[dim];
145 | }
146 |
147 | public:
148 | size_t NumOutputs() const {
149 | return interpreter_->outputs().size();
150 | }
151 |
152 | const void* OutputBuffer(size_t tensor_index) const {
153 | return interpreter_->output_tensor(tensor_index)->data.data;
154 | }
155 |
156 | const size_t NumOutputDims(size_t tensor_index) const {
157 | return interpreter_->output_tensor(tensor_index)->dims->size;
158 | }
159 |
160 | const int OutputDim(size_t tensor_index, size_t dim) const {
161 | return interpreter_->output_tensor(tensor_index)->dims->data[dim];
162 | }
163 |
164 | public:
165 | bool Invoke() {
166 | if (interpreter_->Invoke() != kTfLiteOk) {
167 | std::cerr << "[ERROR] Cannot invoke interpreter" << std::endl;
168 | return false;
169 | }
170 | return true;
171 | }
172 |
173 | void InvokeAsync(size_t id) {
174 | queue_.Push(id);
175 | }
176 |
177 | private:
178 | std::unique_ptr model_;
179 | std::unique_ptr interpreter_;
180 | Queue queue_;
181 | std::thread thread_;
182 | };
183 |
184 | } // namespace
185 |
186 | extern "C" {
187 |
188 | EMSCRIPTEN_KEEPALIVE
189 | void* interpreter_create(const char* model_buffer, size_t model_buffer_size,
190 | int verbosity) {
191 | auto* interpreter = new Interpreter();
192 | if (!interpreter->Init(model_buffer, model_buffer_size, verbosity)) {
193 | delete interpreter;
194 | return nullptr;
195 | }
196 | return interpreter;
197 | }
198 |
199 | EMSCRIPTEN_KEEPALIVE
200 | void interpreter_destroy(void* p) {
201 | delete reinterpret_cast(p);
202 | }
203 |
204 | // Inputs
205 | EMSCRIPTEN_KEEPALIVE
206 | size_t interpreter_num_inputs(void* interpreter) {
207 | return reinterpret_cast(interpreter)->NumInputs();
208 | }
209 |
210 | EMSCRIPTEN_KEEPALIVE
211 | void* interpreter_input_buffer(void* interpreter, size_t tensor_index) {
212 | return reinterpret_cast(interpreter)->InputBuffer(tensor_index);
213 | }
214 |
215 | EMSCRIPTEN_KEEPALIVE
216 | size_t interpreter_num_input_dims(void *interpreter, size_t tensor_index) {
217 | return reinterpret_cast(interpreter)->NumInputDims(tensor_index);
218 | }
219 |
220 | EMSCRIPTEN_KEEPALIVE
221 | size_t interpreter_input_dim(void *interpreter, size_t tensor_index, size_t dim) {
222 | return reinterpret_cast(interpreter)->InputDim(tensor_index, dim);
223 | }
224 |
225 | // Outputs
226 | EMSCRIPTEN_KEEPALIVE
227 | size_t interpreter_num_outputs(void* interpreter) {
228 | return reinterpret_cast(interpreter)->NumOutputs();
229 | }
230 |
231 | EMSCRIPTEN_KEEPALIVE
232 | const void* interpreter_output_buffer(void* interpreter, size_t tensor_index) {
233 | return reinterpret_cast(interpreter)->OutputBuffer(tensor_index);
234 | }
235 |
236 | EMSCRIPTEN_KEEPALIVE
237 | size_t interpreter_num_output_dims(void *interpreter, size_t tensor_index) {
238 | return reinterpret_cast(interpreter)->NumOutputDims(tensor_index);
239 | }
240 |
241 | EMSCRIPTEN_KEEPALIVE
242 | size_t interpreter_output_dim(void *interpreter, size_t tensor_index, size_t dim) {
243 | return reinterpret_cast(interpreter)->OutputDim(tensor_index, dim);
244 | }
245 |
246 | EMSCRIPTEN_KEEPALIVE
247 | void interpreter_invoke_async(void *interpreter, size_t id) {
248 | return reinterpret_cast(interpreter)->InvokeAsync(id);
249 | }
250 |
251 | } // extern "C"
252 |
--------------------------------------------------------------------------------
/site/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Coral USB Accelerator Demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Coral USB Accelerator Demo
14 |
15 |
16 |
17 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
226 |
227 |
228 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/tflite/libusb.cc:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 | #include
15 | #include
16 |
17 | #include
18 | #include
19 |
20 | #include "tflite/queue.h"
21 |
22 | #define LIBUSB_MAJOR 1
23 | #define LIBUSB_MINOR 0
24 | #define LIBUSB_MICRO 24
25 | #define LIBUSB_NANO 0
26 | #define LIBUSB_RC ""
27 |
28 | // #define LIBUSB_ENABLE_LOG
29 | #ifdef LIBUSB_ENABLE_LOG
30 | #define LIBUSB_LOG(...) \
31 | do { \
32 | fprintf(stdout, __VA_ARGS__); \
33 | fprintf(stdout, "\n"); \
34 | fflush(stdout); \
35 | } while (false)
36 | #else // LIBUSB_ENABLE_LOG
37 | #define LIBUSB_LOG(...)
38 | #endif // LIBUSB_ENABLE_LOG
39 |
40 | struct libusb_device {
41 | uint8_t bus_number;
42 | uint8_t port_number;
43 | struct libusb_context *ctx;
44 | struct libusb_device_descriptor descriptor;
45 | };
46 |
47 | struct libusb_context {
48 | Queue completed_transfers;
49 | libusb_device dev;
50 | };
51 |
52 | struct libusb_device_handle {
53 | struct libusb_device *dev;
54 | };
55 |
56 | static const struct libusb_version kVersion = {
57 | LIBUSB_MAJOR,
58 | LIBUSB_MINOR,
59 | LIBUSB_MICRO,
60 | LIBUSB_NANO,
61 | LIBUSB_RC,
62 | "http://libusb.info"
63 | };
64 |
65 | static int js_request_device(struct libusb_device *dev) {
66 | return MAIN_THREAD_EM_ASM_INT({
67 | return Asyncify.handleAsync(async () => {
68 | // Bus 001 Device 005: ID 1a6e:089a Global Unichip Corp.
69 | // Bus 002 Device 007: ID 18d1:9302 Google Inc.
70 | let options = {'filters': [{'vendorId': 0x18d1, 'productId': 0x9302}]};
71 | let devices = await navigator.usb.getDevices();
72 | if (!devices.length) {
73 | try {
74 | let device = await navigator.usb.requestDevice(options);
75 | devices = [device];
76 | } catch (error) {
77 | devices = []
78 | }
79 | }
80 |
81 | if (devices.length === 1) {
82 | let d = devices[0];
83 | _fill_device($0,
84 | /*bcdUSB=*/(d.usbVersionMajor << 8) | d.usbVersionMinor,
85 | /*bDeviceClass=*/d.deviceClass,
86 | /*bDeviceSubClass=*/d.deviceSubClass,
87 | /*bDeviceProtocol=*/d.deviceProtocol,
88 | /*idVendor=*/d.vendorId,
89 | /*idProduct=*/d.productId,
90 | /*bcdDevice=*/(d.deviceVersionMajor << 8) | ((d.deviceVersionMinor << 4) | d.deviceVersionSubminor),
91 | /*bNumConfigurations=*/d.configurations.length);
92 | this.libusb_device = devices[0];
93 | return 1;
94 | }
95 | return 0;
96 | });
97 | }, dev);
98 | }
99 |
100 | static int js_control_transfer(uint8_t bmRequestType, uint8_t bRequest,
101 | uint16_t wValue, uint16_t wIndex, uint8_t *data,
102 | uint16_t wLength, unsigned int timeout) {
103 | return MAIN_THREAD_EM_ASM_INT({
104 | return Asyncify.handleAsync(async () => {
105 | let bmRequestType = $0;
106 | let bRequest = $1;
107 | let wValue = $2;
108 | let wIndex = $3;
109 | let data = $4;
110 | let wLength = $5;
111 | let timeout = $6;
112 |
113 | let setup = {
114 | 'requestType': ['standard', 'class', 'vendor'][(bmRequestType & 0x60) >> 5],
115 | 'recipient': ['device', 'interface', 'endpoint', 'other'][(bmRequestType & 0x1f)],
116 | 'request': bRequest,
117 | 'value': wValue,
118 | 'index': wIndex,
119 | };
120 |
121 | let dir_in = (bmRequestType & 0x80) == 0x80;
122 | if (dir_in) {
123 | let result = await this.libusb_device.controlTransferIn(setup, wLength);
124 | if (result.status != 'ok') {
125 | console.error('controlTransferIn', result);
126 | return 0;
127 | }
128 |
129 | let view = new Uint8Array(result.data.buffer);
130 | writeArrayToMemory(view, data);
131 | return result.data.buffer.byteLength;
132 | } else {
133 | let buffer = new Uint8Array(wLength);
134 | for (let i = 0; i < wLength; ++i)
135 | buffer[i] = getValue(data + i, 'i8');
136 |
137 | let result = await this.libusb_device.controlTransferOut(setup, buffer);
138 | if (result.status != 'ok') {
139 | console.error('controlTransferOut', result);
140 | return 0;
141 | }
142 | return result.bytesWritten;
143 | }
144 | });
145 | }, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout);
146 | }
147 |
148 | static void print(const char* line) { std::cout << line << std::endl; }
149 |
150 | static void print(const char* line, int value, bool hex=false) {
151 | if (hex)
152 | std::cout << line << std::hex << value << std::dec << std::endl;
153 | else
154 | std::cout << line << value << std::endl;
155 | }
156 |
157 | static void print_device(const struct libusb_device* dev) {
158 | print("USB Device");
159 | print(" Bus: ", dev->bus_number);
160 | print(" Port: ", dev->port_number);
161 | print(" Descriptor");
162 | print(" bLength: ", dev->descriptor.bLength);
163 | print(" bDescriptorType: ", dev->descriptor.bDescriptorType);
164 | print(" bcdUSB: 0x", dev->descriptor.bcdUSB, /*hex=*/true);
165 | print(" bDeviceClass: ", dev->descriptor.bDeviceClass);
166 | print(" bDeviceSubClass: ", dev->descriptor.bDeviceSubClass);
167 | print(" bDeviceProtocol: ", dev->descriptor.bDeviceProtocol);
168 | print(" bMaxPacketSize0: ", dev->descriptor.bMaxPacketSize0);
169 | print(" idVendor: 0x", dev->descriptor.idVendor, /*hex=*/true);
170 | print(" idProduct: 0x", dev->descriptor.idProduct, /*hex=*/true);
171 | print(" bcdDevice: 0x", dev->descriptor.bcdDevice, /*hex=*/true);
172 | print(" iManufacturer: ", dev->descriptor.iManufacturer);
173 | print(" iProduct: ", dev->descriptor.iProduct);
174 | print(" iSerialNumber: ", dev->descriptor.iSerialNumber);
175 | print(" bNumConfigurations: ", dev->descriptor.bNumConfigurations);
176 | }
177 |
178 | extern "C" {
179 |
180 | int libusb_init(libusb_context **ctx) {
181 | LIBUSB_LOG("libusb_init");
182 |
183 | if (!EM_ASM_INT(return navigator.usb !== undefined))
184 | return LIBUSB_ERROR_NOT_SUPPORTED;
185 |
186 | *ctx = new libusb_context;
187 | return LIBUSB_SUCCESS;
188 | }
189 |
190 | void LIBUSB_CALL libusb_exit(libusb_context *ctx) {
191 | LIBUSB_LOG("libusb_exit");
192 | delete ctx;
193 | }
194 |
195 | void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level) {
196 | LIBUSB_LOG("libusb_set_debug [NOT IMPLEMENTED]");
197 | }
198 |
199 | const struct libusb_version* LIBUSB_CALL libusb_get_version() {
200 | LIBUSB_LOG("libusb_get_version");
201 | return &kVersion;
202 | }
203 |
204 | struct libusb_transfer* LIBUSB_CALL libusb_alloc_transfer(int iso_packets) {
205 | LIBUSB_LOG("libusb_alloc_transfer");
206 |
207 | size_t size = sizeof(struct libusb_transfer) +
208 | sizeof(struct libusb_iso_packet_descriptor) * iso_packets;
209 |
210 | return reinterpret_cast(calloc(1, size));
211 | }
212 |
213 | int LIBUSB_CALL libusb_submit_transfer(struct libusb_transfer *transfer) {
214 | LIBUSB_LOG("libusb_submit_transfer");
215 |
216 | bool dir_in = (transfer->endpoint & 0x80) == 0x80;
217 | uint8_t endpoint = transfer->endpoint & 0x7f;
218 |
219 | switch (transfer->type) {
220 | case LIBUSB_TRANSFER_TYPE_BULK:
221 | case LIBUSB_TRANSFER_TYPE_INTERRUPT:
222 | if (dir_in) {
223 | MAIN_THREAD_ASYNC_EM_ASM({
224 | this.libusb_device.transferIn($0, $2).then(function(result) {
225 | var data = new Uint8Array(result.data.buffer);
226 | writeArrayToMemory(data, $1);
227 | _set_transfer_completed($3, data.length);
228 | }).catch(function(error) {
229 | console.error('transferIn', error);
230 | _set_transfer_error($3);
231 | });
232 | }, endpoint, transfer->buffer, transfer->length, transfer);
233 | } else {
234 | MAIN_THREAD_ASYNC_EM_ASM({
235 | var data = new Uint8Array($2);
236 | for(let i = 0; i < $2; ++i)
237 | data[i] = getValue($1 + i, 'i8');
238 |
239 | this.libusb_device.transferOut($0, data).then(function(result) {
240 | _set_transfer_completed($3, result.bytesWritten);
241 | }).catch(function(error) {
242 | console.error('transferOut', error);
243 | _set_transfer_error($3);
244 | });
245 | }, endpoint, transfer->buffer, transfer->length, transfer);
246 | }
247 | break;
248 | default:
249 | LIBUSB_LOG("Transfer type not implemented: %u\n", transfer->type);
250 | return LIBUSB_ERROR_IO;
251 | }
252 | return LIBUSB_SUCCESS;
253 | }
254 |
255 | int LIBUSB_CALL libusb_cancel_transfer(struct libusb_transfer *transfer) {
256 | LIBUSB_LOG("libusb_cancel_transfer [NOT IMPLEMENTED]");
257 | return 0;
258 | }
259 |
260 | void LIBUSB_CALL libusb_free_transfer(struct libusb_transfer *transfer) {
261 | LIBUSB_LOG("libusb_free_transfer");
262 | free(transfer);
263 | }
264 |
265 | uint8_t libusb_get_port_number(libusb_device * dev) {
266 | LIBUSB_LOG("libusb_get_port_number");
267 | return dev->port_number;
268 | }
269 |
270 | int libusb_get_port_numbers(libusb_device *dev,uint8_t *port_numbers, int port_numbers_len) {
271 | LIBUSB_LOG("libusb_get_port_numbers");
272 |
273 | if (port_numbers_len <= 0)
274 | return LIBUSB_ERROR_INVALID_PARAM;
275 |
276 | if (port_numbers_len < 1)
277 | return LIBUSB_ERROR_OVERFLOW;
278 |
279 | port_numbers[0] = dev->port_number;
280 | return 1;
281 | }
282 |
283 | int LIBUSB_CALL libusb_handle_events(libusb_context *ctx) {
284 | while (true) {
285 | if (auto item = ctx->completed_transfers.Pop(25/*ms*/)) {
286 | auto* transfer = item.value();
287 | transfer->callback(transfer);
288 | } else {
289 | break;
290 | }
291 | }
292 | return LIBUSB_SUCCESS;
293 | }
294 |
295 | int LIBUSB_CALL libusb_reset_device(libusb_device_handle *dev) {
296 | LIBUSB_LOG("libusb_reset_device");
297 |
298 | return MAIN_THREAD_EM_ASM_INT({
299 | return Asyncify.handleAsync(async () => {
300 | try {
301 | await this.libusb_device.reset();
302 | return 0; // LIBUSB_SUCCESS
303 | } catch (error) {
304 | console.error('reset', error);
305 | // TODO: return -1; // LIBUSB_ERROR_IO
306 | return 0; // LIBUSB_SUCCESS
307 | }
308 | });
309 | });
310 | }
311 |
312 | int LIBUSB_CALL libusb_control_transfer(libusb_device_handle *dev_handle,
313 | uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
314 | unsigned char *data, uint16_t wLength, unsigned int timeout) {
315 | LIBUSB_LOG("libusb_control_transfer");
316 | return js_control_transfer(request_type, bRequest, wValue, wIndex, data,
317 | wLength, timeout);
318 | }
319 |
320 | int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle *dev_handle,
321 | unsigned char endpoint, unsigned char *data, int length,
322 | int *actual_length, unsigned int timeout) {
323 | LIBUSB_LOG("libusb_bulk_transfer [NOT IMPLEMENTED]");
324 | return 0;
325 | }
326 |
327 | int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle,
328 | unsigned char endpoint, unsigned char *data, int length,
329 | int *actual_length, unsigned int timeout) {
330 | LIBUSB_LOG("libusb_interrupt_transfer [NOT IMPLEMENTED]");
331 | return 0;
332 | }
333 |
334 | int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **handle) {
335 | LIBUSB_LOG("libusb_open");
336 | // TODO: check dev->descriptor.idVendor and dev->descriptor.idProduct
337 | MAIN_THREAD_EM_ASM_INT({
338 | return Asyncify.handleAsync(async () => {
339 | await this.libusb_device.open();
340 | try {
341 | // TODO: Avoid resetting on open.
342 | await this.libusb_device.reset();
343 | } catch (error) {
344 | console.error('reset', error);
345 | }
346 | return 1;
347 | });
348 | });
349 |
350 | if (handle) {
351 | *handle = new libusb_device_handle;
352 | (*handle)->dev = dev;
353 | }
354 |
355 | return LIBUSB_SUCCESS;
356 | }
357 |
358 | void LIBUSB_CALL libusb_close(libusb_device_handle *dev_handle) {
359 | LIBUSB_LOG("libusb_close");
360 |
361 | MAIN_THREAD_EM_ASM_INT({
362 | Asyncify.handleAsync(async () => {
363 | return await this.libusb_device.close();
364 | });
365 | });
366 |
367 | delete dev_handle;
368 | }
369 |
370 | libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle) {
371 | LIBUSB_LOG("libusb_get_device");
372 | return dev_handle->dev;
373 | }
374 |
375 | ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx, libusb_device ***list) {
376 | LIBUSB_LOG("libusb_get_device_list");
377 |
378 | auto* dev = &ctx->dev;
379 | dev->ctx = ctx;
380 |
381 | if (js_request_device(dev)) {
382 | print_device(dev);
383 | *list = new libusb_device*[]{dev, nullptr};
384 | return 1;
385 | } else {
386 | *list = new libusb_device*[]{nullptr};
387 | return 0;
388 | }
389 | }
390 |
391 | void LIBUSB_CALL libusb_free_device_list(libusb_device* *list, int unref_devices) {
392 | LIBUSB_LOG("libusb_free_device_list: unref_devices=%d", unref_devices);
393 |
394 | int i = 0;
395 | while (true) {
396 | libusb_device *device = list[i++];
397 | if (device == nullptr) break;
398 | delete device;
399 | }
400 |
401 | delete [] list;
402 | }
403 |
404 | int LIBUSB_CALL libusb_get_device_descriptor(libusb_device *dev, struct libusb_device_descriptor *desc) {
405 | LIBUSB_LOG("libusb_get_device_descriptor");
406 | *desc = dev->descriptor;
407 | return 0;
408 | }
409 |
410 | int LIBUSB_CALL libusb_get_device_speed(libusb_device *dev) {
411 | LIBUSB_LOG("libusb_get_device_speed");
412 | return LIBUSB_SPEED_SUPER; // TODO: Implement.
413 | }
414 |
415 | uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev) {
416 | LIBUSB_LOG("libusb_get_bus_number");
417 | return dev->bus_number;
418 | }
419 |
420 | int LIBUSB_CALL libusb_set_configuration(libusb_device_handle *dev,
421 | int configuration) {
422 | LIBUSB_LOG("libusb_set_configuration [NOT IMPLEMENTED]");
423 | return 0;
424 | }
425 |
426 | int LIBUSB_CALL libusb_claim_interface(libusb_device_handle *dev,
427 | int interface_number) {
428 | LIBUSB_LOG("libusb_claim_interface: interface_number=%d", interface_number);
429 | return MAIN_THREAD_EM_ASM_INT({
430 | return Asyncify.handleAsync(async () => {
431 | try {
432 | await this.libusb_device.claimInterface($0);
433 | return 0; // LIBUSB_SUCCESS
434 | } catch (error) {
435 | console.error('claimInterface:', error);
436 | return -1; // LIBUSB_ERROR_IO
437 | }
438 | });
439 | }, interface_number);
440 | }
441 |
442 | int LIBUSB_CALL libusb_release_interface(libusb_device_handle *dev,
443 | int interface_number) {
444 | LIBUSB_LOG("libusb_release_interface: interface_number=%d", interface_number);
445 | return MAIN_THREAD_EM_ASM_INT({
446 | return Asyncify.handleAsync(async () => {
447 | try {
448 | await this.libusb_device.releaseInterface($0);
449 | return 0; // LIBUSB_SUCCESS
450 | } catch (error) {
451 | console.error('releaseInterface:', error);
452 | return -1; // LIBUSB_ERROR_IO
453 | }
454 | });
455 | }, interface_number);
456 | }
457 |
458 |
459 | EMSCRIPTEN_KEEPALIVE
460 | void set_transfer_error(struct libusb_transfer* transfer) {
461 | LIBUSB_LOG("set_tansfer_error: transfer=%p", transfer);
462 | libusb_context* ctx = transfer->dev_handle->dev->ctx;
463 |
464 | transfer->status = LIBUSB_TRANSFER_CANCELLED;
465 | transfer->actual_length = 0;
466 |
467 | ctx->completed_transfers.Push(transfer);
468 | }
469 |
470 | EMSCRIPTEN_KEEPALIVE
471 | void set_transfer_completed(struct libusb_transfer* transfer, int actual_length) {
472 | LIBUSB_LOG("set_tansfer_completed: transfer=%p, actual_length=%d",
473 | transfer, actual_length);
474 | libusb_context* ctx = transfer->dev_handle->dev->ctx;
475 |
476 | transfer->status = LIBUSB_TRANSFER_COMPLETED;
477 | transfer->actual_length = actual_length;
478 |
479 | ctx->completed_transfers.Push(transfer);
480 | }
481 |
482 | EMSCRIPTEN_KEEPALIVE
483 | void fill_device(struct libusb_device* dev,
484 | uint16_t bcdUSB,
485 | uint8_t bDeviceClass,
486 | uint8_t bDeviceSubClass,
487 | uint8_t bDeviceProtocol,
488 | uint16_t idVendor,
489 | uint16_t idProduct,
490 | uint16_t bcdDevice,
491 | uint8_t bNumConfigurations) {
492 | dev->bus_number = 0;
493 | dev->port_number = 1;
494 |
495 | struct libusb_device_descriptor* d = &dev->descriptor;
496 | d->bLength = LIBUSB_DT_DEVICE_SIZE;
497 | d->bDescriptorType = LIBUSB_DT_DEVICE;
498 | d->bcdUSB = bcdUSB;
499 | d->bDeviceClass = bDeviceClass;
500 | d->bDeviceSubClass = bDeviceSubClass;
501 | d->bDeviceProtocol = bDeviceProtocol;
502 | d->bMaxPacketSize0 = 64;
503 | d->idVendor = idVendor;
504 | d->idProduct = idProduct;
505 | d->bcdDevice = bcdDevice;
506 | d->iManufacturer = 1;
507 | d->iProduct = 2;
508 | d->iSerialNumber = 3;
509 | d->bNumConfigurations = bNumConfigurations;
510 | }
511 |
512 | } // extern "C"
513 |
--------------------------------------------------------------------------------
/site/models.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2021 Google LLC. All Rights Reserved.
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | * =============================================================================
16 | */
17 | const IMAGENET_LABELS = [
18 | "background",
19 | "tench, Tinca tinca",
20 | "goldfish, Carassius auratus",
21 | "great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias",
22 | "tiger shark, Galeocerdo cuvieri",
23 | "hammerhead, hammerhead shark",
24 | "electric ray, crampfish, numbfish, torpedo",
25 | "stingray",
26 | "cock",
27 | "hen",
28 | "ostrich, Struthio camelus",
29 | "brambling, Fringilla montifringilla",
30 | "goldfinch, Carduelis carduelis",
31 | "house finch, linnet, Carpodacus mexicanus",
32 | "junco, snowbird",
33 | "indigo bunting, indigo finch, indigo bird, Passerina cyanea",
34 | "robin, American robin, Turdus migratorius",
35 | "bulbul",
36 | "jay",
37 | "magpie",
38 | "chickadee",
39 | "water ouzel, dipper",
40 | "kite",
41 | "bald eagle, American eagle, Haliaeetus leucocephalus",
42 | "vulture",
43 | "great grey owl, great gray owl, Strix nebulosa",
44 | "European fire salamander, Salamandra salamandra",
45 | "common newt, Triturus vulgaris",
46 | "eft",
47 | "spotted salamander, Ambystoma maculatum",
48 | "axolotl, mud puppy, Ambystoma mexicanum",
49 | "bullfrog, Rana catesbeiana",
50 | "tree frog, tree-frog",
51 | "tailed frog, bell toad, ribbed toad, tailed toad, Ascaphus trui",
52 | "loggerhead, loggerhead turtle, Caretta caretta",
53 | "leatherback turtle, leatherback, leathery turtle, Dermochelys coriacea",
54 | "mud turtle",
55 | "terrapin",
56 | "box turtle, box tortoise",
57 | "banded gecko",
58 | "common iguana, iguana, Iguana iguana",
59 | "American chameleon, anole, Anolis carolinensis",
60 | "whiptail, whiptail lizard",
61 | "agama",
62 | "frilled lizard, Chlamydosaurus kingi",
63 | "alligator lizard",
64 | "Gila monster, Heloderma suspectum",
65 | "green lizard, Lacerta viridis",
66 | "African chameleon, Chamaeleo chamaeleon",
67 | "Komodo dragon, Komodo lizard, dragon lizard, giant lizard, Varanus komodoensis",
68 | "African crocodile, Nile crocodile, Crocodylus niloticus",
69 | "American alligator, Alligator mississipiensis",
70 | "triceratops",
71 | "thunder snake, worm snake, Carphophis amoenus",
72 | "ringneck snake, ring-necked snake, ring snake",
73 | "hognose snake, puff adder, sand viper",
74 | "green snake, grass snake",
75 | "king snake, kingsnake",
76 | "garter snake, grass snake",
77 | "water snake",
78 | "vine snake",
79 | "night snake, Hypsiglena torquata",
80 | "boa constrictor, Constrictor constrictor",
81 | "rock python, rock snake, Python sebae",
82 | "Indian cobra, Naja naja",
83 | "green mamba",
84 | "sea snake",
85 | "horned viper, cerastes, sand viper, horned asp, Cerastes cornutus",
86 | "diamondback, diamondback rattlesnake, Crotalus adamanteus",
87 | "sidewinder, horned rattlesnake, Crotalus cerastes",
88 | "trilobite",
89 | "harvestman, daddy longlegs, Phalangium opilio",
90 | "scorpion",
91 | "black and gold garden spider, Argiope aurantia",
92 | "barn spider, Araneus cavaticus",
93 | "garden spider, Aranea diademata",
94 | "black widow, Latrodectus mactans",
95 | "tarantula",
96 | "wolf spider, hunting spider",
97 | "tick",
98 | "centipede",
99 | "black grouse",
100 | "ptarmigan",
101 | "ruffed grouse, partridge, Bonasa umbellus",
102 | "prairie chicken, prairie grouse, prairie fowl",
103 | "peacock",
104 | "quail",
105 | "partridge",
106 | "African grey, African gray, Psittacus erithacus",
107 | "macaw",
108 | "sulphur-crested cockatoo, Kakatoe galerita, Cacatua galerita",
109 | "lorikeet",
110 | "coucal",
111 | "bee eater",
112 | "hornbill",
113 | "hummingbird",
114 | "jacamar",
115 | "toucan",
116 | "drake",
117 | "red-breasted merganser, Mergus serrator",
118 | "goose",
119 | "black swan, Cygnus atratus",
120 | "tusker",
121 | "echidna, spiny anteater, anteater",
122 | "platypus, duckbill, duckbilled platypus, duck-billed platypus, Ornithorhynchus anatinus",
123 | "wallaby, brush kangaroo",
124 | "koala, koala bear, kangaroo bear, native bear, Phascolarctos cinereus",
125 | "wombat",
126 | "jellyfish",
127 | "sea anemone, anemone",
128 | "brain coral",
129 | "flatworm, platyhelminth",
130 | "nematode, nematode worm, roundworm",
131 | "conch",
132 | "snail",
133 | "slug",
134 | "sea slug, nudibranch",
135 | "chiton, coat-of-mail shell, sea cradle, polyplacophore",
136 | "chambered nautilus, pearly nautilus, nautilus",
137 | "Dungeness crab, Cancer magister",
138 | "rock crab, Cancer irroratus",
139 | "fiddler crab",
140 | "king crab, Alaska crab, Alaskan king crab, Alaska king crab, Paralithodes camtschatica",
141 | "American lobster, Northern lobster, Maine lobster, Homarus americanus",
142 | "spiny lobster, langouste, rock lobster, crawfish, crayfish, sea crawfish",
143 | "crayfish, crawfish, crawdad, crawdaddy",
144 | "hermit crab",
145 | "isopod",
146 | "white stork, Ciconia ciconia",
147 | "black stork, Ciconia nigra",
148 | "spoonbill",
149 | "flamingo",
150 | "little blue heron, Egretta caerulea",
151 | "American egret, great white heron, Egretta albus",
152 | "bittern",
153 | "crane",
154 | "limpkin, Aramus pictus",
155 | "European gallinule, Porphyrio porphyrio",
156 | "American coot, marsh hen, mud hen, water hen, Fulica americana",
157 | "bustard",
158 | "ruddy turnstone, Arenaria interpres",
159 | "red-backed sandpiper, dunlin, Erolia alpina",
160 | "redshank, Tringa totanus",
161 | "dowitcher",
162 | "oystercatcher, oyster catcher",
163 | "pelican",
164 | "king penguin, Aptenodytes patagonica",
165 | "albatross, mollymawk",
166 | "grey whale, gray whale, devilfish, Eschrichtius gibbosus, Eschrichtius robustus",
167 | "killer whale, killer, orca, grampus, sea wolf, Orcinus orca",
168 | "dugong, Dugong dugon",
169 | "sea lion",
170 | "Chihuahua",
171 | "Japanese spaniel",
172 | "Maltese dog, Maltese terrier, Maltese",
173 | "Pekinese, Pekingese, Peke",
174 | "Shih-Tzu",
175 | "Blenheim spaniel",
176 | "papillon",
177 | "toy terrier",
178 | "Rhodesian ridgeback",
179 | "Afghan hound, Afghan",
180 | "basset, basset hound",
181 | "beagle",
182 | "bloodhound, sleuthhound",
183 | "bluetick",
184 | "black-and-tan coonhound",
185 | "Walker hound, Walker foxhound",
186 | "English foxhound",
187 | "redbone",
188 | "borzoi, Russian wolfhound",
189 | "Irish wolfhound",
190 | "Italian greyhound",
191 | "whippet",
192 | "Ibizan hound, Ibizan Podenco",
193 | "Norwegian elkhound, elkhound",
194 | "otterhound, otter hound",
195 | "Saluki, gazelle hound",
196 | "Scottish deerhound, deerhound",
197 | "Weimaraner",
198 | "Staffordshire bullterrier, Staffordshire bull terrier",
199 | "American Staffordshire terrier, Staffordshire terrier, American pit bull terrier, pit bull terrier",
200 | "Bedlington terrier",
201 | "Border terrier",
202 | "Kerry blue terrier",
203 | "Irish terrier",
204 | "Norfolk terrier",
205 | "Norwich terrier",
206 | "Yorkshire terrier",
207 | "wire-haired fox terrier",
208 | "Lakeland terrier",
209 | "Sealyham terrier, Sealyham",
210 | "Airedale, Airedale terrier",
211 | "cairn, cairn terrier",
212 | "Australian terrier",
213 | "Dandie Dinmont, Dandie Dinmont terrier",
214 | "Boston bull, Boston terrier",
215 | "miniature schnauzer",
216 | "giant schnauzer",
217 | "standard schnauzer",
218 | "Scotch terrier, Scottish terrier, Scottie",
219 | "Tibetan terrier, chrysanthemum dog",
220 | "silky terrier, Sydney silky",
221 | "soft-coated wheaten terrier",
222 | "West Highland white terrier",
223 | "Lhasa, Lhasa apso",
224 | "flat-coated retriever",
225 | "curly-coated retriever",
226 | "golden retriever",
227 | "Labrador retriever",
228 | "Chesapeake Bay retriever",
229 | "German short-haired pointer",
230 | "vizsla, Hungarian pointer",
231 | "English setter",
232 | "Irish setter, red setter",
233 | "Gordon setter",
234 | "Brittany spaniel",
235 | "clumber, clumber spaniel",
236 | "English springer, English springer spaniel",
237 | "Welsh springer spaniel",
238 | "cocker spaniel, English cocker spaniel, cocker",
239 | "Sussex spaniel",
240 | "Irish water spaniel",
241 | "kuvasz",
242 | "schipperke",
243 | "groenendael",
244 | "malinois",
245 | "briard",
246 | "kelpie",
247 | "komondor",
248 | "Old English sheepdog, bobtail",
249 | "Shetland sheepdog, Shetland sheep dog, Shetland",
250 | "collie",
251 | "Border collie",
252 | "Bouvier des Flandres, Bouviers des Flandres",
253 | "Rottweiler",
254 | "German shepherd, German shepherd dog, German police dog, alsatian",
255 | "Doberman, Doberman pinscher",
256 | "miniature pinscher",
257 | "Greater Swiss Mountain dog",
258 | "Bernese mountain dog",
259 | "Appenzeller",
260 | "EntleBucher",
261 | "boxer",
262 | "bull mastiff",
263 | "Tibetan mastiff",
264 | "French bulldog",
265 | "Great Dane",
266 | "Saint Bernard, St Bernard",
267 | "Eskimo dog, husky",
268 | "malamute, malemute, Alaskan malamute",
269 | "Siberian husky",
270 | "dalmatian, coach dog, carriage dog",
271 | "affenpinscher, monkey pinscher, monkey dog",
272 | "basenji",
273 | "pug, pug-dog",
274 | "Leonberg",
275 | "Newfoundland, Newfoundland dog",
276 | "Great Pyrenees",
277 | "Samoyed, Samoyede",
278 | "Pomeranian",
279 | "chow, chow chow",
280 | "keeshond",
281 | "Brabancon griffon",
282 | "Pembroke, Pembroke Welsh corgi",
283 | "Cardigan, Cardigan Welsh corgi",
284 | "toy poodle",
285 | "miniature poodle",
286 | "standard poodle",
287 | "Mexican hairless",
288 | "timber wolf, grey wolf, gray wolf, Canis lupus",
289 | "white wolf, Arctic wolf, Canis lupus tundrarum",
290 | "red wolf, maned wolf, Canis rufus, Canis niger",
291 | "coyote, prairie wolf, brush wolf, Canis latrans",
292 | "dingo, warrigal, warragal, Canis dingo",
293 | "dhole, Cuon alpinus",
294 | "African hunting dog, hyena dog, Cape hunting dog, Lycaon pictus",
295 | "hyena, hyaena",
296 | "red fox, Vulpes vulpes",
297 | "kit fox, Vulpes macrotis",
298 | "Arctic fox, white fox, Alopex lagopus",
299 | "grey fox, gray fox, Urocyon cinereoargenteus",
300 | "tabby, tabby cat",
301 | "tiger cat",
302 | "Persian cat",
303 | "Siamese cat, Siamese",
304 | "Egyptian cat",
305 | "cougar, puma, catamount, mountain lion, painter, panther, Felis concolor",
306 | "lynx, catamount",
307 | "leopard, Panthera pardus",
308 | "snow leopard, ounce, Panthera uncia",
309 | "jaguar, panther, Panthera onca, Felis onca",
310 | "lion, king of beasts, Panthera leo",
311 | "tiger, Panthera tigris",
312 | "cheetah, chetah, Acinonyx jubatus",
313 | "brown bear, bruin, Ursus arctos",
314 | "American black bear, black bear, Ursus americanus, Euarctos americanus",
315 | "ice bear, polar bear, Ursus Maritimus, Thalarctos maritimus",
316 | "sloth bear, Melursus ursinus, Ursus ursinus",
317 | "mongoose",
318 | "meerkat, mierkat",
319 | "tiger beetle",
320 | "ladybug, ladybeetle, lady beetle, ladybird, ladybird beetle",
321 | "ground beetle, carabid beetle",
322 | "long-horned beetle, longicorn, longicorn beetle",
323 | "leaf beetle, chrysomelid",
324 | "dung beetle",
325 | "rhinoceros beetle",
326 | "weevil",
327 | "fly",
328 | "bee",
329 | "ant, emmet, pismire",
330 | "grasshopper, hopper",
331 | "cricket",
332 | "walking stick, walkingstick, stick insect",
333 | "cockroach, roach",
334 | "mantis, mantid",
335 | "cicada, cicala",
336 | "leafhopper",
337 | "lacewing, lacewing fly",
338 | "dragonfly, darning needle, devil's darning needle, sewing needle, snake feeder, snake doctor, mosquito hawk, skeeter hawk",
339 | "damselfly",
340 | "admiral",
341 | "ringlet, ringlet butterfly",
342 | "monarch, monarch butterfly, milkweed butterfly, Danaus plexippus",
343 | "cabbage butterfly",
344 | "sulphur butterfly, sulfur butterfly",
345 | "lycaenid, lycaenid butterfly",
346 | "starfish, sea star",
347 | "sea urchin",
348 | "sea cucumber, holothurian",
349 | "wood rabbit, cottontail, cottontail rabbit",
350 | "hare",
351 | "Angora, Angora rabbit",
352 | "hamster",
353 | "porcupine, hedgehog",
354 | "fox squirrel, eastern fox squirrel, Sciurus niger",
355 | "marmot",
356 | "beaver",
357 | "guinea pig, Cavia cobaya",
358 | "sorrel",
359 | "zebra",
360 | "hog, pig, grunter, squealer, Sus scrofa",
361 | "wild boar, boar, Sus scrofa",
362 | "warthog",
363 | "hippopotamus, hippo, river horse, Hippopotamus amphibius",
364 | "ox",
365 | "water buffalo, water ox, Asiatic buffalo, Bubalus bubalis",
366 | "bison",
367 | "ram, tup",
368 | "bighorn, bighorn sheep, cimarron, Rocky Mountain bighorn, Rocky Mountain sheep, Ovis canadensis",
369 | "ibex, Capra ibex",
370 | "hartebeest",
371 | "impala, Aepyceros melampus",
372 | "gazelle",
373 | "Arabian camel, dromedary, Camelus dromedarius",
374 | "llama",
375 | "weasel",
376 | "mink",
377 | "polecat, fitch, foulmart, foumart, Mustela putorius",
378 | "black-footed ferret, ferret, Mustela nigripes",
379 | "otter",
380 | "skunk, polecat, wood pussy",
381 | "badger",
382 | "armadillo",
383 | "three-toed sloth, ai, Bradypus tridactylus",
384 | "orangutan, orang, orangutang, Pongo pygmaeus",
385 | "gorilla, Gorilla gorilla",
386 | "chimpanzee, chimp, Pan troglodytes",
387 | "gibbon, Hylobates lar",
388 | "siamang, Hylobates syndactylus, Symphalangus syndactylus",
389 | "guenon, guenon monkey",
390 | "patas, hussar monkey, Erythrocebus patas",
391 | "baboon",
392 | "macaque",
393 | "langur",
394 | "colobus, colobus monkey",
395 | "proboscis monkey, Nasalis larvatus",
396 | "marmoset",
397 | "capuchin, ringtail, Cebus capucinus",
398 | "howler monkey, howler",
399 | "titi, titi monkey",
400 | "spider monkey, Ateles geoffroyi",
401 | "squirrel monkey, Saimiri sciureus",
402 | "Madagascar cat, ring-tailed lemur, Lemur catta",
403 | "indri, indris, Indri indri, Indri brevicaudatus",
404 | "Indian elephant, Elephas maximus",
405 | "African elephant, Loxodonta africana",
406 | "lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens",
407 | "giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca",
408 | "barracouta, snoek",
409 | "eel",
410 | "coho, cohoe, coho salmon, blue jack, silver salmon, Oncorhynchus kisutch",
411 | "rock beauty, Holocanthus tricolor",
412 | "anemone fish",
413 | "sturgeon",
414 | "gar, garfish, garpike, billfish, Lepisosteus osseus",
415 | "lionfish",
416 | "puffer, pufferfish, blowfish, globefish",
417 | "abacus",
418 | "abaya",
419 | "academic gown, academic robe, judge's robe",
420 | "accordion, piano accordion, squeeze box",
421 | "acoustic guitar",
422 | "aircraft carrier, carrier, flattop, attack aircraft carrier",
423 | "airliner",
424 | "airship, dirigible",
425 | "altar",
426 | "ambulance",
427 | "amphibian, amphibious vehicle",
428 | "analog clock",
429 | "apiary, bee house",
430 | "apron",
431 | "ashcan, trash can, garbage can, wastebin, ash bin, ash-bin, ashbin, dustbin, trash barrel, trash bin",
432 | "assault rifle, assault gun",
433 | "backpack, back pack, knapsack, packsack, rucksack, haversack",
434 | "bakery, bakeshop, bakehouse",
435 | "balance beam, beam",
436 | "balloon",
437 | "ballpoint, ballpoint pen, ballpen, Biro",
438 | "Band Aid",
439 | "banjo",
440 | "bannister, banister, balustrade, balusters, handrail",
441 | "barbell",
442 | "barber chair",
443 | "barbershop",
444 | "barn",
445 | "barometer",
446 | "barrel, cask",
447 | "barrow, garden cart, lawn cart, wheelbarrow",
448 | "baseball",
449 | "basketball",
450 | "bassinet",
451 | "bassoon",
452 | "bathing cap, swimming cap",
453 | "bath towel",
454 | "bathtub, bathing tub, bath, tub",
455 | "beach wagon, station wagon, wagon, estate car, beach waggon, station waggon, waggon",
456 | "beacon, lighthouse, beacon light, pharos",
457 | "beaker",
458 | "bearskin, busby, shako",
459 | "beer bottle",
460 | "beer glass",
461 | "bell cote, bell cot",
462 | "bib",
463 | "bicycle-built-for-two, tandem bicycle, tandem",
464 | "bikini, two-piece",
465 | "binder, ring-binder",
466 | "binoculars, field glasses, opera glasses",
467 | "birdhouse",
468 | "boathouse",
469 | "bobsled, bobsleigh, bob",
470 | "bolo tie, bolo, bola tie, bola",
471 | "bonnet, poke bonnet",
472 | "bookcase",
473 | "bookshop, bookstore, bookstall",
474 | "bottlecap",
475 | "bow",
476 | "bow tie, bow-tie, bowtie",
477 | "brass, memorial tablet, plaque",
478 | "brassiere, bra, bandeau",
479 | "breakwater, groin, groyne, mole, bulwark, seawall, jetty",
480 | "breastplate, aegis, egis",
481 | "broom",
482 | "bucket, pail",
483 | "buckle",
484 | "bulletproof vest",
485 | "bullet train, bullet",
486 | "butcher shop, meat market",
487 | "cab, hack, taxi, taxicab",
488 | "caldron, cauldron",
489 | "candle, taper, wax light",
490 | "cannon",
491 | "canoe",
492 | "can opener, tin opener",
493 | "cardigan",
494 | "car mirror",
495 | "carousel, carrousel, merry-go-round, roundabout, whirligig",
496 | "carpenter's kit, tool kit",
497 | "carton",
498 | "car wheel",
499 | "cash machine, cash dispenser, automated teller machine, automatic teller machine, automated teller, automatic teller, ATM",
500 | "cassette",
501 | "cassette player",
502 | "castle",
503 | "catamaran",
504 | "CD player",
505 | "cello, violoncello",
506 | "cellular telephone, cellular phone, cellphone, cell, mobile phone",
507 | "chain",
508 | "chainlink fence",
509 | "chain mail, ring mail, mail, chain armor, chain armour, ring armor, ring armour",
510 | "chain saw, chainsaw",
511 | "chest",
512 | "chiffonier, commode",
513 | "chime, bell, gong",
514 | "china cabinet, china closet",
515 | "Christmas stocking",
516 | "church, church building",
517 | "cinema, movie theater, movie theatre, movie house, picture palace",
518 | "cleaver, meat cleaver, chopper",
519 | "cliff dwelling",
520 | "cloak",
521 | "clog, geta, patten, sabot",
522 | "cocktail shaker",
523 | "coffee mug",
524 | "coffeepot",
525 | "coil, spiral, volute, whorl, helix",
526 | "combination lock",
527 | "computer keyboard, keypad",
528 | "confectionery, confectionary, candy store",
529 | "container ship, containership, container vessel",
530 | "convertible",
531 | "corkscrew, bottle screw",
532 | "cornet, horn, trumpet, trump",
533 | "cowboy boot",
534 | "cowboy hat, ten-gallon hat",
535 | "cradle",
536 | "crane",
537 | "crash helmet",
538 | "crate",
539 | "crib, cot",
540 | "Crock Pot",
541 | "croquet ball",
542 | "crutch",
543 | "cuirass",
544 | "dam, dike, dyke",
545 | "desk",
546 | "desktop computer",
547 | "dial telephone, dial phone",
548 | "diaper, nappy, napkin",
549 | "digital clock",
550 | "digital watch",
551 | "dining table, board",
552 | "dishrag, dishcloth",
553 | "dishwasher, dish washer, dishwashing machine",
554 | "disk brake, disc brake",
555 | "dock, dockage, docking facility",
556 | "dogsled, dog sled, dog sleigh",
557 | "dome",
558 | "doormat, welcome mat",
559 | "drilling platform, offshore rig",
560 | "drum, membranophone, tympan",
561 | "drumstick",
562 | "dumbbell",
563 | "Dutch oven",
564 | "electric fan, blower",
565 | "electric guitar",
566 | "electric locomotive",
567 | "entertainment center",
568 | "envelope",
569 | "espresso maker",
570 | "face powder",
571 | "feather boa, boa",
572 | "file, file cabinet, filing cabinet",
573 | "fireboat",
574 | "fire engine, fire truck",
575 | "fire screen, fireguard",
576 | "flagpole, flagstaff",
577 | "flute, transverse flute",
578 | "folding chair",
579 | "football helmet",
580 | "forklift",
581 | "fountain",
582 | "fountain pen",
583 | "four-poster",
584 | "freight car",
585 | "French horn, horn",
586 | "frying pan, frypan, skillet",
587 | "fur coat",
588 | "garbage truck, dustcart",
589 | "gasmask, respirator, gas helmet",
590 | "gas pump, gasoline pump, petrol pump, island dispenser",
591 | "goblet",
592 | "go-kart",
593 | "golf ball",
594 | "golfcart, golf cart",
595 | "gondola",
596 | "gong, tam-tam",
597 | "gown",
598 | "grand piano, grand",
599 | "greenhouse, nursery, glasshouse",
600 | "grille, radiator grille",
601 | "grocery store, grocery, food market, market",
602 | "guillotine",
603 | "hair slide",
604 | "hair spray",
605 | "half track",
606 | "hammer",
607 | "hamper",
608 | "hand blower, blow dryer, blow drier, hair dryer, hair drier",
609 | "hand-held computer, hand-held microcomputer",
610 | "handkerchief, hankie, hanky, hankey",
611 | "hard disc, hard disk, fixed disk",
612 | "harmonica, mouth organ, harp, mouth harp",
613 | "harp",
614 | "harvester, reaper",
615 | "hatchet",
616 | "holster",
617 | "home theater, home theatre",
618 | "honeycomb",
619 | "hook, claw",
620 | "hoopskirt, crinoline",
621 | "horizontal bar, high bar",
622 | "horse cart, horse-cart",
623 | "hourglass",
624 | "iPod",
625 | "iron, smoothing iron",
626 | "jack-o'-lantern",
627 | "jean, blue jean, denim",
628 | "jeep, landrover",
629 | "jersey, T-shirt, tee shirt",
630 | "jigsaw puzzle",
631 | "jinrikisha, ricksha, rickshaw",
632 | "joystick",
633 | "kimono",
634 | "knee pad",
635 | "knot",
636 | "lab coat, laboratory coat",
637 | "ladle",
638 | "lampshade, lamp shade",
639 | "laptop, laptop computer",
640 | "lawn mower, mower",
641 | "lens cap, lens cover",
642 | "letter opener, paper knife, paperknife",
643 | "library",
644 | "lifeboat",
645 | "lighter, light, igniter, ignitor",
646 | "limousine, limo",
647 | "liner, ocean liner",
648 | "lipstick, lip rouge",
649 | "Loafer",
650 | "lotion",
651 | "loudspeaker, speaker, speaker unit, loudspeaker system, speaker system",
652 | "loupe, jeweler's loupe",
653 | "lumbermill, sawmill",
654 | "magnetic compass",
655 | "mailbag, postbag",
656 | "mailbox, letter box",
657 | "maillot",
658 | "maillot, tank suit",
659 | "manhole cover",
660 | "maraca",
661 | "marimba, xylophone",
662 | "mask",
663 | "matchstick",
664 | "maypole",
665 | "maze, labyrinth",
666 | "measuring cup",
667 | "medicine chest, medicine cabinet",
668 | "megalith, megalithic structure",
669 | "microphone, mike",
670 | "microwave, microwave oven",
671 | "military uniform",
672 | "milk can",
673 | "minibus",
674 | "miniskirt, mini",
675 | "minivan",
676 | "missile",
677 | "mitten",
678 | "mixing bowl",
679 | "mobile home, manufactured home",
680 | "Model T",
681 | "modem",
682 | "monastery",
683 | "monitor",
684 | "moped",
685 | "mortar",
686 | "mortarboard",
687 | "mosque",
688 | "mosquito net",
689 | "motor scooter, scooter",
690 | "mountain bike, all-terrain bike, off-roader",
691 | "mountain tent",
692 | "mouse, computer mouse",
693 | "mousetrap",
694 | "moving van",
695 | "muzzle",
696 | "nail",
697 | "neck brace",
698 | "necklace",
699 | "nipple",
700 | "notebook, notebook computer",
701 | "obelisk",
702 | "oboe, hautboy, hautbois",
703 | "ocarina, sweet potato",
704 | "odometer, hodometer, mileometer, milometer",
705 | "oil filter",
706 | "organ, pipe organ",
707 | "oscilloscope, scope, cathode-ray oscilloscope, CRO",
708 | "overskirt",
709 | "oxcart",
710 | "oxygen mask",
711 | "packet",
712 | "paddle, boat paddle",
713 | "paddlewheel, paddle wheel",
714 | "padlock",
715 | "paintbrush",
716 | "pajama, pyjama, pj's, jammies",
717 | "palace",
718 | "panpipe, pandean pipe, syrinx",
719 | "paper towel",
720 | "parachute, chute",
721 | "parallel bars, bars",
722 | "park bench",
723 | "parking meter",
724 | "passenger car, coach, carriage",
725 | "patio, terrace",
726 | "pay-phone, pay-station",
727 | "pedestal, plinth, footstall",
728 | "pencil box, pencil case",
729 | "pencil sharpener",
730 | "perfume, essence",
731 | "Petri dish",
732 | "photocopier",
733 | "pick, plectrum, plectron",
734 | "pickelhaube",
735 | "picket fence, paling",
736 | "pickup, pickup truck",
737 | "pier",
738 | "piggy bank, penny bank",
739 | "pill bottle",
740 | "pillow",
741 | "ping-pong ball",
742 | "pinwheel",
743 | "pirate, pirate ship",
744 | "pitcher, ewer",
745 | "plane, carpenter's plane, woodworking plane",
746 | "planetarium",
747 | "plastic bag",
748 | "plate rack",
749 | "plow, plough",
750 | "plunger, plumber's helper",
751 | "Polaroid camera, Polaroid Land camera",
752 | "pole",
753 | "police van, police wagon, paddy wagon, patrol wagon, wagon, black Maria",
754 | "poncho",
755 | "pool table, billiard table, snooker table",
756 | "pop bottle, soda bottle",
757 | "pot, flowerpot",
758 | "potter's wheel",
759 | "power drill",
760 | "prayer rug, prayer mat",
761 | "printer",
762 | "prison, prison house",
763 | "projectile, missile",
764 | "projector",
765 | "puck, hockey puck",
766 | "punching bag, punch bag, punching ball, punchball",
767 | "purse",
768 | "quill, quill pen",
769 | "quilt, comforter, comfort, puff",
770 | "racer, race car, racing car",
771 | "racket, racquet",
772 | "radiator",
773 | "radio, wireless",
774 | "radio telescope, radio reflector",
775 | "rain barrel",
776 | "recreational vehicle, RV, R.V.",
777 | "reel",
778 | "reflex camera",
779 | "refrigerator, icebox",
780 | "remote control, remote",
781 | "restaurant, eating house, eating place, eatery",
782 | "revolver, six-gun, six-shooter",
783 | "rifle",
784 | "rocking chair, rocker",
785 | "rotisserie",
786 | "rubber eraser, rubber, pencil eraser",
787 | "rugby ball",
788 | "rule, ruler",
789 | "running shoe",
790 | "safe",
791 | "safety pin",
792 | "saltshaker, salt shaker",
793 | "sandal",
794 | "sarong",
795 | "sax, saxophone",
796 | "scabbard",
797 | "scale, weighing machine",
798 | "school bus",
799 | "schooner",
800 | "scoreboard",
801 | "screen, CRT screen",
802 | "screw",
803 | "screwdriver",
804 | "seat belt, seatbelt",
805 | "sewing machine",
806 | "shield, buckler",
807 | "shoe shop, shoe-shop, shoe store",
808 | "shoji",
809 | "shopping basket",
810 | "shopping cart",
811 | "shovel",
812 | "shower cap",
813 | "shower curtain",
814 | "ski",
815 | "ski mask",
816 | "sleeping bag",
817 | "slide rule, slipstick",
818 | "sliding door",
819 | "slot, one-armed bandit",
820 | "snorkel",
821 | "snowmobile",
822 | "snowplow, snowplough",
823 | "soap dispenser",
824 | "soccer ball",
825 | "sock",
826 | "solar dish, solar collector, solar furnace",
827 | "sombrero",
828 | "soup bowl",
829 | "space bar",
830 | "space heater",
831 | "space shuttle",
832 | "spatula",
833 | "speedboat",
834 | "spider web, spider's web",
835 | "spindle",
836 | "sports car, sport car",
837 | "spotlight, spot",
838 | "stage",
839 | "steam locomotive",
840 | "steel arch bridge",
841 | "steel drum",
842 | "stethoscope",
843 | "stole",
844 | "stone wall",
845 | "stopwatch, stop watch",
846 | "stove",
847 | "strainer",
848 | "streetcar, tram, tramcar, trolley, trolley car",
849 | "stretcher",
850 | "studio couch, day bed",
851 | "stupa, tope",
852 | "submarine, pigboat, sub, U-boat",
853 | "suit, suit of clothes",
854 | "sundial",
855 | "sunglass",
856 | "sunglasses, dark glasses, shades",
857 | "sunscreen, sunblock, sun blocker",
858 | "suspension bridge",
859 | "swab, swob, mop",
860 | "sweatshirt",
861 | "swimming trunks, bathing trunks",
862 | "swing",
863 | "switch, electric switch, electrical switch",
864 | "syringe",
865 | "table lamp",
866 | "tank, army tank, armored combat vehicle, armoured combat vehicle",
867 | "tape player",
868 | "teapot",
869 | "teddy, teddy bear",
870 | "television, television system",
871 | "tennis ball",
872 | "thatch, thatched roof",
873 | "theater curtain, theatre curtain",
874 | "thimble",
875 | "thresher, thrasher, threshing machine",
876 | "throne",
877 | "tile roof",
878 | "toaster",
879 | "tobacco shop, tobacconist shop, tobacconist",
880 | "toilet seat",
881 | "torch",
882 | "totem pole",
883 | "tow truck, tow car, wrecker",
884 | "toyshop",
885 | "tractor",
886 | "trailer truck, tractor trailer, trucking rig, rig, articulated lorry, semi",
887 | "tray",
888 | "trench coat",
889 | "tricycle, trike, velocipede",
890 | "trimaran",
891 | "tripod",
892 | "triumphal arch",
893 | "trolleybus, trolley coach, trackless trolley",
894 | "trombone",
895 | "tub, vat",
896 | "turnstile",
897 | "typewriter keyboard",
898 | "umbrella",
899 | "unicycle, monocycle",
900 | "upright, upright piano",
901 | "vacuum, vacuum cleaner",
902 | "vase",
903 | "vault",
904 | "velvet",
905 | "vending machine",
906 | "vestment",
907 | "viaduct",
908 | "violin, fiddle",
909 | "volleyball",
910 | "waffle iron",
911 | "wall clock",
912 | "wallet, billfold, notecase, pocketbook",
913 | "wardrobe, closet, press",
914 | "warplane, military plane",
915 | "washbasin, handbasin, washbowl, lavabo, wash-hand basin",
916 | "washer, automatic washer, washing machine",
917 | "water bottle",
918 | "water jug",
919 | "water tower",
920 | "whiskey jug",
921 | "whistle",
922 | "wig",
923 | "window screen",
924 | "window shade",
925 | "Windsor tie",
926 | "wine bottle",
927 | "wing",
928 | "wok",
929 | "wooden spoon",
930 | "wool, woolen, woollen",
931 | "worm fence, snake fence, snake-rail fence, Virginia fence",
932 | "wreck",
933 | "yawl",
934 | "yurt",
935 | "web site, website, internet site, site",
936 | "comic book",
937 | "crossword puzzle, crossword",
938 | "street sign",
939 | "traffic light, traffic signal, stoplight",
940 | "book jacket, dust cover, dust jacket, dust wrapper",
941 | "menu",
942 | "plate",
943 | "guacamole",
944 | "consomme",
945 | "hot pot, hotpot",
946 | "trifle",
947 | "ice cream, icecream",
948 | "ice lolly, lolly, lollipop, popsicle",
949 | "French loaf",
950 | "bagel, beigel",
951 | "pretzel",
952 | "cheeseburger",
953 | "hotdog, hot dog, red hot",
954 | "mashed potato",
955 | "head cabbage",
956 | "broccoli",
957 | "cauliflower",
958 | "zucchini, courgette",
959 | "spaghetti squash",
960 | "acorn squash",
961 | "butternut squash",
962 | "cucumber, cuke",
963 | "artichoke, globe artichoke",
964 | "bell pepper",
965 | "cardoon",
966 | "mushroom",
967 | "Granny Smith",
968 | "strawberry",
969 | "orange",
970 | "lemon",
971 | "fig",
972 | "pineapple, ananas",
973 | "banana",
974 | "jackfruit, jak, jack",
975 | "custard apple",
976 | "pomegranate",
977 | "hay",
978 | "carbonara",
979 | "chocolate sauce, chocolate syrup",
980 | "dough",
981 | "meat loaf, meatloaf",
982 | "pizza, pizza pie",
983 | "potpie",
984 | "burrito",
985 | "red wine",
986 | "espresso",
987 | "cup",
988 | "eggnog",
989 | "alp",
990 | "bubble",
991 | "cliff, drop, drop-off",
992 | "coral reef",
993 | "geyser",
994 | "lakeside, lakeshore",
995 | "promontory, headland, head, foreland",
996 | "sandbar, sand bar",
997 | "seashore, coast, seacoast, sea-coast",
998 | "valley, vale",
999 | "volcano",
1000 | "ballplayer, baseball player",
1001 | "groom, bridegroom",
1002 | "scuba diver",
1003 | "rapeseed",
1004 | "daisy",
1005 | "yellow lady's slipper, yellow lady-slipper, Cypripedium calceolus, Cypripedium parviflorum",
1006 | "corn",
1007 | "acorn",
1008 | "hip, rose hip, rosehip",
1009 | "buckeye, horse chestnut, conker",
1010 | "coral fungus",
1011 | "agaric",
1012 | "gyromitra",
1013 | "stinkhorn, carrion fungus",
1014 | "earthstar",
1015 | "hen-of-the-woods, hen of the woods, Polyporus frondosus, Grifola frondosa",
1016 | "bolete",
1017 | "ear, spike, capitulum",
1018 | "toilet tissue, toilet paper, bathroom tissue"
1019 | ];
1020 |
1021 | const COCO_LABELS = [
1022 | 'person',
1023 | 'bicycle',
1024 | 'car',
1025 | 'motorcycle',
1026 | 'airplane',
1027 | 'bus',
1028 | 'train',
1029 | 'truck',
1030 | 'boat',
1031 | 'traffic light',
1032 | 'fire hydrant',
1033 | 'n/a',
1034 | 'stop sign',
1035 | 'parking meter',
1036 | 'bench',
1037 | 'bird',
1038 | 'cat',
1039 | 'dog',
1040 | 'horse',
1041 | 'sheep',
1042 | 'cow',
1043 | 'elephant',
1044 | 'bear',
1045 | 'zebra',
1046 | 'giraffe',
1047 | 'n/a',
1048 | 'backpack',
1049 | 'umbrella',
1050 | 'n/a',
1051 | 'n/a',
1052 | 'handbag',
1053 | 'tie',
1054 | 'suitcase',
1055 | 'frisbee',
1056 | 'skis',
1057 | 'snowboard',
1058 | 'sports ball',
1059 | 'kite',
1060 | 'baseball bat',
1061 | 'baseball glove',
1062 | 'skateboard',
1063 | 'surfboard',
1064 | 'tennis racket',
1065 | 'bottle',
1066 | 'n/a',
1067 | 'wine glass',
1068 | 'cup',
1069 | 'fork',
1070 | 'knife',
1071 | 'spoon',
1072 | 'bowl',
1073 | 'banana',
1074 | 'apple',
1075 | 'sandwich',
1076 | 'orange',
1077 | 'broccoli',
1078 | 'carrot',
1079 | 'hot dog',
1080 | 'pizza',
1081 | 'donut',
1082 | 'cake',
1083 | 'chair',
1084 | 'couch',
1085 | 'potted plant',
1086 | 'bed',
1087 | 'n/a',
1088 | 'dining table',
1089 | 'n/a',
1090 | 'n/a',
1091 | 'toilet',
1092 | 'n/a',
1093 | 'tv',
1094 | 'laptop',
1095 | 'mouse',
1096 | 'remote',
1097 | 'keyboard',
1098 | 'cell phone',
1099 | 'microwave',
1100 | 'oven',
1101 | 'toaster',
1102 | 'sink',
1103 | 'refrigerator',
1104 | 'n/a',
1105 | 'book',
1106 | 'clock',
1107 | 'vase',
1108 | 'scissors',
1109 | 'teddy bear',
1110 | 'hair drier',
1111 | 'toothbrush',
1112 | ];
1113 |
1114 | const TFLITE_MODELS = {
1115 | 'mobilenet_cpu': {
1116 | 'url': '/models/mobilenet_v1_1.0_224_quant.tflite',
1117 | 'type': 'classification',
1118 | 'device': 'cpu',
1119 | 'labels': IMAGENET_LABELS,
1120 | },
1121 | 'mobilenet_tpu': {
1122 | 'url': '/models/mobilenet_v1_1.0_224_quant_edgetpu.tflite',
1123 | 'type': 'classification',
1124 | 'device': 'tpu',
1125 | 'labels': IMAGENET_LABELS,
1126 | },
1127 | 'ssd_mobilenet_coco_cpu': {
1128 | 'url': '/models/ssd_mobilenet_v2_coco_quant_postprocess.tflite',
1129 | 'type': 'detection',
1130 | 'device': 'cpu',
1131 | 'labels': COCO_LABELS,
1132 | },
1133 | 'ssd_mobilenet_coco_tpu': {
1134 | 'url': '/models/ssd_mobilenet_v2_coco_quant_postprocess_edgetpu.tflite',
1135 | 'type': 'detection',
1136 | 'device': 'tpu',
1137 | 'labels': COCO_LABELS,
1138 | },
1139 | 'ssd_mobilenet_face_cpu': {
1140 | 'url': '/models/ssd_mobilenet_v2_face_quant_postprocess.tflite',
1141 | 'type': 'detection',
1142 | 'device': 'cpu',
1143 | 'labels': ['face'],
1144 | },
1145 | 'ssd_mobilenet_face_tpu': {
1146 | 'url': '/models/ssd_mobilenet_v2_face_quant_postprocess_edgetpu.tflite',
1147 | 'type': 'detection',
1148 | 'device': 'tpu',
1149 | 'labels': ['face'],
1150 | },
1151 | };
1152 |
--------------------------------------------------------------------------------