diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 0000000..4b6138b --- /dev/null +++ b/.bazelrc @@ -0,0 +1,13 @@ +common --experimental_repo_remote_exec + +build --subcommands +build --verbose_failures +build --enable_platform_specific_config + +build:linux --crosstool_top=@crosstool//:toolchains +build:linux --compiler=gcc + +build:macos --cxxopt=-std=c++14 + +build:windows --incompatible_restrict_string_escapes=false +build:windows --cxxopt=/std:c++latest diff --git a/CHANGES.md b/CHANGES.md index 76d190d..b144e14 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,38 @@ -# pycoral +# PyCoral changelog + + +## 2.0 (Grouper release) + +* Improved error reporting for `PipelinedModelRunner`. It now prints error +messages originating from the TensorFlow Lite runtime. + +**Changed APIs (code-breaking):** + +* [`PipelinedModelRunner.push()`](https://coral.ai/docs/reference/py/pycoral.pipeline/#pycoral.pipeline.pipelined_model_runner.PipelinedModelRunner.push) +now requires a dictionary for the `input_tensors` (instead of a list), so that +each input tensor provides a corresponding tensor name as the dictionary key. +This method is also now void instead of returning a bool; it will raise +`RuntimeError` if the push fails. + +* Similarly, +[`PipelinedModelRunner.pop()`](https://coral.ai/docs/reference/py/pycoral.pipeline/#pycoral.pipeline.pipelined_model_runner.PipelinedModelRunner.pop) +now returns a dictionary instead of a list, and also may raise `RuntimeError`. + + +**Updated APIs:** + +* [`make_interpreter()`](https://coral.ai/docs/reference/py/pycoral.utils/#pycoral.utils.edgetpu.make_interpreter) +now accepts an optional `delegate` argument to specify the Edge TPU delegate +object you want to use. +* [`get_objects()`](https://coral.ai/docs/reference/py/pycoral.adapters/#pycoral.adapters.detect.get_objects) +now supports SSD models with different orders in the output tensor. + + +**New APIs:** + +* `utils.edgetpu.set_verbosity()` prints logs related to each Edge TPU. + + +## 1.0 (Frogfish release) * Initial pycoral release diff --git a/Makefile b/Makefile index 6902a83..f2bfbc7 100644 --- a/Makefile +++ b/Makefile @@ -12,11 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. SHELL := /bin/bash -PYTHON ?= python3 +PYTHON ?= $(shell which python3) MAKEFILE_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))) PY3_VER ?= $(shell $(PYTHON) -c "import sys;print('%d%d' % sys.version_info[:2])") OS := $(shell uname -s) +ifeq ($(PYTHON),) +$(error PYTHON must be set) +endif + # Allowed CPU values: k8, armv7a, aarch64, darwin ifeq ($(OS),Linux) CPU ?= k8 @@ -36,19 +40,9 @@ $(error COMPILATION_MODE must be opt, dbg or fastbuild) endif BAZEL_OUT_DIR := $(MAKEFILE_DIR)/bazel-out/$(CPU)-$(COMPILATION_MODE)/bin -COMMON_BAZEL_BUILD_FLAGS_Linux := --crosstool_top=@crosstool//:toolchains \ - --compiler=gcc -COMMON_BAZEL_BUILD_FLAGS_Darwin := COMMON_BAZEL_BUILD_FLAGS := --compilation_mode=$(COMPILATION_MODE) \ --copt=-DNPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION \ - --verbose_failures \ - --sandbox_debug \ - --subcommands \ - --define PY3_VER=$(PY3_VER) \ - --action_env PYTHON_BIN_PATH=$(shell which $(PYTHON)) \ - --cpu=$(CPU) \ - --experimental_repo_remote_exec \ - $(COMMON_BAZEL_BUILD_FLAGS_$(OS)) + --cpu=$(CPU) BAZEL_BUILD_FLAGS_Linux := --linkopt=-L$(MAKEFILE_DIR)/libedgetpu_bin/direct/$(CPU) \ --linkopt=-l:libedgetpu.so.1 @@ -95,15 +89,14 @@ CORAL_WRAPPER_OUT_DIR := $(MAKEFILE_DIR)/pycoral/pybind TFLITE_WRAPPER_OUT_DIR := $(MAKEFILE_DIR)/tflite_runtime TENSORFLOW_DIR = $(shell bazel info output_base)/external/org_tensorflow/ +TENSORFLOW_VERSION_SUFFIX := .post1 TENSORFLOW_VERSION = $(shell bazel aquery $(TFLITE_BAZEL_BUILD_FLAGS) $(TFLITE_WRAPPER_TARGET) >> /dev/null && \ - grep "_VERSION = " "${TENSORFLOW_DIR}/tensorflow/tools/pip_package/setup.py" | cut -d= -f2 | sed "s/[ '-]//g") -TENSORFLOW_COMMIT = $(shell grep "TENSORFLOW_COMMIT =" $(MAKEFILE_DIR)/WORKSPACE | grep -o '[0-9a-f]\{40\}') + grep "_VERSION = " "${TENSORFLOW_DIR}/tensorflow/tools/pip_package/setup.py" | cut -d= -f2 | sed "s/[ '-]//g")$(TENSORFLOW_VERSION_SUFFIX) +TENSORFLOW_COMMIT = $(shell bazel query "@libedgetpu_properties//..." | grep tensorflow_commit | cut -d\# -f2) TFLITE_RUNTIME_VERSION = $(TENSORFLOW_VERSION) TFLITE_RUNTIME_DIR := /tmp/tflite_runtime_root -EDGETPU_RUNTIME_DIR := /tmp/edgetpu_runtime - # $(1): Package version # $(2): Wrapper files define prepare_tflite_runtime @@ -113,12 +106,9 @@ echo "__version__ = '$(1)'" \ echo "__git_version__ = '$(TENSORFLOW_COMMIT)'" \ >> $(TFLITE_RUNTIME_DIR)/tflite_runtime/__init__.py cp $(MAKEFILE_DIR)/tflite_runtime/$(2) \ - $(TENSORFLOW_DIR)/tensorflow/lite/python/interpreter.py \ + $(TENSORFLOW_DIR)/tensorflow/lite/python/{interpreter.py,metrics_interface.py,metrics_portable.py} \ $(TFLITE_RUNTIME_DIR)/tflite_runtime/ -sed -e '/include_package_data=True/a\'$$'\n''\ has_ext_modules = lambda: True,' \ - -e '/pybind11/d' \ - -e 's/numpy >= 1.16.0/numpy >= 1.12.1/' \ - $(TENSORFLOW_DIR)/tensorflow/lite/tools/pip_package/setup_with_bazel.py \ +sed -e "s/'numpy.*/'numpy>=1.16.0',/" $(TENSORFLOW_DIR)/tensorflow/lite/tools/pip_package/setup_with_binary.py \ > $(TFLITE_RUNTIME_DIR)/setup.py endef @@ -152,7 +142,7 @@ endef all: pybind tflite pybind: - bazel build $(BAZEL_BUILD_FLAGS) \ + PYTHON_BIN_PATH=$(PYTHON) bazel build $(BAZEL_BUILD_FLAGS) \ --embed_label='TENSORFLOW_COMMIT=$(TENSORFLOW_COMMIT)' \ --stamp \ //src:_pywrap_coral @@ -161,12 +151,13 @@ pybind: cp -f $(BAZEL_OUT_DIR)/src/_pywrap_coral.so $(CORAL_WRAPPER_OUT_DIR)/$(CORAL_WRAPPER_NAME) tflite: - bazel build $(TFLITE_BAZEL_BUILD_FLAGS) $(TFLITE_WRAPPER_TARGET) + PYTHON_BIN_PATH=$(PYTHON) bazel build $(TFLITE_BAZEL_BUILD_FLAGS) $(TFLITE_WRAPPER_TARGET) mkdir -p $(TFLITE_WRAPPER_OUT_DIR) cp -f $(BAZEL_OUT_DIR)/external/org_tensorflow/tensorflow/lite/python/interpreter_wrapper/_pywrap_tensorflow_interpreter_wrapper.so \ $(TFLITE_WRAPPER_OUT_DIR)/$(TFLITE_WRAPPER_NAME) - cp -f $(TENSORFLOW_DIR)/tensorflow/lite/python/interpreter.py \ + cp -f $(TENSORFLOW_DIR)/tensorflow/lite/python/{interpreter.py,metrics_interface.py,metrics_portable.py} \ $(TFLITE_WRAPPER_OUT_DIR) + touch $(TFLITE_WRAPPER_OUT_DIR)/__init__.py clean: rm -rf $(MAKEFILE_DIR)/bazel-* \ @@ -219,18 +210,7 @@ tflite-deb: $(MAKEFILE_DIR)/dist runtime: - rm -rf $(EDGETPU_RUNTIME_DIR) && mkdir -p $(EDGETPU_RUNTIME_DIR)/{libedgetpu,third_party} - cp -r $(MAKEFILE_DIR)/libedgetpu_bin/{direct,throttled,LICENSE.txt,*.h,*.rules} \ - $(EDGETPU_RUNTIME_DIR)/libedgetpu - cp -r $(MAKEFILE_DIR)/libcoral/third_party/{coral_accelerator_windows,libusb_win,usbdk} \ - $(EDGETPU_RUNTIME_DIR)/third_party - cp -r $(MAKEFILE_DIR)/scripts/runtime/{install.sh,uninstall.sh} \ - $(MAKEFILE_DIR)/scripts/windows/{install.bat,uninstall.bat} \ - $(EDGETPU_RUNTIME_DIR) - mkdir -p $(MAKEFILE_DIR)/dist - (cd $(shell dirname $(EDGETPU_RUNTIME_DIR)) && \ - zip -r $(MAKEFILE_DIR)/dist/edgetpu_runtime_$(shell date '+%Y%m%d').zip \ - $(shell basename $(EDGETPU_RUNTIME_DIR))) + make LIBEDGETPU_BIN=$(MAKEFILE_DIR)/libedgetpu_bin DIST_DIR=$(MAKEFILE_DIR)/dist -C $(MAKEFILE_DIR)/libedgetpu runtime help: @echo "make all - Build all native code" diff --git a/README.md b/README.md index afae1de..06657e8 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ # PyCoral API -This repository contains an easy-to-use Python API to run inferences and -perform on-device transfer learning with TensorFlow Lite models on +This repository contains an easy-to-use Python API that helps you run inferences +and perform on-device transfer learning with TensorFlow Lite models on [Coral devices](https://coral.ai/products/). -You can install this library with the Python wheels listed at +To install the prebuilt PyCoral library, see the instructions at [coral.ai/software/](https://coral.ai/software/#pycoral-api). +**Note:** If you're on a Debian system, be sure to install this library from +apt-get and not from pip. Using `pip install` is not guaranteed compatible with +the other Coral libraries that you must install from apt-get. For details, see +[coral.ai/software/](https://coral.ai/software/#debian-packages). + ## Documentation and examples To learn more about how to use the PyCoral API, see our guide to [Run inference @@ -20,7 +25,17 @@ https://github.com/google-coral/pycoral/tree/master/examples#pycoral-api-example ## Compilation -To build the library yourself, follow these steps: +When building this library yourself, it's critical that you have +version-matching builds of +[libcoral](https://github.com/google-coral/libcoral/tree/master) and +[libedgetpu](https://github.com/google-coral/libedgetpu/tree/master)—notice +these are submodules of the pycoral repo, and they all share the same +`TENSORFLOW_COMMIT` value. So just be sure if you change one, you must change +them all. + +For complete details about how to build all these libraries, read +[Build Coral for your platform](https://coral.ai/docs/notes/build-coral/). +Or to build just this library, follow these steps: 1. Clone this repo and include submodules: diff --git a/WORKSPACE b/WORKSPACE index 9b43de1..4b44eb2 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -15,35 +15,29 @@ workspace(name = "pycoral") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -TENSORFLOW_COMMIT = "48c3bae94a8b324525b45f157d638dfd4e8c3be1" -# Command to calculate: curl -L | sha256sum | awk '{print $1}' -TENSORFLOW_SHA256 = "363420a67b4cfa271cd21e5c8fac0d7d91b18b02180671c3f943c887122499d8" - -# These values come from the Tensorflow workspace. If the TF commit is updated, -# these should be updated to match. -IO_BAZEL_RULES_CLOSURE_COMMIT = "308b05b2419edb5c8ee0471b67a40403df940149" -IO_BAZEL_RULES_CLOSURE_SHA256 = "5b00383d08dd71f28503736db0500b6fb4dda47489ff5fc6bed42557c07c6ba9" - -CORAL_CROSSTOOL_COMMIT = "142e930ac6bf1295ff3ba7ba2b5b6324dfb42839" -CORAL_CROSSTOOL_SHA256 = "088ef98b19a45d7224be13636487e3af57b1564880b67df7be8b3b7eee4a1bfc" - # Configure libedgetpu and downstream libraries (TF and Crosstool). -new_local_repository( +local_repository( name = "libedgetpu", path = "libedgetpu", - build_file = "libedgetpu/BUILD" ) load("@libedgetpu//:workspace.bzl", "libedgetpu_dependencies") -libedgetpu_dependencies(TENSORFLOW_COMMIT, TENSORFLOW_SHA256, - IO_BAZEL_RULES_CLOSURE_COMMIT,IO_BAZEL_RULES_CLOSURE_SHA256, - CORAL_CROSSTOOL_COMMIT,CORAL_CROSSTOOL_SHA256) +libedgetpu_dependencies() + +load("@org_tensorflow//tensorflow:workspace3.bzl", "tf_workspace3") +tf_workspace3() -load("@org_tensorflow//tensorflow:workspace.bzl", "tf_workspace") -tf_workspace(tf_repo_name = "org_tensorflow") +load("@org_tensorflow//tensorflow:workspace2.bzl", "tf_workspace2") +tf_workspace2() + +load("@org_tensorflow//tensorflow:workspace1.bzl", "tf_workspace1") +tf_workspace1() + +load("@org_tensorflow//tensorflow:workspace0.bzl", "tf_workspace0") +tf_workspace0() load("@coral_crosstool//:configure.bzl", "cc_crosstool") -cc_crosstool(name = "crosstool") +cc_crosstool(name = "crosstool", cpp_version = "c++14") http_archive( name = "com_google_glog", @@ -60,10 +54,9 @@ glog_library(with_gflags=0) """, ) -new_local_repository( +local_repository( name = "libcoral", path = "libcoral", - build_file_content = "" ) new_local_repository( @@ -74,28 +67,3 @@ new_local_repository( load("@org_tensorflow//third_party/py:python_configure.bzl", "python_configure") python_configure(name = "local_config_python") -new_local_repository( - name = "python_linux", - path = "/usr/include", - build_file = "third_party/python/linux/BUILD", -) - -new_local_repository( - name = "python_windows", - path = "third_party/python/windows", - build_file = "third_party/python/windows/BUILD", -) - -# Use Python from MacPorts. -new_local_repository( - name = "python_darwin", - path = "/opt/local/Library/Frameworks/Python.framework/Versions", - build_file = "third_party/python/darwin/BUILD", -) - -new_local_repository( - name = "python", - path = "third_party/python", - build_file = "third_party/python/BUILD", -) - diff --git a/benchmarks/test_utils.py b/benchmarks/benchmark_utils.py similarity index 98% rename from benchmarks/test_utils.py rename to benchmarks/benchmark_utils.py index 40a1139..91957cd 100644 --- a/benchmarks/test_utils.py +++ b/benchmarks/benchmark_utils.py @@ -57,10 +57,11 @@ def machine_info(): machine = 'rp3b' elif 'Raspberry Pi 3 Model B Plus Rev' in board_info: machine = 'rp3b+' - elif 'Raspberry Pi 4 Model B Rev 1.1' in board_info: + elif 'Raspberry Pi 4 Model B Rev' in board_info: machine = 'rp4b' else: machine = 'unknown' + print('[WARMING] unknown board: ', board_info) return machine diff --git a/benchmarks/imprinting_benchmarks.py b/benchmarks/imprinting_benchmarks.py index 2a2949f..c413e78 100644 --- a/benchmarks/imprinting_benchmarks.py +++ b/benchmarks/imprinting_benchmarks.py @@ -15,31 +15,34 @@ """Benchmarks imprinting training time on small data set.""" import collections +import sys import time import numpy as np -from benchmarks import test_utils +from benchmarks import benchmark_utils from pycoral.adapters import classify from pycoral.adapters import common -from pycoral.learn.imprinting.engine import ImprintingEngine -from pycoral.utils.edgetpu import make_interpreter +from pycoral.learn.imprinting import engine +from pycoral.utils import edgetpu -def run_benchmark(model): +def run_benchmark(model, delegate): """Measures training time for given model with random data. Args: model: string, file name of the input model. + delegate: Edge TPU delegate. Returns: float, training time in ms. """ - engine = ImprintingEngine( - test_utils.test_data_path(model), keep_classes=False) + imprinting_engine = engine.ImprintingEngine( + benchmark_utils.test_data_path(model), keep_classes=False) - extractor = make_interpreter(engine.serialize_extractor_model()) + extractor = edgetpu.make_interpreter( + imprinting_engine.serialize_extractor_model(), delegate=delegate) extractor.allocate_tensors() width, height = common.input_size(extractor) @@ -58,9 +61,9 @@ def run_benchmark(model): for tensor in tensors: common.set_input(extractor, tensor) extractor.invoke() - engine.train(classify.get_scores(extractor), class_id=class_id) + imprinting_engine.train(classify.get_scores(extractor), class_id=class_id) - engine.serialize_model() + imprinting_engine.serialize_model() training_time = (time.perf_counter() - start) * 1000 @@ -70,18 +73,21 @@ def run_benchmark(model): def main(): - args = test_utils.parse_args() - machine = test_utils.machine_info() - models, reference = test_utils.read_reference( + print('Python version: ', sys.version) + args = benchmark_utils.parse_args() + machine = benchmark_utils.machine_info() + benchmark_utils.check_cpu_scaling_governor_status() + models, reference = benchmark_utils.read_reference( 'imprinting_reference_training_%s.csv' % machine) results = [('MODEL', 'DATA_SET', 'TRAINING_TIME')] + delegate = edgetpu.load_edgetpu_delegate() for i, name in enumerate(models, start=1): print('---------------- %d / %d ----------------' % (i, len(models))) - results.append((name, 'random', run_benchmark(name))) - test_utils.save_as_csv( + results.append((name, 'random', run_benchmark(name, delegate))) + benchmark_utils.save_as_csv( 'imprinting_benchmarks_training_%s_%s.csv' % (machine, time.strftime('%Y%m%d-%H%M%S')), results) - test_utils.check_result(reference, results, args.enable_assertion) + benchmark_utils.check_result(reference, results, args.enable_assertion) if __name__ == '__main__': diff --git a/benchmarks/inference_benchmarks.py b/benchmarks/inference_benchmarks.py index 08c1dbe..ebe33d9 100644 --- a/benchmarks/inference_benchmarks.py +++ b/benchmarks/inference_benchmarks.py @@ -25,21 +25,23 @@ - 'aarch64': Edge TPU dev board. """ +import sys import time import timeit import numpy as np -from benchmarks import test_utils -from pycoral.utils.edgetpu import make_interpreter +from benchmarks import benchmark_utils +from pycoral.utils import edgetpu -def run_benchmark(model): - """Returns average inference time in ms on specified model on random input.""" +def run_benchmark(model, delegate): + """Returns average inference time in ms on specified model with random input.""" print('Benchmark for [%s]' % model) - print('model path = %s' % test_utils.test_data_path(model)) - interpreter = make_interpreter(test_utils.test_data_path(model)) + print('model path = %s' % benchmark_utils.test_data_path(model)) + interpreter = edgetpu.make_interpreter( + benchmark_utils.test_data_path(model), delegate=delegate) interpreter.allocate_tensors() iterations = 200 if 'edgetpu' in model else 20 @@ -55,20 +57,22 @@ def run_benchmark(model): def main(): - args = test_utils.parse_args() - machine = test_utils.machine_info() - test_utils.check_cpu_scaling_governor_status() - models, reference = test_utils.read_reference( + print('Python version: ', sys.version) + args = benchmark_utils.parse_args() + machine = benchmark_utils.machine_info() + benchmark_utils.check_cpu_scaling_governor_status() + models, reference = benchmark_utils.read_reference( 'inference_reference_%s.csv' % machine) results = [('MODEL', 'INFERENCE_TIME')] + delegate = edgetpu.load_edgetpu_delegate() for i, model in enumerate(models, start=1): print('-------------- Model %d / %d ---------------' % (i, len(models))) - results.append((model, run_benchmark(model))) - test_utils.save_as_csv( + results.append((model, run_benchmark(model, delegate))) + benchmark_utils.save_as_csv( 'inference_benchmarks_%s_%s.csv' % (machine, time.strftime('%Y%m%d-%H%M%S')), results) - test_utils.check_result(reference, results, args.enable_assertion) + benchmark_utils.check_result(reference, results, args.enable_assertion) if __name__ == '__main__': diff --git a/benchmarks/multiple_tpus_performance_analysis.py b/benchmarks/multiple_tpus_performance_analysis.py index cc784a7..2afe070 100644 --- a/benchmarks/multiple_tpus_performance_analysis.py +++ b/benchmarks/multiple_tpus_performance_analysis.py @@ -31,21 +31,21 @@ """ import logging +import sys import threading import time from PIL import Image -from benchmarks import test_utils +from benchmarks import benchmark_utils from pycoral.adapters import classify from pycoral.adapters import common from pycoral.adapters import detect -from pycoral.utils.edgetpu import list_edge_tpus -from pycoral.utils.edgetpu import make_interpreter +from pycoral.utils import edgetpu def run_inference_job(model_name, input_filename, num_inferences, num_threads, - task_type, devices): + task_type, delegates): """Runs classification or detection job with `num_threads`. Args: @@ -54,28 +54,29 @@ def run_inference_job(model_name, input_filename, num_inferences, num_threads, num_inferences: int num_threads: int task_type: string, `classification` or `detection` - devices: list of string, all aviable edgtpu devices. + delegates: list of Delegate objects, all aviable edgtpu devices. Returns: double, wall time (in seconds) for running the job. """ - def thread_job(model_name, input_filename, num_inferences, task_type, device): + def thread_job(model_name, input_filename, num_inferences, task_type, + delegate): """Runs classification or detection job on one Python thread.""" tid = threading.get_ident() logging.info('Thread: %d, # inferences: %d, model: %s', tid, num_inferences, model_name) - interpreter = make_interpreter( - test_utils.test_data_path(model_name), device) + interpreter = edgetpu.make_interpreter( + benchmark_utils.test_data_path(model_name), delegate=delegate) interpreter.allocate_tensors() - with test_utils.test_image(input_filename) as img: + with benchmark_utils.test_image(input_filename) as img: if task_type == 'classification': resize_image = img.resize(common.input_size(interpreter), Image.NEAREST) common.set_input(interpreter, resize_image) elif task_type == 'detection': - common.set_resized_input( - interpreter, img.size, lambda size: img.resize(size, Image.NEAREST)) + common.set_resized_input(interpreter, img.size, + lambda size: img.resize(size, Image.NEAREST)) else: raise ValueError( 'task_type should be classification or detection, but is given %s' % @@ -97,7 +98,7 @@ def thread_job(model_name, input_filename, num_inferences, task_type, device): threading.Thread( target=thread_job, args=(model_name, input_filename, num_inferences_per_thread, - task_type, devices[i]))) + task_type, delegates[i]))) for worker in workers: worker.start() @@ -109,12 +110,13 @@ def thread_job(model_name, input_filename, num_inferences, task_type, device): def main(): num_inferences = 30000 input_filename = 'cat.bmp' - all_tpus = list_edge_tpus() + all_tpus = edgetpu.list_edge_tpus() num_devices = len(all_tpus) num_pci_devices = sum(1 for device in all_tpus if device['type'] == 'pci') devices = ['pci:%d' % i for i in range(min(num_devices, num_pci_devices))] + [ 'usb:%d' % i for i in range(max(0, num_devices - num_pci_devices)) ] + delegates = [edgetpu.load_edgetpu_delegate({'device': d}) for d in devices] model_names = [ 'mobilenet_v1_1.0_224_quant_edgetpu.tflite', 'mobilenet_v2_1.0_224_quant_edgetpu.tflite', @@ -140,7 +142,7 @@ def show_speedup(inference_costs): inference_costs_map[model_name] = [0.0] * num_devices for num_threads in range(num_devices, 0, -1): cost = run_inference_job(model_name, input_filename, num_inferences, - num_threads, task_type, devices) + num_threads, task_type, delegates) inference_costs_map[model_name][num_threads - 1] = cost logging.info('model: %s, # threads: %d, cost: %f seconds', model_name, num_threads, cost) @@ -155,6 +157,8 @@ def show_speedup(inference_costs): if __name__ == '__main__': + print('Python version: ', sys.version) + benchmark_utils.check_cpu_scaling_governor_status() logging.basicConfig( format=( '%(asctime)s.%(msecs)03d p%(process)s {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s' diff --git a/benchmarks/online_imprinting_benchmarks.py b/benchmarks/online_imprinting_benchmarks.py index 32f2aa9..13a3227 100644 --- a/benchmarks/online_imprinting_benchmarks.py +++ b/benchmarks/online_imprinting_benchmarks.py @@ -15,32 +15,34 @@ """Benchmarks imprinting inference time under online training mode on small data set.""" import collections +import sys import time import numpy as np -import tflite_runtime.interpreter as tflite -from benchmarks import test_utils +from benchmarks import benchmark_utils from pycoral.adapters import classify from pycoral.adapters import common -from pycoral.learn.imprinting.engine import ImprintingEngine -from pycoral.utils.edgetpu import load_edgetpu_delegate -from pycoral.utils.edgetpu import make_interpreter +from pycoral.learn.imprinting import engine +from pycoral.utils import edgetpu +import tflite_runtime.interpreter as tflite -def run_benchmark(model): +def run_benchmark(model, delegate): """Measures training time for given model with random data. Args: model: string, file name of the input model. + delegate: Edge TPU delegate. Returns: float, training time in ms. """ - engine = ImprintingEngine( - test_utils.test_data_path(model), keep_classes=False) + imprinting_engine = engine.ImprintingEngine( + benchmark_utils.test_data_path(model), keep_classes=False) - extractor = make_interpreter(engine.serialize_extractor_model(), device=':0') + extractor = edgetpu.make_interpreter( + imprinting_engine.serialize_extractor_model(), delegate=delegate) extractor.allocate_tensors() width, height = common.input_size(extractor) @@ -53,18 +55,16 @@ def run_benchmark(model): data_by_category[i].append( np.random.randint(0, 256, (height, width, 3), dtype=np.uint8)) - delegate = load_edgetpu_delegate({'device': ':0'}) - inference_time = 0. for class_id, tensors in enumerate(data_by_category.values()): for tensor in tensors: common.set_input(extractor, tensor) extractor.invoke() - engine.train(classify.get_scores(extractor), class_id=class_id) + imprinting_engine.train(classify.get_scores(extractor), class_id=class_id) start = time.perf_counter() interpreter = tflite.Interpreter( - model_content=engine.serialize_model(), + model_content=imprinting_engine.serialize_model(), experimental_delegates=[delegate]) interpreter.allocate_tensors() common.set_input(interpreter, tensors[0]) @@ -78,18 +78,21 @@ def run_benchmark(model): def main(): - args = test_utils.parse_args() - machine = test_utils.machine_info() - models, reference = test_utils.read_reference( + print('Python version: ', sys.version) + args = benchmark_utils.parse_args() + machine = benchmark_utils.machine_info() + benchmark_utils.check_cpu_scaling_governor_status() + models, reference = benchmark_utils.read_reference( 'imprinting_reference_inference_%s.csv' % machine) results = [('MODEL', 'DATA_SET', 'INFERENCE_TIME')] + delegate = edgetpu.load_edgetpu_delegate() for i, name in enumerate(models, start=1): print('---------------- %d / %d ----------------' % (i, len(models))) - results.append((name, 'random', run_benchmark(name))) - test_utils.save_as_csv( + results.append((name, 'random', run_benchmark(name, delegate))) + benchmark_utils.save_as_csv( 'imprinting_benchmarks_inference_%s_%s.csv' % (machine, time.strftime('%Y%m%d-%H%M%S')), results) - test_utils.check_result(reference, results, args.enable_assertion) + benchmark_utils.check_result(reference, results, args.enable_assertion) if __name__ == '__main__': diff --git a/benchmarks/softmax_regression_benchmarks.py b/benchmarks/softmax_regression_benchmarks.py index e5e7817..04da899 100644 --- a/benchmarks/softmax_regression_benchmarks.py +++ b/benchmarks/softmax_regression_benchmarks.py @@ -14,10 +14,11 @@ # limitations under the License. """Benchmark backprop on small fake data set.""" +import sys import time import numpy as np -from benchmarks import test_utils +from benchmarks import benchmark_utils from pycoral.learn.backprop.softmax_regression import SoftmaxRegression @@ -66,7 +67,9 @@ def _benchmark_for_training(num_classes, feature_dim): def main(): - machine = test_utils.machine_info() + print('Python version: ', sys.version) + machine = benchmark_utils.machine_info() + benchmark_utils.check_cpu_scaling_governor_status() # cases are defined by parameter pairs [num_classes, feature_dim]. cases = [[4, 256], [16, 256], [4, 1024], [16, 1024]] results = [('CASE', 'TRAINING_TIME(s)')] @@ -77,7 +80,7 @@ def main(): (num_classes, feature_dim)) results.append((':'.join(str(i) for i in params), _benchmark_for_training(num_classes, feature_dim))) - test_utils.save_as_csv( + benchmark_utils.save_as_csv( 'softmax_regression_benchmarks_%s_%s.csv' % (machine, time.strftime('%Y%m%d-%H%M%S')), results) diff --git a/debian/changelog b/debian/changelog index 204c2b5..4e53e83 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,8 @@ -coral (1.0.1) stable; urgency=medium +coral (2.0.0) stable; urgency=medium - * pycoral initial release update + * pycoral grouper release - -- Coral Thu, 04 Feb 2021 14:37:49 -0800 + -- Coral Mon, 28 Jun 2021 14:20:28 -0700 coral (1.0) stable; urgency=medium diff --git a/debian/control b/debian/control index 63bee58..de20e52 100644 --- a/debian/control +++ b/debian/control @@ -8,7 +8,7 @@ Homepage: https://coral.ai/ Package: pycoral-examples Architecture: all -Depends: python3-coral (= ${binary:Version}), +Depends: python3-pycoral (= ${binary:Version}), ${misc:Depends} Description: Example code for Coral Python API Python examples to demonstrate how to use the Coral Python API @@ -16,12 +16,12 @@ Description: Example code for Coral Python API Package: python3-pycoral Architecture: any Depends: libc6, - libedgetpu1-std (= 15.0) | libedgetpu1 (= 15.0), + libedgetpu1-std (= 16.0) | libedgetpu1 (= 16.0), libgcc1, libstdc++6, python3-numpy, python3-pil, - python3-tflite-runtime (= 2.5.0), + python3-tflite-runtime (= 2.5.0.post1), ${misc:Depends}, ${python3:Depends} Description: Coral Python API diff --git a/docs/README.md b/docs/README.md index eb86175..1d3307d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,27 +6,40 @@ reference with Sphinx. You can build the reference docs as follows: ``` -# To ensure consistent results, use a Python virtual environment: -python3 -m venv ~/.my_venvs/coraldocs -source ~/.my_venvs/coraldocs/bin/activate - -# Navigate to the pycoral/ dir and build the pybind APIs: -cd pycoral -bash scripts/build.sh -# OR, build for only the python version and architecture you need. Eg: -# DOCKER_CPUS=k8 scripts/build.sh --python_versions 38 - -# Navigate to the pycoral/docs/ dir and install the doc dependencies: +# Start in the pycoral repo root directory... +# First, be sure you sync the required submodules (needed to build pycoral): +git submodule init && git submodule update + +# Build the "pywrap" lib for the python version and architecture you need. Eg: +DOCKER_CPUS=k8 scripts/build.sh --python_versions 39 + +# To ensure compatibility with GLIBC version used in the above library build, +# we should also build the docs in Docker. So open a matching docker shell +# using the same DOCKER_IMAGE that corresponds to the python_version used above +# (see pycoral/scripts/build.sh): +DOCKER_IMAGE=ubuntu:21.04 make docker-shell + +# Inside the Docker shell, install libedgetpu: +echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" | sudo tee /etc/apt/sources.list.d/coral-edgetpu.list +curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add - +sudo apt-get update +sudo apt-get install libedgetpu1-std + +# Now go into the docs dir and install Python libs: cd docs -pip install -r requirements.txt - -# Build the docs: -bash makedocs.sh +sudo apt install python3-pip -y +python3 -m pip install -r requirements.txt +# Look for warnings about libraries installed in location outside your PATH. +# Add them like this: +export PATH=$PATH:/home/yourname/.local/bin + +# Finally, build the docs: +bash makedocs.sh -p ``` The results are output in `_build/`. The `_build/preview/` files are for local viewing--just open the `index.html` page. The `_build/web/` files are designed -for publishing on the Coral website. +for publishing on the Coral website (use the `-w` flag). For more information about the syntax in these RST files, see the [reStructuredText documentation](http://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html). diff --git a/docs/conf.py b/docs/conf.py index 7e1d9e7..ab6198d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -27,6 +27,7 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # +import inspect import os import sys import unittest.mock @@ -36,9 +37,7 @@ # Mock modules not needed for docs sys.modules.update([('tflite_runtime', unittest.mock.MagicMock())]) sys.modules.update([('tflite_runtime.interpreter', unittest.mock.MagicMock())]) -sys.modules.update([('pycoral.pybind', unittest.mock.MagicMock())]) -sys.modules.update([('pycoral.pybind._pywrap_coral', - unittest.mock.MagicMock())]) + # -- Project information ----------------------------------------------------- @@ -47,7 +46,7 @@ author = 'Google LLC' # The short X.Y version -version = '1.0' +version = '2.0' # The full version, including alpha/beta/rc tags release = '' @@ -65,6 +64,7 @@ 'sphinx.ext.intersphinx', # Enables linking to other libs like Pillow 'sphinx.ext.coverage', 'sphinx.ext.napoleon', # Converts Google-style code comments to RST + 'sphinx.ext.linkcode', # Creates links to each API source code ] # Autodoc configurations @@ -120,3 +120,32 @@ html_theme_path = ['.'] html_file_suffix = '.md' html_link_suffix = '/' + + +# Callback from sphinx.ext.linkcode to resolve links to source code +def linkcode_resolve(domain, info): + if domain != 'py': + return None + if not info['module']: + return None + + filename = info['module'].replace('.', '/') + linespec = '' + + try: + obj = sys.modules[info['module']] + for part in info['fullname'].split('.'): + obj = getattr(obj, part) + source, lineno = inspect.getsourcelines(obj) + linespec = '#L%d-L%d' % (lineno, lineno + len(source) - 1) + except TypeError: + # inspect.getsourcelines() fails this way for properties + # But let's not link them anyway because all the links look sloppy + return None + except OSError: + # inspect.getsourcelines() fails this way for namedtuples + # So let's keep the link but they won't get line numbers + pass + + return 'https://github.com/google-coral/pycoral/blob/master/%s.py%s' % ( + filename, linespec) diff --git a/docs/coral_theme/layout.html b/docs/coral_theme/layout.html index 5e74d1b..e810c5c 100644 --- a/docs/coral_theme/layout.html +++ b/docs/coral_theme/layout.html @@ -4,4 +4,7 @@
{% block body %}{% endblock %} -
\ No newline at end of file +
+

