├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── libraries │ ├── animated_vector_drawable_25_0_1.xml │ ├── appcompat_v7_25_0_1.xml │ ├── design_25_0_1.xml │ ├── espresso_core_2_2_2.xml │ ├── espresso_idling_resource_2_2_2.xml │ ├── exposed_instrumentation_api_publish_0_5.xml │ ├── hamcrest_core_1_3.xml │ ├── hamcrest_integration_1_3.xml │ ├── hamcrest_library_1_3.xml │ ├── javassist_3_16_1_GA.xml │ ├── javawriter_2_1_1.xml │ ├── javax_annotation_api_1_2.xml │ ├── javax_inject_1.xml │ ├── json_simple_1_1_1.xml │ ├── jsr305_2_0_1.xml │ ├── junit_4_12.xml │ ├── junit_4_8_2.xml │ ├── msgpack_0_6_8.xml │ ├── recyclerview_v7_25_0_1.xml │ ├── rules_0_5.xml │ ├── runner_0_5.xml │ ├── support_annotations_25_2_0.xml │ ├── support_compat_25_2_0.xml │ ├── support_core_ui_25_2_0.xml │ ├── support_core_utils_25_2_0.xml │ ├── support_fragment_25_2_0.xml │ ├── support_media_compat_25_2_0.xml │ ├── support_v13_25_2_0.xml │ ├── support_v4_25_2_0.xml │ ├── support_vector_drawable_25_0_1.xml │ └── transition_25_0_1.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── CMakeLists.txt ├── build.gradle ├── libs │ ├── javassist-3.16.1-GA.jar │ ├── json-simple-1.1.1.jar │ ├── junit-4.8.2.jar │ └── msgpack-0.6.8.jar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── de │ │ └── hpi │ │ └── xnor_mxnet │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ ├── de │ │ │ └── hpi │ │ │ │ └── xnor_mxnet │ │ │ │ ├── AutoFitTextureView.java │ │ │ │ ├── BorderedText.java │ │ │ │ ├── CameraConnectionFragment.java │ │ │ │ ├── CameraFrameCapture.java │ │ │ │ ├── CameraLiveViewActivity.java │ │ │ │ ├── GPSTracker.java │ │ │ │ ├── GetWiki.java │ │ │ │ ├── HttpHandler.java │ │ │ │ ├── ImageClassificationTask.java │ │ │ │ ├── ImageUtils.java │ │ │ │ ├── LocationDetails.java │ │ │ │ ├── MainActivity.java │ │ │ │ ├── OverlayView.java │ │ │ │ └── imageclassification │ │ │ │ ├── AbstractClassifier.java │ │ │ │ ├── Classification.java │ │ │ │ ├── ImageClassifier.java │ │ │ │ ├── ImageNetClassifier.java │ │ │ │ └── ModelPreparationTask.java │ │ └── org │ │ │ └── dmlc │ │ │ └── mxnet │ │ │ ├── MxnetException.java │ │ │ └── Predictor.java │ ├── jniLibs │ │ └── arm64-v8a │ │ │ ├── libc++_shared.so │ │ │ ├── libmxnet_predict.so │ │ │ └── libopenblas.so │ ├── native-image-utils │ │ ├── imageutils_jni.cc │ │ ├── yuv2rgb.cc │ │ └── yuv2rgb.h │ └── res │ │ ├── layout │ │ ├── activity_gpslogger.xml │ │ ├── content_gpslogger.xml │ │ ├── main_activity.xml │ │ └── placerecognizer_ui.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── raw │ │ ├── binarized_densenet_28_params │ │ ├── binarized_densenet_28_symbol │ │ ├── binarized_resnet_e_18_params │ │ ├── binarized_resnet_e_18_symbol │ │ └── synset.txt │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ ├── de │ └── hpi │ │ └── xnor_mxnet │ │ └── ExampleUnitTest.java │ └── mxnet │ ├── MxnetException.java │ └── Predictor.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | *.gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea/workspace.xml 38 | 39 | # Keystore files 40 | *.jks 41 | 42 | app/.externalNativeBuild/ 43 | app/src/main/jniLibs 44 | app/src/main/res/raw 45 | 46 | .idea 47 | .cxx 48 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/libraries/animated_vector_drawable_25_0_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/appcompat_v7_25_0_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/design_25_0_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/espresso_core_2_2_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/espresso_idling_resource_2_2_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/exposed_instrumentation_api_publish_0_5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/hamcrest_core_1_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/hamcrest_integration_1_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/hamcrest_library_1_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/javassist_3_16_1_GA.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/javawriter_2_1_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/javax_annotation_api_1_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/javax_inject_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/json_simple_1_1_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/jsr305_2_0_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/junit_4_12.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/junit_4_8_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/msgpack_0_6_8.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/recyclerview_v7_25_0_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/rules_0_5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/runner_0_5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/support_annotations_25_2_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/support_compat_25_2_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/support_core_ui_25_2_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/support_core_utils_25_2_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/support_fragment_25_2_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/support_media_compat_25_2_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/support_v13_25_2_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/support_v4_25_2_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/libraries/support_vector_drawable_25_0_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/transition_25_0_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Android Image Classification 2 | ============================ 3 | 4 | This is an example project capable of performing image classification on a live camera feed, using a binarized neural network on Android. 5 | 6 | Requirements 7 | ------------ 8 | 9 | * android device (minimum required Android version is 4.2 (API level 21)) 10 | 11 | Usage 12 | ----- 13 | 14 | Clone the repository, open and compile the project with Android Studio. Tested to work with Android Studio 4.0 and SDK 29. 15 | If you want to use the library on an device that does not have a armv7 or armv8 processor, you have to recompile the BMXNet library as described [here](https://github.com/hpi-xnor/BMXNet-v2/tree/master/amalgamation), using the correct toolchain. 16 | 17 | Please take care when recompiling the library to set the definition of `BINARY_WORD_32` or `BINARY_WORD_64` according to your target architecture and also to convert the model to the right `BINARY_WORD` using the model converter supplied with BMXNet. 18 | 19 | If you want to use any model other than the two provided (`DenseNet-28` and `ResNetE-18`), you'll have to make sure that 20 | the `symbol` file contains a `softmax` as output op. And that the id of the output layer is set correctly in the symbol json file. 21 | You can find the symbol and model files in `app/src/main/res/raw`. 22 | 23 | If you want to change the used default model (from `DensetNet-28` to `ResNetE-18` or vice versa), you'll have to 24 | change [these two](https://github.com/hpi-xnor/android-image-classification/blob/master/app/src/main/java/de/hpi/xnor_mxnet/imageclassification/ImageNetClassifier.java#L56-L57) 25 | lines. 26 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.1) 2 | 3 | set(SourceLocation src/main/native-image-utils) 4 | 5 | add_library(native-image-utils SHARED "${SourceLocation}/imageutils_jni.cc" "${SourceLocation}/yuv2rgb.cc") 6 | include_directories(${SOURCE}) 7 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion "29.0.3" 6 | defaultConfig { 7 | applicationId "de.hpi.xnor_mxnet" 8 | minSdkVersion 21 9 | targetSdkVersion 29 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | externalNativeBuild { 21 | cmake { 22 | path 'CMakeLists.txt' 23 | } 24 | } 25 | ndkVersion '21.0.6113669' 26 | } 27 | 28 | dependencies { 29 | compile fileTree(include: ['*.jar', '*.so'], dir: 'libs') 30 | androidTestCompile('androidx.test.espresso:espresso-core:3.1.0', { 31 | exclude group: 'com.android.support', module: 'support-annotations' 32 | }) 33 | compile 'androidx.appcompat:appcompat:1.0.2' 34 | compile 'com.google.android.material:material:1.0.0' 35 | testCompile 'junit:junit:4.12' 36 | compile 'androidx.legacy:legacy-support-v13:1.0.0' 37 | } 38 | -------------------------------------------------------------------------------- /app/libs/javassist-3.16.1-GA.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpi-xnor/android-image-classification/5013eba9e212a5ba8e98e4bc3f8c9d58d1df46f6/app/libs/javassist-3.16.1-GA.jar -------------------------------------------------------------------------------- /app/libs/json-simple-1.1.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpi-xnor/android-image-classification/5013eba9e212a5ba8e98e4bc3f8c9d58d1df46f6/app/libs/json-simple-1.1.1.jar -------------------------------------------------------------------------------- /app/libs/junit-4.8.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpi-xnor/android-image-classification/5013eba9e212a5ba8e98e4bc3f8c9d58d1df46f6/app/libs/junit-4.8.2.jar -------------------------------------------------------------------------------- /app/libs/msgpack-0.6.8.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpi-xnor/android-image-classification/5013eba9e212a5ba8e98e4bc3f8c9d58d1df46f6/app/libs/msgpack-0.6.8.jar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/timoesterreich/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/de/hpi/xnor_mxnet/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package de.hpi.xnor_mxnet; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("de.hpi.placerecognizer", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | 12 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/de/hpi/xnor_mxnet/AutoFitTextureView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package de.hpi.xnor_mxnet; 18 | 19 | import android.content.Context; 20 | import android.util.AttributeSet; 21 | import android.util.Size; 22 | import android.view.TextureView; 23 | 24 | /** 25 | * A {@link TextureView} that can be adjusted to a specified aspect ratio. 26 | */ 27 | public class AutoFitTextureView extends TextureView { 28 | 29 | private int mRatioWidth = 0; 30 | private int mRatioHeight = 0; 31 | 32 | public AutoFitTextureView(Context context) { 33 | this(context, null); 34 | } 35 | 36 | public AutoFitTextureView(Context context, AttributeSet attrs) { 37 | this(context, attrs, 0); 38 | } 39 | 40 | public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) { 41 | super(context, attrs, defStyle); 42 | } 43 | 44 | /** 45 | * Sets the aspect ratio for this view. The size of the view will be measured based on the ratio 46 | * calculated from the parameters. Note that the actual sizes of parameters don't matter, that 47 | * is, calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result. 48 | * 49 | * @param width Relative horizontal size 50 | * @param height Relative vertical size 51 | */ 52 | public void setAspectRatio(int width, int height) { 53 | if (width < 0 || height < 0) { 54 | throw new IllegalArgumentException("Size cannot be negative."); 55 | } 56 | mRatioWidth = width; 57 | mRatioHeight = height; 58 | requestLayout(); 59 | } 60 | 61 | @Override 62 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 63 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 64 | 65 | int width = MeasureSpec.getSize(widthMeasureSpec); 66 | int height = MeasureSpec.getSize(heightMeasureSpec); 67 | if (0 == mRatioWidth || 0 == mRatioHeight) { 68 | setMeasuredDimension(width, height); 69 | } else { 70 | if (width < height * mRatioWidth / mRatioHeight) { 71 | setMeasuredDimension(width, width * mRatioHeight / mRatioWidth); 72 | } else { 73 | setMeasuredDimension(height * mRatioWidth / mRatioHeight, height); 74 | } 75 | } 76 | } 77 | 78 | } -------------------------------------------------------------------------------- /app/src/main/java/de/hpi/xnor_mxnet/BorderedText.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 The TensorFlow Authors. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | package de.hpi.xnor_mxnet; 17 | 18 | import android.graphics.Canvas; 19 | import android.graphics.Color; 20 | import android.graphics.Paint; 21 | import android.graphics.Paint.Align; 22 | import android.graphics.Paint.Style; 23 | import android.graphics.Rect; 24 | import android.graphics.Typeface; 25 | import java.util.Vector; 26 | 27 | /** 28 | * A class that encapsulates the tedious bits of rendering legible, bordered text onto a canvas. 29 | */ 30 | public class BorderedText { 31 | private final Paint interiorPaint; 32 | private final Paint exteriorPaint; 33 | 34 | private final float textSize; 35 | 36 | /** 37 | * Creates a left-aligned bordered text object with a white interior, and a black exterior with 38 | * the specified text size. 39 | * 40 | * @param textSize text size in pixels 41 | */ 42 | public BorderedText(final float textSize) { 43 | this(Color.WHITE, Color.BLACK, textSize); 44 | } 45 | 46 | /** 47 | * Create a bordered text object with the specified interior and exterior colors, text size and 48 | * alignment. 49 | * 50 | * @param interiorColor the interior text color 51 | * @param exteriorColor the exterior text color 52 | * @param textSize text size in pixels 53 | */ 54 | public BorderedText(final int interiorColor, final int exteriorColor, final float textSize) { 55 | interiorPaint = new Paint(); 56 | interiorPaint.setTextSize(textSize); 57 | interiorPaint.setColor(interiorColor); 58 | interiorPaint.setStyle(Style.FILL); 59 | interiorPaint.setAntiAlias(false); 60 | interiorPaint.setAlpha(255); 61 | 62 | exteriorPaint = new Paint(); 63 | exteriorPaint.setTextSize(textSize); 64 | exteriorPaint.setColor(exteriorColor); 65 | exteriorPaint.setStyle(Style.FILL_AND_STROKE); 66 | exteriorPaint.setStrokeWidth(textSize / 8); 67 | exteriorPaint.setAntiAlias(false); 68 | exteriorPaint.setAlpha(255); 69 | 70 | this.textSize = textSize; 71 | } 72 | 73 | public void setTypeface(Typeface typeface) { 74 | interiorPaint.setTypeface(typeface); 75 | exteriorPaint.setTypeface(typeface); 76 | } 77 | 78 | public void drawText(final Canvas canvas, final float posX, final float posY, final String text) { 79 | canvas.drawText(text, posX, posY, exteriorPaint); 80 | canvas.drawText(text, posX, posY, interiorPaint); 81 | } 82 | 83 | public void drawLines(Canvas canvas, final float posX, final float posY, Vector lines) { 84 | int lineNum = 0; 85 | for (final String line : lines) { 86 | drawText(canvas, posX, posY - getTextSize() * (lines.size() - lineNum - 1), line); 87 | ++lineNum; 88 | } 89 | } 90 | 91 | public void setInteriorColor(final int color) { 92 | interiorPaint.setColor(color); 93 | } 94 | 95 | public void setExteriorColor(final int color) { 96 | exteriorPaint.setColor(color); 97 | } 98 | 99 | public float getTextSize() { 100 | return textSize; 101 | } 102 | 103 | public void setAlpha(final int alpha) { 104 | interiorPaint.setAlpha(alpha); 105 | exteriorPaint.setAlpha(alpha); 106 | } 107 | 108 | public void getTextBounds( 109 | final String line, final int index, final int count, final Rect lineBounds) { 110 | interiorPaint.getTextBounds(line, index, count, lineBounds); 111 | } 112 | 113 | public void setTextAlign(final Align align) { 114 | interiorPaint.setTextAlign(align); 115 | exteriorPaint.setTextAlign(align); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app/src/main/java/de/hpi/xnor_mxnet/CameraConnectionFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 The TensorFlow Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package de.hpi.xnor_mxnet; 18 | 19 | import android.app.Activity; 20 | import android.app.AlertDialog; 21 | import android.app.Dialog; 22 | import android.app.DialogFragment; 23 | import android.app.Fragment; 24 | import android.content.Context; 25 | import android.content.DialogInterface; 26 | import android.content.res.Configuration; 27 | import android.graphics.ImageFormat; 28 | import android.graphics.Matrix; 29 | import android.graphics.RectF; 30 | import android.graphics.SurfaceTexture; 31 | import android.hardware.camera2.CameraAccessException; 32 | import android.hardware.camera2.CameraCaptureSession; 33 | import android.hardware.camera2.CameraCharacteristics; 34 | import android.hardware.camera2.CameraDevice; 35 | import android.hardware.camera2.CameraManager; 36 | import android.hardware.camera2.CaptureRequest; 37 | import android.hardware.camera2.CaptureResult; 38 | import android.hardware.camera2.TotalCaptureResult; 39 | import android.hardware.camera2.params.StreamConfigurationMap; 40 | import android.media.ImageReader; 41 | import android.media.ImageReader.OnImageAvailableListener; 42 | import android.os.Bundle; 43 | import android.os.Handler; 44 | import android.os.HandlerThread; 45 | import android.text.TextUtils; 46 | import android.util.Size; 47 | import android.util.SparseIntArray; 48 | import android.view.LayoutInflater; 49 | import android.view.Surface; 50 | import android.view.TextureView; 51 | import android.view.View; 52 | import android.view.ViewGroup; 53 | import android.view.ViewGroup.LayoutParams; 54 | import android.widget.Toast; 55 | import java.util.ArrayList; 56 | import java.util.Arrays; 57 | import java.util.Collections; 58 | import java.util.Comparator; 59 | import java.util.List; 60 | import java.util.concurrent.Semaphore; 61 | import java.util.concurrent.TimeUnit; 62 | import java.util.stream.Stream; 63 | 64 | public class CameraConnectionFragment extends Fragment { 65 | 66 | /** 67 | * The camera preview size will be chosen to be the smallest frame by pixel size capable of 68 | * containing a DESIRED_SIZE x DESIRED_SIZE square. 69 | */ 70 | private static final int MINIMUM_PREVIEW_SIZE = 320; 71 | 72 | /** 73 | * Conversion from screen rotation to JPEG orientation. 74 | */ 75 | private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); 76 | private static final String FRAGMENT_DIALOG = "dialog"; 77 | 78 | static { 79 | ORIENTATIONS.append(Surface.ROTATION_0, 90); 80 | ORIENTATIONS.append(Surface.ROTATION_90, 0); 81 | ORIENTATIONS.append(Surface.ROTATION_180, 270); 82 | ORIENTATIONS.append(Surface.ROTATION_270, 180); 83 | } 84 | /** 85 | * {@link android.view.TextureView.SurfaceTextureListener} handles several lifecycle events on a 86 | * {@link TextureView}. 87 | */ 88 | private final TextureView.SurfaceTextureListener surfaceTextureListener = 89 | new TextureView.SurfaceTextureListener() { 90 | @Override 91 | public void onSurfaceTextureAvailable( 92 | final SurfaceTexture texture, final int width, final int height) { 93 | openCamera(width, height); 94 | } 95 | 96 | @Override 97 | public void onSurfaceTextureSizeChanged( 98 | final SurfaceTexture texture, final int width, final int height) { 99 | configureTransform(width, height); 100 | } 101 | 102 | @Override 103 | public boolean onSurfaceTextureDestroyed(final SurfaceTexture texture) { 104 | return true; 105 | } 106 | 107 | @Override 108 | public void onSurfaceTextureUpdated(final SurfaceTexture texture) {} 109 | }; 110 | 111 | /** 112 | * Callback for Activities to use to initialize their data once the 113 | * selected preview size is known. 114 | */ 115 | public interface ConnectionCallback { 116 | void onPreviewSizeChosen(Size size, int cameraRotation); 117 | } 118 | 119 | /** 120 | * ID of the current {@link CameraDevice}. 121 | */ 122 | private String cameraId; 123 | 124 | /** 125 | * An {@link AutoFitTextureView} for camera preview. 126 | */ 127 | private AutoFitTextureView textureView; 128 | 129 | /** 130 | * A {@link CameraCaptureSession } for camera preview. 131 | */ 132 | private CameraCaptureSession captureSession; 133 | 134 | /** 135 | * A reference to the opened {@link CameraDevice}. 136 | */ 137 | private CameraDevice cameraDevice; 138 | 139 | /** 140 | * The rotation in degrees of the camera sensor from the display. 141 | */ 142 | private Integer sensorOrientation; 143 | 144 | /** 145 | * The {@link android.util.Size} of camera preview. 146 | */ 147 | private Size previewSize; 148 | 149 | /** 150 | * {@link android.hardware.camera2.CameraDevice.StateCallback} 151 | * is called when {@link CameraDevice} changes its state. 152 | */ 153 | private final CameraDevice.StateCallback stateCallback = 154 | new CameraDevice.StateCallback() { 155 | @Override 156 | public void onOpened(final CameraDevice cd) { 157 | // This method is called when the camera is opened. We start camera preview here. 158 | cameraOpenCloseLock.release(); 159 | cameraDevice = cd; 160 | createCameraPreviewSession(); 161 | } 162 | 163 | @Override 164 | public void onDisconnected(final CameraDevice cd) { 165 | cameraOpenCloseLock.release(); 166 | cd.close(); 167 | cameraDevice = null; 168 | } 169 | 170 | @Override 171 | public void onError(final CameraDevice cd, final int error) { 172 | cameraOpenCloseLock.release(); 173 | cd.close(); 174 | cameraDevice = null; 175 | final Activity activity = getActivity(); 176 | if (null != activity) { 177 | activity.finish(); 178 | } 179 | } 180 | }; 181 | 182 | /** 183 | * An additional thread for running tasks that shouldn't block the UI. 184 | */ 185 | private HandlerThread backgroundThread; 186 | 187 | /** 188 | * A {@link Handler} for running tasks in the background. 189 | */ 190 | private Handler backgroundHandler; 191 | 192 | /** 193 | * An {@link ImageReader} that handles preview frame capture. 194 | */ 195 | private ImageReader previewReader; 196 | 197 | /** 198 | * {@link android.hardware.camera2.CaptureRequest.Builder} for the camera preview 199 | */ 200 | private CaptureRequest.Builder previewRequestBuilder; 201 | 202 | /** 203 | * {@link CaptureRequest} generated by {@link #previewRequestBuilder} 204 | */ 205 | private CaptureRequest previewRequest; 206 | 207 | /** 208 | * A {@link Semaphore} to prevent the app from exiting before closing the camera. 209 | */ 210 | private final Semaphore cameraOpenCloseLock = new Semaphore(1); 211 | 212 | /** 213 | * A {@link OnImageAvailableListener} to receive frames as they are available. 214 | */ 215 | private final OnImageAvailableListener imageListener; 216 | 217 | /** The input size in pixels desired by TensorFlow (width and height of a square bitmap). */ 218 | private final Size inputSize; 219 | 220 | /** 221 | * The layout identifier to inflate for this Fragment. 222 | */ 223 | private final int layout; 224 | 225 | 226 | private final ConnectionCallback cameraConnectionCallback; 227 | 228 | private CameraConnectionFragment( 229 | final ConnectionCallback connectionCallback, 230 | final OnImageAvailableListener imageListener, 231 | final int layout, 232 | final Size inputSize) { 233 | this.cameraConnectionCallback = connectionCallback; 234 | this.imageListener = imageListener; 235 | this.layout = layout; 236 | this.inputSize = inputSize; 237 | } 238 | 239 | /** 240 | * Shows a {@link Toast} on the UI thread. 241 | * 242 | * @param text The message to show 243 | */ 244 | private void showToast(final String text) { 245 | final Activity activity = getActivity(); 246 | if (activity != null) { 247 | activity.runOnUiThread( 248 | new Runnable() { 249 | @Override 250 | public void run() { 251 | Toast.makeText(activity, text, Toast.LENGTH_SHORT).show(); 252 | } 253 | }); 254 | } 255 | } 256 | 257 | /** 258 | * Given {@code choices} of {@code Size}s supported by a camera, chooses the smallest one whose 259 | * width and height are at least as large as the minimum of both, or an exact match if possible. 260 | * 261 | * @param choices The list of sizes that the camera supports for the intended output class 262 | * @param width The minimum desired width 263 | * @param height The minimum desired height 264 | * @return The optimal {@code Size}, or an arbitrary one if none were big enough 265 | */ 266 | private static Size chooseOptimalSize(final Size[] choices, final int width, final int height) { 267 | final int minSize = Math.max(Math.min(width, height), MINIMUM_PREVIEW_SIZE); 268 | final Size desiredSize = new Size(width, height); 269 | 270 | // Collect the supported resolutions that are at least as big as the preview Surface 271 | boolean exactSizeFound = false; 272 | final List bigEnough = new ArrayList(); 273 | final List tooSmall = new ArrayList(); 274 | for (final Size option : choices) { 275 | if (option.equals(desiredSize)) { 276 | // Set the size but don't return yet so that remaining sizes will still be logged. 277 | exactSizeFound = true; 278 | } 279 | 280 | if (option.getHeight() >= minSize && option.getWidth() >= minSize) { 281 | bigEnough.add(option); 282 | } else { 283 | tooSmall.add(option); 284 | } 285 | } 286 | 287 | if (exactSizeFound) { 288 | return desiredSize; 289 | } 290 | 291 | // Pick the smallest of those, assuming we found any 292 | if (bigEnough.size() > 0) { 293 | final Size chosenSize = Collections.min(bigEnough, new CompareSizesByArea()); 294 | return chosenSize; 295 | } else { 296 | return choices[0]; 297 | } 298 | } 299 | 300 | public static CameraConnectionFragment newInstance( 301 | final ConnectionCallback callback, 302 | final OnImageAvailableListener imageListener, 303 | final int layout, 304 | final Size inputSize) { 305 | return new CameraConnectionFragment(callback, imageListener, layout, inputSize); 306 | } 307 | 308 | @Override 309 | public View onCreateView( 310 | final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { 311 | return inflater.inflate(layout, container, false); 312 | } 313 | 314 | @Override 315 | public void onViewCreated(final View view, final Bundle savedInstanceState) { 316 | textureView = (AutoFitTextureView) view.findViewById(R.id.texture); 317 | } 318 | 319 | @Override 320 | public void onActivityCreated(final Bundle savedInstanceState) { 321 | super.onActivityCreated(savedInstanceState); 322 | } 323 | 324 | @Override 325 | public void onResume() { 326 | super.onResume(); 327 | startBackgroundThread(); 328 | 329 | // When the screen is turned off and turned back on, the SurfaceTexture is already 330 | // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open 331 | // a camera and start preview from here (otherwise, we wait until the surface is ready in 332 | // the SurfaceTextureListener). 333 | if (textureView.isAvailable()) { 334 | openCamera(textureView.getWidth(), textureView.getHeight()); 335 | } else { 336 | textureView.setSurfaceTextureListener(surfaceTextureListener); 337 | } 338 | } 339 | 340 | @Override 341 | public void onPause() { 342 | closeCamera(); 343 | stopBackgroundThread(); 344 | super.onPause(); 345 | } 346 | 347 | /** 348 | * Sets up member variables related to camera. 349 | * 350 | * @param width The width of available size for camera preview 351 | * @param height The height of available size for camera preview 352 | */ 353 | private void setUpCameraOutputs(final int width, final int height) { 354 | final Activity activity = getActivity(); 355 | final CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE); 356 | try { 357 | for (final String cameraId : manager.getCameraIdList()) { 358 | final CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); 359 | 360 | // We don't use a front facing camera in this sample. 361 | final Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING); 362 | if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) { 363 | continue; 364 | } 365 | 366 | final StreamConfigurationMap map = 367 | characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 368 | 369 | if (map == null) { 370 | continue; 371 | } 372 | sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); 373 | 374 | // Danger, W.R.! Attempting to use too large a preview size could exceed the camera 375 | // bus' bandwidth limitation, resulting in gorgeous previews but the storage of 376 | // garbage capture data. 377 | previewSize = 378 | chooseOptimalSize( 379 | map.getOutputSizes(SurfaceTexture.class), 380 | inputSize.getWidth(), 381 | inputSize.getHeight()); 382 | 383 | // We fit the aspect ratio of TextureView to the size of preview we picked. 384 | final int orientation = getResources().getConfiguration().orientation; 385 | if (orientation == Configuration.ORIENTATION_LANDSCAPE) { 386 | textureView.setAspectRatio(previewSize.getWidth(), previewSize.getHeight()); 387 | } else { 388 | textureView.setAspectRatio(previewSize.getHeight(), previewSize.getWidth()); 389 | } 390 | 391 | CameraConnectionFragment.this.cameraId = cameraId; 392 | } 393 | } catch (final CameraAccessException e) { 394 | e.printStackTrace(); 395 | } catch (final NullPointerException e) { 396 | // Currently an NPE is thrown when the Camera2API is used but not supported on the 397 | // device this code runs. 398 | // TODO(andrewharp): abstract ErrorDialog/RuntimeException handling out into new method and 399 | // reuse throughout app. 400 | ErrorDialog.newInstance(getString(R.string.camera_error)) 401 | .show(getChildFragmentManager(), FRAGMENT_DIALOG); 402 | throw new RuntimeException(getString(R.string.camera_error)); 403 | } 404 | 405 | cameraConnectionCallback.onPreviewSizeChosen(previewSize, sensorOrientation); 406 | } 407 | 408 | /** 409 | * Opens the camera specified by {@link CameraConnectionFragment#cameraId}. 410 | */ 411 | private void openCamera(final int width, final int height) { 412 | setUpCameraOutputs(width, height); 413 | configureTransform(width, height); 414 | 415 | final Activity activity = getActivity(); 416 | final CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE); 417 | try { 418 | if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { 419 | throw new RuntimeException("Time out waiting to lock camera opening."); 420 | } 421 | manager.openCamera(cameraId, stateCallback, backgroundHandler); 422 | } catch (final CameraAccessException e) { 423 | e.printStackTrace(); 424 | } catch (final InterruptedException e) { 425 | throw new RuntimeException("Interrupted while trying to lock camera opening.", e); 426 | } 427 | } 428 | 429 | /** 430 | * Closes the current {@link CameraDevice}. 431 | */ 432 | private void closeCamera() { 433 | try { 434 | cameraOpenCloseLock.acquire(); 435 | if (null != captureSession) { 436 | captureSession.close(); 437 | captureSession = null; 438 | } 439 | if (null != cameraDevice) { 440 | cameraDevice.close(); 441 | cameraDevice = null; 442 | } 443 | if (null != previewReader) { 444 | previewReader.close(); 445 | previewReader = null; 446 | } 447 | } catch (final InterruptedException e) { 448 | throw new RuntimeException("Interrupted while trying to lock camera closing.", e); 449 | } finally { 450 | cameraOpenCloseLock.release(); 451 | } 452 | } 453 | 454 | /** 455 | * Starts a background thread and its {@link Handler}. 456 | */ 457 | private void startBackgroundThread() { 458 | backgroundThread = new HandlerThread("ImageListener"); 459 | backgroundThread.start(); 460 | backgroundHandler = new Handler(backgroundThread.getLooper()); 461 | } 462 | 463 | /** 464 | * Stops the background thread and its {@link Handler}. 465 | */ 466 | private void stopBackgroundThread() { 467 | backgroundThread.quitSafely(); 468 | try { 469 | backgroundThread.join(); 470 | backgroundThread = null; 471 | backgroundHandler = null; 472 | } catch (final InterruptedException e) { 473 | e.printStackTrace(); 474 | } 475 | } 476 | 477 | private final CameraCaptureSession.CaptureCallback captureCallback = 478 | new CameraCaptureSession.CaptureCallback() { 479 | @Override 480 | public void onCaptureProgressed( 481 | final CameraCaptureSession session, 482 | final CaptureRequest request, 483 | final CaptureResult partialResult) {} 484 | 485 | @Override 486 | public void onCaptureCompleted( 487 | final CameraCaptureSession session, 488 | final CaptureRequest request, 489 | final TotalCaptureResult result) {} 490 | }; 491 | 492 | /** 493 | * Creates a new {@link CameraCaptureSession} for camera preview. 494 | */ 495 | private void createCameraPreviewSession() { 496 | try { 497 | final SurfaceTexture texture = textureView.getSurfaceTexture(); 498 | assert texture != null; 499 | 500 | // We configure the size of default buffer to be the size of camera preview we want. 501 | texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight()); 502 | 503 | // This is the output Surface we need to start preview. 504 | final Surface surface = new Surface(texture); 505 | 506 | // We set up a CaptureRequest.Builder with the output Surface. 507 | previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 508 | previewRequestBuilder.addTarget(surface); 509 | 510 | // Create the reader for the preview frames 511 | previewReader = 512 | ImageReader.newInstance(previewSize.getWidth(), previewSize.getHeight(), ImageFormat.YUV_420_888, 2); 513 | 514 | previewReader.setOnImageAvailableListener(imageListener, backgroundHandler); 515 | previewRequestBuilder.addTarget(previewReader.getSurface()); 516 | 517 | // Here, we create a CameraCaptureSession for camera preview. 518 | cameraDevice.createCaptureSession( 519 | Arrays.asList(surface, previewReader.getSurface()), 520 | // Arrays.asList(surface), 521 | new CameraCaptureSession.StateCallback() { 522 | 523 | @Override 524 | public void onConfigured(final CameraCaptureSession cameraCaptureSession) { 525 | // The camera is already closed 526 | if (null == cameraDevice) { 527 | return; 528 | } 529 | 530 | // When the session is ready, we start displaying the preview. 531 | captureSession = cameraCaptureSession; 532 | try { 533 | // Auto focus should be continuous for camera preview. 534 | previewRequestBuilder.set( 535 | CaptureRequest.CONTROL_AF_MODE, 536 | CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); 537 | // Flash is automatically enabled when necessary. 538 | previewRequestBuilder.set( 539 | CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); 540 | 541 | // Finally, we start displaying the camera preview. 542 | previewRequest = previewRequestBuilder.build(); 543 | captureSession.setRepeatingRequest( 544 | previewRequest, captureCallback, backgroundHandler); 545 | } catch (final CameraAccessException e) { 546 | e.printStackTrace(); 547 | } 548 | } 549 | 550 | @Override 551 | public void onConfigureFailed(final CameraCaptureSession cameraCaptureSession) { 552 | showToast("Failed"); 553 | } 554 | }, 555 | null); 556 | } catch (final CameraAccessException e) { 557 | e.printStackTrace(); 558 | } 559 | } 560 | 561 | /** 562 | * Configures the necessary {@link android.graphics.Matrix} transformation to `mTextureView`. 563 | * This method should be called after the camera preview size is determined in 564 | * setUpCameraOutputs and also the size of `mTextureView` is fixed. 565 | * 566 | * @param viewWidth The width of `mTextureView` 567 | * @param viewHeight The height of `mTextureView` 568 | */ 569 | private void configureTransform(final int viewWidth, final int viewHeight) { 570 | final Activity activity = getActivity(); 571 | if (null == textureView || null == previewSize || null == activity) { 572 | return; 573 | } 574 | final int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); 575 | final Matrix matrix = new Matrix(); 576 | final RectF viewRect = new RectF(0, 0, viewWidth, viewHeight); 577 | final RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth()); 578 | final float centerX = viewRect.centerX(); 579 | final float centerY = viewRect.centerY(); 580 | if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) { 581 | bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY()); 582 | matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL); 583 | final float scale = 584 | Math.max( 585 | (float) viewHeight / previewSize.getHeight(), 586 | (float) viewWidth / previewSize.getWidth()); 587 | matrix.postScale(scale, scale, centerX, centerY); 588 | matrix.postRotate(90 * (rotation - 2), centerX, centerY); 589 | } else if (Surface.ROTATION_180 == rotation) { 590 | matrix.postRotate(180, centerX, centerY); 591 | } 592 | textureView.setTransform(matrix); 593 | } 594 | 595 | /** 596 | * Compares two {@code Size}s based on their areas. 597 | */ 598 | static class CompareSizesByArea implements Comparator { 599 | @Override 600 | public int compare(final Size lhs, final Size rhs) { 601 | // We cast here to ensure the multiplications won't overflow 602 | return Long.signum( 603 | (long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight()); 604 | } 605 | } 606 | 607 | /** 608 | * Shows an error message dialog. 609 | */ 610 | public static class ErrorDialog extends DialogFragment { 611 | private static final String ARG_MESSAGE = "message"; 612 | 613 | public static ErrorDialog newInstance(final String message) { 614 | final ErrorDialog dialog = new ErrorDialog(); 615 | final Bundle args = new Bundle(); 616 | args.putString(ARG_MESSAGE, message); 617 | dialog.setArguments(args); 618 | return dialog; 619 | } 620 | 621 | @Override 622 | public Dialog onCreateDialog(final Bundle savedInstanceState) { 623 | final Activity activity = getActivity(); 624 | return new AlertDialog.Builder(activity) 625 | .setMessage(getArguments().getString(ARG_MESSAGE)) 626 | .setPositiveButton( 627 | android.R.string.ok, 628 | new DialogInterface.OnClickListener() { 629 | @Override 630 | public void onClick(final DialogInterface dialogInterface, final int i) { 631 | activity.finish(); 632 | } 633 | }) 634 | .create(); 635 | } 636 | } 637 | } 638 | -------------------------------------------------------------------------------- /app/src/main/java/de/hpi/xnor_mxnet/CameraFrameCapture.java: -------------------------------------------------------------------------------- 1 | package de.hpi.xnor_mxnet; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.app.AlertDialog; 6 | import android.app.Dialog; 7 | import android.app.DialogFragment; 8 | import android.app.Fragment; 9 | import android.content.Context; 10 | import android.content.DialogInterface; 11 | import android.content.pm.PackageManager; 12 | import android.content.res.Configuration; 13 | import android.graphics.ImageFormat; 14 | import android.graphics.Matrix; 15 | import android.graphics.Point; 16 | import android.graphics.Rect; 17 | import android.graphics.RectF; 18 | import android.graphics.SurfaceTexture; 19 | import android.graphics.YuvImage; 20 | import android.hardware.camera2.CameraAccessException; 21 | import android.hardware.camera2.CameraCaptureSession; 22 | import android.hardware.camera2.CameraCharacteristics; 23 | import android.hardware.camera2.CameraDevice; 24 | import android.hardware.camera2.CameraManager; 25 | import android.hardware.camera2.CaptureRequest; 26 | import android.hardware.camera2.params.StreamConfigurationMap; 27 | import android.media.Image; 28 | import android.media.ImageReader; 29 | import android.os.Bundle; 30 | import android.os.Handler; 31 | import android.os.HandlerThread; 32 | import androidx.annotation.NonNull; 33 | import androidx.core.content.ContextCompat; 34 | import androidx.legacy.app.FragmentCompat; 35 | 36 | import android.util.Size; 37 | import android.util.SparseIntArray; 38 | import android.view.LayoutInflater; 39 | import android.view.Surface; 40 | import android.view.TextureView; 41 | import android.view.View; 42 | import android.view.ViewGroup; 43 | 44 | import java.io.ByteArrayOutputStream; 45 | import java.nio.ByteBuffer; 46 | import java.util.ArrayList; 47 | import java.util.Arrays; 48 | import java.util.Collections; 49 | import java.util.Comparator; 50 | import java.util.List; 51 | import java.util.concurrent.Semaphore; 52 | import java.util.concurrent.TimeUnit; 53 | 54 | 55 | public class CameraFrameCapture extends Fragment 56 | implements View.OnClickListener, FragmentCompat.OnRequestPermissionsResultCallback { 57 | 58 | private static final int REQUEST_CAMERA_PERMISSION = 2; 59 | private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); 60 | private static final int MAX_PREVIEW_WIDTH = 1080; 61 | private static final int MAX_PREVIEW_HEIGHT = 1920; 62 | private static final String FRAGMENT_DIALOG = ""; 63 | 64 | static { 65 | ORIENTATIONS.append(Surface.ROTATION_0, 90); 66 | ORIENTATIONS.append(Surface.ROTATION_90, 0); 67 | ORIENTATIONS.append(Surface.ROTATION_180, 270); 68 | ORIENTATIONS.append(Surface.ROTATION_270, 180); 69 | } 70 | 71 | private final TextureView.SurfaceTextureListener mSurfaceTextureListener 72 | = new TextureView.SurfaceTextureListener() { 73 | 74 | @Override 75 | public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) { 76 | openCamera(width, height); 77 | } 78 | 79 | @Override 80 | public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) { 81 | configureTransform(width, height); 82 | } 83 | 84 | @Override 85 | public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) { 86 | return true; 87 | } 88 | 89 | @Override 90 | public void onSurfaceTextureUpdated(SurfaceTexture texture) { 91 | 92 | } 93 | 94 | }; 95 | 96 | /** 97 | * ID of the current {@link CameraDevice}. 98 | */ 99 | private String mCameraId; 100 | 101 | /** 102 | * An {@link AutoFitTextureView} for camera preview. 103 | */ 104 | private AutoFitTextureView mTextureView; 105 | 106 | /** 107 | * A {@link CameraCaptureSession } for camera preview. 108 | */ 109 | private CameraCaptureSession mCaptureSession; 110 | 111 | /** 112 | * A reference to the opened {@link CameraDevice}. 113 | */ 114 | private CameraDevice mCameraDevice; 115 | 116 | /** 117 | * The {@link android.util.Size} of camera preview. 118 | */ 119 | private Size mPreviewSize; 120 | 121 | /** 122 | * {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state. 123 | */ 124 | private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { 125 | 126 | @Override 127 | public void onOpened(@NonNull CameraDevice cameraDevice) { 128 | // This method is called when the camera is opened. We start camera preview here. 129 | mCameraOpenCloseLock.release(); 130 | mCameraDevice = cameraDevice; 131 | createCameraPreviewSession(); 132 | } 133 | 134 | @Override 135 | public void onDisconnected(@NonNull CameraDevice cameraDevice) { 136 | mCameraOpenCloseLock.release(); 137 | cameraDevice.close(); 138 | mCameraDevice = null; 139 | } 140 | 141 | @Override 142 | public void onError(@NonNull CameraDevice cameraDevice, int error) { 143 | mCameraOpenCloseLock.release(); 144 | cameraDevice.close(); 145 | mCameraDevice = null; 146 | Activity activity = getActivity(); 147 | if (null != activity) { 148 | activity.finish(); 149 | } 150 | } 151 | }; 152 | 153 | /** 154 | * An additional thread for running tasks that shouldn't block the UI. 155 | */ 156 | private HandlerThread mBackgroundThread; 157 | 158 | /** 159 | * A {@link Handler} for running tasks in the background. 160 | */ 161 | private Handler mBackgroundHandler; 162 | 163 | /** 164 | * An {@link ImageReader} that handles still image capture. 165 | */ 166 | private ImageReader mImageReader; 167 | 168 | /** 169 | * This a callback object for the {@link ImageReader}. "onImageAvailable" will be called when a 170 | * still image is ready to be saved. 171 | */ 172 | private ImageReader.OnImageAvailableListener mOnImageAvailableListener; 173 | 174 | /** 175 | * {@link CaptureRequest.Builder} for the camera preview 176 | */ 177 | private CaptureRequest.Builder mPreviewRequestBuilder; 178 | 179 | /** 180 | * {@link CaptureRequest} generated by {@link #mPreviewRequestBuilder} 181 | */ 182 | private CaptureRequest mPreviewRequest; 183 | 184 | /** 185 | * A {@link Semaphore} to prevent the app from exiting before closing the camera. 186 | */ 187 | private Semaphore mCameraOpenCloseLock = new Semaphore(1); 188 | 189 | /** 190 | * A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture. 191 | */ 192 | private CameraCaptureSession.CaptureCallback mCaptureCallback 193 | = new CameraCaptureSession.CaptureCallback() { 194 | }; 195 | 196 | private final ConnectionCallback mCameraConnectionCallback; 197 | 198 | public interface ConnectionCallback { 199 | void onPreviewSizeChosen(Size size); 200 | } 201 | 202 | private CameraFrameCapture(ConnectionCallback connectionCallback, ImageReader.OnImageAvailableListener imageListener) { 203 | this.mCameraConnectionCallback = connectionCallback; 204 | this.mOnImageAvailableListener = imageListener; 205 | } 206 | 207 | public static CameraFrameCapture newInstance( 208 | final ConnectionCallback connectionCallback, 209 | final ImageReader.OnImageAvailableListener imageListener) { 210 | return new CameraFrameCapture(connectionCallback, imageListener); 211 | } 212 | 213 | /** 214 | * Given {@code choices} of {@code Size}s supported by a camera, choose the smallest one that 215 | * is at least as large as the respective texture view size, and that is at most as large as the 216 | * respective max size, and whose aspect ratio matches with the specified value. If such size 217 | * doesn't exist, choose the largest one that is at most as large as the respective max size, 218 | * and whose aspect ratio matches with the specified value. 219 | * 220 | * @param choices The list of sizes that the camera supports for the intended output 221 | * class 222 | * @param textureViewWidth The width of the texture view relative to sensor coordinate 223 | * @param textureViewHeight The height of the texture view relative to sensor coordinate 224 | * @param maxWidth The maximum width that can be chosen 225 | * @param maxHeight The maximum height that can be chosen 226 | * @param aspectRatio The aspect ratio 227 | * @return The optimal {@code Size}, or an arbitrary one if none were big enough 228 | */ 229 | private static Size chooseOptimalSize(Size[] choices, int textureViewWidth, 230 | int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) { 231 | 232 | // Collect the supported resolutions that are at least as big as the preview Surface 233 | List bigEnough = new ArrayList<>(); 234 | // Collect the supported resolutions that are smaller than the preview Surface 235 | List notBigEnough = new ArrayList<>(); 236 | int w = aspectRatio.getWidth(); 237 | int h = aspectRatio.getHeight(); 238 | for (Size option : choices) { 239 | if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight && 240 | option.getHeight() == option.getWidth() * h / w) { 241 | if (option.getWidth() >= textureViewWidth && 242 | option.getHeight() >= textureViewHeight) { 243 | bigEnough.add(option); 244 | } else { 245 | notBigEnough.add(option); 246 | } 247 | } 248 | } 249 | 250 | // Pick the smallest of those big enough. If there is no one big enough, pick the 251 | // largest of those not big enough. 252 | if (bigEnough.size() > 0) { 253 | return Collections.min(bigEnough, new CompareSizesByArea()); 254 | } else if (notBigEnough.size() > 0) { 255 | return Collections.max(notBigEnough, new CompareSizesByArea()); 256 | } else { 257 | return choices[0]; 258 | } 259 | } 260 | 261 | 262 | 263 | @Override 264 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 265 | Bundle savedInstanceState) { 266 | return inflater.inflate(R.layout.placerecognizer_ui, container, false); 267 | } 268 | 269 | @Override 270 | public void onViewCreated(final View view, Bundle savedInstanceState) { 271 | mTextureView = (AutoFitTextureView) view.findViewById(R.id.texture); 272 | } 273 | 274 | @Override 275 | public void onActivityCreated(Bundle savedInstanceState) { 276 | super.onActivityCreated(savedInstanceState); 277 | } 278 | 279 | @Override 280 | public void onResume() { 281 | super.onResume(); 282 | startBackgroundThread(); 283 | 284 | // When the screen is turned off and turned back on, the SurfaceTexture is already 285 | // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open 286 | // a camera and start preview from here (otherwise, we wait until the surface is ready in 287 | // the SurfaceTextureListener). 288 | if (mTextureView.isAvailable()) { 289 | openCamera(mTextureView.getWidth(), mTextureView.getHeight()); 290 | } else { 291 | mTextureView.setSurfaceTextureListener(mSurfaceTextureListener); 292 | } 293 | } 294 | 295 | @Override 296 | public void onPause() { 297 | closeCamera(); 298 | stopBackgroundThread(); 299 | super.onPause(); 300 | } 301 | 302 | private void requestCameraPermission() { 303 | if (FragmentCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) { 304 | new ConfirmationDialog().show(getChildFragmentManager(), FRAGMENT_DIALOG); 305 | } else { 306 | FragmentCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 307 | REQUEST_CAMERA_PERMISSION); 308 | } 309 | } 310 | 311 | @Override 312 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 313 | @NonNull int[] grantResults) { 314 | if (requestCode != REQUEST_CAMERA_PERMISSION) { 315 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 316 | } 317 | } 318 | 319 | /** 320 | * Sets up member variables related to camera. 321 | * 322 | * @param width The width of available size for camera preview 323 | * @param height The height of available size for camera preview 324 | */ 325 | private void setUpCameraOutputs(int width, int height) { 326 | Activity activity = getActivity(); 327 | int mSensorOrientation; 328 | CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE); 329 | try { 330 | for (String cameraId : manager.getCameraIdList()) { 331 | CameraCharacteristics characteristics 332 | = manager.getCameraCharacteristics(cameraId); 333 | 334 | StreamConfigurationMap map = characteristics.get( 335 | CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 336 | if (map == null) { 337 | continue; 338 | } 339 | 340 | // For still image captures, we use the largest available size. 341 | Size largest = Collections.max( 342 | Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), 343 | new CompareSizesByArea()); 344 | mImageReader = 345 | ImageReader.newInstance( 346 | mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.YUV_420_888, /*maxImages*/2); 347 | // mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.JPEG, /*maxImages*/2); 348 | mImageReader.setOnImageAvailableListener( 349 | mOnImageAvailableListener, mBackgroundHandler); 350 | 351 | // Find out if we need to swap dimension to get the preview size relative to sensor 352 | // coordinate. 353 | int displayRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); 354 | //noinspection ConstantConditions 355 | mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); 356 | boolean swappedDimensions = false; 357 | switch (displayRotation) { 358 | case Surface.ROTATION_0: 359 | case Surface.ROTATION_180: 360 | if (mSensorOrientation == 90 || mSensorOrientation == 270) { 361 | swappedDimensions = true; 362 | } 363 | break; 364 | case Surface.ROTATION_90: 365 | case Surface.ROTATION_270: 366 | if (mSensorOrientation == 0 || mSensorOrientation == 180) { 367 | swappedDimensions = true; 368 | } 369 | break; 370 | default: 371 | } 372 | 373 | Point displaySize = new Point(); 374 | activity.getWindowManager().getDefaultDisplay().getSize(displaySize); 375 | int rotatedPreviewWidth = width; 376 | int rotatedPreviewHeight = height; 377 | int maxPreviewWidth = displaySize.x; 378 | int maxPreviewHeight = displaySize.y; 379 | 380 | if (swappedDimensions) { 381 | //noinspection SuspiciousNameCombination 382 | rotatedPreviewWidth = height; 383 | //noinspection SuspiciousNameCombination 384 | rotatedPreviewHeight = width; 385 | //noinspection SuspiciousNameCombination 386 | maxPreviewWidth = displaySize.y; 387 | //noinspection SuspiciousNameCombination 388 | maxPreviewHeight = displaySize.x; 389 | } 390 | 391 | if (maxPreviewWidth > MAX_PREVIEW_WIDTH) { 392 | maxPreviewWidth = MAX_PREVIEW_WIDTH; 393 | } 394 | 395 | if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) { 396 | maxPreviewHeight = MAX_PREVIEW_HEIGHT; 397 | } 398 | 399 | // Danger, W.R.! Attempting to use too large a preview size could exceed the camera 400 | // bus' bandwidth limitation, resulting in gorgeous previews but the storage of 401 | // garbage capture data. 402 | mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), 403 | rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth, 404 | maxPreviewHeight, largest); 405 | 406 | // We fit the aspect ratio of TextureView to the size of preview we picked. 407 | int orientation = getResources().getConfiguration().orientation; 408 | if (orientation == Configuration.ORIENTATION_LANDSCAPE) { 409 | mTextureView.setAspectRatio( 410 | mPreviewSize.getWidth(), mPreviewSize.getHeight()); 411 | } else { 412 | mTextureView.setAspectRatio( 413 | mPreviewSize.getHeight(), mPreviewSize.getWidth()); 414 | } 415 | mCameraId = cameraId; 416 | return; 417 | } 418 | } catch (Exception e) { 419 | e.printStackTrace(); 420 | } 421 | } 422 | 423 | /** 424 | * Opens the camera specified by {@link CameraFrameCapture#mCameraId}. 425 | */ 426 | private void openCamera(int width, int height) { 427 | if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) 428 | != PackageManager.PERMISSION_GRANTED) { 429 | requestCameraPermission(); 430 | return; 431 | } 432 | setUpCameraOutputs(width, height); 433 | configureTransform(width, height); 434 | Activity activity = getActivity(); 435 | CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE); 436 | try { 437 | if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { 438 | throw new RuntimeException("Time out waiting to lock camera opening."); 439 | } 440 | manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler); 441 | mCameraConnectionCallback.onPreviewSizeChosen(mPreviewSize); 442 | } catch (CameraAccessException e) { 443 | e.printStackTrace(); 444 | } catch (InterruptedException e) { 445 | throw new RuntimeException("Interrupted while trying to lock camera opening.", e); 446 | } 447 | } 448 | 449 | /** 450 | * Closes the current {@link CameraDevice}. 451 | */ 452 | private void closeCamera() { 453 | try { 454 | mCameraOpenCloseLock.acquire(); 455 | if (null != mCaptureSession) { 456 | mCaptureSession.close(); 457 | mCaptureSession = null; 458 | } 459 | if (null != mCameraDevice) { 460 | mCameraDevice.close(); 461 | mCameraDevice = null; 462 | } 463 | if (null != mImageReader) { 464 | mImageReader.close(); 465 | mImageReader = null; 466 | } 467 | } catch (InterruptedException e) { 468 | throw new RuntimeException("Interrupted while trying to lock camera closing.", e); 469 | } finally { 470 | mCameraOpenCloseLock.release(); 471 | } 472 | } 473 | 474 | /** 475 | * Starts a background thread and its {@link Handler}. 476 | */ 477 | private void startBackgroundThread() { 478 | mBackgroundThread = new HandlerThread("CameraBackground"); 479 | mBackgroundThread.start(); 480 | mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); 481 | } 482 | 483 | /** 484 | * Stops the background thread and its {@link Handler}. 485 | */ 486 | private void stopBackgroundThread() { 487 | mBackgroundThread.quitSafely(); 488 | try { 489 | mBackgroundThread.join(); 490 | mBackgroundThread = null; 491 | mBackgroundHandler = null; 492 | } catch (InterruptedException e) { 493 | e.printStackTrace(); 494 | } 495 | } 496 | 497 | /** 498 | * Creates a new {@link CameraCaptureSession} for camera preview. 499 | */ 500 | private void createCameraPreviewSession() { 501 | try { 502 | SurfaceTexture texture = mTextureView.getSurfaceTexture(); 503 | assert texture != null; 504 | 505 | // We configure the size of default buffer to be the size of camera preview we want. 506 | texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); 507 | 508 | // This is the output Surface we need to start preview. 509 | Surface surface = new Surface(texture); 510 | 511 | // We set up a CaptureRequest.Builder with the output Surface. 512 | mPreviewRequestBuilder 513 | = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 514 | mPreviewRequestBuilder.addTarget(surface); 515 | mPreviewRequestBuilder.addTarget(mImageReader.getSurface()); 516 | 517 | // Here, we create a CameraCaptureSession for camera preview. 518 | mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), 519 | new CameraCaptureSession.StateCallback() { 520 | 521 | @Override 522 | public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { 523 | // The camera is already closed 524 | if (null == mCameraDevice) { 525 | return; 526 | } 527 | 528 | // When the session is ready, we start displaying the preview. 529 | mCaptureSession = cameraCaptureSession; 530 | try { 531 | // Auto focus should be continuous for camera preview. 532 | mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, 533 | CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); 534 | 535 | // Finally, we start displaying the camera preview. 536 | mPreviewRequest = mPreviewRequestBuilder.build(); 537 | mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler); 538 | } catch (CameraAccessException e) { 539 | e.printStackTrace(); 540 | } 541 | } 542 | 543 | @Override 544 | public void onConfigureFailed( 545 | @NonNull CameraCaptureSession cameraCaptureSession) { 546 | } 547 | }, null 548 | ); 549 | } catch (CameraAccessException e) { 550 | e.printStackTrace(); 551 | } 552 | } 553 | 554 | /** 555 | * Configures the necessary {@link android.graphics.Matrix} transformation to `mTextureView`. 556 | * This method should be called after the camera preview size is determined in 557 | * setUpCameraOutputs and also the size of `mTextureView` is fixed. 558 | * 559 | * @param viewWidth The width of `mTextureView` 560 | * @param viewHeight The height of `mTextureView` 561 | */ 562 | private void configureTransform(int viewWidth, int viewHeight) { 563 | Activity activity = getActivity(); 564 | if (null == mTextureView || null == mPreviewSize || null == activity) { 565 | return; 566 | } 567 | int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); 568 | Matrix matrix = new Matrix(); 569 | RectF viewRect = new RectF(0, 0, viewWidth, viewHeight); 570 | RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth()); 571 | float centerX = viewRect.centerX(); 572 | float centerY = viewRect.centerY(); 573 | if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) { 574 | bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY()); 575 | matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL); 576 | float scale = Math.max( 577 | (float) viewHeight / mPreviewSize.getHeight(), 578 | (float) viewWidth / mPreviewSize.getWidth()); 579 | matrix.postScale(scale, scale, centerX, centerY); 580 | matrix.postRotate(90 * (rotation - 2), centerX, centerY); 581 | } else if (Surface.ROTATION_180 == rotation) { 582 | matrix.postRotate(180, centerX, centerY); 583 | } 584 | mTextureView.setTransform(matrix); 585 | } 586 | 587 | @Override 588 | public void onClick(View view) { 589 | } 590 | 591 | /** 592 | * Compares two {@code Size}s based on their areas. 593 | */ 594 | static class CompareSizesByArea implements Comparator { 595 | 596 | @Override 597 | public int compare(Size lhs, Size rhs) { 598 | // We cast here to ensure the multiplications won't overflow 599 | return Long.signum((long) lhs.getWidth() * lhs.getHeight() - 600 | (long) rhs.getWidth() * rhs.getHeight()); 601 | } 602 | 603 | } 604 | 605 | /** 606 | * Shows OK/Cancel confirmation dialog about camera permission. 607 | */ 608 | public static class ConfirmationDialog extends DialogFragment { 609 | 610 | @Override 611 | public Dialog onCreateDialog(Bundle savedInstanceState) { 612 | final Fragment parent = getParentFragment(); 613 | return new AlertDialog.Builder(getActivity()) 614 | .setMessage("CreateTest")//R.string.request_permission) 615 | .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 616 | @Override 617 | public void onClick(DialogInterface dialog, int which) { 618 | FragmentCompat.requestPermissions(parent, 619 | new String[]{Manifest.permission.CAMERA}, 620 | REQUEST_CAMERA_PERMISSION); 621 | } 622 | }) 623 | .setNegativeButton(android.R.string.cancel, 624 | new DialogInterface.OnClickListener() { 625 | @Override 626 | public void onClick(DialogInterface dialog, int which) { 627 | Activity activity = parent.getActivity(); 628 | if (activity != null) { 629 | activity.finish(); 630 | } 631 | } 632 | }) 633 | .create(); 634 | } 635 | } 636 | } -------------------------------------------------------------------------------- /app/src/main/java/de/hpi/xnor_mxnet/CameraLiveViewActivity.java: -------------------------------------------------------------------------------- 1 | package de.hpi.xnor_mxnet; 2 | 3 | 4 | import android.app.Activity; 5 | 6 | public abstract class CameraLiveViewActivity extends Activity { 7 | protected boolean computing = false; 8 | 9 | abstract public void runCameraLiveView(); 10 | 11 | public void setComputing(boolean computing) { 12 | this.computing = computing; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/de/hpi/xnor_mxnet/GPSTracker.java: -------------------------------------------------------------------------------- 1 | package de.hpi.xnor_mxnet; 2 | 3 | import android.Manifest; 4 | import android.app.AlertDialog; 5 | import android.app.Service; 6 | import android.content.Context; 7 | import android.content.DialogInterface; 8 | import android.content.Intent; 9 | import android.content.pm.PackageManager; 10 | import android.location.Location; 11 | import android.location.LocationListener; 12 | import android.location.LocationManager; 13 | import android.os.Bundle; 14 | import android.os.IBinder; 15 | import android.provider.Settings; 16 | import androidx.annotation.Nullable; 17 | import androidx.core.app.ActivityCompat; 18 | 19 | import android.util.Log; 20 | 21 | public class GPSTracker extends Service implements LocationListener { 22 | 23 | private final Context mContext; 24 | 25 | // flag for GPS status 26 | boolean isGPSEnabled = false; 27 | 28 | // flag for network status 29 | boolean isNetworkEnabled = false; 30 | 31 | boolean canGetLocation = false; 32 | 33 | Location location; 34 | double latitude; 35 | double longitude; 36 | 37 | // The minimum distance to change Updates in meters 38 | private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters 39 | 40 | // The minimum time between updates in milliseconds 41 | private static final long MIN_TIME_BW_UPDATES = 1000 * 60; // 1 minute 42 | 43 | // Declaring a Location Manager 44 | protected LocationManager locationManager; 45 | 46 | public GPSTracker(Context context) { 47 | this.mContext = context; 48 | getLocation(); 49 | } 50 | 51 | public Location getLocation() { 52 | try { 53 | locationManager = (LocationManager) mContext 54 | .getSystemService(LOCATION_SERVICE); 55 | 56 | // getting GPS status 57 | isGPSEnabled = locationManager 58 | .isProviderEnabled(LocationManager.GPS_PROVIDER); 59 | 60 | // getting network status 61 | isNetworkEnabled = locationManager 62 | .isProviderEnabled(LocationManager.NETWORK_PROVIDER); 63 | 64 | if (isGPSEnabled && isNetworkEnabled) { 65 | this.canGetLocation = true; 66 | if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && 67 | ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { 68 | 69 | AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext); 70 | alertDialog.setTitle("GPS permissions"); 71 | alertDialog.setMessage("Please grant the App permissions to use GPS."); 72 | } 73 | if (isNetworkEnabled) { 74 | locationManager.requestLocationUpdates( 75 | LocationManager.NETWORK_PROVIDER, 76 | MIN_TIME_BW_UPDATES, 77 | MIN_DISTANCE_CHANGE_FOR_UPDATES, this); 78 | Log.d("Network", "Network Enabled"); 79 | if (locationManager != null) { 80 | location = locationManager 81 | .getLastKnownLocation(LocationManager.NETWORK_PROVIDER); 82 | if (location != null) { 83 | latitude = location.getLatitude(); 84 | longitude = location.getLongitude(); 85 | } 86 | } 87 | } 88 | // if GPS Enabled get lat/long using GPS Services 89 | if (isGPSEnabled) { 90 | if (location == null) { 91 | locationManager.requestLocationUpdates( 92 | LocationManager.GPS_PROVIDER, 93 | MIN_TIME_BW_UPDATES, 94 | MIN_DISTANCE_CHANGE_FOR_UPDATES, this); 95 | Log.d("GPS", "GPS Enabled"); 96 | if (locationManager != null) { 97 | location = locationManager 98 | .getLastKnownLocation(LocationManager.GPS_PROVIDER); 99 | if (location != null) { 100 | latitude = location.getLatitude(); 101 | longitude = location.getLongitude(); 102 | } 103 | } 104 | } 105 | } 106 | } 107 | 108 | } catch (Exception e) { 109 | e.printStackTrace(); 110 | } 111 | 112 | return location; 113 | } 114 | 115 | public double getLatitude() { 116 | if (location != null) { 117 | latitude = location.getLatitude(); 118 | } 119 | 120 | return latitude; 121 | } 122 | 123 | public double getLongitude() { 124 | if (location != null) { 125 | longitude = location.getLongitude(); 126 | } 127 | 128 | return longitude; 129 | } 130 | 131 | public void showSettingsAlert() { 132 | AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext); 133 | 134 | // Setting Dialog Title 135 | alertDialog.setTitle("GPS is settings"); 136 | 137 | // Setting Dialog Message 138 | alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu?"); 139 | 140 | // On pressing Settings button 141 | alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() { 142 | public void onClick(DialogInterface dialog, int which) { 143 | Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); 144 | mContext.startActivity(intent); 145 | } 146 | }); 147 | 148 | // on pressing cancel button 149 | alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { 150 | public void onClick(DialogInterface dialog, int which) { 151 | dialog.cancel(); 152 | } 153 | }); 154 | 155 | // Showing Alert Message 156 | alertDialog.show(); 157 | } 158 | 159 | public boolean canGetLocation() { 160 | return this.canGetLocation; 161 | } 162 | 163 | @Nullable 164 | @Override 165 | public IBinder onBind(Intent intent) { 166 | return null; 167 | } 168 | 169 | @Override 170 | public void onLocationChanged(Location location) { 171 | 172 | } 173 | 174 | @Override 175 | public void onStatusChanged(String s, int i, Bundle bundle) { 176 | 177 | } 178 | 179 | @Override 180 | public void onProviderEnabled(String s) { 181 | 182 | } 183 | 184 | @Override 185 | public void onProviderDisabled(String s) { 186 | 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /app/src/main/java/de/hpi/xnor_mxnet/GetWiki.java: -------------------------------------------------------------------------------- 1 | package de.hpi.xnor_mxnet; 2 | 3 | import android.os.AsyncTask; 4 | import android.util.Log; 5 | 6 | import org.json.JSONException; 7 | import org.json.JSONObject; 8 | 9 | import java.text.SimpleDateFormat; 10 | import java.util.Date; 11 | import java.util.Iterator; 12 | 13 | //Json parser for wikipedia 14 | class GetWiki extends AsyncTask { 15 | 16 | private static final String WIK = "WikiActivity"; 17 | 18 | @Override 19 | protected void onPreExecute() { 20 | super.onPreExecute(); 21 | } 22 | 23 | @Override 24 | protected LocationDetails doInBackground(String... strings) { 25 | String urlTitle = strings[0]; 26 | String url = "https://en.wikipedia.org/w/api.php?format=json&action=query&redirects&exintro=&explaintext=&prop=extracts&titles=" + urlTitle; 27 | HttpHandler handler = new HttpHandler(); 28 | 29 | // Making a request to url and getting response 30 | String jsonStr = handler.makeServiceCall(url); 31 | 32 | if (jsonStr != null) { 33 | try { 34 | JSONObject jsonObj = new JSONObject(jsonStr); 35 | JSONObject pages = jsonObj.getJSONObject("query").getJSONObject("pages"); 36 | Iterator keys = pages.keys(); 37 | String pageId= keys.next(); 38 | JSONObject page = pages.getJSONObject(pageId); 39 | 40 | String title = page.getString("title"); 41 | String extract = page.getString("extract"); 42 | SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss"); 43 | String date = sdf.format(new Date()); 44 | 45 | return new LocationDetails(title,extract,date); 46 | } catch (JSONException e) { 47 | e.printStackTrace(); 48 | } 49 | } 50 | return null; 51 | } 52 | 53 | @Override 54 | protected void onPostExecute(LocationDetails result) { 55 | super.onPostExecute(result); 56 | Log.i(WIK,"good"); 57 | } 58 | } -------------------------------------------------------------------------------- /app/src/main/java/de/hpi/xnor_mxnet/HttpHandler.java: -------------------------------------------------------------------------------- 1 | package de.hpi.xnor_mxnet; 2 | 3 | import android.util.Log; 4 | 5 | import java.io.BufferedInputStream; 6 | import java.io.BufferedReader; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.InputStreamReader; 10 | import java.net.HttpURLConnection; 11 | import java.net.MalformedURLException; 12 | import java.net.ProtocolException; 13 | import java.net.URL; 14 | 15 | class HttpHandler { 16 | 17 | private static final String TAG = HttpHandler.class.getSimpleName(); 18 | 19 | HttpHandler() { 20 | } 21 | 22 | String makeServiceCall(String reqUrl) { 23 | String response = null; 24 | try { 25 | URL url = new URL(reqUrl); 26 | HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 27 | conn.setRequestMethod("GET"); 28 | // read the response 29 | InputStream in = new BufferedInputStream(conn.getInputStream()); 30 | response = convertStreamToString(in); 31 | } catch (MalformedURLException e) { 32 | Log.e(TAG, "MalformedURLException: " + e.getMessage()); 33 | } catch (ProtocolException e) { 34 | Log.e(TAG, "ProtocolException: " + e.getMessage()); 35 | } catch (IOException e) { 36 | Log.e(TAG, "IOException: " + e.getMessage()); 37 | } catch (Exception e) { 38 | Log.e(TAG, "Exception: " + e.getMessage()); 39 | } 40 | return response; 41 | } 42 | 43 | private String convertStreamToString(InputStream is) { 44 | BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 45 | StringBuilder sb = new StringBuilder(); 46 | 47 | String line; 48 | try { 49 | while ((line = reader.readLine()) != null) { 50 | sb.append(line).append('\n'); 51 | } 52 | } catch (IOException e) { 53 | e.printStackTrace(); 54 | } finally { 55 | try { 56 | is.close(); 57 | } catch (IOException e) { 58 | e.printStackTrace(); 59 | } 60 | } 61 | return sb.toString(); 62 | } 63 | } -------------------------------------------------------------------------------- /app/src/main/java/de/hpi/xnor_mxnet/ImageClassificationTask.java: -------------------------------------------------------------------------------- 1 | package de.hpi.xnor_mxnet; 2 | 3 | import android.app.Activity; 4 | import android.graphics.Bitmap; 5 | import android.widget.TextView; 6 | 7 | import de.hpi.xnor_mxnet.imageclassification.Classification; 8 | import de.hpi.xnor_mxnet.imageclassification.ImageClassifier; 9 | import de.hpi.xnor_mxnet.imageclassification.ImageNetClassifier; 10 | 11 | class ImageClassificationTask implements Runnable { 12 | 13 | private final Bitmap mImage; 14 | private final ImageClassifier mClassifier; 15 | private final CameraLiveViewActivity mActivity; 16 | 17 | ImageClassificationTask(Bitmap image, CameraLiveViewActivity activity, ImageClassifier classifier) { 18 | mImage = image; 19 | mClassifier = classifier; 20 | mActivity = activity; 21 | } 22 | 23 | private String join(String delimiter, String[] s) { 24 | StringBuilder out = new StringBuilder(); 25 | out.append(s[0]); 26 | for (int i = 1; i < s.length; ++i) { 27 | out.append(delimiter).append(s[i]); 28 | } 29 | return out.toString(); 30 | } 31 | 32 | @Override 33 | public void run() { 34 | final Classification[] results = mClassifier.classifyImage(mImage); 35 | final String[] resultStrings = new String[results.length]; 36 | 37 | for (int i = 0; i < results.length; ++i) { 38 | resultStrings[i] = String.format("%s, %.3f", results[i].get_label(), results[i].get_probability()); 39 | } 40 | 41 | mActivity.runOnUiThread(new Runnable() { 42 | @Override 43 | public void run() { 44 | TextView textView = (TextView) mActivity.findViewById(R.id.classDisplay); 45 | String text = join("\n", resultStrings); 46 | if (textView != null) { 47 | textView.setText(text); 48 | } 49 | System.out.println(text); 50 | mActivity.setComputing(false); 51 | } 52 | }); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/de/hpi/xnor_mxnet/ImageUtils.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 The TensorFlow Authors. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | package de.hpi.xnor_mxnet; 17 | 18 | 19 | import android.graphics.Matrix; 20 | 21 | public class ImageUtils { 22 | /** 23 | * Utility method to compute the allocated size in bytes of a YUV420SP image 24 | * of the given dimensions. 25 | */ 26 | public static int getYUVByteSize(final int width, final int height) { 27 | // The luminance plane requires 1 byte per pixel. 28 | final int ySize = width * height; 29 | 30 | // The UV plane works on 2x2 blocks, so dimensions with odd size must be rounded up. 31 | // Each 2x2 block takes 2 bytes to encode, one each for U and V. 32 | final int uvSize = ((width + 1) / 2) * ((height + 1) / 2) * 2; 33 | 34 | return ySize + uvSize; 35 | } 36 | 37 | /** 38 | * Converts YUV420 semi-planar data to ARGB 8888 data using the supplied width 39 | * and height. The input and output must already be allocated and non-null. 40 | * For efficiency, no error checking is performed. 41 | * 42 | * @param input The array of YUV 4:2:0 input data. 43 | * @param output A pre-allocated array for the ARGB 8:8:8:8 output data. 44 | * @param width The width of the input image. 45 | * @param height The height of the input image. 46 | * @param halfSize If true, downsample to 50% in each dimension, otherwise not. 47 | */ 48 | public static native void convertYUV420SPToARGB8888( 49 | byte[] input, int[] output, int width, int height, boolean halfSize); 50 | 51 | /** 52 | * Converts YUV420 semi-planar data to ARGB 8888 data using the supplied width 53 | * and height. The input and output must already be allocated and non-null. 54 | * For efficiency, no error checking is performed. 55 | * 56 | * @param y 57 | * @param u 58 | * @param v 59 | * @param uvPixelStride 60 | * @param width The width of the input image. 61 | * @param height The height of the input image. 62 | * @param halfSize If true, downsample to 50% in each dimension, otherwise not. 63 | * @param output A pre-allocated array for the ARGB 8:8:8:8 output data. 64 | */ 65 | public static native void convertYUV420ToARGB8888( 66 | byte[] y, 67 | byte[] u, 68 | byte[] v, 69 | int[] output, 70 | int width, 71 | int height, 72 | int yRowStride, 73 | int uvRowStride, 74 | int uvPixelStride, 75 | boolean halfSize); 76 | 77 | /** 78 | * Converts YUV420 semi-planar data to RGB 565 data using the supplied width 79 | * and height. The input and output must already be allocated and non-null. 80 | * For efficiency, no error checking is performed. 81 | * 82 | * @param input The array of YUV 4:2:0 input data. 83 | * @param output A pre-allocated array for the RGB 5:6:5 output data. 84 | * @param width The width of the input image. 85 | * @param height The height of the input image. 86 | */ 87 | public static native void convertYUV420SPToRGB565( 88 | byte[] input, byte[] output, int width, int height); 89 | 90 | /** 91 | * Returns a transformation matrix from one reference frame into another. 92 | * Handles cropping (if maintaining aspect ratio is desired) and rotation. 93 | * 94 | * @param srcWidth Width of source frame. 95 | * @param srcHeight Height of source frame. 96 | * @param dstWidth Width of destination frame. 97 | * @param dstHeight Height of destination frame. 98 | * @param applyRotation Amount of rotation to apply from one frame to another. 99 | * Must be a multiple of 90. 100 | * @param maintainAspectRatio If true, will ensure that scaling in x and y remains constant, 101 | * cropping the image if necessary. 102 | * @return The transformation fulfilling the desired requirements. 103 | */ 104 | public static Matrix getTransformationMatrix( 105 | final int srcWidth, 106 | final int srcHeight, 107 | final int dstWidth, 108 | final int dstHeight, 109 | final int applyRotation, 110 | final boolean maintainAspectRatio) { 111 | final Matrix matrix = new Matrix(); 112 | 113 | if (applyRotation != 0) { 114 | // Translate so center of image is at origin. 115 | matrix.postTranslate(-srcWidth / 2.0f, -srcHeight / 2.0f); 116 | 117 | // Rotate around origin. 118 | matrix.postRotate(applyRotation); 119 | } 120 | 121 | // Account for the already applied rotation, if any, and then determine how 122 | // much scaling is needed for each axis. 123 | final boolean transpose = (Math.abs(applyRotation) + 90) % 180 == 0; 124 | 125 | final int inWidth = transpose ? srcHeight : srcWidth; 126 | final int inHeight = transpose ? srcWidth : srcHeight; 127 | 128 | // Apply scaling if necessary. 129 | if (inWidth != dstWidth || inHeight != dstHeight) { 130 | final float scaleFactorX = dstWidth / (float) inWidth; 131 | final float scaleFactorY = dstHeight / (float) inHeight; 132 | 133 | if (maintainAspectRatio) { 134 | // Scale by minimum factor so that dst is filled completely while 135 | // maintaining the aspect ratio. Some image may fall off the edge. 136 | final float scaleFactor = Math.max(scaleFactorX, scaleFactorY); 137 | matrix.postScale(scaleFactor, scaleFactor); 138 | } else { 139 | // Scale exactly to fill dst from src. 140 | matrix.postScale(scaleFactorX, scaleFactorY); 141 | } 142 | } 143 | 144 | if (applyRotation != 0) { 145 | // Translate back from origin centered reference to destination frame. 146 | matrix.postTranslate(dstWidth / 2.0f, dstHeight / 2.0f); 147 | } 148 | 149 | return matrix; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /app/src/main/java/de/hpi/xnor_mxnet/LocationDetails.java: -------------------------------------------------------------------------------- 1 | package de.hpi.xnor_mxnet; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | class LocationDetails { 7 | 8 | public String title; 9 | String descritpion; 10 | String date; 11 | 12 | LocationDetails(String title, String descritpion, String date) { 13 | this.title = title; 14 | this.descritpion = descritpion; 15 | this.date = date; 16 | } 17 | 18 | Map toMap() { 19 | HashMap result = new HashMap<>(); 20 | result.put("title", title); 21 | result.put("description", descritpion); 22 | result.put("date", date); 23 | 24 | return result; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/de/hpi/xnor_mxnet/MainActivity.java: -------------------------------------------------------------------------------- 1 | package de.hpi.xnor_mxnet; 2 | 3 | import android.Manifest; 4 | import android.app.Fragment; 5 | import android.content.pm.PackageManager; 6 | import android.graphics.Bitmap; 7 | import android.graphics.Canvas; 8 | import android.graphics.Matrix; 9 | import android.graphics.Paint; 10 | import android.graphics.Typeface; 11 | import android.media.Image; 12 | import android.media.ImageReader; 13 | import android.os.Build; 14 | import android.os.Bundle; 15 | import android.os.Handler; 16 | import android.os.HandlerThread; 17 | import android.os.Trace; 18 | import android.util.Log; 19 | import android.util.Size; 20 | import android.util.TypedValue; 21 | import android.view.KeyEvent; 22 | import android.view.WindowManager; 23 | import android.widget.Toast; 24 | 25 | import java.nio.ByteBuffer; 26 | import java.util.Vector; 27 | 28 | import de.hpi.xnor_mxnet.imageclassification.ImageClassifier; 29 | import de.hpi.xnor_mxnet.imageclassification.ImageNetClassifier; 30 | 31 | 32 | public class MainActivity extends CameraLiveViewActivity implements ImageReader.OnImageAvailableListener { 33 | static { 34 | System.loadLibrary("native-image-utils"); 35 | } 36 | private static final int PERMISSIONS_REQUEST = 1; 37 | 38 | private static final String PERMISSION_CAMERA = Manifest.permission.CAMERA; 39 | private static final String PERMISSION_STORAGE = Manifest.permission.READ_EXTERNAL_STORAGE; 40 | private static final float TEXT_SIZE_DIP = 10; 41 | private static String TAG; 42 | 43 | private Handler handler; 44 | private HandlerThread handlerThread; 45 | 46 | 47 | private ImageClassifier mImageClassifier; 48 | private byte[][] yuvBytes; 49 | private int[] rgbBytes; 50 | private int previewWidth; 51 | private int previewHeight; 52 | private Bitmap rgbFrameBitmap; 53 | private Bitmap croppedBitmap; 54 | private Matrix frameToCropTransform; 55 | private Matrix cropToFrameTransform; 56 | private boolean debug; 57 | private Bitmap cropCopyBitmap; 58 | private BorderedText borderedText; 59 | private long lasProcessingTimeMs; 60 | private boolean isFirstImage = true; 61 | 62 | private boolean hasPermission() { 63 | return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || 64 | checkSelfPermission(PERMISSION_CAMERA) == PackageManager.PERMISSION_GRANTED && 65 | checkSelfPermission(PERMISSION_STORAGE) == PackageManager.PERMISSION_GRANTED; 66 | } 67 | 68 | private void requestPermission() { 69 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 70 | if (shouldShowRequestPermissionRationale(PERMISSION_CAMERA) || shouldShowRequestPermissionRationale(PERMISSION_STORAGE)) { 71 | Toast.makeText(MainActivity.this, "Camera AND storage permission are required for this demo", Toast.LENGTH_LONG).show(); 72 | } 73 | requestPermissions(new String[] {PERMISSION_CAMERA, PERMISSION_STORAGE}, PERMISSIONS_REQUEST); 74 | } 75 | } 76 | 77 | private void buildImageClassifier() { 78 | mImageClassifier = new ImageNetClassifier(this); 79 | } 80 | 81 | @Override 82 | protected void onCreate(Bundle savedInstanceState) { 83 | super.onCreate(savedInstanceState); 84 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 85 | TAG = getResources().getString(R.string.app_name); 86 | 87 | setContentView(R.layout.main_activity); 88 | 89 | if (hasPermission()) { 90 | buildImageClassifier(); 91 | } else { 92 | requestPermission(); 93 | } 94 | } 95 | 96 | @Override 97 | public synchronized void onResume() { 98 | super.onResume(); 99 | 100 | handlerThread = new HandlerThread("inference"); 101 | handlerThread.start(); 102 | handler = new Handler(handlerThread.getLooper()); 103 | } 104 | 105 | @Override 106 | public synchronized void onPause() { 107 | Log.d(TAG, "Pausing"); 108 | 109 | if (!isFinishing()) { 110 | finish(); 111 | } 112 | 113 | handlerThread.quitSafely(); 114 | try { 115 | handlerThread.join(); 116 | handlerThread = null; 117 | handler = null; 118 | } catch (final InterruptedException e) { 119 | Log.e(TAG, e.getMessage()); 120 | } 121 | 122 | super.onPause(); 123 | } 124 | 125 | public void runCameraLiveView() { 126 | final Fragment cameraView = CameraConnectionFragment.newInstance( 127 | new CameraConnectionFragment.ConnectionCallback() { 128 | @Override 129 | public void onPreviewSizeChosen(Size size, int rotation) { 130 | MainActivity.this.onPreviewSizeChosen(size); 131 | } 132 | }, 133 | this, 134 | R.layout.placerecognizer_ui, 135 | new Size(mImageClassifier.getImageWidth(), mImageClassifier.getImageHeight()) 136 | ); 137 | 138 | getFragmentManager().beginTransaction() 139 | .replace(R.id.container, cameraView) 140 | .commit(); 141 | } 142 | 143 | public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) 144 | { 145 | switch (requestCode) { 146 | case PERMISSIONS_REQUEST: { 147 | if (grantResults.length > 0 148 | && grantResults[0] == PackageManager.PERMISSION_GRANTED 149 | && grantResults[1] == PackageManager.PERMISSION_GRANTED) { 150 | buildImageClassifier(); 151 | } else { 152 | requestPermission(); 153 | } 154 | } 155 | } 156 | } 157 | 158 | public void onPreviewSizeChosen(final Size size) { 159 | final float textSizePx = 160 | TypedValue.applyDimension( 161 | TypedValue.COMPLEX_UNIT_DIP, TEXT_SIZE_DIP, getResources().getDisplayMetrics()); 162 | borderedText = new BorderedText(textSizePx); 163 | borderedText.setTypeface(Typeface.MONOSPACE); 164 | 165 | previewWidth = size.getWidth(); 166 | previewHeight = size.getHeight(); 167 | 168 | Log.i(TAG, String.format("Initializing cameraPreview at size %dx%d", previewWidth, previewHeight)); 169 | rgbBytes = new int[previewWidth * previewHeight]; 170 | rgbFrameBitmap = Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888); 171 | croppedBitmap = Bitmap.createBitmap(mImageClassifier.getImageWidth(), mImageClassifier.getImageHeight(), Bitmap.Config.ARGB_8888); 172 | 173 | frameToCropTransform = 174 | ImageUtils.getTransformationMatrix( 175 | previewWidth, previewHeight, 176 | mImageClassifier.getImageWidth(), mImageClassifier.getImageHeight(), 177 | 90, true); 178 | 179 | cropToFrameTransform = new Matrix(); 180 | frameToCropTransform.invert(cropToFrameTransform); 181 | 182 | yuvBytes = new byte[3][]; 183 | 184 | addCallback( 185 | new OverlayView.DrawCallback() { 186 | @Override 187 | public void drawCallback(final Canvas canvas) { 188 | renderDebug(canvas); 189 | } 190 | }); 191 | } 192 | 193 | protected void fillBytes(final Image.Plane[] planes, final byte[][] yuvBytes) { 194 | // Because of the variable row stride it's not possible to know in 195 | // advance the actual necessary dimensions of the yuv planes. 196 | for (int i = 0; i < planes.length; ++i) { 197 | final ByteBuffer buffer = planes[i].getBuffer(); 198 | if (yuvBytes[i] == null) { 199 | Log.d(TAG, String.format("Initializing buffer %d at size %d", i, buffer.capacity())); 200 | yuvBytes[i] = new byte[buffer.capacity()]; 201 | } 202 | buffer.get(yuvBytes[i]); 203 | } 204 | } 205 | 206 | public void requestRender() { 207 | final OverlayView overlay = (OverlayView) findViewById(R.id.debug_overlay); 208 | if (overlay != null) { 209 | overlay.postInvalidate(); 210 | } 211 | } 212 | 213 | public void addCallback(final OverlayView.DrawCallback callback) { 214 | final OverlayView overlay = (OverlayView) findViewById(R.id.debug_overlay); 215 | if (overlay != null) { 216 | overlay.addCallback(callback); 217 | } 218 | } 219 | 220 | @Override 221 | public boolean onKeyDown(final int keyCode, final KeyEvent event) { 222 | if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) { 223 | debug = !debug; 224 | requestRender(); 225 | return true; 226 | } 227 | return super.onKeyDown(keyCode, event); 228 | } 229 | 230 | private void renderDebug(final Canvas canvas) { 231 | if (!debug) { 232 | return; 233 | } 234 | final Bitmap copy = cropCopyBitmap; 235 | if (copy != null) { 236 | final Matrix matrix = new Matrix(); 237 | final float scaleFactor = 2; 238 | matrix.postScale(scaleFactor, scaleFactor); 239 | matrix.postTranslate( 240 | canvas.getWidth() - copy.getWidth() * scaleFactor, 241 | canvas.getHeight() - copy.getHeight() * scaleFactor); 242 | canvas.drawBitmap(copy, matrix, new Paint()); 243 | 244 | final Vector lines = new Vector<>(); 245 | 246 | lines.add("Frame: " + previewWidth + "x" + previewHeight); 247 | lines.add("Crop: " + copy.getWidth() + "x" + copy.getHeight()); 248 | lines.add("View: " + canvas.getWidth() + "x" + canvas.getHeight()); 249 | lines.add("Inference time: " + lasProcessingTimeMs + "ms"); 250 | 251 | borderedText.drawLines(canvas, 10, canvas.getHeight() - 10, lines); 252 | } 253 | } 254 | 255 | @Override 256 | public void onImageAvailable(ImageReader imageReader) { 257 | Image image = null; 258 | 259 | try { 260 | image = imageReader.acquireLatestImage(); 261 | 262 | if (image == null) { 263 | return; 264 | } 265 | 266 | if (computing || isFirstImage) { 267 | isFirstImage = false; 268 | image.close(); 269 | return; 270 | } 271 | computing = true; 272 | 273 | Trace.beginSection("imageAvailable"); 274 | 275 | final Image.Plane[] planes = image.getPlanes(); 276 | fillBytes(planes, yuvBytes); 277 | 278 | final int yRowStride = planes[0].getRowStride(); 279 | final int uvRowStride = planes[1].getRowStride(); 280 | final int uvPixelStride = planes[1].getPixelStride(); 281 | ImageUtils.convertYUV420ToARGB8888( 282 | yuvBytes[0], 283 | yuvBytes[1], 284 | yuvBytes[2], 285 | rgbBytes, 286 | previewWidth, 287 | previewHeight, 288 | yRowStride, 289 | uvRowStride, 290 | uvPixelStride, 291 | false); 292 | image.close(); 293 | Trace.endSection(); 294 | } catch (final Exception e) { 295 | if (image != null) { 296 | image.close(); 297 | } 298 | Log.e(TAG, String.format("Exception in onImageAvailable: %s", e)); 299 | Trace.endSection(); 300 | return; 301 | } 302 | 303 | rgbFrameBitmap.setPixels(rgbBytes, 0, previewWidth, 0, 0, previewWidth, previewHeight); 304 | final Canvas canvas = new Canvas(croppedBitmap); 305 | canvas.drawBitmap(rgbFrameBitmap, frameToCropTransform, null); 306 | cropCopyBitmap = Bitmap.createBitmap(croppedBitmap); 307 | 308 | if (handler != null) { 309 | handler.post(new ImageClassificationTask(croppedBitmap, this, mImageClassifier)); 310 | } 311 | } 312 | 313 | public void setLasProcessingTimeMs(long lasProcessingTimeMs) { 314 | this.lasProcessingTimeMs = lasProcessingTimeMs; 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /app/src/main/java/de/hpi/xnor_mxnet/OverlayView.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 The TensorFlow Authors. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | package de.hpi.xnor_mxnet; 17 | 18 | import android.content.Context; 19 | import android.graphics.Canvas; 20 | import android.util.AttributeSet; 21 | import android.view.View; 22 | import java.util.LinkedList; 23 | import java.util.List; 24 | 25 | /** 26 | * A simple View providing a render callback to other classes. 27 | */ 28 | public class OverlayView extends View { 29 | private final List callbacks = new LinkedList(); 30 | 31 | public OverlayView(final Context context, final AttributeSet attrs) { 32 | super(context, attrs); 33 | } 34 | 35 | /** 36 | * Interface defining the callback for client classes. 37 | */ 38 | public interface DrawCallback { 39 | public void drawCallback(final Canvas canvas); 40 | } 41 | 42 | public void addCallback(final DrawCallback callback) { 43 | callbacks.add(callback); 44 | } 45 | 46 | @Override 47 | public synchronized void draw(final Canvas canvas) { 48 | for (final DrawCallback callback : callbacks) { 49 | callback.drawCallback(canvas); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/de/hpi/xnor_mxnet/imageclassification/AbstractClassifier.java: -------------------------------------------------------------------------------- 1 | package de.hpi.xnor_mxnet.imageclassification; 2 | 3 | 4 | import android.content.Context; 5 | 6 | import org.dmlc.mxnet.Predictor; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.ByteArrayOutputStream; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.io.InputStreamReader; 13 | import java.util.ArrayList; 14 | import java.util.Arrays; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | import de.hpi.xnor_mxnet.BuildConfig; 19 | import de.hpi.xnor_mxnet.MainActivity; 20 | 21 | abstract class AbstractClassifier implements ImageClassifier { 22 | final MainActivity mActivity; 23 | Predictor mPredictor; 24 | List mLabels; 25 | Map mMean; 26 | Map mStdDev; 27 | 28 | int mImageWidth; 29 | int mImageHeight; 30 | 31 | protected final boolean modelNeedsMeanAdjust = true; 32 | protected final boolean modelNeedsStdAdjust = true; 33 | 34 | @Override 35 | public int getImageWidth() { 36 | return mImageWidth; 37 | } 38 | 39 | @Override 40 | public int getImageHeight() { 41 | return mImageHeight; 42 | } 43 | 44 | AbstractClassifier(MainActivity activity) { 45 | mActivity = activity; 46 | ModelPreparationTask preparationTask = new ModelPreparationTask(); 47 | preparationTask.setContext(activity); 48 | preparationTask.execute(this); 49 | } 50 | 51 | static byte[] readRawFile(Context ctx, int resId) 52 | { 53 | ByteArrayOutputStream outputStream=new ByteArrayOutputStream(); 54 | int size = 0; 55 | byte[] buffer = new byte[1024]; 56 | try (InputStream ins = ctx.getResources().openRawResource(resId)) { 57 | while((size=ins.read(buffer,0,1024))>=0){ 58 | outputStream.write(buffer,0,size); 59 | } 60 | } catch (IOException e) { 61 | e.printStackTrace(); 62 | } 63 | return outputStream.toByteArray(); 64 | } 65 | 66 | static List readRawTextFile(Context ctx, int resId) 67 | { 68 | List result = new ArrayList<>(); 69 | InputStream inputStream = ctx.getResources().openRawResource(resId); 70 | 71 | InputStreamReader inputreader = new InputStreamReader(inputStream); 72 | BufferedReader buffreader = new BufferedReader(inputreader); 73 | String line; 74 | 75 | try { 76 | while (( line = buffreader.readLine()) != null) { 77 | result.add(line); 78 | } 79 | } catch (IOException e) { 80 | return null; 81 | } 82 | return result; 83 | } 84 | 85 | Classification[] getTopKresults(float[] input_matrix, int k) { 86 | float[] sorted_values = input_matrix.clone(); 87 | Arrays.sort(sorted_values); 88 | 89 | Classification[] topK = new Classification[k]; 90 | List input_list = new ArrayList<>(); 91 | for (float f: input_matrix) { 92 | input_list.add(f); 93 | } 94 | 95 | if (BuildConfig.DEBUG && sorted_values.length < k) { 96 | throw new RuntimeException("Too few predicted values for getting topK results!"); 97 | } 98 | 99 | for (int i = 0; i < topK.length; ++i) { 100 | int classId = input_list.indexOf(sorted_values[sorted_values.length - i - 1]); 101 | String tag = mLabels.get(classId); 102 | String [] tagInfo = tag.split(" ", 2); 103 | topK[i] = new Classification(classId, tagInfo[1], input_matrix[classId]); 104 | } 105 | return topK; 106 | } 107 | 108 | float[] prepareInputImage(byte[] bytes) { 109 | float[] colors = new float[bytes.length / 4 * 3]; 110 | 111 | // the R,G,B order has been tested (by HJ, 19.10.17), the R->G->B (1,2,3) got better results from prediction 112 | int imageOffset = mImageWidth * mImageHeight; 113 | for (int i = 0; i < bytes.length; i += 4) { 114 | int j = i / 4; 115 | 116 | int indexR = j; 117 | int indexG = imageOffset + j; 118 | int indexB = 2 * imageOffset + j; 119 | 120 | colors[indexR] = (float)(((int)(bytes[i + 1])) & 0xFF); 121 | colors[indexG] = (float)(((int)(bytes[i + 2])) & 0xFF); 122 | colors[indexB] = (float)(((int)(bytes[i + 3])) & 0xFF); 123 | 124 | if (modelNeedsMeanAdjust) { 125 | colors[indexR] -= mMean.get("r"); 126 | colors[indexG] -= mMean.get("g"); 127 | colors[indexB] -= mMean.get("b"); 128 | } 129 | 130 | if (modelNeedsStdAdjust) { 131 | colors[indexR] /= mStdDev.get("r"); 132 | colors[indexG] /= mStdDev.get("g"); 133 | colors[indexB] /= mStdDev.get("b"); 134 | } 135 | } 136 | return colors; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /app/src/main/java/de/hpi/xnor_mxnet/imageclassification/Classification.java: -------------------------------------------------------------------------------- 1 | package de.hpi.xnor_mxnet.imageclassification; 2 | 3 | public class Classification { 4 | private String _id; 5 | private String _label; 6 | private float _probability; 7 | 8 | Classification(String id, String label, float probability) { 9 | _id = id; _label = label; _probability = probability; 10 | } 11 | 12 | public Classification(int id, String label, float probability) { 13 | _id = String.valueOf(id); _label = label; _probability = probability; 14 | } 15 | 16 | public String get_id() { return _id; } 17 | public String get_label() { return _label; } 18 | public float get_probability() { return _probability; } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/de/hpi/xnor_mxnet/imageclassification/ImageClassifier.java: -------------------------------------------------------------------------------- 1 | package de.hpi.xnor_mxnet.imageclassification; 2 | 3 | import android.graphics.Bitmap; 4 | 5 | public interface ImageClassifier { 6 | int getImageWidth(); 7 | int getImageHeight(); 8 | Classification[] classifyImage(Bitmap bitmap); 9 | void loadModel(); 10 | void loadLabels(); 11 | void loadMean(); 12 | void loadStdDev(); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/de/hpi/xnor_mxnet/imageclassification/ImageNetClassifier.java: -------------------------------------------------------------------------------- 1 | package de.hpi.xnor_mxnet.imageclassification; 2 | 3 | import android.graphics.Bitmap; 4 | import android.os.SystemClock; 5 | import android.os.Trace; 6 | 7 | import org.dmlc.mxnet.Predictor; 8 | 9 | import java.nio.ByteBuffer; 10 | import java.util.HashMap; 11 | 12 | import de.hpi.xnor_mxnet.MainActivity; 13 | import de.hpi.xnor_mxnet.R; 14 | 15 | public class ImageNetClassifier extends AbstractClassifier { 16 | protected final boolean modelNeedsMeanAdjust = true; 17 | protected final boolean modelNeedsStdAdjust = true; 18 | 19 | public ImageNetClassifier(MainActivity activity) { 20 | super(activity); 21 | mImageWidth = 224; 22 | mImageHeight = 224; 23 | } 24 | 25 | public Classification[] classifyImage(Bitmap bitmap) { 26 | Trace.beginSection("create Image Buffer"); 27 | ByteBuffer byteBuffer = ByteBuffer.allocate(bitmap.getByteCount()); 28 | bitmap.copyPixelsToBuffer(byteBuffer); 29 | byte[] bytes = byteBuffer.array(); 30 | Trace.endSection(); 31 | 32 | Trace.beginSection("color adaption"); 33 | float[] colors; 34 | colors = prepareInputImage(bytes); 35 | 36 | Trace.endSection(); 37 | Trace.beginSection("Model execution"); 38 | 39 | final long startTime = SystemClock.uptimeMillis(); 40 | mPredictor.forward("data", colors); 41 | mActivity.setLasProcessingTimeMs(SystemClock.uptimeMillis() - startTime); 42 | 43 | final float[] result = mPredictor.getOutput(0); 44 | Trace.endSection(); 45 | 46 | Trace.beginSection("gather top results"); 47 | Classification[] results = getTopKresults(result, 5); 48 | Trace.endSection(); 49 | 50 | mActivity.requestRender(); 51 | return results; 52 | } 53 | 54 | @Override 55 | public void loadModel() { 56 | final byte[] symbol = readRawFile(mActivity, R.raw.binarized_densenet_28_symbol); 57 | final byte[] params = readRawFile(mActivity, R.raw.binarized_densenet_28_params); 58 | final Predictor.Device device = new Predictor.Device(Predictor.Device.Type.CPU, 0); 59 | final int[] shape = {1, 3, mImageHeight, mImageWidth}; 60 | final String key = "data"; 61 | final Predictor.InputNode node = new Predictor.InputNode(key, shape); 62 | 63 | mPredictor = new Predictor(symbol, params, device, new Predictor.InputNode[]{node}); 64 | } 65 | 66 | @Override 67 | public void loadLabels() { 68 | mLabels = readRawTextFile(mActivity, R.raw.synset); 69 | } 70 | 71 | @Override 72 | public void loadMean() { 73 | mMean = new HashMap<>(); 74 | if (modelNeedsMeanAdjust) { 75 | mMean.put("b", (float) 103.939); 76 | mMean.put("g", (float) 116.779); 77 | mMean.put("r", (float) 123.68); 78 | } 79 | 80 | } 81 | 82 | @Override 83 | public void loadStdDev() { 84 | mStdDev = new HashMap<>(); 85 | if (modelNeedsStdAdjust) { 86 | mStdDev.put("b", (float) 57.375); 87 | mStdDev.put("g", (float) 57.12); 88 | mStdDev.put("r", (float) 58.393); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/java/de/hpi/xnor_mxnet/imageclassification/ModelPreparationTask.java: -------------------------------------------------------------------------------- 1 | package de.hpi.xnor_mxnet.imageclassification; 2 | 3 | 4 | import android.app.ProgressDialog; 5 | import android.os.AsyncTask; 6 | 7 | import de.hpi.xnor_mxnet.CameraLiveViewActivity; 8 | import de.hpi.xnor_mxnet.R; 9 | 10 | 11 | public class ModelPreparationTask extends AsyncTask { 12 | private ProgressDialog mProgressDialog; 13 | private CameraLiveViewActivity context; 14 | 15 | public void setContext(CameraLiveViewActivity context) { 16 | this.context = context; 17 | } 18 | 19 | @Override 20 | protected void onPreExecute() { 21 | mProgressDialog = new ProgressDialog(context); 22 | mProgressDialog.setTitle(R.string.loading_model); 23 | mProgressDialog.setCancelable(false); 24 | mProgressDialog.setIndeterminate(true); 25 | mProgressDialog.show(); 26 | } 27 | 28 | @Override 29 | protected Void doInBackground(ImageClassifier... imageClassifiers) { 30 | for (ImageClassifier classifier: imageClassifiers) { 31 | classifier.loadModel(); 32 | classifier.loadLabels(); 33 | classifier.loadMean(); 34 | classifier.loadStdDev(); 35 | } 36 | return null; 37 | } 38 | 39 | @Override 40 | protected void onPostExecute(Void voids) { 41 | if (mProgressDialog != null) { 42 | mProgressDialog.dismiss(); 43 | } 44 | context.runCameraLiveView(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/org/dmlc/mxnet/MxnetException.java: -------------------------------------------------------------------------------- 1 | package org.dmlc.mxnet; 2 | 3 | public class MxnetException extends Exception { 4 | public MxnetException(){} 5 | public MxnetException(String txt) { 6 | super(txt); 7 | } 8 | } 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/org/dmlc/mxnet/Predictor.java: -------------------------------------------------------------------------------- 1 | package org.dmlc.mxnet; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Color; 5 | 6 | public class Predictor { 7 | static { 8 | System.loadLibrary("mxnet_predict"); 9 | } 10 | 11 | public static class InputNode { 12 | String key; 13 | int[] shape; 14 | public InputNode(String key, int[] shape) { 15 | this.key = key; 16 | this.shape = shape; 17 | } 18 | } 19 | 20 | public static class Device { 21 | public enum Type { 22 | CPU, GPU, CPU_PINNED 23 | } 24 | 25 | public Device(Type t, int i) { 26 | this.type = t; 27 | this.id = i; 28 | } 29 | 30 | Type type; 31 | int id; 32 | int ctype() { 33 | return this.type == Type.CPU? 1: this.type == Type.GPU? 2: 3; 34 | } 35 | } 36 | 37 | private long handle = 0; 38 | 39 | public Predictor(byte[] symbol, byte[] params, Device dev, InputNode[] input) { 40 | String[] keys = new String[input.length]; 41 | int[][] shapes = new int[input.length][]; 42 | for (int i=0; i 20 | #include 21 | 22 | #include "yuv2rgb.h" 23 | 24 | #define IMAGEUTILS_METHOD(METHOD_NAME) \ 25 | Java_de_hpi_xnor_1mxnet_ImageUtils_##METHOD_NAME // NOLINT 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | JNIEXPORT void JNICALL 32 | IMAGEUTILS_METHOD(convertYUV420SPToARGB8888)( 33 | JNIEnv* env, jclass clazz, jbyteArray input, jintArray output, 34 | jint width, jint height, jboolean halfSize); 35 | 36 | JNIEXPORT void JNICALL IMAGEUTILS_METHOD(convertYUV420ToARGB8888)( 37 | JNIEnv* env, jclass clazz, jbyteArray y, jbyteArray u, jbyteArray v, 38 | jintArray output, jint width, jint height, jint y_row_stride, 39 | jint uv_row_stride, jint uv_pixel_stride, jboolean halfSize); 40 | 41 | JNIEXPORT void JNICALL IMAGEUTILS_METHOD(convertYUV420SPToRGB565)( 42 | JNIEnv* env, jclass clazz, jbyteArray input, jbyteArray output, jint width, 43 | jint height); 44 | 45 | #ifdef __cplusplus 46 | } 47 | #endif 48 | 49 | JNIEXPORT void JNICALL 50 | IMAGEUTILS_METHOD(convertYUV420SPToARGB8888)( 51 | JNIEnv* env, jclass clazz, jbyteArray input, jintArray output, 52 | jint width, jint height, jboolean halfSize) { 53 | jboolean inputCopy = JNI_FALSE; 54 | jbyte* const i = env->GetByteArrayElements(input, &inputCopy); 55 | 56 | jboolean outputCopy = JNI_FALSE; 57 | jint* const o = env->GetIntArrayElements(output, &outputCopy); 58 | 59 | if (halfSize) { 60 | ConvertYUV420SPToARGB8888HalfSize(reinterpret_cast(i), 61 | reinterpret_cast(o), width, 62 | height); 63 | } else { 64 | ConvertYUV420SPToARGB8888(reinterpret_cast(i), 65 | reinterpret_cast(i) + width * height, 66 | reinterpret_cast(o), width, height); 67 | } 68 | 69 | env->ReleaseByteArrayElements(input, i, JNI_ABORT); 70 | env->ReleaseIntArrayElements(output, o, 0); 71 | } 72 | 73 | JNIEXPORT void JNICALL IMAGEUTILS_METHOD(convertYUV420ToARGB8888)( 74 | JNIEnv* env, jclass clazz, jbyteArray y, jbyteArray u, jbyteArray v, 75 | jintArray output, jint width, jint height, jint y_row_stride, 76 | jint uv_row_stride, jint uv_pixel_stride, jboolean halfSize) { 77 | jboolean inputCopy = JNI_FALSE; 78 | jbyte* const y_buff = env->GetByteArrayElements(y, &inputCopy); 79 | jboolean outputCopy = JNI_FALSE; 80 | jint* const o = env->GetIntArrayElements(output, &outputCopy); 81 | 82 | if (halfSize) { 83 | ConvertYUV420SPToARGB8888HalfSize(reinterpret_cast(y_buff), 84 | reinterpret_cast(o), width, 85 | height); 86 | } else { 87 | jbyte* const u_buff = env->GetByteArrayElements(u, &inputCopy); 88 | jbyte* const v_buff = env->GetByteArrayElements(v, &inputCopy); 89 | 90 | ConvertYUV420ToARGB8888( 91 | reinterpret_cast(y_buff), reinterpret_cast(u_buff), 92 | reinterpret_cast(v_buff), reinterpret_cast(o), 93 | width, height, y_row_stride, uv_row_stride, uv_pixel_stride); 94 | 95 | env->ReleaseByteArrayElements(u, u_buff, JNI_ABORT); 96 | env->ReleaseByteArrayElements(v, v_buff, JNI_ABORT); 97 | } 98 | 99 | env->ReleaseByteArrayElements(y, y_buff, JNI_ABORT); 100 | env->ReleaseIntArrayElements(output, o, 0); 101 | } 102 | 103 | JNIEXPORT void JNICALL IMAGEUTILS_METHOD(convertYUV420SPToRGB565)( 104 | JNIEnv* env, jclass clazz, jbyteArray input, jbyteArray output, jint width, 105 | jint height) { 106 | jboolean inputCopy = JNI_FALSE; 107 | jbyte* const i = env->GetByteArrayElements(input, &inputCopy); 108 | 109 | jboolean outputCopy = JNI_FALSE; 110 | jbyte* const o = env->GetByteArrayElements(output, &outputCopy); 111 | 112 | ConvertYUV420SPToRGB565(reinterpret_cast(i), 113 | reinterpret_cast(o), width, height); 114 | 115 | env->ReleaseByteArrayElements(input, i, JNI_ABORT); 116 | env->ReleaseByteArrayElements(output, o, 0); 117 | } 118 | -------------------------------------------------------------------------------- /app/src/main/native-image-utils/yuv2rgb.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 The TensorFlow Authors. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | // This is a collection of routines which converts various YUV image formats 17 | // to ARGB. 18 | 19 | #include "yuv2rgb.h" 20 | 21 | #ifndef MAX 22 | #define MAX(a, b) ({__typeof__(a) _a = (a); __typeof__(b) _b = (b); _a > _b ? _a : _b; }) 23 | #define MIN(a, b) ({__typeof__(a) _a = (a); __typeof__(b) _b = (b); _a < _b ? _a : _b; }) 24 | #endif 25 | 26 | // This value is 2 ^ 18 - 1, and is used to clamp the RGB values before their ranges 27 | // are normalized to eight bits. 28 | static const int kMaxChannelValue = 262143; 29 | 30 | static inline uint32_t YUV2RGB(int nY, int nU, int nV) { 31 | nY -= 16; 32 | nU -= 128; 33 | nV -= 128; 34 | if (nY < 0) nY = 0; 35 | 36 | // This is the floating point equivalent. We do the conversion in integer 37 | // because some Android devices do not have floating point in hardware. 38 | // nR = (int)(1.164 * nY + 2.018 * nU); 39 | // nG = (int)(1.164 * nY - 0.813 * nV - 0.391 * nU); 40 | // nB = (int)(1.164 * nY + 1.596 * nV); 41 | 42 | int nR = (int)(1192 * nY + 1634 * nV); 43 | int nG = (int)(1192 * nY - 833 * nV - 400 * nU); 44 | int nB = (int)(1192 * nY + 2066 * nU); 45 | 46 | nR = MIN(kMaxChannelValue, MAX(0, nR)); 47 | nG = MIN(kMaxChannelValue, MAX(0, nG)); 48 | nB = MIN(kMaxChannelValue, MAX(0, nB)); 49 | 50 | nR = (nR >> 10) & 0xff; 51 | nG = (nG >> 10) & 0xff; 52 | nB = (nB >> 10) & 0xff; 53 | 54 | return 0xff000000 | (nR << 16) | (nG << 8) | nB; 55 | } 56 | 57 | // Accepts a YUV 4:2:0 image with a plane of 8 bit Y samples followed by 58 | // separate u and v planes with arbitrary row and column strides, 59 | // containing 8 bit 2x2 subsampled chroma samples. 60 | // Converts to a packed ARGB 32 bit output of the same pixel dimensions. 61 | void ConvertYUV420ToARGB8888(const uint8_t* const yData, 62 | const uint8_t* const uData, 63 | const uint8_t* const vData, uint32_t* const output, 64 | const int width, const int height, 65 | const int y_row_stride, const int uv_row_stride, 66 | const int uv_pixel_stride) { 67 | uint32_t* out = output; 68 | 69 | for (int y = 0; y < height; y++) { 70 | const uint8_t* pY = yData + y_row_stride * y; 71 | 72 | const int uv_row_start = uv_row_stride * (y >> 1); 73 | const uint8_t* pU = uData + uv_row_start; 74 | const uint8_t* pV = vData + uv_row_start; 75 | 76 | for (int x = 0; x < width; x++) { 77 | const int uv_offset = (x >> 1) * uv_pixel_stride; 78 | *out++ = YUV2RGB(pY[x], pU[uv_offset], pV[uv_offset]); 79 | } 80 | } 81 | } 82 | 83 | // Accepts a YUV 4:2:0 image with a plane of 8 bit Y samples followed by an 84 | // interleaved U/V plane containing 8 bit 2x2 subsampled chroma samples, 85 | // except the interleave order of U and V is reversed. Converts to a packed 86 | // ARGB 32 bit output of the same pixel dimensions. 87 | void ConvertYUV420SPToARGB8888(const uint8_t* const yData, 88 | const uint8_t* const uvData, 89 | uint32_t* const output, const int width, 90 | const int height) { 91 | const uint8_t* pY = yData; 92 | const uint8_t* pUV = uvData; 93 | uint32_t* out = output; 94 | 95 | for (int y = 0; y < height; y++) { 96 | for (int x = 0; x < width; x++) { 97 | int nY = *pY++; 98 | int offset = (y >> 1) * width + 2 * (x >> 1); 99 | #ifdef __APPLE__ 100 | int nU = pUV[offset]; 101 | int nV = pUV[offset + 1]; 102 | #else 103 | int nV = pUV[offset]; 104 | int nU = pUV[offset + 1]; 105 | #endif 106 | 107 | *out++ = YUV2RGB(nY, nU, nV); 108 | } 109 | } 110 | } 111 | 112 | // The same as above, but downsamples each dimension to half size. 113 | void ConvertYUV420SPToARGB8888HalfSize(const uint8_t* const input, 114 | uint32_t* const output, int width, 115 | int height) { 116 | const uint8_t* pY = input; 117 | const uint8_t* pUV = input + (width * height); 118 | uint32_t* out = output; 119 | int stride = width; 120 | width >>= 1; 121 | height >>= 1; 122 | 123 | for (int y = 0; y < height; y++) { 124 | for (int x = 0; x < width; x++) { 125 | int nY = (pY[0] + pY[1] + pY[stride] + pY[stride + 1]) >> 2; 126 | pY += 2; 127 | #ifdef __APPLE__ 128 | int nU = *pUV++; 129 | int nV = *pUV++; 130 | #else 131 | int nV = *pUV++; 132 | int nU = *pUV++; 133 | #endif 134 | 135 | *out++ = YUV2RGB(nY, nU, nV); 136 | } 137 | pY += stride; 138 | } 139 | } 140 | 141 | // Accepts a YUV 4:2:0 image with a plane of 8 bit Y samples followed by an 142 | // interleaved U/V plane containing 8 bit 2x2 subsampled chroma samples, 143 | // except the interleave order of U and V is reversed. Converts to a packed 144 | // RGB 565 bit output of the same pixel dimensions. 145 | void ConvertYUV420SPToRGB565(const uint8_t* const input, uint16_t* const output, 146 | const int width, const int height) { 147 | const uint8_t* pY = input; 148 | const uint8_t* pUV = input + (width * height); 149 | uint16_t* out = output; 150 | 151 | for (int y = 0; y < height; y++) { 152 | for (int x = 0; x < width; x++) { 153 | int nY = *pY++; 154 | int offset = (y >> 1) * width + 2 * (x >> 1); 155 | #ifdef __APPLE__ 156 | int nU = pUV[offset]; 157 | int nV = pUV[offset + 1]; 158 | #else 159 | int nV = pUV[offset]; 160 | int nU = pUV[offset + 1]; 161 | #endif 162 | 163 | nY -= 16; 164 | nU -= 128; 165 | nV -= 128; 166 | if (nY < 0) nY = 0; 167 | 168 | // This is the floating point equivalent. We do the conversion in integer 169 | // because some Android devices do not have floating point in hardware. 170 | // nR = (int)(1.164 * nY + 2.018 * nU); 171 | // nG = (int)(1.164 * nY - 0.813 * nV - 0.391 * nU); 172 | // nB = (int)(1.164 * nY + 1.596 * nV); 173 | 174 | int nR = (int)(1192 * nY + 1634 * nV); 175 | int nG = (int)(1192 * nY - 833 * nV - 400 * nU); 176 | int nB = (int)(1192 * nY + 2066 * nU); 177 | 178 | nR = MIN(kMaxChannelValue, MAX(0, nR)); 179 | nG = MIN(kMaxChannelValue, MAX(0, nG)); 180 | nB = MIN(kMaxChannelValue, MAX(0, nB)); 181 | 182 | // Shift more than for ARGB8888 and apply appropriate bitmask. 183 | nR = (nR >> 13) & 0x1f; 184 | nG = (nG >> 12) & 0x3f; 185 | nB = (nB >> 13) & 0x1f; 186 | 187 | // R is high 5 bits, G is middle 6 bits, and B is low 5 bits. 188 | *out++ = (nR << 11) | (nG << 5) | nB; 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /app/src/main/native-image-utils/yuv2rgb.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 The TensorFlow Authors. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | // This is a collection of routines which converts various YUV image formats 17 | // to (A)RGB. 18 | 19 | #ifndef ORG_TENSORFLOW_JNI_IMAGEUTILS_YUV2RGB_H_ 20 | #define ORG_TENSORFLOW_JNI_IMAGEUTILS_YUV2RGB_H_ 21 | 22 | #include 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | void ConvertYUV420ToARGB8888(const uint8_t* const yData, 29 | const uint8_t* const uData, 30 | const uint8_t* const vData, uint32_t* const output, 31 | const int width, const int height, 32 | const int y_row_stride, const int uv_row_stride, 33 | const int uv_pixel_stride); 34 | 35 | // Converts YUV420 semi-planar data to ARGB 8888 data using the supplied width 36 | // and height. The input and output must already be allocated and non-null. 37 | // For efficiency, no error checking is performed. 38 | void ConvertYUV420SPToARGB8888(const uint8_t* const pY, 39 | const uint8_t* const pUV, uint32_t* const output, 40 | const int width, const int height); 41 | 42 | // The same as above, but downsamples each dimension to half size. 43 | void ConvertYUV420SPToARGB8888HalfSize(const uint8_t* const input, 44 | uint32_t* const output, int width, 45 | int height); 46 | 47 | // Converts YUV420 semi-planar data to RGB 565 data using the supplied width 48 | // and height. The input and output must already be allocated and non-null. 49 | // For efficiency, no error checking is performed. 50 | void ConvertYUV420SPToRGB565(const uint8_t* const input, uint16_t* const output, 51 | const int width, const int height); 52 | 53 | #ifdef __cplusplus 54 | } 55 | #endif 56 | 57 | #endif // ORG_TENSORFLOW_JNI_IMAGEUTILS_YUV2RGB_H_ 58 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_gpslogger.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | 16 | 22 | 23 | 24 | 25 | 33 | 34 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_gpslogger.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/main_activity.xml: -------------------------------------------------------------------------------- 1 | 16 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/placerecognizer_ui.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 21 | 22 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpi-xnor/android-image-classification/5013eba9e212a5ba8e98e4bc3f8c9d58d1df46f6/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpi-xnor/android-image-classification/5013eba9e212a5ba8e98e4bc3f8c9d58d1df46f6/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpi-xnor/android-image-classification/5013eba9e212a5ba8e98e4bc3f8c9d58d1df46f6/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpi-xnor/android-image-classification/5013eba9e212a5ba8e98e4bc3f8c9d58d1df46f6/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpi-xnor/android-image-classification/5013eba9e212a5ba8e98e4bc3f8c9d58d1df46f6/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/raw/binarized_densenet_28_params: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpi-xnor/android-image-classification/5013eba9e212a5ba8e98e4bc3f8c9d58d1df46f6/app/src/main/res/raw/binarized_densenet_28_params -------------------------------------------------------------------------------- /app/src/main/res/raw/binarized_resnet_e_18_params: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpi-xnor/android-image-classification/5013eba9e212a5ba8e98e4bc3f8c9d58d1df46f6/app/src/main/res/raw/binarized_resnet_e_18_params -------------------------------------------------------------------------------- /app/src/main/res/raw/synset.txt: -------------------------------------------------------------------------------- 1 | n01440764 tench, Tinca tinca 2 | n01443537 goldfish, Carassius auratus 3 | n01484850 great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias 4 | n01491361 tiger shark, Galeocerdo cuvieri 5 | n01494475 hammerhead, hammerhead shark 6 | n01496331 electric ray, crampfish, numbfish, torpedo 7 | n01498041 stingray 8 | n01514668 cock 9 | n01514859 hen 10 | n01518878 ostrich, Struthio camelus 11 | n01530575 brambling, Fringilla montifringilla 12 | n01531178 goldfinch, Carduelis carduelis 13 | n01532829 house finch, linnet, Carpodacus mexicanus 14 | n01534433 junco, snowbird 15 | n01537544 indigo bunting, indigo finch, indigo bird, Passerina cyanea 16 | n01558993 robin, American robin, Turdus migratorius 17 | n01560419 bulbul 18 | n01580077 jay 19 | n01582220 magpie 20 | n01592084 chickadee 21 | n01601694 water ouzel, dipper 22 | n01608432 kite 23 | n01614925 bald eagle, American eagle, Haliaeetus leucocephalus 24 | n01616318 vulture 25 | n01622779 great grey owl, great gray owl, Strix nebulosa 26 | n01629819 European fire salamander, Salamandra salamandra 27 | n01630670 common newt, Triturus vulgaris 28 | n01631663 eft 29 | n01632458 spotted salamander, Ambystoma maculatum 30 | n01632777 axolotl, mud puppy, Ambystoma mexicanum 31 | n01641577 bullfrog, Rana catesbeiana 32 | n01644373 tree frog, tree-frog 33 | n01644900 tailed frog, bell toad, ribbed toad, tailed toad, Ascaphus trui 34 | n01664065 loggerhead, loggerhead turtle, Caretta caretta 35 | n01665541 leatherback turtle, leatherback, leathery turtle, Dermochelys coriacea 36 | n01667114 mud turtle 37 | n01667778 terrapin 38 | n01669191 box turtle, box tortoise 39 | n01675722 banded gecko 40 | n01677366 common iguana, iguana, Iguana iguana 41 | n01682714 American chameleon, anole, Anolis carolinensis 42 | n01685808 whiptail, whiptail lizard 43 | n01687978 agama 44 | n01688243 frilled lizard, Chlamydosaurus kingi 45 | n01689811 alligator lizard 46 | n01692333 Gila monster, Heloderma suspectum 47 | n01693334 green lizard, Lacerta viridis 48 | n01694178 African chameleon, Chamaeleo chamaeleon 49 | n01695060 Komodo dragon, Komodo lizard, dragon lizard, giant lizard, Varanus komodoensis 50 | n01697457 African crocodile, Nile crocodile, Crocodylus niloticus 51 | n01698640 American alligator, Alligator mississipiensis 52 | n01704323 triceratops 53 | n01728572 thunder snake, worm snake, Carphophis amoenus 54 | n01728920 ringneck snake, ring-necked snake, ring snake 55 | n01729322 hognose snake, puff adder, sand viper 56 | n01729977 green snake, grass snake 57 | n01734418 king snake, kingsnake 58 | n01735189 garter snake, grass snake 59 | n01737021 water snake 60 | n01739381 vine snake 61 | n01740131 night snake, Hypsiglena torquata 62 | n01742172 boa constrictor, Constrictor constrictor 63 | n01744401 rock python, rock snake, Python sebae 64 | n01748264 Indian cobra, Naja naja 65 | n01749939 green mamba 66 | n01751748 sea snake 67 | n01753488 horned viper, cerastes, sand viper, horned asp, Cerastes cornutus 68 | n01755581 diamondback, diamondback rattlesnake, Crotalus adamanteus 69 | n01756291 sidewinder, horned rattlesnake, Crotalus cerastes 70 | n01768244 trilobite 71 | n01770081 harvestman, daddy longlegs, Phalangium opilio 72 | n01770393 scorpion 73 | n01773157 black and gold garden spider, Argiope aurantia 74 | n01773549 barn spider, Araneus cavaticus 75 | n01773797 garden spider, Aranea diademata 76 | n01774384 black widow, Latrodectus mactans 77 | n01774750 tarantula 78 | n01775062 wolf spider, hunting spider 79 | n01776313 tick 80 | n01784675 centipede 81 | n01795545 black grouse 82 | n01796340 ptarmigan 83 | n01797886 ruffed grouse, partridge, Bonasa umbellus 84 | n01798484 prairie chicken, prairie grouse, prairie fowl 85 | n01806143 peacock 86 | n01806567 quail 87 | n01807496 partridge 88 | n01817953 African grey, African gray, Psittacus erithacus 89 | n01818515 macaw 90 | n01819313 sulphur-crested cockatoo, Kakatoe galerita, Cacatua galerita 91 | n01820546 lorikeet 92 | n01824575 coucal 93 | n01828970 bee eater 94 | n01829413 hornbill 95 | n01833805 hummingbird 96 | n01843065 jacamar 97 | n01843383 toucan 98 | n01847000 drake 99 | n01855032 red-breasted merganser, Mergus serrator 100 | n01855672 goose 101 | n01860187 black swan, Cygnus atratus 102 | n01871265 tusker 103 | n01872401 echidna, spiny anteater, anteater 104 | n01873310 platypus, duckbill, duckbilled platypus, duck-billed platypus, Ornithorhynchus anatinus 105 | n01877812 wallaby, brush kangaroo 106 | n01882714 koala, koala bear, kangaroo bear, native bear, Phascolarctos cinereus 107 | n01883070 wombat 108 | n01910747 jellyfish 109 | n01914609 sea anemone, anemone 110 | n01917289 brain coral 111 | n01924916 flatworm, platyhelminth 112 | n01930112 nematode, nematode worm, roundworm 113 | n01943899 conch 114 | n01944390 snail 115 | n01945685 slug 116 | n01950731 sea slug, nudibranch 117 | n01955084 chiton, coat-of-mail shell, sea cradle, polyplacophore 118 | n01968897 chambered nautilus, pearly nautilus, nautilus 119 | n01978287 Dungeness crab, Cancer magister 120 | n01978455 rock crab, Cancer irroratus 121 | n01980166 fiddler crab 122 | n01981276 king crab, Alaska crab, Alaskan king crab, Alaska king crab, Paralithodes camtschatica 123 | n01983481 American lobster, Northern lobster, Maine lobster, Homarus americanus 124 | n01984695 spiny lobster, langouste, rock lobster, crawfish, crayfish, sea crawfish 125 | n01985128 crayfish, crawfish, crawdad, crawdaddy 126 | n01986214 hermit crab 127 | n01990800 isopod 128 | n02002556 white stork, Ciconia ciconia 129 | n02002724 black stork, Ciconia nigra 130 | n02006656 spoonbill 131 | n02007558 flamingo 132 | n02009229 little blue heron, Egretta caerulea 133 | n02009912 American egret, great white heron, Egretta albus 134 | n02011460 bittern 135 | n02012849 crane 136 | n02013706 limpkin, Aramus pictus 137 | n02017213 European gallinule, Porphyrio porphyrio 138 | n02018207 American coot, marsh hen, mud hen, water hen, Fulica americana 139 | n02018795 bustard 140 | n02025239 ruddy turnstone, Arenaria interpres 141 | n02027492 red-backed sandpiper, dunlin, Erolia alpina 142 | n02028035 redshank, Tringa totanus 143 | n02033041 dowitcher 144 | n02037110 oystercatcher, oyster catcher 145 | n02051845 pelican 146 | n02056570 king penguin, Aptenodytes patagonica 147 | n02058221 albatross, mollymawk 148 | n02066245 grey whale, gray whale, devilfish, Eschrichtius gibbosus, Eschrichtius robustus 149 | n02071294 killer whale, killer, orca, grampus, sea wolf, Orcinus orca 150 | n02074367 dugong, Dugong dugon 151 | n02077923 sea lion 152 | n02085620 Chihuahua 153 | n02085782 Japanese spaniel 154 | n02085936 Maltese dog, Maltese terrier, Maltese 155 | n02086079 Pekinese, Pekingese, Peke 156 | n02086240 Shih-Tzu 157 | n02086646 Blenheim spaniel 158 | n02086910 papillon 159 | n02087046 toy terrier 160 | n02087394 Rhodesian ridgeback 161 | n02088094 Afghan hound, Afghan 162 | n02088238 basset, basset hound 163 | n02088364 beagle 164 | n02088466 bloodhound, sleuthhound 165 | n02088632 bluetick 166 | n02089078 black-and-tan coonhound 167 | n02089867 Walker hound, Walker foxhound 168 | n02089973 English foxhound 169 | n02090379 redbone 170 | n02090622 borzoi, Russian wolfhound 171 | n02090721 Irish wolfhound 172 | n02091032 Italian greyhound 173 | n02091134 whippet 174 | n02091244 Ibizan hound, Ibizan Podenco 175 | n02091467 Norwegian elkhound, elkhound 176 | n02091635 otterhound, otter hound 177 | n02091831 Saluki, gazelle hound 178 | n02092002 Scottish deerhound, deerhound 179 | n02092339 Weimaraner 180 | n02093256 Staffordshire bullterrier, Staffordshire bull terrier 181 | n02093428 American Staffordshire terrier, Staffordshire terrier, American pit bull terrier, pit bull terrier 182 | n02093647 Bedlington terrier 183 | n02093754 Border terrier 184 | n02093859 Kerry blue terrier 185 | n02093991 Irish terrier 186 | n02094114 Norfolk terrier 187 | n02094258 Norwich terrier 188 | n02094433 Yorkshire terrier 189 | n02095314 wire-haired fox terrier 190 | n02095570 Lakeland terrier 191 | n02095889 Sealyham terrier, Sealyham 192 | n02096051 Airedale, Airedale terrier 193 | n02096177 cairn, cairn terrier 194 | n02096294 Australian terrier 195 | n02096437 Dandie Dinmont, Dandie Dinmont terrier 196 | n02096585 Boston bull, Boston terrier 197 | n02097047 miniature schnauzer 198 | n02097130 giant schnauzer 199 | n02097209 standard schnauzer 200 | n02097298 Scotch terrier, Scottish terrier, Scottie 201 | n02097474 Tibetan terrier, chrysanthemum dog 202 | n02097658 silky terrier, Sydney silky 203 | n02098105 soft-coated wheaten terrier 204 | n02098286 West Highland white terrier 205 | n02098413 Lhasa, Lhasa apso 206 | n02099267 flat-coated retriever 207 | n02099429 curly-coated retriever 208 | n02099601 golden retriever 209 | n02099712 Labrador retriever 210 | n02099849 Chesapeake Bay retriever 211 | n02100236 German short-haired pointer 212 | n02100583 vizsla, Hungarian pointer 213 | n02100735 English setter 214 | n02100877 Irish setter, red setter 215 | n02101006 Gordon setter 216 | n02101388 Brittany spaniel 217 | n02101556 clumber, clumber spaniel 218 | n02102040 English springer, English springer spaniel 219 | n02102177 Welsh springer spaniel 220 | n02102318 cocker spaniel, English cocker spaniel, cocker 221 | n02102480 Sussex spaniel 222 | n02102973 Irish water spaniel 223 | n02104029 kuvasz 224 | n02104365 schipperke 225 | n02105056 groenendael 226 | n02105162 malinois 227 | n02105251 briard 228 | n02105412 kelpie 229 | n02105505 komondor 230 | n02105641 Old English sheepdog, bobtail 231 | n02105855 Shetland sheepdog, Shetland sheep dog, Shetland 232 | n02106030 collie 233 | n02106166 Border collie 234 | n02106382 Bouvier des Flandres, Bouviers des Flandres 235 | n02106550 Rottweiler 236 | n02106662 German shepherd, German shepherd dog, German police dog, alsatian 237 | n02107142 Doberman, Doberman pinscher 238 | n02107312 miniature pinscher 239 | n02107574 Greater Swiss Mountain dog 240 | n02107683 Bernese mountain dog 241 | n02107908 Appenzeller 242 | n02108000 EntleBucher 243 | n02108089 boxer 244 | n02108422 bull mastiff 245 | n02108551 Tibetan mastiff 246 | n02108915 French bulldog 247 | n02109047 Great Dane 248 | n02109525 Saint Bernard, St Bernard 249 | n02109961 Eskimo dog, husky 250 | n02110063 malamute, malemute, Alaskan malamute 251 | n02110185 Siberian husky 252 | n02110341 dalmatian, coach dog, carriage dog 253 | n02110627 affenpinscher, monkey pinscher, monkey dog 254 | n02110806 basenji 255 | n02110958 pug, pug-dog 256 | n02111129 Leonberg 257 | n02111277 Newfoundland, Newfoundland dog 258 | n02111500 Great Pyrenees 259 | n02111889 Samoyed, Samoyede 260 | n02112018 Pomeranian 261 | n02112137 chow, chow chow 262 | n02112350 keeshond 263 | n02112706 Brabancon griffon 264 | n02113023 Pembroke, Pembroke Welsh corgi 265 | n02113186 Cardigan, Cardigan Welsh corgi 266 | n02113624 toy poodle 267 | n02113712 miniature poodle 268 | n02113799 standard poodle 269 | n02113978 Mexican hairless 270 | n02114367 timber wolf, grey wolf, gray wolf, Canis lupus 271 | n02114548 white wolf, Arctic wolf, Canis lupus tundrarum 272 | n02114712 red wolf, maned wolf, Canis rufus, Canis niger 273 | n02114855 coyote, prairie wolf, brush wolf, Canis latrans 274 | n02115641 dingo, warrigal, warragal, Canis dingo 275 | n02115913 dhole, Cuon alpinus 276 | n02116738 African hunting dog, hyena dog, Cape hunting dog, Lycaon pictus 277 | n02117135 hyena, hyaena 278 | n02119022 red fox, Vulpes vulpes 279 | n02119789 kit fox, Vulpes macrotis 280 | n02120079 Arctic fox, white fox, Alopex lagopus 281 | n02120505 grey fox, gray fox, Urocyon cinereoargenteus 282 | n02123045 tabby, tabby cat 283 | n02123159 tiger cat 284 | n02123394 Persian cat 285 | n02123597 Siamese cat, Siamese 286 | n02124075 Egyptian cat 287 | n02125311 cougar, puma, catamount, mountain lion, painter, panther, Felis concolor 288 | n02127052 lynx, catamount 289 | n02128385 leopard, Panthera pardus 290 | n02128757 snow leopard, ounce, Panthera uncia 291 | n02128925 jaguar, panther, Panthera onca, Felis onca 292 | n02129165 lion, king of beasts, Panthera leo 293 | n02129604 tiger, Panthera tigris 294 | n02130308 cheetah, chetah, Acinonyx jubatus 295 | n02132136 brown bear, bruin, Ursus arctos 296 | n02133161 American black bear, black bear, Ursus americanus, Euarctos americanus 297 | n02134084 ice bear, polar bear, Ursus Maritimus, Thalarctos maritimus 298 | n02134418 sloth bear, Melursus ursinus, Ursus ursinus 299 | n02137549 mongoose 300 | n02138441 meerkat, mierkat 301 | n02165105 tiger beetle 302 | n02165456 ladybug, ladybeetle, lady beetle, ladybird, ladybird beetle 303 | n02167151 ground beetle, carabid beetle 304 | n02168699 long-horned beetle, longicorn, longicorn beetle 305 | n02169497 leaf beetle, chrysomelid 306 | n02172182 dung beetle 307 | n02174001 rhinoceros beetle 308 | n02177972 weevil 309 | n02190166 fly 310 | n02206856 bee 311 | n02219486 ant, emmet, pismire 312 | n02226429 grasshopper, hopper 313 | n02229544 cricket 314 | n02231487 walking stick, walkingstick, stick insect 315 | n02233338 cockroach, roach 316 | n02236044 mantis, mantid 317 | n02256656 cicada, cicala 318 | n02259212 leafhopper 319 | n02264363 lacewing, lacewing fly 320 | n02268443 dragonfly, darning needle, devil's darning needle, sewing needle, snake feeder, snake doctor, mosquito hawk, skeeter hawk 321 | n02268853 damselfly 322 | n02276258 admiral 323 | n02277742 ringlet, ringlet butterfly 324 | n02279972 monarch, monarch butterfly, milkweed butterfly, Danaus plexippus 325 | n02280649 cabbage butterfly 326 | n02281406 sulphur butterfly, sulfur butterfly 327 | n02281787 lycaenid, lycaenid butterfly 328 | n02317335 starfish, sea star 329 | n02319095 sea urchin 330 | n02321529 sea cucumber, holothurian 331 | n02325366 wood rabbit, cottontail, cottontail rabbit 332 | n02326432 hare 333 | n02328150 Angora, Angora rabbit 334 | n02342885 hamster 335 | n02346627 porcupine, hedgehog 336 | n02356798 fox squirrel, eastern fox squirrel, Sciurus niger 337 | n02361337 marmot 338 | n02363005 beaver 339 | n02364673 guinea pig, Cavia cobaya 340 | n02389026 sorrel 341 | n02391049 zebra 342 | n02395406 hog, pig, grunter, squealer, Sus scrofa 343 | n02396427 wild boar, boar, Sus scrofa 344 | n02397096 warthog 345 | n02398521 hippopotamus, hippo, river horse, Hippopotamus amphibius 346 | n02403003 ox 347 | n02408429 water buffalo, water ox, Asiatic buffalo, Bubalus bubalis 348 | n02410509 bison 349 | n02412080 ram, tup 350 | n02415577 bighorn, bighorn sheep, cimarron, Rocky Mountain bighorn, Rocky Mountain sheep, Ovis canadensis 351 | n02417914 ibex, Capra ibex 352 | n02422106 hartebeest 353 | n02422699 impala, Aepyceros melampus 354 | n02423022 gazelle 355 | n02437312 Arabian camel, dromedary, Camelus dromedarius 356 | n02437616 llama 357 | n02441942 weasel 358 | n02442845 mink 359 | n02443114 polecat, fitch, foulmart, foumart, Mustela putorius 360 | n02443484 black-footed ferret, ferret, Mustela nigripes 361 | n02444819 otter 362 | n02445715 skunk, polecat, wood pussy 363 | n02447366 badger 364 | n02454379 armadillo 365 | n02457408 three-toed sloth, ai, Bradypus tridactylus 366 | n02480495 orangutan, orang, orangutang, Pongo pygmaeus 367 | n02480855 gorilla, Gorilla gorilla 368 | n02481823 chimpanzee, chimp, Pan troglodytes 369 | n02483362 gibbon, Hylobates lar 370 | n02483708 siamang, Hylobates syndactylus, Symphalangus syndactylus 371 | n02484975 guenon, guenon monkey 372 | n02486261 patas, hussar monkey, Erythrocebus patas 373 | n02486410 baboon 374 | n02487347 macaque 375 | n02488291 langur 376 | n02488702 colobus, colobus monkey 377 | n02489166 proboscis monkey, Nasalis larvatus 378 | n02490219 marmoset 379 | n02492035 capuchin, ringtail, Cebus capucinus 380 | n02492660 howler monkey, howler 381 | n02493509 titi, titi monkey 382 | n02493793 spider monkey, Ateles geoffroyi 383 | n02494079 squirrel monkey, Saimiri sciureus 384 | n02497673 Madagascar cat, ring-tailed lemur, Lemur catta 385 | n02500267 indri, indris, Indri indri, Indri brevicaudatus 386 | n02504013 Indian elephant, Elephas maximus 387 | n02504458 African elephant, Loxodonta africana 388 | n02509815 lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens 389 | n02510455 giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca 390 | n02514041 barracouta, snoek 391 | n02526121 eel 392 | n02536864 coho, cohoe, coho salmon, blue jack, silver salmon, Oncorhynchus kisutch 393 | n02606052 rock beauty, Holocanthus tricolor 394 | n02607072 anemone fish 395 | n02640242 sturgeon 396 | n02641379 gar, garfish, garpike, billfish, Lepisosteus osseus 397 | n02643566 lionfish 398 | n02655020 puffer, pufferfish, blowfish, globefish 399 | n02666196 abacus 400 | n02667093 abaya 401 | n02669723 academic gown, academic robe, judge's robe 402 | n02672831 accordion, piano accordion, squeeze box 403 | n02676566 acoustic guitar 404 | n02687172 aircraft carrier, carrier, flattop, attack aircraft carrier 405 | n02690373 airliner 406 | n02692877 airship, dirigible 407 | n02699494 altar 408 | n02701002 ambulance 409 | n02704792 amphibian, amphibious vehicle 410 | n02708093 analog clock 411 | n02727426 apiary, bee house 412 | n02730930 apron 413 | n02747177 ashcan, trash can, garbage can, wastebin, ash bin, ash-bin, ashbin, dustbin, trash barrel, trash bin 414 | n02749479 assault rifle, assault gun 415 | n02769748 backpack, back pack, knapsack, packsack, rucksack, haversack 416 | n02776631 bakery, bakeshop, bakehouse 417 | n02777292 balance beam, beam 418 | n02782093 balloon 419 | n02783161 ballpoint, ballpoint pen, ballpen, Biro 420 | n02786058 Band Aid 421 | n02787622 banjo 422 | n02788148 bannister, banister, balustrade, balusters, handrail 423 | n02790996 barbell 424 | n02791124 barber chair 425 | n02791270 barbershop 426 | n02793495 barn 427 | n02794156 barometer 428 | n02795169 barrel, cask 429 | n02797295 barrow, garden cart, lawn cart, wheelbarrow 430 | n02799071 baseball 431 | n02802426 basketball 432 | n02804414 bassinet 433 | n02804610 bassoon 434 | n02807133 bathing cap, swimming cap 435 | n02808304 bath towel 436 | n02808440 bathtub, bathing tub, bath, tub 437 | n02814533 beach wagon, station wagon, wagon, estate car, beach waggon, station waggon, waggon 438 | n02814860 beacon, lighthouse, beacon light, pharos 439 | n02815834 beaker 440 | n02817516 bearskin, busby, shako 441 | n02823428 beer bottle 442 | n02823750 beer glass 443 | n02825657 bell cote, bell cot 444 | n02834397 bib 445 | n02835271 bicycle-built-for-two, tandem bicycle, tandem 446 | n02837789 bikini, two-piece 447 | n02840245 binder, ring-binder 448 | n02841315 binoculars, field glasses, opera glasses 449 | n02843684 birdhouse 450 | n02859443 boathouse 451 | n02860847 bobsled, bobsleigh, bob 452 | n02865351 bolo tie, bolo, bola tie, bola 453 | n02869837 bonnet, poke bonnet 454 | n02870880 bookcase 455 | n02871525 bookshop, bookstore, bookstall 456 | n02877765 bottlecap 457 | n02879718 bow 458 | n02883205 bow tie, bow-tie, bowtie 459 | n02892201 brass, memorial tablet, plaque 460 | n02892767 brassiere, bra, bandeau 461 | n02894605 breakwater, groin, groyne, mole, bulwark, seawall, jetty 462 | n02895154 breastplate, aegis, egis 463 | n02906734 broom 464 | n02909870 bucket, pail 465 | n02910353 buckle 466 | n02916936 bulletproof vest 467 | n02917067 bullet train, bullet 468 | n02927161 butcher shop, meat market 469 | n02930766 cab, hack, taxi, taxicab 470 | n02939185 caldron, cauldron 471 | n02948072 candle, taper, wax light 472 | n02950826 cannon 473 | n02951358 canoe 474 | n02951585 can opener, tin opener 475 | n02963159 cardigan 476 | n02965783 car mirror 477 | n02966193 carousel, carrousel, merry-go-round, roundabout, whirligig 478 | n02966687 carpenter's kit, tool kit 479 | n02971356 carton 480 | n02974003 car wheel 481 | n02977058 cash machine, cash dispenser, automated teller machine, automatic teller machine, automated teller, automatic teller, ATM 482 | n02978881 cassette 483 | n02979186 cassette player 484 | n02980441 castle 485 | n02981792 catamaran 486 | n02988304 CD player 487 | n02992211 cello, violoncello 488 | n02992529 cellular telephone, cellular phone, cellphone, cell, mobile phone 489 | n02999410 chain 490 | n03000134 chainlink fence 491 | n03000247 chain mail, ring mail, mail, chain armor, chain armour, ring armor, ring armour 492 | n03000684 chain saw, chainsaw 493 | n03014705 chest 494 | n03016953 chiffonier, commode 495 | n03017168 chime, bell, gong 496 | n03018349 china cabinet, china closet 497 | n03026506 Christmas stocking 498 | n03028079 church, church building 499 | n03032252 cinema, movie theater, movie theatre, movie house, picture palace 500 | n03041632 cleaver, meat cleaver, chopper 501 | n03042490 cliff dwelling 502 | n03045698 cloak 503 | n03047690 clog, geta, patten, sabot 504 | n03062245 cocktail shaker 505 | n03063599 coffee mug 506 | n03063689 coffeepot 507 | n03065424 coil, spiral, volute, whorl, helix 508 | n03075370 combination lock 509 | n03085013 computer keyboard, keypad 510 | n03089624 confectionery, confectionary, candy store 511 | n03095699 container ship, containership, container vessel 512 | n03100240 convertible 513 | n03109150 corkscrew, bottle screw 514 | n03110669 cornet, horn, trumpet, trump 515 | n03124043 cowboy boot 516 | n03124170 cowboy hat, ten-gallon hat 517 | n03125729 cradle 518 | n03126707 crane 519 | n03127747 crash helmet 520 | n03127925 crate 521 | n03131574 crib, cot 522 | n03133878 Crock Pot 523 | n03134739 croquet ball 524 | n03141823 crutch 525 | n03146219 cuirass 526 | n03160309 dam, dike, dyke 527 | n03179701 desk 528 | n03180011 desktop computer 529 | n03187595 dial telephone, dial phone 530 | n03188531 diaper, nappy, napkin 531 | n03196217 digital clock 532 | n03197337 digital watch 533 | n03201208 dining table, board 534 | n03207743 dishrag, dishcloth 535 | n03207941 dishwasher, dish washer, dishwashing machine 536 | n03208938 disk brake, disc brake 537 | n03216828 dock, dockage, docking facility 538 | n03218198 dogsled, dog sled, dog sleigh 539 | n03220513 dome 540 | n03223299 doormat, welcome mat 541 | n03240683 drilling platform, offshore rig 542 | n03249569 drum, membranophone, tympan 543 | n03250847 drumstick 544 | n03255030 dumbbell 545 | n03259280 Dutch oven 546 | n03271574 electric fan, blower 547 | n03272010 electric guitar 548 | n03272562 electric locomotive 549 | n03290653 entertainment center 550 | n03291819 envelope 551 | n03297495 espresso maker 552 | n03314780 face powder 553 | n03325584 feather boa, boa 554 | n03337140 file, file cabinet, filing cabinet 555 | n03344393 fireboat 556 | n03345487 fire engine, fire truck 557 | n03347037 fire screen, fireguard 558 | n03355925 flagpole, flagstaff 559 | n03372029 flute, transverse flute 560 | n03376595 folding chair 561 | n03379051 football helmet 562 | n03384352 forklift 563 | n03388043 fountain 564 | n03388183 fountain pen 565 | n03388549 four-poster 566 | n03393912 freight car 567 | n03394916 French horn, horn 568 | n03400231 frying pan, frypan, skillet 569 | n03404251 fur coat 570 | n03417042 garbage truck, dustcart 571 | n03424325 gasmask, respirator, gas helmet 572 | n03425413 gas pump, gasoline pump, petrol pump, island dispenser 573 | n03443371 goblet 574 | n03444034 go-kart 575 | n03445777 golf ball 576 | n03445924 golfcart, golf cart 577 | n03447447 gondola 578 | n03447721 gong, tam-tam 579 | n03450230 gown 580 | n03452741 grand piano, grand 581 | n03457902 greenhouse, nursery, glasshouse 582 | n03459775 grille, radiator grille 583 | n03461385 grocery store, grocery, food market, market 584 | n03467068 guillotine 585 | n03476684 hair slide 586 | n03476991 hair spray 587 | n03478589 half track 588 | n03481172 hammer 589 | n03482405 hamper 590 | n03483316 hand blower, blow dryer, blow drier, hair dryer, hair drier 591 | n03485407 hand-held computer, hand-held microcomputer 592 | n03485794 handkerchief, hankie, hanky, hankey 593 | n03492542 hard disc, hard disk, fixed disk 594 | n03494278 harmonica, mouth organ, harp, mouth harp 595 | n03495258 harp 596 | n03496892 harvester, reaper 597 | n03498962 hatchet 598 | n03527444 holster 599 | n03529860 home theater, home theatre 600 | n03530642 honeycomb 601 | n03532672 hook, claw 602 | n03534580 hoopskirt, crinoline 603 | n03535780 horizontal bar, high bar 604 | n03538406 horse cart, horse-cart 605 | n03544143 hourglass 606 | n03584254 iPod 607 | n03584829 iron, smoothing iron 608 | n03590841 jack-o'-lantern 609 | n03594734 jean, blue jean, denim 610 | n03594945 jeep, landrover 611 | n03595614 jersey, T-shirt, tee shirt 612 | n03598930 jigsaw puzzle 613 | n03599486 jinrikisha, ricksha, rickshaw 614 | n03602883 joystick 615 | n03617480 kimono 616 | n03623198 knee pad 617 | n03627232 knot 618 | n03630383 lab coat, laboratory coat 619 | n03633091 ladle 620 | n03637318 lampshade, lamp shade 621 | n03642806 laptop, laptop computer 622 | n03649909 lawn mower, mower 623 | n03657121 lens cap, lens cover 624 | n03658185 letter opener, paper knife, paperknife 625 | n03661043 library 626 | n03662601 lifeboat 627 | n03666591 lighter, light, igniter, ignitor 628 | n03670208 limousine, limo 629 | n03673027 liner, ocean liner 630 | n03676483 lipstick, lip rouge 631 | n03680355 Loafer 632 | n03690938 lotion 633 | n03691459 loudspeaker, speaker, speaker unit, loudspeaker system, speaker system 634 | n03692522 loupe, jeweler's loupe 635 | n03697007 lumbermill, sawmill 636 | n03706229 magnetic compass 637 | n03709823 mailbag, postbag 638 | n03710193 mailbox, letter box 639 | n03710637 maillot 640 | n03710721 maillot, tank suit 641 | n03717622 manhole cover 642 | n03720891 maraca 643 | n03721384 marimba, xylophone 644 | n03724870 mask 645 | n03729826 matchstick 646 | n03733131 maypole 647 | n03733281 maze, labyrinth 648 | n03733805 measuring cup 649 | n03742115 medicine chest, medicine cabinet 650 | n03743016 megalith, megalithic structure 651 | n03759954 microphone, mike 652 | n03761084 microwave, microwave oven 653 | n03763968 military uniform 654 | n03764736 milk can 655 | n03769881 minibus 656 | n03770439 miniskirt, mini 657 | n03770679 minivan 658 | n03773504 missile 659 | n03775071 mitten 660 | n03775546 mixing bowl 661 | n03776460 mobile home, manufactured home 662 | n03777568 Model T 663 | n03777754 modem 664 | n03781244 monastery 665 | n03782006 monitor 666 | n03785016 moped 667 | n03786901 mortar 668 | n03787032 mortarboard 669 | n03788195 mosque 670 | n03788365 mosquito net 671 | n03791053 motor scooter, scooter 672 | n03792782 mountain bike, all-terrain bike, off-roader 673 | n03792972 mountain tent 674 | n03793489 mouse, computer mouse 675 | n03794056 mousetrap 676 | n03796401 moving van 677 | n03803284 muzzle 678 | n03804744 nail 679 | n03814639 neck brace 680 | n03814906 necklace 681 | n03825788 nipple 682 | n03832673 notebook, notebook computer 683 | n03837869 obelisk 684 | n03838899 oboe, hautboy, hautbois 685 | n03840681 ocarina, sweet potato 686 | n03841143 odometer, hodometer, mileometer, milometer 687 | n03843555 oil filter 688 | n03854065 organ, pipe organ 689 | n03857828 oscilloscope, scope, cathode-ray oscilloscope, CRO 690 | n03866082 overskirt 691 | n03868242 oxcart 692 | n03868863 oxygen mask 693 | n03871628 packet 694 | n03873416 paddle, boat paddle 695 | n03874293 paddlewheel, paddle wheel 696 | n03874599 padlock 697 | n03876231 paintbrush 698 | n03877472 pajama, pyjama, pj's, jammies 699 | n03877845 palace 700 | n03884397 panpipe, pandean pipe, syrinx 701 | n03887697 paper towel 702 | n03888257 parachute, chute 703 | n03888605 parallel bars, bars 704 | n03891251 park bench 705 | n03891332 parking meter 706 | n03895866 passenger car, coach, carriage 707 | n03899768 patio, terrace 708 | n03902125 pay-phone, pay-station 709 | n03903868 pedestal, plinth, footstall 710 | n03908618 pencil box, pencil case 711 | n03908714 pencil sharpener 712 | n03916031 perfume, essence 713 | n03920288 Petri dish 714 | n03924679 photocopier 715 | n03929660 pick, plectrum, plectron 716 | n03929855 pickelhaube 717 | n03930313 picket fence, paling 718 | n03930630 pickup, pickup truck 719 | n03933933 pier 720 | n03935335 piggy bank, penny bank 721 | n03937543 pill bottle 722 | n03938244 pillow 723 | n03942813 ping-pong ball 724 | n03944341 pinwheel 725 | n03947888 pirate, pirate ship 726 | n03950228 pitcher, ewer 727 | n03954731 plane, carpenter's plane, woodworking plane 728 | n03956157 planetarium 729 | n03958227 plastic bag 730 | n03961711 plate rack 731 | n03967562 plow, plough 732 | n03970156 plunger, plumber's helper 733 | n03976467 Polaroid camera, Polaroid Land camera 734 | n03976657 pole 735 | n03977966 police van, police wagon, paddy wagon, patrol wagon, wagon, black Maria 736 | n03980874 poncho 737 | n03982430 pool table, billiard table, snooker table 738 | n03983396 pop bottle, soda bottle 739 | n03991062 pot, flowerpot 740 | n03992509 potter's wheel 741 | n03995372 power drill 742 | n03998194 prayer rug, prayer mat 743 | n04004767 printer 744 | n04005630 prison, prison house 745 | n04008634 projectile, missile 746 | n04009552 projector 747 | n04019541 puck, hockey puck 748 | n04023962 punching bag, punch bag, punching ball, punchball 749 | n04026417 purse 750 | n04033901 quill, quill pen 751 | n04033995 quilt, comforter, comfort, puff 752 | n04037443 racer, race car, racing car 753 | n04039381 racket, racquet 754 | n04040759 radiator 755 | n04041544 radio, wireless 756 | n04044716 radio telescope, radio reflector 757 | n04049303 rain barrel 758 | n04065272 recreational vehicle, RV, R.V. 759 | n04067472 reel 760 | n04069434 reflex camera 761 | n04070727 refrigerator, icebox 762 | n04074963 remote control, remote 763 | n04081281 restaurant, eating house, eating place, eatery 764 | n04086273 revolver, six-gun, six-shooter 765 | n04090263 rifle 766 | n04099969 rocking chair, rocker 767 | n04111531 rotisserie 768 | n04116512 rubber eraser, rubber, pencil eraser 769 | n04118538 rugby ball 770 | n04118776 rule, ruler 771 | n04120489 running shoe 772 | n04125021 safe 773 | n04127249 safety pin 774 | n04131690 saltshaker, salt shaker 775 | n04133789 sandal 776 | n04136333 sarong 777 | n04141076 sax, saxophone 778 | n04141327 scabbard 779 | n04141975 scale, weighing machine 780 | n04146614 school bus 781 | n04147183 schooner 782 | n04149813 scoreboard 783 | n04152593 screen, CRT screen 784 | n04153751 screw 785 | n04154565 screwdriver 786 | n04162706 seat belt, seatbelt 787 | n04179913 sewing machine 788 | n04192698 shield, buckler 789 | n04200800 shoe shop, shoe-shop, shoe store 790 | n04201297 shoji 791 | n04204238 shopping basket 792 | n04204347 shopping cart 793 | n04208210 shovel 794 | n04209133 shower cap 795 | n04209239 shower curtain 796 | n04228054 ski 797 | n04229816 ski mask 798 | n04235860 sleeping bag 799 | n04238763 slide rule, slipstick 800 | n04239074 sliding door 801 | n04243546 slot, one-armed bandit 802 | n04251144 snorkel 803 | n04252077 snowmobile 804 | n04252225 snowplow, snowplough 805 | n04254120 soap dispenser 806 | n04254680 soccer ball 807 | n04254777 sock 808 | n04258138 solar dish, solar collector, solar furnace 809 | n04259630 sombrero 810 | n04263257 soup bowl 811 | n04264628 space bar 812 | n04265275 space heater 813 | n04266014 space shuttle 814 | n04270147 spatula 815 | n04273569 speedboat 816 | n04275548 spider web, spider's web 817 | n04277352 spindle 818 | n04285008 sports car, sport car 819 | n04286575 spotlight, spot 820 | n04296562 stage 821 | n04310018 steam locomotive 822 | n04311004 steel arch bridge 823 | n04311174 steel drum 824 | n04317175 stethoscope 825 | n04325704 stole 826 | n04326547 stone wall 827 | n04328186 stopwatch, stop watch 828 | n04330267 stove 829 | n04332243 strainer 830 | n04335435 streetcar, tram, tramcar, trolley, trolley car 831 | n04336792 stretcher 832 | n04344873 studio couch, day bed 833 | n04346328 stupa, tope 834 | n04347754 submarine, pigboat, sub, U-boat 835 | n04350905 suit, suit of clothes 836 | n04355338 sundial 837 | n04355933 sunglass 838 | n04356056 sunglasses, dark glasses, shades 839 | n04357314 sunscreen, sunblock, sun blocker 840 | n04366367 suspension bridge 841 | n04367480 swab, swob, mop 842 | n04370456 sweatshirt 843 | n04371430 swimming trunks, bathing trunks 844 | n04371774 swing 845 | n04372370 switch, electric switch, electrical switch 846 | n04376876 syringe 847 | n04380533 table lamp 848 | n04389033 tank, army tank, armored combat vehicle, armoured combat vehicle 849 | n04392985 tape player 850 | n04398044 teapot 851 | n04399382 teddy, teddy bear 852 | n04404412 television, television system 853 | n04409515 tennis ball 854 | n04417672 thatch, thatched roof 855 | n04418357 theater curtain, theatre curtain 856 | n04423845 thimble 857 | n04428191 thresher, thrasher, threshing machine 858 | n04429376 throne 859 | n04435653 tile roof 860 | n04442312 toaster 861 | n04443257 tobacco shop, tobacconist shop, tobacconist 862 | n04447861 toilet seat 863 | n04456115 torch 864 | n04458633 totem pole 865 | n04461696 tow truck, tow car, wrecker 866 | n04462240 toyshop 867 | n04465501 tractor 868 | n04467665 trailer truck, tractor trailer, trucking rig, rig, articulated lorry, semi 869 | n04476259 tray 870 | n04479046 trench coat 871 | n04482393 tricycle, trike, velocipede 872 | n04483307 trimaran 873 | n04485082 tripod 874 | n04486054 triumphal arch 875 | n04487081 trolleybus, trolley coach, trackless trolley 876 | n04487394 trombone 877 | n04493381 tub, vat 878 | n04501370 turnstile 879 | n04505470 typewriter keyboard 880 | n04507155 umbrella 881 | n04509417 unicycle, monocycle 882 | n04515003 upright, upright piano 883 | n04517823 vacuum, vacuum cleaner 884 | n04522168 vase 885 | n04523525 vault 886 | n04525038 velvet 887 | n04525305 vending machine 888 | n04532106 vestment 889 | n04532670 viaduct 890 | n04536866 violin, fiddle 891 | n04540053 volleyball 892 | n04542943 waffle iron 893 | n04548280 wall clock 894 | n04548362 wallet, billfold, notecase, pocketbook 895 | n04550184 wardrobe, closet, press 896 | n04552348 warplane, military plane 897 | n04553703 washbasin, handbasin, washbowl, lavabo, wash-hand basin 898 | n04554684 washer, automatic washer, washing machine 899 | n04557648 water bottle 900 | n04560804 water jug 901 | n04562935 water tower 902 | n04579145 whiskey jug 903 | n04579432 whistle 904 | n04584207 wig 905 | n04589890 window screen 906 | n04590129 window shade 907 | n04591157 Windsor tie 908 | n04591713 wine bottle 909 | n04592741 wing 910 | n04596742 wok 911 | n04597913 wooden spoon 912 | n04599235 wool, woolen, woollen 913 | n04604644 worm fence, snake fence, snake-rail fence, Virginia fence 914 | n04606251 wreck 915 | n04612504 yawl 916 | n04613696 yurt 917 | n06359193 web site, website, internet site, site 918 | n06596364 comic book 919 | n06785654 crossword puzzle, crossword 920 | n06794110 street sign 921 | n06874185 traffic light, traffic signal, stoplight 922 | n07248320 book jacket, dust cover, dust jacket, dust wrapper 923 | n07565083 menu 924 | n07579787 plate 925 | n07583066 guacamole 926 | n07584110 consomme 927 | n07590611 hot pot, hotpot 928 | n07613480 trifle 929 | n07614500 ice cream, icecream 930 | n07615774 ice lolly, lolly, lollipop, popsicle 931 | n07684084 French loaf 932 | n07693725 bagel, beigel 933 | n07695742 pretzel 934 | n07697313 cheeseburger 935 | n07697537 hotdog, hot dog, red hot 936 | n07711569 mashed potato 937 | n07714571 head cabbage 938 | n07714990 broccoli 939 | n07715103 cauliflower 940 | n07716358 zucchini, courgette 941 | n07716906 spaghetti squash 942 | n07717410 acorn squash 943 | n07717556 butternut squash 944 | n07718472 cucumber, cuke 945 | n07718747 artichoke, globe artichoke 946 | n07720875 bell pepper 947 | n07730033 cardoon 948 | n07734744 mushroom 949 | n07742313 Granny Smith 950 | n07745940 strawberry 951 | n07747607 orange 952 | n07749582 lemon 953 | n07753113 fig 954 | n07753275 pineapple, ananas 955 | n07753592 banana 956 | n07754684 jackfruit, jak, jack 957 | n07760859 custard apple 958 | n07768694 pomegranate 959 | n07802026 hay 960 | n07831146 carbonara 961 | n07836838 chocolate sauce, chocolate syrup 962 | n07860988 dough 963 | n07871810 meat loaf, meatloaf 964 | n07873807 pizza, pizza pie 965 | n07875152 potpie 966 | n07880968 burrito 967 | n07892512 red wine 968 | n07920052 espresso 969 | n07930864 cup 970 | n07932039 eggnog 971 | n09193705 alp 972 | n09229709 bubble 973 | n09246464 cliff, drop, drop-off 974 | n09256479 coral reef 975 | n09288635 geyser 976 | n09332890 lakeside, lakeshore 977 | n09399592 promontory, headland, head, foreland 978 | n09421951 sandbar, sand bar 979 | n09428293 seashore, coast, seacoast, sea-coast 980 | n09468604 valley, vale 981 | n09472597 volcano 982 | n09835506 ballplayer, baseball player 983 | n10148035 groom, bridegroom 984 | n10565667 scuba diver 985 | n11879895 rapeseed 986 | n11939491 daisy 987 | n12057211 yellow lady's slipper, yellow lady-slipper, Cypripedium calceolus, Cypripedium parviflorum 988 | n12144580 corn 989 | n12267677 acorn 990 | n12620546 hip, rose hip, rosehip 991 | n12768682 buckeye, horse chestnut, conker 992 | n12985857 coral fungus 993 | n12998815 agaric 994 | n13037406 gyromitra 995 | n13040303 stinkhorn, carrion fungus 996 | n13044778 earthstar 997 | n13052670 hen-of-the-woods, hen of the woods, Polyporus frondosus, Grifola frondosa 998 | n13054560 bolete 999 | n13133613 ear, spike, capitulum 1000 | n15075141 toilet tissue, toilet paper, bathroom tissue 1001 | -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | XNOR MXNET 3 | Please Wait 4 | Loading Model 5 | This device doesn\'t support Camera2 API. 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 15 | 16 |