-
-
Notifications
You must be signed in to change notification settings - Fork 16.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Android project for TFLite inference
- Loading branch information
Showing
84 changed files
with
5,825 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
*.iml | ||
.gradle | ||
/local.properties | ||
/.idea/libraries | ||
/.idea/modules.xml | ||
/.idea/workspace.xml | ||
.DS_Store | ||
/build | ||
/captures | ||
.externalNativeBuild | ||
|
||
/.gradle/ | ||
/.idea/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/build | ||
/build/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
apply plugin: 'com.android.application' | ||
apply plugin: 'de.undercouch.download' | ||
|
||
android { | ||
compileSdkVersion 28 | ||
buildToolsVersion '28.0.3' | ||
defaultConfig { | ||
applicationId "org.tensorflow.lite.examples.detection" | ||
minSdkVersion 21 | ||
targetSdkVersion 28 | ||
versionCode 1 | ||
versionName "1.0" | ||
|
||
ndk { | ||
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86' | ||
} | ||
} | ||
buildTypes { | ||
release { | ||
minifyEnabled false | ||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | ||
} | ||
} | ||
aaptOptions { | ||
noCompress "tflite" | ||
} | ||
compileOptions { | ||
sourceCompatibility = '1.8' | ||
targetCompatibility = '1.8' | ||
} | ||
lintOptions { | ||
abortOnError false | ||
} | ||
} | ||
|
||
// import DownloadModels task | ||
project.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets' | ||
project.ext.TMP_DIR = project.buildDir.toString() + '/downloads' | ||
|
||
// Download default models; if you wish to use your own models then | ||
// place them in the "assets" directory and comment out this line. | ||
//apply from: "download_model.gradle" | ||
|
||
//apply from: 'download_model.gradle' | ||
|
||
dependencies { | ||
// implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) | ||
// implementation files('bin/gdbserver') | ||
implementation 'androidx.appcompat:appcompat:1.1.0' | ||
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.1.0' | ||
implementation 'com.google.android.material:material:1.1.0' | ||
implementation 'org.tensorflow:tensorflow-lite:2.3.0' | ||
implementation 'org.tensorflow:tensorflow-lite-gpu:2.3.0' | ||
// implementation 'org.tensorflow:tensorflow-lite-select-tf-ops:2.3.0' | ||
// implementation 'org.tensorflow:tensorflow-lite:2.2.0' | ||
// implementation 'org.tensorflow:tensorflow-lite-gpu:2.2.0' | ||
// implementation 'org.tensorflow:tensorflow-lite:1.14.0' | ||
// implementation(name:'tensorflow-lite', ext:'aar') | ||
// implementation(name:'tensorflow-lite-gpu', ext:'aar') | ||
|
||
// implementation 'org.tensorflow:tensorflow-lite-gpu:1.14.0' | ||
// implementation 'org.tensorflow:tensorflow-lite:0.0.0-gpu-experimental' | ||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' | ||
implementation 'com.google.code.gson:gson:2.8.6' | ||
androidTestImplementation 'androidx.test.ext:junit:1.1.1' | ||
androidTestImplementation 'com.android.support.test:rules:1.0.2' | ||
androidTestImplementation 'com.google.truth:truth:1.0.1' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
|
||
task downloadZipFile(type: Download) { | ||
src 'http://storage.googleapis.com/download.tensorflow.org/models/tflite/coco_ssd_mobilenet_v1_1.0_quant_2018_06_29.zip' | ||
dest new File(buildDir, 'zips/') | ||
overwrite false | ||
} | ||
|
||
|
||
task downloadAndUnzipFile(dependsOn: downloadZipFile, type: Copy) { | ||
from zipTree(downloadZipFile.dest) | ||
into project.ext.ASSET_DIR | ||
} | ||
|
||
|
||
task extractModels(type: Copy) { | ||
dependsOn downloadAndUnzipFile | ||
} | ||
|
||
tasks.whenTaskAdded { task -> | ||
if (task.name == 'assembleDebug') { | ||
task.dependsOn 'extractModels' | ||
} | ||
if (task.name == 'assembleRelease') { | ||
task.dependsOn 'extractModels' | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Add project specific ProGuard rules here. | ||
# You can control the set of applied configuration files using the | ||
# proguardFiles setting in build.gradle. | ||
# | ||
# For more details, see | ||
# http://developer.android.com/guide/developing/tools/proguard.html | ||
|
||
# If your project uses WebView with JS, uncomment the following | ||
# and specify the fully qualified class name to the JavaScript interface | ||
# class: | ||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { | ||
# public *; | ||
#} | ||
|
||
# Uncomment this to preserve the line number information for | ||
# debugging stack traces. | ||
#-keepattributes SourceFile,LineNumberTable | ||
|
||
# If you keep the line number information, uncomment this to | ||
# hide the original source file name. | ||
#-renamesourcefileattribute SourceFile |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
dining_table 27.492085 97.94615 623.1435 444.8627 0.48828125 | ||
knife 342.53433 243.71082 583.89185 416.34595 0.4765625 | ||
cup 68.025925 197.5857 202.02031 374.2206 0.4375 | ||
book 185.43098 139.64153 244.51149 203.37737 0.3125 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
package="org.tensorflow.lite.examples.detection"> | ||
<uses-sdk /> | ||
</manifest> |
165 changes: 165 additions & 0 deletions
165
android/app/src/androidTest/java/org/tensorflow/lite/examples/detection/DetectorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
/* | ||
* Copyright 2020 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 org.tensorflow.lite.examples.detection; | ||
|
||
import static com.google.common.truth.Truth.assertThat; | ||
import static java.lang.Math.abs; | ||
import static java.lang.Math.max; | ||
import static java.lang.Math.min; | ||
|
||
import android.content.res.AssetManager; | ||
import android.graphics.Bitmap; | ||
import android.graphics.Bitmap.Config; | ||
import android.graphics.BitmapFactory; | ||
import android.graphics.Canvas; | ||
import android.graphics.Matrix; | ||
import android.graphics.RectF; | ||
import android.util.Size; | ||
import androidx.test.ext.junit.runners.AndroidJUnit4; | ||
import androidx.test.platform.app.InstrumentationRegistry; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Scanner; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
import org.tensorflow.lite.examples.detection.env.ImageUtils; | ||
import org.tensorflow.lite.examples.detection.tflite.Classifier; | ||
import org.tensorflow.lite.examples.detection.tflite.Classifier.Recognition; | ||
import org.tensorflow.lite.examples.detection.tflite.TFLiteObjectDetectionAPIModel; | ||
|
||
/** Golden test for Object Detection Reference app. */ | ||
@RunWith(AndroidJUnit4.class) | ||
public class DetectorTest { | ||
|
||
private static final int MODEL_INPUT_SIZE = 300; | ||
private static final boolean IS_MODEL_QUANTIZED = true; | ||
private static final String MODEL_FILE = "detect.tflite"; | ||
private static final String LABELS_FILE = "file:///android_asset/coco.txt"; | ||
private static final Size IMAGE_SIZE = new Size(640, 480); | ||
|
||
private Classifier detector; | ||
private Bitmap croppedBitmap; | ||
private Matrix frameToCropTransform; | ||
private Matrix cropToFrameTransform; | ||
|
||
@Before | ||
public void setUp() throws IOException { | ||
AssetManager assetManager = | ||
InstrumentationRegistry.getInstrumentation().getContext().getAssets(); | ||
detector = | ||
TFLiteObjectDetectionAPIModel.create( | ||
assetManager, | ||
MODEL_FILE, | ||
LABELS_FILE, | ||
MODEL_INPUT_SIZE, | ||
IS_MODEL_QUANTIZED); | ||
int cropSize = MODEL_INPUT_SIZE; | ||
int previewWidth = IMAGE_SIZE.getWidth(); | ||
int previewHeight = IMAGE_SIZE.getHeight(); | ||
int sensorOrientation = 0; | ||
croppedBitmap = Bitmap.createBitmap(cropSize, cropSize, Config.ARGB_8888); | ||
|
||
frameToCropTransform = | ||
ImageUtils.getTransformationMatrix( | ||
previewWidth, previewHeight, | ||
cropSize, cropSize, | ||
sensorOrientation, false); | ||
cropToFrameTransform = new Matrix(); | ||
frameToCropTransform.invert(cropToFrameTransform); | ||
} | ||
|
||
@Test | ||
public void detectionResultsShouldNotChange() throws Exception { | ||
Canvas canvas = new Canvas(croppedBitmap); | ||
canvas.drawBitmap(loadImage("table.jpg"), frameToCropTransform, null); | ||
final List<Recognition> results = detector.recognizeImage(croppedBitmap); | ||
final List<Recognition> expected = loadRecognitions("table_results.txt"); | ||
|
||
for (Recognition target : expected) { | ||
// Find a matching result in results | ||
boolean matched = false; | ||
for (Recognition item : results) { | ||
RectF bbox = new RectF(); | ||
cropToFrameTransform.mapRect(bbox, item.getLocation()); | ||
if (item.getTitle().equals(target.getTitle()) | ||
&& matchBoundingBoxes(bbox, target.getLocation()) | ||
&& matchConfidence(item.getConfidence(), target.getConfidence())) { | ||
matched = true; | ||
break; | ||
} | ||
} | ||
assertThat(matched).isTrue(); | ||
} | ||
} | ||
|
||
// Confidence tolerance: absolute 1% | ||
private static boolean matchConfidence(float a, float b) { | ||
return abs(a - b) < 0.01; | ||
} | ||
|
||
// Bounding Box tolerance: overlapped area > 95% of each one | ||
private static boolean matchBoundingBoxes(RectF a, RectF b) { | ||
float areaA = a.width() * a.height(); | ||
float areaB = b.width() * b.height(); | ||
RectF overlapped = | ||
new RectF( | ||
max(a.left, b.left), max(a.top, b.top), min(a.right, b.right), min(a.bottom, b.bottom)); | ||
float overlappedArea = overlapped.width() * overlapped.height(); | ||
return overlappedArea > 0.95 * areaA && overlappedArea > 0.95 * areaB; | ||
} | ||
|
||
private static Bitmap loadImage(String fileName) throws Exception { | ||
AssetManager assetManager = | ||
InstrumentationRegistry.getInstrumentation().getContext().getAssets(); | ||
InputStream inputStream = assetManager.open(fileName); | ||
return BitmapFactory.decodeStream(inputStream); | ||
} | ||
|
||
// The format of result: | ||
// category bbox.left bbox.top bbox.right bbox.bottom confidence | ||
// ... | ||
// Example: | ||
// Apple 99 25 30 75 80 0.99 | ||
// Banana 25 90 75 200 0.98 | ||
// ... | ||
private static List<Recognition> loadRecognitions(String fileName) throws Exception { | ||
AssetManager assetManager = | ||
InstrumentationRegistry.getInstrumentation().getContext().getAssets(); | ||
InputStream inputStream = assetManager.open(fileName); | ||
Scanner scanner = new Scanner(inputStream); | ||
List<Recognition> result = new ArrayList<>(); | ||
while (scanner.hasNext()) { | ||
String category = scanner.next(); | ||
category = category.replace('_', ' '); | ||
if (!scanner.hasNextFloat()) { | ||
break; | ||
} | ||
float left = scanner.nextFloat(); | ||
float top = scanner.nextFloat(); | ||
float right = scanner.nextFloat(); | ||
float bottom = scanner.nextFloat(); | ||
RectF boundingBox = new RectF(left, top, right, bottom); | ||
float confidence = scanner.nextFloat(); | ||
Recognition recognition = new Recognition(null, category, confidence, boundingBox); | ||
result.add(recognition); | ||
} | ||
return result; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
package="org.tensorflow.lite.examples.detection"> | ||
<!-- Tell the system this app requires OpenGL ES 3.1. --> | ||
<uses-feature android:glEsVersion="0x00030001" android:required="true" /> | ||
|
||
<uses-sdk /> | ||
|
||
<uses-permission android:name="android.permission.CAMERA" /> | ||
|
||
<uses-feature android:name="android.hardware.camera" /> | ||
<uses-feature android:name="android.hardware.camera.autofocus" /> | ||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> | ||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> | ||
<uses-permission android:name="android.permission.INTERNET"/> | ||
|
||
<application | ||
android:allowBackup="false" | ||
android:icon="@mipmap/ic_launcher" | ||
android:label="@string/tfe_od_app_name" | ||
android:roundIcon="@mipmap/ic_launcher_round" | ||
android:supportsRtl="true" | ||
android:theme="@style/AppTheme.ObjectDetection" | ||
android:hardwareAccelerated="true" | ||
android:debuggable="true" | ||
android:installLocation="internalOnly"> | ||
|
||
<activity | ||
android:name=".DetectorActivity" | ||
android:label="@string/tfe_od_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> |
Oops, something went wrong.