API version {{ version }}

+
+ diff --git a/docs/makedocs.sh b/docs/makedocs.sh index a84c0a0..727cf83 100644 --- a/docs/makedocs.sh +++ b/docs/makedocs.sh @@ -30,7 +30,7 @@ makeSphinxWeb() { find ${WEB_DIR} -mindepth 1 -not -name "*.md" -delete rm ${WEB_DIR}/search.md ${WEB_DIR}/genindex.md ${WEB_DIR}/py-modindex.md # Some custom tweaks to the output: - python postprocess.py -f ${WEB_DIR}/ + python3 postprocess.py -f ${WEB_DIR}/ echo "All done. Web pages are in ${WEB_DIR}." } diff --git a/docs/requirements.txt b/docs/requirements.txt index eea2831..5b91edb 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,5 @@ # Python packages required to build the docs -sphinx +sphinx==3.0 sphinx_rtd_theme recommonmark numpy diff --git a/examples/classify_image.py b/examples/classify_image.py index 8bd8de4..a67acd0 100644 --- a/examples/classify_image.py +++ b/examples/classify_image.py @@ -32,6 +32,7 @@ import argparse import time +import numpy as np from PIL import Image from pycoral.adapters import classify from pycoral.adapters import common @@ -42,18 +43,27 @@ def main(): parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument('-m', '--model', required=True, - help='File path of .tflite file.') - parser.add_argument('-i', '--input', required=True, - help='Image to be classified.') - parser.add_argument('-l', '--labels', - help='File path of labels file.') - parser.add_argument('-k', '--top_k', type=int, default=1, - help='Max number of classification results') - parser.add_argument('-t', '--threshold', type=float, default=0.0, - help='Classification score threshold') - parser.add_argument('-c', '--count', type=int, default=5, - help='Number of times to run inference') + parser.add_argument( + '-m', '--model', required=True, help='File path of .tflite file.') + parser.add_argument( + '-i', '--input', required=True, help='Image to be classified.') + parser.add_argument( + '-l', '--labels', help='File path of labels file.') + parser.add_argument( + '-k', '--top_k', type=int, default=1, + help='Max number of classification results') + parser.add_argument( + '-t', '--threshold', type=float, default=0.0, + help='Classification score threshold') + parser.add_argument( + '-c', '--count', type=int, default=5, + help='Number of times to run inference') + parser.add_argument( + '-a', '--input_mean', type=float, default=128.0, + help='Mean value for input normalization') + parser.add_argument( + '-s', '--input_std', type=float, default=128.0, + help='STD value for input normalization') args = parser.parse_args() labels = read_label_file(args.labels) if args.labels else {} @@ -61,10 +71,37 @@ def main(): interpreter = make_interpreter(*args.model.split('@')) interpreter.allocate_tensors() + # Model must be uint8 quantized + if common.input_details(interpreter, 'dtype') != np.uint8: + raise ValueError('Only support uint8 input type.') + size = common.input_size(interpreter) image = Image.open(args.input).convert('RGB').resize(size, Image.ANTIALIAS) - common.set_input(interpreter, image) + # Image data must go through two transforms before running inference: + # 1. normalization: f = (input - mean) / std + # 2. quantization: q = f / scale + zero_point + # The following code combines the two steps as such: + # q = (input - mean) / (std * scale) + zero_point + # However, if std * scale equals 1, and mean - zero_point equals 0, the input + # does not need any preprocessing (but in practice, even if the results are + # very close to 1 and 0, it is probably okay to skip preprocessing for better + # efficiency; we use 1e-5 below instead of absolute zero). + params = common.input_details(interpreter, 'quantization_parameters') + scale = params['scales'] + zero_point = params['zero_points'] + mean = args.input_mean + std = args.input_std + if abs(scale * std - 1) < 1e-5 and abs(mean - zero_point) < 1e-5: + # Input data does not require preprocessing. + common.set_input(interpreter, image) + else: + # Input data requires preprocessing + normalized_input = (np.asarray(image) - mean) / (std * scale) + zero_point + np.clip(normalized_input, 0, 255, out=normalized_input) + common.set_input(interpreter, normalized_input.astype(np.uint8)) + + # Run inference print('----INFERENCE TIME----') print('Note: The first inference on Edge TPU is slow because it includes', 'loading the model into Edge TPU memory.') diff --git a/examples/model_pipelining_classify_image.py b/examples/model_pipelining_classify_image.py index 3e79c26..03cb3b8 100644 --- a/examples/model_pipelining_classify_image.py +++ b/examples/model_pipelining_classify_image.py @@ -99,16 +99,24 @@ def main(): 'inception_v3_299_quant_segment_%d_of_2_edgetpu.tflite')) parser.add_argument( '-i', '--input', required=True, help='Image to be classified.') + parser.add_argument('-l', '--labels', help='File path of labels file.') parser.add_argument( - '-l', '--labels', help='File path of labels file.') - parser.add_argument( - '-k', '--top_k', type=int, default=1, + '-k', + '--top_k', + type=int, + default=1, help='Max number of classification results') parser.add_argument( - '-t', '--threshold', type=float, default=0.0, + '-t', + '--threshold', + type=float, + default=0.0, help='Classification score threshold') parser.add_argument( - '-c', '--count', type=int, default=5, + '-c', + '--count', + type=int, + default=5, help='Number of times to run inference') args = parser.parse_args() labels = read_label_file(args.labels) if args.labels else {} @@ -124,13 +132,14 @@ def main(): runner = _make_runner(model_paths, devices) size = common.input_size(runner.interpreters()[0]) + name = common.input_details(runner.interpreters()[0], 'name') image = np.array( Image.open(args.input).convert('RGB').resize(size, Image.ANTIALIAS)) def producer(): for _ in range(args.count): - runner.push([image]) - runner.push([]) + runner.push({name: image}) + runner.push({}) def consumer(): output_details = runner.interpreters()[-1].get_output_details()[0] @@ -139,7 +148,8 @@ def consumer(): result = runner.pop() if not result: break - scores = scale * (result[0][0].astype(np.int64) - zero_point) + values, = result.values() + scores = scale * (values[0].astype(np.int64) - zero_point) classes = classify.get_classes_from_scores(scores, args.top_k, args.threshold) print('-------RESULTS--------') diff --git a/examples/movenet_pose_estimation.py b/examples/movenet_pose_estimation.py new file mode 100644 index 0000000..7a9bea6 --- /dev/null +++ b/examples/movenet_pose_estimation.py @@ -0,0 +1,82 @@ +# Lint as: python3 +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""Example using PyCoral to estimate a single human pose with Edge TPU MoveNet. + +To run this code, you must attach an Edge TPU to the host and +install the Edge TPU runtime (`libedgetpu.so`) and `tflite_runtime`. For +device setup instructions, see coral.ai/docs/setup. + +For more details about MoveNet and its best practices, please see +https://www.tensorflow.org/hub/tutorials/movenet + +Example usage: +``` +bash examples/install_requirements.sh movenet_pose_estimation.py + +python3 examples/movenet_pose_estimation.py \ + --model test_data/movenet_single_pose_lightning_ptq_edgetpu.tflite \ + --input test_data/squat.bmp +``` +""" + +import argparse + +from PIL import Image +from PIL import ImageDraw +from pycoral.adapters import common +from pycoral.utils.edgetpu import make_interpreter + +_NUM_KEYPOINTS = 17 + + +def main(): + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument( + '-m', '--model', required=True, help='File path of .tflite file.') + parser.add_argument( + '-i', '--input', required=True, help='Image to be classified.') + parser.add_argument( + '--output', + default='movenet_result.jpg', + help='File path of the output image.') + args = parser.parse_args() + + interpreter = make_interpreter(args.model) + interpreter.allocate_tensors() + + img = Image.open(args.input) + resized_img = img.resize(common.input_size(interpreter), Image.ANTIALIAS) + common.set_input(interpreter, resized_img) + + interpreter.invoke() + + pose = common.output_tensor(interpreter, 0).copy().reshape(_NUM_KEYPOINTS, 3) + print(pose) + draw = ImageDraw.Draw(img) + width, height = img.size + for i in range(0, _NUM_KEYPOINTS): + draw.ellipse( + xy=[ + pose[i][1] * width - 2, pose[i][0] * height - 2, + pose[i][1] * width + 2, pose[i][0] * height + 2 + ], + fill=(255, 0, 0)) + img.save(args.output) + print('Done. Results saved at', args.output) + + +if __name__ == '__main__': + main() diff --git a/libcoral b/libcoral index 8b21123..6589d0b 160000 --- a/libcoral +++ b/libcoral @@ -1 +1 @@ -Subproject commit 8b21123c74d1f19c94b9d37aa16b26b80ef5e83b +Subproject commit 6589d0bb49c7fdbc4194ce178d06f8cdc7b5df60 diff --git a/libedgetpu b/libedgetpu index 14eee1a..3164995 160000 --- a/libedgetpu +++ b/libedgetpu @@ -1 +1 @@ -Subproject commit 14eee1a076aa1af7ec1ae3c752be79ae2604a708 +Subproject commit 3164995622300286ef2bb14d7fdc2792dae045b7 diff --git a/libedgetpu_bin/LICENSE.txt b/libedgetpu_bin/LICENSE.txt deleted file mode 100644 index 9dadd0b..0000000 --- a/libedgetpu_bin/LICENSE.txt +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2019 Google LLC. This software is provided as-is, without warranty -or representation for any use or purpose. Your use of it is subject to your -agreements with Google covering this software, or if no such agreement -applies, your use is subject to a limited, non-transferable, non-exclusive -license solely to run the software for your testing use, unless and until -revoked by Google. - diff --git a/libedgetpu_bin/Makefile b/libedgetpu_bin/Makefile deleted file mode 100644 index 18a8eb4..0000000 --- a/libedgetpu_bin/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -SHELL := /bin/bash -MAKEFILE_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))) - -.PHONY: all \ - deb \ - help - -all: deb - -deb: - dpkg-buildpackage -rfakeroot -us -uc -tc -b - dpkg-buildpackage -rfakeroot -us -uc -tc -b -a armhf -d - dpkg-buildpackage -rfakeroot -us -uc -tc -b -a arm64 -d - mkdir -p $(MAKEFILE_DIR)/../dist - mv $(MAKEFILE_DIR)/../*.{deb,changes,buildinfo} \ - $(MAKEFILE_DIR)/../dist - -help: - @echo "make all - Build Debian packages for all platforms" - @echo "make help - Print help message" - diff --git a/libedgetpu_bin/debian/changelog b/libedgetpu_bin/debian/changelog deleted file mode 100644 index e99e249..0000000 --- a/libedgetpu_bin/debian/changelog +++ /dev/null @@ -1,57 +0,0 @@ -libedgetpu (15.0) stable; urgency=medium - * New release - -- Coral Mon, 02 Nov 2020 10:58:23 -0800 -libedgetpu (14.1) stable; urgency=medium - * New release - -- Coral Tue, 07 Jul 2020 13:47:32 -0700 -libedgetpu (14.0) stable; urgency=medium - * New release - -- Coral Wed, 25 Mar 2020 14:25:24 -0700 -libedgetpu (13.0) stable; urgency=medium - * New release - -- Coral Tue, 28 Jan 2020 15:58:19 -0700 -libedgetpu (12.1-1) mendel-chef; urgency=medium - * New release - -- Coral Wed, 30 Oct 2019 15:58:16 -0700 -libedgetpu (12-1) mendel-chef; urgency=medium - * New release - -- Coral Mon, 16 Sep 2019 13:27:18 -0700 -libedgetpu (11-1) mendel-chef; urgency=medium - * New release - -- Coral Mon, 15 Jul 2019 15:52:14 -0700 -libedgetpu (10-2) mendel-chef; urgency=medium - * New release - -- Coral Thu, 18 Apr 2019 13:37:19 -0700 -libedgetpu (9-2) mendel-chef; urgency=medium - * New release - -- Coral Wed, 03 Apr 2019 14:11:47 -0800 -libedgetpu (8-2) mendel-chef; urgency=medium - * New release - -- Coral Tue, 02 Apr 2019 14:11:47 -0800 -libedgetpu (7-2) mendel-chef; urgency=medium - * New release - -- Coral Thu, 28 Mar 2019 14:11:47 -0800 -libedgetpu (6-2) mendel-chef; urgency=medium - * New release - -- Coral Tue, 19 Mar 2019 17:08:25 -0700 -libedgetpu (5-2) mendel-beaker; urgency=medium - * New release - -- Coral Fri, 08 Mar 2019 14:11:47 -0800 -libedgetpu (4-2) mendel-beaker; urgency=medium - * New release - -- Coral Wed, 27 Feb 2019 11:00:25 -0800 -libedgetpu (3-2) mendel-beaker; urgency=medium - * New release - -- Coral Mon, 04 Feb 2019 11:20:13 -0800 -libedgetpu (3-1) mendel-beaker; urgency=medium - * New release - -- Coral Mon, 28 Jan 2019 14:10:00 -0800 -libedgetpu (2-1) mendel-beaker; urgency=medium - * New release - -- Coral Tue, 22 Jan 2019 10:42:07 -0800 -libedgetpu (1-1) mendel-beaker; urgency=medium - * New release - -- Coral Wed, 16 Jan 2019 12:00:00 -0800 -libedgetpu (0.1) UNRELEASED; urgency=medium - * Initial release. - -- Coral Mon, 04 Jun 2018 16:14:00 -0800 diff --git a/libedgetpu_bin/debian/compat b/libedgetpu_bin/debian/compat deleted file mode 100644 index f599e28..0000000 --- a/libedgetpu_bin/debian/compat +++ /dev/null @@ -1 +0,0 @@ -10 diff --git a/libedgetpu_bin/debian/control b/libedgetpu_bin/debian/control deleted file mode 100644 index 71fd6b2..0000000 --- a/libedgetpu_bin/debian/control +++ /dev/null @@ -1,45 +0,0 @@ -Source: libedgetpu -Maintainer: Coral -Priority: optional -Build-Depends: debhelper (>= 9) -Standards-Version: 3.9.6 -Homepage: https://coral.ai/ - -Package: libedgetpu1-std -Provides: libedgetpu1 (= ${binary:Version}) -Conflicts: libedgetpu1, libedgetpu1-legacy -Section: misc -Priority: optional -Architecture: any -Multi-Arch: same -Depends: libc6, - libgcc1, - libstdc++6, - libusb-1.0-0, - ${misc:Depends} -Description: Support library for Edge TPU - Support library (standard speed) for the Edge TPU - -Package: libedgetpu1-max -Provides: libedgetpu1 (= ${binary:Version}) -Conflicts: libedgetpu1, libedgetpu1-legacy -Section: misc -Priority: optional -Architecture: any -Multi-Arch: same -Depends: libc6, - libgcc1, - libstdc++6, - libusb-1.0-0, - ${misc:Depends} -Description: Support library for Edge TPU - Support library (max speed) for the Edge TPU - -Package:libedgetpu-dev -Section: libdevel -Priority: optional -Architecture: any -Depends: libedgetpu1-std (= ${binary:Version}) | libedgetpu1 (= ${binary:Version}), - ${misc:Depends} -Description: Development files for libedgetpu - This package contains C++ Header files for libedgetpu.so diff --git a/libedgetpu_bin/debian/copyright b/libedgetpu_bin/debian/copyright deleted file mode 100644 index d4bf188..0000000 --- a/libedgetpu_bin/debian/copyright +++ /dev/null @@ -1,7 +0,0 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: edgetpu -Source: https://github.com/google-coral/edgetpu - -Files: * -Copyright: Copyright 2018 Google, LLC -License: Apache-2.0 diff --git a/libedgetpu_bin/debian/libedgetpu-dev.install b/libedgetpu_bin/debian/libedgetpu-dev.install deleted file mode 100644 index e7c66ba..0000000 --- a/libedgetpu_bin/debian/libedgetpu-dev.install +++ /dev/null @@ -1,2 +0,0 @@ -edgetpu.h /usr/include -edgetpu_c.h /usr/include diff --git a/libedgetpu_bin/debian/libedgetpu1-max.lintian-overrides b/libedgetpu_bin/debian/libedgetpu1-max.lintian-overrides deleted file mode 100644 index 3591ed2..0000000 --- a/libedgetpu_bin/debian/libedgetpu1-max.lintian-overrides +++ /dev/null @@ -1,4 +0,0 @@ -# We provide two conflicting package variants with the same soname inside. -libedgetpu1-max: package-name-doesnt-match-sonames libedgetpu1 -libedgetpu1-max: missing-debconf-dependency-for-preinst -libedgetpu1-max: too-long-short-description-in-templates libedgetpu/accepted-eula diff --git a/libedgetpu_bin/debian/libedgetpu1-max.preinst b/libedgetpu_bin/debian/libedgetpu1-max.preinst deleted file mode 100644 index 5a7a763..0000000 --- a/libedgetpu_bin/debian/libedgetpu1-max.preinst +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -e - -. /usr/share/debconf/confmodule - -db_version 2.0 - -db_get libedgetpu/accepted-eula -if [ "$RET" = "true" ]; then - exit 0 # already accepted -fi - -db_fset libedgetpu/accepted-eula seen false -db_input critical libedgetpu/accepted-eula -db_go || true - -db_get libedgetpu/accepted-eula -if [ "$RET" = "true" ]; then - exit 0 # accepted -fi - -db_input critical libedgetpu/error-eula -db_go || true - -exit 1 # not accepted diff --git a/libedgetpu_bin/debian/libedgetpu1-max.templates b/libedgetpu_bin/debian/libedgetpu1-max.templates deleted file mode 100644 index 6c276be..0000000 --- a/libedgetpu_bin/debian/libedgetpu1-max.templates +++ /dev/null @@ -1,21 +0,0 @@ -Template: libedgetpu/accepted-eula -Type: boolean -Default: false -Description: Continue to install the Edge TPU runtime that runs at the maximum operating frequency? - You're about to install the Edge TPU runtime that runs at the maximum operating frequency. - . - Warning: If you're using the Coral USB Accelerator, it may heat up during operation, depending - on the computation workloads and operating frequency. Touching the metal part of the USB - Accelerator after it has been operating for an extended period of time may lead to discomfort - and/or skin burns. As such, if you install the Edge TPU runtime using the maximum operating - frequency, the USB Accelerator should be operated at an ambient temperature of 25°C or less. - (If you instead install the Edge TPU runtime using the reduced operating frequency, then the - device is intended to safely operate at an ambient temperature of 35°C or less.) - . - Google does not accept any responsibility for any loss or damage if the device is operated - outside of the recommended ambient temperature range. - -Template: libedgetpu/error-eula -Type: error -Description: Install aborted. - For help setting up your device, see g.co/coral/setup. diff --git a/libedgetpu_bin/debian/libedgetpu1-max.triggers b/libedgetpu_bin/debian/libedgetpu1-max.triggers deleted file mode 100644 index dd86603..0000000 --- a/libedgetpu_bin/debian/libedgetpu1-max.triggers +++ /dev/null @@ -1 +0,0 @@ -activate-noawait ldconfig diff --git a/libedgetpu_bin/debian/libedgetpu1-max.udev b/libedgetpu_bin/debian/libedgetpu1-max.udev deleted file mode 120000 index e52da57..0000000 --- a/libedgetpu_bin/debian/libedgetpu1-max.udev +++ /dev/null @@ -1 +0,0 @@ -../edgetpu-accelerator.rules \ No newline at end of file diff --git a/libedgetpu_bin/debian/libedgetpu1-std.lintian-overrides b/libedgetpu_bin/debian/libedgetpu1-std.lintian-overrides deleted file mode 100644 index cfa2624..0000000 --- a/libedgetpu_bin/debian/libedgetpu1-std.lintian-overrides +++ /dev/null @@ -1,2 +0,0 @@ -# We provide two conflicting package variants with the same soname inside. -libedgetpu1-std: package-name-doesnt-match-sonames libedgetpu1 diff --git a/libedgetpu_bin/debian/libedgetpu1-std.triggers b/libedgetpu_bin/debian/libedgetpu1-std.triggers deleted file mode 100644 index dd86603..0000000 --- a/libedgetpu_bin/debian/libedgetpu1-std.triggers +++ /dev/null @@ -1 +0,0 @@ -activate-noawait ldconfig diff --git a/libedgetpu_bin/debian/libedgetpu1-std.udev b/libedgetpu_bin/debian/libedgetpu1-std.udev deleted file mode 120000 index e52da57..0000000 --- a/libedgetpu_bin/debian/libedgetpu1-std.udev +++ /dev/null @@ -1 +0,0 @@ -../edgetpu-accelerator.rules \ No newline at end of file diff --git a/libedgetpu_bin/debian/rules b/libedgetpu_bin/debian/rules deleted file mode 100755 index 4fbaee5..0000000 --- a/libedgetpu_bin/debian/rules +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/make -f -# -*- makefile -*- - -# Uncomment this to turn on verbose mode. -# export DH_VERBOSE=1 -FILENAME := libedgetpu.so.1.0 -SONAME := libedgetpu.so.1 -LIB_DEV := debian/libedgetpu-dev/usr/lib/$(DEB_HOST_GNU_TYPE) -LIB_STD := debian/libedgetpu1-std/usr/lib/$(DEB_HOST_GNU_TYPE) -LIB_MAX := debian/libedgetpu1-max/usr/lib/$(DEB_HOST_GNU_TYPE) - -ifeq ($(DEB_TARGET_ARCH),armhf) - CPU := armv7a -else ifeq ($(DEB_TARGET_ARCH),arm64) - CPU := aarch64 -else ifeq ($(DEB_TARGET_ARCH),amd64) - CPU := k8 -endif - -%: - dh $@ - -override_dh_auto_install: - dh_auto_install - - mkdir -p $(LIB_DEV) - ln -fs $(FILENAME) $(LIB_DEV)/libedgetpu.so - - mkdir -p $(LIB_STD) - cp -f throttled/$(CPU)/$(FILENAME) $(LIB_STD)/$(FILENAME) - ln -fs $(FILENAME) $(LIB_STD)/$(SONAME) - - mkdir -p $(LIB_MAX) - cp -f direct/$(CPU)/$(FILENAME) $(LIB_MAX)/$(FILENAME) - ln -fs $(FILENAME) $(LIB_MAX)/$(SONAME) - -# Skip auto build and auto clean. -override_dh_auto_clean: -override_dh_auto_build: - -# Skip .so post processing. -override_dh_strip: -override_dh_shlibdeps: - -# Skip tests. -override_dh_auto_test: diff --git a/libedgetpu_bin/direct/aarch64/libedgetpu.so.1.0 b/libedgetpu_bin/direct/aarch64/libedgetpu.so.1.0 old mode 100755 new mode 100644 index 84b140c..b64dfa7 Binary files a/libedgetpu_bin/direct/aarch64/libedgetpu.so.1.0 and b/libedgetpu_bin/direct/aarch64/libedgetpu.so.1.0 differ diff --git a/libedgetpu_bin/direct/armv6/libedgetpu.so.1 b/libedgetpu_bin/direct/armv6/libedgetpu.so.1 deleted file mode 120000 index 90ac68c..0000000 --- a/libedgetpu_bin/direct/armv6/libedgetpu.so.1 +++ /dev/null @@ -1 +0,0 @@ -libedgetpu.so.1.0 \ No newline at end of file diff --git a/libedgetpu_bin/direct/armv6/libedgetpu.so.1.0 b/libedgetpu_bin/direct/armv6/libedgetpu.so.1.0 deleted file mode 100755 index 6b4f3fc..0000000 Binary files a/libedgetpu_bin/direct/armv6/libedgetpu.so.1.0 and /dev/null differ diff --git a/libedgetpu_bin/direct/armv7a/libedgetpu.so.1.0 b/libedgetpu_bin/direct/armv7a/libedgetpu.so.1.0 old mode 100755 new mode 100644 index 4b8c73e..6945e20 Binary files a/libedgetpu_bin/direct/armv7a/libedgetpu.so.1.0 and b/libedgetpu_bin/direct/armv7a/libedgetpu.so.1.0 differ diff --git a/libedgetpu_bin/direct/darwin/libedgetpu.1.0.dylib b/libedgetpu_bin/direct/darwin/libedgetpu.1.0.dylib old mode 100755 new mode 100644 index 3561512..204804d Binary files a/libedgetpu_bin/direct/darwin/libedgetpu.1.0.dylib and b/libedgetpu_bin/direct/darwin/libedgetpu.1.0.dylib differ diff --git a/libedgetpu_bin/direct/k8/libedgetpu.so.1.0 b/libedgetpu_bin/direct/k8/libedgetpu.so.1.0 old mode 100755 new mode 100644 index 8d5cd6a..774993b Binary files a/libedgetpu_bin/direct/k8/libedgetpu.so.1.0 and b/libedgetpu_bin/direct/k8/libedgetpu.so.1.0 differ diff --git a/libedgetpu_bin/direct/x64_windows/edgetpu.dll b/libedgetpu_bin/direct/x64_windows/edgetpu.dll index c07d541..606b1e8 100644 Binary files a/libedgetpu_bin/direct/x64_windows/edgetpu.dll and b/libedgetpu_bin/direct/x64_windows/edgetpu.dll differ diff --git a/libedgetpu_bin/direct/x64_windows/edgetpu.dll.if.lib b/libedgetpu_bin/direct/x64_windows/edgetpu.dll.if.lib index 7a5bf61..24e6a48 100644 Binary files a/libedgetpu_bin/direct/x64_windows/edgetpu.dll.if.lib and b/libedgetpu_bin/direct/x64_windows/edgetpu.dll.if.lib differ diff --git a/libedgetpu_bin/edgetpu-accelerator.rules b/libedgetpu_bin/edgetpu-accelerator.rules deleted file mode 100644 index 60e034c..0000000 --- a/libedgetpu_bin/edgetpu-accelerator.rules +++ /dev/null @@ -1,2 +0,0 @@ -SUBSYSTEM=="usb",ATTRS{idVendor}=="1a6e",GROUP="plugdev" -SUBSYSTEM=="usb",ATTRS{idVendor}=="18d1",GROUP="plugdev" diff --git a/libedgetpu_bin/edgetpu.h b/libedgetpu_bin/edgetpu.h deleted file mode 100644 index ed4e4e2..0000000 --- a/libedgetpu_bin/edgetpu.h +++ /dev/null @@ -1,317 +0,0 @@ -/* -Copyright 2018 Google LLC - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -==============================================================================*/ -// @cond BEGIN doxygen exclude -// This header file defines EdgeTpuManager and EdgeTpuContext. -// See below for more details. - -#ifndef TFLITE_PUBLIC_EDGETPU_H_ -#define TFLITE_PUBLIC_EDGETPU_H_ - -// If the ABI changes in a backward-incompatible way, please increment the -// version number in the BUILD file. - -#include -#include -#include -#include -#include - -#include "tensorflow/lite/context.h" - -#if defined(_WIN32) -#ifdef EDGETPU_COMPILE_LIBRARY -#define EDGETPU_EXPORT __declspec(dllexport) -#else -#define EDGETPU_EXPORT __declspec(dllimport) -#endif // EDGETPU_COMPILE_LIBRARY -#else -#define EDGETPU_EXPORT __attribute__((visibility("default"))) -#endif // _WIN32 -// END doxygen exclude @endcond - -namespace edgetpu { - -// Edge TPU custom op. -static const char kCustomOp[] = "edgetpu-custom-op"; - -// The device interface used with the host -enum class DeviceType { - // PCIe Gen2 x1 - kApexPci = 0, - // USB 2.0 or 3.1 Gen1 - kApexUsb = 1, -}; - -class EdgeTpuContext; - -// Singleton Edge TPU manager for allocating new TPU contexts. -// Functions in this interface are thread-safe. -class EDGETPU_EXPORT EdgeTpuManager { - public: - // See EdgeTpuContext::GetDeviceOptions(). - using DeviceOptions = std::unordered_map; - // Details about a particular Edge TPU - struct DeviceEnumerationRecord { - // The Edge TPU device type, either PCIe or USB - DeviceType type; - // System path for the Edge TPU device - std::string path; - - // Returns true if two enumeration records point to the same device. - friend bool operator==(const DeviceEnumerationRecord& lhs, - const DeviceEnumerationRecord& rhs) { - return (lhs.type == rhs.type) && (lhs.path == rhs.path); - } - - // Returns true if two enumeration records point to defferent devices. - friend bool operator!=(const DeviceEnumerationRecord& lhs, - const DeviceEnumerationRecord& rhs) { - return !(lhs == rhs); - } - }; - - // Returns a pointer to the singleton object, or nullptr if not supported on - // this platform. - static EdgeTpuManager* GetSingleton(); - - // @cond BEGIN doxygen exclude for deprecated APIs. - - // NewEdgeTpuContext family functions has been deprecated and will be removed - // in the future. Please use OpenDevice for new code. - // - // These functions return an unique_ptr to EdgeTpuContext, with - // the intention that the device will be closed, and associate resources - // released, when the unique_ptr leaves scope. - // - // These functions seek exclusive ownership of the opened devices. As they - // cannot open devices already opened by OpenDevice, and vice versa. - // Devices opened through these functions would have attribute - // "ExclusiveOwnership", which can be queried through - // #EdgeTpuContext::GetDeviceOptions(). - - // Creates a new Edge TPU context to be assigned to Tflite::Interpreter. The - // Edge TPU context is associated with the default TPU device. May be null - // if underlying device cannot be found or open. Caller owns the returned new - // context and should destroy the context either implicity or explicitly after - // all interpreters sharing this context are destroyed. - virtual std::unique_ptr NewEdgeTpuContext() = 0; - - // Same as above, but the created context is associated with the specified - // type. - virtual std::unique_ptr NewEdgeTpuContext( - DeviceType device_type) = 0; - - // Same as above, but the created context is associated with the specified - // type and device path. - virtual std::unique_ptr NewEdgeTpuContext( - DeviceType device_type, const std::string& device_path) = 0; - - // Same as above, but the created context is associated with the given device - // type, path and options. - // - // Available options are: - // - "Performance": ["Low", "Medium", "High", "Max"] (Default is "Max") - // - "Usb.AlwaysDfu": ["True", "False"] (Default is "False") - // - "Usb.MaxBulkInQueueLength": ["0",.., "255"] (Default is "32") - virtual std::unique_ptr NewEdgeTpuContext( - DeviceType device_type, const std::string& device_path, - const DeviceOptions& options) = 0; - // END doxygen exclude for deprecated APIs @endcond - - - // Enumerates all connected Edge TPU devices. - virtual std::vector EnumerateEdgeTpu() const = 0; - - // Opens the default Edge TPU device. - // - // All `OpenDevice` functions return a shared_ptr to EdgeTpuContext, with - // the intention that the device can be shared among multiple software - // components. The device is closed after the last reference leaves scope. - // - // Multiple invocations of this function could return handle to the same - // device, but there is no guarantee. - // - // You cannot open devices opened by `NewEdgeTpuContext`, and vice versa. - // - // @return A shared pointer to Edge TPU device. The shared_ptr could point to - // nullptr in case of error. - virtual std::shared_ptr OpenDevice() = 0; - - // Same as above, but the returned context is associated with the specified - // type. - // - // @param device_type The DeviceType you want to open. - virtual std::shared_ptr OpenDevice( - DeviceType device_type) = 0; - - // Same as above, but the returned context is associated with the specified - // type and device path. If path is empty, any device of the specified type - // could be returned. - // - // @param device_type The DeviceType you want to open. - // @param device_path A path to the device you want. - // - // @return A shared pointer to Edge TPU device. The shared_ptr could point to - // nullptr in case of error. - virtual std::shared_ptr OpenDevice( - DeviceType device_type, const std::string& device_path) = 0; - - // Same as above, but the specified options are used to create a new context - // if no existing device is compatible with the specified type and path. - // - // If a device of compatible type and path is not found, the options could be - // ignored. It is the caller's responsibility to verify if the returned - // context is desirable, through EdgeTpuContext::GetDeviceOptions(). - // - // @param device_type The DeviceType you want to open. - // @param device_path A path to the device you want. - // @param options Specific criteria for the device you want. - // Available options are: - // - "Performance": ["Low", "Medium", "High", "Max"] (Default is "Max") - // - "Usb.AlwaysDfu": ["True", "False"] (Default is "False") - // - "Usb.MaxBulkInQueueLength": ["0",.., "255"] (Default is "32") - // - // @return A shared pointer to Edge TPU device. The shared_ptr could point to - // nullptr in case of error. - virtual std::shared_ptr OpenDevice( - DeviceType device_type, const std::string& device_path, - const DeviceOptions& options) = 0; - - // Returns a snapshot of currently opened shareable devices. - // Exclusively owned Edge TPU devices cannot be returned here, as they're - // owned by unique pointers. - virtual std::vector> GetOpenedDevices() - const = 0; - - // Sets the verbosity of operating logs related to each Edge TPU. - // - // @param verbosity The verbosity level, which may be 0 to 10. - // 10 is the most verbose; 0 is the default. - virtual TfLiteStatus SetVerbosity(int verbosity) = 0; - - // Returns the version of the Edge TPU runtime stack. - virtual std::string Version() const = 0; - - protected: - // No deletion for this singleton instance. - virtual ~EdgeTpuManager() = default; -}; - -// EdgeTpuContext is an object associated with one or more tflite::Interpreter. -// Instances of this class should be allocated with EdgeTpuManager::OpenDevice. -// -// More than one Interpreter instances can point to the same context. This means -// the tasks from both would be executed under the same TPU context. -// The lifetime of this context must be longer than all associated -// tflite::Interpreter instances. -// -// Functions in this interface are thread-safe. -// -// Typical usage with Coral: -// -// ``` -// // Sets up the tpu_context. -// auto tpu_context = -// edgetpu::EdgeTpuManager::GetSingleton()->OpenDevice(); -// -// std::unique_ptr interpreter; -// tflite::ops::builtin::BuiltinOpResolver resolver; -// auto model = -// tflite::FlatBufferModel::BuildFromFile(model_file_name.c_str()); -// -// // Registers Edge TPU custom op handler with Tflite resolver. -// resolver.AddCustom(edgetpu::kCustomOp, edgetpu::RegisterCustomOp()); -// -// tflite::InterpreterBuilder(*model, resolver)(&interpreter); -// -// // Binds a context with a specific interpreter. -// interpreter->SetExternalContext(kTfLiteEdgeTpuContext, -// tpu_context.get()); -// -// // Note that all edge TPU context set ups should be done before this -// // function is called. -// interpreter->AllocateTensors(); -// .... (Prepare input tensors) -// interpreter->Invoke(); -// .... (retrieving the result from output tensors) -// -// // Releases interpreter instance to free up resources associated with -// // this custom op. -// interpreter.reset(); -// -// // Closes the edge TPU. -// tpu_context.reset(); -// ``` -// -// Typical usage with Android NNAPI: -// -// ``` -// std::unique_ptr interpreter; -// tflite::ops::builtin::BuiltinOpResolver resolver; -// auto model = -// tflite::FlatBufferModel::BuildFromFile(model_file_name.c_str()); -// -// // Registers Edge TPU custom op handler with Tflite resolver. -// resolver.AddCustom(edgetpu::kCustomOp, edgetpu::RegisterCustomOp()); -// -// tflite::InterpreterBuilder(*model, resolver)(&interpreter); -// -// interpreter->AllocateTensors(); -// .... (Prepare input tensors) -// interpreter->Invoke(); -// .... (retrieving the result from output tensors) -// -// // Releases interpreter instance to free up resources associated with -// // this custom op. -// interpreter.reset(); -// ``` -class EdgeTpuContext : public TfLiteExternalContext { - public: - virtual ~EdgeTpuContext() = 0; - - // Returns a pointer to the device enumeration record for this device, - // if available. - virtual const EdgeTpuManager::DeviceEnumerationRecord& GetDeviceEnumRecord() - const = 0; - - // Returns a snapshot of the options used to open this - // device, and current state, if available. - // - // Supported attributes are: - // - "ExclusiveOwnership": present when it is under exclusive ownership - // (unique_ptr returned by NewEdgeTpuContext). - // - "IsReady": present when it is ready for further requests. - virtual EdgeTpuManager::DeviceOptions GetDeviceOptions() const = 0; - - // Returns true if the device is most likely ready to accept requests. - // When there are fatal errors, including unplugging of an USB device, the - // state of this device would be changed. - virtual bool IsReady() const = 0; -}; - -// Returns pointer to an instance of TfLiteRegistration to handle -// Edge TPU custom ops, to be used with -// tflite::ops::builtin::BuiltinOpResolver::AddCustom -EDGETPU_EXPORT TfLiteRegistration* RegisterCustomOp(); - -// Inserts name of device type into ostream. Returns the modified ostream. -EDGETPU_EXPORT std::ostream& operator<<(std::ostream& out, - DeviceType device_type); - -} // namespace edgetpu - - -#endif // TFLITE_PUBLIC_EDGETPU_H_ diff --git a/libedgetpu_bin/edgetpu_c.h b/libedgetpu_bin/edgetpu_c.h deleted file mode 100644 index 64b73de..0000000 --- a/libedgetpu_bin/edgetpu_c.h +++ /dev/null @@ -1,116 +0,0 @@ -/* -Copyright 2019 Google LLC - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -==============================================================================*/ -// -// This header defines C API to provide edge TPU support for TensorFlow Lite -// framework. It is only available for non-NNAPI use cases. -// -// Typical API usage from C++ code involves serveral steps: -// -// 1. Create tflite::FlatBufferModel which may contain edge TPU custom op. -// -// auto model = -// tflite::FlatBufferModel::BuildFromFile(model_file_name.c_str()); -// -// 2. Create tflite::Interpreter. -// -// tflite::ops::builtin::BuiltinOpResolver resolver; -// std::unique_ptr interpreter; -// tflite::InterpreterBuilder(model, resolver)(&interpreter); -// -// 3. Enumerate edge TPU devices. -// -// size_t num_devices; -// std::unique_ptr devices( -// edgetpu_list_devices(&num_devices), &edgetpu_free_devices); -// -// assert(num_devices > 0); -// const auto& device = devices.get()[0]; -// -// 4. Modify interpreter with the delegate. -// -// auto* delegate = -// edgetpu_create_delegate(device.type, device.path, nullptr, 0); -// interpreter->ModifyGraphWithDelegate({delegate, edgetpu_free_delegate}); -// -// 5. Prepare input tensors and run inference. -// -// interpreter->AllocateTensors(); -// .... (Prepare input tensors) -// interpreter->Invoke(); -// .... (Retrieve the result from output tensors) - -#ifndef TFLITE_PUBLIC_EDGETPU_C_H_ -#define TFLITE_PUBLIC_EDGETPU_C_H_ - -#include "tensorflow/lite/c/common.h" - -#if defined(_WIN32) -#ifdef EDGETPU_COMPILE_LIBRARY -#define EDGETPU_EXPORT __declspec(dllexport) -#else -#define EDGETPU_EXPORT __declspec(dllimport) -#endif // EDGETPU_COMPILE_LIBRARY -#else -#define EDGETPU_EXPORT __attribute__((visibility("default"))) -#endif // _WIN32 - -#ifdef __cplusplus -extern "C" { -#endif - -enum edgetpu_device_type { - EDGETPU_APEX_PCI = 0, - EDGETPU_APEX_USB = 1, -}; - -struct edgetpu_device { - enum edgetpu_device_type type; - const char* path; -}; - -struct edgetpu_option { - const char* name; - const char* value; -}; - -// Returns array of connected edge TPU devices. -EDGETPU_EXPORT struct edgetpu_device* edgetpu_list_devices(size_t* num_devices); - -// Frees array returned by `edgetpu_list_devices`. -EDGETPU_EXPORT void edgetpu_free_devices(struct edgetpu_device* dev); - -// Creates a delegate which handles all edge TPU custom ops inside -// `tflite::Interpreter`. Options must be available only during the call of this -// function. -EDGETPU_EXPORT TfLiteDelegate* edgetpu_create_delegate( - enum edgetpu_device_type type, const char* name, - const struct edgetpu_option* options, size_t num_options); - -// Frees delegate returned by `edgetpu_create_delegate`. -EDGETPU_EXPORT void edgetpu_free_delegate(TfLiteDelegate* delegate); - -// Sets verbosity of operating logs related to edge TPU. -// Verbosity level can be set to [0-10], in which 10 is the most verbose. -EDGETPU_EXPORT void edgetpu_verbosity(int verbosity); - -// Returns the version of edge TPU runtime stack. -EDGETPU_EXPORT const char* edgetpu_version(); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // TFLITE_PUBLIC_EDGETPU_C_H_ diff --git a/libedgetpu_bin/throttled/aarch64/libedgetpu.so.1.0 b/libedgetpu_bin/throttled/aarch64/libedgetpu.so.1.0 old mode 100755 new mode 100644 index f093e95..b3d4d21 Binary files a/libedgetpu_bin/throttled/aarch64/libedgetpu.so.1.0 and b/libedgetpu_bin/throttled/aarch64/libedgetpu.so.1.0 differ diff --git a/libedgetpu_bin/throttled/armv6/libedgetpu.so.1 b/libedgetpu_bin/throttled/armv6/libedgetpu.so.1 deleted file mode 120000 index 90ac68c..0000000 --- a/libedgetpu_bin/throttled/armv6/libedgetpu.so.1 +++ /dev/null @@ -1 +0,0 @@ -libedgetpu.so.1.0 \ No newline at end of file diff --git a/libedgetpu_bin/throttled/armv6/libedgetpu.so.1.0 b/libedgetpu_bin/throttled/armv6/libedgetpu.so.1.0 deleted file mode 100755 index fedd538..0000000 Binary files a/libedgetpu_bin/throttled/armv6/libedgetpu.so.1.0 and /dev/null differ diff --git a/libedgetpu_bin/throttled/armv7a/libedgetpu.so.1.0 b/libedgetpu_bin/throttled/armv7a/libedgetpu.so.1.0 old mode 100755 new mode 100644 index ef2aa71..7a4821b Binary files a/libedgetpu_bin/throttled/armv7a/libedgetpu.so.1.0 and b/libedgetpu_bin/throttled/armv7a/libedgetpu.so.1.0 differ diff --git a/libedgetpu_bin/throttled/darwin/libedgetpu.1.0.dylib b/libedgetpu_bin/throttled/darwin/libedgetpu.1.0.dylib old mode 100755 new mode 100644 index a00b2e5..217ab20 Binary files a/libedgetpu_bin/throttled/darwin/libedgetpu.1.0.dylib and b/libedgetpu_bin/throttled/darwin/libedgetpu.1.0.dylib differ diff --git a/libedgetpu_bin/throttled/k8/libedgetpu.so.1.0 b/libedgetpu_bin/throttled/k8/libedgetpu.so.1.0 old mode 100755 new mode 100644 index feb6c64..19167da Binary files a/libedgetpu_bin/throttled/k8/libedgetpu.so.1.0 and b/libedgetpu_bin/throttled/k8/libedgetpu.so.1.0 differ diff --git a/libedgetpu_bin/throttled/x64_windows/edgetpu.dll b/libedgetpu_bin/throttled/x64_windows/edgetpu.dll index d7eb6b4..ef9e415 100644 Binary files a/libedgetpu_bin/throttled/x64_windows/edgetpu.dll and b/libedgetpu_bin/throttled/x64_windows/edgetpu.dll differ diff --git a/libedgetpu_bin/throttled/x64_windows/edgetpu.dll.if.lib b/libedgetpu_bin/throttled/x64_windows/edgetpu.dll.if.lib index 1a1591d..738407f 100644 Binary files a/libedgetpu_bin/throttled/x64_windows/edgetpu.dll.if.lib and b/libedgetpu_bin/throttled/x64_windows/edgetpu.dll.if.lib differ diff --git a/pycoral/__init__.py b/pycoral/__init__.py index f7a4fc1..349dbdd 100644 --- a/pycoral/__init__.py +++ b/pycoral/__init__.py @@ -14,4 +14,4 @@ # limitations under the License. """Version information for Coral Python APIs.""" -__version__ = "1.0.1" +__version__ = "2.0.0" diff --git a/pycoral/adapters/classify.py b/pycoral/adapters/classify.py index 4a477ee..eed057c 100644 --- a/pycoral/adapters/classify.py +++ b/pycoral/adapters/classify.py @@ -61,7 +61,7 @@ def get_scores(interpreter): # Always convert to np.int64 to avoid overflow on subtraction. return scale * (output_data.astype(np.int64) - zero_point) - return output_data + return output_data.copy() def get_classes_from_scores(scores, diff --git a/pycoral/adapters/detect.py b/pycoral/adapters/detect.py index b36dfbf..f7b63cd 100644 --- a/pycoral/adapters/detect.py +++ b/pycoral/adapters/detect.py @@ -85,13 +85,15 @@ def scale(self, sx, sy): Args: sx (float): Scale factor for the x-axis. sy (float): Scale factor for the y-axis. + Returns: A :obj:`BBox` object with the rescaled dimensions. """ - return BBox(xmin=sx * self.xmin, - ymin=sy * self.ymin, - xmax=sx * self.xmax, - ymax=sy * self.ymax) + return BBox( + xmin=sx * self.xmin, + ymin=sy * self.ymin, + xmax=sx * self.xmax, + ymax=sy * self.ymax) def translate(self, dx, dy): """Translates the bounding box position. @@ -99,26 +101,30 @@ def translate(self, dx, dy): Args: dx (int): Number of pixels to move the box on the x-axis. dy (int): Number of pixels to move the box on the y-axis. + Returns: A :obj:`BBox` object at the new position. """ - return BBox(xmin=dx + self.xmin, - ymin=dy + self.ymin, - xmax=dx + self.xmax, - ymax=dy + self.ymax) + return BBox( + xmin=dx + self.xmin, + ymin=dy + self.ymin, + xmax=dx + self.xmax, + ymax=dy + self.ymax) def map(self, f): """Maps all box coordinates to a new position using a given function. Args: f: A function that takes a single coordinate and returns a new one. + Returns: A :obj:`BBox` with the new coordinates. """ - return BBox(xmin=f(self.xmin), - ymin=f(self.ymin), - xmax=f(self.xmax), - ymax=f(self.ymax)) + return BBox( + xmin=f(self.xmin), + ymin=f(self.ymin), + xmax=f(self.xmax), + ymax=f(self.ymax)) @staticmethod def intersect(a, b): @@ -127,14 +133,16 @@ def intersect(a, b): Args: a: :obj:`BBox` A. b: :obj:`BBox` B. + Returns: A :obj:`BBox` representing the area where the two boxes intersect (may be an invalid box, check with :func:`valid`). """ - return BBox(xmin=max(a.xmin, b.xmin), - ymin=max(a.ymin, b.ymin), - xmax=min(a.xmax, b.xmax), - ymax=min(a.ymax, b.ymax)) + return BBox( + xmin=max(a.xmin, b.xmin), + ymin=max(a.ymin, b.ymin), + xmax=min(a.xmax, b.xmax), + ymax=min(a.ymax, b.ymax)) @staticmethod def union(a, b): @@ -143,14 +151,16 @@ def union(a, b): Args: a: :obj:`BBox` A. b: :obj:`BBox` B. + Returns: A :obj:`BBox` representing the unified area of the two boxes (always a valid box). """ - return BBox(xmin=min(a.xmin, b.xmin), - ymin=min(a.ymin, b.ymin), - xmax=max(a.xmax, b.xmax), - ymax=max(a.ymax, b.ymax)) + return BBox( + xmin=min(a.xmin, b.xmin), + ymin=min(a.ymin, b.ymin), + xmax=max(a.xmax, b.xmax), + ymax=max(a.ymax, b.ymax)) @staticmethod def iou(a, b): @@ -159,6 +169,7 @@ def iou(a, b): Args: a: :obj:`BBox` A. b: :obj:`BBox` B. + Returns: The intersection-over-union value: 1.0 meaning the two boxes are perfectly aligned, 0 if not overlapping at all (invalid intersection). @@ -179,17 +190,37 @@ def get_objects(interpreter, interpreter: The ``tf.lite.Interpreter`` to query for results. score_threshold (float): The score threshold for results. All returned results have a score greater-than-or-equal-to this value. - image_scale (float, float): Scaling factor to apply to the bounding boxes - as (x-scale-factor, y-scale-factor), where each factor is from 0 to 1.0. + image_scale (float, float): Scaling factor to apply to the bounding boxes as + (x-scale-factor, y-scale-factor), where each factor is from 0 to 1.0. Returns: A list of :obj:`Object` objects, which each contains the detected object's id, score, and bounding box as :obj:`BBox`. """ - boxes = common.output_tensor(interpreter, 0)[0] - class_ids = common.output_tensor(interpreter, 1)[0] - scores = common.output_tensor(interpreter, 2)[0] - count = int(common.output_tensor(interpreter, 3)[0]) + # If a model has signature, we use the signature output tensor names to parse + # the results. Otherwise, we parse the results based on some assumption of the + # output tensor order and size. + # pylint: disable=protected-access + signature_list = interpreter._get_full_signature_list() + # pylint: enable=protected-access + if signature_list: + if len(signature_list) > 1: + raise ValueError('Only support model with one signature.') + signature = signature_list[next(iter(signature_list))] + count = int(interpreter.tensor(signature['outputs']['output_0'])()[0]) + scores = interpreter.tensor(signature['outputs']['output_1'])()[0] + class_ids = interpreter.tensor(signature['outputs']['output_2'])()[0] + boxes = interpreter.tensor(signature['outputs']['output_3'])()[0] + elif common.output_tensor(interpreter, 3).size == 1: + boxes = common.output_tensor(interpreter, 0)[0] + class_ids = common.output_tensor(interpreter, 1)[0] + scores = common.output_tensor(interpreter, 2)[0] + count = int(common.output_tensor(interpreter, 3)[0]) + else: + scores = common.output_tensor(interpreter, 0)[0] + boxes = common.output_tensor(interpreter, 1)[0] + count = (int)(common.output_tensor(interpreter, 2)[0]) + class_ids = common.output_tensor(interpreter, 3)[0] width, height = common.input_size(interpreter) image_scale_x, image_scale_y = image_scale @@ -200,9 +231,7 @@ def make(i): return Object( id=int(class_ids[i]), score=float(scores[i]), - bbox=BBox(xmin=xmin, - ymin=ymin, - xmax=xmax, + bbox=BBox(xmin=xmin, ymin=ymin, xmax=xmax, ymax=ymax).scale(sx, sy).map(int)) return [make(i) for i in range(count) if scores[i] >= score_threshold] diff --git a/pycoral/learn/imprinting/engine.py b/pycoral/learn/imprinting/engine.py index 2ac9b15..779a486 100644 --- a/pycoral/learn/imprinting/engine.py +++ b/pycoral/learn/imprinting/engine.py @@ -30,11 +30,13 @@ def __init__(self, model_path, keep_classes=False): """Performs weight imprinting (transfer learning) with the given model. Args: - model_path (str): Path to the model you want to retrain. This model must - be a ``.tflite`` file output by the ``join_tflite_models`` tool. For - more information about how to create a compatible model, read `Retrain - an image classification model on-device - `_. + model_path (str): Path to the ``.tflite`` model you want to retrain. + This must be a model that's specially-designed for this API. You + can use our `weight imprinting model + `_ that + has a pre-trained base model, or you can train the base model yourself + by following our guide to `Retrain the base MobileNet model + `_. keep_classes (bool): If True, keep the existing classes from the pre-trained model (and use training to add additional classes). If False, drop the existing classes and train the model to include new diff --git a/pycoral/pipeline/pipelined_model_runner.py b/pycoral/pipeline/pipelined_model_runner.py index 05e0be1..ee92f0d 100644 --- a/pycoral/pipeline/pipelined_model_runner.py +++ b/pycoral/pipeline/pipelined_model_runner.py @@ -68,17 +68,19 @@ def __init__(self, interpreters): self._interpreters = interpreters self._runner = _pywrap_coral.PipelinedModelRunnerWrapper( [i._native_handle() for i in interpreters]) - self._input_types = [ - d['dtype'] for d in self._interpreters[0].get_input_details() - ] - self._output_shapes = [ - d['shape'] for d in self._interpreters[-1].get_output_details() - ] + + self._input_types = {} + for d in self._interpreters[0].get_input_details(): + self._input_types[d['name']] = d['dtype'] + + self._output_shapes = {} + for d in self._interpreters[-1].get_output_details(): + self._output_shapes[d['name']] = d['shape'] def __del__(self): if self._runner: # Push empty request to stop the pipeline in case user forgot. - self.push([]) + self.push({}) num_unconsumed = 0 # Release any unconsumed tensors if any. while self.pop(): @@ -119,7 +121,7 @@ def set_output_queue_size(self, size): def push(self, input_tensors): """Pushes input tensors to trigger inference. - Pushing an empty list is allowed, which signals the class that no more + Pushing an empty dict is allowed, which signals the class that no more inputs will be added (the function will return false if inputs were pushed after this special push). This special push allows the ``pop()`` consumer to properly drain unconsumed output tensors. @@ -129,23 +131,25 @@ def push(self, input_tensors): size threshold is unlimited, in this case, call to push() is non-blocking. Args: - input_tensors: A list of :obj:`numpy.array` as the input for the given - model, in the appropriate order. + input_tensors: A dictionary with key of type string, and value of type + :obj:`numpy.array` representing the model's input tensors, where keys + are the tensor names. - Returns: - True if push is successful; False otherwise. + Raises: + RuntimeError: error during pushing pipelined model inference request. """ if input_tensors and len(input_tensors) != len(self._input_types): raise ValueError('Expected input of length {}, but got {}'.format( len(self._input_types), len(input_tensors))) - for tensor, input_type in zip(input_tensors, self._input_types): + for key, tensor in input_tensors.items(): + input_type = self._input_types[key] if not isinstance(tensor, np.ndarray) or tensor.dtype != input_type: raise ValueError( 'Input should be a list of numpy array of type {}'.format( input_type)) - return self._runner.Push(input_tensors) + self._runner.Push(input_tensors) def pop(self): """Returns a single inference result. @@ -153,13 +157,17 @@ def pop(self): This function blocks the calling thread until a result is returned. Returns: - List of :obj:`numpy.array` objects representing the model's output - tensor. Returns None when a ``push()`` receives an empty list, indicating + Dictionary with key of type string, and value of type :obj:`numpy.array` + representing the model's output tensors, where keys are the tensor names. + Returns None when a ``push()`` receives an empty dict input, indicating there are no more output tensors available. + + Raises: + RuntimeError: error during retrieving pipelined model inference results. """ result = self._runner.Pop() if result: - result = [r.reshape(s) for r, s in zip(result, self._output_shapes)] + result = {k: v.reshape(self._output_shapes[k]) for k, v in result.items()} return result def interpreters(self): diff --git a/pycoral/utils/dataset.py b/pycoral/utils/dataset.py index ceba145..caa141e 100644 --- a/pycoral/utils/dataset.py +++ b/pycoral/utils/dataset.py @@ -41,5 +41,5 @@ def read_label_file(file_path): if len(pair) == 2 and pair[0].strip().isdigit(): ret[int(pair[0])] = pair[1].strip() else: - ret[row_number] = pair[0].strip() + ret[row_number] = content.strip() return ret diff --git a/pycoral/utils/edgetpu.py b/pycoral/utils/edgetpu.py index cb3ff0e..c69aaf1 100644 --- a/pycoral/utils/edgetpu.py +++ b/pycoral/utils/edgetpu.py @@ -26,6 +26,7 @@ from pycoral.pybind._pywrap_coral import InvokeWithDmaBuffer as invoke_with_dmabuffer from pycoral.pybind._pywrap_coral import InvokeWithMemBuffer as invoke_with_membuffer from pycoral.pybind._pywrap_coral import ListEdgeTpus as list_edge_tpus +from pycoral.pybind._pywrap_coral import SetVerbosity as set_verbosity from pycoral.pybind._pywrap_coral import SupportsDmabuf as supports_dmabuf import platform import tflite_runtime.interpreter as tflite @@ -38,32 +39,52 @@ def load_edgetpu_delegate(options=None): - """Loads the Edge TPU delegate with the given options.""" + """Loads the Edge TPU delegate with the given options. + + Args: + options (dict): Options that are passed to the Edge TPU delegate, via + ``tf.lite.load_delegate``. The only option you should use is + "device", which defines the Edge TPU to use. Supported values are the same + as `device` in :func:`make_interpreter`. + Returns: + The Edge TPU delegate object. + """ return tflite.load_delegate(_EDGETPU_SHARED_LIB, options or {}) -def make_interpreter(model_path_or_content, device=None): - """Returns a new interpreter instance. +def make_interpreter(model_path_or_content, device=None, delegate=None): + """Creates a new ``tf.lite.Interpreter`` instance using the given model. - Interpreter is created from either model path or model content and attached - to an Edge TPU device. + **Note:** If you have multiple Edge TPUs, you should always specify the + ``device`` argument. Args: model_path_or_content (str or bytes): `str` object is interpreted as model path, `bytes` object is interpreted as model content. - device (str): The type of Edge TPU device you want: + device (str): The Edge TPU device you want: - + None -- use any Edge TPU - + ":" -- use N-th Edge TPU + + None -- use any Edge TPU (this is the default) + + ":" -- use N-th Edge TPU (this corresponds to the enumerated + index position from :func:`list_edge_tpus`) + "usb" -- use any USB Edge TPU + "usb:" -- use N-th USB Edge TPU + "pci" -- use any PCIe Edge TPU + "pci:" -- use N-th PCIe Edge TPU + If left as None, you cannot reliably predict which device you'll get. + So if you have multiple Edge TPUs and want to run a specific model on + each one, then you must specify the device. + delegate: A pre-loaded Edge TPU delegate object, as provided by + :func:`load_edgetpu_delegate`. If provided, the `device` argument + is ignored. + Returns: New ``tf.lite.Interpreter`` instance. """ - delegates = [load_edgetpu_delegate({'device': device} if device else {})] + if delegate: + delegates = [delegate] + else: + delegates = [load_edgetpu_delegate({'device': device} if device else {})] if isinstance(model_path_or_content, bytes): return tflite.Interpreter( model_content=model_path_or_content, experimental_delegates=delegates) diff --git a/scripts/build.sh b/scripts/build.sh index 838ebc6..bd003a5 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -18,7 +18,7 @@ set -ex readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly MAKEFILE="${SCRIPT_DIR}/../Makefile" readonly DOCKER_CPUS="${DOCKER_CPUS:=k8 aarch64 armv7a}" -PYTHON_VERSIONS="35 36 37 38" +PYTHON_VERSIONS="36 37 38 39" while [[ $# -gt 0 ]]; do case "$1" in @@ -39,10 +39,10 @@ done function docker_image { case $1 in - 35) echo "ubuntu:16.04" ;; 36) echo "ubuntu:18.04" ;; 37) echo "debian:buster" ;; 38) echo "ubuntu:20.04" ;; + 39) echo "debian:bullseye" ;; *) echo "Unsupported python version: $1" 1>&2; exit 1 ;; esac } diff --git a/scripts/build_deb.sh b/scripts/build_deb.sh index 9966ca1..727cebb 100755 --- a/scripts/build_deb.sh +++ b/scripts/build_deb.sh @@ -17,7 +17,7 @@ set -ex readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly MAKEFILE="${SCRIPT_DIR}/../Makefile" -readonly CMD="make deb tflite-deb && make -C libedgetpu_bin deb" +readonly CMD="make deb tflite-deb && LIBEDGETPU_BIN=../pycoral/libedgetpu_bin DIST_DIR=../pycoral/dist make -C ../libedgetpu deb" "${SCRIPT_DIR}/build.sh" make DOCKER_SHELL_COMMAND="${CMD}" -f "${MAKEFILE}" docker-shell diff --git a/scripts/runtime/install.sh b/scripts/runtime/install.sh deleted file mode 100755 index e4657da..0000000 --- a/scripts/runtime/install.sh +++ /dev/null @@ -1,187 +0,0 @@ -#!/bin/bash -# -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -if [[ -d "${SCRIPT_DIR}/libedgetpu" ]]; then - LIBEDGETPU_DIR="${SCRIPT_DIR}/libedgetpu" -else - LIBEDGETPU_DIR="${SCRIPT_DIR}/../../libedgetpu_bin" -fi - -function info { - echo -e "\033[0;32m${1}\033[0m" # green -} - -function warn { - echo -e "\033[0;33m${1}\033[0m" # yellow -} - -function error { - echo -e "\033[0;31m${1}\033[0m" # red -} - -function install_file { - local name="${1}" - local src="${2}" - local dst="${3}" - - info "Installing ${name} [${dst}]..." - if [[ -f "${dst}" ]]; then - warn "File already exists. Replacing it..." - rm -f "${dst}" - fi - cp -a "${src}" "${dst}" -} - -if [[ "${EUID}" != 0 ]]; then - error "Please use sudo to run as root." - exit 1 -fi - -if [[ -f /etc/mendel_version ]]; then - error "Looks like you're using a Coral Dev Board. You should instead use Debian packages to manage Edge TPU software." - exit 1 -fi - -readonly OS="$(uname -s)" -readonly MACHINE="$(uname -m)" - -if [[ "${OS}" == "Linux" ]]; then - case "${MACHINE}" in - x86_64) - HOST_GNU_TYPE=x86_64-linux-gnu - CPU=k8 - ;; - armv6l) - HOST_GNU_TYPE=arm-linux-gnueabihf - CPU=armv6 - ;; - armv7l) - HOST_GNU_TYPE=arm-linux-gnueabihf - CPU=armv7a - ;; - aarch64) - HOST_GNU_TYPE=aarch64-linux-gnu - CPU=aarch64 - ;; - *) - error "Your Linux platform is not supported." - exit 1 - ;; - esac -elif [[ "${OS}" == "Darwin" ]]; then - CPU=darwin - - MACPORTS_PATH_AUTO="$(command -v port || true)" - MACPORTS_PATH="${MACPORTS_PATH_AUTO:-/opt/local/bin/port}" - - BREW_PATH_AUTO="$(command -v brew || true)" - BREW_PATH="${BREW_PATH_AUTO:-/usr/local/bin/brew}" - - if [[ -x "${MACPORTS_PATH}" ]]; then - DARWIN_INSTALL_COMMAND="${MACPORTS_PATH}" - DARWIN_INSTALL_USER="$(whoami)" - elif [[ -x "${BREW_PATH}" ]]; then - DARWIN_INSTALL_COMMAND="${BREW_PATH}" - DARWIN_INSTALL_USER="${SUDO_USER}" - else - error "You need to install either Homebrew or MacPorts first." - exit 1 - fi -else - error "Your operating system is not supported." - exit 1 -fi - -cat << EOM -Warning: If you're using the Coral USB Accelerator, it may heat up during operation, depending -on the computation workloads and operating frequency. Touching the metal part of the USB -Accelerator after it has been operating for an extended period of time may lead to discomfort -and/or skin burns. As such, if you enable the Edge TPU runtime using the maximum operating -frequency, the USB Accelerator should be operated at an ambient temperature of 25°C or less. -Alternatively, if you enable the Edge TPU runtime using the reduced operating frequency, then -the device is intended to safely operate at an ambient temperature of 35°C or less. - -Google does not accept any responsibility for any loss or damage if the device -is operated outside of the recommended ambient temperature range. - -Note: This question affects only USB-based Coral devices, and is irrelevant for PCIe devices. -................................................................................ -Would you like to enable the maximum operating frequency for your Coral USB device? Y/N -EOM - -read USE_MAX_FREQ -case "${USE_MAX_FREQ}" in - [yY]) - info "Using the maximum operating frequency for Coral USB devices." - FREQ_DIR=direct - ;; - *) - info "Using the reduced operating frequency for Coral USB devices." - FREQ_DIR=throttled - ;; -esac - -if [[ "${CPU}" == "darwin" ]]; then - sudo -u "${DARWIN_INSTALL_USER}" "${DARWIN_INSTALL_COMMAND}" install libusb - - DARWIN_INSTALL_LIB_DIR="$(dirname "$(dirname "${DARWIN_INSTALL_COMMAND}")")/lib" - LIBEDGETPU_LIB_DIR="/usr/local/lib" - mkdir -p "${LIBEDGETPU_LIB_DIR}" - - install_file "Edge TPU runtime library" \ - "${LIBEDGETPU_DIR}/${FREQ_DIR}/darwin/libedgetpu.1.0.dylib" \ - "${LIBEDGETPU_LIB_DIR}" - - install_file "Edge TPU runtime library symlink" \ - "${LIBEDGETPU_DIR}/${FREQ_DIR}/darwin/libedgetpu.1.dylib" \ - "${LIBEDGETPU_LIB_DIR}" - - install_name_tool -id "${LIBEDGETPU_LIB_DIR}/libedgetpu.1.dylib" \ - "${LIBEDGETPU_LIB_DIR}/libedgetpu.1.0.dylib" - - install_name_tool -change "/opt/local/lib/libusb-1.0.0.dylib" \ - "${DARWIN_INSTALL_LIB_DIR}/libusb-1.0.0.dylib" \ - "${LIBEDGETPU_LIB_DIR}/libedgetpu.1.0.dylib" -else - for pkg in libc6 libgcc1 libstdc++6 libusb-1.0-0; do - if ! dpkg -l "${pkg}" > /dev/null; then - PACKAGES+=" ${pkg}" - fi - done - - if [[ -n "${PACKAGES}" ]]; then - info "Installing library dependencies:${PACKAGES}..." - apt-get update && apt-get install -y ${PACKAGES} - info "Done." - fi - - if [[ -x "$(command -v udevadm)" ]]; then - install_file "device rule file" \ - "${LIBEDGETPU_DIR}/edgetpu-accelerator.rules" \ - "/etc/udev/rules.d/99-edgetpu-accelerator.rules" - udevadm control --reload-rules && udevadm trigger - info "Done." - fi - - install_file "Edge TPU runtime library" \ - "${LIBEDGETPU_DIR}/${FREQ_DIR}/${CPU}/libedgetpu.so.1.0" \ - "/usr/lib/${HOST_GNU_TYPE}/libedgetpu.so.1.0" - ldconfig # Generates libedgetpu.so.1 symlink - info "Done." -fi diff --git a/scripts/runtime/uninstall.sh b/scripts/runtime/uninstall.sh deleted file mode 100755 index 9ec0126..0000000 --- a/scripts/runtime/uninstall.sh +++ /dev/null @@ -1,103 +0,0 @@ -#!/bin/bash -# -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -function info { - echo -e "\033[0;32m${1}\033[0m" # green -} - -function warn { - echo -e "\033[0;33m${1}\033[0m" # yellow -} - -function error { - echo -e "\033[0;31m${1}\033[0m" # red -} - -if [[ "${EUID}" != 0 ]]; then - error "Please use sudo to run as root." - exit 1 -fi - -if [[ -f /etc/mendel_version ]]; then - error "Looks like you're using a Coral Dev Board. You should instead use Debian packages to manage Edge TPU software." - exit 1 -fi - -readonly OS="$(uname -s)" -readonly MACHINE="$(uname -m)" - -if [[ "${OS}" == "Linux" ]]; then - case "${MACHINE}" in - x86_64) - HOST_GNU_TYPE=x86_64-linux-gnu - CPU_DIR=k8 - ;; - armv7l) - HOST_GNU_TYPE=arm-linux-gnueabihf - CPU_DIR=armv7a - ;; - aarch64) - HOST_GNU_TYPE=aarch64-linux-gnu - CPU_DIR=aarch64 - ;; - *) - error "Your Linux platform is not supported. There's nothing to uninstall." - exit 1 - ;; - esac -elif [[ "${OS}" == "Darwin" ]]; then - CPU=darwin -else - error "Your operating system is not supported. There's nothing to uninstall." - exit 1 -fi - -if [[ "${CPU}" == "darwin" ]]; then - LIBEDGETPU_LIB_DIR="/usr/local/lib" - - if [[ -f "${LIBEDGETPU_LIB_DIR}/libedgetpu.1.0.dylib" ]]; then - info "Uninstalling Edge TPU runtime library..." - rm -f "${LIBEDGETPU_LIB_DIR}/libedgetpu.1.0.dylib" - info "Done" - fi - - if [[ -L "${LIBEDGETPU_LIB_DIR}/libedgetpu.1.dylib" ]]; then - info "Uninstalling Edge TPU runtime library symlink..." - rm -f "${LIBEDGETPU_LIB_DIR}/libedgetpu.1.dylib" - info "Done" - fi -else - if [[ -x "$(command -v udevadm)" ]]; then - UDEV_RULE_PATH="/etc/udev/rules.d/99-edgetpu-accelerator.rules" - if [[ -f "${UDEV_RULE_PATH}" ]]; then - info "Uninstalling device rule file [${UDEV_RULE_PATH}]..." - rm -f "${UDEV_RULE_PATH}" - udevadm control --reload-rules && udevadm trigger - info "Done." - fi - fi - - LIBEDGETPU_DST="/usr/lib/${HOST_GNU_TYPE}/libedgetpu.so.1.0" - if [[ -f "${LIBEDGETPU_DST}" ]]; then - info "Uninstalling Edge TPU runtime library [${LIBEDGETPU_DST}]..." - rm -f "${LIBEDGETPU_DST}" - ldconfig - info "Done." - fi -fi diff --git a/scripts/windows/build.bat b/scripts/windows/build.bat index 505689e..42713e7 100644 --- a/scripts/windows/build.bat +++ b/scripts/windows/build.bat @@ -1,3 +1,17 @@ +:: Copyright 2019-2021 Google LLC +:: +:: Licensed under the Apache License, Version 2.0 (the "License"); +:: you may not use this file except in compliance with the License. +:: You may obtain a copy of the License at +:: +:: https://www.apache.org/licenses/LICENSE-2.0 +:: +:: Unless required by applicable law or agreed to in writing, software +:: distributed under the License is distributed on an "AS IS" BASIS, +:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +:: See the License for the specific language governing permissions and +:: limitations under the License. + echo off setlocal enabledelayedexpansion @@ -8,15 +22,12 @@ if defined BAZEL_OUTPUT_BASE ( set BAZEL_CMD=%BAZEL_CMD% --output_base=%BAZEL_OUTPUT_BASE% ) -set BAZEL_INFO_FLAGS=^ ---experimental_repo_remote_exec - set BAZEL_VS=C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools set BAZEL_VC=C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC call "%BAZEL_VC%\Auxiliary\Build\vcvars64.bat" -for /f %%i in ('%BAZEL_CMD% info %BAZEL_INFO_FLAGS% output_base') do set "BAZEL_OUTPUT_BASE=%%i" -for /f %%i in ('%BAZEL_CMD% info %BAZEL_INFO_FLAGS% output_path') do set "BAZEL_OUTPUT_PATH=%%i" +for /f %%i in ('%BAZEL_CMD% info output_base') do set "BAZEL_OUTPUT_BASE=%%i" +for /f %%i in ('%BAZEL_CMD% info output_path') do set "BAZEL_OUTPUT_PATH=%%i" for /f %%i in ('%PYTHON% -c "import sys;print(str(sys.version_info.major)+str(sys.version_info.minor))"') do set "PY3_VER=%%i" for /f %%i in ('%PYTHON% -c "import sys;print(sys.executable)"') do set "PYTHON_BIN_PATH=%%i" for /f %%i in ('%PYTHON% -c "import sys;print(sys.base_prefix)"') do set "PYTHON_LIB_PATH=%%i\Lib" @@ -51,16 +62,13 @@ if defined ARG ( goto PROCESSARGS ) -for /f "tokens=3" %%i in ('type %ROOTDIR%\WORKSPACE ^| findstr /C:"TENSORFLOW_COMMIT ="') do set "TENSORFLOW_COMMIT=%%i" +for /f "tokens=1" %%i in ('bazel query "@libedgetpu_properties//..." ^| findstr /C:"tensorflow_commit" ^| cut -d# -f2') do set "TENSORFLOW_COMMIT=%%i" set BAZEL_BUILD_FLAGS= ^ --compilation_mode=%COMPILATION_MODE% ^ --copt=/DNPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION ^ --copt=/D_HAS_DEPRECATED_RESULT_OF ^ --linkopt=/DEFAULTLIB:%LIBEDGETPU_DLL_PATH%.if.lib ^ ---experimental_repo_remote_exec ^ ---copt=/std:c++latest -set BAZEL_QUERY_FLAGS=^ ---experimental_repo_remote_exec +--copt=-D__PRETTY_FUNCTION__=__FUNCSIG__ rem PYBIND %BAZEL_CMD% build %BAZEL_BUILD_FLAGS% ^ @@ -80,6 +88,9 @@ if not exist %TFLITE_WRAPPER_OUT_DIR% md %TFLITE_WRAPPER_OUT_DIR% copy %BAZEL_OUT_DIR%\external\org_tensorflow\tensorflow\lite\python\interpreter_wrapper\_pywrap_tensorflow_interpreter_wrapper.pyd ^ %TFLITE_WRAPPER_PATH% >NUL copy %BAZEL_OUTPUT_BASE%\external\org_tensorflow\tensorflow\lite\python\interpreter.py %TFLITE_WRAPPER_OUT_DIR% +copy %BAZEL_OUTPUT_BASE%\external\org_tensorflow\tensorflow\lite\python\metrics_interface.py %TFLITE_WRAPPER_OUT_DIR% +copy %BAZEL_OUTPUT_BASE%\external\org_tensorflow\tensorflow\lite\python\metrics_portable.py %TFLITE_WRAPPER_OUT_DIR% +type NUL >%TFLITE_WRAPPER_OUT_DIR%\__init__.py :exit exit /b %ERRORLEVEL% diff --git a/scripts/windows/build_wheel.bat b/scripts/windows/build_wheel.bat index 6da516e..2d4cbc6 100644 --- a/scripts/windows/build_wheel.bat +++ b/scripts/windows/build_wheel.bat @@ -1,3 +1,17 @@ +:: Copyright 2019-2021 Google LLC +:: +:: Licensed under the Apache License, Version 2.0 (the "License"); +:: you may not use this file except in compliance with the License. +:: You may obtain a copy of the License at +:: +:: https://www.apache.org/licenses/LICENSE-2.0 +:: +:: Unless required by applicable law or agreed to in writing, software +:: distributed under the License is distributed on an "AS IS" BASIS, +:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +:: See the License for the specific language governing permissions and +:: limitations under the License. + echo off setlocal enabledelayedexpansion diff --git a/scripts/windows/clean.bat b/scripts/windows/clean.bat index 6e404f4..fee85cf 100644 --- a/scripts/windows/clean.bat +++ b/scripts/windows/clean.bat @@ -1,3 +1,17 @@ +:: Copyright 2019-2021 Google LLC +:: +:: Licensed under the Apache License, Version 2.0 (the "License"); +:: you may not use this file except in compliance with the License. +:: You may obtain a copy of the License at +:: +:: https://www.apache.org/licenses/LICENSE-2.0 +:: +:: Unless required by applicable law or agreed to in writing, software +:: distributed under the License is distributed on an "AS IS" BASIS, +:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +:: See the License for the specific language governing permissions and +:: limitations under the License. + echo off setlocal enabledelayedexpansion diff --git a/scripts/windows/docker_build.bat b/scripts/windows/docker_build.bat index a2f53f6..7bc2be6 100644 --- a/scripts/windows/docker_build.bat +++ b/scripts/windows/docker_build.bat @@ -1,3 +1,17 @@ +:: Copyright 2019-2021 Google LLC +:: +:: Licensed under the Apache License, Version 2.0 (the "License"); +:: you may not use this file except in compliance with the License. +:: You may obtain a copy of the License at +:: +:: https://www.apache.org/licenses/LICENSE-2.0 +:: +:: Unless required by applicable law or agreed to in writing, software +:: distributed under the License is distributed on an "AS IS" BASIS, +:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +:: See the License for the specific language governing permissions and +:: limitations under the License. + echo off setlocal enabledelayedexpansion diff --git a/scripts/windows/docker_make_image.bat b/scripts/windows/docker_make_image.bat index bc9cee0..917245f 100644 --- a/scripts/windows/docker_make_image.bat +++ b/scripts/windows/docker_make_image.bat @@ -1,6 +1,20 @@ +:: Copyright 2019-2021 Google LLC +:: +:: Licensed under the Apache License, Version 2.0 (the "License"); +:: you may not use this file except in compliance with the License. +:: You may obtain a copy of the License at +:: +:: https://www.apache.org/licenses/LICENSE-2.0 +:: +:: Unless required by applicable law or agreed to in writing, software +:: distributed under the License is distributed on an "AS IS" BASIS, +:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +:: See the License for the specific language governing permissions and +:: limitations under the License. + echo off setlocal enabledelayedexpansion -set ROOTDIR=%~dp0\..\..\ +set ROOTDIR=%~dp0\..\..\..\libcoral docker build -t edgetpu-win -f %ROOTDIR%\docker\Dockerfile.windows %ROOTDIR%\docker diff --git a/scripts/windows/docker_make_wheels.bat b/scripts/windows/docker_make_wheels.bat index 902e11e..5d08493 100644 --- a/scripts/windows/docker_make_wheels.bat +++ b/scripts/windows/docker_make_wheels.bat @@ -1,3 +1,17 @@ +:: Copyright 2019-2021 Google LLC +:: +:: Licensed under the Apache License, Version 2.0 (the "License"); +:: you may not use this file except in compliance with the License. +:: You may obtain a copy of the License at +:: +:: https://www.apache.org/licenses/LICENSE-2.0 +:: +:: Unless required by applicable law or agreed to in writing, software +:: distributed under the License is distributed on an "AS IS" BASIS, +:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +:: See the License for the specific language governing permissions and +:: limitations under the License. + echo off setlocal enabledelayedexpansion @@ -54,3 +68,14 @@ docker run -m %MEM_KB%KB --cpus %NUMBER_OF_PROCESSORS% --rm ^ -w c:\edgetpu ^ -e BAZEL_OUTPUT_BASE=c:\temp\edgetpu ^ -e PYTHON=c:\python38\python.exe edgetpu-win scripts\windows\build_wheel.bat + +rem Build Python 3.9 wheel +call %ROOTDIR%\scripts\windows\clean.bat +docker run -m %MEM_KB%KB --cpus %NUMBER_OF_PROCESSORS% --rm ^ + -v %ROOTDIR%:c:\edgetpu ^ + -v %TEST_DATA_DIR%:c:\edgetpu\test_data ^ + -v %LIBCORAL_DIR%:c:\edgetpu\libcoral ^ + -v %LIBEDGETPU_DIR%:c:\edgetpu\libedgetpu ^ + -w c:\edgetpu ^ + -e BAZEL_OUTPUT_BASE=c:\temp\edgetpu ^ + -e PYTHON=c:\python39\python.exe edgetpu-win scripts\windows\build_wheel.bat diff --git a/scripts/windows/install.bat b/scripts/windows/install.bat deleted file mode 100644 index 711d844..0000000 --- a/scripts/windows/install.bat +++ /dev/null @@ -1,54 +0,0 @@ -@echo off -setlocal enabledelayedexpansion - -rem Check for Admin privileges -fsutil dirty query %systemdrive% >NUL -if not %ERRORLEVEL% == 0 ( - powershell Start-Process -FilePath '%0' -ArgumentList "elevated" -verb runas - exit /b -) - -if exist "%~dp0\libedgetpu" ( - rem Running with the script in the root - set ROOTDIR=%~dp0 -) else ( - rem Running with the script in scripts\windows - set ROOTDIR=%~dp0\..\..\ -) - -cd /d "%ROOTDIR%" -set ROOTDIR=%CD% - -echo Warning: During normal operation, the Edge TPU Accelerator may heat up, -echo depending on the computation workloads and operating frequency. Touching the -echo metal part of the device after it has been operating for an extended period of -echo time may lead to discomfort and/or skin burns. As such, when running at the -echo default operating frequency, the device is intended to safely operate at an -echo ambient temperature of 35C or less. Or when running at the maximum operating -echo frequency, it should be operated at an ambient temperature of 25C or less. -echo. -echo Google does not accept any responsibility for any loss or damage if the device -echo is operated outside of the recommended ambient temperature range. -echo ................................................................................ -set /p USE_MAX_FREQ="Would you like to enable the maximum operating frequency for the USB Accelerator? Y/N " -if "%USE_MAX_FREQ%" == "y" set FREQ_DIR=direct -if "%USE_MAX_FREQ%" == "Y" set FREQ_DIR=direct -if not defined FREQ_DIR set FREQ_DIR=throttled - -echo Installing UsbDk -start /wait msiexec /i "%ROOTDIR%\third_party\usbdk\UsbDk_1.0.21_x64.msi" /quiet /qb! /norestart - -echo Installing Windows drivers -pnputil /add-driver "%ROOTDIR%\third_party\coral_accelerator_windows\*.inf" /install - -echo Installing performance counters -lodctr /M:"%ROOTDIR%\third_party\coral_accelerator_windows\coral.man" - -echo Copying edgetpu and libusb to System32 -copy "%ROOTDIR%\libedgetpu\%FREQ_DIR%\x64_windows\edgetpu.dll" c:\windows\system32 -copy "%ROOTDIR%\third_party\libusb_win\libusb-1.0.dll" c:\windows\system32 - -echo Install complete! -rem If %1 is elevated, this means we were re-invoked to gain Administrator. -rem In this case, we're in a new window, so call pause to allow the user to view output. -if "%1" == "elevated" pause diff --git a/scripts/windows/uninstall.bat b/scripts/windows/uninstall.bat deleted file mode 100644 index d54f67d..0000000 --- a/scripts/windows/uninstall.bat +++ /dev/null @@ -1,43 +0,0 @@ -@echo off -setlocal enabledelayedexpansion - -rem Check for Admin privileges -fsutil dirty query %systemdrive% >NUL -if not %ERRORLEVEL% == 0 ( - powershell Start-Process -FilePath '%0' -ArgumentList "elevated" -verb runas - exit /b -) - -if exist "%~dp0\libedgetpu" ( - rem Running with the script in the root - set ROOTDIR=%~dp0 -) else ( - rem Running with the script in scripts\windows - set ROOTDIR=%~dp0\..\..\ -) - -cd /d "%ROOTDIR%"" -set ROOTDIR=%CD% - -echo Deleting edgetpu and libusb from System32 -del c:\windows\system32\edgetpu.dll -del c:\windows\system32\libusb-1.0.dll - -echo Unistalling WinUSB drivers -for /f "tokens=3" %%a in ('pnputil /enum-devices /class {88bae032-5a81-49f0-bc3d-a4ff138216d6} ^| findstr /b "Driver Name:"') do ( - set infs=%%a !infs! -) -set infs=%infs:---=inf% -echo %infs% -for %%a in (%infs%) do ( - echo %%a - pnputil /delete-driver %%a /uninstall -) - -echo Uninstalling UsbDk -start /wait msiexec /x "%ROOTDIR%\third_party\usbdk\UsbDk_1.0.21_x64.msi" /quiet /qb! /norestart - -echo Uninstall complete! -rem If %1 is elevated, this means we were re-invoked to gain Administrator. -rem In this case, we're in a new window, so call pause to allow the user to view output. -if "%1" == "elevated" pause diff --git a/setup.py b/setup.py index 9c65691..a43cc2c 100644 --- a/setup.py +++ b/setup.py @@ -11,28 +11,33 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +"""Setup script for pycoral library.""" +import importlib.machinery import os import re -import importlib.machinery -from setuptools import setup, find_packages -from setuptools.dist import Distribution +from setuptools import find_packages +from setuptools import setup + def read(filename): path = os.path.join(os.path.abspath(os.path.dirname(__file__)), filename) - with open(path , 'r') as f: + with open(path, 'r', encoding='utf-8') as f: return f.read() + def find_version(text): match = re.search(r"^__version__\s*=\s*['\"](.*)['\"]\s*$", text, re.MULTILINE) return match.group(1) + setup( name='pycoral', description='Coral Python API', long_description=read('README.md'), + long_description_content_type='text/markdown', license='Apache 2', version=find_version(read('pycoral/__init__.py')), author='Coral', @@ -45,10 +50,10 @@ def find_version(text): 'Intended Audience :: Science/Research', 'License :: OSI Approved :: Apache Software License', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', 'Topic :: Scientific/Engineering', 'Topic :: Scientific/Engineering :: Mathematics', 'Topic :: Scientific/Engineering :: Artificial Intelligence', @@ -56,15 +61,20 @@ def find_version(text): 'Topic :: Software Development :: Libraries', 'Topic :: Software Development :: Libraries :: Python Modules', ], - packages=find_packages(), + packages=find_packages(exclude=['tflite_runtime']), package_data={ - '': [os.environ.get('WRAPPER_NAME', '*' + importlib.machinery.EXTENSION_SUFFIXES[-1])] + '': [ + os.environ.get('WRAPPER_NAME', + '*' + importlib.machinery.EXTENSION_SUFFIXES[-1]) + ] }, install_requires=[ - 'numpy>=1.12.1', + 'numpy>=1.16.0', 'Pillow>=4.0.0', - 'tflite-runtime==2.5.0', + 'tflite-runtime==2.5.0.post1', ], - **({'has_ext_modules': lambda: True} if 'WRAPPER_NAME' in os.environ else {}), + **({ + 'has_ext_modules': lambda: True + } if 'WRAPPER_NAME' in os.environ else {}), python_requires='>=3.5.2', - ) +) diff --git a/src/BUILD b/src/BUILD index a67c96a..6874888 100644 --- a/src/BUILD +++ b/src/BUILD @@ -1,10 +1,24 @@ +# Copyright 2019-2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + load("@org_tensorflow//tensorflow:tensorflow.bzl", "pybind_extension") package( default_visibility = ["//visibility:public"], ) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) config_setting( name = "windows", @@ -79,9 +93,10 @@ pybind_extension( "@libcoral//coral/pipeline:common", "@libcoral//coral/pipeline:pipelined_model_runner", "@libedgetpu//tflite/public:edgetpu", + "@local_config_python//:numpy_headers", + "@local_config_python//:python_headers", "@org_tensorflow//tensorflow/lite:stateful_error_reporter", "@org_tensorflow//tensorflow/lite/c:common", "@pybind11", - "@python", ], ) diff --git a/src/builddata.cc b/src/builddata.cc index 460109b..b1d4ab4 100644 --- a/src/builddata.cc +++ b/src/builddata.cc @@ -1,8 +1,23 @@ +/* Copyright 2019-2021 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + extern "C" const char kPythonWrapperBuildEmbedLabel[]; const char kPythonWrapperBuildEmbedLabel[] = BUILD_EMBED_LABEL; extern "C" const char kPythonWrapperBaseChangeList[]; -const char kPythonWrapperBaseChangeList[] = "CL_NUMBER=340495397"; +const char kPythonWrapperBaseChangeList[] = "CL_NUMBER=386924392"; namespace { // Build a type whose constructor will contain references to all the build data diff --git a/src/coral_wrapper.cc b/src/coral_wrapper.cc index e28bd0b..411a86e 100644 --- a/src/coral_wrapper.cc +++ b/src/coral_wrapper.cc @@ -1,3 +1,18 @@ +/* Copyright 2019-2021 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + // Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -224,13 +239,13 @@ PYBIND11_MODULE(_pywrap_coral, m) { throw std::runtime_error(std::string(status.message())); }, R"pbdoc( - Invoke the given ``tflite.Interpreter`` with a pointer to a native + Invoke the given ``tf.lite.Interpreter`` with a pointer to a native memory allocation. Works only for Edge TPU models running on PCIe TPU devices. Args: - interpreter: The ``tflite:Interpreter`` to invoke. + interpreter: The ``tf.lite:Interpreter`` to invoke. buffer (intptr_t): Pointer to memory buffer with input data. size (size_t): The buffer size. )pbdoc"); @@ -252,10 +267,10 @@ PYBIND11_MODULE(_pywrap_coral, m) { throw std::runtime_error(std::string(status.message())); }, R"pbdoc( - Invoke the given ``tflite.Interpreter`` with bytes as input. + Invoke the given ``tf.lite.Interpreter`` with bytes as input. Args: - interpreter: The ``tflite:Interpreter`` to invoke. + interpreter: The ``tf.lite:Interpreter`` to invoke. input_data (bytes): Raw bytes as input data. )pbdoc"); @@ -273,14 +288,14 @@ PYBIND11_MODULE(_pywrap_coral, m) { throw std::runtime_error(std::string(status.message())); }, R"pbdoc( - Invoke the given ``tflite.Interpreter`` using a given Linux dma-buf + Invoke the given ``tf.lite.Interpreter`` using a given Linux dma-buf file descriptor as an input tensor. Works only for Edge TPU models running on PCIe-based Coral devices. You can verify device support with ``supports_dmabuf()``. Args: - interpreter: The ``tflite:Interpreter`` to invoke. + interpreter: The ``tf.lite:Interpreter`` to invoke. dma_fd (int): DMA file descriptor. size (size_t): DMA buffer size. )pbdoc"); @@ -301,7 +316,7 @@ PYBIND11_MODULE(_pywrap_coral, m) { Checks whether the device supports Linux dma-buf. Args: - interpreter: The ``tflite:Interpreter`` that's bound to the + interpreter: The ``tf.lite:Interpreter`` that's bound to the Edge TPU you want to query. Returns: True if the device supports DMA buffers. @@ -335,7 +350,28 @@ PYBIND11_MODULE(_pywrap_coral, m) { Lists all available Edge TPU devices. Returns: - A list of dictionary, each representing a device record of type and path. + A list of dictionary items, each representing an Edge TPU in the system. + Each dictionary includes a "type" (either "usb" or "pci") and a + "path" (the device location in the system). Note: The order of the + Edge TPUs in this list are not guaranteed to be consistent across + system reboots. + )pbdoc"); + + m.def( + "SetVerbosity", + [](int verbosity) { + auto status = + edgetpu::EdgeTpuManager::GetSingleton()->SetVerbosity(verbosity); + return status == TfLiteStatus::kTfLiteOk; + }, + R"pbdoc( + Sets the verbosity of operating logs related to each Edge TPU. + 10 is the most verbose; 0 is the default. + + Args: + verbosity(int): Desired verbosity 0-10. + Returns: + A boolean indicating if verbosity was succesfully set. )pbdoc"); py::class_(m, "ImprintingEnginePythonWrapper") @@ -428,45 +464,56 @@ PYBIND11_MODULE(_pywrap_coral, m) { .def("SetOutputQueueSize", &coral::PipelinedModelRunner::SetOutputQueueSize) .def("Push", - [](coral::PipelinedModelRunner& self, py::list& list) -> bool { - std::vector input_tensors(list.size()); - for (int i = 0; i < list.size(); ++i) { - const auto info = list[i].cast().request(); + [](coral::PipelinedModelRunner& self, py::dict& input_tensor_dict) { + std::vector input_tensors( + input_tensor_dict.size()); + int i = 0; + for (const auto& item : input_tensor_dict) { + input_tensors[i].name = item.first.cast(); + const auto info = item.second.cast().request(); input_tensors[i].type = NumpyDtypeToTfLiteType(info.format); input_tensors[i].bytes = info.size * info.itemsize; input_tensors[i].buffer = self.GetInputTensorAllocator()->Alloc( input_tensors[i].bytes); std::memcpy(input_tensors[i].buffer->ptr(), info.ptr, input_tensors[i].bytes); + ++i; } // Release GIL because Push can be blocking (if input queue size is // bigger than input queue size threshold). py::gil_scoped_release release; - auto push_status = self.Push(input_tensors); + const auto push_status = self.Push(input_tensors); py::gil_scoped_acquire acquire; - return push_status; + if (!push_status.ok()) { + throw std::runtime_error(std::string(push_status.message())); + } }) .def("Pop", [](coral::PipelinedModelRunner& self) -> py::object { std::vector output_tensors; // Release GIL because Pop is blocking. py::gil_scoped_release release; - self.Pop(&output_tensors); + const auto pop_status = self.Pop(&output_tensors); py::gil_scoped_acquire acquire; + if (!pop_status.ok()) { + throw std::runtime_error(std::string(pop_status.message())); + } + if (output_tensors.empty()) { return py::none(); } - py::list result; + py::dict result; for (auto tensor : output_tensors) { // Underlying memory's ownership is passed to numpy object. py::capsule free_when_done(tensor.buffer->ptr(), [](void* ptr) { std::free(ptr); }); - result.append(py::array(TfLiteTypeToNumpyDtype(tensor.type), - /*shape=*/{tensor.bytes}, - /*strides=*/{1}, tensor.buffer->ptr(), - free_when_done)); + result[PyUnicode_DecodeLatin1(tensor.name.data(), tensor.name.size(), + nullptr)] = + py::array(TfLiteTypeToNumpyDtype(tensor.type), + /*shape=*/{tensor.bytes}, + /*strides=*/{1}, tensor.buffer->ptr(), free_when_done); self.GetOutputTensorAllocator()->Free(tensor.buffer); } return result; diff --git a/test_data b/test_data index c21de44..104342d 160000 --- a/test_data +++ b/test_data @@ -1 +1 @@ -Subproject commit c21de4450f88a20ac5968628d375787745932a5a +Subproject commit 104342d2d3480b3e66203073dac24f4e2dbb4c41 diff --git a/tests/classify_test.py b/tests/classify_test.py index 460de7d..faa1ca3 100644 --- a/tests/classify_test.py +++ b/tests/classify_test.py @@ -18,9 +18,8 @@ import unittest from pycoral.adapters import classify from pycoral.adapters import common -from pycoral.utils.edgetpu import make_interpreter -from tests.test_utils import coral_test_main -from tests.test_utils import test_data_path +from pycoral.utils import edgetpu +from tests import test_utils CHICKADEE = 20 TABBY_CAT = 282 @@ -31,7 +30,8 @@ def test_image(image_file, size): - return Image.open(test_data_path(image_file)).resize(size, Image.NEAREST) + return Image.open(test_utils.test_data_path(image_file)).resize( + size, Image.NEAREST) def rescale_image(image, image_quantization, tensor_quatization, tensor_dtype): @@ -51,11 +51,12 @@ def rescale(x): return rescale(image) -def classify_image(model_file, image_file, image_quantization=None): +def classify_image(model_file, delegate, image_file, image_quantization=None): """Runs image classification and returns result with the highest score. Args: model_file: string, model file name. + delegate: Edge TPU delegate. image_file: string, image file name. image_quantization: (scale: float, zero_point: float), assumed image quantization parameters. @@ -63,7 +64,8 @@ def classify_image(model_file, image_file, image_quantization=None): Returns: Classification result with the highest score as (index, score) tuple. """ - interpreter = make_interpreter(test_data_path(model_file)) + interpreter = edgetpu.make_interpreter( + test_utils.test_data_path(model_file), delegate=delegate) interpreter.allocate_tensors() image = test_image(image_file, common.input_size(interpreter)) @@ -98,27 +100,36 @@ def efficientnet(input_type): class TestClassify(unittest.TestCase): + @classmethod + def setUpClass(cls): + super(TestClassify, cls).setUpClass() + cls.delegate = edgetpu.load_edgetpu_delegate() + def test_mobilenet_v1_100_224(self): - index, score = classify_image(mobilenet_v1(1.0, 224), 'cat.bmp') + index, score = classify_image( + mobilenet_v1(1.0, 224), self.delegate, 'cat.bmp') self.assertEqual(index, EGYPTIAN_CAT) self.assertGreater(score, 0.78) def test_mobilenet_v1_050_160(self): - index, score = classify_image(mobilenet_v1(0.5, 160), 'cat.bmp') + index, score = classify_image( + mobilenet_v1(0.5, 160), self.delegate, 'cat.bmp') self.assertEqual(index, EGYPTIAN_CAT) self.assertGreater(score, 0.67) def test_mobilenet_v1_float_224(self): - index, score = classify_image(mobilenet_v1_float_io(1.0, 224), 'cat.bmp') + index, score = classify_image( + mobilenet_v1_float_io(1.0, 224), self.delegate, 'cat.bmp') self.assertEqual(index, EGYPTIAN_CAT) self.assertGreater(score, 0.7) def test_efficientnet_l(self): index, score = classify_image( - efficientnet('L'), 'cat.bmp', EFFICIENTNET_IMAGE_QUANTIZATION) + efficientnet('L'), self.delegate, 'cat.bmp', + EFFICIENTNET_IMAGE_QUANTIZATION) self.assertEqual(index, EGYPTIAN_CAT) - self.assertGreater(score, 0.65) + self.assertGreater(score, 0.45) if __name__ == '__main__': - coral_test_main() + test_utils.coral_test_main() diff --git a/tests/detect_test.py b/tests/detect_test.py index 05b4317..f5189c5 100644 --- a/tests/detect_test.py +++ b/tests/detect_test.py @@ -18,9 +18,8 @@ import unittest from pycoral.adapters import common from pycoral.adapters import detect -from pycoral.utils.edgetpu import make_interpreter -from tests.test_utils import coral_test_main -from tests.test_utils import test_data_path +from pycoral.utils import edgetpu +from tests import test_utils BBox = detect.BBox @@ -28,10 +27,11 @@ ABYSSINIAN = 0 # pet_labels.txt -def get_objects(model_file, image_file, score_threshold=0.0): - interpreter = make_interpreter(test_data_path(model_file)) +def get_objects(model_file, delegate, image_file, score_threshold=0.0): + interpreter = edgetpu.make_interpreter( + test_utils.test_data_path(model_file), delegate=delegate) interpreter.allocate_tensors() - image = Image.open(test_data_path(image_file)) + image = Image.open(test_utils.test_data_path(image_file)) _, scale = common.set_resized_input( interpreter, image.size, lambda size: image.resize(size, Image.ANTIALIAS)) interpreter.invoke() @@ -43,10 +43,17 @@ def face_model(): return 'ssd_mobilenet_v2_face_quant_postprocess_edgetpu.tflite' -def coco_model(version): +def tf1_coco_model(version): return 'ssd_mobilenet_v%d_coco_quant_postprocess_edgetpu.tflite' % version +def tf2_coco_model(version): + if version == 1: + return 'tf2_ssd_mobilenet_v1_fpn_640x640_coco17_ptq_edgetpu.tflite' + else: + return 'tf2_ssd_mobilenet_v2_coco17_ptq_edgetpu.tflite' + + def fine_tuned_model(): return 'ssd_mobilenet_v1_fine_tuned_pet_edgetpu.tflite' @@ -98,21 +105,26 @@ def test_union(self): class DetectTest(unittest.TestCase): - def assert_bbox_almost_equal(self, first, second, overlap_factor=0.95): + @classmethod + def setUpClass(cls): + super(DetectTest, cls).setUpClass() + cls.delegate = edgetpu.load_edgetpu_delegate() + + def assert_bbox_almost_equal(self, first, second, overlap_factor=0.8): self.assertGreaterEqual( BBox.iou(first, second), overlap_factor, msg='iou(%s, %s) is less than expected' % (first, second)) def test_face(self): - objs = get_objects(face_model(), 'grace_hopper.bmp') + objs = get_objects(face_model(), self.delegate, 'grace_hopper.bmp') self.assertEqual(len(objs), 1) self.assertGreater(objs[0].score, 0.996) self.assert_bbox_almost_equal(objs[0].bbox, BBox(xmin=125, ymin=40, xmax=402, ymax=363)) - def test_coco_v1(self): - objs = get_objects(coco_model(version=1), 'cat.bmp') + def test_tf1_coco_v1(self): + objs = get_objects(tf1_coco_model(version=1), self.delegate, 'cat.bmp') self.assertGreater(len(objs), 0) obj = objs[0] self.assertEqual(obj.id, CAT) @@ -120,8 +132,8 @@ def test_coco_v1(self): self.assert_bbox_almost_equal(obj.bbox, BBox(xmin=29, ymin=39, xmax=377, ymax=347)) - def test_coco_v2(self): - objs = get_objects(coco_model(version=2), 'cat.bmp') + def test_tf1_coco_v2(self): + objs = get_objects(tf1_coco_model(version=2), self.delegate, 'cat.bmp') self.assertGreater(len(objs), 0) obj = objs[0] self.assertEqual(obj.id, CAT) @@ -129,8 +141,26 @@ def test_coco_v2(self): self.assert_bbox_almost_equal(obj.bbox, BBox(xmin=43, ymin=35, xmax=358, ymax=333)) + def test_tf2_coco_v1(self): + objs = get_objects(tf2_coco_model(version=1), self.delegate, 'cat.bmp') + self.assertGreater(len(objs), 0) + obj = objs[0] + self.assertEqual(obj.id, CAT) + self.assertGreater(obj.score, 0.7) + self.assert_bbox_almost_equal(obj.bbox, + BBox(xmin=43, ymin=35, xmax=358, ymax=333)) + + def test_tf2_coco_v2(self): + objs = get_objects(tf2_coco_model(version=2), self.delegate, 'cat.bmp') + self.assertGreater(len(objs), 0) + obj = objs[0] + self.assertEqual(obj.id, CAT) + self.assertGreater(obj.score, 0.7) + self.assert_bbox_almost_equal(obj.bbox, + BBox(xmin=43, ymin=35, xmax=358, ymax=333)) + def test_fine_tuned(self): - objs = get_objects(fine_tuned_model(), 'cat.bmp') + objs = get_objects(fine_tuned_model(), self.delegate, 'cat.bmp') self.assertGreater(len(objs), 0) obj = objs[0] self.assertEqual(obj.id, ABYSSINIAN) @@ -140,4 +170,4 @@ def test_fine_tuned(self): if __name__ == '__main__': - coral_test_main() + test_utils.coral_test_main() diff --git a/tests/edgetpu_utils_test.py b/tests/edgetpu_utils_test.py index d68a218..77900cd 100644 --- a/tests/edgetpu_utils_test.py +++ b/tests/edgetpu_utils_test.py @@ -72,16 +72,22 @@ def required_input_array_size(interpreter): # Use --config=asan for better coverage. class TestEdgeTpuUtils(unittest.TestCase): + @classmethod + def setUpClass(cls): + super(TestEdgeTpuUtils, cls).setUpClass() + cls.delegate = edgetpu.load_edgetpu_delegate() + def _default_test_model_path(self): return test_utils.test_data_path( 'mobilenet_v1_1.0_224_quant_edgetpu.tflite') def test_load_from_model_file(self): - edgetpu.make_interpreter(self._default_test_model_path()) + edgetpu.make_interpreter( + self._default_test_model_path(), delegate=self.delegate) def test_load_from_model_content(self): with io.open(self._default_test_model_path(), 'rb') as model_file: - edgetpu.make_interpreter(model_file.read()) + edgetpu.make_interpreter(model_file.read(), delegate=self.delegate) def test_load_from_invalid_model_path(self): with self.assertRaisesRegex( @@ -144,14 +150,16 @@ def _run_inference_with_gst(self, interpreter, input_data): return np.copy(ret) def test_run_inference_with_different_types(self): - interpreter = edgetpu.make_interpreter(self._default_test_model_path()) + interpreter = edgetpu.make_interpreter( + self._default_test_model_path(), delegate=self.delegate) interpreter.allocate_tensors() input_size = required_input_array_size(interpreter) input_data = test_utils.generate_random_input(1, input_size) self._run_inference_with_different_input_types(interpreter, input_data) def test_run_inference_larger_input_size(self): - interpreter = edgetpu.make_interpreter(self._default_test_model_path()) + interpreter = edgetpu.make_interpreter( + self._default_test_model_path(), delegate=self.delegate) interpreter.allocate_tensors() input_size = required_input_array_size(interpreter) input_data = test_utils.generate_random_input(1, input_size + 1) @@ -159,7 +167,8 @@ def test_run_inference_larger_input_size(self): def test_compare_expected_and_larger_input_size(self): if _libgst: - interpreter = edgetpu.make_interpreter(self._default_test_model_path()) + interpreter = edgetpu.make_interpreter( + self._default_test_model_path(), delegate=self.delegate) interpreter.allocate_tensors() input_size = required_input_array_size(interpreter) larger_input_data = test_utils.generate_random_input(1, input_size + 1) @@ -171,7 +180,8 @@ def test_compare_expected_and_larger_input_size(self): print('Can not import gi. Skip test on Gst.Buffer input type.') def test_run_inference_smaller_input_size(self): - interpreter = edgetpu.make_interpreter(self._default_test_model_path()) + interpreter = edgetpu.make_interpreter( + self._default_test_model_path(), delegate=self.delegate) interpreter.allocate_tensors() input_size = required_input_array_size(interpreter) input_data = test_utils.generate_random_input(1, input_size - 1) @@ -180,7 +190,8 @@ def test_run_inference_smaller_input_size(self): self._run_inference_with_different_input_types(interpreter, input_data) def test_invoke_with_dma_buffer_model_not_ready(self): - interpreter = edgetpu.make_interpreter(self._default_test_model_path()) + interpreter = edgetpu.make_interpreter( + self._default_test_model_path(), delegate=self.delegate) input_size = 224 * 224 * 3 # Note: Exception is triggered because interpreter.allocate_tensors() is not # called. @@ -189,7 +200,8 @@ def test_invoke_with_dma_buffer_model_not_ready(self): edgetpu.invoke_with_dmabuffer(interpreter._native_handle(), 0, input_size) def test_invoke_with_mem_buffer_model_not_ready(self): - interpreter = edgetpu.make_interpreter(self._default_test_model_path()) + interpreter = edgetpu.make_interpreter( + self._default_test_model_path(), delegate=self.delegate) input_size = 224 * 224 * 3 np_input = np.zeros(input_size, dtype=np.uint8) # Note: Exception is triggered because interpreter.allocate_tensors() is not @@ -202,6 +214,12 @@ def test_invoke_with_mem_buffer_model_not_ready(self): def test_list_edge_tpu_paths(self): self.assertGreater(len(edgetpu.list_edge_tpus()), 0) + def test_set_verbosity(self): + # Simply sets the verbosity and ensure it returns success. + self.assertTrue(edgetpu.set_verbosity(10)) + # Returns the verbosity back to zero. + self.assertTrue(edgetpu.set_verbosity(0)) + if __name__ == '__main__': test_utils.coral_test_main() diff --git a/tests/imprinting_engine_test.py b/tests/imprinting_engine_test.py index 2c097a0..5b8e5b0 100644 --- a/tests/imprinting_engine_test.py +++ b/tests/imprinting_engine_test.py @@ -18,8 +18,8 @@ from pycoral.adapters import classify from pycoral.adapters import common -from pycoral.learn.imprinting.engine import ImprintingEngine -from pycoral.utils.edgetpu import make_interpreter +from pycoral.learn.imprinting import engine +from pycoral.utils import edgetpu from tests import test_utils import unittest @@ -39,13 +39,18 @@ def set_input(interpreter, image): class TestImprintingEnginePythonAPI(unittest.TestCase): + @classmethod + def setUpClass(cls): + super(TestImprintingEnginePythonAPI, cls).setUpClass() + cls.delegate = edgetpu.load_edgetpu_delegate() + def _train_and_test(self, model_path, train_points, test_points, keep_classes): # Train. - engine = ImprintingEngine(model_path, keep_classes) + imprinting_engine = engine.ImprintingEngine(model_path, keep_classes) - extractor = make_interpreter( - engine.serialize_extractor_model(), device=':0') + extractor = edgetpu.make_interpreter( + imprinting_engine.serialize_extractor_model(), delegate=self.delegate) extractor.allocate_tensors() for point in train_points: @@ -54,12 +59,12 @@ def _train_and_test(self, model_path, train_points, test_points, set_input(extractor, img) extractor.invoke() embedding = classify.get_scores(extractor) - self.assertEqual(len(embedding), engine.embedding_dim) - engine.train(embedding, point.class_id) + self.assertEqual(len(embedding), imprinting_engine.embedding_dim) + imprinting_engine.train(embedding, point.class_id) # Test. - trained_model = engine.serialize_model() - classifier = make_interpreter(trained_model, device=':0') + trained_model = imprinting_engine.serialize_model() + classifier = edgetpu.make_interpreter(trained_model, delegate=self.delegate) classifier.allocate_tensors() self.assertEqual(len(classifier.get_output_details()), 1) @@ -152,21 +157,21 @@ def test_imprinting_engine_saving_without_training(self): 'mobilenet_v1_1.0_224_l2norm_quant_edgetpu.tflite' ] for model in model_list: - engine = ImprintingEngine( + imprinting_engine = engine.ImprintingEngine( test_utils.test_data_path(model), keep_classes=False) with self.assertRaisesRegex(RuntimeError, 'Model is not trained.'): - engine.serialize_model() + imprinting_engine.serialize_model() def test_imprinting_engine_invalid_model_path(self): with self.assertRaisesRegex( ValueError, 'Failed to open file: invalid_model_path.tflite'): - ImprintingEngine('invalid_model_path.tflite') + engine.ImprintingEngine('invalid_model_path.tflite') def test_imprinting_engine_load_extractor_with_wrong_format(self): expected_message = ('Unsupported model architecture. Input model must have ' 'an L2Norm layer.') with self.assertRaisesRegex(ValueError, expected_message): - ImprintingEngine( + engine.ImprintingEngine( test_utils.test_data_path('mobilenet_v1_1.0_224_quant.tflite')) diff --git a/tests/imprinting_evaluation_test.py b/tests/imprinting_evaluation_test.py index 9fe8932..b06baf3 100644 --- a/tests/imprinting_evaluation_test.py +++ b/tests/imprinting_evaluation_test.py @@ -20,8 +20,8 @@ from pycoral.adapters import classify from pycoral.adapters import common -from pycoral.learn.imprinting.engine import ImprintingEngine -from pycoral.utils.edgetpu import make_interpreter +from pycoral.learn.imprinting import engine +from pycoral.utils import edgetpu from tests import test_utils import unittest @@ -35,6 +35,11 @@ def test_image(path): class ImprintingEngineEvaluationTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + super(ImprintingEngineEvaluationTest, cls).setUpClass() + cls.delegate = edgetpu.load_edgetpu_delegate() + def _transfer_learn_and_evaluate(self, model_path, keep_classes, dataset_path, test_ratio, top_k_range): """Transfer-learns with given params and returns the evaluation result. @@ -51,12 +56,13 @@ def _transfer_learn_and_evaluate(self, model_path, keep_classes, dataset_path, Returns: list of float numbers. """ - engine = ImprintingEngine(model_path, keep_classes) + imprinting_engine = engine.ImprintingEngine(model_path, keep_classes) - extractor = make_interpreter(engine.serialize_extractor_model()) + extractor = edgetpu.make_interpreter( + imprinting_engine.serialize_extractor_model(), delegate=self.delegate) extractor.allocate_tensors() - num_classes = engine.num_classes + num_classes = imprinting_engine.num_classes print('--------------- Parsing dataset ----------------') print('Dataset path:', dataset_path) @@ -85,16 +91,17 @@ def _transfer_learn_and_evaluate(self, model_path, keep_classes, dataset_path, with test_image(image) as img: common.set_input(extractor, img.resize(size, Image.NEAREST)) extractor.invoke() - engine.train(classify.get_scores(extractor), - class_id=num_classes + class_id) + imprinting_engine.train( + classify.get_scores(extractor), class_id=num_classes + class_id) print('---------------- Training finished -----------------') with test_utils.temporary_file(suffix='.tflite') as output_model_path: - output_model_path.write(engine.serialize_model()) + output_model_path.write(imprinting_engine.serialize_model()) # Evaluate print('---------------- Start evaluating -----------------') - classifier = make_interpreter(output_model_path.name) + classifier = edgetpu.make_interpreter( + output_model_path.name, delegate=self.delegate) classifier.allocate_tensors() # top[i] represents number of top (i+1) correct inference. diff --git a/tests/pipelined_model_runner_test.py b/tests/pipelined_model_runner_test.py index f419b23..ca2a513 100644 --- a/tests/pipelined_model_runner_test.py +++ b/tests/pipelined_model_runner_test.py @@ -33,9 +33,13 @@ def _get_ref_result(ref_model, input_tensors): output_details = interpreter.get_output_details() assert len(output_details) == 1 - interpreter.tensor(input_details[0]['index'])()[0][:, :] = input_tensors[0] + input_tensor, = input_tensors.values() + interpreter.tensor(input_details[0]['index'])()[0][:, :] = input_tensor interpreter.invoke() - return np.array(interpreter.tensor(output_details[0]['index'])()) + return { + output_details[0]['name']: + np.array(interpreter.tensor(output_details[0]['index'])()) + } def _get_devices(num_devices): @@ -66,7 +70,7 @@ def _get_devices(num_devices): ] -def _make_runner(model_paths, devices): +def _make_runner(model_paths, devices, allocate_tensors=True): print('Using devices: ', devices) print('Using models: ', model_paths) @@ -77,77 +81,92 @@ def _make_runner(model_paths, devices): make_interpreter(test_utils.test_data_path(m), d) for m, d in zip(model_paths, devices) ] - for interpreter in interpreters: - interpreter.allocate_tensors() + if allocate_tensors: + for interpreter in interpreters: + interpreter.allocate_tensors() return pipeline.PipelinedModelRunner(interpreters) class PipelinedModelRunnerTest(unittest.TestCase): - def setUp(self): - super(PipelinedModelRunnerTest, self).setUp() - model_segments = [ - 'pipeline/inception_v3_299_quant_segment_0_of_2_edgetpu.tflite', - 'pipeline/inception_v3_299_quant_segment_1_of_2_edgetpu.tflite', - ] + _MODEL_SEGMENTS = [ + 'pipeline/inception_v3_299_quant_segment_0_of_2_edgetpu.tflite', + 'pipeline/inception_v3_299_quant_segment_1_of_2_edgetpu.tflite', + ] + _REF_MODEL = 'inception_v3_299_quant_edgetpu.tflite' + + def _prepare_pipeline_inference(self, + model_segments, + ref_model=None, + allocate_tensors=True): self.runner = _make_runner(model_segments, - _get_devices(len(model_segments))) + _get_devices(len(model_segments)), + allocate_tensors) input_details = self.runner.interpreters()[0].get_input_details() self.assertEqual(len(input_details), 1) self.input_shape = input_details[0]['shape'] np.random.seed(0) - self.input_tensors = [ - np.random.randint(0, 256, size=self.input_shape, dtype=np.uint8) - ] + self.input_tensors = { + 'input': + np.random.randint(0, 256, size=self.input_shape, dtype=np.uint8) + } - ref_model = 'inception_v3_299_quant_edgetpu.tflite' - self.ref_result = _get_ref_result(ref_model, self.input_tensors) + if ref_model: + self.ref_result = _get_ref_result(ref_model, self.input_tensors) + else: + self.ref_result = None def test_bad_segments(self): - model_segments = [ + bad_model_segments = [ 'pipeline/inception_v3_299_quant_segment_1_of_2_edgetpu.tflite', 'pipeline/inception_v3_299_quant_segment_0_of_2_edgetpu.tflite', ] with self.assertRaisesRegex( ValueError, r'Interpreter [\d]+ can not get its input tensors'): - unused_runner = _make_runner(model_segments, [None] * len(model_segments)) + unused_runner = _make_runner(bad_model_segments, + [None] * len(self._MODEL_SEGMENTS)) def test_unsupported_input_type(self): + self._prepare_pipeline_inference(self._MODEL_SEGMENTS) with self.assertRaisesRegex( ValueError, 'Input should be a list of numpy array of type*'): - self.runner.push([np.random.random(self.input_shape)]) + self.runner.push({'input': np.random.random(self.input_shape)}) def test_check_unconsumed_tensor(self): # Everything should work fine without crashing. + self._prepare_pipeline_inference(self._MODEL_SEGMENTS) self.runner.push(self.input_tensors) def test_push_and_pop(self): - self.assertTrue(self.runner.push(self.input_tensors)) + self._prepare_pipeline_inference(self._MODEL_SEGMENTS, self._REF_MODEL) + self.runner.push(self.input_tensors) result = self.runner.pop() - self.assertEqual(len(result), 1) - np.testing.assert_equal(result[0], self.ref_result) + np.testing.assert_equal(result, self.ref_result) - # Check after [] is pushed. - self.assertTrue(self.runner.push([])) - self.assertFalse(self.runner.push(self.input_tensors)) + # Check after {} is pushed. + self.runner.push({}) + with self.assertRaisesRegex(RuntimeError, + 'Pipeline was turned off before.'): + self.runner.push(self.input_tensors) self.assertIsNone(self.runner.pop()) def test_producer_and_consumer_threads(self): + self._prepare_pipeline_inference(self._MODEL_SEGMENTS, self._REF_MODEL) num_requests = 5 def producer(self): for _ in range(num_requests): self.runner.push(self.input_tensors) - self.runner.push([]) + self.runner.push({}) def consumer(self): while True: result = self.runner.pop() if not result: break - np.testing.assert_equal(result[0], self.ref_result) + np.testing.assert_equal(result, self.ref_result) producer_thread = threading.Thread(target=producer, args=(self,)) consumer_thread = threading.Thread(target=consumer, args=(self,)) @@ -158,6 +177,7 @@ def consumer(self): consumer_thread.join() def test_set_input_and_output_queue_size(self): + self._prepare_pipeline_inference(self._MODEL_SEGMENTS) self.runner.set_input_queue_size(1) self.runner.set_output_queue_size(1) num_segments = len(self.runner.interpreters()) @@ -170,7 +190,7 @@ def test_set_input_and_output_queue_size(self): # Push `max_buffered_requests` to pipeline, such that the next `push` will # be blocking as there is no consumer to process the results at the moment. for _ in range(max_buffered_requests): - self.assertTrue(self.runner.push(self.input_tensors)) + self.runner.push(self.input_tensors) # Sleep for `max_buffered_requests` seconds to make sure the first request # already reaches the last segments. This assumes that it takes 1 second for @@ -178,8 +198,8 @@ def test_set_input_and_output_queue_size(self): time.sleep(max_buffered_requests) def push_new_request(self): - self.assertTrue(self.runner.push(self.input_tensors)) - self.assertTrue(self.runner.push([])) + self.runner.push(self.input_tensors) + self.runner.push({}) producer_thread = threading.Thread(target=push_new_request, args=(self,)) producer_thread.start() @@ -199,6 +219,21 @@ def push_new_request(self): producer_thread.join(1.0) self.assertFalse(producer_thread.is_alive()) + def test_interpreter_inference_error(self): + self._prepare_pipeline_inference( + self._MODEL_SEGMENTS, allocate_tensors=False) + # Interpreter error can only be caught by the first pop call. + self.runner.push(self.input_tensors) + with self.assertRaisesRegex( + RuntimeError, + 'Segment 0 runner error: Invoke called on model that is not ready.'): + self.runner.pop() + # Once error occurs, the following Push calls will fail. + with self.assertRaisesRegex( + RuntimeError, + 'Segment 0 runner error: Invoke called on model that is not ready.'): + self.runner.push(self.input_tensors) + if __name__ == '__main__': test_utils.coral_test_main() diff --git a/tests/segment_test.py b/tests/segment_test.py index d76caf6..7aae040 100644 --- a/tests/segment_test.py +++ b/tests/segment_test.py @@ -17,9 +17,8 @@ import unittest from pycoral.adapters import common from pycoral.adapters import segment -from pycoral.utils.edgetpu import make_interpreter -from tests.test_utils import coral_test_main -from tests.test_utils import test_data_path +from pycoral.utils import edgetpu +from tests import test_utils def deeplab_model_dm05(tpu): @@ -42,11 +41,12 @@ def array_iou(a, b): return count / (a.size + b.size - count) -def segment_image(model_file, image_file, mask_file): - interpreter = make_interpreter(test_data_path(model_file)) +def segment_image(model_file, delegate, image_file, mask_file): + interpreter = edgetpu.make_interpreter( + test_utils.test_data_path(model_file), delegate=delegate) interpreter.allocate_tensors() - image = Image.open(test_data_path(image_file)).resize( + image = Image.open(test_utils.test_data_path(image_file)).resize( common.input_size(interpreter), Image.ANTIALIAS) common.set_input(interpreter, image) interpreter.invoke() @@ -55,18 +55,23 @@ def segment_image(model_file, image_file, mask_file): if len(result.shape) > 2: result = np.argmax(result, axis=2) - reference = np.asarray(Image.open(test_data_path(mask_file))) + reference = np.asarray(Image.open(test_utils.test_data_path(mask_file))) return array_iou(result, reference) class SegmentTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + super(SegmentTest, cls).setUpClass() + cls.delegate = edgetpu.load_edgetpu_delegate() + def test_deeplab_dm10(self): for tpu in [False, True]: with self.subTest(tpu=tpu): self.assertGreater( segment_image( - deeplab_model_dm10(tpu), 'bird_segmentation.bmp', + deeplab_model_dm10(tpu), self.delegate, 'bird_segmentation.bmp', 'bird_segmentation_mask.bmp'), 0.90) def test_deeplab_dm05(self): @@ -74,7 +79,7 @@ def test_deeplab_dm05(self): with self.subTest(tpu=tpu): self.assertGreater( segment_image( - deeplab_model_dm05(tpu), 'bird_segmentation.bmp', + deeplab_model_dm05(tpu), self.delegate, 'bird_segmentation.bmp', 'bird_segmentation_mask.bmp'), 0.90) def test_keras_post_training_unet_mv2_128(self): @@ -82,18 +87,18 @@ def test_keras_post_training_unet_mv2_128(self): with self.subTest(tpu=tpu): self.assertGreater( segment_image( - keras_post_training_unet_mv2(tpu, 128), 'dog_segmentation.bmp', - 'dog_segmentation_mask.bmp'), 0.86) + keras_post_training_unet_mv2(tpu, 128), self.delegate, + 'dog_segmentation.bmp', 'dog_segmentation_mask.bmp'), 0.86) def test_keras_post_training_unet_mv2_256(self): for tpu in [False, True]: with self.subTest(tpu=tpu): self.assertGreater( segment_image( - keras_post_training_unet_mv2(tpu, 256), + keras_post_training_unet_mv2(tpu, 256), self.delegate, 'dog_segmentation_256.bmp', 'dog_segmentation_mask_256.bmp'), 0.81) if __name__ == '__main__': - coral_test_main() + test_utils.coral_test_main() diff --git a/tests/test_utils.py b/tests/test_utils.py index fd4953b..ed626df 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -29,6 +29,16 @@ _TEST_DATA_DIR = '' +def get_test_tmpdir(): + """Returns default test temp dir.""" + tmpdir = os.environ.get('TMPDIR', '') + if not tmpdir: + tmpdir = os.path.join(tempfile.gettempdir(), 'coral') + if not os.path.exists(tmpdir): + os.mkdir(tmpdir) + return tmpdir + + def test_data_path(path, *paths): """Returns absolute path for a given test file.""" return os.path.abspath(os.path.join(_TEST_DATA_DIR, path, *paths)) @@ -153,6 +163,7 @@ def coral_test_main(): test_data is under edgetpu/test_data. """ + print('Python version: ', sys.version) global _TEST_DATA_DIR test_data_dir_default = os.path.join( os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'test_data') diff --git a/third_party/python/BUILD b/third_party/python/BUILD deleted file mode 100644 index d371c5a..0000000 --- a/third_party/python/BUILD +++ /dev/null @@ -1,23 +0,0 @@ -config_setting( - name = "windows", - values = { - "cpu": "x64_windows", - } -) - -config_setting( - name = "darwin", - values = { - "cpu": "darwin", - } -) - -cc_library( - name = "python", - deps = select({ - ":windows": ["@python_windows"], - ":darwin": ["@python_darwin//:python3-headers"], - "//conditions:default": ["@python_linux//:python3-headers"], - }), - visibility = ["//visibility:public"], -) diff --git a/third_party/python/darwin/BUILD b/third_party/python/darwin/BUILD deleted file mode 100644 index 8253b9b..0000000 --- a/third_party/python/darwin/BUILD +++ /dev/null @@ -1,48 +0,0 @@ -config_setting( - name = "py35", - define_values = {"PY3_VER": "35"} -) - -config_setting( - name = "py36", - define_values = {"PY3_VER": "36"} -) - -config_setting( - name = "py37", - define_values = {"PY3_VER": "37"} -) - -config_setting( - name = "py38", - define_values = {"PY3_VER": "38"} -) - -# sudo port install python35 python36 python37 python38 -# sudo port install py35-numpy py36-numpy py37-numpy py38-numpy -cc_library( - name = "python3-headers", - hdrs = select({ - "py35": glob(["3.5/include/python3.5m/*.h", - "3.5/lib/python3.5/site-packages/numpy/core/include/numpy/*.h"]), - "py36": glob(["3.6/include/python3.6m/*.h", - "3.6/lib/python3.6/site-packages/numpy/core/include/numpy/*.h"]), - "py37": glob(["3.7/include/python3.7m/*.h", - "3.7/lib/python3.7/site-packages/numpy/core/include/numpy/*.h"]), - "py38": glob(["3.8/include/python3.8/*.h", - "3.8/include/python3.8/cpython/*.h", - "3.8/lib/python3.8/site-packages/numpy/core/include/numpy/*.h"]), - }, no_match_error = "PY3_VER is not specified"), - includes = select({ - "py35": ["3.5/include/python3.5m", - "3.5/lib/python3.5/site-packages/numpy/core/include"], - "py36": ["3.6/include/python3.6m", - "3.6/lib/python3.6/site-packages/numpy/core/include"], - "py37": ["3.7/include/python3.7m", - "3.7/lib/python3.7/site-packages/numpy/core/include"], - "py38": ["3.8/include/python3.8", - "3.8/include/python3.8/cpython", - "3.8/lib/python3.8/site-packages/numpy/core/include"], - }, no_match_error = "PY3_VER is not specified"), - visibility = ["//visibility:public"], -) diff --git a/third_party/python/linux/BUILD b/third_party/python/linux/BUILD deleted file mode 100644 index 8606801..0000000 --- a/third_party/python/linux/BUILD +++ /dev/null @@ -1,48 +0,0 @@ -config_setting( - name = "py35", - define_values = {"PY3_VER": "35"} -) - -config_setting( - name = "py36", - define_values = {"PY3_VER": "36"} -) - -config_setting( - name = "py37", - define_values = {"PY3_VER": "37"} -) - -config_setting( - name = "py38", - define_values = {"PY3_VER": "38"} -) - -cc_library( - name = "python3-headers", - hdrs = select({ - "py35": glob(["python3.5m/*.h", - "python3.5m/numpy/*.h", - "aarch64-linux-gnu/python3.5m/*.h", - "arm-linux-gnueabihf/python3.5m/*.h"]), - "py36": glob(["python3.6m/*.h", - "python3.6m/numpy/*.h", - "aarch64-linux-gnu/python3.6m/*.h", - "arm-linux-gnueabihf/python3.6m/*.h"]), - "py37": glob(["python3.7m/*.h", - "python3.7m/numpy/*.h", - "aarch64-linux-gnu/python3.7m/*.h", - "arm-linux-gnueabihf/python3.7m/*.h"]), - "py38": glob(["python3.8m/*.h", - "python3.8m/numpy/*.h", - "aarch64-linux-gnu/python3.8m/*.h", - "arm-linux-gnueabihf/python3.8m/*.h"]), - }, no_match_error = "PY3_VER is not specified"), - includes = select({ - "py35": [".", "python3.5m"], - "py36": [".", "python3.6m"], - "py37": [".", "python3.7m"], - "py38": [".", "python3.8m"], - }, no_match_error = "PY3_VER is not specified"), - visibility = ["//visibility:public"], -) diff --git a/third_party/python/windows/BUILD b/third_party/python/windows/BUILD deleted file mode 100644 index 1d4f23b..0000000 --- a/third_party/python/windows/BUILD +++ /dev/null @@ -1,8 +0,0 @@ -cc_library( - name = "python_windows", - deps = [ - "@local_config_python//:python_headers", - "@local_config_python//:numpy_headers", - ], - visibility = ["//visibility:public"], -)