├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── LICENSE ├── README.md ├── app ├── .idea │ ├── gradle.xml │ ├── misc.xml │ ├── modules.xml │ ├── runConfigurations.xml │ └── workspace.xml ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src │ └── main │ ├── AndroidManifest.xml │ ├── BUILD │ ├── assets │ └── BUILD │ ├── java │ └── com │ │ └── example │ │ └── android │ │ └── tflitecamerademo │ │ ├── CameraActivity.java │ │ ├── CustomCameraFragment.java │ │ └── ImageClassifier.java │ └── res │ ├── drawable-hdpi │ └── ic_launcher.png │ ├── drawable-mdpi │ └── ic_launcher.png │ ├── drawable-xhdpi │ └── ic_launcher.png │ ├── drawable-xxhdpi │ ├── cancel.png │ ├── capture.png │ ├── focus.png │ ├── ic_launcher.png │ └── image.png │ ├── drawable │ ├── divider.xml │ ├── green_progress.xml │ ├── handle.xml │ ├── red_progress.xml │ └── yellow_progress.xml │ ├── layout │ ├── activity_camera.xml │ ├── camera.xml │ └── fragment_custom_camera.xml │ └── values │ ├── strings.xml │ └── styles.xml ├── 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 | model_android/ 6 | 7 | # Files for the ART/Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | 18 | # Gradle files 19 | .gradle/ 20 | build/ 21 | 22 | # Local configuration file (sdk path, etc) 23 | local.properties 24 | 25 | # Proguard folder generated by Eclipse 26 | proguard/ 27 | 28 | # Log Files 29 | *.log 30 | 31 | # Android Studio Navigation editor temp files 32 | .navigation/ 33 | 34 | # Android Studio captures folder 35 | captures/ 36 | 37 | # IntelliJ 38 | *.iml 39 | .idea/workspace.xml 40 | .idea/tasks.xml 41 | .idea/gradle.xml 42 | .idea/assetWizardSettings.xml 43 | .idea/dictionaries 44 | .idea/libraries 45 | .idea/caches 46 | 47 | # Keystore files 48 | # Uncomment the following line if you do not want to check your keystore files in. 49 | #*.jks 50 | 51 | # External native build folder generated in Android Studio 2.2 and later 52 | .externalNativeBuild 53 | 54 | # Google Services (e.g. APIs or Firebase) 55 | google-services.json 56 | 57 | # Freeline 58 | freeline.py 59 | freeline/ 60 | freeline_project_description.json 61 | 62 | # fastlane 63 | fastlane/report.xml 64 | fastlane/Preview.html 65 | fastlane/screenshots 66 | fastlane/test_output 67 | fastlane/readme.md 68 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Cloud Annotations 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Object Detection Android App 2 | 3 | You can find an in depth walkthrough for training a TensorFlow lite model [here](https://github.com/cloud-annotations/training/). 4 | 5 | ## Setup 6 | `git clone` the repo and `cd` into it by running the following command: 7 | 8 | ```bash 9 | git clone https://github.com/cloud-annotations/object-detection-android.git 10 | cd object-detection-android 11 | ``` 12 | 13 | ## Install Android Studio 14 | The recommended way to develop applications for Android is by using Android Studio, which can be downloaded [here](https://developer.android.com/studio/index.html) 15 | 16 | ## Open the project with Android Studio 17 | Launch Android Studio and choose **Open an existing Android Studio project** 18 | 19 | ![](https://codelabs.developers.google.com/codelabs/tensorflow-for-poets-2-tflite/img/1482ddc7911df61b.png) 20 | 21 | In the file selector, choose `object-detection-android`. 22 | 23 | You will get a **Gradle Sync** popup, the first time you open the project, asking about using gradle wrapper. Click **OK**. 24 | 25 | ![](https://codelabs.developers.google.com/codelabs/tensorflow-for-poets-2-tflite/img/b9f9a03dd27fd1bb.png) 26 | 27 | ## Set up an Android device 28 | You can't load the app from android studio onto your phone unless you activate **developer mode** and **USB Debugging**. This is a one time setup process. 29 | 30 | Follow these [instructions](https://developer.android.com/studio/debug/dev-options.html#enable). 31 | 32 | ## Add your model files to the project 33 | Copy the `model_android` directory generated from the classification walkthrough and paste it into the `object-detection-android/app/src/main/assets` folder of this repo. 34 | 35 | ## Run the app 36 | In Android Studio run a **Gradle sync** so the build system can find your files. 37 | 38 | ![](https://codelabs.developers.google.com/codelabs/tensorflow-for-poets-2-tflite/img/774326d4e89c2559.png) 39 | 40 | Then hit play to start the build and install process. 41 | -------------------------------------------------------------------------------- /app/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | -------------------------------------------------------------------------------- /app/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 39 | 40 | 41 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 69 | 70 | 73 | 74 | 75 | 76 | 79 | 80 | 83 | 84 | 85 | 86 | 89 | 90 | 93 | 94 | 97 | 98 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 120 | 121 | 122 | 123 | 124 | 137 | 138 | 145 | 146 | 147 | 148 | 166 | 173 | 174 | 182 | 183 | 188 | 189 | 191 | 192 | 193 | 195 | 196 | 197 | 198 | 1549840363417 199 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | project.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets' 4 | 5 | android { 6 | compileSdkVersion 28 7 | defaultConfig { 8 | applicationId "android.example.com.tflitecamerademo" 9 | minSdkVersion 14 10 | targetSdkVersion 28 11 | versionCode 1 12 | versionName "1.0" 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | } 15 | lintOptions { 16 | abortOnError false 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | aaptOptions { 25 | noCompress "tflite" 26 | noCompress "lite" 27 | } 28 | 29 | compileOptions { 30 | sourceCompatibility JavaVersion.VERSION_1_8 31 | targetCompatibility JavaVersion.VERSION_1_8 32 | } 33 | } 34 | 35 | repositories { 36 | maven { 37 | url 'https://google.bintray.com/tensorflow' 38 | } 39 | google() 40 | } 41 | 42 | dependencies { 43 | implementation fileTree(dir: 'libs', include: ['*.jar']) 44 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { 45 | exclude group: 'com.android.support', module: 'support-annotations' 46 | }) 47 | implementation 'com.xlythe:camera-view:1.2.8' 48 | implementation 'androidx.legacy:legacy-support-v4:+' 49 | implementation 'androidx.appcompat:appcompat:+' 50 | implementation 'androidx.exifinterface:exifinterface:+' 51 | implementation 'com.google.android.material:material:+' 52 | implementation 'androidx.recyclerview:recyclerview:+' 53 | 54 | implementation 'org.tensorflow:tensorflow-lite:+' 55 | 56 | testImplementation 'junit:junit:4.12' 57 | } 58 | -------------------------------------------------------------------------------- /app/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-annotations/object-detection-android/565d65dbbd966281fc42a48f33a25c0ed354da03/app/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Feb 10 18:12:47 EST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip 7 | -------------------------------------------------------------------------------- /app/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /app/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/BUILD: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//visibility:private"]) 2 | 3 | licenses(["notice"]) # Apache 2.0 4 | 5 | android_binary( 6 | name = "TfLiteCameraDemo", 7 | srcs = glob(["java/**/*.java"]), 8 | assets = [ 9 | "@tflite_mobilenet//:labels.txt", 10 | "@tflite_mobilenet//:mobilenet_quant_v1_224.tflite", 11 | ], 12 | assets_dir = "", 13 | custom_package = "com.example.android.tflitecamerademo", 14 | manifest = "AndroidManifest.xml", 15 | nocompress_extensions = [ 16 | ".tflite", 17 | ], 18 | resource_files = glob(["res/**"]), 19 | # In some platforms we don't have an Android SDK/NDK and this target 20 | # can't be built. We need to prevent the build system from trying to 21 | # use the target in that case. 22 | tags = ["manual"], 23 | deps = [ 24 | "//tensorflow/contrib/lite/java:tensorflowlite", 25 | "//tensorflow/contrib/lite/java/src/testhelper/java/org/tensorflow/lite:testhelper", 26 | "@androidsdk//com.android.support:support-v13-25.2.0", 27 | "@androidsdk//com.android.support:support-v4-25.2.0", 28 | ], 29 | ) 30 | 31 | filegroup( 32 | name = "all_files", 33 | srcs = glob( 34 | ["**/*"], 35 | exclude = [ 36 | "**/METADATA", 37 | "**/OWNERS", 38 | ], 39 | ), 40 | visibility = ["//tensorflow:__subpackages__"], 41 | ) 42 | -------------------------------------------------------------------------------- /app/src/main/assets/BUILD: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//visibility:private"]) 2 | 3 | licenses(["notice"]) # Apache 2.0 4 | 5 | exports_files( 6 | glob( 7 | ["**/*"], 8 | exclude = [ 9 | "BUILD", 10 | ], 11 | ), 12 | ) 13 | 14 | filegroup( 15 | name = "all_files", 16 | srcs = glob( 17 | ["**/*"], 18 | exclude = [ 19 | "**/METADATA", 20 | "**/OWNERS", 21 | ], 22 | ), 23 | visibility = ["//tensorflow:__subpackages__"], 24 | ) 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/tflitecamerademo/CameraActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.android.tflitecamerademo; 2 | 3 | import android.os.Bundle; 4 | 5 | import androidx.appcompat.app.AppCompatActivity; 6 | 7 | public class CameraActivity extends AppCompatActivity { 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | setContentView(R.layout.activity_camera); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/tflitecamerademo/CustomCameraFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.android.tflitecamerademo; 2 | 3 | import android.Manifest; 4 | import android.content.Context; 5 | import android.graphics.Bitmap; 6 | import android.graphics.Canvas; 7 | import android.graphics.Color; 8 | import android.graphics.Matrix; 9 | import android.graphics.Paint; 10 | import android.graphics.Point; 11 | import android.graphics.RectF; 12 | import android.hardware.display.DisplayManager; 13 | import android.media.ThumbnailUtils; 14 | import android.os.Build; 15 | import android.os.Bundle; 16 | import android.os.Handler; 17 | import android.text.TextPaint; 18 | import android.util.Log; 19 | import android.view.Display; 20 | import android.view.LayoutInflater; 21 | import android.view.TextureView; 22 | import android.view.View; 23 | import android.view.ViewGroup; 24 | import android.widget.Button; 25 | import android.widget.ImageView; 26 | import android.widget.LinearLayout; 27 | 28 | import com.google.android.material.bottomsheet.BottomSheetBehavior; 29 | import com.xlythe.fragment.camera.CameraFragment; 30 | import com.xlythe.view.camera.CameraView; 31 | import com.xlythe.view.camera.PermissionChecker; 32 | 33 | import java.io.File; 34 | import java.io.IOException; 35 | import java.lang.reflect.Field; 36 | import java.util.ArrayList; 37 | import java.util.Arrays; 38 | import java.util.List; 39 | 40 | import androidx.annotation.NonNull; 41 | import androidx.annotation.Nullable; 42 | import androidx.core.content.ContextCompat; 43 | import androidx.fragment.app.Fragment; 44 | import androidx.recyclerview.widget.DividerItemDecoration; 45 | import androidx.recyclerview.widget.LinearLayoutManager; 46 | import androidx.recyclerview.widget.RecyclerView; 47 | 48 | import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED; 49 | import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN; 50 | 51 | public class CustomCameraFragment extends Fragment { 52 | private static final String[] REQUIRED_PERMISSIONS; 53 | private static final String[] OPTIONAL_PERMISSIONS; 54 | private static final int REQUEST_CODE_PERMISSIONS = 10; 55 | static { 56 | // In KitKat+, WRITE_EXTERNAL_STORAGE is optional 57 | if (Build.VERSION.SDK_INT >= 19) { 58 | REQUIRED_PERMISSIONS = new String[] { 59 | Manifest.permission.CAMERA, 60 | Manifest.permission.RECORD_AUDIO 61 | }; 62 | OPTIONAL_PERMISSIONS = new String[] { 63 | Manifest.permission.WRITE_EXTERNAL_STORAGE, 64 | Manifest.permission.ACCESS_FINE_LOCATION, 65 | Manifest.permission.VIBRATE 66 | }; 67 | } else { 68 | REQUIRED_PERMISSIONS = new String[] { 69 | Manifest.permission.CAMERA, 70 | Manifest.permission.RECORD_AUDIO, 71 | Manifest.permission.WRITE_EXTERNAL_STORAGE 72 | }; 73 | OPTIONAL_PERMISSIONS = new String[] { 74 | Manifest.permission.ACCESS_FINE_LOCATION, 75 | Manifest.permission.VIBRATE 76 | }; 77 | } 78 | } 79 | 80 | private ImageClassifier mClassifier; 81 | 82 | private CameraView mCamera; 83 | private ImageView mPreview; 84 | private ViewGroup mCameraLayout; 85 | private ViewGroup mPreviewLayout; 86 | private View mPermissionPrompt; 87 | private View mPermissionRequest; 88 | private Button mCapture; 89 | private Button mClose; 90 | 91 | private boolean mShowCamera; 92 | 93 | private View.OnClickListener mBitmapCaptured = (view) -> { 94 | try { 95 | // Reflection. 96 | Field field = mCamera.getClass().getDeclaredField("mCameraView"); 97 | field.setAccessible(true); 98 | TextureView textureView = (TextureView) field.get(mCamera); 99 | 100 | Matrix matrix = textureView.getTransform(null); 101 | float[] values = new float[9]; 102 | matrix.getValues(values); 103 | int newWidth = textureView.getWidth() - (int)values[2] * 2; 104 | int newHeight = textureView.getHeight() - (int)values[5] * 2; 105 | 106 | // Get the image and classify it. 107 | Bitmap bitmap = textureView.getBitmap(newWidth, newHeight); 108 | Bitmap scaled = Bitmap.createScaledBitmap(bitmap, ImageClassifier.DIM_IMG_SIZE_X, ImageClassifier.DIM_IMG_SIZE_Y, false); 109 | List predictions = mClassifier.classifyFrame(scaled); 110 | 111 | float dpi = getResources().getDisplayMetrics().density; 112 | 113 | Canvas canvas = new Canvas(bitmap); 114 | Paint paint = new Paint(); 115 | paint.setColor(Color.rgb(36,101,255)); 116 | paint.setStyle(Paint.Style.STROKE); 117 | paint.setAntiAlias(true); 118 | paint.setStrokeWidth(dpi * 3); 119 | 120 | Paint labelPaint = new Paint(); 121 | labelPaint.setColor(Color.rgb(36,101,255)); 122 | labelPaint.setStyle(Paint.Style.FILL); 123 | labelPaint.setAntiAlias(true); 124 | 125 | TextPaint textPaint = new TextPaint(); 126 | textPaint.setAntiAlias(true); 127 | textPaint.setTextSize(14 * dpi); 128 | textPaint.setColor(Color.WHITE); 129 | 130 | for (Prediction prediction : predictions) { 131 | float boxLeft = prediction.bbox.x * canvas.getWidth(); 132 | float boxTop = prediction.bbox.y * canvas.getHeight(); 133 | float boxRight = (prediction.bbox.x + prediction.bbox.width) * canvas.getWidth(); 134 | float boxBottom =(prediction.bbox.y + prediction.bbox.height) * canvas.getHeight(); 135 | 136 | canvas.drawRoundRect(boxLeft, boxTop, boxRight, boxBottom,dpi * 6, dpi * 6, paint); 137 | } 138 | 139 | // Draw text separately so it doesn't get overlapped by boxes. 140 | for (Prediction prediction : predictions) { 141 | float boxLeft = prediction.bbox.x * canvas.getWidth(); 142 | float boxTop = prediction.bbox.y * canvas.getHeight(); 143 | 144 | float width = textPaint.measureText(prediction.label) + 6 * dpi; 145 | float height = -textPaint.ascent() + textPaint.descent(); 146 | float xOrigin = boxLeft; 147 | float yOrigin = boxTop - height - 3 * dpi; 148 | canvas.drawRect(xOrigin, yOrigin, xOrigin + width, yOrigin + height, labelPaint); 149 | canvas.drawText(prediction.label, boxLeft + 3 * dpi, boxTop - 6 * dpi, textPaint); 150 | } 151 | 152 | mPreview.setScaleType(ImageView.ScaleType.CENTER_CROP); 153 | mPreview.setImageBitmap(bitmap); 154 | showPreview(); 155 | } catch (NoSuchFieldException e) { 156 | e.printStackTrace(); 157 | } catch (IllegalAccessException e) { 158 | e.printStackTrace(); 159 | } 160 | }; 161 | 162 | private View.OnClickListener mClosePreview = (view) -> showCamera(); 163 | 164 | private View.OnClickListener mPermissionListener = (view) -> requestPermissions( 165 | concat(REQUIRED_PERMISSIONS, OPTIONAL_PERMISSIONS), REQUEST_CODE_PERMISSIONS 166 | ); 167 | 168 | private void showPreview() { 169 | mShowCamera = false; 170 | mCameraLayout.setVisibility(View.GONE); 171 | mPreviewLayout.setVisibility(View.VISIBLE); 172 | } 173 | 174 | private void showCamera() { 175 | mShowCamera = true; 176 | mCameraLayout.setVisibility(View.VISIBLE); 177 | mPreviewLayout.setVisibility(View.GONE); 178 | mPermissionPrompt.setVisibility(View.GONE); 179 | } 180 | 181 | private void showPermissionPrompt() { 182 | mCameraLayout.setVisibility(View.GONE); 183 | mPreviewLayout.setVisibility(View.GONE); 184 | mPermissionPrompt.setVisibility(View.VISIBLE); 185 | } 186 | 187 | @Override 188 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 189 | if (requestCode == REQUEST_CODE_PERMISSIONS) { 190 | if (PermissionChecker.hasPermissions(getContext(), REQUIRED_PERMISSIONS)) { 191 | showCamera(); 192 | } else { 193 | showPermissionPrompt(); 194 | } 195 | } 196 | } 197 | 198 | @Override 199 | public void onCreate(Bundle savedInstanceState) { 200 | super.onCreate(savedInstanceState); 201 | mShowCamera = true; 202 | try { 203 | mClassifier = new ImageClassifier(getContext()); 204 | } catch (IOException e) { 205 | e.printStackTrace(); 206 | } 207 | } 208 | 209 | @Override 210 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 211 | return View.inflate(getContext(), R.layout.fragment_custom_camera, container); 212 | } 213 | 214 | @Override 215 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 216 | super.onViewCreated(view, savedInstanceState); 217 | mCamera = view.findViewById(R.id.camera); 218 | mPreview = view.findViewById(R.id.image_result); 219 | mCameraLayout = view.findViewById(R.id.layout_camera); 220 | mPreviewLayout = view.findViewById(R.id.layout_preview); 221 | mCapture = view.findViewById(R.id.capture_bitmap); 222 | mClose = view.findViewById(R.id.close_preview); 223 | mPermissionPrompt = view.findViewById(R.id.layout_permissions); 224 | mPermissionRequest = view.findViewById(R.id.request_permissions); 225 | 226 | mCamera.open(); 227 | } 228 | 229 | @Override 230 | public void onActivityCreated(Bundle savedInstanceState) { 231 | super.onActivityCreated(savedInstanceState); 232 | mCapture.setOnClickListener(mBitmapCaptured); 233 | mClose.setOnClickListener(mClosePreview); 234 | mPermissionRequest.setOnClickListener(mPermissionListener); 235 | } 236 | 237 | @Override 238 | public void onStart() { 239 | super.onStart(); 240 | if (PermissionChecker.hasPermissions(getContext(), REQUIRED_PERMISSIONS)) { 241 | if (mShowCamera) { 242 | showCamera(); 243 | } else { 244 | showPreview(); 245 | } 246 | } else { 247 | showPermissionPrompt(); 248 | } 249 | } 250 | 251 | @Override 252 | public void onStop() { 253 | mCamera.close(); 254 | mClassifier.close(); 255 | super.onStop(); 256 | } 257 | 258 | private static T[] concat(T[] first, T[] second) { 259 | T[] result = Arrays.copyOf(first, first.length + second.length); 260 | System.arraycopy(second, 0, result, first.length, second.length); 261 | return result; 262 | } 263 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/tflitecamerademo/ImageClassifier.java: -------------------------------------------------------------------------------- 1 | package com.example.android.tflitecamerademo; 2 | 3 | import android.content.Context; 4 | import android.content.res.AssetFileDescriptor; 5 | import android.graphics.Bitmap; 6 | import android.util.Log; 7 | 8 | import org.json.JSONArray; 9 | import org.json.JSONException; 10 | import org.tensorflow.lite.Interpreter; 11 | 12 | import java.io.BufferedReader; 13 | import java.io.FileInputStream; 14 | import java.io.IOException; 15 | import java.io.InputStreamReader; 16 | import java.nio.ByteBuffer; 17 | import java.nio.ByteOrder; 18 | import java.nio.MappedByteBuffer; 19 | import java.nio.channels.FileChannel; 20 | import java.util.ArrayList; 21 | import java.util.Collections; 22 | import java.util.HashMap; 23 | import java.util.List; 24 | import java.util.Map; 25 | 26 | class BBox { 27 | float x; 28 | float width; 29 | float y; 30 | float height; 31 | 32 | BBox(float x, float width, float y, float height) { 33 | this.x = x; 34 | this.width = width; 35 | this.y = y; 36 | this.height = height; 37 | } 38 | } 39 | 40 | class Prediction implements Comparable { 41 | String label; 42 | BBox bbox; 43 | Float score; 44 | 45 | Prediction(String label, float score, BBox bbox) { 46 | this.label = label; 47 | this.score = score; 48 | this.bbox = bbox; 49 | } 50 | 51 | @Override 52 | public int compareTo(Prediction o) { 53 | return o.score.compareTo(score); 54 | } 55 | } 56 | 57 | class ImageClassifier { 58 | private static final String TAG = "TfLiteCameraDemo"; 59 | 60 | private static final String MODEL_PATH = "model_android/model.tflite"; 61 | private static final String LABEL_PATH = "model_android/labels.json"; 62 | private static final String ANCHORS_PATH = "model_android/anchors.json"; 63 | 64 | private static final int NUM_ANCHORS = 1917; 65 | private static final int DIM_BATCH_SIZE = 1; 66 | private static final int DIM_PIXEL_SIZE = 3; 67 | static final int DIM_IMG_SIZE_X = 300; 68 | static final int DIM_IMG_SIZE_Y = 300; 69 | 70 | private static final float IOU_THRESHOLD = 0.5f; 71 | private static final float CONFIDENCE_THRESHOLD = 0.5f; 72 | 73 | private static final int IMAGE_MEAN = 128; 74 | private static final float IMAGE_STD = 128.0f; 75 | 76 | private static final int NUM_THREADS = 4; 77 | 78 | private Interpreter mTensorFlowLite; 79 | private List mLabelList; 80 | private double[][] mAnchors; 81 | 82 | ImageClassifier(Context context) throws IOException { 83 | Interpreter.Options options = new Interpreter.Options(); 84 | options.setNumThreads(NUM_THREADS); 85 | mTensorFlowLite = new Interpreter(loadModelFile(context), options); 86 | mLabelList = loadLabelList(context); 87 | mAnchors = loadAnchors(context); 88 | } 89 | 90 | /** Classifies a frame from the preview stream. */ 91 | List classifyFrame(Bitmap bitmap) { 92 | if (mTensorFlowLite == null) { 93 | Log.e(TAG, "Image classifier has not been initialized; Skipped."); 94 | return new ArrayList<>(); 95 | } 96 | ByteBuffer buffer = convertBitmapToByteBuffer(bitmap); 97 | 98 | // The models output expects a shape of [1 x 1917 x 1 x 4] 99 | float[][][][] _boxPredictionsPointer_ = new float[1][NUM_ANCHORS][1][4]; 100 | // The models output expects a shape of [1 x 1917 x labels_size + 1] 101 | float[][][] _classPredictionsPointer_ = new float[1][NUM_ANCHORS][mLabelList.size() + 1]; 102 | 103 | Object[] inputArray = {buffer}; 104 | Map outputMap = new HashMap<>(); 105 | outputMap.put(0, _boxPredictionsPointer_); 106 | outputMap.put(1, _classPredictionsPointer_); 107 | mTensorFlowLite.runForMultipleInputsOutputs(inputArray, outputMap); 108 | 109 | List predictions = buildPredictions(_boxPredictionsPointer_, _classPredictionsPointer_); 110 | predictions = nms(predictions); 111 | return predictions; 112 | } 113 | 114 | void close() { 115 | mTensorFlowLite.close(); 116 | mTensorFlowLite = null; 117 | } 118 | 119 | /** Reads label list from Assets. */ 120 | private List loadLabelList(Context context) throws IOException { 121 | List labelList = new ArrayList<>(); 122 | try (BufferedReader br = new BufferedReader(new InputStreamReader(context.getAssets().open(LABEL_PATH)))) { 123 | StringBuilder json = new StringBuilder(); 124 | String line; 125 | while((line = br.readLine()) != null) { 126 | json.append(line); 127 | } 128 | JSONArray jsonarray = new JSONArray(json.toString()); 129 | for (int i = 0; i < jsonarray.length(); i++) { 130 | labelList.add(jsonarray.getString(i)); 131 | } 132 | } catch (JSONException e) { 133 | e.printStackTrace(); 134 | } 135 | return labelList; 136 | } 137 | 138 | /** Reads anchors from Assets. */ 139 | private double[][] loadAnchors(Context context) throws IOException { 140 | double[][] anchors = new double[NUM_ANCHORS][4]; 141 | try (BufferedReader br = new BufferedReader(new InputStreamReader(context.getAssets().open(ANCHORS_PATH)))) { 142 | StringBuilder json = new StringBuilder(); 143 | String line; 144 | while((line = br.readLine()) != null) { 145 | json.append(line); 146 | } 147 | JSONArray jsonarray = new JSONArray(json.toString()); 148 | for (int i = 0; i < jsonarray.length(); i++) { 149 | JSONArray anchor = jsonarray.getJSONArray(i); 150 | for (int j = 0; j < anchor.length(); j++) { 151 | anchors[i][j] = anchor.getDouble(j); 152 | } 153 | } 154 | } catch (JSONException e) { 155 | e.printStackTrace(); 156 | } 157 | return anchors; 158 | } 159 | 160 | /** Memory-map the model file in Assets. */ 161 | private MappedByteBuffer loadModelFile(Context context) throws IOException { 162 | AssetFileDescriptor fileDescriptor = context.getAssets().openFd(MODEL_PATH); 163 | FileInputStream inputStream = new FileInputStream(fileDescriptor.getFileDescriptor()); 164 | FileChannel fileChannel = inputStream.getChannel(); 165 | long startOffset = fileDescriptor.getStartOffset(); 166 | long declaredLength = fileDescriptor.getDeclaredLength(); 167 | return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength); 168 | } 169 | 170 | /** Writes Image data into a {@code ByteBuffer}. */ 171 | private ByteBuffer convertBitmapToByteBuffer(Bitmap bitmap) { 172 | ByteBuffer buffer = ByteBuffer.allocateDirect(4 * DIM_BATCH_SIZE * DIM_IMG_SIZE_X * DIM_IMG_SIZE_Y * DIM_PIXEL_SIZE); 173 | buffer.order(ByteOrder.nativeOrder()); 174 | buffer.rewind(); 175 | 176 | int[] intValues = new int[DIM_IMG_SIZE_X * DIM_IMG_SIZE_Y]; 177 | bitmap.getPixels(intValues, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight()); 178 | // Convert the image to floating point. 179 | for (final int val : intValues) { 180 | buffer.putFloat((((val >> 16) & 0xFF) - IMAGE_MEAN) / IMAGE_STD); 181 | buffer.putFloat((((val >> 8) & 0xFF) - IMAGE_MEAN) / IMAGE_STD); 182 | buffer.putFloat((((val) & 0xFF) - IMAGE_MEAN) / IMAGE_STD); 183 | } 184 | return buffer; 185 | } 186 | 187 | private List buildPredictions(float[][][][] boxes, float[][][] classes) { 188 | List predictions = new ArrayList<>(); 189 | for (int c = 1; c <= mLabelList.size(); c++) { 190 | for (int b = 0; b <= NUM_ANCHORS - 1; b++) { 191 | float score = classes[0][b][c]; 192 | if (score > CONFIDENCE_THRESHOLD) { 193 | String label = mLabelList.get(c - 1); 194 | 195 | float ty = boxes[0][b][0][0] / 10.0f; 196 | float tx = boxes[0][b][0][1] / 10.0f; 197 | float th = boxes[0][b][0][2] / 5.0f; 198 | float tw = boxes[0][b][0][3] / 5.0f; 199 | 200 | float yACtr = (float) mAnchors[b][0]; 201 | float xACtr = (float) mAnchors[b][1]; 202 | float ha = (float) mAnchors[b][2]; 203 | float wa = (float) mAnchors[b][3]; 204 | 205 | float w = (float) Math.exp(tw) * wa; 206 | float h = (float) Math.exp(th) * ha; 207 | 208 | float yCtr = ty * ha + yACtr; 209 | float xCtr = tx * wa + xACtr; 210 | 211 | float yMin = yCtr - h / 2.0f; 212 | float xMin = xCtr - w / 2.0f; 213 | float yMax = yCtr + h / 2.0f; 214 | float xMax = xCtr + w / 2.0f; 215 | 216 | BBox bbox = new BBox(xMin, xMax - xMin, yMin, yMax - yMin); 217 | predictions.add(new Prediction(label, score, bbox)); 218 | } 219 | } 220 | } 221 | 222 | return predictions; 223 | } 224 | 225 | private List nms(List predictions) { 226 | Collections.sort(predictions); 227 | 228 | List selected = new ArrayList<>(); 229 | 230 | for (Prediction predictionA : predictions) { 231 | 232 | boolean shouldSelect = true; 233 | BBox boxA = predictionA.bbox; 234 | 235 | // Does the current box overlap one of the selected boxes more than the 236 | // given threshold amount? Then it's too similar, so don't keep it. 237 | for (Prediction predictionB : selected) { 238 | BBox boxB = predictionB.bbox; 239 | if (IOU(boxA, boxB) > IOU_THRESHOLD) { 240 | shouldSelect = false; 241 | break; 242 | } 243 | } 244 | 245 | // This bounding box did not overlap too much with any previously selected 246 | // bounding box, so we'll keep it. 247 | if (shouldSelect) { 248 | selected.add(predictionA); 249 | } 250 | } 251 | 252 | return selected; 253 | } 254 | 255 | private float IOU(BBox a, BBox b ) { 256 | float areaA = a.width * a.height; 257 | if (areaA <= 0) { 258 | return 0; 259 | } 260 | 261 | float areaB = b.width * b.height; 262 | if (areaB <= 0) { 263 | return 0; 264 | } 265 | 266 | float intersectionMinX = Math.max(a.x, b.x); 267 | float intersectionMinY = Math.max(a.y, b.y); 268 | float intersectionMaxX = Math.min(a.width + a.x, b.width + b.x); 269 | float intersectionMaxY = Math.min(a.height + a.y, b.height + a.y); 270 | float intersectionArea = Math.max(intersectionMaxY - intersectionMinY, 0) * Math.max(intersectionMaxX - intersectionMinX, 0); 271 | return intersectionArea / (areaA + areaB - intersectionArea); 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-annotations/object-detection-android/565d65dbbd966281fc42a48f33a25c0ed354da03/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-annotations/object-detection-android/565d65dbbd966281fc42a48f33a25c0ed354da03/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-annotations/object-detection-android/565d65dbbd966281fc42a48f33a25c0ed354da03/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-annotations/object-detection-android/565d65dbbd966281fc42a48f33a25c0ed354da03/app/src/main/res/drawable-xxhdpi/cancel.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/capture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-annotations/object-detection-android/565d65dbbd966281fc42a48f33a25c0ed354da03/app/src/main/res/drawable-xxhdpi/capture.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/focus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-annotations/object-detection-android/565d65dbbd966281fc42a48f33a25c0ed354da03/app/src/main/res/drawable-xxhdpi/focus.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-annotations/object-detection-android/565d65dbbd966281fc42a48f33a25c0ed354da03/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloud-annotations/object-detection-android/565d65dbbd966281fc42a48f33a25c0ed354da03/app/src/main/res/drawable-xxhdpi/image.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/green_progress.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/handle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/red_progress.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/yellow_progress.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_camera.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/layout/camera.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_custom_camera.xml: -------------------------------------------------------------------------------- 1 | 16 | 20 | 21 | 25 | 26 |