summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorerdgeist <erdgeist@erdgeist.org>2026-04-24 16:42:18 +0200
committererdgeist <erdgeist@erdgeist.org>2026-04-24 16:42:18 +0200
commitb4695e613f0bca451485f95572f23d464b56a95e (patch)
treef893b2035458b47a9b8bbdb3a78205511d2ce3c5
Initial importmain
-rw-r--r--.gitignore60
-rw-r--r--README.md10
-rw-r--r--build.gradle64
-rw-r--r--gradle.properties21
-rw-r--r--gradle/wrapper/gradle-wrapper.jarbin0 -> 54329 bytes
-rw-r--r--gradle/wrapper/gradle-wrapper.properties6
-rwxr-xr-xgradlew172
-rw-r--r--gradlew.bat84
-rw-r--r--settings.gradle1
-rw-r--r--tflite/.gitignore1
-rw-r--r--tflite/build.gradle75
-rw-r--r--tflite/src/main/AndroidManifest.xml51
-rw-r--r--tflite/src/main/ic_launcher-playstore.pngbin0 -> 1096 bytes
-rw-r--r--tflite/src/main/java/com/example/android/camerax/tflite/CameraActivity.kt281
-rw-r--r--tflite/src/main/res/drawable/ic_launcher_background.xml89
-rw-r--r--tflite/src/main/res/drawable/ic_shutter.xml21
-rw-r--r--tflite/src/main/res/drawable/ic_shutter_focused.xml28
-rw-r--r--tflite/src/main/res/drawable/ic_shutter_normal.xml28
-rw-r--r--tflite/src/main/res/drawable/ic_shutter_pressed.xml28
-rw-r--r--tflite/src/main/res/drawable/shape_rectangle.xml28
-rw-r--r--tflite/src/main/res/layout-land/activity_camera.xml72
-rw-r--r--tflite/src/main/res/layout/activity_camera.xml72
-rw-r--r--tflite/src/main/res/mipmap-anydpi-v26/ic_launcher.xml20
-rw-r--r--tflite/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml20
-rw-r--r--tflite/src/main/res/mipmap-hdpi/ic_launcher.pngbin0 -> 1869 bytes
-rw-r--r--tflite/src/main/res/mipmap-hdpi/ic_launcher_foreground.pngbin0 -> 2708 bytes
-rw-r--r--tflite/src/main/res/mipmap-hdpi/ic_launcher_round.pngbin0 -> 3816 bytes
-rw-r--r--tflite/src/main/res/mipmap-mdpi/ic_launcher.pngbin0 -> 1312 bytes
-rw-r--r--tflite/src/main/res/mipmap-mdpi/ic_launcher_foreground.pngbin0 -> 1630 bytes
-rw-r--r--tflite/src/main/res/mipmap-mdpi/ic_launcher_round.pngbin0 -> 2370 bytes
-rw-r--r--tflite/src/main/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 2578 bytes
-rw-r--r--tflite/src/main/res/mipmap-xhdpi/ic_launcher_foreground.pngbin0 -> 3962 bytes
-rw-r--r--tflite/src/main/res/mipmap-xhdpi/ic_launcher_round.pngbin0 -> 5401 bytes
-rw-r--r--tflite/src/main/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 4132 bytes
-rw-r--r--tflite/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.pngbin0 -> 7404 bytes
-rw-r--r--tflite/src/main/res/mipmap-xxhdpi/ic_launcher_round.pngbin0 -> 8712 bytes
-rw-r--r--tflite/src/main/res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 5897 bytes
-rw-r--r--tflite/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.pngbin0 -> 11849 bytes
-rw-r--r--tflite/src/main/res/mipmap-xxxhdpi/ic_launcher_round.pngbin0 -> 12878 bytes
-rw-r--r--tflite/src/main/res/values/dimens.xml33
-rw-r--r--tflite/src/main/res/values/strings.xml21
-rw-r--r--tflite/src/main/res/values/styles.xml27
-rw-r--r--utils/.gitignore2
-rw-r--r--utils/README.md2
-rw-r--r--utils/build.gradle75
-rw-r--r--utils/src/main/AndroidManifest.xml17
-rw-r--r--utils/src/main/java/com/example/android/camera/utils/AutoFitSurfaceView.kt79
-rw-r--r--utils/src/main/java/com/example/android/camera/utils/CameraSizes.kt79
-rw-r--r--utils/src/main/java/com/example/android/camera/utils/ExifUtils.kt73
-rw-r--r--utils/src/main/java/com/example/android/camera/utils/GenericListAdapter.kt55
-rw-r--r--utils/src/main/java/com/example/android/camera/utils/OrientationLiveData.kt95
-rw-r--r--utils/src/main/java/com/example/android/camera/utils/Yuv.kt191
-rw-r--r--utils/src/main/java/com/example/android/camera/utils/YuvToRgbConverter.kt99
-rw-r--r--utils/src/main/res/drawable/ic_shutter.xml21
-rw-r--r--utils/src/main/res/drawable/ic_shutter_focused.xml28
-rw-r--r--utils/src/main/res/drawable/ic_shutter_normal.xml28
-rw-r--r--utils/src/main/res/drawable/ic_shutter_pressed.xml28
57 files changed, 2185 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..24bddc8
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,60 @@
1# Built application files
2app/release
3*.apk
4*.ap_
5
6# Files for the ART/Dalvik VM
7*.dex
8
9# Java class files
10*.class
11
12# Generated files
13bin/
14gen/
15out/
16
17# Gradle files
18.gradle/
19build/
20
21# Local configuration file (sdk path, etc)
22local.properties
23
24# Proguard folder generated by Eclipse
25proguard/
26
27# Log Files
28*.log
29
30# Android Studio Navigation editor temp files
31.navigation/
32
33# Android Studio captures folder
34captures/
35
36# IntelliJ
37*.iml
38.idea/*
39!.idea/runConfigurations
40
41# Keystore files
42*.jks
43
44# External native build folder generated in Android Studio 2.2 and later
45.externalNativeBuild
46
47# Google Services (e.g. APIs or Firebase)
48google-services.json
49
50# Freeline
51freeline.py
52freeline/
53freeline_project_description.json
54
55# fastlane
56fastlane/report.xml
57fastlane/Preview.html
58fastlane/screenshots
59fastlane/test_output
60fastlane/readme.md
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0e90f33
--- /dev/null
+++ b/README.md
@@ -0,0 +1,10 @@
1# CCCB Display Camera Control Tool
2
3
4Main logic is in
5
6`CCCB_Camera_Andoroid/tflite/src/main/java/com/example/android/camerax/tflite`
7
8the rest is boilerplate from
9
10https://github.com/android/camera-samples
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..30ce363
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,64 @@
1/*
2 * Copyright 2020 Google LLC
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 * https://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
17buildscript {
18
19 ext {
20 // Top-level variables used for versioning
21 ext.kotlin_version = '1.5.21'
22 ext.java_version = JavaVersion.VERSION_1_8
23 }
24
25 repositories {
26 google()
27 mavenCentral()
28 }
29
30 dependencies {
31 classpath 'com.android.tools.build:gradle:7.4.2'
32 classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
33 classpath 'com.diffplug.spotless:spotless-plugin-gradle:5.11.1'
34 }
35}
36
37allprojects {
38 repositories {
39 google()
40 mavenCentral()
41 maven { // repo for TFLite snapshot
42 name 'ossrh-snapshot'
43 url 'https://s01.oss.sonatype.org/content/repositories/snapshots'
44 }
45 }
46}
47
48subprojects {
49 apply plugin: 'com.diffplug.spotless'
50 spotless {
51 java {
52 target "**/*.java"
53 trimTrailingWhitespace()
54 removeUnusedImports()
55 googleJavaFormat()
56 endWithNewline()
57 }
58 kotlin {
59 target "**/*.kt"
60 trimTrailingWhitespace()
61 endWithNewline()
62 }
63 }
64}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..23339e0
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,21 @@
1# Project-wide Gradle settings.
2# IDE (e.g. Android Studio) users:
3# Gradle settings configured through the IDE *will override*
4# any settings specified in this file.
5# For more details on how to configure your build environment visit
6# http://www.gradle.org/docs/current/userguide/build_environment.html
7# Specifies the JVM arguments used for the daemon process.
8# The setting is particularly useful for tweaking memory settings.
9org.gradle.jvmargs=-Xmx1536m
10# When configured, Gradle will run in incubating parallel mode.
11# This option should only be used with decoupled projects. More details, visit
12# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13# org.gradle.parallel=true
14# AndroidX package structure to make it clearer which packages are bundled with the
15# Android operating system, and which are packaged with your app's APK
16# https://developer.android.com/topic/libraries/support-library/androidx-rn
17android.useAndroidX=true
18# Automatically convert third-party libraries to use AndroidX
19android.enableJetifier=true
20# Kotlin code style for this project: "official" or "obsolete":
21kotlin.code.style=official
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f6b961f
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..38c6ed2
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
1#Wed Dec 16 12:00:46 WET 2020
2distributionBase=GRADLE_USER_HOME
3distributionPath=wrapper/dists
4zipStoreBase=GRADLE_USER_HOME
5zipStorePath=wrapper/dists
6distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
1#!/usr/bin/env sh
2
3##############################################################################
4##
5## Gradle start up script for UN*X
6##
7##############################################################################
8
9# Attempt to set APP_HOME
10# Resolve links: $0 may be a link
11PRG="$0"
12# Need this for relative symlinks.
13while [ -h "$PRG" ] ; do
14 ls=`ls -ld "$PRG"`
15 link=`expr "$ls" : '.*-> \(.*\)$'`
16 if expr "$link" : '/.*' > /dev/null; then
17 PRG="$link"
18 else
19 PRG=`dirname "$PRG"`"/$link"
20 fi
21done
22SAVED="`pwd`"
23cd "`dirname \"$PRG\"`/" >/dev/null
24APP_HOME="`pwd -P`"
25cd "$SAVED" >/dev/null
26
27APP_NAME="Gradle"
28APP_BASE_NAME=`basename "$0"`
29
30# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31DEFAULT_JVM_OPTS=""
32
33# Use the maximum available, or set MAX_FD != -1 to use that value.
34MAX_FD="maximum"
35
36warn () {
37 echo "$*"
38}
39
40die () {
41 echo
42 echo "$*"
43 echo
44 exit 1
45}
46
47# OS specific support (must be 'true' or 'false').
48cygwin=false
49msys=false
50darwin=false
51nonstop=false
52case "`uname`" in
53 CYGWIN* )
54 cygwin=true
55 ;;
56 Darwin* )
57 darwin=true
58 ;;
59 MINGW* )
60 msys=true
61 ;;
62 NONSTOP* )
63 nonstop=true
64 ;;
65esac
66
67CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68
69# Determine the Java command to use to start the JVM.
70if [ -n "$JAVA_HOME" ] ; then
71 if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 # IBM's JDK on AIX uses strange locations for the executables
73 JAVACMD="$JAVA_HOME/jre/sh/java"
74 else
75 JAVACMD="$JAVA_HOME/bin/java"
76 fi
77 if [ ! -x "$JAVACMD" ] ; then
78 die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79
80Please set the JAVA_HOME variable in your environment to match the
81location of your Java installation."
82 fi
83else
84 JAVACMD="java"
85 which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86
87Please set the JAVA_HOME variable in your environment to match the
88location of your Java installation."
89fi
90
91# Increase the maximum file descriptors if we can.
92if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 MAX_FD_LIMIT=`ulimit -H -n`
94 if [ $? -eq 0 ] ; then
95 if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 MAX_FD="$MAX_FD_LIMIT"
97 fi
98 ulimit -n $MAX_FD
99 if [ $? -ne 0 ] ; then
100 warn "Could not set maximum file descriptor limit: $MAX_FD"
101 fi
102 else
103 warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 fi
105fi
106
107# For Darwin, add options to specify how the application appears in the dock
108if $darwin; then
109 GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110fi
111
112# For Cygwin, switch paths to Windows format before running java
113if $cygwin ; then
114 APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 JAVACMD=`cygpath --unix "$JAVACMD"`
117
118 # We build the pattern for arguments to be converted via cygpath
119 ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 SEP=""
121 for dir in $ROOTDIRSRAW ; do
122 ROOTDIRS="$ROOTDIRS$SEP$dir"
123 SEP="|"
124 done
125 OURCYGPATTERN="(^($ROOTDIRS))"
126 # Add a user-defined pattern to the cygpath arguments
127 if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 fi
130 # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 i=0
132 for arg in "$@" ; do
133 CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135
136 if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 else
139 eval `echo args$i`="\"$arg\""
140 fi
141 i=$((i+1))
142 done
143 case $i in
144 (0) set -- ;;
145 (1) set -- "$args0" ;;
146 (2) set -- "$args0" "$args1" ;;
147 (3) set -- "$args0" "$args1" "$args2" ;;
148 (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 esac
155fi
156
157# Escape application args
158save () {
159 for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 echo " "
161}
162APP_ARGS=$(save "$@")
163
164# Collect all arguments for the java command, following the shell quoting and substitution rules
165eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166
167# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 cd "$(dirname "$0")"
170fi
171
172exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..f955316
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
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
9if "%OS%"=="Windows_NT" setlocal
10
11set DIRNAME=%~dp0
12if "%DIRNAME%" == "" set DIRNAME=.
13set APP_BASE_NAME=%~n0
14set APP_HOME=%DIRNAME%
15
16@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17set DEFAULT_JVM_OPTS=
18
19@rem Find java.exe
20if defined JAVA_HOME goto findJavaFromJavaHome
21
22set JAVA_EXE=java.exe
23%JAVA_EXE% -version >NUL 2>&1
24if "%ERRORLEVEL%" == "0" goto init
25
26echo.
27echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28echo.
29echo Please set the JAVA_HOME variable in your environment to match the
30echo location of your Java installation.
31
32goto fail
33
34:findJavaFromJavaHome
35set JAVA_HOME=%JAVA_HOME:"=%
36set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37
38if exist "%JAVA_EXE%" goto init
39
40echo.
41echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42echo.
43echo Please set the JAVA_HOME variable in your environment to match the
44echo location of your Java installation.
45
46goto fail
47
48:init
49@rem Get command-line arguments, handling Windows variants
50
51if not "%OS%" == "Windows_NT" goto win9xME_args
52
53:win9xME_args
54@rem Slurp the command line arguments.
55set CMD_LINE_ARGS=
56set _SKIP=2
57
58:win9xME_args_slurp
59if "x%~1" == "x" goto execute
60
61set CMD_LINE_ARGS=%*
62
63:execute
64@rem Setup the command line
65
66set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67
68@rem Execute Gradle
69"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70
71:end
72@rem End local scope for the variables with windows NT shell
73if "%ERRORLEVEL%"=="0" goto mainEnd
74
75:fail
76rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77rem the _cmd.exe /c_ return code!
78if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79exit /b 1
80
81:mainEnd
82if "%OS%"=="Windows_NT" endlocal
83
84:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..844db50
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
include 'tflite'
diff --git a/tflite/.gitignore b/tflite/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/tflite/.gitignore
@@ -0,0 +1 @@
/build
diff --git a/tflite/build.gradle b/tflite/build.gradle
new file mode 100644
index 0000000..8e9fcf4
--- /dev/null
+++ b/tflite/build.gradle
@@ -0,0 +1,75 @@
1/*
2 * Copyright 2020 Google LLC
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 * https://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
17apply plugin: "com.android.application"
18apply plugin: "kotlin-android"
19
20android {
21 compileSdkVersion 31
22 ndkVersion "21.3.6528147"
23
24 defaultConfig {
25 applicationId 'com.android.example.camerax.tflite'
26 minSdkVersion 21
27 targetSdkVersion 30
28 versionCode 1
29 versionName "0.0.1"
30 }
31
32 compileOptions {
33 sourceCompatibility rootProject.ext.java_version
34 targetCompatibility rootProject.ext.java_version
35 }
36
37 kotlinOptions {
38 jvmTarget = rootProject.ext.java_version
39 }
40
41
42 buildFeatures {
43 viewBinding true
44 }
45 androidResources {
46 noCompress 'lite'
47 }
48 namespace 'com.android.example.camerax.tflite'
49}
50
51dependencies {
52 implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
53
54 // App compat and UI things
55 implementation 'androidx.appcompat:appcompat:1.3.1'
56 implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
57 implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
58
59 // CameraX
60 def camerax_version = "1.1.0-beta01"
61 implementation "androidx.camera:camera-core:${camerax_version}"
62 implementation "androidx.camera:camera-camera2:${camerax_version}"
63 implementation "androidx.camera:camera-lifecycle:${camerax_version}"
64 implementation "androidx.camera:camera-view:${camerax_version}"
65 implementation "androidx.camera:camera-video:${camerax_version}"
66
67
68 // Tensorflow lite dependencies
69 implementation 'org.tensorflow:tensorflow-lite:2.9.0'
70 implementation 'org.tensorflow:tensorflow-lite-gpu:2.9.0'
71 implementation 'org.tensorflow:tensorflow-lite-support:0.4.2'
72
73 testImplementation 'org.robolectric:robolectric:4.4'
74 testImplementation 'junit:junit:4.13.2'
75}
diff --git a/tflite/src/main/AndroidManifest.xml b/tflite/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d38c505
--- /dev/null
+++ b/tflite/src/main/AndroidManifest.xml
@@ -0,0 +1,51 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--
3 ~ Copyright 2020 Google LLC
4 ~
5 ~ Licensed under the Apache License, Version 2.0 (the "License");
6 ~ you may not use this file except in compliance with the License.
7 ~ You may obtain a copy of the License at
8 ~
9 ~ https://www.apache.org/licenses/LICENSE-2.0
10 ~
11 ~ Unless required by applicable law or agreed to in writing, software
12 ~ distributed under the License is distributed on an "AS IS" BASIS,
13 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ~ See the License for the specific language governing permissions and
15 ~ limitations under the License.
16 -->
17<manifest xmlns:android="http://schemas.android.com/apk/res/android"
18 xmlns:tools="http://schemas.android.com/tools">
19
20 <!-- Declare features -->
21 <uses-feature android:name="android.hardware.camera" />
22
23 <!-- Declare permissions -->
24 <uses-permission android:name="android.permission.CAMERA" />
25 <uses-permission android:name="android.permission.INTERNET" />
26
27 <application
28 android:allowBackup="true"
29 android:icon="@mipmap/ic_launcher"
30 android:label="@string/app_name"
31 android:roundIcon="@mipmap/ic_launcher_round"
32 android:supportsRtl="true"
33 android:theme="@style/AppTheme"
34 android:screenOrientation="landscape"
35
36 tools:ignore="AllowBackup,GoogleAppIndexingWarning">
37
38 <activity
39 android:name="com.example.android.camerax.tflite.CameraActivity"
40 android:rotationAnimation="seamless"
41 android:screenOrientation="landscape"
42 tools:targetApi="O">
43 <intent-filter>
44 <action android:name="android.intent.action.MAIN"/>
45 <category android:name="android.intent.category.LAUNCHER"/>
46 </intent-filter>
47 </activity>
48
49 </application>
50
51</manifest>
diff --git a/tflite/src/main/ic_launcher-playstore.png b/tflite/src/main/ic_launcher-playstore.png
new file mode 100644
index 0000000..dd70d80
--- /dev/null
+++ b/tflite/src/main/ic_launcher-playstore.png
Binary files differ
diff --git a/tflite/src/main/java/com/example/android/camerax/tflite/CameraActivity.kt b/tflite/src/main/java/com/example/android/camerax/tflite/CameraActivity.kt
new file mode 100644
index 0000000..cde9778
--- /dev/null
+++ b/tflite/src/main/java/com/example/android/camerax/tflite/CameraActivity.kt
@@ -0,0 +1,281 @@
1/*
2 * Copyright 2020 Google LLC
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 * https://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
17package com.example.android.camerax.tflite
18
19import android.Manifest
20import android.annotation.SuppressLint
21import android.content.Context
22import android.content.pm.PackageManager
23import android.graphics.Bitmap
24import android.graphics.Color
25import android.graphics.Matrix
26import android.graphics.RectF
27import android.os.Bundle
28import android.util.Log
29import android.util.Size
30import android.view.View
31import android.view.ViewGroup
32import androidx.appcompat.app.AppCompatActivity
33import androidx.camera.core.AspectRatio
34import androidx.camera.core.CameraSelector
35import androidx.camera.core.ImageAnalysis
36import androidx.camera.core.Preview
37import androidx.camera.lifecycle.ProcessCameraProvider
38import androidx.core.app.ActivityCompat
39import androidx.core.content.ContextCompat
40import androidx.lifecycle.LifecycleOwner
41import com.android.example.camerax.tflite.databinding.ActivityCameraBinding
42import java.net.DatagramPacket
43import java.net.DatagramSocket
44import java.net.InetSocketAddress
45import java.util.concurrent.Executors
46import java.util.concurrent.TimeUnit
47import kotlin.random.Random
48
49
50/** Activity that displays the camera and performs object detection on the incoming frames */
51class CameraActivity : AppCompatActivity() {
52
53 private lateinit var activityCameraBinding: ActivityCameraBinding
54
55 private lateinit var bitmapBuffer: Bitmap
56
57 private val executor = Executors.newSingleThreadExecutor()
58 private val permissions = listOf(Manifest.permission.CAMERA)
59 private val permissionsRequestCode = Random.nextInt(0, 10000)
60
61 private var lensFacing: Int = CameraSelector.LENS_FACING_BACK
62 private val isFrontFacing get() = lensFacing == CameraSelector.LENS_FACING_FRONT
63
64 private var pauseAnalysis = false
65 private var imageRotationDegrees: Int = 0
66 private var socket: DatagramSocket = DatagramSocket()
67
68 override fun onCreate(savedInstanceState: Bundle?) {
69 super.onCreate(savedInstanceState)
70 activityCameraBinding = ActivityCameraBinding.inflate(layoutInflater)
71 setContentView(activityCameraBinding.root)
72 }
73
74 override fun onDestroy() {
75
76 // Terminate all outstanding analyzing jobs (if there is any).
77 executor.apply {
78 shutdown()
79 awaitTermination(1000, TimeUnit.MILLISECONDS)
80 }
81 super.onDestroy()
82 }
83
84 /** Declare and bind preview and analysis use cases */
85 @SuppressLint("UnsafeExperimentalUsageError")
86 private fun bindCameraUseCases() = activityCameraBinding.viewFinder.post {
87
88 val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
89 cameraProviderFuture.addListener ({
90
91 // Camera provider is now guaranteed to be available
92 val cameraProvider = cameraProviderFuture.get()
93
94 // Set up the view finder use case to display camera preview
95 val preview = Preview.Builder()
96 .setTargetAspectRatio(AspectRatio.RATIO_4_3)
97 .setTargetRotation(activityCameraBinding.viewFinder.display.rotation)
98 .build()
99
100 // Set up the image analysis use case which will process frames in real time
101 val imageAnalysis = ImageAnalysis.Builder()
102 .setTargetResolution(Size(448, 236))
103 .setTargetRotation(activityCameraBinding.viewFinder.display.rotation)
104 .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
105 .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
106 .build()
107
108 var frameCounter = 0
109 var lastFpsTimestamp = System.currentTimeMillis()
110
111 imageAnalysis.setAnalyzer(executor, ImageAnalysis.Analyzer { image ->
112 if (!::bitmapBuffer.isInitialized) {
113 // The image rotation and RGB image buffer are initialized only once
114 // the analyzer has started running
115 imageRotationDegrees = image.imageInfo.rotationDegrees
116 bitmapBuffer = Bitmap.createBitmap(
117 image.width, image.height, Bitmap.Config.ARGB_8888)
118 }
119
120 // Early exit: image analysis is in paused state
121 if (pauseAnalysis) {
122 image.close()
123 return@Analyzer
124 }
125
126 // Copy out RGB bits to our shared buffer
127 image.use { bitmapBuffer.copyPixelsFromBuffer(image.planes[0].buffer) }
128
129// val CAM_WIDTH = 858
130// val CAM_HEIGHT = 480
131 val CAM_WIDTH = 480
132 val CAM_HEIGHT = 360
133
134 val DISPLAY_WIDTH = 448
135 val DISPLAY_HEIGHT = 160
136 val DISPLAY_VHEIGHT = 236
137
138 val scaledBitmap = Bitmap.createScaledBitmap(bitmapBuffer, DISPLAY_WIDTH, DISPLAY_VHEIGHT, true)
139
140 val scratch = IntArray((2 + DISPLAY_VHEIGHT) * DISPLAY_WIDTH)
141 val output = UByteArray(10 + DISPLAY_HEIGHT * DISPLAY_WIDTH / 8)
142 output[1] = 0x12u
143 output[4] = 0x23u
144 var offset = 10
145/*
146 for ( row in 0..DISPLAY_VHEIGHT - 1) {
147 val zeile = (row * bitmapBuffer.height ) / DISPLAY_VHEIGHT
148 for (column in 0..DISPLAY_WIDTH - 1) {
149 val spalte = (column * bitmapBuffer.width ) / DISPLAY_WIDTH
150 val pixel = bitmapBuffer.getPixel(spalte, zeile)
151 scratch[row * DISPLAY_WIDTH + column] = Color.red(pixel) * 19535 + Color.green(pixel) * 38470 + Color.blue(pixel) * 7448
152 }
153 }
154*/
155 scaledBitmap.getPixels(scratch, 0, DISPLAY_WIDTH, 0, 0, DISPLAY_WIDTH, DISPLAY_VHEIGHT)
156 for (off in 0..DISPLAY_VHEIGHT * DISPLAY_WIDTH)
157 scratch[off] = Color.red(scratch[off]) * 19535 + Color.green(scratch[off]) * 38470 + Color.blue(scratch[off]) * 7448
158
159 var acc = 0
160 var accv = 0
161
162 for ( row in 0.. DISPLAY_VHEIGHT - 1)
163 for ( column in 0..DISPLAY_WIDTH - 1) {
164 val pixel = scratch[row * DISPLAY_WIDTH + column]
165 val bwpixel = if (pixel < 0x810000) 0 else 0xFFFFFF
166
167 if (row % 12 < 8) {
168 acc = (acc shl 1) + (bwpixel shr 23)
169 if (++accv == 8) {
170 output[offset++] = acc.toUByte()
171 acc = 0
172 accv = 0
173 }
174 }
175
176 val err = (pixel - bwpixel) / 42
177 fun AddSatShift(xoff : Int, yoff: Int, shift : Int) {
178 val pixelold = scratch[(row + yoff) * DISPLAY_WIDTH + column + xoff ]
179 var r = pixelold + (err shl (16 - shift))
180 if ( r < 0 ) r = 0
181 if ( r > 0xFFFFFF) r = 0xFFFFFF
182 scratch[(row + yoff) * DISPLAY_WIDTH + column + xoff ] = r
183 }
184
185 AddSatShift(0, 1, 13)
186 AddSatShift(0, 2, 14)
187 if (column > 0) {
188 AddSatShift(-1, 1, 14)
189 AddSatShift(-1, 2, 15)
190 }
191
192 if (column > 1) {
193 AddSatShift(-2, 1, 15)
194 AddSatShift(-2, 2, 16)
195 }
196
197 if (column < DISPLAY_WIDTH - 1) {
198 AddSatShift( 1, 0, 13)
199 AddSatShift( 1, 1, 14)
200 AddSatShift( 1, 2, 15)
201 }
202
203 if (column < DISPLAY_WIDTH - 2) {
204 AddSatShift( 2, 0, 14)
205 AddSatShift( 2, 1, 15)
206 AddSatShift( 2, 2, 16)
207 }
208 }
209 val address = InetSocketAddress("172.23.42.29", 2342 )
210// val address = InetSocketAddress("192.168.178.69", 2342 )
211 try {
212 socket.send(DatagramPacket(output.toByteArray(), offset, address))
213 } catch (e: Exception) {
214 // Ignore network exceptions
215 }
216
217 // Compute the FPS of the entire pipeline
218 val frameCount = 10
219 if (++frameCounter % frameCount == 0) {
220 frameCounter = 0
221 val now = System.currentTimeMillis()
222 val delta = now - lastFpsTimestamp
223 val fps = 1000 * frameCount.toFloat() / delta
224// Log.d(TAG, "FPS: ${"%.02f".format(fps)} " + bitmapBuffer.width + " x " + bitmapBuffer.height)
225
226 activityCameraBinding.viewFinder.post {
227 activityCameraBinding.textPrediction.text = "FPS: ${"%.02f".format(fps)}"
228 }
229
230 lastFpsTimestamp = now
231 }
232 })
233
234 // Create a new camera selector each time, enforcing lens facing
235 val cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
236
237 // Apply declared configs to CameraX using the same lifecycle owner
238 cameraProvider.unbindAll()
239 cameraProvider.bindToLifecycle(
240 this as LifecycleOwner, cameraSelector, preview, imageAnalysis)
241
242 // Use the camera object to link our preview use case with the view
243 preview.setSurfaceProvider(activityCameraBinding.viewFinder.surfaceProvider)
244
245 }, ContextCompat.getMainExecutor(this))
246 }
247
248 override fun onResume() {
249 super.onResume()
250
251 // Request permissions each time the app resumes, since they can be revoked at any time
252 if (!hasPermissions(this)) {
253 ActivityCompat.requestPermissions(
254 this, permissions.toTypedArray(), permissionsRequestCode)
255 } else {
256 bindCameraUseCases()
257 }
258 }
259
260 override fun onRequestPermissionsResult(
261 requestCode: Int,
262 permissions: Array<out String>,
263 grantResults: IntArray
264 ) {
265 super.onRequestPermissionsResult(requestCode, permissions, grantResults)
266 if (requestCode == permissionsRequestCode && hasPermissions(this)) {
267 bindCameraUseCases()
268 } else {
269 finish() // If we don't have the required permissions, we can't run
270 }
271 }
272
273 /** Convenience method used to check if all permissions required by this app are granted */
274 private fun hasPermissions(context: Context) = permissions.all {
275 ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
276 }
277
278 companion object {
279 private val TAG = CameraActivity::class.java.simpleName
280 }
281}
diff --git a/tflite/src/main/res/drawable/ic_launcher_background.xml b/tflite/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..268681b
--- /dev/null
+++ b/tflite/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,89 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--
3 ~ Copyright 2020 Google LLC
4 ~
5 ~ Licensed under the Apache License, Version 2.0 (the "License");
6 ~ you may not use this file except in compliance with the License.
7 ~ You may obtain a copy of the License at
8 ~
9 ~ https://www.apache.org/licenses/LICENSE-2.0
10 ~
11 ~ Unless required by applicable law or agreed to in writing, software
12 ~ distributed under the License is distributed on an "AS IS" BASIS,
13 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ~ See the License for the specific language governing permissions and
15 ~ limitations under the License.
16 -->
17<vector
18 xmlns:android="http://schemas.android.com/apk/res/android"
19 android:height="108dp"
20 android:width="108dp"
21 android:viewportHeight="108"
22 android:viewportWidth="108">
23 <path android:fillColor="#008577"
24 android:pathData="M0,0h108v108h-108z"/>
25 <path android:fillColor="#00000000" android:pathData="M9,0L9,108"
26 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
27 <path android:fillColor="#00000000" android:pathData="M19,0L19,108"
28 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
29 <path android:fillColor="#00000000" android:pathData="M29,0L29,108"
30 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
31 <path android:fillColor="#00000000" android:pathData="M39,0L39,108"
32 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
33 <path android:fillColor="#00000000" android:pathData="M49,0L49,108"
34 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
35 <path android:fillColor="#00000000" android:pathData="M59,0L59,108"
36 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
37 <path android:fillColor="#00000000" android:pathData="M69,0L69,108"
38 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
39 <path android:fillColor="#00000000" android:pathData="M79,0L79,108"
40 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
41 <path android:fillColor="#00000000" android:pathData="M89,0L89,108"
42 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
43 <path android:fillColor="#00000000" android:pathData="M99,0L99,108"
44 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
45 <path android:fillColor="#00000000" android:pathData="M0,9L108,9"
46 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
47 <path android:fillColor="#00000000" android:pathData="M0,19L108,19"
48 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
49 <path android:fillColor="#00000000" android:pathData="M0,29L108,29"
50 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
51 <path android:fillColor="#00000000" android:pathData="M0,39L108,39"
52 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
53 <path android:fillColor="#00000000" android:pathData="M0,49L108,49"
54 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
55 <path android:fillColor="#00000000" android:pathData="M0,59L108,59"
56 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
57 <path android:fillColor="#00000000" android:pathData="M0,69L108,69"
58 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
59 <path android:fillColor="#00000000" android:pathData="M0,79L108,79"
60 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
61 <path android:fillColor="#00000000" android:pathData="M0,89L108,89"
62 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
63 <path android:fillColor="#00000000" android:pathData="M0,99L108,99"
64 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
65 <path android:fillColor="#00000000" android:pathData="M19,29L89,29"
66 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
67 <path android:fillColor="#00000000" android:pathData="M19,39L89,39"
68 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
69 <path android:fillColor="#00000000" android:pathData="M19,49L89,49"
70 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
71 <path android:fillColor="#00000000" android:pathData="M19,59L89,59"
72 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
73 <path android:fillColor="#00000000" android:pathData="M19,69L89,69"
74 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
75 <path android:fillColor="#00000000" android:pathData="M19,79L89,79"
76 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
77 <path android:fillColor="#00000000" android:pathData="M29,19L29,89"
78 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
79 <path android:fillColor="#00000000" android:pathData="M39,19L39,89"
80 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
81 <path android:fillColor="#00000000" android:pathData="M49,19L49,89"
82 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
83 <path android:fillColor="#00000000" android:pathData="M59,19L59,89"
84 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
85 <path android:fillColor="#00000000" android:pathData="M69,19L69,89"
86 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
87 <path android:fillColor="#00000000" android:pathData="M79,19L79,89"
88 android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
89</vector>
diff --git a/tflite/src/main/res/drawable/ic_shutter.xml b/tflite/src/main/res/drawable/ic_shutter.xml
new file mode 100644
index 0000000..ab352ba
--- /dev/null
+++ b/tflite/src/main/res/drawable/ic_shutter.xml
@@ -0,0 +1,21 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--
3 ~ Copyright 2019 Google LLC
4 ~
5 ~ Licensed under the Apache License, Version 2.0 (the "License");
6 ~ you may not use this file except in compliance with the License.
7 ~ You may obtain a copy of the License at
8 ~
9 ~ https://www.apache.org/licenses/LICENSE-2.0
10 ~
11 ~ Unless required by applicable law or agreed to in writing, software
12 ~ distributed under the License is distributed on an "AS IS" BASIS,
13 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ~ See the License for the specific language governing permissions and
15 ~ limitations under the License.
16 -->
17<selector xmlns:android="http://schemas.android.com/apk/res/android">
18 <item android:state_pressed="true" android:drawable="@drawable/ic_shutter_pressed" />
19 <item android:state_focused="true" android:drawable="@drawable/ic_shutter_focused" />
20 <item android:drawable="@drawable/ic_shutter_normal" />
21</selector> \ No newline at end of file
diff --git a/tflite/src/main/res/drawable/ic_shutter_focused.xml b/tflite/src/main/res/drawable/ic_shutter_focused.xml
new file mode 100644
index 0000000..fa852ac
--- /dev/null
+++ b/tflite/src/main/res/drawable/ic_shutter_focused.xml
@@ -0,0 +1,28 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--
3 ~ Copyright 2019 Google LLC
4 ~
5 ~ Licensed under the Apache License, Version 2.0 (the "License");
6 ~ you may not use this file except in compliance with the License.
7 ~ You may obtain a copy of the License at
8 ~
9 ~ https://www.apache.org/licenses/LICENSE-2.0
10 ~
11 ~ Unless required by applicable law or agreed to in writing, software
12 ~ distributed under the License is distributed on an "AS IS" BASIS,
13 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ~ See the License for the specific language governing permissions and
15 ~ limitations under the License.
16 -->
17<vector xmlns:android="http://schemas.android.com/apk/res/android"
18 android:width="24dp"
19 android:height="24dp"
20 android:viewportWidth="74"
21 android:viewportHeight="74">
22 <path android:fillColor="#FFFFFF" android:fillType="evenOdd"
23 android:pathData="M73.1,37C73.1,17.0637 56.9373,0.9 37,0.9C17.0627,0.9 0.9,17.0637 0.9,37C0.9,56.9373 17.0627,73.1 37,73.1C56.9373,73.1 73.1,56.9373 73.1,37"
24 android:strokeColor="#00000000" android:strokeWidth="1"/>
25 <path android:fillColor="#58A0C4" android:fillType="evenOdd"
26 android:pathData="M67.4,37C67.4,53.7895 53.7895,67.4 37,67.4C20.2105,67.4 6.6,53.7895 6.6,37C6.6,20.2105 20.2105,6.6 37,6.6C53.7895,6.6 67.4,20.2105 67.4,37"
27 android:strokeColor="#00000000" android:strokeWidth="1"/>
28</vector>
diff --git a/tflite/src/main/res/drawable/ic_shutter_normal.xml b/tflite/src/main/res/drawable/ic_shutter_normal.xml
new file mode 100644
index 0000000..25a10e1
--- /dev/null
+++ b/tflite/src/main/res/drawable/ic_shutter_normal.xml
@@ -0,0 +1,28 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--
3 ~ Copyright 2019 Google LLC
4 ~
5 ~ Licensed under the Apache License, Version 2.0 (the "License");
6 ~ you may not use this file except in compliance with the License.
7 ~ You may obtain a copy of the License at
8 ~
9 ~ https://www.apache.org/licenses/LICENSE-2.0
10 ~
11 ~ Unless required by applicable law or agreed to in writing, software
12 ~ distributed under the License is distributed on an "AS IS" BASIS,
13 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ~ See the License for the specific language governing permissions and
15 ~ limitations under the License.
16 -->
17<vector xmlns:android="http://schemas.android.com/apk/res/android"
18 android:width="24dp"
19 android:height="24dp"
20 android:viewportWidth="74"
21 android:viewportHeight="74">
22 <path android:fillColor="#FFFFFF" android:fillType="evenOdd"
23 android:pathData="M73.1,37C73.1,17.0637 56.9373,0.9 37,0.9C17.0627,0.9 0.9,17.0637 0.9,37C0.9,56.9373 17.0627,73.1 37,73.1C56.9373,73.1 73.1,56.9373 73.1,37"
24 android:strokeColor="#00000000" android:strokeWidth="1"/>
25 <path android:fillColor="#CFD7DB" android:fillType="evenOdd"
26 android:pathData="M67.4,37C67.4,53.7895 53.7895,67.4 37,67.4C20.2105,67.4 6.6,53.7895 6.6,37C6.6,20.2105 20.2105,6.6 37,6.6C53.7895,6.6 67.4,20.2105 67.4,37"
27 android:strokeColor="#00000000" android:strokeWidth="1"/>
28</vector>
diff --git a/tflite/src/main/res/drawable/ic_shutter_pressed.xml b/tflite/src/main/res/drawable/ic_shutter_pressed.xml
new file mode 100644
index 0000000..fa852ac
--- /dev/null
+++ b/tflite/src/main/res/drawable/ic_shutter_pressed.xml
@@ -0,0 +1,28 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--
3 ~ Copyright 2019 Google LLC
4 ~
5 ~ Licensed under the Apache License, Version 2.0 (the "License");
6 ~ you may not use this file except in compliance with the License.
7 ~ You may obtain a copy of the License at
8 ~
9 ~ https://www.apache.org/licenses/LICENSE-2.0
10 ~
11 ~ Unless required by applicable law or agreed to in writing, software
12 ~ distributed under the License is distributed on an "AS IS" BASIS,
13 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ~ See the License for the specific language governing permissions and
15 ~ limitations under the License.
16 -->
17<vector xmlns:android="http://schemas.android.com/apk/res/android"
18 android:width="24dp"
19 android:height="24dp"
20 android:viewportWidth="74"
21 android:viewportHeight="74">
22 <path android:fillColor="#FFFFFF" android:fillType="evenOdd"
23 android:pathData="M73.1,37C73.1,17.0637 56.9373,0.9 37,0.9C17.0627,0.9 0.9,17.0637 0.9,37C0.9,56.9373 17.0627,73.1 37,73.1C56.9373,73.1 73.1,56.9373 73.1,37"
24 android:strokeColor="#00000000" android:strokeWidth="1"/>
25 <path android:fillColor="#58A0C4" android:fillType="evenOdd"
26 android:pathData="M67.4,37C67.4,53.7895 53.7895,67.4 37,67.4C20.2105,67.4 6.6,53.7895 6.6,37C6.6,20.2105 20.2105,6.6 37,6.6C53.7895,6.6 67.4,20.2105 67.4,37"
27 android:strokeColor="#00000000" android:strokeWidth="1"/>
28</vector>
diff --git a/tflite/src/main/res/drawable/shape_rectangle.xml b/tflite/src/main/res/drawable/shape_rectangle.xml
new file mode 100644
index 0000000..5365e4c
--- /dev/null
+++ b/tflite/src/main/res/drawable/shape_rectangle.xml
@@ -0,0 +1,28 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--
3 ~ Copyright 2020 Google LLC
4 ~
5 ~ Licensed under the Apache License, Version 2.0 (the "License");
6 ~ you may not use this file except in compliance with the License.
7 ~ You may obtain a copy of the License at
8 ~
9 ~ https://www.apache.org/licenses/LICENSE-2.0
10 ~
11 ~ Unless required by applicable law or agreed to in writing, software
12 ~ distributed under the License is distributed on an "AS IS" BASIS,
13 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ~ See the License for the specific language governing permissions and
15 ~ limitations under the License.
16 -->
17<shape xmlns:android="http://schemas.android.com/apk/res/android"
18 android:shape="rectangle">
19 <corners
20 android:radius="4dp"
21 android:topRightRadius="0dp"
22 android:bottomRightRadius="0dp"
23 android:bottomLeftRadius="0dp" />
24 <stroke
25 android:width="4dp"
26 android:color="@android:color/white" />
27 <solid android:color="@android:color/transparent"/>
28</shape> \ No newline at end of file
diff --git a/tflite/src/main/res/layout-land/activity_camera.xml b/tflite/src/main/res/layout-land/activity_camera.xml
new file mode 100644
index 0000000..1cc66f1
--- /dev/null
+++ b/tflite/src/main/res/layout-land/activity_camera.xml
@@ -0,0 +1,72 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--
3 ~ Copyright 2020 Google LLC
4 ~
5 ~ Licensed under the Apache License, Version 2.0 (the "License");
6 ~ you may not use this file except in compliance with the License.
7 ~ You may obtain a copy of the License at
8 ~
9 ~ https://www.apache.org/licenses/LICENSE-2.0
10 ~
11 ~ Unless required by applicable law or agreed to in writing, software
12 ~ distributed under the License is distributed on an "AS IS" BASIS,
13 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ~ See the License for the specific language governing permissions and
15 ~ limitations under the License.
16 -->
17<androidx.constraintlayout.widget.ConstraintLayout
18 xmlns:android="http://schemas.android.com/apk/res/android"
19 xmlns:app="http://schemas.android.com/apk/res-auto"
20 xmlns:tools="http://schemas.android.com/tools"
21 android:id="@+id/camera_container"
22 android:background="@android:color/black"
23 android:layout_width="match_parent"
24 android:layout_height="match_parent">
25
26 <androidx.camera.view.PreviewView
27 android:id="@+id/view_finder"
28 android:layout_width="match_parent"
29 android:layout_height="match_parent"/>
30
31 <ImageView
32 android:id="@+id/image_predicted"
33 android:layout_width="match_parent"
34 android:layout_height="match_parent"
35 android:scaleType="centerCrop"
36 android:visibility="gone" />
37
38 <TextView
39 android:id="@+id/text_prediction"
40 android:layout_width="wrap_content"
41 android:layout_height="wrap_content"
42 android:layout_marginTop="@dimen/margin_xsmall"
43 app:layout_constraintTop_toTopOf="parent"
44 app:layout_constraintStart_toStartOf="parent"
45 app:layout_constraintEnd_toEndOf="parent"
46 android:textAllCaps="true"
47 android:textAppearance="@style/TextAppearance.AppCompat.Display1"
48 android:text="@string/unknown" />
49
50 <View
51 android:id="@+id/box_prediction"
52 android:layout_width="0dp"
53 android:layout_height="0dp"
54 android:background="@drawable/shape_rectangle"
55 app:layout_constraintTop_toTopOf="parent"
56 app:layout_constraintStart_toStartOf="parent" />
57
58 <!-- Camera control buttons -->
59
60 <ImageButton
61 android:id="@+id/camera_capture_button"
62 android:layout_width="@dimen/round_button_large"
63 android:layout_height="@dimen/round_button_large"
64 android:layout_marginEnd="@dimen/shutter_button_margin"
65 android:scaleType="fitCenter"
66 android:background="@drawable/ic_shutter"
67 app:layout_constraintEnd_toEndOf="parent"
68 app:layout_constraintTop_toTopOf="parent"
69 app:layout_constraintBottom_toBottomOf="parent"
70 android:contentDescription="@string/capture_button_alt" />
71
72</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file
diff --git a/tflite/src/main/res/layout/activity_camera.xml b/tflite/src/main/res/layout/activity_camera.xml
new file mode 100644
index 0000000..c094f94
--- /dev/null
+++ b/tflite/src/main/res/layout/activity_camera.xml
@@ -0,0 +1,72 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--
3 ~ Copyright 2020 Google LLC
4 ~
5 ~ Licensed under the Apache License, Version 2.0 (the "License");
6 ~ you may not use this file except in compliance with the License.
7 ~ You may obtain a copy of the License at
8 ~
9 ~ https://www.apache.org/licenses/LICENSE-2.0
10 ~
11 ~ Unless required by applicable law or agreed to in writing, software
12 ~ distributed under the License is distributed on an "AS IS" BASIS,
13 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ~ See the License for the specific language governing permissions and
15 ~ limitations under the License.
16 -->
17<androidx.constraintlayout.widget.ConstraintLayout
18 xmlns:android="http://schemas.android.com/apk/res/android"
19 xmlns:app="http://schemas.android.com/apk/res-auto"
20 xmlns:tools="http://schemas.android.com/tools"
21 android:id="@+id/camera_container"
22 android:background="@android:color/black"
23 android:layout_width="match_parent"
24 android:layout_height="match_parent">
25
26 <androidx.camera.view.PreviewView
27 android:id="@+id/view_finder"
28 android:layout_width="match_parent"
29 android:layout_height="match_parent"/>
30
31 <ImageView
32 android:id="@+id/image_predicted"
33 android:layout_width="match_parent"
34 android:layout_height="match_parent"
35 android:scaleType="centerCrop"
36 android:visibility="gone" />
37
38 <TextView
39 android:id="@+id/text_prediction"
40 android:layout_width="wrap_content"
41 android:layout_height="wrap_content"
42 android:layout_marginTop="@dimen/margin_xsmall"
43 app:layout_constraintTop_toTopOf="parent"
44 app:layout_constraintStart_toStartOf="parent"
45 app:layout_constraintEnd_toEndOf="parent"
46 android:textAllCaps="true"
47 android:textAppearance="@style/TextAppearance.AppCompat.Display1"
48 android:text="@string/unknown" />
49
50 <View
51 android:id="@+id/box_prediction"
52 android:layout_width="0dp"
53 android:layout_height="0dp"
54 android:background="@drawable/shape_rectangle"
55 app:layout_constraintTop_toTopOf="parent"
56 app:layout_constraintStart_toStartOf="parent" />
57
58 <!-- Camera control buttons -->
59
60 <ImageButton
61 android:id="@+id/camera_capture_button"
62 android:layout_width="@dimen/round_button_large"
63 android:layout_height="@dimen/round_button_large"
64 android:layout_marginBottom="@dimen/shutter_button_margin"
65 android:scaleType="fitCenter"
66 android:background="@drawable/ic_shutter"
67 app:layout_constraintLeft_toLeftOf="parent"
68 app:layout_constraintRight_toRightOf="parent"
69 app:layout_constraintBottom_toBottomOf="parent"
70 android:contentDescription="@string/capture_button_alt" />
71
72</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file
diff --git a/tflite/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/tflite/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..fe90d56
--- /dev/null
+++ b/tflite/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,20 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--
3 ~ Copyright 2019 Google LLC
4 ~
5 ~ Licensed under the Apache License, Version 2.0 (the "License");
6 ~ you may not use this file except in compliance with the License.
7 ~ You may obtain a copy of the License at
8 ~
9 ~ https://www.apache.org/licenses/LICENSE-2.0
10 ~
11 ~ Unless required by applicable law or agreed to in writing, software
12 ~ distributed under the License is distributed on an "AS IS" BASIS,
13 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ~ See the License for the specific language governing permissions and
15 ~ limitations under the License.
16 -->
17<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
18 <background android:drawable="@drawable/ic_launcher_background"/>
19 <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
20</adaptive-icon> \ No newline at end of file
diff --git a/tflite/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/tflite/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..fe90d56
--- /dev/null
+++ b/tflite/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,20 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--
3 ~ Copyright 2019 Google LLC
4 ~
5 ~ Licensed under the Apache License, Version 2.0 (the "License");
6 ~ you may not use this file except in compliance with the License.
7 ~ You may obtain a copy of the License at
8 ~
9 ~ https://www.apache.org/licenses/LICENSE-2.0
10 ~
11 ~ Unless required by applicable law or agreed to in writing, software
12 ~ distributed under the License is distributed on an "AS IS" BASIS,
13 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ~ See the License for the specific language governing permissions and
15 ~ limitations under the License.
16 -->
17<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
18 <background android:drawable="@drawable/ic_launcher_background"/>
19 <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
20</adaptive-icon> \ No newline at end of file
diff --git a/tflite/src/main/res/mipmap-hdpi/ic_launcher.png b/tflite/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..edd7b36
--- /dev/null
+++ b/tflite/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/tflite/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/tflite/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..a050bf2
--- /dev/null
+++ b/tflite/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/tflite/src/main/res/mipmap-hdpi/ic_launcher_round.png b/tflite/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..ec13a43
--- /dev/null
+++ b/tflite/src/main/res/mipmap-hdpi/ic_launcher_round.png
Binary files differ
diff --git a/tflite/src/main/res/mipmap-mdpi/ic_launcher.png b/tflite/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..6fd4f91
--- /dev/null
+++ b/tflite/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/tflite/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/tflite/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..df81982
--- /dev/null
+++ b/tflite/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/tflite/src/main/res/mipmap-mdpi/ic_launcher_round.png b/tflite/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..2616048
--- /dev/null
+++ b/tflite/src/main/res/mipmap-mdpi/ic_launcher_round.png
Binary files differ
diff --git a/tflite/src/main/res/mipmap-xhdpi/ic_launcher.png b/tflite/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..fdae11d
--- /dev/null
+++ b/tflite/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tflite/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/tflite/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..667d566
--- /dev/null
+++ b/tflite/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/tflite/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/tflite/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..cb11f72
--- /dev/null
+++ b/tflite/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Binary files differ
diff --git a/tflite/src/main/res/mipmap-xxhdpi/ic_launcher.png b/tflite/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..5d4fa36
--- /dev/null
+++ b/tflite/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tflite/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/tflite/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..33dc1c6
--- /dev/null
+++ b/tflite/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/tflite/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/tflite/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..07d1393
--- /dev/null
+++ b/tflite/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/tflite/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/tflite/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..675e313
--- /dev/null
+++ b/tflite/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/tflite/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/tflite/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..9f9808a
--- /dev/null
+++ b/tflite/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/tflite/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/tflite/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..54e07e0
--- /dev/null
+++ b/tflite/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/tflite/src/main/res/values/dimens.xml b/tflite/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..53948b6
--- /dev/null
+++ b/tflite/src/main/res/values/dimens.xml
@@ -0,0 +1,33 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--
3 ~ Copyright 2020 Google LLC
4 ~
5 ~ Licensed under the Apache License, Version 2.0 (the "License");
6 ~ you may not use this file except in compliance with the License.
7 ~ You may obtain a copy of the License at
8 ~
9 ~ https://www.apache.org/licenses/LICENSE-2.0
10 ~
11 ~ Unless required by applicable law or agreed to in writing, software
12 ~ distributed under the License is distributed on an "AS IS" BASIS,
13 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ~ See the License for the specific language governing permissions and
15 ~ limitations under the License.
16 -->
17<resources>
18 <dimen name="margin_xsmall">16dp</dimen>
19 <dimen name="margin_small">32dp</dimen>
20 <dimen name="margin_medium">48dp</dimen>
21 <dimen name="margin_large">64dp</dimen>
22 <dimen name="margin_xlarge">92dp</dimen>
23
24 <dimen name="spacing_small">4dp</dimen>
25 <dimen name="spacing_medium">8dp</dimen>
26 <dimen name="spacing_large">16dp</dimen>
27
28 <dimen name="round_button_small">32dp</dimen>
29 <dimen name="round_button_medium">64dp</dimen>
30 <dimen name="round_button_large">92dp</dimen>
31
32 <dimen name="shutter_button_margin">80dp</dimen>
33</resources> \ No newline at end of file
diff --git a/tflite/src/main/res/values/strings.xml b/tflite/src/main/res/values/strings.xml
new file mode 100644
index 0000000..dbfb4c0
--- /dev/null
+++ b/tflite/src/main/res/values/strings.xml
@@ -0,0 +1,21 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--
3 ~ Copyright 2020 Google LLC
4 ~
5 ~ Licensed under the Apache License, Version 2.0 (the "License");
6 ~ you may not use this file except in compliance with the License.
7 ~ You may obtain a copy of the License at
8 ~
9 ~ https://www.apache.org/licenses/LICENSE-2.0
10 ~
11 ~ Unless required by applicable law or agreed to in writing, software
12 ~ distributed under the License is distributed on an "AS IS" BASIS,
13 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ~ See the License for the specific language governing permissions and
15 ~ limitations under the License.
16 -->
17<resources>
18 <string name="app_name">Camera Object Detector</string>
19 <string name="capture_button_alt">Capture</string>
20 <string name="unknown">UNKNOWN</string>
21</resources>
diff --git a/tflite/src/main/res/values/styles.xml b/tflite/src/main/res/values/styles.xml
new file mode 100644
index 0000000..8bfac62
--- /dev/null
+++ b/tflite/src/main/res/values/styles.xml
@@ -0,0 +1,27 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--
3 ~ Copyright 2020 Google LLC
4 ~
5 ~ Licensed under the Apache License, Version 2.0 (the "License");
6 ~ you may not use this file except in compliance with the License.
7 ~ You may obtain a copy of the License at
8 ~
9 ~ https://www.apache.org/licenses/LICENSE-2.0
10 ~
11 ~ Unless required by applicable law or agreed to in writing, software
12 ~ distributed under the License is distributed on an "AS IS" BASIS,
13 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ~ See the License for the specific language governing permissions and
15 ~ limitations under the License.
16 -->
17<resources>
18
19 <!-- Base application theme. -->
20 <style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
21 <item name="android:immersive">true</item>
22 <item name="android:windowFullscreen">true</item>
23 <item name="android:windowTranslucentStatus">true</item>
24 <item name="android:windowTranslucentNavigation">true</item>
25 </style>
26
27</resources>
diff --git a/utils/.gitignore b/utils/.gitignore
new file mode 100644
index 0000000..38c5875
--- /dev/null
+++ b/utils/.gitignore
@@ -0,0 +1,2 @@
1/build
2.idea \ No newline at end of file
diff --git a/utils/README.md b/utils/README.md
new file mode 100644
index 0000000..7a10722
--- /dev/null
+++ b/utils/README.md
@@ -0,0 +1,2 @@
1Do not modify code under this folder outside of `CameraUtils`, it is copied
2automatically by `.github/scripts/copy_utils.sh`. \ No newline at end of file
diff --git a/utils/build.gradle b/utils/build.gradle
new file mode 100644
index 0000000..1ef65fa
--- /dev/null
+++ b/utils/build.gradle
@@ -0,0 +1,75 @@
1/*
2 * Copyright 2020 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 * https://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
17apply plugin: 'com.android.library'
18apply plugin: 'kotlin-android'
19
20android {
21 compileSdkVersion 29
22
23 defaultConfig {
24 minSdkVersion 21
25 targetSdkVersion 29
26 versionCode 1
27 versionName "1.0"
28
29 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
30 consumerProguardFiles 'consumer-rules.pro'
31 }
32
33 compileOptions {
34 sourceCompatibility rootProject.ext.java_version
35 targetCompatibility rootProject.ext.java_version
36 }
37
38 kotlinOptions {
39 jvmTarget = "$rootProject.ext.java_version"
40 }
41
42 buildTypes {
43 release {
44 minifyEnabled false
45 proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
46 }
47 }
48}
49
50dependencies {
51
52 // Kotlin lang
53 implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
54 implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.4'
55
56 // App compat and UI things
57 implementation 'androidx.appcompat:appcompat:1.1.0'
58 implementation 'androidx.recyclerview:recyclerview:1.1.0'
59
60 // EXIF Interface
61 implementation 'androidx.exifinterface:exifinterface:1.2.0'
62
63 // Unit testing
64 testImplementation 'androidx.test.ext:junit:1.1.1'
65 testImplementation 'androidx.test:rules:1.2.0'
66 testImplementation 'androidx.test:runner:1.2.0'
67 testImplementation 'androidx.test.espresso:espresso-core:3.2.0'
68 testImplementation 'org.robolectric:robolectric:4.3.1'
69
70 // Instrumented testing
71 androidTestImplementation 'androidx.test.ext:junit:1.1.1'
72 androidTestImplementation 'androidx.test:rules:1.2.0'
73 androidTestImplementation 'androidx.test:runner:1.2.0'
74 androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
75}
diff --git a/utils/src/main/AndroidManifest.xml b/utils/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..2e13c37
--- /dev/null
+++ b/utils/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<!--
3 ~ Copyright 2020 The Android Open Source Project
4 ~
5 ~ Licensed under the Apache License, Version 2.0 (the "License");
6 ~ you may not use this file except in compliance with the License.
7 ~ You may obtain a copy of the License at
8 ~
9 ~ https://www.apache.org/licenses/LICENSE-2.0
10 ~
11 ~ Unless required by applicable law or agreed to in writing, software
12 ~ distributed under the License is distributed on an "AS IS" BASIS,
13 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ~ See the License for the specific language governing permissions and
15 ~ limitations under the License.
16 -->
17<manifest package="com.example.android.camera.utils" />
diff --git a/utils/src/main/java/com/example/android/camera/utils/AutoFitSurfaceView.kt b/utils/src/main/java/com/example/android/camera/utils/AutoFitSurfaceView.kt
new file mode 100644
index 0000000..3d900d1
--- /dev/null
+++ b/utils/src/main/java/com/example/android/camera/utils/AutoFitSurfaceView.kt
@@ -0,0 +1,79 @@
1/*
2 * Copyright 2020 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 * https://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
17package com.example.android.camera.utils
18
19import android.content.Context
20import android.util.AttributeSet
21import android.util.Log
22import android.view.SurfaceView
23import kotlin.math.roundToInt
24
25/**
26 * A [SurfaceView] that can be adjusted to a specified aspect ratio and
27 * performs center-crop transformation of input frames.
28 */
29class AutoFitSurfaceView @JvmOverloads constructor(
30 context: Context,
31 attrs: AttributeSet? = null,
32 defStyle: Int = 0
33) : SurfaceView(context, attrs, defStyle) {
34
35 private var aspectRatio = 0f
36
37 /**
38 * Sets the aspect ratio for this view. The size of the view will be
39 * measured based on the ratio calculated from the parameters.
40 *
41 * @param width Camera resolution horizontal size
42 * @param height Camera resolution vertical size
43 */
44 fun setAspectRatio(width: Int, height: Int) {
45 require(width > 0 && height > 0) { "Size cannot be negative" }
46 aspectRatio = width.toFloat() / height.toFloat()
47 holder.setFixedSize(width, height)
48 requestLayout()
49 }
50
51 override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
52 super.onMeasure(widthMeasureSpec, heightMeasureSpec)
53 val width = MeasureSpec.getSize(widthMeasureSpec)
54 val height = MeasureSpec.getSize(heightMeasureSpec)
55 if (aspectRatio == 0f) {
56 setMeasuredDimension(width, height)
57 } else {
58
59 // Performs center-crop transformation of the camera frames
60 val newWidth: Int
61 val newHeight: Int
62 val actualRatio = if (width > height) aspectRatio else 1f / aspectRatio
63 if (width < height * actualRatio) {
64 newHeight = height
65 newWidth = (height * actualRatio).roundToInt()
66 } else {
67 newWidth = width
68 newHeight = (width / actualRatio).roundToInt()
69 }
70
71 Log.d(TAG, "Measured dimensions set: $newWidth x $newHeight")
72 setMeasuredDimension(newWidth, newHeight)
73 }
74 }
75
76 companion object {
77 private val TAG = AutoFitSurfaceView::class.java.simpleName
78 }
79}
diff --git a/utils/src/main/java/com/example/android/camera/utils/CameraSizes.kt b/utils/src/main/java/com/example/android/camera/utils/CameraSizes.kt
new file mode 100644
index 0000000..6db01d3
--- /dev/null
+++ b/utils/src/main/java/com/example/android/camera/utils/CameraSizes.kt
@@ -0,0 +1,79 @@
1/*
2 * Copyright 2020 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 * https://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
17package com.example.android.camera.utils
18
19import android.graphics.Point
20import android.hardware.camera2.CameraCharacteristics
21import android.hardware.camera2.params.StreamConfigurationMap
22import android.util.Size
23import android.view.Display
24import kotlin.math.max
25import kotlin.math.min
26
27/** Helper class used to pre-compute shortest and longest sides of a [Size] */
28class SmartSize(width: Int, height: Int) {
29 var size = Size(width, height)
30 var long = max(size.width, size.height)
31 var short = min(size.width, size.height)
32 override fun toString() = "SmartSize(${long}x${short})"
33}
34
35/** Standard High Definition size for pictures and video */
36val SIZE_1080P: SmartSize = SmartSize(1920, 1080)
37
38/** Returns a [SmartSize] object for the given [Display] */
39fun getDisplaySmartSize(display: Display): SmartSize {
40 val outPoint = Point()
41 display.getRealSize(outPoint)
42 return SmartSize(outPoint.x, outPoint.y)
43}
44
45/**
46 * Returns the largest available PREVIEW size. For more information, see:
47 * https://d.android.com/reference/android/hardware/camera2/CameraDevice and
48 * https://developer.android.com/reference/android/hardware/camera2/params/StreamConfigurationMap
49 */
50fun <T>getPreviewOutputSize(
51 display: Display,
52 characteristics: CameraCharacteristics,
53 targetClass: Class<T>,
54 format: Int? = null
55): Size {
56
57 // Find which is smaller: screen or 1080p
58 val screenSize = getDisplaySmartSize(display)
59 val hdScreen = screenSize.long >= SIZE_1080P.long || screenSize.short >= SIZE_1080P.short
60 val maxSize = if (hdScreen) SIZE_1080P else screenSize
61
62 // If image format is provided, use it to determine supported sizes; else use target class
63 val config = characteristics.get(
64 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
65 if (format == null)
66 assert(StreamConfigurationMap.isOutputSupportedFor(targetClass))
67 else
68 assert(config.isOutputSupportedFor(format))
69 val allSizes = if (format == null)
70 config.getOutputSizes(targetClass) else config.getOutputSizes(format)
71
72 // Get available sizes and sort them by area from largest to smallest
73 val validSizes = allSizes
74 .sortedWith(compareBy { it.height * it.width })
75 .map { SmartSize(it.width, it.height) }.reversed()
76
77 // Then, get the largest output size that is smaller or equal than our max size
78 return validSizes.first { it.long <= maxSize.long && it.short <= maxSize.short }.size
79} \ No newline at end of file
diff --git a/utils/src/main/java/com/example/android/camera/utils/ExifUtils.kt b/utils/src/main/java/com/example/android/camera/utils/ExifUtils.kt
new file mode 100644
index 0000000..561c14b
--- /dev/null
+++ b/utils/src/main/java/com/example/android/camera/utils/ExifUtils.kt
@@ -0,0 +1,73 @@
1/*
2 * Copyright 2020 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 * https://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
17package com.example.android.camera.utils
18
19import android.graphics.Bitmap
20import android.graphics.Matrix
21import android.util.Log
22import androidx.exifinterface.media.ExifInterface
23
24private const val TAG: String = "ExifUtils"
25
26/** Transforms rotation and mirroring information into one of the [ExifInterface] constants */
27fun computeExifOrientation(rotationDegrees: Int, mirrored: Boolean) = when {
28 rotationDegrees == 0 && !mirrored -> ExifInterface.ORIENTATION_NORMAL
29 rotationDegrees == 0 && mirrored -> ExifInterface.ORIENTATION_FLIP_HORIZONTAL
30 rotationDegrees == 180 && !mirrored -> ExifInterface.ORIENTATION_ROTATE_180
31 rotationDegrees == 180 && mirrored -> ExifInterface.ORIENTATION_FLIP_VERTICAL
32 rotationDegrees == 270 && mirrored -> ExifInterface.ORIENTATION_TRANSVERSE
33 rotationDegrees == 90 && !mirrored -> ExifInterface.ORIENTATION_ROTATE_90
34 rotationDegrees == 90 && mirrored -> ExifInterface.ORIENTATION_TRANSPOSE
35 rotationDegrees == 270 && mirrored -> ExifInterface.ORIENTATION_ROTATE_270
36 rotationDegrees == 270 && !mirrored -> ExifInterface.ORIENTATION_TRANSVERSE
37 else -> ExifInterface.ORIENTATION_UNDEFINED
38}
39
40/**
41 * Helper function used to convert an EXIF orientation enum into a transformation matrix
42 * that can be applied to a bitmap.
43 *
44 * @return matrix - Transformation required to properly display [Bitmap]
45 */
46fun decodeExifOrientation(exifOrientation: Int): Matrix {
47 val matrix = Matrix()
48
49 // Apply transformation corresponding to declared EXIF orientation
50 when (exifOrientation) {
51 ExifInterface.ORIENTATION_NORMAL -> Unit
52 ExifInterface.ORIENTATION_UNDEFINED -> Unit
53 ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90F)
54 ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180F)
55 ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270F)
56 ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> matrix.postScale(-1F, 1F)
57 ExifInterface.ORIENTATION_FLIP_VERTICAL -> matrix.postScale(1F, -1F)
58 ExifInterface.ORIENTATION_TRANSPOSE -> {
59 matrix.postScale(-1F, 1F)
60 matrix.postRotate(270F)
61 }
62 ExifInterface.ORIENTATION_TRANSVERSE -> {
63 matrix.postScale(-1F, 1F)
64 matrix.postRotate(90F)
65 }
66
67 // Error out if the EXIF orientation is invalid
68 else -> Log.e(TAG, "Invalid orientation: $exifOrientation")
69 }
70
71 // Return the resulting matrix
72 return matrix
73}
diff --git a/utils/src/main/java/com/example/android/camera/utils/GenericListAdapter.kt b/utils/src/main/java/com/example/android/camera/utils/GenericListAdapter.kt
new file mode 100644
index 0000000..a55af27
--- /dev/null
+++ b/utils/src/main/java/com/example/android/camera/utils/GenericListAdapter.kt
@@ -0,0 +1,55 @@
1/*
2 * Copyright 2020 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 * https://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
17package com.example.android.camera.utils
18
19import android.view.LayoutInflater
20import android.view.View
21import android.view.ViewGroup
22import androidx.recyclerview.widget.RecyclerView
23
24/** Type helper used for the callback triggered once our view has been bound */
25typealias BindCallback<T> = (view: View, data: T, position: Int) -> Unit
26
27/** List adapter for generic types, intended used for small-medium lists of data */
28class GenericListAdapter<T>(
29 private val dataset: List<T>,
30 private val itemLayoutId: Int? = null,
31 private val itemViewFactory: (() -> View)? = null,
32 private val onBind: BindCallback<T>
33) : RecyclerView.Adapter<GenericListAdapter.GenericListViewHolder>() {
34
35 class GenericListViewHolder(val view: View) : RecyclerView.ViewHolder(view)
36
37 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = GenericListViewHolder(when {
38 itemViewFactory != null -> itemViewFactory.invoke()
39 itemLayoutId != null -> {
40 LayoutInflater.from(parent.context)
41 .inflate(itemLayoutId, parent, false)
42 }
43 else -> {
44 throw IllegalStateException(
45 "Either the layout ID or the view factory need to be non-null")
46 }
47 })
48
49 override fun onBindViewHolder(holder: GenericListViewHolder, position: Int) {
50 if (position < 0 || position > dataset.size) return
51 onBind(holder.view, dataset[position], position)
52 }
53
54 override fun getItemCount() = dataset.size
55} \ No newline at end of file
diff --git a/utils/src/main/java/com/example/android/camera/utils/OrientationLiveData.kt b/utils/src/main/java/com/example/android/camera/utils/OrientationLiveData.kt
new file mode 100644
index 0000000..f9d9a47
--- /dev/null
+++ b/utils/src/main/java/com/example/android/camera/utils/OrientationLiveData.kt
@@ -0,0 +1,95 @@
1/*
2 * Copyright 2020 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 * https://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
17package com.example.android.camera.utils
18
19import android.content.Context
20import android.hardware.camera2.CameraCharacteristics
21import android.view.OrientationEventListener
22import android.view.Surface
23import androidx.lifecycle.LiveData
24
25
26/**
27 * Calculates closest 90-degree orientation to compensate for the device
28 * rotation relative to sensor orientation, i.e., allows user to see camera
29 * frames with the expected orientation.
30 */
31class OrientationLiveData(
32 context: Context,
33 characteristics: CameraCharacteristics
34): LiveData<Int>() {
35
36 private val listener = object : OrientationEventListener(context.applicationContext) {
37 override fun onOrientationChanged(orientation: Int) {
38 val rotation = when {
39 orientation <= 45 -> Surface.ROTATION_0
40 orientation <= 135 -> Surface.ROTATION_90
41 orientation <= 225 -> Surface.ROTATION_180
42 orientation <= 315 -> Surface.ROTATION_270
43 else -> Surface.ROTATION_0
44 }
45 val relative = computeRelativeRotation(characteristics, rotation)
46 if (relative != value) postValue(relative)
47 }
48 }
49
50 override fun onActive() {
51 super.onActive()
52 listener.enable()
53 }
54
55 override fun onInactive() {
56 super.onInactive()
57 listener.disable()
58 }
59
60 companion object {
61
62 /**
63 * Computes rotation required to transform from the camera sensor orientation to the
64 * device's current orientation in degrees.
65 *
66 * @param characteristics the [CameraCharacteristics] to query for the sensor orientation.
67 * @param surfaceRotation the current device orientation as a Surface constant
68 * @return the relative rotation from the camera sensor to the current device orientation.
69 */
70 @JvmStatic
71 private fun computeRelativeRotation(
72 characteristics: CameraCharacteristics,
73 surfaceRotation: Int
74 ): Int {
75 val sensorOrientationDegrees =
76 characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!
77
78 val deviceOrientationDegrees = when (surfaceRotation) {
79 Surface.ROTATION_0 -> 0
80 Surface.ROTATION_90 -> 90
81 Surface.ROTATION_180 -> 180
82 Surface.ROTATION_270 -> 270
83 else -> 0
84 }
85
86 // Reverse device orientation for front-facing cameras
87 val sign = if (characteristics.get(CameraCharacteristics.LENS_FACING) ==
88 CameraCharacteristics.LENS_FACING_FRONT) 1 else -1
89
90 // Calculate desired JPEG orientation relative to camera orientation to make
91 // the image upright relative to the device orientation
92 return (sensorOrientationDegrees - (deviceOrientationDegrees * sign) + 360) % 360
93 }
94 }
95}
diff --git a/utils/src/main/java/com/example/android/camera/utils/Yuv.kt b/utils/src/main/java/com/example/android/camera/utils/Yuv.kt
new file mode 100644
index 0000000..c476ad0
--- /dev/null
+++ b/utils/src/main/java/com/example/android/camera/utils/Yuv.kt
@@ -0,0 +1,191 @@
1package com.example.android.camera.utils
2
3import android.graphics.ImageFormat
4import android.media.Image
5import androidx.annotation.IntDef
6import java.nio.ByteBuffer
7
8/*
9This file is converted from part of https://github.com/gordinmitya/yuv2buf.
10Follow the link to find demo app, performance benchmarks and unit tests.
11
12Intro to YUV image formats:
13YUV_420_888 - is a generic format that can be represented as I420, YV12, NV21, and NV12.
14420 means that for each 4 luminosity pixels we have 2 chroma pixels: U and V.
15
16* I420 format represents an image as Y plane followed by U then followed by V plane
17 without chroma channels interleaving.
18 For example:
19 Y Y Y Y
20 Y Y Y Y
21 U U V V
22
23* NV21 format represents an image as Y plane followed by V and U interleaved. First V then U.
24 For example:
25 Y Y Y Y
26 Y Y Y Y
27 V U V U
28
29* YV12 and NV12 are the same as previous formats but with swapped order of V and U. (U then V)
30
31Visualization of these 4 formats:
32https://user-images.githubusercontent.com/9286092/89119601-4f6f8100-d4b8-11ea-9a51-2765f7e513c2.jpg
33
34It's guaranteed that image.getPlanes() always returns planes in order Y U V for YUV_420_888.
35https://developer.android.com/reference/android/graphics/ImageFormat#YUV_420_888
36
37Because I420 and NV21 are more widely supported (RenderScript, OpenCV, MNN)
38the conversion is done into these formats.
39
40More about each format: https://www.fourcc.org/yuv.php
41*/
42
43@kotlin.annotation.Retention(AnnotationRetention.SOURCE)
44@IntDef(ImageFormat.NV21, ImageFormat.YUV_420_888)
45annotation class YuvType
46
47class YuvByteBuffer(image: Image, dstBuffer: ByteBuffer? = null) {
48 @YuvType
49 val type: Int
50 val buffer: ByteBuffer
51
52 init {
53 val wrappedImage = ImageWrapper(image)
54
55 type = if (wrappedImage.u.pixelStride == 1) {
56 ImageFormat.YUV_420_888
57 } else {
58 ImageFormat.NV21
59 }
60 val size = image.width * image.height * 3 / 2
61 buffer = if (
62 dstBuffer == null || dstBuffer.capacity() < size ||
63 dstBuffer.isReadOnly || !dstBuffer.isDirect
64 ) {
65 ByteBuffer.allocateDirect(size) }
66 else {
67 dstBuffer
68 }
69 buffer.rewind()
70
71 removePadding(wrappedImage)
72 }
73
74 // Input buffers are always direct as described in
75 // https://developer.android.com/reference/android/media/Image.Plane#getBuffer()
76 private fun removePadding(image: ImageWrapper) {
77 val sizeLuma = image.y.width * image.y.height
78 val sizeChroma = image.u.width * image.u.height
79 if (image.y.rowStride > image.y.width) {
80 removePaddingCompact(image.y, buffer, 0)
81 } else {
82 buffer.position(0)
83 buffer.put(image.y.buffer)
84 }
85 if (type == ImageFormat.YUV_420_888) {
86 if (image.u.rowStride > image.u.width) {
87 removePaddingCompact(image.u, buffer, sizeLuma)
88 removePaddingCompact(image.v, buffer, sizeLuma + sizeChroma)
89 } else {
90 buffer.position(sizeLuma)
91 buffer.put(image.u.buffer)
92 buffer.position(sizeLuma + sizeChroma)
93 buffer.put(image.v.buffer)
94 }
95 } else {
96 if (image.u.rowStride > image.u.width * 2) {
97 removePaddingNotCompact(image, buffer, sizeLuma)
98 } else {
99 buffer.position(sizeLuma)
100 var uv = image.v.buffer
101 val properUVSize = image.v.height * image.v.rowStride - 1
102 if (uv.capacity() > properUVSize) {
103 uv = clipBuffer(image.v.buffer, 0, properUVSize)
104 }
105 buffer.put(uv)
106 val lastOne = image.u.buffer[image.u.buffer.capacity() - 1]
107 buffer.put(buffer.capacity() - 1, lastOne)
108 }
109 }
110 buffer.rewind()
111 }
112
113 private fun removePaddingCompact(
114 plane: PlaneWrapper,
115 dst: ByteBuffer,
116 offset: Int
117 ) {
118 require(plane.pixelStride == 1) {
119 "use removePaddingCompact with pixelStride == 1"
120 }
121
122 val src = plane.buffer
123 val rowStride = plane.rowStride
124 var row: ByteBuffer
125 dst.position(offset)
126 for (i in 0 until plane.height) {
127 row = clipBuffer(src, i * rowStride, plane.width)
128 dst.put(row)
129 }
130 }
131
132 private fun removePaddingNotCompact(
133 image: ImageWrapper,
134 dst: ByteBuffer,
135 offset: Int
136 ) {
137 require(image.u.pixelStride == 2) {
138 "use removePaddingNotCompact pixelStride == 2"
139 }
140 val width = image.u.width
141 val height = image.u.height
142 val rowStride = image.u.rowStride
143 var row: ByteBuffer
144 dst.position(offset)
145 for (i in 0 until height - 1) {
146 row = clipBuffer(image.v.buffer, i * rowStride, width * 2)
147 dst.put(row)
148 }
149 row = clipBuffer(image.u.buffer, (height - 1) * rowStride - 1, width * 2)
150 dst.put(row)
151 }
152
153 private fun clipBuffer(buffer: ByteBuffer, start: Int, size: Int): ByteBuffer {
154 val duplicate = buffer.duplicate()
155 duplicate.position(start)
156 duplicate.limit(start + size)
157 return duplicate.slice()
158 }
159
160 private class ImageWrapper(image:Image) {
161 val width= image.width
162 val height = image.height
163 val y = PlaneWrapper(width, height, image.planes[0])
164 val u = PlaneWrapper(width / 2, height / 2, image.planes[1])
165 val v = PlaneWrapper(width / 2, height / 2, image.planes[2])
166
167 // Check this is a supported image format
168 // https://developer.android.com/reference/android/graphics/ImageFormat#YUV_420_888
169 init {
170 require(y.pixelStride == 1) {
171 "Pixel stride for Y plane must be 1 but got ${y.pixelStride} instead."
172 }
173 require(u.pixelStride == v.pixelStride && u.rowStride == v.rowStride) {
174 "U and V planes must have the same pixel and row strides " +
175 "but got pixel=${u.pixelStride} row=${u.rowStride} for U " +
176 "and pixel=${v.pixelStride} and row=${v.rowStride} for V"
177 }
178 require(u.pixelStride == 1 || u.pixelStride == 2) {
179 "Supported" + " pixel strides for U and V planes are 1 and 2"
180 }
181 }
182 }
183
184 private class PlaneWrapper(width: Int, height: Int, plane: Image.Plane) {
185 val width = width
186 val height = height
187 val buffer: ByteBuffer = plane.buffer
188 val rowStride = plane.rowStride
189 val pixelStride = plane.pixelStride
190 }
191} \ No newline at end of file
diff --git a/utils/src/main/java/com/example/android/camera/utils/YuvToRgbConverter.kt b/utils/src/main/java/com/example/android/camera/utils/YuvToRgbConverter.kt
new file mode 100644
index 0000000..8dcd559
--- /dev/null
+++ b/utils/src/main/java/com/example/android/camera/utils/YuvToRgbConverter.kt
@@ -0,0 +1,99 @@
1/*
2 * Copyright 2020 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 * https://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
17package com.example.android.camera.utils
18
19import android.content.Context
20import android.graphics.Bitmap
21import android.graphics.ImageFormat
22import android.media.Image
23import android.renderscript.Allocation
24import android.renderscript.Element
25import android.renderscript.RenderScript
26import android.renderscript.ScriptIntrinsicYuvToRGB
27import android.renderscript.Type
28import java.nio.ByteBuffer
29
30/**
31 * Helper class used to convert a [Image] object from
32 * [ImageFormat.YUV_420_888] format to an RGB [Bitmap] object, it has equivalent
33 * functionality to https://github
34 * .com/androidx/androidx/blob/androidx-main/camera/camera-core/src/main/java/androidx/camera/core/ImageYuvToRgbConverter.java
35 *
36 * NOTE: This has been tested in a limited number of devices and is not
37 * considered production-ready code. It was created for illustration purposes,
38 * since this is not an efficient camera pipeline due to the multiple copies
39 * required to convert each frame. For example, this
40 * implementation
41 * (https://stackoverflow.com/questions/52726002/camera2-captured-picture-conversion-from-yuv-420-888-to-nv21/52740776#52740776)
42 * might have better performance.
43 */
44class YuvToRgbConverter(context: Context) {
45 private val rs = RenderScript.create(context)
46 private val scriptYuvToRgb =
47 ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs))
48
49 // Do not add getters/setters functions to these private variables
50 // because yuvToRgb() assume they won't be modified elsewhere
51 private var yuvBits: ByteBuffer? = null
52 private var bytes: ByteArray = ByteArray(0)
53 private var inputAllocation: Allocation? = null
54 private var outputAllocation: Allocation? = null
55
56 @Synchronized
57 fun yuvToRgb(image: Image, output: Bitmap) {
58 val yuvBuffer = YuvByteBuffer(image, yuvBits)
59 yuvBits = yuvBuffer.buffer
60
61 if (needCreateAllocations(image, yuvBuffer)) {
62 val yuvType = Type.Builder(rs, Element.U8(rs))
63 .setX(image.width)
64 .setY(image.height)
65 .setYuvFormat(yuvBuffer.type)
66 inputAllocation = Allocation.createTyped(
67 rs,
68 yuvType.create(),
69 Allocation.USAGE_SCRIPT
70 )
71 bytes = ByteArray(yuvBuffer.buffer.capacity())
72 val rgbaType = Type.Builder(rs, Element.RGBA_8888(rs))
73 .setX(image.width)
74 .setY(image.height)
75 outputAllocation = Allocation.createTyped(
76 rs,
77 rgbaType.create(),
78 Allocation.USAGE_SCRIPT
79 )
80 }
81
82 yuvBuffer.buffer.get(bytes)
83 inputAllocation!!.copyFrom(bytes)
84
85 // Convert NV21 or YUV_420_888 format to RGB
86 inputAllocation!!.copyFrom(bytes)
87 scriptYuvToRgb.setInput(inputAllocation)
88 scriptYuvToRgb.forEach(outputAllocation)
89 outputAllocation!!.copyTo(output)
90 }
91
92 private fun needCreateAllocations(image: Image, yuvBuffer: YuvByteBuffer): Boolean {
93 return (inputAllocation == null || // the very 1st call
94 inputAllocation!!.type.x != image.width || // image size changed
95 inputAllocation!!.type.y != image.height ||
96 inputAllocation!!.type.yuv != yuvBuffer.type || // image format changed
97 bytes.size == yuvBuffer.buffer.capacity())
98 }
99}
diff --git a/utils/src/main/res/drawable/ic_shutter.xml b/utils/src/main/res/drawable/ic_shutter.xml
new file mode 100644
index 0000000..9bb91ab
--- /dev/null
+++ b/utils/src/main/res/drawable/ic_shutter.xml
@@ -0,0 +1,21 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--
3 ~ Copyright 2020 The Android Open Source Project
4 ~
5 ~ Licensed under the Apache License, Version 2.0 (the "License");
6 ~ you may not use this file except in compliance with the License.
7 ~ You may obtain a copy of the License at
8 ~
9 ~ https://www.apache.org/licenses/LICENSE-2.0
10 ~
11 ~ Unless required by applicable law or agreed to in writing, software
12 ~ distributed under the License is distributed on an "AS IS" BASIS,
13 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ~ See the License for the specific language governing permissions and
15 ~ limitations under the License.
16 -->
17<selector xmlns:android="http://schemas.android.com/apk/res/android">
18 <item android:state_pressed="true" android:drawable="@drawable/ic_shutter_pressed" />
19 <item android:state_focused="true" android:drawable="@drawable/ic_shutter_focused" />
20 <item android:drawable="@drawable/ic_shutter_normal" />
21</selector> \ No newline at end of file
diff --git a/utils/src/main/res/drawable/ic_shutter_focused.xml b/utils/src/main/res/drawable/ic_shutter_focused.xml
new file mode 100644
index 0000000..9bf521d
--- /dev/null
+++ b/utils/src/main/res/drawable/ic_shutter_focused.xml
@@ -0,0 +1,28 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--
3 ~ Copyright 2020 The Android Open Source Project
4 ~
5 ~ Licensed under the Apache License, Version 2.0 (the "License");
6 ~ you may not use this file except in compliance with the License.
7 ~ You may obtain a copy of the License at
8 ~
9 ~ https://www.apache.org/licenses/LICENSE-2.0
10 ~
11 ~ Unless required by applicable law or agreed to in writing, software
12 ~ distributed under the License is distributed on an "AS IS" BASIS,
13 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ~ See the License for the specific language governing permissions and
15 ~ limitations under the License.
16 -->
17<vector xmlns:android="http://schemas.android.com/apk/res/android"
18 android:width="24dp"
19 android:height="24dp"
20 android:viewportWidth="74"
21 android:viewportHeight="74">
22 <path android:fillColor="#FFFFFF" android:fillType="evenOdd"
23 android:pathData="M73.1,37C73.1,17.0637 56.9373,0.9 37,0.9C17.0627,0.9 0.9,17.0637 0.9,37C0.9,56.9373 17.0627,73.1 37,73.1C56.9373,73.1 73.1,56.9373 73.1,37"
24 android:strokeColor="#00000000" android:strokeWidth="1"/>
25 <path android:fillColor="#58A0C4" android:fillType="evenOdd"
26 android:pathData="M67.4,37C67.4,53.7895 53.7895,67.4 37,67.4C20.2105,67.4 6.6,53.7895 6.6,37C6.6,20.2105 20.2105,6.6 37,6.6C53.7895,6.6 67.4,20.2105 67.4,37"
27 android:strokeColor="#00000000" android:strokeWidth="1"/>
28</vector>
diff --git a/utils/src/main/res/drawable/ic_shutter_normal.xml b/utils/src/main/res/drawable/ic_shutter_normal.xml
new file mode 100644
index 0000000..cb50026
--- /dev/null
+++ b/utils/src/main/res/drawable/ic_shutter_normal.xml
@@ -0,0 +1,28 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--
3 ~ Copyright 2020 The Android Open Source Project
4 ~
5 ~ Licensed under the Apache License, Version 2.0 (the "License");
6 ~ you may not use this file except in compliance with the License.
7 ~ You may obtain a copy of the License at
8 ~
9 ~ https://www.apache.org/licenses/LICENSE-2.0
10 ~
11 ~ Unless required by applicable law or agreed to in writing, software
12 ~ distributed under the License is distributed on an "AS IS" BASIS,
13 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ~ See the License for the specific language governing permissions and
15 ~ limitations under the License.
16 -->
17<vector xmlns:android="http://schemas.android.com/apk/res/android"
18 android:width="24dp"
19 android:height="24dp"
20 android:viewportWidth="74"
21 android:viewportHeight="74">
22 <path android:fillColor="#FFFFFF" android:fillType="evenOdd"
23 android:pathData="M73.1,37C73.1,17.0637 56.9373,0.9 37,0.9C17.0627,0.9 0.9,17.0637 0.9,37C0.9,56.9373 17.0627,73.1 37,73.1C56.9373,73.1 73.1,56.9373 73.1,37"
24 android:strokeColor="#00000000" android:strokeWidth="1"/>
25 <path android:fillColor="#CFD7DB" android:fillType="evenOdd"
26 android:pathData="M67.4,37C67.4,53.7895 53.7895,67.4 37,67.4C20.2105,67.4 6.6,53.7895 6.6,37C6.6,20.2105 20.2105,6.6 37,6.6C53.7895,6.6 67.4,20.2105 67.4,37"
27 android:strokeColor="#00000000" android:strokeWidth="1"/>
28</vector>
diff --git a/utils/src/main/res/drawable/ic_shutter_pressed.xml b/utils/src/main/res/drawable/ic_shutter_pressed.xml
new file mode 100644
index 0000000..9bf521d
--- /dev/null
+++ b/utils/src/main/res/drawable/ic_shutter_pressed.xml
@@ -0,0 +1,28 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!--
3 ~ Copyright 2020 The Android Open Source Project
4 ~
5 ~ Licensed under the Apache License, Version 2.0 (the "License");
6 ~ you may not use this file except in compliance with the License.
7 ~ You may obtain a copy of the License at
8 ~
9 ~ https://www.apache.org/licenses/LICENSE-2.0
10 ~
11 ~ Unless required by applicable law or agreed to in writing, software
12 ~ distributed under the License is distributed on an "AS IS" BASIS,
13 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ~ See the License for the specific language governing permissions and
15 ~ limitations under the License.
16 -->
17<vector xmlns:android="http://schemas.android.com/apk/res/android"
18 android:width="24dp"
19 android:height="24dp"
20 android:viewportWidth="74"
21 android:viewportHeight="74">
22 <path android:fillColor="#FFFFFF" android:fillType="evenOdd"
23 android:pathData="M73.1,37C73.1,17.0637 56.9373,0.9 37,0.9C17.0627,0.9 0.9,17.0637 0.9,37C0.9,56.9373 17.0627,73.1 37,73.1C56.9373,73.1 73.1,56.9373 73.1,37"
24 android:strokeColor="#00000000" android:strokeWidth="1"/>
25 <path android:fillColor="#58A0C4" android:fillType="evenOdd"
26 android:pathData="M67.4,37C67.4,53.7895 53.7895,67.4 37,67.4C20.2105,67.4 6.6,53.7895 6.6,37C6.6,20.2105 20.2105,6.6 37,6.6C53.7895,6.6 67.4,20.2105 67.4,37"
27 android:strokeColor="#00000000" android:strokeWidth="1"/>
28</vector>