chore: import upstream snapshot with attribution
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s

This commit is contained in:
wehub-resource-sync
2026-07-13 12:14:16 +08:00
commit 8a852e4b4e
36502 changed files with 9277225 additions and 0 deletions
+132
View File
@@ -0,0 +1,132 @@
# Description:
# OVIC Benchmarker Java API.
load("@build_bazel_rules_android//android:rules.bzl", "android_library")
load("@rules_java//java:defs.bzl", "java_binary", "java_library", "java_test")
load("//tensorflow/java:build_defs.bzl", "JAVACOPTS")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = ["//visibility:public"],
licenses = ["notice"],
)
# Build targets for OVIC classification.
java_test(
name = "OvicClassifierTest",
size = "medium",
srcs = ["src/test/java/org/tensorflow/ovic/OvicClassifierTest.java"],
data = [
"//tensorflow/lite/java/ovic/src/testdata:labels.txt",
"//tensorflow/lite/java/ovic/src/testdata:ovic_testdata",
],
javacopts = JAVACOPTS,
tags = [
"no_oss",
],
test_class = "org.tensorflow.ovic.OvicClassifierTest",
visibility = ["//visibility:public"],
deps = [
"//tensorflow/lite/java/ovic:ovicbenchmarkerlib_java",
"@com_google_truth",
"@junit",
],
)
java_binary(
name = "ovic_validator",
srcs = ["src/main/java/org/tensorflow/ovic/OvicValidator.java"],
data = [
"//tensorflow/lite/java/ovic/src/testdata:labels.txt",
],
main_class = "org.tensorflow.ovic.OvicValidator",
tags = ["no_oss"],
deps = [
"//tensorflow/lite/java/ovic:ovicbenchmarkerlib_java",
"//tensorflow/lite/java/ovic:ovicdetectionbenchmarkerlib_java",
],
)
android_library(
name = "ovicbenchmarkerlib",
srcs = [
"src/main/java/org/tensorflow/ovic/OvicBenchmarker.java",
"src/main/java/org/tensorflow/ovic/OvicClassificationResult.java",
"src/main/java/org/tensorflow/ovic/OvicClassifier.java",
"src/main/java/org/tensorflow/ovic/OvicClassifierBenchmarker.java",
],
tags = ["no_oss"],
deps = [
"//tensorflow/lite/java:tensorflowlite",
"//tensorflow/lite/java/src/testhelper/java/org/tensorflow/lite:testhelper",
],
)
java_library(
name = "ovicbenchmarkerlib_java",
srcs = [
"src/main/java/org/tensorflow/ovic/OvicClassificationResult.java",
"src/main/java/org/tensorflow/ovic/OvicClassifier.java",
],
javacopts = JAVACOPTS,
tags = ["no_oss"],
deps = [
"//tensorflow/lite/java:libtensorflowlite_jni.so",
"//tensorflow/lite/java:tensorflowlite_javalib",
"//tensorflow/lite/java/src/main/native",
"//tensorflow/lite/java/src/testhelper/java/org/tensorflow/lite:testhelper_javalib",
],
)
# Build targets for OVIC detection.
java_test(
name = "OvicDetectorTest",
size = "medium",
srcs = ["src/test/java/org/tensorflow/ovic/OvicDetectorTest.java"],
data = [
"//tensorflow/lite/java/ovic/src/testdata:coco_labels.txt",
"//tensorflow/lite/java/ovic/src/testdata:ovic_testdata",
],
javacopts = JAVACOPTS,
tags = [
"no_oss",
],
test_class = "org.tensorflow.ovic.OvicDetectorTest",
visibility = ["//visibility:public"],
deps = [
"//tensorflow/lite/java/ovic:ovicdetectionbenchmarkerlib_java",
"@com_google_truth",
"@junit",
],
)
android_library(
name = "ovicdetectionbenchmarkerlib",
srcs = [
"src/main/java/org/tensorflow/ovic/BoundingBox.java",
"src/main/java/org/tensorflow/ovic/OvicBenchmarker.java",
"src/main/java/org/tensorflow/ovic/OvicDetectionResult.java",
"src/main/java/org/tensorflow/ovic/OvicDetector.java",
"src/main/java/org/tensorflow/ovic/OvicDetectorBenchmarker.java",
],
deps = [
"//tensorflow/lite/java:tensorflowlite",
"//tensorflow/lite/java/src/testhelper/java/org/tensorflow/lite:testhelper",
],
)
java_library(
name = "ovicdetectionbenchmarkerlib_java",
srcs = [
"src/main/java/org/tensorflow/ovic/BoundingBox.java",
"src/main/java/org/tensorflow/ovic/OvicDetectionResult.java",
"src/main/java/org/tensorflow/ovic/OvicDetector.java",
],
javacopts = JAVACOPTS,
deps = [
"//tensorflow/lite/java:libtensorflowlite_jni.so",
"//tensorflow/lite/java:tensorflowlite_javalib",
"//tensorflow/lite/java/src/main/native",
"//tensorflow/lite/java/src/testhelper/java/org/tensorflow/lite:testhelper_javalib",
],
)
+321
View File
@@ -0,0 +1,321 @@
# OVIC Benchmarker for LPCV 2020
This folder contains the SDK for track one of the
[Low Power Computer Vision workshop at CVPR 2020.](https://lpcv.ai/2020CVPR/ovic-track)
## Pre-requisite
Follow the steps [here](https://www.tensorflow.org/lite/demo_android) to install
Tensorflow, Bazel, and the Android NDK and SDK.
## Test the benchmarker:
The testing utilities helps the developers (you) to make sure that your
submissions in TfLite format will be processed as expected in the competition's
benchmarking system.
Note: for now the tests only provides correctness checks, i.e. classifier
predicts the correct category on the test image, but no on-device latency
measurements. To test the latency measurement functionality, the tests will
print the latency running on a desktop computer, which is not indicative of the
on-device run-time. We are releasing an benchmarker Apk that would allow
developers to measure latency on their own devices.
### Obtain the sample models
The test data (models and images) should be downloaded automatically for you by
Bazel. In case they are not, you can manually install them as below.
Note: all commands should be called from your tensorflow installation folder
(under this folder you should find `tensorflow/lite`).
* Download the
[testdata package](https://storage.googleapis.com/download.tensorflow.org/data/ovic_2019_04_30.zip):
```sh
curl -L https://storage.googleapis.com/download.tensorflow.org/data/ovic_2019_04_30.zip -o /tmp/ovic.zip
```
* Unzip the package into the testdata folder:
```sh
unzip -j /tmp/ovic.zip -d tensorflow/lite/java/ovic/src/testdata/
```
### Run tests
You can run test with Bazel as below. This helps to ensure that the installation
is correct.
```sh
bazel test //tensorflow/lite/java/ovic:OvicClassifierTest --cxxopt=-Wno-all --test_output=all
bazel test //tensorflow/lite/java/ovic:OvicDetectorTest --cxxopt=-Wno-all --test_output=all
```
### Test your submissions
Once you have a submission that follows the instructions from the
[competition site](https://lpcv.ai/2020CVPR/ovic-track), you can verify it in
two ways:
#### Validate using randomly generated images
You can call the validator binary below to verify that your model fits the
format requirements. This often helps you to catch size mismatches (e.g. output
for classification should be [1, 1001] instead of [1,1,1,1001]). Let say the
submission file is located at `/path/to/my_model.lite`, then call:
```sh
bazel build //tensorflow/lite/java/ovic:ovic_validator --cxxopt=-Wno-all
bazel-bin/tensorflow/lite/java/ovic/ovic_validator /path/to/my_model.lite classify
```
Successful validation should print the following message to terminal:
```
Successfully validated /path/to/my_model.lite.
```
To validate detection models, use the same command but provide "detect" as the
second argument instead of "classify".
#### Test that the model produces sensible outcomes
You can go a step further to verify that the model produces results as expected.
This helps you catch bugs during TFLite conversion (e.g. using the wrong mean
and std values).
* Move your submission to the testdata folder:
```sh
cp /path/to/my_model.lite tensorflow/lite/java/ovic/src/testdata/
```
* Resize the test image to the resolutions that are expected by your
submission:
The test images can be found at
`tensorflow/lite/java/ovic/src/testdata/test_image_*.jpg`. You may reuse these
images if your image resolutions are 128x128 or 224x224.
* Add your model and test image to the BUILD rule at
`tensorflow/lite/java/ovic/src/testdata/BUILD`:
```JSON
filegroup(
name = "ovic_testdata",
srcs = [
"@tflite_ovic_testdata//:detect.lite",
"@tflite_ovic_testdata//:float_model.lite",
"@tflite_ovic_testdata//:low_res_model.lite",
"@tflite_ovic_testdata//:quantized_model.lite",
"@tflite_ovic_testdata//:test_image_128.jpg",
"@tflite_ovic_testdata//:test_image_224.jpg"
"my_model.lite", # <--- Your submission.
"my_test_image.jpg", # <--- Your test image.
],
...
```
* For classification models, modify `OvicClassifierTest.java`:
* change `TEST_IMAGE_PATH` to `my_test_image.jpg`.
* change either `FLOAT_MODEL_PATH` or `QUANTIZED_MODEL_PATH` to
`my_model.lite` depending on whether your model runs inference in float
or
[8-bit](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/quantize).
* change `TEST_IMAGE_GROUNDTRUTH` (ImageNet class ID) to be consistent
with your test image.
* For detection models, modify `OvicDetectorTest.java`:
* change `TEST_IMAGE_PATH` to `my_test_image.jpg`.
* change `MODEL_PATH` to `my_model.lite`.
* change `GROUNDTRUTH` (COCO class ID) to be consistent with your test
image.
Now you can run the bazel tests to catch any runtime issues with the submission.
Note: Please make sure that your submission passes the test. If a submission
fails to pass the test it will not be processed by the submission server.
## Measure on-device latency
We provide two ways to measure the on-device latency of your submission. The
first is through our competition server, which is reliable and repeatable, but
is limited to a few trials per day. The second is through the benchmarker Apk,
which requires a device and may not be as accurate as the server, but has a fast
turn-around and no access limitations. We recommend that the participants use
the benchmarker apk for early development, and reserve the competition server
for evaluating promising submissions.
### Running the benchmarker app
Make sure that you have followed instructions in
[Test your submissions](#test-your-submissions) to add your model to the
testdata folder and to the corresponding build rules.
Modify `tensorflow/lite/java/ovic/demo/app/OvicBenchmarkerActivity.java`:
* Add your model to the benchmarker apk by changing `modelPath` and
`testImagePath` to your submission and test image.
```
if (benchmarkClassification) {
...
testImagePath = "my_test_image.jpg";
modelPath = "my_model.lite";
} else { // Benchmarking detection.
...
```
If you are adding a detection model, simply modify `modelPath` and
`testImagePath` in the else block above.
* Adjust the benchmark parameters when needed:
You can change the length of each experiment, and the processor affinity below.
`BIG_CORE_MASK` is an integer whose binary encoding represents the set of used
cores. This number is phone-specific. For example, Pixel 4 has 8 cores: the 4
little cores are represented by the 4 less significant bits, and the 4 big cores
by the 4 more significant bits. Therefore a mask value of 16, or in binary
`00010000`, represents using only the first big core. The mask 32, or in binary
`00100000` uses the second big core and should deliver identical results as the
mask 16 because the big cores are interchangeable.
```
/** Wall time for each benchmarking experiment. */
private static final double WALL_TIME = 3000;
/** Maximum number of iterations in each benchmarking experiment. */
private static final int MAX_ITERATIONS = 100;
/** Mask for binding to a single big core. Pixel 1 (4), Pixel 4 (16). */
private static final int BIG_CORE_MASK = 16;
```
Note: You'll need ROOT access to the phone to change processor affinity.
* Build and install the app.
```
bazel build -c opt --cxxopt=-Wno-all //tensorflow/lite/java/ovic/demo/app:ovic_benchmarker_binary
adb install -r bazel-bin/tensorflow/lite/java/ovic/demo/app/ovic_benchmarker_binary.apk
```
Start the app and pick a task by clicking either the `CLF` button for
classification or the `DET` button for detection. The button should turn bright
green, signaling that the experiment is running. The benchmarking results will
be displayed after about the `WALL_TIME` you specified above. For example:
```
my_model.lite: Average latency=158.6ms after 20 runs.
```
### Sample latencies
Note: the benchmarking results can be quite different depending on the
background processes running on the phone. A few things that help stabilize the
app's readings are placing the phone on a cooling plate, restarting the phone,
and shutting down internet access.
Classification Model | Pixel 1 | Pixel 2 | Pixel 4
-------------------- | :-----: | ------: | :-----:
float_model.lite | 97 | 113 | 37
quantized_model.lite | 73 | 61 | 13
low_res_model.lite | 3 | 3 | 1
Detection Model | Pixel 2 | Pixel 4
---------------------- | :-----: | :-----:
detect.lite | 248 | 82
quantized_detect.lite | 59 | 17
quantized_fpnlite.lite | 96 | 29
All latency numbers are in milliseconds. The Pixel 1 and Pixel 2 latency numbers
are measured on `Oct 17 2019` (Github commit hash
[I05def66f58fa8f2161522f318e00c1b520cf0606](https://github.com/tensorflow/tensorflow/commit/4b02bc0e0ff7a0bc02264bc87528253291b7c949#diff-4e94df4d2961961ba5f69bbd666e0552))
The Pixel 4 latency numbers are measured on `Apr 14 2020` (Github commit hash
[4b2cb67756009dda843c6b56a8b320c8a54373e0](https://github.com/tensorflow/tensorflow/commit/4b2cb67756009dda843c6b56a8b320c8a54373e0)).
Since Pixel 4 has excellent support for 8-bit quantized models, we strongly
recommend you to check out the
[Post-Training Quantization tutorial](https://www.tensorflow.org/lite/performance/post_training_quantization).
The detection models above are both single-shot models (i.e. no object proposal
generation) using TfLite's *fast* version of Non-Max-Suppression (NMS). The fast
NMS is significant faster than the regular NMS (used by the ObjectDetectionAPI
in training) at the expense of about 1% mAP for the listed models.
### Latency table
We have compiled a latency table for common neural network operators such as
convolutions, separable convolutions, and matrix multiplications. The table of
results is available here:
* https://storage.cloud.google.com/ovic-data/
The results were generated by creating a small network containing a single
operation, and running the op under the test harness. For more details see the
NetAdapt paper<sup>1</sup>. We plan to expand table regularly as we test with
newer OS releases and updates to Tensorflow Lite.
### Sample benchmarks
Below are the baseline models (MobileNetV2, MnasNet, and MobileNetV3) used to
compute the reference accuracy for ImageNet classification. The naming
convention of the models are `[precision]_[model
class]_[resolution]_[multiplier]`. Pixel 2 Latency (ms) is measured on a single
Pixel 2 big core using the competition server on `Oct 17 2019`, while Pixel 4
latency (ms) is measured on a single Pixel 4 big core using the competition
server on `Apr 14 2020`. You can find these models on TFLite's
[hosted model page](https://www.tensorflow.org/lite/guide/hosted_models#image_classification).
Model | Pixel 2 | Pixel 4 | Top-1 Accuracy
:---------------------------------: | :-----: | :-----: | :------------:
quant_mobilenetv2_96_35 | 4 | 1 | 0.420
quant_mobilenetv2_96_50 | 5 | 1 | 0.478
quant_mobilenetv2_128_35 | 6 | 2 | 0.474
quant_mobilenetv2_128_50 | 8 | 2 | 0.546
quant_mobilenetv2_160_35 | 9 | 2 | 0.534
quant_mobilenetv2_96_75 | 8 | 2 | 0.560
quant_mobilenetv2_96_100 | 10 | 3 | 0.579
quant_mobilenetv2_160_50 | 12 | 3 | 0.583
quant_mobilenetv2_192_35 | 12 | 3 | 0.557
quant_mobilenetv2_128_75 | 13 | 3 | 0.611
quant_mobilenetv2_192_50 | 16 | 4 | 0.616
quant_mobilenetv2_128_100 | 16 | 4 | 0.629
quant_mobilenetv2_224_35 | 17 | 5 | 0.581
quant_mobilenetv2_160_75 | 20 | 5 | 0.646
float_mnasnet_96_100 | 21 | 7 | 0.625
quant_mobilenetv2_224_50 | 22 | 6 | 0.637
quant_mobilenetv2_160_100 | 25 | 6 | 0.674
quant_mobilenetv2_192_75 | 29 | 7 | 0.674
quant_mobilenetv2_192_100 | 35 | 9 | 0.695
float_mnasnet_224_50 | 35 | 12 | 0.679
quant_mobilenetv2_224_75 | 39 | 10 | 0.684
float_mnasnet_160_100 | 45 | 15 | 0.706
quant_mobilenetv2_224_100 | 48 | 12 | 0.704
float_mnasnet_224_75 | 55 | 18 | 0.718
float_mnasnet_192_100 | 62 | 20 | 0.724
float_mnasnet_224_100 | 84 | 27 | 0.742
float_mnasnet_224_130 | 126 | 40 | 0.758
float_v3-small-minimalistic_224_100 | - | 5 | 0.620
quant_v3-small_224_100 | - | 5 | 0.641
float_v3-small_224_75 | - | 5 | 0.656
float_v3-small_224_100 | - | 7 | 0.677
quant_v3-large_224_100 | - | 12 | 0.728
float_v3-large_224_75 | - | 15 | 0.735
float_v3-large-minimalistic_224_100 | - | 17 | 0.722
float_v3-large_224_100 | - | 20 | 0.753
### References
1. **NetAdapt: Platform-Aware Neural Network Adaptation for Mobile
Applications**<br />
Yang, Tien-Ju, Andrew Howard, Bo Chen, Xiao Zhang, Alec Go, Mark Sandler,
Vivienne Sze, and Hartwig Adam. In Proceedings of the European Conference
on Computer Vision (ECCV), pp. 285-300. 2018<br />
[[link]](https://arxiv.org/abs/1804.03230) arXiv:1804.03230, 2018.
@@ -0,0 +1,121 @@
<!--
• This is a README.md template we encourage you to use when you release your model.
• There are general sections we added to this template for various ML models.
• You may need to add or remove sections depending on your needs.
-->
# Project Name
## Authors
The **1st place winner** of the **4th On-device Visual Intelligence Competition** ([OVIC](https://docs.google.com/document/d/1Rxm_N7dGRyPXjyPIdRwdhZNRye52L56FozDnfYuCi0k/edit#)) of Low-Power Computer Vision Challenge ([LPCVC](https://lpcv.ai/))
* Last name, First name ([@GitHubUsername](https://github.com/username))
* Last name, First name ([@GitHubUsername](https://github.com/username))
* Last name, First name ([@GitHubUsername](https://github.com/username))
## Description
<!-- Provide description of the model -->
The model submitted for the OVIC and full implementation code for training, evaluation, and inference
* OVIC track: Image Classification, Object Detection
## Algorithm
<!-- Provide details of the algorithms used -->
## Requirements
<!--
• Provide description of the model
• Provide brief information of the algorithms used
-->
To install requirements:
```setup
pip install -r requirements.txt
```
## Pre-trained Models
| Model | Download | MD5 checksum |
|-------|----------|--------------|
| Model Name | Download Link (Size: KB) | MD5 checksum |
The model tar file contains the followings:
* Trained model checkpoint
* Frozen trained model
* TensorFlow Lite model
## Results
### [4th OVIC Public Ranked Leaderboard](https://lpcvc.ecn.purdue.edu/score_board_r4/?contest=round4)
#### Image Classification (from the Leaderboard)
| Rank | Username | Latency | Accuracy on Classified | # Classified | Accuracy/Time | Metric | Reference Accuracy |
|------|----------|---------|------------------------|--------------|---------------|--------|--------------------|
| 1 | Username | xx.x | 0.xxxx | 20000.0 | xxx | 0.xxxxx | 0.xxxxx |
* **Metric**: Accuracy improvement over the reference accuracy from the Pareto optimal curve
* **Accuracy on Classified**: The accuracy in [0, 1] computed based only on the images classified within the wall-time
* **\# Classified**: The number of images classified within the wall-time
* **Accuracy/Time**: The accuracy divided by either the total inference time or the wall-time, whichever is longer
* **Reference accuracy**: The reference accuracy of models from the Pareto optimal curve that have the same latency as the submission
#### Object Detection
| Rank | Username | Metric | Runtime | mAP over time | mAP of processed |
|------|----------|--------|---------|---------------|------------------|
| 1 | Username | 0.xxxxx | xxx.x | xxx | xxx |
* **Metric**: COCO mAP computed on the entire minival dataset
* **mAP over time**: COCO mAP on the minival dataset divided by latency per image
* **mAP of processed**: COCO mAP computed only on the processed images
## Dataset
<!--
• Provide detailed information of the dataset used
-->
## Training
<!--
• Provide detailed training information (preprocessing, hyperparameters, random seeds, and environment)
• Provide a command line example for training.
-->
Please run this command line for training.
```shell
python3 ...
```
## Evaluation
<!--
• Provide evaluation script with details of how to reproduce results.
• Describe data preprocessing / postprocessing steps
• Provide a command line example for evaluation.
-->
Please run this command line for evaluation.
```shell
python3 ...
```
## References
<!-- Link to references -->
## License
<!--
• Place your license text in a file named LICENSE.txt (or LICENSE.md) in the root of the repository.
• Please also include information about your license in this README.md file.
e.g., [Adding a license to a repository](https://help.github.com/en/github/building-a-strong-community/adding-a-license-to-a-repository)
-->
This project is licensed under the terms of the **Apache License 2.0**.
## Citation
<!--
If you want to make your repository citable, please follow the instructions at [Making Your Code Citable](https://guides.github.com/activities/citable-code/)
-->
If you want to cite this repository in your research paper, please use the following information.
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018 The Android Open Source Project
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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ovic.demo.app"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="19"
android:targetSdkVersion="21" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:largeHeap="true"
android:label="@string/app_name">
<activity
android:name="ovic.demo.app.OvicBenchmarkerActivity"
android:label="@string/app_name"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
+34
View File
@@ -0,0 +1,34 @@
# Sample app for OVIC benchmarking.
load("@build_bazel_rules_android//android:rules.bzl", "android_binary")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
licenses = ["notice"],
)
android_binary(
name = "ovic_benchmarker_binary",
srcs = [
"OvicBenchmarkerActivity.java",
],
assets = [
"//tensorflow/lite/java/ovic/src/testdata:coco_labels.txt",
"//tensorflow/lite/java/ovic/src/testdata:labels.txt",
"//tensorflow/lite/java/ovic/src/testdata:ovic_testdata",
],
assets_dir = "",
custom_package = "ovic.demo.app",
manifest = "AndroidManifest.xml",
nocompress_extensions = [
".lite",
".tflite",
],
resource_files = glob(["res/**"]),
tags = ["manual"],
deps = [
"//tensorflow/lite/java:tensorflowlite",
"//tensorflow/lite/java/ovic:ovicbenchmarkerlib",
"//tensorflow/lite/java/ovic:ovicdetectionbenchmarkerlib",
],
)
@@ -0,0 +1,279 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
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.
==============================================================================*/
package ovic.demo.app;
import android.app.Activity;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Process;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.text.DecimalFormat;
import org.tensorflow.ovic.OvicBenchmarker;
import org.tensorflow.ovic.OvicClassifierBenchmarker;
import org.tensorflow.ovic.OvicDetectorBenchmarker;
/** Class that benchmark image classifier models. */
public class OvicBenchmarkerActivity extends Activity {
/** Tag for the {@link Log}. */
private static final String TAG = "OvicBenchmarkerActivity";
/** Name of the task-dependent data files stored in Assets. */
private static String labelPath = null;
private static String testImagePath = null;
private static String modelPath = null;
/**
* Each bottom press will launch a benchmarking experiment. The experiment stops when either the
* total native latency reaches WALL_TIME or the number of iterations reaches MAX_ITERATIONS,
* whichever comes first.
*/
/** Wall time for each benchmarking experiment. */
private static final double WALL_TIME = 3000;
/** Maximum number of iterations in each benchmarking experiment. */
private static final int MAX_ITERATIONS = 100;
/** Mask for binding to a single big core. Pixel 1 (4), Pixel 2 (16). */
private static final int BIG_CORE_MASK = 16;
/** Amount of time in milliseconds to wait for affinity to set. */
private static final int WAIT_TIME_FOR_AFFINITY = 1000;
/* The model to be benchmarked. */
private MappedByteBuffer model = null;
private InputStream labelInputStream = null;
private OvicBenchmarker benchmarker;
private TextView textView = null;
// private Button startButton = null;
private static final DecimalFormat df2 = new DecimalFormat(".##");
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// TextView used to display the progress, for information purposes only.
textView = (TextView) findViewById(R.id.textView);
}
private Bitmap loadTestBitmap() throws IOException {
InputStream imageStream = getAssets().open(testImagePath);
return BitmapFactory.decodeStream(imageStream);
}
public void initializeTest(boolean benchmarkClassification) throws IOException {
Log.i(TAG, "Initializing benchmarker.");
if (benchmarkClassification) {
benchmarker = new OvicClassifierBenchmarker(WALL_TIME);
labelPath = "labels.txt";
testImagePath = "test_image_224.jpg";
modelPath = "quantized_model.lite";
} else { // Benchmarking detection.
benchmarker = new OvicDetectorBenchmarker(WALL_TIME);
labelPath = "coco_labels.txt";
testImagePath = "test_image_224.jpg";
modelPath = "detect.lite";
}
AssetManager am = getAssets();
AssetFileDescriptor fileDescriptor = am.openFd(modelPath);
FileInputStream modelInputStream = new FileInputStream(fileDescriptor.getFileDescriptor());
FileChannel fileChannel = modelInputStream.getChannel();
long startOffset = fileDescriptor.getStartOffset();
long declaredLength = fileDescriptor.getDeclaredLength();
model = fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength);
labelInputStream = am.open(labelPath);
}
public Boolean doTestIteration() throws IOException, InterruptedException {
if (benchmarker == null) {
throw new RuntimeException("Benchmarker has not been initialized.");
}
if (benchmarker.shouldStop()) {
return false;
}
if (!benchmarker.readyToTest()) {
Log.i(TAG, "getting ready to test.");
benchmarker.getReadyToTest(labelInputStream, model);
if (!benchmarker.readyToTest()) {
throw new RuntimeException("Failed to get the benchmarker ready.");
}
}
Log.i(TAG, "Going to do test iter.");
// Start testing.
Bitmap testImageBitmap = loadTestBitmap();
try {
if (!benchmarker.processBitmap(testImageBitmap)) {
throw new RuntimeException("Failed to run test.");
}
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
testImageBitmap.recycle();
}
String iterResultString = benchmarker.getLastResultString();
if (iterResultString == null) {
throw new RuntimeException("Inference failed to produce a result.");
}
Log.i(TAG, iterResultString);
return true;
}
public void detectPressed(View view) throws IOException {
benchmarkSession(false);
}
public void classifyPressed(View view) throws IOException {
benchmarkSession(true);
}
private void benchmarkSession(boolean benchmarkClassification) throws IOException {
try {
initializeTest(benchmarkClassification);
} catch (IOException e) {
Log.e(TAG, "Can't initialize benchmarker.", e);
throw e;
}
String displayText = "";
if (benchmarkClassification) {
displayText = "Classification benchmark: ";
} else {
displayText = "Detection benchmark: ";
}
try {
setProcessorAffinity(BIG_CORE_MASK);
} catch (IOException e) {
Log.e(TAG, e.getMessage());
displayText = e.getMessage() + "\n";
}
Log.i(TAG, "Successfully initialized benchmarker.");
int testIter = 0;
Boolean iterSuccess = false;
while (testIter < MAX_ITERATIONS) {
try {
iterSuccess = doTestIteration();
} catch (IOException e) {
Log.e(TAG, "Error during iteration " + testIter);
throw e;
} catch (InterruptedException e) {
Log.e(TAG, "Interrupted at iteration " + testIter);
displayText += e.getMessage() + "\n";
}
if (!iterSuccess) {
break;
}
testIter++;
}
Log.i(TAG, "Benchmarking finished");
if (textView != null) {
if (testIter > 0) {
textView.setText(
displayText
+ modelPath
+ ": Average latency="
+ df2.format(benchmarker.getTotalRuntimeNano() * 1.0e-6 / testIter)
+ "ms after "
+ testIter
+ " runs.");
} else {
textView.setText("Benchmarker failed to run on more than one images.");
}
}
}
// TODO(b/153429929) Remove with resolution of issue (see below).
@SuppressWarnings("RuntimeExec")
private static void setProcessorAffinity(int mask) throws IOException {
int myPid = Process.myPid();
Log.i(TAG, String.format("Setting processor affinity to 0x%02x", mask));
String command = String.format("taskset -a -p %x %d", mask, myPid);
try {
// TODO(b/153429929) This is deprecated, but updating is not safe while verification is hard.
Runtime.getRuntime().exec(command).waitFor();
} catch (InterruptedException e) {
throw new IOException("Interrupted: " + e);
}
// Make sure set took effect - try for a second to confirm the change took. If not then fail.
long startTimeMs = SystemClock.elapsedRealtime();
while (true) {
int readBackMask = readCpusAllowedMask();
if (readBackMask == mask) {
Log.i(TAG, String.format("Successfully set affinity to 0x%02x", mask));
return;
}
if (SystemClock.elapsedRealtime() > startTimeMs + WAIT_TIME_FOR_AFFINITY) {
throw new IOException(
String.format(
"Core-binding failed: affinity set to 0x%02x but read back as 0x%02x\n"
+ "please root device.",
mask, readBackMask));
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// Ignore sleep interrupted, will sleep again and compare is final cross-check.
}
}
}
public static int readCpusAllowedMask() throws IOException {
// Determine how many CPUs there are total
final String pathname = "/proc/self/status";
final String resultPrefix = "Cpus_allowed:";
File file = new File(pathname);
String line = "<NO LINE READ>";
String allowedCPU = "";
Integer allowedMask = null;
BufferedReader bufReader = null;
try {
bufReader = new BufferedReader(new FileReader(file));
while ((line = bufReader.readLine()) != null) {
if (line.startsWith(resultPrefix)) {
allowedMask = Integer.valueOf(line.substring(resultPrefix.length()).trim(), 16);
allowedCPU = bufReader.readLine();
break;
}
}
} catch (RuntimeException e) {
throw new IOException(
"Invalid number in " + pathname + " line: \"" + line + "\": " + e.getMessage());
} finally {
if (bufReader != null) {
bufReader.close();
}
}
if (allowedMask == null) {
throw new IOException(pathname + " missing " + resultPrefix + " line");
}
Log.i(TAG, allowedCPU);
return allowedMask;
}
}
@@ -0,0 +1,49 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
buildToolsVersion "27.0.3"
defaultConfig {
applicationId "android.example.com.ovicbenchmarker"
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
}
lintOptions {
abortOnError false
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
aaptOptions {
noCompress "lite", "tflite"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
repositories {
mavenCentral()
maven { // Only for snapshot artifacts
name 'ossrh-snapshot'
url 'https://oss.sonatype.org/content/repositories/snapshots'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:25.2.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
implementation 'com.android.support:design:25.2.0'
implementation 'com.android.support:support-annotations:25.3.1'
implementation 'com.android.support:support-v13:25.2.0'
implementation 'org.tensorflow:tensorflow-lite:+'
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018 The Android Open Source Project
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.
-->
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_enabled="false">
<shape android:shape="rectangle">
<solid android:color="#808080"/>
</shape>
</item>
<item
android:state_enabled="true"
android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="#44ff44"/>
</shape>
</item>
<item
android:state_enabled="true"
android:state_pressed="false">
<shape android:shape="rectangle" >
<solid android:color="#227f22"/>
</shape>
</item>
</selector>
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018 The Android Open Source Project
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.
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="ovic.demo.app.OvicBenchmarkerActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/initial_status_msg"
android:id="@+id/textView"
android:layout_above="@+id/button_clf_start"
android:layout_alignParentTop="true"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/start_clf_label"
android:id="@id/button_clf_start"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:background="@drawable/start_button_color"
android:padding="10dp"
android:layout_marginRight="30dp"
android:layout_marginLeft="100dp"
android:layout_marginTop="10dp"
android:foreground="#000000"
android:textColor="#ffffff"
android:enabled="true"
style="?android:attr/buttonBarButtonStyle"
android:onClick="classifyPressed"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/start_det_label"
android:id="@+id/button_det_start"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_toRightOf="@id/button_clf_start"
android:background="@drawable/start_button_color"
android:padding="10dp"
android:layout_marginRight="100dp"
android:layout_marginLeft="30dp"
android:layout_marginTop="10dp"
android:foreground="#000000"
android:textColor="#ffffff"
android:enabled="true"
style="?android:attr/buttonBarButtonStyle"
android:onClick="detectPressed"/>
</RelativeLayout>
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018 The Android Open Source Project
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.
-->
<resources>
<dimen name="activity_vertical_margin">20dp</dimen>
<dimen name="activity_horizontal_margin">16dp</dimen>
</resources>
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018 The Android Open Source Project
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.
-->
<resources>
<string name="app_name" translatable="false">Benchmarker</string>
<string name="start_clf_label" translatable="false">Clf</string>
<string name="start_det_label" translatable="false">Det</string>
<string name="initial_status_msg" translatable="false"> Press start to run the benchmarks.</string>
</resources>
@@ -0,0 +1,29 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
mavenCentral()
maven { // Only for snapshot artifacts
name 'ossrh-snapshot'
url 'https://oss.sonatype.org/content/repositories/snapshots'
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
@@ -0,0 +1,17 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
Binary file not shown.
@@ -0,0 +1,6 @@
#Thu Sep 28 09:01:41 PDT 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
+160
View File
@@ -0,0 +1,160 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+105
View File
@@ -0,0 +1,105 @@
:: Copyright 2026 The TensorFlow Authors. All Rights Reserved.
::
:: 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.
:: ==============================================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@@ -0,0 +1 @@
include ':app'
@@ -0,0 +1,68 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
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.
==============================================================================*/
package org.tensorflow.ovic;
/** Class for holding a detection bounding box with category and confidence. */
public class BoundingBox {
// Upper left point.
public float x1;
public float y1;
// Lower right point.
public float x2;
public float y2;
// The area of the box
public float area;
// The object category
public int category;
// The confidence of the detection
public float score;
public BoundingBox(float x1, float y1, float x2, float y2, int category, float score) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.category = category;
this.score = score;
// -1 stands for area not initialized
this.area = -1;
}
// The intersection area of two bounding boxes
public float intersect(BoundingBox bbx) {
return Math.max(0, Math.min(x2, bbx.x2) - Math.max(x1, bbx.x1))
* Math.max(0, Math.min(y2, bbx.y2) - Math.max(y1, bbx.y1));
}
// The union area of two bounding boxes
public float union(BoundingBox bbx) {
return bbx.getArea() + this.getArea() - this.intersect(bbx);
}
public float getArea() {
if (area < 0) {
area = (x2 - x1) * (y2 - y1);
}
return area;
}
public float computeIoU(BoundingBox bbx) {
return (float) (this.intersect(bbx) * 1.0 / this.union(bbx));
}
}
@@ -0,0 +1,147 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
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.
==============================================================================*/
package org.tensorflow.ovic;
import android.graphics.Bitmap;
import android.os.SystemClock;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
/**
* Base class that benchmarks image models.
*
* <p>===================== General workflow =======================
*
* <pre>{@code
* benchmarker = new OvicBenchmarker();
* benchmarker.getReadyToTest(labelInputStream, model);
* while (!benchmarker.shouldStop()) {
* Bitmap bitmap = ...
* imgId = ...
* benchmarker.processBitmap(bitmap, imgId);
* }
* }</pre>
*/
public abstract class OvicBenchmarker {
/** Tag for the {@link Log}. */
private static final String TAG = "OvicBenchmarker";
/** Dimensions of inputs. */
protected static final int DIM_BATCH_SIZE = 1;
protected static final int DIM_PIXEL_SIZE = 3;
protected int imgHeight = 224;
protected int imgWidth = 224;
/* Preallocated buffers for storing image data in. */
protected int[] intValues = null;
/** A ByteBuffer to hold image data, to be feed into classifier as inputs. */
protected ByteBuffer imgData = null;
/** Total runtime in ns. */
protected double totalRuntimeNano = 0.0;
/** Total allowed runtime in ms. */
protected double wallTimeMilli = 20000 * 30.0;
/** Record whether benchmark has started (used to skip the first image). */
protected boolean benchmarkStarted = false;
/**
* Initializes an {@link OvicBenchmarker}
*
* @param wallTimeMilli: a double number specifying the total amount of time to benchmark.
*/
protected OvicBenchmarker(double wallTimeMilli) {
benchmarkStarted = false;
totalRuntimeNano = 0.0;
this.wallTimeMilli = wallTimeMilli;
}
/** Return the cumulative latency of all runs so far. */
public double getTotalRuntimeNano() {
return totalRuntimeNano;
}
/** Check whether the benchmarker should stop. */
public Boolean shouldStop() {
if ((totalRuntimeNano * 1.0 / 1e6) >= wallTimeMilli) {
Log.e(
TAG,
"Total runtime "
+ (totalRuntimeNano * 1.0 / 1e6)
+ " exceeded walltime (ms) "
+ wallTimeMilli);
return true;
}
return false;
}
/** Abstract class for checking whether the benchmarker is ready to start processing images */
public abstract boolean readyToTest();
/**
* Abstract class for getting the benchmarker ready.
*
* @param labelInputStream: an {@link InputStream} specifying where the list of labels should be
* read from.
* @param model: a {@link MappedByteBuffer} model to benchmark.
*/
public abstract void getReadyToTest(InputStream labelInputStream, MappedByteBuffer model);
/**
* Perform test on a single bitmap image.
*
* @param bitmap: a {@link Bitmap} image to process.
* @param imageId: an ID uniquely representing the image.
*/
public abstract boolean processBitmap(Bitmap bitmap, int imageId)
throws IOException, InterruptedException;
/** Perform test on a single bitmap image without an image ID. */
public boolean processBitmap(Bitmap bitmap) throws IOException, InterruptedException {
return processBitmap(bitmap, /* imageId = */ 0);
}
/** Returns the last inference results as string. */
public abstract String getLastResultString();
/**
* Loads input buffer from intValues into ByteBuffer for the interpreter. Input buffer must be
* loaded in intValues and output will be placed in imgData.
*/
protected void loadsInputToByteBuffer() {
if (imgData == null || intValues == null) {
throw new RuntimeException("Benchmarker is not yet ready to test.");
}
// Convert the image to ByteBuffer.
imgData.rewind();
int pixel = 0;
long startTime = SystemClock.uptimeMillis();
for (int i = 0; i < imgHeight; ++i) {
for (int j = 0; j < imgWidth; ++j) {
final int pixelValue = intValues[pixel++];
imgData.put((byte) ((pixelValue >> 16) & 0xFF));
imgData.put((byte) ((pixelValue >> 8) & 0xFF));
imgData.put((byte) (pixelValue & 0xFF));
}
}
long endTime = SystemClock.uptimeMillis();
Log.d(TAG, "Timecost to put values into ByteBuffer: " + Long.toString(endTime - startTime));
}
}
@@ -0,0 +1,59 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
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.
==============================================================================*/
package org.tensorflow.ovic;
import java.util.ArrayList;
/** Result class for inference run on a single image. */
public class OvicClassificationResult {
/** Top K classes and probabilities. */
public final ArrayList<String> topKClasses;
public final ArrayList<Float> topKProbs;
public final ArrayList<Integer> topKIndices;
/** Latency (ms). */
public Long latencyMilli;
/** Latency (ns). */
public Long latencyNano;
OvicClassificationResult() {
topKClasses = new ArrayList<>();
topKProbs = new ArrayList<>();
topKIndices = new ArrayList<>();
latencyMilli = -1L;
latencyNano = -1L;
}
@Override
public String toString() {
String textToShow = latencyMilli + "ms";
textToShow += "\n" + latencyNano + "ns";
for (int k = 0; k < topKProbs.size(); ++k) {
textToShow +=
"\nPrediction ["
+ k
+ "] = Class "
+ topKIndices.get(k)
+ " ("
+ topKClasses.get(k)
+ ") : "
+ topKProbs.get(k);
}
return textToShow;
}
}
@@ -0,0 +1,227 @@
/*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
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.
==============================================================================*/
package org.tensorflow.ovic;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import org.tensorflow.lite.Interpreter;
import org.tensorflow.lite.TestHelper;
/** Class for running ImageNet classification with a TfLite model. */
public class OvicClassifier {
/** Tag for the {@link Log}. */
private static final String TAG = "OvicClassifier";
/** Number of results to show (i.e. the "K" in top-K predictions). */
private static final int RESULTS_TO_SHOW = 5;
/** An instance of the driver class to run model inference with Tensorflow Lite. */
private Interpreter tflite;
/** Labels corresponding to the output of the vision model. */
private final List<String> labelList;
/** An array to hold inference results, to be feed into Tensorflow Lite as outputs. */
private byte[][] inferenceOutputArray = null;
/** An array to hold final prediction probabilities. */
private float[][] labelProbArray = null;
/** Input resultion. */
private int[] inputDims = null;
/** Whether the model runs as float or quantized. */
private Boolean outputIsFloat = null;
private final PriorityQueue<Map.Entry<Integer, Float>> sortedLabels =
new PriorityQueue<>(
RESULTS_TO_SHOW,
new Comparator<Map.Entry<Integer, Float>>() {
@Override
public int compare(Map.Entry<Integer, Float> o1, Map.Entry<Integer, Float> o2) {
return o1.getValue().compareTo(o2.getValue());
}
});
/** Initializes an {@code OvicClassifier}. */
public OvicClassifier(InputStream labelInputStream, MappedByteBuffer model) throws IOException {
if (model == null) {
throw new RuntimeException("Input model is empty.");
}
labelList = loadLabelList(labelInputStream);
// OVIC uses one thread for CPU inference.
tflite = new Interpreter(model, new Interpreter.Options().setNumThreads(1));
inputDims = TestHelper.getInputDims(tflite, 0);
if (inputDims.length != 4) {
throw new RuntimeException("The model's input dimensions must be 4 (BWHC).");
}
if (inputDims[0] != 1) {
throw new IllegalStateException(
"The model must have a batch size of 1, got " + inputDims[0] + " instead.");
}
if (inputDims[3] != 3) {
throw new IllegalStateException(
"The model must have three color channels, got " + inputDims[3] + " instead.");
}
int minSide = Math.min(inputDims[1], inputDims[2]);
int maxSide = Math.max(inputDims[1], inputDims[2]);
if (minSide <= 0 || maxSide > 1000) {
throw new RuntimeException("The model's resolution must be between (0, 1000].");
}
String outputDataType = TestHelper.getOutputDataType(tflite, 0);
switch (outputDataType) {
case "float":
outputIsFloat = true;
break;
case "byte":
outputIsFloat = false;
break;
default:
throw new IllegalStateException("Cannot process output type: " + outputDataType);
}
inferenceOutputArray = new byte[1][labelList.size()];
labelProbArray = new float[1][labelList.size()];
}
/** Classifies a {@link ByteBuffer} image. */
// @throws RuntimeException if model is uninitialized.
public OvicClassificationResult classifyByteBuffer(ByteBuffer imgData) {
if (tflite == null) {
throw new RuntimeException(TAG + ": ImageNet classifier has not been initialized; Failed.");
}
if (outputIsFloat == null) {
throw new RuntimeException(TAG + ": Classifier output type has not been resolved.");
}
if (outputIsFloat) {
tflite.run(imgData, labelProbArray);
} else {
tflite.run(imgData, inferenceOutputArray);
/** Convert results to float */
for (int i = 0; i < inferenceOutputArray[0].length; i++) {
labelProbArray[0][i] = (inferenceOutputArray[0][i] & 0xff) / 255.0f;
}
}
OvicClassificationResult iterResult = computeTopKLabels();
iterResult.latencyMilli = getLastNativeInferenceLatencyMilliseconds();
iterResult.latencyNano = getLastNativeInferenceLatencyNanoseconds();
return iterResult;
}
/** Return the probability array of all classes. */
public float[][] getlabelProbArray() {
return labelProbArray;
}
/** Return the number of top labels predicted by the classifier. */
public int getNumPredictions() {
return RESULTS_TO_SHOW;
}
/** Return the four dimensions of the input image. */
public int[] getInputDims() {
return inputDims;
}
/**
* Get native inference latency of last image classification run.
*
* @throws RuntimeException if model is uninitialized.
*/
public Long getLastNativeInferenceLatencyMilliseconds() {
if (tflite == null) {
throw new RuntimeException(TAG + ": ImageNet classifier has not been initialized; Failed.");
}
Long latency = tflite.getLastNativeInferenceDurationNanoseconds();
return (latency == null) ? null : (Long) (latency / 1000000);
}
/**
* Get native inference latency of last image classification run.
*
* @throws RuntimeException if model is uninitialized.
*/
public Long getLastNativeInferenceLatencyNanoseconds() {
if (tflite == null) {
throw new IllegalStateException(
TAG + ": ImageNet classifier has not been initialized; Failed.");
}
return tflite.getLastNativeInferenceDurationNanoseconds();
}
/** Closes tflite to release resources. */
public void close() {
tflite.close();
tflite = null;
}
/** Reads label list from Assets. */
private static List<String> loadLabelList(InputStream labelInputStream) throws IOException {
List<String> labelList = new ArrayList<>();
try (BufferedReader reader =
new BufferedReader(new InputStreamReader(labelInputStream, UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
labelList.add(line);
}
}
return labelList;
}
/** Computes top-K labels. */
private OvicClassificationResult computeTopKLabels() {
if (labelList == null) {
throw new RuntimeException("Label file has not been loaded.");
}
for (int i = 0; i < labelList.size(); ++i) {
sortedLabels.add(new AbstractMap.SimpleEntry<>(i, labelProbArray[0][i]));
if (sortedLabels.size() > RESULTS_TO_SHOW) {
sortedLabels.poll();
}
}
OvicClassificationResult singleImageResult = new OvicClassificationResult();
if (sortedLabels.size() != RESULTS_TO_SHOW) {
throw new RuntimeException(
"Number of returned labels does not match requirement: "
+ sortedLabels.size()
+ " returned, but "
+ RESULTS_TO_SHOW
+ " required.");
}
for (int i = 0; i < RESULTS_TO_SHOW; ++i) {
Map.Entry<Integer, Float> label = sortedLabels.poll();
// ImageNet model prediction indices are 0-based.
singleImageResult.topKIndices.add(label.getKey());
singleImageResult.topKClasses.add(labelList.get(label.getKey()));
singleImageResult.topKProbs.add(label.getValue());
}
// Labels with lowest probability are returned first, hence need to reverse them.
Collections.reverse(singleImageResult.topKIndices);
Collections.reverse(singleImageResult.topKClasses);
Collections.reverse(singleImageResult.topKProbs);
return singleImageResult;
}
}
@@ -0,0 +1,142 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
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.
==============================================================================*/
package org.tensorflow.ovic;
import android.graphics.Bitmap;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
/** Class that benchmarks image classifier models. */
public final class OvicClassifierBenchmarker extends OvicBenchmarker {
/** Tag for the {@link Log}. */
private static final String TAG = "OvicClassifierBenchmarker";
/** ImageNet preprocessing parameters. */
private static final float CENTRAL_FRACTION = 0.875f;
private OvicClassifier classifier;
private OvicClassificationResult iterResult = null;
public OvicClassifierBenchmarker(double wallTime) {
super(wallTime);
}
/** Test if the classifier is ready for benchmarking. */
@Override
public boolean readyToTest() {
return (classifier != null);
}
/**
* Getting the benchmarker ready for classifying images.
*
* @param labelInputStream: an {@link InputStream} specifying where the list of labels should be
* read from.
* @param model: a {@link MappedByteBuffer} model to benchmark.
*/
@Override
public void getReadyToTest(InputStream labelInputStream, MappedByteBuffer model) {
try {
Log.i(TAG, "Creating classifier.");
classifier = new OvicClassifier(labelInputStream, model);
int [] inputDims = classifier.getInputDims();
imgHeight = inputDims[1];
imgWidth = inputDims[2];
// Only accept QUANTIZED_UINT8 input.
imgData = ByteBuffer.allocateDirect(DIM_BATCH_SIZE * imgHeight * imgWidth * DIM_PIXEL_SIZE);
imgData.order(ByteOrder.nativeOrder());
intValues = new int[imgHeight * imgWidth];
} catch (Exception e) {
Log.e(TAG, e.getMessage());
Log.e(TAG, "Failed to initialize ImageNet classifier for the benchmarker.");
}
}
/**
* Perform classification on a single bitmap image.
*
* @param bitmap: a {@link Bitmap} image to process.
* @param imageId: an ID uniquely representing the image.
*/
@Override
public boolean processBitmap(Bitmap bitmap, int imageId)
throws IOException, InterruptedException {
if (shouldStop() || !readyToTest()) {
return false;
}
try {
Log.i(TAG, "Converting bitmap.");
convertBitmapToInput(bitmap);
Log.i(TAG, "Classifying image: " + imageId);
iterResult = classifier.classifyByteBuffer(imgData);
} catch (RuntimeException e) {
Log.e(TAG, e.getMessage());
Log.e(TAG, "Failed to classify image.");
}
if (iterResult == null || iterResult.latencyMilli == null || iterResult.latencyNano == null) {
throw new RuntimeException("Classification result or timing is invalid.");
}
Log.d(TAG, "Native inference latency (ms): " + iterResult.latencyMilli);
Log.d(TAG, "Native inference latency (ns): " + iterResult.latencyNano);
Log.i(TAG, iterResult.toString());
if (!benchmarkStarted) { // Skip the first image to discount warming-up time.
benchmarkStarted = true;
} else {
totalRuntimeNano += ((double) iterResult.latencyNano);
}
return true;
}
/** Return how many classes are predicted per image. */
public int getNumPredictions() {
return classifier.getNumPredictions();
}
public OvicClassificationResult getLastClassificationResult() {
return iterResult;
}
@Override
public String getLastResultString() {
if (iterResult == null) {
return null;
} else {
return iterResult.toString();
}
}
/**
* Preprocess bitmap according to ImageNet protocol then writes result into a {@link ByteBuffer}.
*
* @param bitmap: a {@link Bitmap} source image.
*/
private void convertBitmapToInput(Bitmap bitmap) {
// Perform transformations corresponding to evaluation mode.
float width = (float) bitmap.getWidth();
float height = (float) bitmap.getHeight();
int stWidth = Math.round((width - width * CENTRAL_FRACTION) / 2);
int stHeight = Math.round((height - height * CENTRAL_FRACTION) / 2);
int newWidth = Math.round(width - stWidth * 2);
int newHeight = Math.round(height - stHeight * 2);
bitmap = Bitmap.createBitmap(bitmap, stWidth, stHeight, newWidth, newHeight);
bitmap = Bitmap.createScaledBitmap(bitmap, imgWidth, imgHeight, true);
bitmap.getPixels(intValues, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
loadsInputToByteBuffer();
}
}
@@ -0,0 +1,95 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
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.
==============================================================================*/
package org.tensorflow.ovic;
import java.util.ArrayList;
/** Result class for inference run on a single image. */
public class OvicDetectionResult {
// Top K classes and probabilities.
public final ArrayList<BoundingBox> detections;
// Latency (ms).
public Long latencyMilli = -1L;
// Latency (ns).
public Long latencyNano = -1L;
// id of the image.
public int id = -1;
// Number of valid detections (separately maintained, maybe different from detections.size()).
public int count = 0;
// Create OvicDetectionResult object with pre-filled capacity. Note that detections.size() will
// be equal to capacity after this call.
OvicDetectionResult(int capacity) {
detections = new ArrayList<BoundingBox>(capacity);
for (int i = 0; i < capacity; i++) {
detections.add(new BoundingBox(-1.0f, -1.0f, -1.0f, -1.0f, -1, -1.0f));
}
}
public void resetTo(Long latencyMilli, Long latencyNano, int id) {
count = 0;
this.latencyMilli = latencyMilli;
this.latencyNano = latencyNano;
this.id = id;
}
public void addBox(float x1, float y1, float x2, float y2, int category, float score) {
detections.get(count).x1 = x1;
detections.get(count).y1 = y1;
detections.get(count).x2 = x2;
detections.get(count).y2 = y2;
detections.get(count).category = category;
detections.get(count).score = score;
count += 1;
}
public void scaleUp(double scaleFactorWidth, double scaleFactorHeight) {
for (BoundingBox box : detections) {
box.x1 = (float) (box.x1 * scaleFactorWidth);
box.y1 = (float) (box.y1 * scaleFactorHeight);
box.x2 = (float) (box.x2 * scaleFactorWidth);
box.y2 = (float) (box.y2 * scaleFactorHeight);
}
}
@Override
public String toString() {
String textToShow = latencyMilli + "ms";
textToShow += "\n" + latencyNano + "ns";
int k = 0;
for (BoundingBox box : detections) {
textToShow +=
"\nPrediction ["
+ k
+ "] = Class "
+ box.category
+ " ("
+ box.x1
+ ", "
+ box.y1
+ ", "
+ box.x2
+ ", "
+ box.y2
+ ") : "
+ box.score;
k++;
}
return textToShow;
}
}
@@ -0,0 +1,198 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
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.
==============================================================================*/
package org.tensorflow.ovic;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.tensorflow.lite.Interpreter;
import org.tensorflow.lite.TestHelper;
/** Class for running COCO detection with a TfLite model. */
public class OvicDetector implements AutoCloseable {
/** Tag for the {@link Log}. */
private static final String TAG = "OvicDetector";
/** An instance of the driver class to run model inference with Tensorflow Lite. */
private Interpreter tflite;
/** Labels corresponding to the output of the vision model. */
private final List<String> labelList;
/** Number of detections per image. 10 for demo, 100 for the actual competition. */
private static final int NUM_RESULTS = 100;
/** The output arrays for the mobilenet SSD. */
private float[][][] outputLocations;
private float[][] outputClasses;
private float[][] outputScores;
private float[] numDetections;
private Map<Integer, Object> outputMap;
/** Input resolution. */
private final int[] inputDims;
/** Final result. */
public OvicDetectionResult result = null;
OvicDetector(InputStream labelInputStream, MappedByteBuffer model) throws IOException {
// Load the label list.
labelList = loadLabelList(labelInputStream);
// Create the TfLite interpreter.
tflite = new Interpreter(model, new Interpreter.Options().setNumThreads(1));
inputDims = TestHelper.getInputDims(tflite, 0);
if (TestHelper.getInputDataType(tflite, 0).equals("float")) {
throw new RuntimeException("The model's input must be QUANTIZED_UINT8.");
}
if (inputDims.length != 4) {
throw new RuntimeException("The model's input dimensions must be 4 (BWHC).");
}
if (inputDims[0] != 1) {
throw new RuntimeException(
"The model must have a batch size of 1, got " + inputDims[0] + " instead.");
}
if (inputDims[3] != 3) {
throw new RuntimeException(
"The model must have three color channels, got " + inputDims[3] + " instead.");
}
// Check the resolution.
int minSide = Math.min(inputDims[1], inputDims[2]);
int maxSide = Math.max(inputDims[1], inputDims[2]);
if (minSide <= 0 || maxSide > 1000) {
throw new RuntimeException("The model's resolution must be between (0, 1000].");
}
// Initialize the input array and result arrays. The input images are stored in a list of
// Object. Since this function anaylzed one image per time, there is only 1 item.
// The output is fomulated as a map of int -> Object. The output arrays are added to the map.
outputLocations = new float[1][NUM_RESULTS][4];
outputClasses = new float[1][NUM_RESULTS];
outputScores = new float[1][NUM_RESULTS];
numDetections = new float[1];
outputMap = new HashMap<>();
outputMap.put(0, outputLocations);
outputMap.put(1, outputClasses);
outputMap.put(2, outputScores);
outputMap.put(3, numDetections);
// Preallocate the result. This will be where inference result is stored after each
// detectByteBuffer call.
result = new OvicDetectionResult(NUM_RESULTS);
}
/** Reads label list from Assets. */
private static List<String> loadLabelList(InputStream labelInputStream) throws IOException {
List<String> labelList = new ArrayList<>();
try (BufferedReader reader =
new BufferedReader(new InputStreamReader(labelInputStream, UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
labelList.add(line);
}
}
return labelList;
}
/**
* The interface to run the detection. This method currently only support float mobilenet_ssd
* model. The quantized models will be added in the future.
*
* @param imgData The image buffer in ByteBuffer format.
* @return boolean indicator of whether detection was a success. If success, the detection results
* is available in the result member variable.
* See OvicDetectionResult.java for details.
*/
boolean detectByteBuffer(ByteBuffer imgData, int imageId) {
if (tflite == null) {
throw new RuntimeException(TAG + ": Detector has not been initialized; Failed.");
}
Object[] inputArray = {imgData};
tflite.runForMultipleInputsOutputs(inputArray, outputMap);
Long latencyMilli = getLastNativeInferenceLatencyMilliseconds();
Long latencyNano = getLastNativeInferenceLatencyNanoseconds();
// Update the results.
result.resetTo(latencyMilli, latencyNano, imageId);
for (int i = 0; i < NUM_RESULTS; i++) {
// The model returns normalized coordinates [start_y, start_x, end_y, end_x].
// The boxes expect pixel coordinates [x1, y1, x2, y2].
// The height and width of the input are in inputDims[1] and inputDims[2].
// The following command converts between model outputs to bounding boxes.
result.addBox(
outputLocations[0][i][1] * inputDims[2],
outputLocations[0][i][0] * inputDims[1],
outputLocations[0][i][3] * inputDims[2],
outputLocations[0][i][2] * inputDims[1],
Math.round(outputClasses[0][i] + 1 /* Label offset */),
outputScores[0][i]);
}
return true; // Marks that the result is available.
}
/**
* Get native inference latency of last image detection run.
*
* @throws RuntimeException if model is uninitialized.
* @return The inference latency in milliseconds.
*/
public Long getLastNativeInferenceLatencyMilliseconds() {
if (tflite == null) {
throw new RuntimeException(TAG + ": ImageNet classifier has not been initialized; Failed.");
}
Long latency = tflite.getLastNativeInferenceDurationNanoseconds();
return (latency == null) ? null : (Long) (latency / 1000000);
}
/**
* Get native inference latency of last image detection run.
*
* @throws RuntimeException if model is uninitialized.
* @return The inference latency in nanoseconds.
*/
public Long getLastNativeInferenceLatencyNanoseconds() {
if (tflite == null) {
throw new IllegalStateException(
TAG + ": ImageNet classifier has not been initialized; Failed.");
}
return tflite.getLastNativeInferenceDurationNanoseconds();
}
public int[] getInputDims() {
return inputDims;
}
public List<String> getLabels() {
return labelList;
}
/** Closes tflite to release resources. */
@Override
public void close() {
tflite.close();
tflite = null;
}
}
@@ -0,0 +1,154 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
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.
==============================================================================*/
package org.tensorflow.ovic;
import android.graphics.Bitmap;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
/**
* Class that benchmarks object detection models.
*/
public final class OvicDetectorBenchmarker extends OvicBenchmarker {
/** Tag for the {@link Log}. */
private static final String TAG = "OvicDetectorBenchmarker";
public double scaleFactorWidth = 1.0f;
public double scaleFactorHeight = 1.0f;
private Bitmap scaledBitmap = null; // Preallocate bitmap for scaling.
private OvicDetector detector;
/**
* Initializes an {@link OvicDetectionBenchmarker}
*
* @param wallTime: a double number specifying the total amount of time to benchmark.
*/
public OvicDetectorBenchmarker(double wallTime) {
super(wallTime);
}
/** Check to see if the detector is ready to test. */
@Override
public boolean readyToTest() {
return (detector != null);
}
/**
* Getting the benchmarker ready for detecting images.
*
* @param labelInputStream: an {@link InputStream} specifying where the list of labels should be
* read from.
* @param model: a {@link MappedByteBuffer} model to benchmark.
*/
@Override
public void getReadyToTest(InputStream labelInputStream, MappedByteBuffer model) {
try {
Log.i(TAG, "Creating detector.");
detector = new OvicDetector(labelInputStream, model);
int[] inputDims = detector.getInputDims();
imgHeight = inputDims[1];
imgWidth = inputDims[2];
imgData = ByteBuffer.allocateDirect(DIM_BATCH_SIZE * imgHeight * imgWidth * DIM_PIXEL_SIZE);
imgData.order(ByteOrder.nativeOrder());
intValues = new int[imgHeight * imgWidth];
benchmarkStarted = false;
} catch (Exception e) {
Log.e(TAG, e.getMessage());
Log.e(TAG, "Failed to initialize COCO detector for the benchmarker.", e);
}
}
/**
* Perform detection on a single ByteBuffer {@link ByteBuffer} image. The image must have the
* same dimension that the model expects.
*
* @param image: a {@link ByteBuffer} image to process.
* @param imageId: an ID uniquely representing the image.
*/
public boolean processBuffer(ByteBuffer image, int imageId) {
if (!readyToTest()) {
return false;
}
try {
if (!detector.detectByteBuffer(image, imageId)) {
return false;
}
} catch (RuntimeException e) {
Log.e(TAG, e.getMessage());
return false;
}
if (!benchmarkStarted) { // Skip the first image to discount warming-up time.
benchmarkStarted = true;
} else {
totalRuntimeNano += ((double) detector.result.latencyNano);
}
return true; // Indicating that result is ready.
}
/**
* Perform detection on a single bitmap image.
*
* @param bitmap: a {@link Bitmap} image to process.
* @param imageId: an ID uniquely representing the image.
*/
@Override
public boolean processBitmap(Bitmap bitmap, int imageId)
throws IOException, InterruptedException {
if (shouldStop() || !readyToTest()) {
return false;
}
convertBitmapToInput(bitmap); // Scale bitmap if needed, store result in imgData.
if (!processBuffer(imgData, imageId)) {
return false;
}
// Scale results back to original image coordinates.
detector.result.scaleUp(scaleFactorWidth, scaleFactorHeight);
return true; // Indicating that result is ready.
}
public OvicDetectionResult getLastDetectionResult() {
return detector.result;
}
@Override
public String getLastResultString() {
if (detector.result == null) {
return null;
}
return detector.result.toString();
}
/**
* Preprocess bitmap image into {@link ByteBuffer} format for the detector.
*
* @param bitmap: a {@link Bitmap} source image.
*/
private void convertBitmapToInput(Bitmap bitmap) {
int originalWidth = bitmap.getWidth();
int originalHeight = bitmap.getHeight();
scaledBitmap = Bitmap.createScaledBitmap(bitmap, imgWidth, imgHeight, true);
scaleFactorWidth = originalWidth * 1.0 / imgWidth;
scaleFactorHeight = originalHeight * 1.0 / imgHeight;
scaledBitmap.getPixels(intValues, 0, imgWidth, 0, 0, imgWidth, imgHeight);
scaledBitmap.recycle();
loadsInputToByteBuffer();
}
}
@@ -0,0 +1,103 @@
/*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
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.
==============================================================================*/
package org.tensorflow.ovic;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Random;
/** Validate a submission model. */
public class OvicValidator {
private static void printUsage(PrintStream s) {
s.println("Java program that validates a submission model.");
s.println();
s.println("Usage: ovic_validator <submission file> [<task>]");
s.println();
s.println("Where:");
s.println("<submission file> is the model in TfLite format,");
s.println("<task> is the type of the task: \"classify\" (default) or \"detect\";");
}
public static void main(String[] args) {
if (args.length != 2) {
printUsage(System.err);
System.exit(1);
}
final String modelFile = args[0];
final String taskString = args[1];
final boolean isDetection = taskString.equals("detect");
// Label file for detection is never used, so the same label file is used for both tasks.
final String labelPath =
"tensorflow/lite/java/ovic/src/testdata/labels.txt";
try {
MappedByteBuffer model = loadModelFile(modelFile);
File labelsfile = new File(labelPath);
InputStream labelsInputStream = new FileInputStream(labelsfile);
if (isDetection) {
OvicDetector detector = new OvicDetector(labelsInputStream, model);
int[] inputDims = detector.getInputDims();
ByteBuffer imgData = createByteBuffer(inputDims[1], inputDims[2]);
if (!detector.detectByteBuffer(imgData, /*imageId=*/ 0)) {
throw new RuntimeException("Failed to return detections.");
}
} else {
OvicClassifier classifier = new OvicClassifier(labelsInputStream, model);
int[] inputDims = classifier.getInputDims();
ByteBuffer imgData = createByteBuffer(inputDims[1], inputDims[2]);
OvicClassificationResult testResult = classifier.classifyByteBuffer(imgData);
if (testResult.topKClasses.isEmpty()) {
throw new RuntimeException("Failed to return top K predictions.");
}
}
System.out.printf("Successfully validated %s.%n", modelFile);
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.printf("Failed to validate %s.%n", modelFile);
}
}
private static ByteBuffer createByteBuffer(int imgWidth, int imgHeight) {
ByteBuffer imgData = ByteBuffer.allocateDirect(imgHeight * imgWidth * 3);
imgData.order(ByteOrder.nativeOrder());
Random rand = new Random();
for (int y = 0; y < imgHeight; y++) {
for (int x = 0; x < imgWidth; x++) {
int val = rand.nextInt();
imgData.put((byte) ((val >> 16) & 0xFF));
imgData.put((byte) ((val >> 8) & 0xFF));
imgData.put((byte) (val & 0xFF));
}
}
return imgData;
}
private static MappedByteBuffer loadModelFile(String modelFilePath) throws IOException {
File modelfile = new File(modelFilePath);
FileInputStream inputStream = new FileInputStream(modelfile);
FileChannel fileChannel = inputStream.getChannel();
long startOffset = 0L;
long declaredLength = fileChannel.size();
return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength);
}
}
@@ -0,0 +1,174 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
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.
==============================================================================*/
package org.tensorflow.ovic;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import javax.imageio.ImageIO;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link org.tensorflow.ovic.OvicClassifier}. */
@RunWith(JUnit4.class)
public final class OvicClassifierTest {
private OvicClassifier classifier;
private InputStream labelsInputStream = null;
private MappedByteBuffer quantizedModel = null;
private MappedByteBuffer floatModel = null;
private MappedByteBuffer lowResModel = null;
private ByteBuffer testImage = null;
private ByteBuffer lowResTestImage = null;
private OvicClassificationResult testResult = null;
private static final String LABELS_PATH =
"tensorflow/lite/java/ovic/src/testdata/labels.txt";
private static final String QUANTIZED_MODEL_PATH =
"external/tflite_ovic_testdata/quantized_model.lite";
private static final String LOW_RES_MODEL_PATH =
"external/tflite_ovic_testdata/low_res_model.lite";
private static final String FLOAT_MODEL_PATH =
"external/tflite_ovic_testdata/float_model.lite";
private static final String TEST_IMAGE_PATH =
"external/tflite_ovic_testdata/test_image_224.jpg";
private static final String TEST_LOW_RES_IMAGE_PATH =
"external/tflite_ovic_testdata/test_image_128.jpg";
private static final int TEST_IMAGE_GROUNDTRUTH = 653; // "military uniform"
@Before
public void setUp() {
try {
File labelsfile = new File(LABELS_PATH);
labelsInputStream = new FileInputStream(labelsfile);
quantizedModel = loadModelFile(QUANTIZED_MODEL_PATH);
floatModel = loadModelFile(FLOAT_MODEL_PATH);
lowResModel = loadModelFile(LOW_RES_MODEL_PATH);
File imageFile = new File(TEST_IMAGE_PATH);
BufferedImage img = ImageIO.read(imageFile);
testImage = toByteBuffer(img);
// Low res image and models.
imageFile = new File(TEST_LOW_RES_IMAGE_PATH);
img = ImageIO.read(imageFile);
lowResTestImage = toByteBuffer(img);
} catch (IOException e) {
System.out.print(e.getMessage());
}
System.out.println("Successful setup");
}
@Test
public void ovicClassifier_quantizedModelCreateSuccess() throws Exception {
classifier = new OvicClassifier(labelsInputStream, quantizedModel);
assertThat(classifier).isNotNull();
}
@Test
public void ovicClassifier_floatModelCreateSuccess() throws Exception {
classifier = new OvicClassifier(labelsInputStream, floatModel);
assertThat(classifier).isNotNull();
}
@Test
public void ovicClassifier_quantizedModelClassifySuccess() throws Exception {
classifier = new OvicClassifier(labelsInputStream, quantizedModel);
testResult = classifier.classifyByteBuffer(testImage);
assertCorrectTopK(testResult);
}
@Test
public void ovicClassifier_floatModelClassifySuccess() throws Exception {
classifier = new OvicClassifier(labelsInputStream, floatModel);
testResult = classifier.classifyByteBuffer(testImage);
assertCorrectTopK(testResult);
}
@Test
public void ovicClassifier_lowResModelClassifySuccess() throws Exception {
classifier = new OvicClassifier(labelsInputStream, lowResModel);
testResult = classifier.classifyByteBuffer(lowResTestImage);
assertCorrectTopK(testResult);
}
@Test
public void ovicClassifier_latencyNotNull() throws Exception {
classifier = new OvicClassifier(labelsInputStream, floatModel);
testResult = classifier.classifyByteBuffer(testImage);
assertThat(testResult.latencyNano).isNotNull();
}
@Test
public void ovicClassifier_mismatchedInputResolutionFails() throws Exception {
classifier = new OvicClassifier(labelsInputStream, lowResModel);
int[] inputDims = classifier.getInputDims();
assertThat(inputDims[1]).isEqualTo(128);
assertThat(inputDims[2]).isEqualTo(128);
try {
testResult = classifier.classifyByteBuffer(testImage);
fail();
} catch (IllegalArgumentException e) {
// Success.
}
}
private static ByteBuffer toByteBuffer(BufferedImage image) {
ByteBuffer imgData = ByteBuffer.allocateDirect(
image.getHeight() * image.getWidth() * 3);
imgData.order(ByteOrder.nativeOrder());
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
int val = image.getRGB(x, y);
imgData.put((byte) ((val >> 16) & 0xFF));
imgData.put((byte) ((val >> 8) & 0xFF));
imgData.put((byte) (val & 0xFF));
}
}
return imgData;
}
private static void assertCorrectTopK(OvicClassificationResult testResult) {
assertThat(testResult.topKClasses.size()).isGreaterThan(0);
Boolean topKAccurate = false;
// Assert that the correct class is in the top K.
for (int i = 0; i < testResult.topKIndices.size(); i++) {
if (testResult.topKIndices.get(i) == TEST_IMAGE_GROUNDTRUTH) {
topKAccurate = true;
break;
}
}
System.out.println(testResult.toString());
System.out.flush();
assertThat(topKAccurate).isTrue();
}
private static MappedByteBuffer loadModelFile(String modelFilePath) throws IOException {
File modelfile = new File(modelFilePath);
FileInputStream inputStream = new FileInputStream(modelfile);
FileChannel fileChannel = inputStream.getChannel();
long startOffset = 0L;
long declaredLength = fileChannel.size();
return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength);
}
}
@@ -0,0 +1,131 @@
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
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.
==============================================================================*/
package org.tensorflow.ovic;
import static com.google.common.truth.Truth.assertThat;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import javax.imageio.ImageIO;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit test for {@link org.tensorflow.ovic.OvicDetector}. */
@RunWith(JUnit4.class)
public final class OvicDetectorTest {
private OvicDetector detector = null;
private InputStream labelsInputStream = null;
private MappedByteBuffer model = null;
private ByteBuffer testImage = null;
private static final String LABELS_PATH =
"tensorflow/lite/java/ovic/src/testdata/coco_labels.txt";
private static final String MODEL_PATH =
"external/tflite_ovic_testdata/quantized_detect.lite";
private static final String TEST_IMAGE_PATH =
"external/tflite_ovic_testdata/test_image_224.jpg";
private static final int GROUNDTRUTH = 1 /* Person */;
@Before
public void setUp() {
try {
// load models.
model = loadModelFile(MODEL_PATH);
// Load label files;
File labelsfile = new File(LABELS_PATH);
labelsInputStream = new FileInputStream(labelsfile);
// Create detector.
detector = new OvicDetector(labelsInputStream, model);
// Load test image and convert into byte buffer.
File imageFile = new File(TEST_IMAGE_PATH);
BufferedImage rawimg = ImageIO.read(imageFile);
int[] inputDims = detector.getInputDims();
BufferedImage img = new BufferedImage(inputDims[1], inputDims[2], rawimg.getType());
Graphics2D g = img.createGraphics();
g.drawImage(rawimg, 0, 0, inputDims[1], inputDims[2], null);
g.dispose();
testImage = toByteBuffer(img);
} catch (IOException e) {
System.out.println(e.getMessage());
}
System.out.println("Successfully setup");
}
private static MappedByteBuffer loadModelFile(String modelFilePath) throws IOException {
File modelfile = new File(modelFilePath);
FileInputStream inputStream = new FileInputStream(modelfile);
FileChannel fileChannel = inputStream.getChannel();
long startOffset = 0L;
long declaredLength = fileChannel.size();
return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength);
}
private static ByteBuffer toByteBuffer(BufferedImage image) {
ByteBuffer imgData = ByteBuffer.allocateDirect(image.getHeight() * image.getWidth() * 3);
imgData.order(ByteOrder.nativeOrder());
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
int pixelValue = image.getRGB(x, y);
imgData.put((byte) ((pixelValue >> 16) & 0xFF));
imgData.put((byte) ((pixelValue >> 8) & 0xFF));
imgData.put((byte) (pixelValue & 0xFF));
}
}
return imgData;
}
@Test
public void ovicDetector_detectSuccess() throws Exception {
assertThat(detector.detectByteBuffer(testImage, 1)).isTrue();
assertThat(detector.result).isNotNull();
}
@Test
public void ovicDetector_simpleBatchTest() throws Exception {
final int numRepeats = 5;
for (int i = 0; i < numRepeats; i++) {
assertThat(detector.detectByteBuffer(testImage, 1)).isTrue();
OvicDetectionResult result = detector.result;
Boolean detectWithinTop5 = false;
for (int j = 0; j < Math.min(5, result.count); j++) {
if (result.detections.get(j).category == GROUNDTRUTH) {
detectWithinTop5 = true;
break;
}
}
if (!detectWithinTop5) {
System.out.println("---------------- Image " + i + " ---------------------");
System.out.println("Expect category " + GROUNDTRUTH);
System.out.println("Detection results: ");
System.out.println(result.toString());
}
assertThat(detectWithinTop5).isTrue();
}
}
}
+28
View File
@@ -0,0 +1,28 @@
# Testdata for OVIC benchmarker demo App and tests.
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
licenses = ["notice"],
)
filegroup(
name = "ovic_testdata",
srcs = [
"@tflite_ovic_testdata//:detect.lite",
"@tflite_ovic_testdata//:float_model.lite",
"@tflite_ovic_testdata//:low_res_model.lite",
"@tflite_ovic_testdata//:quantized_detect.lite",
"@tflite_ovic_testdata//:quantized_model.lite",
"@tflite_ovic_testdata//:test_image_128.jpg",
"@tflite_ovic_testdata//:test_image_224.jpg",
],
visibility = ["//visibility:public"],
)
exports_files(
[
"labels.txt",
"coco_labels.txt",
],
visibility = ["//visibility:public"],
)
+91
View File
@@ -0,0 +1,91 @@
person
bicycle
car
motorcycle
airplane
bus
train
truck
boat
traffic light
fire hydrant
empty
stop sign
parking meter
bench
bird
cat
dog
horse
sheep
cow
elephant
bear
zebra
giraffe
empty
backpack
umbrella
empty
empty
handbag
tie
suitcase
frisbee
skis
snowboard
sports ball
kite
baseball bat
baseball glove
skateboard
surfboard
tennis racket
bottle
empty
wine glasses
cup
fork
knife
spoon
bowl
banana
apple
sandwich
orange
broccoli
carrot
hot dog
pizza
donut
cake
chair
couch
potted plant
bed
empty
dining table
empty
empty
toilet
empty
tv
laptop
mouse
remote
keyboard
cell phone
microwave
oven
toaster
sink
refrigerator
empty
book
clock
vase
scissors
teddy bear
hair drier
toothbrush
empty
File diff suppressed because it is too large Load Diff