Source code cleanup + README update
Change-Id: I4874f60bbd6ccf5c7878b44152ceabf0095f621c
diff --git a/README.md b/README.md
index fd1c20f..01a0eb1 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,10 @@
-# WebCoral
+# WebCoral demo
-This is a project to demonstrate how to access
-[Coral USB Accelerator](https://coral.ai/products/accelerator/) from
-Chrome browser using [WebUSB](https://wicg.github.io/webusb/).
-The demo works on Linux and macOS.
+This is a project to demonstrate how to use the
+[Coral USB Accelerator](https://coral.ai/products/accelerator/) from the Chrome
+browser with [WebUSB](https://wicg.github.io/webusb/).
+
+Currently, this demo is verified to work only on Linux and macOS.
## Web Server Setup
@@ -17,14 +18,14 @@
cd webcoral
```
-Download `.tflite` model files to `site/models`:
+Download `.tflite` model files to `site/models/`:
```
make download
```
-Build WASM files inside Docker container:
+Build WASM files inside a Docker container:
```
-DOCKER_SHELL_COMMAND="make wasm" make docker-shell
+DOCKER_SHELL_COMMAND="make COMPILATION_MODE=opt wasm" make docker-shell
```
Run local web server using python:
@@ -36,9 +37,11 @@
## System Setup
-On **Linux** you need to install device rules to make Coral USB device
+On **macOS**, you don't need to install anything else.
+
+On **Linux**, you need to install device rules to make the Coral USB Accelerator
visible in Chrome. You probably already have them installed if you are using
-Coral products. Just in case this repo has a bash script right for that:
+Coral products. If not, this repo has a bash script to install the device rules:
```
scripts/linux_device_rules.sh install
```
@@ -47,22 +50,20 @@
scripts/linux_device_rules.sh uninstall
```
-On **macOS** you don't need to install anything.
-
## Device Setup
-Coral USB accelerator doesn't have preinstalled firmware. If you run `lsusb`
-command right after plugging USB device in, you'll see
+To use the USB Accelerator from the web browser, you need to update the USB
+Accelerator's firmware as follows. The firmware is usually automatically flashed
+by [libedgetpu](https://github.com/google-coral/libedgetpu) library when using
+C++ or Python programs, but that’s not the case from the browser.
+
+If you run `lsusb` command right after plugging USB device in, you'll see:
```
Bus 001 Device 008: ID 1a6e:089a Global Unichip Corp.
```
This means firmware is not flashed yet. It is possible to flash firmware
-directly from Chrome or from command line. Either way after flashing you'll see:
-```
-Bus 001 Device 009: ID 18d1:9302 Google Inc.
-```
-which means that device is ready to use.
+directly from Chrome or using command line.
To flash firmware from command line:
```
@@ -74,10 +75,18 @@
There is an [issue](https://crbug.com/1189418) on macOS. It is already fixed and
the fix will be available in Chrome 91.
+Either way, you should now verify it's flashed by again running `lsusb` and you
+should see:
+```
+Bus 001 Device 009: ID 18d1:9302 Google Inc.
+```
+which means that device is ready to use.
+
+
## Demo
-Make sure the USB device was properly flashed. Choose the model you like to run
-and press `Initialize Interpreter` button. TPU model will require to select USB
-device in the dialog. CPU model will be ready immediately. At this moment you
-can run inference on any local image file by pressing `Choose Image File`
-button.
+Open Chrome at http://localhost:8000/. Choose the model you want to run and
+press the **Initialize Interpreter** button. Selecting an Edge TPU model then
+requires you to select the USB Accelerator in the dialog; selecting a CPU model
+will be ready immediately. At this moment, you can run inference on any local
+image file by pressing the **Choose Image File** button.
diff --git a/site/index.html b/site/index.html
index 0e381b2..ac3f674 100644
--- a/site/index.html
+++ b/site/index.html
@@ -1,93 +1,85 @@
<!DOCTYPE html>
<html>
<head>
- <meta charset="UTF-8">
- <title>Coral USB Accelerator + WebUSB</title>
- <script src="dfu.js"></script>
- <script src="dfuse.js"></script>
- <script src="models.js"></script>
- <script src="tflite.js"></script>
- <script src="interpreter.js"></script>
+ <meta charset='UTF-8'>
+ <title>Coral USB Accelerator Demo</title>
+ <script src='dfu.js'></script>
+ <script src='dfuse.js'></script>
+ <script src='models.js'></script>
+ <script src='tflite.js'></script>
+ <script src='interpreter.js'></script>
</head>
<body>
<h1>Coral USB Accelerator Demo</h1>
- <button id="button-firmware">1. Flash Device Firmware</button> <span id="firmware-status"></span><br/><br/>
- <button id="button-init">2. Initialize Interpreter</button>
- <select id="model">
- <option value="mobilenet_cpu">MobileNet V1 (CPU)</option>
- <option value="mobilenet_tpu">MobileNet V1 (TPU)</option>
- <option value="ssd_mobilenet_coco_cpu">SSD MobileNet V2 - Coco (CPU)</option>
- <option value="ssd_mobilenet_coco_tpu">SSD MobileNet V2 - Coco (TPU)</option>
- <option value="ssd_mobilenet_face_cpu">SSD MobileNet V2 - Face (CPU)</option>
- <option value="ssd_mobilenet_face_tpu">SSD MobileNet V2 - Face (TPU)</option>
- </select><br/><br/>
- <button id="button-image" onclick="document.getElementById('file').click();" disabled>3. Choose Image File</button><br/><br/>
- <input type="file" id="file" style="display:none;"/><br/>
- <h2 id="result"></h2>
- <canvas id="canvas"></canvas>
+ <button id='button-firmware'>1. Flash USB Firmware</button> <span id='firmware-status'></span>
+ <br/><br/>
+ <button id='button-init'>2. Initialize Interpreter</button>
+ <select id='model'>
+ <option value='mobilenet_tpu'>MobileNet V1 (TPU)</option>
+ <option value='mobilenet_cpu'>MobileNet V1 (CPU)</option>
+ <option value='ssd_mobilenet_coco_tpu'>SSD MobileNet V2 - Coco (TPU)</option>
+ <option value='ssd_mobilenet_coco_cpu'>SSD MobileNet V2 - Coco (CPU)</option>
+ <option value='ssd_mobilenet_face_tpu'>SSD MobileNet V2 - Face (TPU)</option>
+ <option value='ssd_mobilenet_face_cpu'>SSD MobileNet V2 - Face (CPU)</option>
+ </select>
+ <br/><br/>
+ <button id='button-image' onclick='document.getElementById("file").click();' disabled>3. Choose Image File</button>
+ <input type='file' id='file' style='display:none;'/>
+ <br/><br/>
+ <h2 id='result'></h2>
+ <canvas id='canvas'></canvas>
<br/><br/>
- <!--button id="test">Inference Test</button><br/><br/-->
<script>
- async function loadLocalImage(file) {
- return new Promise(resolve => {
- var reader = new FileReader();
- reader.onload = function() {
- var img = new Image();
- img.onload = function() {
- resolve(img);
- };
- img.src = reader.result;
- };
- reader.readAsDataURL(file);
- });
- }
-
+ // DFU functions are based on the code from
+ // https://github.com/devanlai/webdfu/blob/gh-pages/dfu-util/dfu-util.js
async function getDFUDescriptorProperties(device) {
let data = await device.readConfigurationDescriptor(0);
let configDesc = dfu.parseConfigurationDescriptor(data);
let funcDesc = null;
if (configDesc.bConfigurationValue == device.settings.configuration.configurationValue) {
- for (let desc of configDesc.descriptors) {
- if (desc.bDescriptorType == 0x21 && desc.hasOwnProperty("bcdDFUVersion")) {
- funcDesc = desc;
- break;
- }
+ for (let desc of configDesc.descriptors) {
+ if (desc.bDescriptorType == 0x21 && desc.hasOwnProperty('bcdDFUVersion')) {
+ funcDesc = desc;
+ break;
}
+ }
}
if (!funcDesc) return {};
return {
- WillDetach: ((funcDesc.bmAttributes & 0x08) != 0),
- ManifestationTolerant: ((funcDesc.bmAttributes & 0x04) != 0),
- CanUpload: ((funcDesc.bmAttributes & 0x02) != 0),
- CanDnload: ((funcDesc.bmAttributes & 0x01) != 0),
- TransferSize: funcDesc.wTransferSize,
- DetachTimeOut: funcDesc.wDetachTimeOut,
- DFUVersion: funcDesc.bcdDFUVersion
+ WillDetach: ((funcDesc.bmAttributes & 0x08) != 0),
+ ManifestationTolerant: ((funcDesc.bmAttributes & 0x04) != 0),
+ CanUpload: ((funcDesc.bmAttributes & 0x02) != 0),
+ CanDnload: ((funcDesc.bmAttributes & 0x01) != 0),
+ TransferSize: funcDesc.wTransferSize,
+ DetachTimeOut: funcDesc.wDetachTimeOut,
+ DFUVersion: funcDesc.bcdDFUVersion
};
}
async function connect(device) {
- await device.open();
+ await device.open();
- let desc = await getDFUDescriptorProperties(device);
- if (desc && Object.keys(desc).length > 0)
- device.properties = desc;
+ let desc = await getDFUDescriptorProperties(device);
+ if (desc && Object.keys(desc).length > 0)
+ device.properties = desc;
- device.logDebug = console.log;
- device.logInfo = console.log;
- device.logWarning = console.log;
- device.logError = console.log;
- device.logProgress = console.log;
+ device.logDebug = console.log;
+ device.logInfo = console.log;
+ device.logWarning = console.log;
+ device.logError = console.log;
+ device.logProgress = console.log;
- return device;
+ return device;
}
async function openDevice() {
try {
- let device = await navigator.usb.requestDevice({ 'filters': [{'vendorId': 0x1a6e, 'productId': 0x089a}] });
+ let device = await navigator.usb.requestDevice({
+ 'filters': [{'vendorId': 0x1a6e, 'productId': 0x089a}]
+ });
let interfaces = dfu.findDeviceDfuInterfaces(device);
if (interfaces.length != 1) return null;
return await connect(new dfu.Device(device, interfaces[0]));
@@ -96,19 +88,31 @@
}
}
+ async function loadLocalImage(file) {
+ return new Promise(resolve => {
+ var reader = new FileReader();
+ reader.onload = () => {
+ var img = new Image();
+ img.onload = () => { resolve(img); };
+ img.src = reader.result;
+ };
+ reader.readAsDataURL(file);
+ });
+ }
+
async function loadFile(url) {
return new Promise(resolve => {
let req = new XMLHttpRequest();
req.open('GET', url, true);
req.responseType = 'arraybuffer';
- req.onload = function (event) { resolve(req.response); };
+ req.onload = event => { resolve(req.response); };
req.send(null);
});
}
document.addEventListener('DOMContentLoaded', event => {
- let downloadButton = document.querySelector("#button-firmware");
- downloadButton.addEventListener('click', async function(event) {
+ let downloadButton = document.querySelector('#button-firmware');
+ downloadButton.addEventListener('click', async event => {
let firmwareFile = await loadFile('/firmware.bin');
let device = await openDevice();
if (device) {
@@ -118,7 +122,7 @@
}
if (device && firmwareFile) {
- document.getElementById("firmware-status").textContent = "Flashing...";
+ document.getElementById('firmware-status').textContent = 'Flashing...';
try {
let status = await device.getStatus();
@@ -126,7 +130,7 @@
await device.clearStatus();
}
} catch (error) {
- device.logWarning("Failed to clear status");
+ device.logWarning('Failed to clear status');
}
try {
@@ -134,40 +138,32 @@
await device.do_download(device.properties.TransferSize, firmwareFile, manifestationTolerant);
if (!manifestationTolerant)
await device.waitDisconnected(5000);
- document.getElementById("firmware-status").textContent = "Ready :)";
+ document.getElementById('firmware-status').textContent = 'Ready :)';
} catch (error) {
- document.getElementById("firmware-status").textContent = "Failed :(";
+ document.getElementById('firmware-status').textContent = 'Failed :(';
}
}
});
});
Module['onRuntimeInitialized'] = () => {
- let inference_test = Module.cwrap("inference_test", null, [], { async: true });
- let testButton = document.querySelector("#test");
- if (testButton) {
- testButton.addEventListener('click', async () => {
- inference_test();
- });
- }
-
let interpreter;
let model;
- document.querySelector("#button-init").addEventListener('click', async () => {
+ document.querySelector('#button-init').addEventListener('click', async () => {
model = TFLITE_MODELS[document.getElementById('model').value];
console.log(model);
interpreter = new tflite.Interpreter();
if (await interpreter.createFromBuffer(await loadFile(model.url))) {
- document.getElementById("button-firmware").disabled = true;
- document.getElementById("button-init").disabled = true;
- document.getElementById("model").disabled = true;
- document.getElementById("button-image").disabled = false;
+ document.getElementById('button-firmware').disabled = true;
+ document.getElementById('button-init').disabled = true;
+ document.getElementById('model').disabled = true;
+ document.getElementById('button-image').disabled = false;
}
});
- document.querySelector("#file").addEventListener('change', async () => {
- const input = document.getElementById("file");
+ document.querySelector('#file').addEventListener('change', async () => {
+ const input = document.getElementById('file');
const file = input.files[0];
if (!file) return;
@@ -175,7 +171,7 @@
console.log('SHAPE:', width, 'x', height);
const img = await loadLocalImage(file);
- const c = document.getElementById("canvas");
+ const c = document.getElementById('canvas');
c.width = width;
c.height = height;
const ctx = c.getContext('2d');
@@ -198,7 +194,7 @@
const imageData = ctx.getImageData(0, 0, width, height);
tflite.setRgbaInput(interpreter, imageData.data);
- document.getElementById("result").textContent = "Recognizing...";
+ document.getElementById('result').textContent = 'Recognizing...';
const inferenceStart = Date.now();
await interpreter.invoke();
const inferenceTime = Date.now() - inferenceStart;
@@ -207,14 +203,11 @@
switch (model.type) {
case 'classification':
const maxIndex = tflite.getClassificationOutput(interpreter);
- console.log('Class:', maxIndex);
label = model.labels[maxIndex];
break;
case 'detection':
const objects = tflite.getDetectionOutput(interpreter, threshold=0.5);
- console.log('Detected objects:', objects);
for (obj of objects) {
- console.log(obj);
const x = obj.bbox.xmin * width;
const y = obj.bbox.ymin * height;
const w = obj.bbox.xmax * width - x;
@@ -225,7 +218,7 @@
label = `${objects.length} ${objects.length == 1 ? 'object' : 'objects'}`;
break;
}
- document.getElementById("result").textContent = `${label}: ${inferenceTime} ms`;
+ document.getElementById('result').textContent = `${label}: ${inferenceTime} ms`;
});
};
Module['print'] = txt => console.log(txt);
diff --git a/site/tflite.js b/site/tflite.js
index 1547317..5bc2bb7 100644
--- a/site/tflite.js
+++ b/site/tflite.js
@@ -102,10 +102,10 @@
this.id = nextId++;
- Module['invokeDone'] = function(id) {
+ Module['invokeDone'] = function(id, result) {
const key = callbackKey(id);
const callback = Module[key];
- if (callback) callback();
+ if (callback) callback(result);
delete Module[key];
};
}
@@ -181,8 +181,10 @@
tflite.Interpreter.prototype.invoke = function() {
const self = this;
- return new Promise(resolve => {
- Module[callbackKey(self.id)] = resolve;
+ return new Promise((resolve, reject) => {
+ Module[callbackKey(self.id)] = result => {
+ (result ? resolve : reject)();
+ };
self.interpreter_invoke_async(self.interpreter, self.id);
});
}
diff --git a/tflite/interpreter.cc b/tflite/interpreter.cc
index fbda4d4..a227dfc 100644
--- a/tflite/interpreter.cc
+++ b/tflite/interpreter.cc
@@ -11,12 +11,12 @@
// 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.
-
#include <cstring>
#include <fstream>
#include <iostream>
#include <memory>
#include <thread>
+#include <utility>
#include <emscripten.h>
@@ -24,10 +24,8 @@
#include "tensorflow/lite/model_builder.h"
#include "tensorflow/lite/interpreter.h"
-//#include "tensorflow/lite/mutable_op_resolver.h"
#include "tensorflow/lite/interpreter_builder.h"
#include "tensorflow/lite/kernels/register.h" // BuiltinOpResolver
-//#include "tensorflow/lite/model.h"
#include "tflite/queue.h"
@@ -36,6 +34,9 @@
constexpr char kEdgeTpuCustomOp[] = "edgetpu-custom-op";
constexpr int kExit = -1;
+using DelegatePtr = std::unique_ptr<TfLiteDelegate,
+ decltype(&edgetpu_free_delegate)>;
+
bool HasCustomOp(const tflite::FlatBufferModel& model, const char* name) {
const auto* opcodes = model->operator_codes();
if (!opcodes) return false;
@@ -52,10 +53,11 @@
public:
Interpreter(): thread_([this]() {
while (true) {
- if (auto cmd = queue_.Pop(250)) {
+ if (auto cmd = queue_.Pop(250/*ms*/)) {
if (cmd.value() == kExit) break;
- Invoke();
- MAIN_THREAD_ASYNC_EM_ASM({Module['invokeDone']($0);}, cmd.value());
+ auto result = Invoke();
+ MAIN_THREAD_ASYNC_EM_ASM({Module['invokeDone']($0, $1);},
+ cmd.value(), result);
}
}
}) {}
@@ -76,7 +78,8 @@
}
bool Init(const char* model_buffer, size_t model_buffer_size, int verbosity) {
- auto model = tflite::FlatBufferModel::BuildFromBuffer(model_buffer, model_buffer_size);
+ auto model = tflite::FlatBufferModel::BuildFromBuffer(model_buffer,
+ model_buffer_size);
if (!model) {
std::cerr << "[ERROR] Cannot load model" << std::endl;
return false;
@@ -88,9 +91,6 @@
// Model
model_ = std::move(model);
- //tflite::MutableOpResolver resolver;
- //resolver.AddCustom("TFLite_Detection_PostProcess",
- // tflite::ops::custom::Register_DETECTION_POSTPROCESS());
tflite::ops::builtin::BuiltinOpResolver resolver;
if (tflite::InterpreterBuilder(*model_, resolver)(&interpreter_) != kTfLiteOk) {
std::cerr << "[ERROR] Cannot create interpreter" << std::endl;
@@ -110,10 +110,10 @@
auto& device = devices.get()[0];
edgetpu_option option = {"Usb.AlwaysDfu", "False"};
- auto* delegate = edgetpu_create_delegate(device.type, device.path, &option, 1);
-
- if (interpreter_->ModifyGraphWithDelegate(
- std::unique_ptr<TfLiteDelegate, decltype(&edgetpu_free_delegate)>(delegate, edgetpu_free_delegate)) != kTfLiteOk) {
+ DelegatePtr delegate(edgetpu_create_delegate(device.type, device.path,
+ &option, 1),
+ edgetpu_free_delegate);
+ if (interpreter_->ModifyGraphWithDelegate(std::move(delegate)) != kTfLiteOk) {
std::cerr << "[ERROR] Cannot apply EdgeTPU delegate" << std::endl;
return false;
}
@@ -162,10 +162,12 @@
}
public:
- void Invoke() {
+ bool Invoke() {
if (interpreter_->Invoke() != kTfLiteOk) {
- std::cout << "[ERROR] Cannot invoke interpreter" << std::endl;
+ std::cerr << "[ERROR] Cannot invoke interpreter" << std::endl;
+ return false;
}
+ return true;
}
void InvokeAsync(size_t id) {
@@ -181,73 +183,11 @@
} // namespace
-int main(int argc, char* argv[]) {
- std::cout << "main()" << std::endl;
- return 0;
-}
-
extern "C" {
-// --linkopt=--preload-file=$(MAKEFILE_DIR)/mobilenet_v1_1.0_224_quant_edgetpu.tflite@model.tflite
-// --linkopt=--preload-file=$(MAKEFILE_DIR)/cat.rgb@cat.rgb
-EMSCRIPTEN_KEEPALIVE void inference_test() {
- // auto* interpreter = new Interpreter();
- // interpreter->Init("model.tflite", 10);
-
- // std::ifstream file("cat.rgb", std::ios::binary);
- // if (!file) {
- // std::cout << "[ERROR] Cannot open file" << std::endl;
- // return;
- // }
-
- // if (!file.read(reinterpret_cast<char*>(interpreter->Input()),
- // interpreter->InputSize())) {
- // std::cout << "[ERROR] Cannot read file" << std::endl;
- // return;
- // }
-
- // std::thread th([interpreter]() {
- // auto index = interpreter->Invoke();
- // std::cout << "====> Result: " << index << std::endl;
- // delete interpreter;
- // });
- // th.detach();
-}
-
-// EMSCRIPTEN_KEEPALIVE void update_firmware() {
-// std::thread th([]() {
-// platforms::darwinn::api::Driver::Options options;
-
-// platforms::darwinn::api::Device device;
-// device.chip = platforms::darwinn::api::Chip::kBeagle;
-// device.type = platforms::darwinn::api::Device::Type::USB;
-// device.path = "default";
-
-// auto* factory = platforms::darwinn::api::DriverFactory::GetOrCreate();
-// std::cout << factory << std::endl;
-// auto driver = factory->CreateDriver(device);
-// if (!driver.ok()) {
-// std::cout << "[ERROR] Cannot create USB driver" << std::endl;
-// MAIN_THREAD_ASYNC_EM_ASM({Module['firmwareDone']($0);}, 0);
-// return;
-// }
-
-// auto* usb_driver = static_cast<platforms::darwinn::driver::UsbDriver*>(driver->get());
-// auto status = usb_driver->FlashUsbDevice();
-// if (!status.ok()) {
-// std::cout << "[ERROR] Cannot flash USB device" << std::endl;
-// MAIN_THREAD_ASYNC_EM_ASM({Module['firmwareDone']($0);}, 0);
-// return;
-// }
-
-// std::cout << "USB device is ready!" << std::endl;
-// MAIN_THREAD_ASYNC_EM_ASM({Module['firmwareDone']($0);}, 1);
-// return;
-// });
-// th.detach();
-// }
-
-EMSCRIPTEN_KEEPALIVE void* interpreter_create(const char* model_buffer, size_t model_buffer_size, int verbosity) {
+EMSCRIPTEN_KEEPALIVE
+void* interpreter_create(const char* model_buffer, size_t model_buffer_size,
+ int verbosity) {
auto* interpreter = new Interpreter();
if (!interpreter->Init(model_buffer, model_buffer_size, verbosity)) {
delete interpreter;
@@ -256,45 +196,55 @@
return interpreter;
}
-EMSCRIPTEN_KEEPALIVE void interpreter_destroy(void* p) {
+EMSCRIPTEN_KEEPALIVE
+void interpreter_destroy(void* p) {
delete reinterpret_cast<Interpreter*>(p);
}
// Inputs
-EMSCRIPTEN_KEEPALIVE size_t interpreter_num_inputs(void* interpreter) {
+EMSCRIPTEN_KEEPALIVE
+size_t interpreter_num_inputs(void* interpreter) {
return reinterpret_cast<Interpreter*>(interpreter)->NumInputs();
}
-EMSCRIPTEN_KEEPALIVE void* interpreter_input_buffer(void* interpreter, size_t tensor_index) {
+EMSCRIPTEN_KEEPALIVE
+void* interpreter_input_buffer(void* interpreter, size_t tensor_index) {
return reinterpret_cast<Interpreter*>(interpreter)->InputBuffer(tensor_index);
}
-EMSCRIPTEN_KEEPALIVE size_t interpreter_num_input_dims(void *interpreter, size_t tensor_index) {
+EMSCRIPTEN_KEEPALIVE
+size_t interpreter_num_input_dims(void *interpreter, size_t tensor_index) {
return reinterpret_cast<Interpreter*>(interpreter)->NumInputDims(tensor_index);
}
-EMSCRIPTEN_KEEPALIVE size_t interpreter_input_dim(void *interpreter, size_t tensor_index, size_t dim) {
+EMSCRIPTEN_KEEPALIVE
+size_t interpreter_input_dim(void *interpreter, size_t tensor_index, size_t dim) {
return reinterpret_cast<Interpreter*>(interpreter)->InputDim(tensor_index, dim);
}
// Outputs
-EMSCRIPTEN_KEEPALIVE size_t interpreter_num_outputs(void* interpreter) {
+EMSCRIPTEN_KEEPALIVE
+size_t interpreter_num_outputs(void* interpreter) {
return reinterpret_cast<Interpreter*>(interpreter)->NumOutputs();
}
-EMSCRIPTEN_KEEPALIVE const void* interpreter_output_buffer(void* interpreter, size_t tensor_index) {
+EMSCRIPTEN_KEEPALIVE
+const void* interpreter_output_buffer(void* interpreter, size_t tensor_index) {
return reinterpret_cast<Interpreter*>(interpreter)->OutputBuffer(tensor_index);
}
-EMSCRIPTEN_KEEPALIVE size_t interpreter_num_output_dims(void *interpreter, size_t tensor_index) {
+EMSCRIPTEN_KEEPALIVE
+size_t interpreter_num_output_dims(void *interpreter, size_t tensor_index) {
return reinterpret_cast<Interpreter*>(interpreter)->NumOutputDims(tensor_index);
}
-EMSCRIPTEN_KEEPALIVE size_t interpreter_output_dim(void *interpreter, size_t tensor_index, size_t dim) {
+EMSCRIPTEN_KEEPALIVE
+size_t interpreter_output_dim(void *interpreter, size_t tensor_index, size_t dim) {
return reinterpret_cast<Interpreter*>(interpreter)->OutputDim(tensor_index, dim);
}
-EMSCRIPTEN_KEEPALIVE void interpreter_invoke_async(void *interpreter, size_t id) {
+EMSCRIPTEN_KEEPALIVE
+void interpreter_invoke_async(void *interpreter, size_t id) {
return reinterpret_cast<Interpreter*>(interpreter)->InvokeAsync(id);
}
diff --git a/tflite/libusb.cc b/tflite/libusb.cc
index 18ac6f6..0445998 100644
--- a/tflite/libusb.cc
+++ b/tflite/libusb.cc
@@ -11,7 +11,6 @@
// 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.
-
#include <iostream>
#include <emscripten.h>
@@ -25,8 +24,7 @@
#define LIBUSB_NANO 0
#define LIBUSB_RC ""
-//#define LIBUSB_ENABLE_LOG
-
+// #define LIBUSB_ENABLE_LOG
#ifdef LIBUSB_ENABLE_LOG
#define LIBUSB_LOG(...) \
do { \
@@ -41,7 +39,6 @@
uint8_t bus_number;
uint8_t port_number;
struct libusb_context *ctx;
-
struct libusb_device_descriptor descriptor;
};
@@ -54,7 +51,7 @@
struct libusb_device *dev;
};
-static const struct libusb_version libusb_version_internal = {
+static const struct libusb_version kVersion = {
LIBUSB_MAJOR,
LIBUSB_MINOR,
LIBUSB_MICRO,
@@ -66,12 +63,9 @@
static int js_request_device(struct libusb_device *dev) {
return MAIN_THREAD_EM_ASM_INT({
return Asyncify.handleAsync(async () => {
- //console.log('js_request_device');
-
// Bus 001 Device 005: ID 1a6e:089a Global Unichip Corp.
// Bus 002 Device 007: ID 18d1:9302 Google Inc.
let options = {'filters': [{'vendorId': 0x18d1, 'productId': 0x9302}]};
-
let devices = await navigator.usb.getDevices();
if (!devices.length) {
try {
@@ -82,13 +76,11 @@
}
}
- //console.log('js_request_device, device list:', devices);
-
if (devices.length === 1) {
let d = devices[0];
_fill_device($0,
/*bcdUSB=*/(d.usbVersionMajor << 8) | d.usbVersionMinor,
- /*bDeviceClass*/d.deviceClass,
+ /*bDeviceClass=*/d.deviceClass,
/*bDeviceSubClass=*/d.deviceSubClass,
/*bDeviceProtocol=*/d.deviceProtocol,
/*idVendor=*/d.vendorId,
@@ -103,12 +95,11 @@
}, dev);
}
-static int js_control_transfer(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue,
- uint16_t wIndex, uint8_t *data, uint16_t wLength, unsigned int timeout) {
+static int js_control_transfer(uint8_t bmRequestType, uint8_t bRequest,
+ uint16_t wValue, uint16_t wIndex, uint8_t *data,
+ uint16_t wLength, unsigned int timeout) {
return MAIN_THREAD_EM_ASM_INT({
return Asyncify.handleAsync(async () => {
- //console.log('js_control_transfer');
-
let bmRequestType = $0;
let bRequest = $1;
let wValue = $2;
@@ -128,8 +119,8 @@
let dir_in = (bmRequestType & 0x80) == 0x80;
if (dir_in) {
let result = await this.libusb_device.controlTransferIn(setup, wLength);
- if (result.status != "ok") {
- out("JS[js_control_transfer]: <ERROR> controlTransferIn response: " + result.status);
+ if (result.status != 'ok') {
+ console.error('controlTransferIn', result);
return 0;
}
@@ -139,11 +130,11 @@
} else {
let buffer = new Uint8Array(wLength);
for (let i = 0; i < wLength; ++i)
- buffer[i] = getValue(data + i, "i8");
+ buffer[i] = getValue(data + i, 'i8');
let result = await this.libusb_device.controlTransferOut(setup, buffer);
- if(result.status != "ok") {
- out("JS[js_control_transfer]: <ERROR> controlTransferOut response: " + result.status);
+ if (result.status != 'ok') {
+ console.error('controlTransferOut', result);
return 0;
}
return result.bytesWritten;
@@ -152,22 +143,34 @@
}, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout);
}
-static void print_device_descriptor(const struct libusb_device_descriptor* d) {
- std::cout << "Descriptor" << std::endl;
- std::cout << " bLength: " << static_cast<int>(d->bLength) << std::endl;
- std::cout << " bDescriptorType: " << static_cast<int>(d->bDescriptorType) << std::endl;
- std::cout << " bcdUSB: 0x" << std::hex << static_cast<int>(d->bcdUSB) << std::dec << std::endl;
- std::cout << " bDeviceClass: " << static_cast<int>(d->bDeviceClass) << std::endl;
- std::cout << " bDeviceSubClass: " << static_cast<int>(d->bDeviceSubClass) << std::endl;
- std::cout << " bDeviceProtocol: " << static_cast<int>(d->bDeviceProtocol) << std::endl;
- std::cout << " bMaxPacketSize0: " << static_cast<int>(d->bMaxPacketSize0) << std::endl;
- std::cout << " idVendor: 0x" << std::hex << static_cast<int>(d->idVendor) << std::dec << std::endl;
- std::cout << " idProduct: 0x" << std::hex << static_cast<int>(d->idProduct) << std::dec << std::endl;
- std::cout << " bcdDevice: 0x" << std::hex << static_cast<int>(d->bcdDevice) << std::dec << std::endl;
- std::cout << " iManufacturer: " << static_cast<int>(d->iManufacturer) << std::endl;
- std::cout << " iProduct: " << static_cast<int>(d->iProduct) << std::endl;
- std::cout << " iSerialNumber: " << static_cast<int>(d->iSerialNumber) << std::endl;
- std::cout << " bNumConfigurations: " << static_cast<int>(d->bNumConfigurations) << std::endl;
+static void print(const char* line) { std::cout << line << std::endl; }
+
+static void print(const char* line, int value, bool hex=false) {
+ if (hex)
+ std::cout << line << std::hex << value << std::dec << std::endl;
+ else
+ std::cout << line << value << std::endl;
+}
+
+static void print_device(const struct libusb_device* dev) {
+ print("USB Device");
+ print(" Bus: ", dev->bus_number);
+ print(" Port: ", dev->port_number);
+ print(" Descriptor");
+ print(" bLength: ", dev->descriptor.bLength);
+ print(" bDescriptorType: ", dev->descriptor.bDescriptorType);
+ print(" bcdUSB: 0x", dev->descriptor.bcdUSB, /*hex=*/true);
+ print(" bDeviceClass: ", dev->descriptor.bDeviceClass);
+ print(" bDeviceSubClass: ", dev->descriptor.bDeviceSubClass);
+ print(" bDeviceProtocol: ", dev->descriptor.bDeviceProtocol);
+ print(" bMaxPacketSize0: ", dev->descriptor.bMaxPacketSize0);
+ print(" idVendor: 0x", dev->descriptor.idVendor, /*hex=*/true);
+ print(" idProduct: 0x", dev->descriptor.idProduct, /*hex=*/true);
+ print(" bcdDevice: 0x", dev->descriptor.bcdDevice, /*hex=*/true);
+ print(" iManufacturer: ", dev->descriptor.iManufacturer);
+ print(" iProduct: ", dev->descriptor.iProduct);
+ print(" iSerialNumber: ", dev->descriptor.iSerialNumber);
+ print(" bNumConfigurations: ", dev->descriptor.bNumConfigurations);
}
extern "C" {
@@ -188,12 +191,12 @@
}
void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level) {
- LIBUSB_LOG("<NOT IMPLEMENTED> libusb_set_debug");
+ LIBUSB_LOG("libusb_set_debug [NOT IMPLEMENTED]");
}
const struct libusb_version* LIBUSB_CALL libusb_get_version() {
LIBUSB_LOG("libusb_get_version");
- return &libusb_version_internal;
+ return &kVersion;
}
struct libusb_transfer* LIBUSB_CALL libusb_alloc_transfer(int iso_packets) {
@@ -202,7 +205,7 @@
size_t size = sizeof(struct libusb_transfer) +
sizeof(struct libusb_iso_packet_descriptor) * iso_packets;
- return reinterpret_cast<libusb_transfer*>(calloc(1, size));;
+ return reinterpret_cast<libusb_transfer*>(calloc(1, size));
}
int LIBUSB_CALL libusb_submit_transfer(struct libusb_transfer *transfer) {
@@ -216,29 +219,25 @@
case LIBUSB_TRANSFER_TYPE_INTERRUPT:
if (dir_in) {
MAIN_THREAD_ASYNC_EM_ASM({
- //console.log('js_submit_bulk_in_transfer, endpoint: ', $0);
-
this.libusb_device.transferIn($0, $2).then(function(result) {
var data = new Uint8Array(result.data.buffer);
writeArrayToMemory(data, $1);
_set_transfer_completed($3, data.length);
}).catch(function(error) {
- console.error('js_submit_bulk_in_transfer:', error);
+ console.error('transferIn', error);
_set_transfer_error($3);
});
}, endpoint, transfer->buffer, transfer->length, transfer);
} else {
MAIN_THREAD_ASYNC_EM_ASM({
- //console.log('js_submit_bulk_out_transfer, endpoint: ', $0);
-
var data = new Uint8Array($2);
for(let i = 0; i < $2; ++i)
- data[i] = getValue($1 + i, "i8");
+ data[i] = getValue($1 + i, 'i8');
this.libusb_device.transferOut($0, data).then(function(result) {
_set_transfer_completed($3, result.bytesWritten);
}).catch(function(error) {
- console.error('js_submit_bulk_out_transfer:', error);
+ console.error('transferOut', error);
_set_transfer_error($3);
});
}, endpoint, transfer->buffer, transfer->length, transfer);
@@ -252,7 +251,7 @@
}
int LIBUSB_CALL libusb_cancel_transfer(struct libusb_transfer *transfer) {
- LIBUSB_LOG("<NOT IMPLEMENTED> libusb_cancel_transfer");
+ LIBUSB_LOG("libusb_cancel_transfer [NOT IMPLEMENTED]");
return 0;
}
@@ -280,11 +279,11 @@
}
int LIBUSB_CALL libusb_handle_events(libusb_context *ctx) {
- //std::cout << "libusb_handle_events" << std::endl;
+ LIBUSB_LOG("libusb_handle_events");
+
while (true) {
- if (auto item = ctx->completed_transfers.Pop(25)) {
+ if (auto item = ctx->completed_transfers.Pop(25/*ms*/)) {
auto* transfer = item.value();
- //std::cout << "== Running callback ==" << std::endl;
transfer->callback(transfer);
} else {
break;
@@ -299,51 +298,50 @@
return MAIN_THREAD_EM_ASM_INT({
return Asyncify.handleAsync(async () => {
try {
- //console.log('js_reset_device');
await this.libusb_device.reset();
return 0; // LIBUSB_SUCCESS
} catch (error) {
- console.error('js_reset_device:', error);
- //return -1; // LIBUSB_ERROR_IO
- return 0; // TODO(dkovalev)
+ console.error('reset', error);
+ // TODO: return -1; // LIBUSB_ERROR_IO
+ return 0; // LIBUSB_SUCCESS
}
});
});
}
int LIBUSB_CALL libusb_control_transfer(libusb_device_handle *dev_handle,
- uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
- unsigned char *data, uint16_t wLength, unsigned int timeout) {
+ uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
+ unsigned char *data, uint16_t wLength, unsigned int timeout) {
LIBUSB_LOG("libusb_control_transfer");
- return js_control_transfer(request_type, bRequest, wValue, wIndex, data, wLength, timeout);
+ return js_control_transfer(request_type, bRequest, wValue, wIndex, data,
+ wLength, timeout);
}
int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle *dev_handle,
- unsigned char endpoint, unsigned char *data, int length,
- int *actual_length, unsigned int timeout) {
- LIBUSB_LOG("<NOT IMPLEMENTED> libusb_bulk_transfer");
+ unsigned char endpoint, unsigned char *data, int length,
+ int *actual_length, unsigned int timeout) {
+ LIBUSB_LOG("libusb_bulk_transfer [NOT IMPLEMENTED]");
return 0;
}
int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle,
- unsigned char endpoint, unsigned char *data, int length,
- int *actual_length, unsigned int timeout) {
- LIBUSB_LOG("<NOT IMPLEMENTED> libusb_interrupt_transfer");
+ unsigned char endpoint, unsigned char *data, int length,
+ int *actual_length, unsigned int timeout) {
+ LIBUSB_LOG("libusb_interrupt_transfer [NOT IMPLEMENTED]");
return 0;
}
int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **handle) {
LIBUSB_LOG("libusb_open");
- // dev->descriptor.idVendor, dev->descriptor.idProduct
-
+ // TODO: check dev->descriptor.idVendor and dev->descriptor.idProduct
MAIN_THREAD_EM_ASM_INT({
return Asyncify.handleAsync(async () => {
- //console.log('js_open_device');
await this.libusb_device.open();
try {
- await this.libusb_device.reset(); // TODO(dkovalev)
+ // TODO: Avoid resetting on open.
+ await this.libusb_device.reset();
} catch (error) {
- console.error('this.libusb_device.reset()', error);
+ console.error('reset', error);
}
return 1;
});
@@ -362,7 +360,6 @@
MAIN_THREAD_EM_ASM_INT({
Asyncify.handleAsync(async () => {
- //console.log('js_close_device');
return await this.libusb_device.close();
});
});
@@ -382,11 +379,11 @@
dev->ctx = ctx;
if (js_request_device(dev)) {
- print_device_descriptor(&dev->descriptor);
- *list = new libusb_device*[2]{dev, nullptr};
+ print_device(dev);
+ *list = new libusb_device*[]{dev, nullptr};
return 1;
} else {
- *list = new libusb_device*[1]{nullptr};
+ *list = new libusb_device*[]{nullptr};
return 0;
}
}
@@ -412,7 +409,7 @@
int LIBUSB_CALL libusb_get_device_speed(libusb_device *dev) {
LIBUSB_LOG("libusb_get_device_speed");
- return LIBUSB_SPEED_SUPER; // TODO(dkovalev)
+ return LIBUSB_SPEED_SUPER; // TODO: Implement.
}
uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev) {
@@ -420,37 +417,38 @@
return dev->bus_number;
}
-int LIBUSB_CALL libusb_set_configuration(libusb_device_handle *dev, int configuration) {
- LIBUSB_LOG("<NOT IMPLEMENTED> libusb_set_configuration");
+int LIBUSB_CALL libusb_set_configuration(libusb_device_handle *dev,
+ int configuration) {
+ LIBUSB_LOG("libusb_set_configuration [NOT IMPLEMENTED]");
return 0;
}
-int LIBUSB_CALL libusb_claim_interface(libusb_device_handle *dev, int interface_number) {
- LIBUSB_LOG("libusb_claim_interface: %d", interface_number);
+int LIBUSB_CALL libusb_claim_interface(libusb_device_handle *dev,
+ int interface_number) {
+ LIBUSB_LOG("libusb_claim_interface: interface_number=%d", interface_number);
return MAIN_THREAD_EM_ASM_INT({
return Asyncify.handleAsync(async () => {
try {
- //console.log('js_claim_interface: ', $0);
await this.libusb_device.claimInterface($0);
return 0; // LIBUSB_SUCCESS
} catch (error) {
- console.error('js_claim_interface:', error);
+ console.error('claimInterface:', error);
return -1; // LIBUSB_ERROR_IO
}
});
}, interface_number);
}
-int LIBUSB_CALL libusb_release_interface(libusb_device_handle *dev, int interface_number) {
- LIBUSB_LOG("libusb_release_interface: %d", interface_number);
+int LIBUSB_CALL libusb_release_interface(libusb_device_handle *dev,
+ int interface_number) {
+ LIBUSB_LOG("libusb_release_interface: interface_number=%d", interface_number);
return MAIN_THREAD_EM_ASM_INT({
return Asyncify.handleAsync(async () => {
try {
- //console.log('js_release_interface: ', $0, this.libusb_device);
await this.libusb_device.releaseInterface($0);
return 0; // LIBUSB_SUCCESS
} catch (error) {
- console.error('js_release_interface:', error);
+ console.error('releaseInterface:', error);
return -1; // LIBUSB_ERROR_IO
}
});
@@ -458,8 +456,9 @@
}
-EMSCRIPTEN_KEEPALIVE void set_transfer_error(struct libusb_transfer* transfer) {
- LIBUSB_LOG("==> set_tansfer_error [%p]", transfer);
+EMSCRIPTEN_KEEPALIVE
+void set_transfer_error(struct libusb_transfer* transfer) {
+ LIBUSB_LOG("set_tansfer_error: transfer=%p", transfer);
libusb_context* ctx = transfer->dev_handle->dev->ctx;
transfer->status = LIBUSB_TRANSFER_CANCELLED;
@@ -468,8 +467,10 @@
ctx->completed_transfers.Push(transfer);
}
-EMSCRIPTEN_KEEPALIVE void set_transfer_completed(struct libusb_transfer* transfer, int actual_length) {
- LIBUSB_LOG("==> set_tansfer_completed [%p]: %d", transfer, actual_length);
+EMSCRIPTEN_KEEPALIVE
+void set_transfer_completed(struct libusb_transfer* transfer, int actual_length) {
+ LIBUSB_LOG("set_tansfer_completed: transfer=%p, actual_length=%d",
+ transfer, actual_length);
libusb_context* ctx = transfer->dev_handle->dev->ctx;
transfer->status = LIBUSB_TRANSFER_COMPLETED;
@@ -478,7 +479,8 @@
ctx->completed_transfers.Push(transfer);
}
-EMSCRIPTEN_KEEPALIVE void fill_device(struct libusb_device* dev,
+EMSCRIPTEN_KEEPALIVE
+void fill_device(struct libusb_device* dev,
uint16_t bcdUSB,
uint8_t bDeviceClass,
uint8_t bDeviceSubClass,
diff --git a/tflite/queue.h b/tflite/queue.h
index 18d9cce..cc08f1f 100644
--- a/tflite/queue.h
+++ b/tflite/queue.h
@@ -11,9 +11,8 @@
// 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.
-
-#ifndef INTERPRETER_QUEUE_H_
-#define INTERPRETER_QUEUE_H_
+#ifndef TFLITE_QUEUE_H_
+#define TFLITE_QUEUE_H_
#include <chrono>
#include <condition_variable>
@@ -26,7 +25,7 @@
public:
Queue() {}
- void Push(T t){
+ void Push(T t) {
{
std::lock_guard<std::mutex> lock(m_);
q_.push(t);
@@ -36,8 +35,9 @@
std::optional<T> Pop(int timeout_ms) {
std::unique_lock<std::mutex> lock(m_);
- if (!cv_.wait_for(lock, std::chrono::milliseconds(timeout_ms),
- [this]{ return !q_.empty(); }))
+ if (!cv_.wait_for(lock,
+ std::chrono::milliseconds(timeout_ms),
+ [this]{ return !q_.empty(); }))
return std::nullopt;
auto t = q_.front();
@@ -51,4 +51,4 @@
std::queue<T> q_;
};
-#endif // INTERPRETER_QUEUE_H_
+#endif // TFLITE_QUEUE_H_
diff --git a/third_party/libedgetpu b/third_party/libedgetpu
index 999ad40..e68e773 160000
--- a/third_party/libedgetpu
+++ b/third_party/libedgetpu
@@ -1 +1 @@
-Subproject commit 999ad407bbaac56d53fc03a2c1d9c281cde970ef
+Subproject commit e68e773ba65b14fc68e6ff01c58f0dc761682125