├── 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 | ![WebCoral Detection Demo](https://user-images.githubusercontent.com/716798/117263928-999adf80-ae07-11eb-90e2-23d692426cfb.gif) 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 | --------------------------------------------------------------------------------