├── .yaml
├── LICENSE
├── Model
├── style_predict_f16_256.tflite
├── style_predict_quantized_256.tflite
├── style_transfer_f16_384.tflite
└── style_transfer_quantized_384.tflite
├── README.md
├── Ruse.ipynb
├── android
├── README.md
├── app
│ ├── build.gradle
│ ├── download_model.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── assets
│ │ └── thumbnails
│ │ │ ├── style0.jpg
│ │ │ ├── style1.jpg
│ │ │ ├── style10.jpg
│ │ │ ├── style11.jpg
│ │ │ ├── style12.jpg
│ │ │ ├── style13.jpg
│ │ │ ├── style14.jpg
│ │ │ ├── style15.jpg
│ │ │ ├── style16.jpg
│ │ │ ├── style17.jpg
│ │ │ ├── style18.jpg
│ │ │ ├── style19.jpg
│ │ │ ├── style2.jpg
│ │ │ ├── style20.jpg
│ │ │ ├── style21.jpg
│ │ │ ├── style22.jpg
│ │ │ ├── style23.jpg
│ │ │ ├── style24.jpg
│ │ │ ├── style25.jpg
│ │ │ ├── style3.jpg
│ │ │ ├── style4.jpg
│ │ │ ├── style5.jpg
│ │ │ ├── style6.jpg
│ │ │ ├── style7.jpg
│ │ │ ├── style8.jpg
│ │ │ └── style9.jpg
│ │ ├── java
│ │ └── org
│ │ │ └── tensorflow
│ │ │ └── lite
│ │ │ └── examples
│ │ │ └── styletransfer
│ │ │ ├── ImageUtils.kt
│ │ │ ├── MLExecutionViewModel.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── ModelExecutionResult.kt
│ │ │ ├── StyleFragment.kt
│ │ │ ├── StyleRecyclerViewAdapter.kt
│ │ │ ├── StyleTransferModelExecutor.kt
│ │ │ └── camera
│ │ │ ├── AutoFitSurfaceView.kt
│ │ │ ├── CameraFragment.kt
│ │ │ ├── CameraSizes.kt
│ │ │ └── OrientationLiveData.kt
│ │ └── res
│ │ ├── anim
│ │ └── scale_anim.xml
│ │ ├── drawable-hdpi
│ │ └── ic_launcher.png
│ │ ├── drawable-mdpi
│ │ └── ic_launcher.png
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable-xxhdpi
│ │ ├── ic_launcher.png
│ │ ├── icn_chevron_down.png
│ │ ├── icn_chevron_up.png
│ │ ├── styles_square_thumb.jpg
│ │ ├── tfl2_logo.png
│ │ └── tfl2_logo_dark.png
│ │ ├── drawable
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_switchcam.xml
│ │ └── rounded_edge.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── bottom_sheet_layout.xml
│ │ ├── fragment_style_list.xml
│ │ └── image_item.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── placeholder.txt
└── settings.gradle
└── ios
├── Assets.xcassets
├── AppIcon.appiconset
│ ├── Contents.json
│ ├── Ruse1024.png
│ ├── Ruse120-1.png
│ ├── Ruse120.png
│ ├── Ruse152.png
│ ├── Ruse167.png
│ ├── Ruse180.png
│ ├── Ruse20.png
│ ├── Ruse29.png
│ ├── Ruse40-1.png
│ ├── Ruse40-2.png
│ ├── Ruse40.png
│ ├── Ruse58-1.png
│ ├── Ruse58.png
│ ├── Ruse60.png
│ ├── Ruse76.png
│ ├── Ruse80-1.png
│ ├── Ruse80.png
│ └── Ruse87.png
├── Contents.json
├── face1.imageset
│ ├── Contents.json
│ └── face1.jpg
├── face2.imageset
│ ├── Contents.json
│ └── face2.jpg
├── face3.imageset
│ ├── Contents.json
│ └── face3.jpg
├── face4.imageset
│ ├── Contents.json
│ └── face4.jpg
├── face5.imageset
│ ├── Contents.json
│ └── face5.jpg
├── photo_camera.imageset
│ ├── Contents.json
│ ├── photo_camera_2x.png
│ └── photo_camera_3x.png
├── photo_library.imageset
│ ├── Contents.json
│ ├── photo_library_2x.png
│ └── photo_library_3x.png
├── style0.imageset
│ ├── Contents.json
│ └── style0.jpg
├── style1.imageset
│ ├── Contents.json
│ └── style1.jpg
├── style10.imageset
│ ├── Contents.json
│ └── style10.jpg
├── style11.imageset
│ ├── Contents.json
│ └── style11.jpg
├── style12.imageset
│ ├── Contents.json
│ └── style12.jpg
├── style13.imageset
│ ├── Contents.json
│ └── style13.jpg
├── style14.imageset
│ ├── Contents.json
│ └── style14.jpg
├── style15.imageset
│ ├── Contents.json
│ └── style15.jpg
├── style16.imageset
│ ├── Contents.json
│ └── style16.jpg
├── style17.imageset
│ ├── Contents.json
│ └── style17.jpg
├── style18.imageset
│ ├── Contents.json
│ └── style18.jpg
├── style19.imageset
│ ├── Contents.json
│ └── style19.jpg
├── style2.imageset
│ ├── Contents.json
│ └── style2.jpg
├── style20.imageset
│ ├── Contents.json
│ └── style20.jpg
├── style21.imageset
│ ├── Contents.json
│ └── style21.jpg
├── style22.imageset
│ ├── Contents.json
│ └── style22.jpg
├── style23.imageset
│ ├── Contents.json
│ └── style23.jpg
├── style24.imageset
│ ├── Contents.json
│ └── style24.jpg
├── style25.imageset
│ ├── Contents.json
│ └── style25.jpg
├── style3.imageset
│ ├── Contents.json
│ └── style3.jpg
├── style4.imageset
│ ├── Contents.json
│ └── style4.jpg
├── style5.imageset
│ ├── Contents.json
│ └── style5.jpg
├── style6.imageset
│ ├── Contents.json
│ └── style6.jpg
├── style7.imageset
│ ├── Contents.json
│ └── style7.jpg
├── style8.imageset
│ ├── Contents.json
│ └── style8.jpg
├── style9.imageset
│ ├── Contents.json
│ └── style9.jpg
├── switch_camera.imageset
│ ├── Contents.json
│ ├── switch_camera_2x.png
│ └── switch_camera_3x.png
└── video_camera.imageset
│ ├── Contents.json
│ ├── video_camera_2x.png
│ └── video_camera_3x.png
├── Podfile
├── README.md
├── Resources
├── face1.jpg
├── face2.jpg
├── face3.jpg
├── face4.jpg
├── face5.jpg
├── style1.jpg
├── style2.jpg
├── style3.jpg
├── style4.jpg
├── style5.jpg
└── style6.jpg
├── Ruse.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
│ └── xcuserdata
│ │ └── mike.kiser.xcuserdatad
│ │ ├── UserInterfaceState.xcuserstate
│ │ └── WorkspaceSettings.xcsettings
└── xcuserdata
│ └── mike.kiser.xcuserdatad
│ └── xcschemes
│ ├── Ruse.xcscheme
│ └── xcschememanagement.plist
├── Ruse.xcworkspace
├── contents.xcworkspacedata
├── xcshareddata
│ └── IDEWorkspaceChecks.plist
└── xcuserdata
│ └── mike.kiser.xcuserdatad
│ └── UserInterfaceState.xcuserstate
└── Ruse
├── AppDelegate.swift
├── Base.lproj
├── LaunchScreen.storyboard
└── Main.storyboard
├── CameraViewController.swift
├── Info.plist
├── MLKitExtensions.swift
├── SimplexNoise.swift
├── StylePickerViewController.swift
├── StyleTransferer.swift
├── TFLiteExtension.swift
├── UIKitExtension.swift
├── UIUtilities.swift
└── ViewController.swift
/.yaml:
--------------------------------------------------------------------------------
1 | os: osx
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 derrumbe
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Model/style_predict_f16_256.tflite:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/Model/style_predict_f16_256.tflite
--------------------------------------------------------------------------------
/Model/style_predict_quantized_256.tflite:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/Model/style_predict_quantized_256.tflite
--------------------------------------------------------------------------------
/Model/style_transfer_f16_384.tflite:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/Model/style_transfer_f16_384.tflite
--------------------------------------------------------------------------------
/Model/style_transfer_quantized_384.tflite:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/Model/style_transfer_quantized_384.tflite
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ruse
2 |
3 |
4 |
5 | 
6 | 
7 | 
8 |
9 |
10 |
11 | 
12 |
13 |
14 | Mobile camera-based application that attempts to alter photos to preserve their utility to humans while making them unusable for facial recognition systems.
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | 
24 |
25 | ## Installation
26 |
27 | (1) Easy Method: Wait and download app from appropriate app store.
28 |
29 | (2) Download and run ios app via XCode (see Development setup for more detail)
30 |
31 |
32 | ## Usage example
33 |
34 | App is developed as a camera-based app, allowing for the modification of faces on new camera capture or current photos on camera roll with the goal of keeping them useful for social media and human consumption while making it difficult for facial recognition systems to utilize them accurately and effectively.
35 |
36 | This is done through a variety of methods based on previous research. Due to the limits of mobile and TensorFlow Lite, learning on the device itself is not possible—so some of the more advanced techniques are not yet possible (but research and development may yield future results.)
37 |
38 | Instructions on usage and a full video to come with first release.
39 |
40 | The Jupyter notebook illustrates the "arbitrary fast style" adversarial technique that is possible on mobile:
41 | 
42 |
43 | In the long term, this technique will be applied selectively (likey to segments of the photographs), along with perlin/simplex noise generated on a per image basis, a la https://github.com/kieranbrowne/camera-adversaria.
44 |
45 | A variety of methods are used to conceal the faces from commerical recognition systems (e.g. arbitrary file transfer, perlin noise introduction). Before saving to the camera roll or being used for online purposes, an onboard facility checks to see if faces can be detected.
46 |
47 |
48 |
49 | The effect of these adversarial approaches may then be checked wihtout needing to have network access.
50 |
51 | (Future versions plan on including a similar onboard estimation of how a sample recognition system fairs against the modified image (classification as opposed to merely detection.))
52 |
53 |
54 | ## Development setup
55 |
56 | Requirements: Xcode >12
57 |
58 | Pods installed as part of the process below: TensorFlow Lite (Swift nightly build) // GoogleMLKit // GPUImage3
59 |
60 | - Download ios and model (tflite models) directory
61 | - run "pod install" in the downloaded directory
62 | - Open Ruse.xcworkspace
63 |
64 |
65 | Installation to device is left as an excercise for the reader.
66 |
67 | Future Note: On-device learning is now possible with TFLite. This still may not be practical as the iterations might take too long to be useful for normal users.
68 |
69 | ## Release History
70 | * 0.2 (in Progress):
71 | * Redesign of interface to make it more consumer friendly
72 | * Addition of a new research technique (in process) - OPOM, one person / one mask
73 | * onboard training to create a class-wise mask
74 | * Thanks to all the beta testers for your feeback!
75 | * 0.1
76 | * Android code checking in (finally)
77 | * 0.0.1
78 | * Work in progress - iOS code working and checked in
79 |
80 | ## Furture work
81 | * web assembly offloading
82 | * adjustment (independently) of simplex noise and style transfer
83 | * onboard checking of facial classification
84 | (dependent on advancement of tensorflow lite, etc)
85 | * Redesign of app from PoC to actual, you know, usability
86 |
87 | ## Meta
88 |
89 |
90 | Distributed under the MIT license. See ``LICENSE`` for more information.
91 |
92 | [https://github.com/derrumbe/Ruse](https://github.com/derrumbe/Ruse)
93 |
94 |
95 |
96 |
97 | [travis-image]: https://travis-ci.com/derrumbe/Ruse.svg?branch=master
98 | [travis-url]: https://travis-ci.com/derrumbe/Ruse/
99 | [wiki]: https://github.com/derrumbe/Ruse/wiki
100 |
--------------------------------------------------------------------------------
/android/README.md:
--------------------------------------------------------------------------------
1 | # Ruse
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 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 |
17 | apply plugin: 'com.android.application'
18 | apply plugin: 'kotlin-android'
19 | apply plugin: 'kotlin-android-extensions'
20 | apply plugin: 'kotlin-kapt'
21 | apply plugin: 'de.undercouch.download'
22 |
23 | android {
24 | signingConfigs {
25 | release {
26 | storeFile file('/Users/lgusm/projects/keys/release')
27 | storePassword 'teste123'
28 | keyAlias = 'key0'
29 | keyPassword '123456'
30 | }
31 | }
32 | compileSdkVersion 29
33 | buildToolsVersion "29.0.2"
34 | defaultConfig {
35 | applicationId "org.tensorflow.lite.examples.styletransfersample"
36 | minSdkVersion 26
37 | targetSdkVersion 28
38 | versionCode 1
39 | versionName "1.0"
40 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
41 | }
42 | buildTypes {
43 |
44 | }
45 | aaptOptions {
46 | noCompress "tflite"
47 | }
48 | compileOptions {
49 | sourceCompatibility JavaVersion.VERSION_1_8
50 | targetCompatibility JavaVersion.VERSION_1_8
51 | }
52 | }
53 |
54 | // import DownloadModels task
55 | project.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'
56 |
57 | // Download default models; if you wish to use your own models then
58 | // place them in the "assets" directory and comment out this line.
59 | //apply from: "download_model.gradle"
60 |
61 | apply from: 'download_model.gradle'
62 |
63 | dependencies {
64 | implementation fileTree(dir: 'libs', include: ['*.jar'])
65 |
66 | def camerax_version = "1.0.0-alpha02"
67 | def lifecycle_version = "2.1.0-alpha02"
68 | def coroutines_version = "1.1.1"
69 | def tfl_version = "0.0.0-nightly-SNAPSHOT"
70 | def glide_version = "4.9.0"
71 |
72 | implementation 'androidx.appcompat:appcompat:1.1.0-rc01'
73 | implementation "androidx.camera:camera-core:${camerax_version}"
74 | implementation "androidx.camera:camera-camera2:${camerax_version}"
75 | implementation 'androidx.core:core-ktx:1.0.2'
76 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
77 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:${lifecycle_version}"
78 | implementation "androidx.lifecycle:lifecycle-extensions:${lifecycle_version}"
79 | implementation 'androidx.recyclerview:recyclerview:1.0.0'
80 | kapt "androidx.lifecycle:lifecycle-compiler:${lifecycle_version}"
81 |
82 | implementation "com.github.bumptech.glide:glide:${glide_version}"
83 | kapt "com.github.bumptech.glide:compiler:${glide_version}"
84 |
85 | implementation 'com.google.android.material:material:1.1.0-alpha08'
86 |
87 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${kotlin_version}"
88 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${coroutines_version}"
89 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:${coroutines_version}"
90 |
91 | implementation("org.tensorflow:tensorflow-lite:${tfl_version}") { changing = true }
92 | implementation("org.tensorflow:tensorflow-lite-gpu:${tfl_version}") { changing = true }
93 | }
94 |
--------------------------------------------------------------------------------
/android/app/download_model.gradle:
--------------------------------------------------------------------------------
1 | task downloadPredictInt8ModelFile(type: Download) {
2 | src 'https://tfhub.dev/google/lite-model/magenta/arbitrary-image-stylization-v1-256/int8/prediction/1?lite-format=tflite'
3 | dest project.ext.ASSET_DIR + '/style_predict_quantized_256.tflite'
4 | overwrite false
5 | }
6 |
7 | task downloadTransformInt8ModelFile(type: Download) {
8 | src 'https://tfhub.dev/google/lite-model/magenta/arbitrary-image-stylization-v1-256/int8/transfer/1?lite-format=tflite'
9 | dest project.ext.ASSET_DIR + '/style_transfer_quantized_384.tflite'
10 | overwrite false
11 | }
12 |
13 | task downloadPredictFloat16ModelFile(type: Download) {
14 | src 'https://tfhub.dev/google/lite-model/magenta/arbitrary-image-stylization-v1-256/fp16/prediction/1?lite-format=tflite'
15 | dest project.ext.ASSET_DIR + '/style_predict_f16_256.tflite'
16 | overwrite false
17 | }
18 |
19 | task downloadTransformFloat16ModelFile(type: Download) {
20 | src 'https://tfhub.dev/google/lite-model/magenta/arbitrary-image-stylization-v1-256/fp16/transfer/1?lite-format=tflite'
21 | dest project.ext.ASSET_DIR + '/style_transfer_f16_384.tflite'
22 | overwrite false
23 | }
24 |
25 | tasks.whenTaskAdded { task ->
26 | if ((task.name == 'assembleDebug') || (task.name == 'assembleRelease')) {
27 | task.dependsOn 'downloadPredictInt8ModelFile'
28 | task.dependsOn 'downloadTransformInt8ModelFile'
29 | task.dependsOn 'downloadPredictFloat16ModelFile'
30 | task.dependsOn 'downloadTransformFloat16ModelFile'
31 | }
32 | }
33 |
34 |
35 |
--------------------------------------------------------------------------------
/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style0.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style1.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style10.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style11.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style12.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style13.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style13.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style14.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style15.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style15.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style16.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style16.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style17.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style17.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style18.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style18.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style19.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style19.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style2.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style20.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style20.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style21.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style21.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style22.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style22.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style23.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style23.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style24.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style24.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style25.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style25.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style3.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style4.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style5.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style6.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style7.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style8.jpg
--------------------------------------------------------------------------------
/android/app/src/main/assets/thumbnails/style9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/assets/thumbnails/style9.jpg
--------------------------------------------------------------------------------
/android/app/src/main/java/org/tensorflow/lite/examples/styletransfer/ImageUtils.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 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 |
17 | package org.tensorflow.lite.examples.styletransfer
18 |
19 | import android.content.Context
20 | import android.graphics.Bitmap
21 | import android.graphics.BitmapFactory
22 | import android.graphics.Color
23 | import android.graphics.Matrix
24 | import android.graphics.RectF
25 | import androidx.exifinterface.media.ExifInterface
26 | import java.io.File
27 | import java.nio.ByteBuffer
28 | import java.nio.ByteOrder
29 |
30 | /**
31 | * Collection of image reading and manipulation utilities in the form of static functions.
32 | * TODO: this class should be moved to the common code in the future
33 | */
34 | abstract class ImageUtils {
35 | companion object {
36 |
37 | /**
38 | * Helper function used to convert an EXIF orientation enum into a transformation matrix
39 | * that can be applied to a bitmap.
40 | *
41 | * @param orientation - One of the constants from [ExifInterface]
42 | */
43 | private fun decodeExifOrientation(orientation: Int): Matrix {
44 | val matrix = Matrix()
45 |
46 | // Apply transformation corresponding to declared EXIF orientation
47 | when (orientation) {
48 | ExifInterface.ORIENTATION_NORMAL, ExifInterface.ORIENTATION_UNDEFINED -> Unit
49 | ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90F)
50 | ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180F)
51 | ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270F)
52 | ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> matrix.postScale(-1F, 1F)
53 | ExifInterface.ORIENTATION_FLIP_VERTICAL -> matrix.postScale(1F, -1F)
54 | ExifInterface.ORIENTATION_TRANSPOSE -> {
55 | matrix.postScale(-1F, 1F)
56 | matrix.postRotate(270F)
57 | }
58 | ExifInterface.ORIENTATION_TRANSVERSE -> {
59 | matrix.postScale(-1F, 1F)
60 | matrix.postRotate(90F)
61 | }
62 |
63 | // Error out if the EXIF orientation is invalid
64 | else -> throw IllegalArgumentException("Invalid orientation: $orientation")
65 | }
66 |
67 | // Return the resulting matrix
68 | return matrix
69 | }
70 |
71 | /**
72 | * sets the Exif orientation of an image.
73 | * this method is used to fix the exit of pictures taken by the camera
74 | *
75 | * @param filePath - The image file to change
76 | * @param value - the orientation of the file
77 | */
78 | fun setExifOrientation(
79 | filePath: String,
80 | value: String
81 | ) {
82 | val exif = ExifInterface(filePath)
83 | exif.setAttribute(
84 | ExifInterface.TAG_ORIENTATION, value
85 | )
86 | exif.saveAttributes()
87 | }
88 |
89 | /** Transforms rotation and mirroring information into one of the [ExifInterface] constants */
90 | fun computeExifOrientation(rotationDegrees: Int, mirrored: Boolean) = when {
91 | rotationDegrees == 0 && !mirrored -> ExifInterface.ORIENTATION_NORMAL
92 | rotationDegrees == 0 && mirrored -> ExifInterface.ORIENTATION_FLIP_HORIZONTAL
93 | rotationDegrees == 180 && !mirrored -> ExifInterface.ORIENTATION_ROTATE_180
94 | rotationDegrees == 180 && mirrored -> ExifInterface.ORIENTATION_FLIP_VERTICAL
95 | rotationDegrees == 270 && mirrored -> ExifInterface.ORIENTATION_TRANSVERSE
96 | rotationDegrees == 90 && !mirrored -> ExifInterface.ORIENTATION_ROTATE_90
97 | rotationDegrees == 90 && mirrored -> ExifInterface.ORIENTATION_TRANSPOSE
98 | rotationDegrees == 270 && mirrored -> ExifInterface.ORIENTATION_ROTATE_270
99 | rotationDegrees == 270 && !mirrored -> ExifInterface.ORIENTATION_TRANSVERSE
100 | else -> ExifInterface.ORIENTATION_UNDEFINED
101 | }
102 |
103 | /**
104 | * Decode a bitmap from a file and apply the transformations described in its EXIF data
105 | *
106 | * @param file - The image file to be read using [BitmapFactory.decodeFile]
107 | */
108 | fun decodeBitmap(file: File): Bitmap {
109 | // First, decode EXIF data and retrieve transformation matrix
110 | val exif = ExifInterface(file.absolutePath)
111 | val transformation =
112 | decodeExifOrientation(
113 | exif.getAttributeInt(
114 | ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_ROTATE_90
115 | )
116 | )
117 |
118 | // Read bitmap using factory methods, and transform it using EXIF data
119 | val bitmap = BitmapFactory.decodeFile(file.absolutePath)
120 | return Bitmap.createBitmap(
121 | BitmapFactory.decodeFile(file.absolutePath),
122 | 0, 0, bitmap.width, bitmap.height, transformation, true
123 | )
124 | }
125 |
126 | fun scaleBitmapAndKeepRatio(
127 | targetBmp: Bitmap,
128 | reqHeightInPixels: Int,
129 | reqWidthInPixels: Int
130 | ): Bitmap {
131 | if (targetBmp.height == reqHeightInPixels && targetBmp.width == reqWidthInPixels) {
132 | return targetBmp
133 | }
134 | val matrix = Matrix()
135 | matrix.setRectToRect(
136 | RectF(
137 | 0f, 0f,
138 | targetBmp.width.toFloat(),
139 | targetBmp.width.toFloat()
140 | ),
141 | RectF(
142 | 0f, 0f,
143 | reqWidthInPixels.toFloat(),
144 | reqHeightInPixels.toFloat()
145 | ),
146 | Matrix.ScaleToFit.FILL
147 | )
148 | return Bitmap.createBitmap(
149 | targetBmp, 0, 0,
150 | targetBmp.width,
151 | targetBmp.width, matrix, true
152 | )
153 | }
154 |
155 | fun bitmapToByteBuffer(
156 | bitmapIn: Bitmap,
157 | width: Int,
158 | height: Int,
159 | mean: Float = 0.0f,
160 | std: Float = 255.0f
161 | ): ByteBuffer {
162 | val bitmap = scaleBitmapAndKeepRatio(bitmapIn, width, height)
163 | val inputImage = ByteBuffer.allocateDirect(1 * width * height * 3 * 4)
164 | inputImage.order(ByteOrder.nativeOrder())
165 | inputImage.rewind()
166 |
167 | val intValues = IntArray(width * height)
168 | bitmap.getPixels(intValues, 0, width, 0, 0, width, height)
169 | var pixel = 0
170 | for (y in 0 until height) {
171 | for (x in 0 until width) {
172 | val value = intValues[pixel++]
173 |
174 | // Normalize channel values to [-1.0, 1.0]. This requirement varies by
175 | // model. For example, some models might require values to be normalized
176 | // to the range [0.0, 1.0] instead.
177 | inputImage.putFloat(((value shr 16 and 0xFF) - mean) / std)
178 | inputImage.putFloat(((value shr 8 and 0xFF) - mean) / std)
179 | inputImage.putFloat(((value and 0xFF) - mean) / std)
180 | }
181 | }
182 |
183 | inputImage.rewind()
184 | return inputImage
185 | }
186 |
187 | fun createEmptyBitmap(imageWidth: Int, imageHeigth: Int, color: Int = 0): Bitmap {
188 | val ret = Bitmap.createBitmap(imageWidth, imageHeigth, Bitmap.Config.RGB_565)
189 | if (color != 0) {
190 | ret.eraseColor(color)
191 | }
192 | return ret
193 | }
194 |
195 | fun loadBitmapFromResources(context: Context, path: String): Bitmap {
196 | val inputStream = context.assets.open(path)
197 | return BitmapFactory.decodeStream(inputStream)
198 | }
199 |
200 | fun convertArrayToBitmap(
201 | imageArray: Array>>,
202 | imageWidth: Int,
203 | imageHeight: Int
204 | ): Bitmap {
205 | val conf = Bitmap.Config.ARGB_8888 // see other conf types
206 | val styledImage = Bitmap.createBitmap(imageWidth, imageHeight, conf)
207 |
208 | for (x in imageArray[0].indices) {
209 | for (y in imageArray[0][0].indices) {
210 | val color = Color.rgb(
211 | ((imageArray[0][x][y][0] * 255).toInt()),
212 | ((imageArray[0][x][y][1] * 255).toInt()),
213 | (imageArray[0][x][y][2] * 255).toInt()
214 | )
215 |
216 | // this y, x is in the correct order!!!
217 | styledImage.setPixel(y, x, color)
218 | }
219 | }
220 | return styledImage
221 | }
222 | }
223 | }
224 |
--------------------------------------------------------------------------------
/android/app/src/main/java/org/tensorflow/lite/examples/styletransfer/MLExecutionViewModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 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 |
17 | package org.tensorflow.lite.examples.styletransfer
18 |
19 | import androidx.lifecycle.ViewModel
20 | import android.content.Context
21 | import androidx.lifecycle.LiveData
22 | import androidx.lifecycle.MutableLiveData
23 | import kotlinx.coroutines.CoroutineScope
24 | import kotlinx.coroutines.ExecutorCoroutineDispatcher
25 | import kotlinx.coroutines.Job
26 | import kotlinx.coroutines.launch
27 |
28 | class MLExecutionViewModel : ViewModel() {
29 |
30 | private val _styledBitmap = MutableLiveData()
31 |
32 | val styledBitmap: LiveData
33 | get() = _styledBitmap
34 |
35 | private val viewModelJob = Job()
36 | private val viewModelScope = CoroutineScope(viewModelJob)
37 |
38 | /*
39 | Older version of onApplyStyle; static , fixed paths and a few other older approaches
40 | fun onApplyStyle(
41 | context: Context,
42 | contentFileStaticPath: String,
43 | styleFileStaticPath: String,
44 | styleTransferModelExecutor: StyleTransferModelExecutor,
45 | inferenceThread: ExecutorCoroutineDispatcher
46 | ) {
47 | viewModelScope.launch(inferenceThread) {
48 | val result = styleTransferStaticModelExecutor.execute(contentFilePath, styleFilePath, context)
49 | _styledBitmap.postValue(result)
50 | }
51 | }*/
52 |
53 | fun onApplyStyle(
54 | context: Context,
55 | contentFilePath: String,
56 | styleFilePath: String,
57 | styleTransferModelExecutor: StyleTransferModelExecutor,
58 | inferenceThread: ExecutorCoroutineDispatcher
59 | ) {
60 | viewModelScope.launch(inferenceThread) {
61 | val result = styleTransferModelExecutor.execute(contentFilePath, styleFilePath, context)
62 | _styledBitmap.postValue(result)
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/android/app/src/main/java/org/tensorflow/lite/examples/styletransfer/MainActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 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 |
17 | package org.tensorflow.lite.examples.styletransfer
18 |
19 | import android.Manifest
20 | import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory
21 | import android.content.pm.PackageManager
22 | import android.graphics.Bitmap
23 | import android.hardware.camera2.CameraCharacteristics
24 | import android.os.Bundle
25 | import android.os.Process
26 | import androidx.appcompat.app.AppCompatActivity
27 | import androidx.appcompat.widget.Toolbar
28 | import android.util.Log
29 | import android.view.View
30 | import android.view.animation.AnimationUtils
31 | import android.view.animation.BounceInterpolator
32 | import android.widget.Button
33 | import android.widget.FrameLayout
34 | import android.widget.HorizontalScrollView
35 | import android.widget.ImageButton
36 | import android.widget.ImageView
37 | import android.widget.ProgressBar
38 | import android.widget.Switch
39 | import android.widget.TextView
40 | import android.widget.Toast
41 | import androidx.core.app.ActivityCompat
42 | import androidx.lifecycle.Observer
43 | import com.bumptech.glide.Glide
44 | import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
45 | import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
46 | import com.bumptech.glide.request.RequestOptions
47 | import java.io.File
48 | import java.nio.charset.Charset
49 | import java.security.MessageDigest
50 | import java.util.concurrent.Executors
51 | import kotlinx.coroutines.MainScope
52 | import kotlinx.coroutines.asCoroutineDispatcher
53 | import kotlinx.coroutines.async
54 | import org.tensorflow.lite.examples.styletransfer.camera.CameraFragment
55 |
56 | // This is an arbitrary number we are using to keep tab of the permission
57 | // request. Where an app has multiple context for requesting permission,
58 | // this can help differentiate the different contexts
59 | private const val REQUEST_CODE_PERMISSIONS = 10
60 |
61 | // This is an array of all the permission specified in the manifest
62 | private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
63 |
64 | private const val TAG = "MainActivity"
65 |
66 | class MainActivity :
67 | AppCompatActivity(),
68 | StyleFragment.OnListFragmentInteractionListener,
69 | CameraFragment.OnCaptureFinished {
70 |
71 | private var isRunningModel = false
72 | private val stylesFragment: StyleFragment = StyleFragment()
73 | private var selectedStyle: String = ""
74 |
75 | private lateinit var cameraFragment: CameraFragment
76 | private lateinit var viewModel: MLExecutionViewModel
77 | private lateinit var viewFinder: FrameLayout
78 | private lateinit var resultImageView: ImageView
79 | private lateinit var originalImageView: ImageView
80 | private lateinit var styleImageView: ImageView
81 | private lateinit var rerunButton: Button
82 | private lateinit var captureButton: ImageButton
83 | private lateinit var progressBar: ProgressBar
84 | private lateinit var horizontalScrollView: HorizontalScrollView
85 |
86 | private var lastSavedFile = ""
87 | private var useGPU = false
88 | private lateinit var styleTransferModelExecutor: StyleTransferModelExecutor
89 | private val inferenceThread = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
90 | private val mainScope = MainScope()
91 |
92 | private var lensFacing = CameraCharacteristics.LENS_FACING_FRONT
93 |
94 | override fun onCreate(savedInstanceState: Bundle?) {
95 | super.onCreate(savedInstanceState)
96 | setContentView(R.layout.activity_main)
97 |
98 | val toolbar: Toolbar = findViewById(R.id.toolbar)
99 | setSupportActionBar(toolbar)
100 | supportActionBar?.setDisplayShowTitleEnabled(false)
101 |
102 | viewFinder = findViewById(R.id.view_finder)
103 | resultImageView = findViewById(R.id.result_imageview)
104 | originalImageView = findViewById(R.id.original_imageview)
105 | styleImageView = findViewById(R.id.style_imageview)
106 | captureButton = findViewById(R.id.capture_button)
107 | progressBar = findViewById(R.id.progress_circular)
108 | horizontalScrollView = findViewById(R.id.horizontal_scroll_view)
109 | val useGpuSwitch: Switch = findViewById(R.id.switch_use_gpu)
110 |
111 | // Request camera permissions
112 | if (allPermissionsGranted()) {
113 | addCameraFragment()
114 | } else {
115 | ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
116 | }
117 |
118 | viewModel = AndroidViewModelFactory(application).create(MLExecutionViewModel::class.java)
119 |
120 | viewModel.styledBitmap.observe(
121 | this,
122 | Observer { resultImage ->
123 | if (resultImage != null) {
124 | updateUIWithResults(resultImage)
125 | }
126 | }
127 | )
128 |
129 | mainScope.async(inferenceThread) {
130 | styleTransferModelExecutor = StyleTransferModelExecutor(this@MainActivity, useGPU)
131 | Log.d(TAG, "Executor created")
132 | }
133 |
134 | useGpuSwitch.setOnCheckedChangeListener { _, isChecked ->
135 | useGPU = isChecked
136 | // Disable control buttons to avoid running model before initialization
137 | enableControls(false)
138 |
139 | // Reinitialize TF Lite models with new GPU setting
140 | mainScope.async(inferenceThread) {
141 | styleTransferModelExecutor.close()
142 | styleTransferModelExecutor = StyleTransferModelExecutor(this@MainActivity, useGPU)
143 |
144 | // Re-enable control buttons
145 | runOnUiThread { enableControls(true) }
146 | }
147 | }
148 |
149 | rerunButton = findViewById(R.id.rerun_button)
150 | rerunButton.setOnClickListener { startRunningModel() }
151 |
152 | styleImageView.setOnClickListener {
153 | if (!isRunningModel) {
154 | stylesFragment.show(supportFragmentManager, "StylesFragment")
155 | }
156 | }
157 |
158 | progressBar.visibility = View.INVISIBLE
159 | lastSavedFile = getLastTakenPicture()
160 | setImageView(originalImageView, lastSavedFile)
161 |
162 | animateCameraButton()
163 | setupControls()
164 | enableControls(true)
165 |
166 | Log.d(TAG, "finished onCreate!!")
167 | }
168 |
169 | private fun animateCameraButton() {
170 | val animation = AnimationUtils.loadAnimation(this, R.anim.scale_anim)
171 | animation.interpolator = BounceInterpolator()
172 | captureButton.animation = animation
173 | captureButton.animation.start()
174 | }
175 |
176 | private fun setImageView(imageView: ImageView, image: Bitmap) {
177 | Glide.with(baseContext).load(image).override(512, 512).fitCenter().into(imageView)
178 | }
179 |
180 | private fun setImageView(imageView: ImageView, imagePath: String) {
181 | Glide.with(baseContext)
182 | .asBitmap()
183 | .load(imagePath)
184 | .override(512, 512)
185 | .apply(RequestOptions().transform(CropTop()))
186 | .into(imageView)
187 | }
188 |
189 | private fun updateUIWithResults(modelExecutionResult: ModelExecutionResult) {
190 | progressBar.visibility = View.INVISIBLE
191 | resultImageView.visibility = View.VISIBLE
192 | setImageView(resultImageView, modelExecutionResult.styledImage)
193 | val logText: TextView = findViewById(R.id.log_view)
194 | logText.text = modelExecutionResult.executionLog
195 | enableControls(true)
196 | horizontalScrollView.fullScroll(HorizontalScrollView.FOCUS_RIGHT)
197 | }
198 |
199 | private fun enableControls(enable: Boolean) {
200 | isRunningModel = !enable
201 | rerunButton.isEnabled = enable
202 | captureButton.isEnabled = enable
203 | }
204 |
205 | private fun setupControls() {
206 | captureButton.setOnClickListener {
207 | it.clearAnimation()
208 | cameraFragment.takePicture()
209 | }
210 |
211 | findViewById(R.id.toggle_button).setOnClickListener {
212 | lensFacing =
213 | if (lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
214 | CameraCharacteristics.LENS_FACING_FRONT
215 | } else {
216 | CameraCharacteristics.LENS_FACING_BACK
217 | }
218 | cameraFragment.setFacingCamera(lensFacing)
219 | addCameraFragment()
220 | }
221 | }
222 |
223 | private fun addCameraFragment() {
224 | cameraFragment = CameraFragment.newInstance()
225 | cameraFragment.setFacingCamera(lensFacing)
226 | supportFragmentManager.popBackStack()
227 | supportFragmentManager.beginTransaction().replace(R.id.view_finder, cameraFragment).commit()
228 | }
229 |
230 | /**
231 | * Process result from permission request dialog box, has the request been granted? If yes, start
232 | * Camera. Otherwise display a toast
233 | */
234 | override fun onRequestPermissionsResult(
235 | requestCode: Int,
236 | permissions: Array,
237 | grantResults: IntArray
238 | ) {
239 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
240 | if (requestCode == REQUEST_CODE_PERMISSIONS) {
241 | if (allPermissionsGranted()) {
242 | addCameraFragment()
243 | viewFinder.post { setupControls() }
244 | } else {
245 | Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_SHORT).show()
246 | finish()
247 | }
248 | }
249 | }
250 |
251 | /** Check if all permission specified in the manifest have been granted */
252 | private fun allPermissionsGranted() =
253 | REQUIRED_PERMISSIONS.all {
254 | checkPermission(it, Process.myPid(), Process.myUid()) == PackageManager.PERMISSION_GRANTED
255 | }
256 |
257 | override fun onCaptureFinished(file: File) {
258 | val msg = "Photo capture succeeded: ${file.absolutePath}"
259 | Log.d(TAG, msg)
260 |
261 | lastSavedFile = file.absolutePath
262 | setImageView(originalImageView, lastSavedFile)
263 | }
264 |
265 | // And update once new picture is taken?
266 | // Alternatively we can provide user an ability to select any of taken photos
267 | private fun getLastTakenPicture(): String {
268 | val directory = baseContext.filesDir // externalMediaDirs.first()
269 | var files =
270 | directory.listFiles()?.filter { file -> file.absolutePath.endsWith(".jpg") }?.sorted()
271 | if (files == null || files.isEmpty()) {
272 | Log.d(TAG, "there is no previous saved file")
273 | return ""
274 | }
275 |
276 | val file = files.last()
277 | Log.d(TAG, "lastsavedfile: " + file.absolutePath)
278 | return file.absolutePath
279 | }
280 |
281 | override fun onListFragmentInteraction(item: String) {
282 | Log.d(TAG, item)
283 | selectedStyle = item
284 | stylesFragment.dismiss()
285 |
286 | startRunningModel()
287 | }
288 |
289 | private fun getUriFromAssetThumb(thumb: String): String {
290 | return "file:///android_asset/thumbnails/$thumb"
291 | }
292 |
293 | private fun startRunningModel() {
294 | if (!isRunningModel && lastSavedFile.isNotEmpty() && selectedStyle.isNotEmpty()) {
295 | val chooseStyleLabel: TextView = findViewById(R.id.choose_style_text_view)
296 | chooseStyleLabel.visibility = View.GONE
297 | enableControls(false)
298 | setImageView(styleImageView, getUriFromAssetThumb(selectedStyle))
299 | resultImageView.visibility = View.INVISIBLE
300 | progressBar.visibility = View.VISIBLE
301 | viewModel.onApplyStyle(
302 | baseContext,
303 | lastSavedFile,
304 | selectedStyle,
305 | styleTransferModelExecutor,
306 | inferenceThread
307 | )
308 | } else {
309 | Toast.makeText(this, "Previous Model still running", Toast.LENGTH_SHORT).show()
310 | }
311 | }
312 |
313 | // this transformation is necessary to show the top square of the image as the model
314 | // will work on this part only, making the preview and the result show the same base
315 | // NB: need to extend this model a bit to incorporate higher-res images
316 | class CropTop : BitmapTransformation() {
317 | override fun transform(
318 | pool: BitmapPool,
319 | toTransform: Bitmap,
320 | outWidth: Int,
321 | outHeight: Int
322 | ): Bitmap {
323 | return if (toTransform.width == outWidth && toTransform.height == outHeight) {
324 | toTransform
325 | } else ImageUtils.scaleBitmapAndKeepRatio(toTransform, outWidth, outHeight)
326 | }
327 |
328 | override fun equals(other: Any?): Boolean {
329 | return other is CropTop
330 | }
331 |
332 | override fun hashCode(): Int {
333 | return ID.hashCode()
334 | }
335 |
336 | override fun updateDiskCacheKey(messageDigest: MessageDigest) {
337 | messageDigest.update(ID_BYTES)
338 | }
339 |
340 | companion object {
341 | private const val ID = "org.tensorflow.lite.examples.styletransfer.CropTop"
342 | private val ID_BYTES = ID.toByteArray(Charset.forName("UTF-8"))
343 | }
344 | }
345 | }
346 |
--------------------------------------------------------------------------------
/android/app/src/main/java/org/tensorflow/lite/examples/styletransfer/ModelExecutionResult.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 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 |
17 | package org.tensorflow.lite.examples.styletransfer
18 |
19 | import android.graphics.Bitmap
20 |
21 | @SuppressWarnings("GoodTime")
22 | data class ModelExecutionResult(
23 | val styledImage: Bitmap,
24 | val preProcessTime: Long = 0L,
25 | val stylePredictTime: Long = 0L,
26 | val styleTransferTime: Long = 0L,
27 | val postProcessTime: Long = 0L,
28 | val totalExecutionTime: Long = 0L,
29 | val executionLog: String = "",
30 | val errorMessage: String = ""
31 | )
32 |
--------------------------------------------------------------------------------
/android/app/src/main/java/org/tensorflow/lite/examples/styletransfer/StyleFragment.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 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 |
17 | package org.tensorflow.lite.examples.styletransfer
18 |
19 | import android.content.Context
20 | import android.os.Bundle
21 | import androidx.fragment.app.DialogFragment
22 | import androidx.recyclerview.widget.GridLayoutManager
23 | import androidx.recyclerview.widget.RecyclerView
24 | import android.view.LayoutInflater
25 | import android.view.View
26 | import android.view.ViewGroup
27 |
28 | /**
29 | * A fragment representing a list of available Styles to apply
30 | * Activities containing this fragment MUST implement the
31 | * [StyleFragment.OnListFragmentInteractionListener] interface.
32 | */
33 | class StyleFragment : DialogFragment() {
34 |
35 | private var listener: OnListFragmentInteractionListener? = null
36 |
37 | override fun onCreateView(
38 | inflater: LayoutInflater,
39 | container: ViewGroup?,
40 | savedInstanceState: Bundle?
41 | ): View? {
42 | val view = inflater.inflate(R.layout.fragment_style_list, container, false)
43 |
44 | val styles = ArrayList()
45 | styles.addAll(activity!!.assets.list("thumbnails")!!)
46 |
47 | // Set the adapter
48 | if (view is RecyclerView) {
49 | with(view) {
50 | layoutManager = GridLayoutManager(context, 3)
51 | adapter = StyleRecyclerViewAdapter(styles, context, listener)
52 | }
53 | }
54 | return view
55 | }
56 |
57 | override fun onAttach(context: Context) {
58 | super.onAttach(context)
59 | if (context is OnListFragmentInteractionListener) {
60 | listener = context
61 | } else {
62 | throw RuntimeException("$context must implement OnListFragmentInteractionListener")
63 | }
64 | }
65 |
66 | override fun onDetach() {
67 | super.onDetach()
68 | listener = null
69 | }
70 |
71 | /**
72 | * This interface must be implemented by activities that contain this
73 | * fragment to allow an interaction in this fragment to be communicated
74 | * to the activity and potentially other fragments contained in that
75 | * activity.
76 | *
77 | *
78 | * See the Android Training lesson
79 | * [Communicating with Other Fragments](http://developer.android.com/training/basics/fragments/communicating.html)
80 | * for more information.
81 | */
82 | interface OnListFragmentInteractionListener {
83 | fun onListFragmentInteraction(item: String)
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/android/app/src/main/java/org/tensorflow/lite/examples/styletransfer/StyleRecyclerViewAdapter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 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 |
17 | package org.tensorflow.lite.examples.styletransfer
18 |
19 | import android.content.Context
20 | import android.net.Uri
21 | import androidx.recyclerview.widget.RecyclerView
22 | import android.view.LayoutInflater
23 | import android.view.View
24 | import android.view.ViewGroup
25 | import android.widget.ImageView
26 | import com.bumptech.glide.Glide
27 | import org.tensorflow.lite.examples.styletransfer.StyleFragment.OnListFragmentInteractionListener
28 |
29 | /**
30 | * [StyleRecyclerViewAdapter] that can display a [StyleItem] and makes a call to the
31 | * specified [OnListFragmentInteractionListener].
32 | */
33 | class StyleRecyclerViewAdapter(
34 | private val styles: List,
35 | private val context: Context,
36 | private val mListener: OnListFragmentInteractionListener?
37 | ) : RecyclerView.Adapter() {
38 |
39 | private val mOnClickListener: View.OnClickListener
40 |
41 | init {
42 | mOnClickListener = View.OnClickListener { v ->
43 | val item = v.tag as String
44 | // Notify the active callbacks interface (the activity, if the fragment is attached to
45 | // one) that an item has been selected.
46 | mListener?.onListFragmentInteraction(item)
47 | }
48 | }
49 |
50 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StyleItemViewHolder {
51 | val view = LayoutInflater.from(parent.context)
52 | .inflate(R.layout.image_item, parent, false)
53 | return StyleItemViewHolder(view)
54 | }
55 |
56 | override fun onBindViewHolder(holder: StyleItemViewHolder, position: Int) {
57 | val imagePath = styles[position]
58 |
59 | Glide.with(context)
60 | .load(Uri.parse("file:///android_asset/thumbnails/$imagePath"))
61 | .centerInside()
62 | .into(holder.imageView)
63 |
64 | with(holder.mView) {
65 | tag = imagePath
66 | setOnClickListener(mOnClickListener)
67 | }
68 | }
69 |
70 | override fun getItemCount(): Int = styles.size
71 |
72 | inner class StyleItemViewHolder(val mView: View) : RecyclerView.ViewHolder(mView) {
73 | var imageView: ImageView = mView.findViewById(R.id.image_view)
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/android/app/src/main/java/org/tensorflow/lite/examples/styletransfer/StyleTransferModelExecutor.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 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 |
17 | package org.tensorflow.lite.examples.styletransfer
18 |
19 | import android.content.Context
20 | import android.os.SystemClock
21 | import android.util.Log
22 | import java.io.File
23 | import java.io.FileInputStream
24 | import java.io.IOException
25 | import java.nio.MappedByteBuffer
26 | import java.nio.channels.FileChannel
27 | import kotlin.collections.set
28 | import org.tensorflow.lite.Interpreter
29 | import org.tensorflow.lite.gpu.GpuDelegate
30 |
31 | @SuppressWarnings("GoodTime")
32 | class StyleTransferModelExecutor(
33 | context: Context,
34 | private var useGPU: Boolean = false
35 | ) {
36 | private var gpuDelegate: GpuDelegate? = null
37 | private var numberThreads = 4
38 |
39 | private val interpreterPredict: Interpreter
40 | private val interpreterTransform: Interpreter
41 |
42 | private var fullExecutionTime = 0L
43 | private var preProcessTime = 0L
44 | private var stylePredictTime = 0L
45 | private var styleTransferTime = 0L
46 | private var postProcessTime = 0L
47 |
48 | init {
49 | if (useGPU) {
50 | interpreterPredict = getInterpreter(context, STYLE_PREDICT_FLOAT16_MODEL, true)
51 | interpreterTransform = getInterpreter(context, STYLE_TRANSFER_FLOAT16_MODEL, true)
52 | } else {
53 | interpreterPredict = getInterpreter(context, STYLE_PREDICT_INT8_MODEL, false)
54 | interpreterTransform = getInterpreter(context, STYLE_TRANSFER_INT8_MODEL, false)
55 | }
56 | }
57 |
58 | companion object {
59 | private const val TAG = "StyleTransferMExec"
60 | private const val STYLE_IMAGE_SIZE = 256
61 | private const val CONTENT_IMAGE_SIZE = 384
62 | private const val BOTTLENECK_SIZE = 100
63 | private const val STYLE_PREDICT_INT8_MODEL = "style_predict_quantized_256.tflite"
64 | private const val STYLE_TRANSFER_INT8_MODEL = "style_transfer_quantized_384.tflite"
65 | private const val STYLE_PREDICT_FLOAT16_MODEL = "style_predict_f16_256.tflite"
66 | private const val STYLE_TRANSFER_FLOAT16_MODEL = "style_transfer_f16_384.tflite"
67 | }
68 |
69 | fun execute(
70 | contentImagePath: String,
71 | styleImageName: String,
72 | context: Context
73 | ): ModelExecutionResult {
74 | try {
75 | Log.i(TAG, "running models")
76 |
77 | fullExecutionTime = SystemClock.uptimeMillis()
78 | preProcessTime = SystemClock.uptimeMillis()
79 |
80 | val contentImage = ImageUtils.decodeBitmap(File(contentImagePath))
81 | val contentArray =
82 | ImageUtils.bitmapToByteBuffer(contentImage, CONTENT_IMAGE_SIZE, CONTENT_IMAGE_SIZE)
83 | val styleBitmap =
84 | ImageUtils.loadBitmapFromResources(context, "thumbnails/$styleImageName")
85 | val input = ImageUtils.bitmapToByteBuffer(styleBitmap, STYLE_IMAGE_SIZE, STYLE_IMAGE_SIZE)
86 |
87 | val inputsForPredict = arrayOf(input)
88 | val outputsForPredict = HashMap()
89 | val styleBottleneck = Array(1) { Array(1) { Array(1) { FloatArray(BOTTLENECK_SIZE) } } }
90 | outputsForPredict[0] = styleBottleneck
91 | preProcessTime = SystemClock.uptimeMillis() - preProcessTime
92 |
93 | stylePredictTime = SystemClock.uptimeMillis()
94 | // The results of this inference could be reused given the style does not change
95 | // That would be a good practice in case this was applied to a video stream.
96 | interpreterPredict.runForMultipleInputsOutputs(inputsForPredict, outputsForPredict)
97 | stylePredictTime = SystemClock.uptimeMillis() - stylePredictTime
98 | Log.d(TAG, "Style Predict Time to run: $stylePredictTime")
99 |
100 | val inputsForStyleTransfer = arrayOf(contentArray, styleBottleneck)
101 | val outputsForStyleTransfer = HashMap()
102 | val outputImage =
103 | Array(1) { Array(CONTENT_IMAGE_SIZE) { Array(CONTENT_IMAGE_SIZE) { FloatArray(3) } } }
104 | outputsForStyleTransfer[0] = outputImage
105 |
106 | styleTransferTime = SystemClock.uptimeMillis()
107 | interpreterTransform.runForMultipleInputsOutputs(
108 | inputsForStyleTransfer,
109 | outputsForStyleTransfer
110 | )
111 | styleTransferTime = SystemClock.uptimeMillis() - styleTransferTime
112 | Log.d(TAG, "Style apply Time to run: $styleTransferTime")
113 |
114 | postProcessTime = SystemClock.uptimeMillis()
115 | var styledImage =
116 | ImageUtils.convertArrayToBitmap(outputImage, CONTENT_IMAGE_SIZE, CONTENT_IMAGE_SIZE)
117 | postProcessTime = SystemClock.uptimeMillis() - postProcessTime
118 |
119 | fullExecutionTime = SystemClock.uptimeMillis() - fullExecutionTime
120 | Log.d(TAG, "Time to run everything: $fullExecutionTime")
121 |
122 | return ModelExecutionResult(
123 | styledImage,
124 | preProcessTime,
125 | stylePredictTime,
126 | styleTransferTime,
127 | postProcessTime,
128 | fullExecutionTime,
129 | formatExecutionLog()
130 | )
131 | } catch (e: Exception) {
132 | val exceptionLog = "something went wrong: ${e.message}"
133 | Log.d(TAG, exceptionLog)
134 |
135 | val emptyBitmap =
136 | ImageUtils.createEmptyBitmap(
137 | CONTENT_IMAGE_SIZE,
138 | CONTENT_IMAGE_SIZE
139 | )
140 | return ModelExecutionResult(
141 | emptyBitmap, errorMessage = e.message!!
142 | )
143 | }
144 | }
145 |
146 | @Throws(IOException::class)
147 | private fun loadModelFile(context: Context, modelFile: String): MappedByteBuffer {
148 | val fileDescriptor = context.assets.openFd(modelFile)
149 | val inputStream = FileInputStream(fileDescriptor.fileDescriptor)
150 | val fileChannel = inputStream.channel
151 | val startOffset = fileDescriptor.startOffset
152 | val declaredLength = fileDescriptor.declaredLength
153 | val retFile = fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength)
154 | fileDescriptor.close()
155 | return retFile
156 | }
157 |
158 | @Throws(IOException::class)
159 | private fun getInterpreter(
160 | context: Context,
161 | modelName: String,
162 | useGpu: Boolean = false
163 | ): Interpreter {
164 | val tfliteOptions = Interpreter.Options()
165 | tfliteOptions.setNumThreads(numberThreads)
166 |
167 | gpuDelegate = null
168 | if (useGpu) {
169 | gpuDelegate = GpuDelegate()
170 | tfliteOptions.addDelegate(gpuDelegate)
171 | }
172 |
173 | tfliteOptions.setNumThreads(numberThreads)
174 | return Interpreter(loadModelFile(context, modelName), tfliteOptions)
175 | }
176 |
177 | private fun formatExecutionLog(): String {
178 | val sb = StringBuilder()
179 | sb.append("Input Image Size: $CONTENT_IMAGE_SIZE x $CONTENT_IMAGE_SIZE\n")
180 | sb.append("GPU enabled: $useGPU\n")
181 | sb.append("Number of threads: $numberThreads\n")
182 | sb.append("Pre-process execution time: $preProcessTime ms\n")
183 | sb.append("Predicting style execution time: $stylePredictTime ms\n")
184 | sb.append("Transferring style execution time: $styleTransferTime ms\n")
185 | sb.append("Post-process execution time: $postProcessTime ms\n")
186 | sb.append("Full execution time: $fullExecutionTime ms\n")
187 | return sb.toString()
188 | }
189 |
190 | fun close() {
191 | interpreterPredict.close()
192 | interpreterTransform.close()
193 | if (gpuDelegate != null) {
194 | gpuDelegate!!.close()
195 | }
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/android/app/src/main/java/org/tensorflow/lite/examples/styletransfer/camera/AutoFitSurfaceView.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 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 |
17 | package org.tensorflow.lite.examples.styletransfer.camera
18 |
19 | import android.content.Context
20 | import android.util.AttributeSet
21 | import android.util.Log
22 | import android.view.SurfaceView
23 | import 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 | */
29 | class AutoFitSurfaceView(
30 | context: Context,
31 | attrs: AttributeSet? = null,
32 | defStyle: Int = 0
33 | ) : SurfaceView(context, attrs, defStyle) {
34 |
35 | private var aspectRatio = 0f
36 | private var widthDiff = 0
37 | private var heightDiff = 0
38 | private var requestLayout = false
39 |
40 | /**
41 | * Sets the aspect ratio for this view. The size of the view will be
42 | * measured based on the ratio calculated from the parameters. Note that
43 | * the actual sizes of parameters don't matter, that is, calling
44 | * setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result.
45 | *
46 | * @param width Relative horizontal size
47 | * @param height Relative vertical size
48 | */
49 | fun setAspectRatio(width: Int, height: Int) {
50 | require(width > 0 && height > 0) { "Size cannot be negative" }
51 | aspectRatio = width.toFloat() / height.toFloat()
52 | requestLayout()
53 | }
54 |
55 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
56 | val width = MeasureSpec.getSize(widthMeasureSpec)
57 | val height = MeasureSpec.getSize(heightMeasureSpec)
58 | if (aspectRatio == 0f) {
59 | setMeasuredDimension(width, height)
60 | } else {
61 | // Performs center-crop transformation of the camera frames
62 | val newWidth: Int
63 | val newHeight: Int
64 | if (width < height * aspectRatio) {
65 | newHeight = height
66 | newWidth = (height / aspectRatio).roundToInt()
67 | } else {
68 | newWidth = width
69 | newHeight = (width / aspectRatio).roundToInt()
70 | }
71 |
72 | Log.d(TAG, "Measured dimensions set: $newWidth x $newHeight")
73 | widthDiff = width - newWidth
74 | heightDiff = height - newHeight
75 | requestLayout = true
76 | setMeasuredDimension(newWidth, newHeight)
77 | }
78 | }
79 |
80 | override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
81 | if (requestLayout) {
82 | requestLayout = false
83 | layout(
84 | widthDiff / 2,
85 | heightDiff / 2,
86 | right + (widthDiff / 2),
87 | bottom + (heightDiff / 2)
88 | )
89 | }
90 | super.onLayout(changed, left, top, right, bottom)
91 | }
92 |
93 | companion object {
94 | private val TAG = AutoFitSurfaceView::class.java.simpleName
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/android/app/src/main/java/org/tensorflow/lite/examples/styletransfer/camera/CameraSizes.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 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 |
17 | package org.tensorflow.lite.examples.styletransfer.camera
18 |
19 | import android.graphics.Point
20 | import android.hardware.camera2.CameraCharacteristics
21 | import android.hardware.camera2.params.StreamConfigurationMap
22 | import android.util.Size
23 | import android.view.Display
24 | import kotlin.math.max
25 | import kotlin.math.min
26 |
27 | /** Helper class used to pre-compute shortest and longest sides of a [Size] */
28 | class 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 */
36 | val SIZE_1080P: SmartSize = SmartSize(1920, 1080)
37 |
38 | /** Returns a [SmartSize] object for the given [Display] */
39 | fun getDisplaySmartSize(display: Display): SmartSize {
40 | val outPoint = Point()
41 | display.getRealSize(outPoint)
42 | return SmartSize(outPoint.x, outPoint.y)
43 | }
44 |
45 | // verify that the given width and height are on the expected aspect ratio
46 | fun verifyAspectRatio(width: Int, height: Int, aspectRatio: Size): Boolean {
47 | return (width * aspectRatio.height) == (height * aspectRatio.width)
48 | }
49 |
50 | /**
51 | * Returns the largest available PREVIEW size. For more information, see:
52 | * https://d.android.com/reference/android/hardware/camera2/CameraDevice
53 | */
54 | fun getPreviewOutputSize(
55 | display: Display,
56 | characteristics: CameraCharacteristics,
57 | targetClass: Class,
58 | aspectRatio: Size,
59 | format: Int? = null
60 | ): Size {
61 | // Find which is smaller: screen or 1080p
62 | val screenSize = getDisplaySmartSize(display)
63 | val hdScreen = screenSize.long >= SIZE_1080P.long || screenSize.short >= SIZE_1080P.short
64 | val maxSize = if (hdScreen) SIZE_1080P else screenSize
65 |
66 | // If image format is provided, use it to determine supported sizes; else use target class
67 | val config = characteristics.get(
68 | CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP
69 | )!!
70 | if (format == null) {
71 | assert(StreamConfigurationMap.isOutputSupportedFor(targetClass))
72 | } else {
73 | assert(config.isOutputSupportedFor(format))
74 | }
75 | val allSizes = if (format == null) {
76 | config.getOutputSizes(targetClass)
77 | } else {
78 | config.getOutputSizes(format)
79 | }
80 |
81 | // Get available sizes and sort them by area from largest to smallest
82 | val validSizes = allSizes
83 | .sortedWith(compareBy { it.height * it.width })
84 | .filter { verifyAspectRatio(it.width, it.height, aspectRatio) }
85 | .map { SmartSize(it.width, it.height) }.reversed()
86 |
87 | // Then, get the largest output size that is smaller or equal than our max size
88 | return validSizes.first { it.long <= maxSize.long && it.short <= maxSize.short }.size
89 | }
90 |
--------------------------------------------------------------------------------
/android/app/src/main/java/org/tensorflow/lite/examples/styletransfer/camera/OrientationLiveData.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 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 |
17 | package org.tensorflow.lite.examples.styletransfer.camera
18 |
19 | import android.content.Context
20 | import android.hardware.camera2.CameraCharacteristics
21 | import android.view.OrientationEventListener
22 | import android.view.Surface
23 | import androidx.lifecycle.LiveData
24 |
25 | /**
26 | * Calculates closest 90-degree orientation to compensate for the device rotation relative to sensor
27 | * orientation, i.e., allows user to see camera frames with the expected orientation.
28 | */
29 | class OrientationLiveData(context: Context, characteristics: CameraCharacteristics) :
30 | LiveData() {
31 |
32 | private val listener =
33 | object : OrientationEventListener(context.applicationContext) {
34 | override fun onOrientationChanged(orientation: Int) {
35 | val rotation =
36 | when {
37 | orientation <= 45 -> Surface.ROTATION_0
38 | orientation <= 135 -> Surface.ROTATION_90
39 | orientation <= 225 -> Surface.ROTATION_180
40 | orientation <= 315 -> Surface.ROTATION_270
41 | else -> Surface.ROTATION_0
42 | }
43 | val relative = computeRelativeRotation(characteristics, rotation)
44 | if (relative != value) postValue(relative)
45 | }
46 | }
47 |
48 | override fun onActive() {
49 | super.onActive()
50 | listener.enable()
51 | }
52 |
53 | override fun onInactive() {
54 | super.onInactive()
55 | listener.disable()
56 | }
57 |
58 | companion object {
59 |
60 | /**
61 | * Computes rotation required to transform from the camera sensor orientation to the device's
62 | * current orientation in degrees.
63 | *
64 | * @param characteristics the [CameraCharacteristics] to query for the sensor orientation.
65 | * @param surfaceRotation the current device orientation as a Surface constant
66 | * @return the relative rotation from the camera sensor to the current device orientation.
67 | */
68 | @JvmStatic
69 | private fun computeRelativeRotation(
70 | characteristics: CameraCharacteristics,
71 | surfaceRotation: Int
72 | ): Int {
73 | val sensorOrientationDegrees = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!
74 |
75 | val deviceOrientationDegrees =
76 | when (surfaceRotation) {
77 | Surface.ROTATION_0 -> 0
78 | Surface.ROTATION_90 -> 90
79 | Surface.ROTATION_180 -> 180
80 | Surface.ROTATION_270 -> 270
81 | else -> 0
82 | }
83 |
84 | // Reverse device orientation for front-facing cameras
85 | val sign =
86 | if (characteristics.get(CameraCharacteristics.LENS_FACING) ==
87 | CameraCharacteristics.LENS_FACING_FRONT
88 | )
89 | 1
90 | else -1
91 |
92 | // Calculate desired JPEG orientation relative to camera orientation to make
93 | // the image upright relative to the device orientation
94 | return (sensorOrientationDegrees - (deviceOrientationDegrees * sign) + 360) % 360
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/android/app/src/main/res/anim/scale_anim.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxhdpi/icn_chevron_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/drawable-xxhdpi/icn_chevron_down.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxhdpi/icn_chevron_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/drawable-xxhdpi/icn_chevron_up.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxhdpi/styles_square_thumb.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/drawable-xxhdpi/styles_square_thumb.jpg
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxhdpi/tfl2_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/drawable-xxhdpi/tfl2_logo.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxhdpi/tfl2_logo_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/drawable-xxhdpi/tfl2_logo_dark.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/ic_switchcam.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 | -
10 |
11 |
12 |
13 |
14 |
15 | -
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/rounded_edge.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
10 |
11 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
19 |
20 |
21 |
27 |
28 |
32 |
33 |
38 |
39 |
40 |
44 |
45 |
49 |
50 |
56 |
57 |
60 |
61 |
67 |
68 |
79 |
80 |
81 |
84 |
85 |
90 |
91 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
116 |
117 |
124 |
125 |
136 |
--------------------------------------------------------------------------------
/android/app/src/main/res/layout/bottom_sheet_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
23 |
24 |
32 |
33 |
39 |
40 |
47 |
48 |
--------------------------------------------------------------------------------
/android/app/src/main/res/layout/fragment_style_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
--------------------------------------------------------------------------------
/android/app/src/main/res/layout/image_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
21 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #ffa800
4 | #ff6f00
5 | #425066
6 |
7 | #66000000
8 | #ffffff
9 | #E4E4E4
10 |
11 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 256dp
4 | 16dp
5 | 15dp
6 | 8dp
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | TFL Style Transfer
3 | GPU
4 | Re-run model
5 | Choose a Style
6 |
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.3.50'
5 | repositories {
6 | google()
7 | mavenCentral()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:4.0.0'
11 | classpath 'de.undercouch:gradle-download-task:4.0.2'
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | mavenCentral()
22 | maven {
23 | name 'ossrh-snapshot'
24 | url 'http://oss.sonatype.org/content/repositories/snapshots'
25 | }
26 | }
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
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.
9 | org.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
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/android/placeholder.txt:
--------------------------------------------------------------------------------
1 | android code tk
2 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'Ruse'
2 | include ':app'
--------------------------------------------------------------------------------
/ios/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Ruse40.png",
5 | "idiom" : "iphone",
6 | "scale" : "2x",
7 | "size" : "20x20"
8 | },
9 | {
10 | "filename" : "Ruse60.png",
11 | "idiom" : "iphone",
12 | "scale" : "3x",
13 | "size" : "20x20"
14 | },
15 | {
16 | "filename" : "Ruse58.png",
17 | "idiom" : "iphone",
18 | "scale" : "2x",
19 | "size" : "29x29"
20 | },
21 | {
22 | "filename" : "Ruse87.png",
23 | "idiom" : "iphone",
24 | "scale" : "3x",
25 | "size" : "29x29"
26 | },
27 | {
28 | "filename" : "Ruse80.png",
29 | "idiom" : "iphone",
30 | "scale" : "2x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "filename" : "Ruse120.png",
35 | "idiom" : "iphone",
36 | "scale" : "3x",
37 | "size" : "40x40"
38 | },
39 | {
40 | "filename" : "Ruse120-1.png",
41 | "idiom" : "iphone",
42 | "scale" : "2x",
43 | "size" : "60x60"
44 | },
45 | {
46 | "filename" : "Ruse180.png",
47 | "idiom" : "iphone",
48 | "scale" : "3x",
49 | "size" : "60x60"
50 | },
51 | {
52 | "filename" : "Ruse20.png",
53 | "idiom" : "ipad",
54 | "scale" : "1x",
55 | "size" : "20x20"
56 | },
57 | {
58 | "filename" : "Ruse40-1.png",
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "20x20"
62 | },
63 | {
64 | "filename" : "Ruse29.png",
65 | "idiom" : "ipad",
66 | "scale" : "1x",
67 | "size" : "29x29"
68 | },
69 | {
70 | "filename" : "Ruse58-1.png",
71 | "idiom" : "ipad",
72 | "scale" : "2x",
73 | "size" : "29x29"
74 | },
75 | {
76 | "filename" : "Ruse40-2.png",
77 | "idiom" : "ipad",
78 | "scale" : "1x",
79 | "size" : "40x40"
80 | },
81 | {
82 | "filename" : "Ruse80-1.png",
83 | "idiom" : "ipad",
84 | "scale" : "2x",
85 | "size" : "40x40"
86 | },
87 | {
88 | "filename" : "Ruse76.png",
89 | "idiom" : "ipad",
90 | "scale" : "1x",
91 | "size" : "76x76"
92 | },
93 | {
94 | "filename" : "Ruse152.png",
95 | "idiom" : "ipad",
96 | "scale" : "2x",
97 | "size" : "76x76"
98 | },
99 | {
100 | "filename" : "Ruse167.png",
101 | "idiom" : "ipad",
102 | "scale" : "2x",
103 | "size" : "83.5x83.5"
104 | },
105 | {
106 | "filename" : "Ruse1024.png",
107 | "idiom" : "ios-marketing",
108 | "scale" : "1x",
109 | "size" : "1024x1024"
110 | }
111 | ],
112 | "info" : {
113 | "author" : "xcode",
114 | "version" : 1
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/AppIcon.appiconset/Ruse1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/AppIcon.appiconset/Ruse1024.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/AppIcon.appiconset/Ruse120-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/AppIcon.appiconset/Ruse120-1.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/AppIcon.appiconset/Ruse120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/AppIcon.appiconset/Ruse120.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/AppIcon.appiconset/Ruse152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/AppIcon.appiconset/Ruse152.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/AppIcon.appiconset/Ruse167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/AppIcon.appiconset/Ruse167.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/AppIcon.appiconset/Ruse180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/AppIcon.appiconset/Ruse180.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/AppIcon.appiconset/Ruse20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/AppIcon.appiconset/Ruse20.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/AppIcon.appiconset/Ruse29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/AppIcon.appiconset/Ruse29.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/AppIcon.appiconset/Ruse40-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/AppIcon.appiconset/Ruse40-1.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/AppIcon.appiconset/Ruse40-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/AppIcon.appiconset/Ruse40-2.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/AppIcon.appiconset/Ruse40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/AppIcon.appiconset/Ruse40.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/AppIcon.appiconset/Ruse58-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/AppIcon.appiconset/Ruse58-1.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/AppIcon.appiconset/Ruse58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/AppIcon.appiconset/Ruse58.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/AppIcon.appiconset/Ruse60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/AppIcon.appiconset/Ruse60.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/AppIcon.appiconset/Ruse76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/AppIcon.appiconset/Ruse76.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/AppIcon.appiconset/Ruse80-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/AppIcon.appiconset/Ruse80-1.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/AppIcon.appiconset/Ruse80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/AppIcon.appiconset/Ruse80.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/AppIcon.appiconset/Ruse87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/AppIcon.appiconset/Ruse87.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/face1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "face1.jpg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/face1.imageset/face1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/face1.imageset/face1.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/face2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "face2.jpg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/face2.imageset/face2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/face2.imageset/face2.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/face3.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "face3.jpg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/face3.imageset/face3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/face3.imageset/face3.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/face4.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "face4.jpg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/face4.imageset/face4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/face4.imageset/face4.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/face5.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "face5.jpg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/face5.imageset/face5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/face5.imageset/face5.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/photo_camera.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "photo_camera_2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "photo_camera_3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/ios/Assets.xcassets/photo_camera.imageset/photo_camera_2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/photo_camera.imageset/photo_camera_2x.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/photo_camera.imageset/photo_camera_3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/photo_camera.imageset/photo_camera_3x.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/photo_library.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "photo_library_2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "photo_library_3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/ios/Assets.xcassets/photo_library.imageset/photo_library_2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/photo_library.imageset/photo_library_2x.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/photo_library.imageset/photo_library_3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/photo_library.imageset/photo_library_3x.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style0.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style0.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style0.imageset/style0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style0.imageset/style0.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style1.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style1.imageset/style1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style1.imageset/style1.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style10.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style10.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style10.imageset/style10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style10.imageset/style10.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style11.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style11.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style11.imageset/style11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style11.imageset/style11.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style12.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style12.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style12.imageset/style12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style12.imageset/style12.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style13.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style13.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style13.imageset/style13.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style13.imageset/style13.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style14.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style14.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style14.imageset/style14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style14.imageset/style14.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style15.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style15.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style15.imageset/style15.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style15.imageset/style15.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style16.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style16.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style16.imageset/style16.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style16.imageset/style16.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style17.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style17.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style17.imageset/style17.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style17.imageset/style17.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style18.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style18.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style18.imageset/style18.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style18.imageset/style18.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style19.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style19.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style19.imageset/style19.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style19.imageset/style19.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style2.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style2.imageset/style2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style2.imageset/style2.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style20.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style20.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style20.imageset/style20.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style20.imageset/style20.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style21.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style21.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style21.imageset/style21.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style21.imageset/style21.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style22.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style22.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style22.imageset/style22.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style22.imageset/style22.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style23.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style23.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style23.imageset/style23.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style23.imageset/style23.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style24.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style24.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style24.imageset/style24.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style24.imageset/style24.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style25.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style25.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style25.imageset/style25.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style25.imageset/style25.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style3.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style3.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style3.imageset/style3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style3.imageset/style3.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style4.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style4.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style4.imageset/style4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style4.imageset/style4.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style5.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style5.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style5.imageset/style5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style5.imageset/style5.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style6.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style6.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style6.imageset/style6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style6.imageset/style6.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style7.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style7.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style7.imageset/style7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style7.imageset/style7.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style8.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style8.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style8.imageset/style8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style8.imageset/style8.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style9.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "style9.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/style9.imageset/style9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/style9.imageset/style9.jpg
--------------------------------------------------------------------------------
/ios/Assets.xcassets/switch_camera.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "switch_camera_2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "switch_camera_3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ios/Assets.xcassets/switch_camera.imageset/switch_camera_2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/switch_camera.imageset/switch_camera_2x.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/switch_camera.imageset/switch_camera_3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/switch_camera.imageset/switch_camera_3x.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/video_camera.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "video_camera_2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "video_camera_3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/ios/Assets.xcassets/video_camera.imageset/video_camera_2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/video_camera.imageset/video_camera_2x.png
--------------------------------------------------------------------------------
/ios/Assets.xcassets/video_camera.imageset/video_camera_3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Assets.xcassets/video_camera.imageset/video_camera_3x.png
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '10.0'
2 | target 'Ruse' do
3 |
4 | use_frameworks!
5 |
6 | #project 'Spartacus.xcodeproj'
7 |
8 |
9 | pod 'GoogleMLKit/FaceDetection'
10 |
11 | pod 'TensorFlowLiteSwift', '~> 0.0.1-nightly', :subspecs => ['Metal']
12 |
13 | #pod 'GPUImage3'
14 |
15 |
16 |
17 | end
18 |
19 |
20 |
--------------------------------------------------------------------------------
/ios/README.md:
--------------------------------------------------------------------------------
1 | # Ruse
2 | See main Readme for project info
3 |
--------------------------------------------------------------------------------
/ios/Resources/face1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Resources/face1.jpg
--------------------------------------------------------------------------------
/ios/Resources/face2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Resources/face2.jpg
--------------------------------------------------------------------------------
/ios/Resources/face3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Resources/face3.jpg
--------------------------------------------------------------------------------
/ios/Resources/face4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Resources/face4.jpg
--------------------------------------------------------------------------------
/ios/Resources/face5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Resources/face5.jpg
--------------------------------------------------------------------------------
/ios/Resources/style1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Resources/style1.jpg
--------------------------------------------------------------------------------
/ios/Resources/style2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Resources/style2.jpg
--------------------------------------------------------------------------------
/ios/Resources/style3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Resources/style3.jpg
--------------------------------------------------------------------------------
/ios/Resources/style4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Resources/style4.jpg
--------------------------------------------------------------------------------
/ios/Resources/style5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Resources/style5.jpg
--------------------------------------------------------------------------------
/ios/Resources/style6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Resources/style6.jpg
--------------------------------------------------------------------------------
/ios/Ruse.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Ruse.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Ruse.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Ruse.xcodeproj/project.xcworkspace/xcuserdata/mike.kiser.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Ruse.xcodeproj/project.xcworkspace/xcuserdata/mike.kiser.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/ios/Ruse.xcodeproj/project.xcworkspace/xcuserdata/mike.kiser.xcuserdatad/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildLocationStyle
6 | UseAppPreferences
7 | CustomBuildLocationType
8 | RelativeToDerivedData
9 | DerivedDataLocationStyle
10 | Default
11 | IssueFilterStyle
12 | ShowActiveSchemeOnly
13 | LiveSourceIssuesEnabled
14 |
15 | ShowSharedSchemesAutomaticallyEnabled
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/ios/Ruse.xcodeproj/xcuserdata/mike.kiser.xcuserdatad/xcschemes/Ruse.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
45 |
51 |
52 |
53 |
54 |
60 |
62 |
68 |
69 |
70 |
71 |
73 |
74 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/ios/Ruse.xcodeproj/xcuserdata/mike.kiser.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Ruse.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 | RuseObjc.xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 19
16 |
17 | SpartacusObjc.xcscheme_^#shared#^_
18 |
19 | orderHint
20 | 1
21 |
22 | VisionExampleObjc.xcscheme_^#shared#^_
23 |
24 | orderHint
25 | 36
26 |
27 |
28 | SuppressBuildableAutocreation
29 |
30 | BB287B3420729CE90069707A
31 |
32 | primary
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/ios/Ruse.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Ruse.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Ruse.xcworkspace/xcuserdata/mike.kiser.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derrumbe/Ruse/4467e14fb15274812aed2cd949f210ba813594ba/ios/Ruse.xcworkspace/xcuserdata/mike.kiser.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/ios/Ruse/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2018 Google Inc.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 | //
16 |
17 | import UIKit
18 |
19 | @UIApplicationMain
20 | class AppDelegate: UIResponder, UIApplicationDelegate {
21 |
22 | var window: UIWindow?
23 |
24 | func application(
25 | _ application: UIApplication,
26 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
27 | ) -> Bool {
28 | return true
29 | }
30 | }
31 |
32 |
33 |
--------------------------------------------------------------------------------
/ios/Ruse/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/ios/Ruse/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIcons
10 |
11 | CFBundleIcons~ipad
12 |
13 | CFBundleIdentifier
14 | $(PRODUCT_BUNDLE_IDENTIFIER)
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | $(PRODUCT_NAME)
19 | CFBundlePackageType
20 | APPL
21 | CFBundleShortVersionString
22 | 1.0
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
27 | NSCameraUsageDescription
28 | We use the data from the camera for the image recognition.
29 | NSPhotoLibraryAddUsageDescription
30 | We can add photos to your photos
31 | NSPhotoLibraryUsageDescription
32 | We use the data from the photo library for the image recognition.
33 | UILaunchStoryboardName
34 | LaunchScreen
35 | UIMainStoryboardFile
36 | Main
37 | UIRequiredDeviceCapabilities
38 |
39 | armv7
40 |
41 | UISupportedInterfaceOrientations
42 |
43 | UIInterfaceOrientationPortrait
44 | UIInterfaceOrientationPortraitUpsideDown
45 |
46 | UISupportedInterfaceOrientations~ipad
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationPortraitUpsideDown
50 | UIInterfaceOrientationLandscapeLeft
51 | UIInterfaceOrientationLandscapeRight
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/ios/Ruse/MLKitExtensions.swift:
--------------------------------------------------------------------------------
1 | import CoreGraphics
2 | import UIKit
3 |
4 | // MARK: - UIImage
5 |
6 | extension UIImage {
7 |
8 | /// Creates and returns a new image scaled to the given size. The image preserves its original PNG
9 | /// or JPEG bitmap info.
10 | ///
11 | /// - Parameter size: The size to scale the image to.
12 | /// - Returns: The scaled image or `nil` if image could not be resized.
13 | /*
14 | public func scaledImage(with size: CGSize) -> UIImage? {
15 | UIGraphicsBeginImageContextWithOptions(size, false, scale)
16 | defer { UIGraphicsEndImageContext() }
17 | draw(in: CGRect(origin: .zero, size: size))
18 | return UIGraphicsGetImageFromCurrentImageContext()?.data.flatMap(UIImage.init)
19 | }
20 |
21 | // MARK: - Private
22 |
23 | /// The PNG or JPEG data representation of the image or `nil` if the conversion failed.
24 | private var data: Data? {
25 | #if swift(>=4.2)
26 | return self.pngData() ?? self.jpegData(compressionQuality: Constant.jpegCompressionQuality)
27 | #else
28 | return self.pngData() ?? self.jpegData(compressionQuality: Constant.jpegCompressionQuality)
29 | #endif // swift(>=4.2)
30 | }
31 | */
32 | }
33 |
34 | // MARK: - Constants
35 |
36 | private enum Constant {
37 | static let jpegCompressionQuality: CGFloat = 0.8
38 | }
39 |
--------------------------------------------------------------------------------
/ios/Ruse/SimplexNoise.swift:
--------------------------------------------------------------------------------
1 | /*
2 | A speed-improved simplex noise algorithm for 2D. Based on public domain
3 | example code by Stefan Gustavson.
4 |
5 | http://staffwww.itn.liu.se/~stegu/simplexnoise/SimplexNoise.java
6 |
7 | This is free and unencumbered software released into the public domain.
8 |
9 | Anyone is free to copy, modify, publish, use, compile, sell, or
10 | distribute this software, either in source code form or as a compiled
11 | binary, for any purpose, commercial or non-commercial, and by any
12 | means.
13 |
14 | In jurisdictions that recognize copyright laws, the author or authors
15 | of this software dedicate any and all copyright interest in the
16 | software to the public domain. We make this dedication for the benefit
17 | of the public at large and to the detriment of our heirs and
18 | successors. We intend this dedication to be an overt act of
19 | relinquishment in perpetuity of all present and future rights to this
20 | software under copyright law.
21 |
22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
26 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
27 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 | OTHER DEALINGS IN THE SOFTWARE.
29 |
30 | For more information, please refer to
31 |
32 | */
33 |
34 | import Foundation
35 | import SpriteKit
36 |
37 | class SimplexNoise {
38 |
39 | private let grad3 : [Grad] = [
40 | Grad(x: 1, y: 1, z: 0), Grad(x: -1, y: 1, z: 0), Grad(x: 1, y: -1, z: 0), Grad(x: -1, y: -1, z: 0),
41 | Grad(x: 1, y: 0, z: 1), Grad(x: -1, y: 0, z: 1), Grad(x: 1, y: 0, z: -1), Grad(x: -1, y: 0, z: -1),
42 | Grad(x: 0, y: 1, z: 1), Grad(x: 0, y: -1, z: 1), Grad(x: 0, y: 1, z: -1), Grad(x: 0, y: -1, z: -1)
43 | ]
44 |
45 | private let p : [Int] = [151,160,137,91,90,15, 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
46 | 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
47 | 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
48 | 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
49 | 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
50 | 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
51 | 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
52 | 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
53 | 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
54 | 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
55 | 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
56 | 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180]
57 |
58 | private var perm = Array(repeating: 0, count: 512)
59 | private var permMod12 = Array(repeating: 0, count: 512)
60 |
61 | init() {
62 | for i in 0..<512 {
63 | perm[i] = p[i & 255]
64 | permMod12[i] = perm[i] % 12
65 | }
66 | }
67 |
68 | private let F2 = 0.5 * (sqrt(3.0) - 1.0)
69 | private let G2 = (3.0 - sqrt(3.0)) / 6.0
70 |
71 | private func dot(g: Grad, x: Double, y: Double) -> Double {
72 | return g.x * x + g.y * y
73 | }
74 |
75 | private func fastfloor(x: Double) -> Int {
76 | let xi = Int(x)
77 | return x < Double(xi) ? xi - 1 : xi
78 | }
79 |
80 | func generatedNoise(chunk: CGPoint, octaves: Int, roughness: Double, scale: Double) -> [[Double]] {
81 | let width = Int(MapConstants.chunkSize.width)
82 | let height = Int(MapConstants.chunkSize.height)
83 |
84 | var noise = Array(count: width, repeatedValue: Array(count: height, repeatedValue: 0.0))
85 |
86 | var layerFrequency = scale
87 | var layerWeight = 1.0
88 | var weightSum = 0.0
89 |
90 | for _ in 0.. Double {
113 | var n0, n1, n2: Double // noise from the three corners
114 |
115 | // skew the input space to determine the current simplex cell
116 | let s = (x + y) * F2 // hairy factor for 2D
117 | let i = fastfloor(x: (x + s))
118 | let j = fastfloor(x: (y + s))
119 | let t = Double(i + j) * G2
120 |
121 | // unskew the cell origins back to (x,y) space and
122 | // calculate the x/y distances from the cell origin
123 | let xorigin = Double(i) - t
124 | let yorigin = Double(j) - t
125 | let x0 = x - xorigin
126 | let y0 = y - yorigin
127 |
128 | // for 2D the simplex shape is an equilateral triangle
129 | // determine which simplex we are in
130 | var i1, j1 : Int // offsets for second (middle) corner of simplex in (i,j) coords
131 | if (x0 > y0) {
132 | // lower triangle, XY order: (0,0)->(1,0)->(1,1)
133 | i1 = 1
134 | j1 = 0
135 | } else {
136 | // upper triangle, YX order: (0,0)->(0,1)->(1,1)
137 | i1 = 0
138 | j1 = 1
139 | }
140 | // a step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
141 | // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
142 | // c = (3-sqrt(3))/6
143 | let x1 = x0 - Double(i1) + G2 // offsets for middle corner in (x,y) unskewed coords
144 | let y1 = y0 - Double(j1) + G2
145 | let x2 = x0 - 1.0 + 2.0 * G2 // offsets for the last corner (x,y) unskewed coords
146 | let y2 = y0 - 1.0 + 2.0 * G2
147 |
148 | // work out the hashed gradient indices of the three simplex corners
149 | let ii = i & 255
150 | let jj = j & 255
151 | let gi0 = permMod12[ii + perm[jj]]
152 | let gi1 = permMod12[ii + i1 + perm[jj + j1]]
153 | let gi2 = permMod12[ii + 1 + perm[jj + 1]]
154 |
155 | // calculate the contribution from the three corners
156 | var t0 = 0.5 - x0 * x0 - y0 * y0
157 | if (t0 < 0) {
158 | n0 = 0.0
159 | } else {
160 | t0 *= t0
161 | n0 = t0 * t0 * dot(g: grad3[gi0], x: x0, y: y0) // (x,y) of grad3 used for 2D gradient
162 | }
163 |
164 | var t1 = 0.5 - x1 * x1 - y1 * y1
165 | if (t1 < 0) {
166 | n1 = 0.0
167 | } else {
168 | t1 *= t1
169 | n1 = t1 * t1 * dot(g: grad3[gi1], x: x1, y: y1)
170 | }
171 |
172 | var t2 = 0.5 - x2 * x2 - y2 * y2
173 | if (t2 < 0) {
174 | n2 = 0.0
175 | } else {
176 | t2 *= t2
177 | n2 = t2 * t2 * dot(g: grad3[gi2], x: x2, y: y2)
178 | }
179 |
180 | // add contributions from each corner to get the final noise
181 | // the result is scaled to return values in the interval [-1,1]
182 | return 70 * (n0 + n1 + n2)
183 | }
184 |
185 | }
186 |
187 | private struct Grad {
188 | let x, y, z: Double
189 | }
190 |
191 |
--------------------------------------------------------------------------------
/ios/Ruse/StylePickerViewController.swift:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The TensorFlow Authors. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import UIKit
16 |
17 | protocol StylePickerViewControllerDelegate {
18 |
19 | func picker(_: StylePickerViewController, didSelectStyle image: UIImage)
20 |
21 | }
22 |
23 | class StylePickerViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
24 |
25 | var delegate: StylePickerViewControllerDelegate?
26 |
27 | class func fromStoryboard() -> StylePickerViewController {
28 | return UIStoryboard(name: "Main", bundle: nil)
29 | .instantiateViewController(withIdentifier: "StylePickerViewController")
30 | as! StylePickerViewController
31 | }
32 |
33 | private let dataSource = StylePickerDataSource()
34 |
35 | override func viewDidLoad() {
36 | super.viewDidLoad()
37 | collectionView.dataSource = dataSource
38 | collectionView.delegate = self
39 | }
40 |
41 | override func collectionView(_ collectionView: UICollectionView,
42 | didSelectItemAt indexPath: IndexPath) {
43 | if let image = dataSource.imageForStyle(at: indexPath.item) {
44 | collectionView.deselectItem(at: indexPath, animated: true)
45 | delegate?.picker(self, didSelectStyle: image)
46 | dismiss(animated: true, completion: nil)
47 | }
48 | }
49 |
50 | func collectionView(_ collectionView: UICollectionView,
51 | layout collectionViewLayout: UICollectionViewLayout,
52 | sizeForItemAt indexPath: IndexPath) -> CGSize {
53 | guard let layout = collectionViewLayout as? UICollectionViewFlowLayout else { return .zero }
54 | let smallestDimension = collectionView.bounds.width < collectionView.bounds.height ?
55 | collectionView.bounds.width : collectionView.bounds.height
56 | let extraPadding: CGFloat = 3 // magic number
57 | let itemDimension =
58 | smallestDimension / 2 - collectionView.contentInset.left - collectionView.contentInset.right
59 | - layout.sectionInset.left - layout.sectionInset.right - layout.minimumInteritemSpacing
60 | - extraPadding
61 | return CGSize(width: itemDimension, height: itemDimension)
62 | }
63 |
64 | }
65 |
66 | class StylePickerDataSource: NSObject, UICollectionViewDataSource {
67 |
68 | static func defaultStyle() -> UIImage {
69 | return UIImage(named: "style3")! // use as default
70 | }
71 |
72 | lazy private var images: [UIImage] = {
73 | print("in get images")
74 | var index = 0
75 | var images: [UIImage] = []
76 | while let image = UIImage(named: "style\(index)") {
77 | print(index)
78 | images.append(image)
79 | index += 1
80 | }
81 | print("all images found")
82 | return images
83 | }()
84 |
85 | func imageForStyle(at index: Int) -> UIImage? {
86 | return images[index]
87 | }
88 |
89 | func collectionView(_ collectionView: UICollectionView,
90 | numberOfItemsInSection section: Int) -> Int {
91 | return images.count
92 | }
93 |
94 | func collectionView(_ collectionView: UICollectionView,
95 | cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
96 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "StylePickerCell",
97 | for: indexPath) as! StylePickerCollectionViewCell
98 | let image = imageForStyle(at: indexPath.item)
99 | cell.styleImageView.image = image
100 | return cell
101 | }
102 |
103 | }
104 |
105 | class StylePickerCollectionViewCell: UICollectionViewCell {
106 |
107 | @IBOutlet weak var styleImageView: UIImageView!
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/ios/Ruse/TFLiteExtension.swift:
--------------------------------------------------------------------------------
1 | // Copyright 2020 The TensorFlow Authors. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import CoreGraphics
16 | import Foundation
17 | import UIKit
18 |
19 | // MARK: - UIImage
20 |
21 | /// Extension of iOS classes that is useful for working with TensorFlow Lite computer vision models.
22 | extension UIImage {
23 |
24 | /// Creates and returns a new image scaled to the given size. The image preserves its original PNG
25 | /// or JPEG bitmap info.
26 | ///
27 | /// - Parameter size: The size to scale the image to.
28 | /// - Returns: The scaled image or `nil` if image could not be resized.
29 | func scaledImage(with size: CGSize) -> UIImage? {
30 | UIGraphicsBeginImageContextWithOptions(size, false, scale)
31 | defer { UIGraphicsEndImageContext() }
32 | draw(in: CGRect(origin: .zero, size: size))
33 | return UIGraphicsGetImageFromCurrentImageContext()?.data.flatMap(UIImage.init)
34 | }
35 |
36 | /// Returns the data representation of the image after scaling to the given `size` and removing
37 | /// the alpha component. This function assumes a batch size of one and three channels per image.
38 | /// Changing these parameters in the TF Lite model will cause conflicts.
39 | ///
40 | /// - Parameters
41 | /// - size: Size to scale the image to (i.e. image size used while training the model).
42 | /// - isQuantized: Whether the model is quantized (i.e. fixed point values rather than floating
43 | /// point values).
44 | /// - Returns: The scaled image as data or `nil` if the image could not be scaled.
45 | func scaledData(with size: CGSize, isQuantized: Bool) -> Data? {
46 | guard let cgImage = self.cgImage else { return nil }
47 | return UIImage.normalizedData(from: cgImage, resizingTo: size, quantum: Float32.self)
48 | }
49 |
50 | /// Make the same image with orientation being `.up`.
51 | /// - Returns: A copy of the image with .up orientation or `nil` if the image could not be
52 | /// rotated.
53 | func transformOrientationToUp() -> UIImage? {
54 | // Check if the image orientation is already .up and don't need any rotation.
55 | guard imageOrientation != UIImage.Orientation.up else {
56 | // No rotation needed so return a copy of this image.
57 | return self.copy() as? UIImage
58 | }
59 |
60 | // Make sure that this image has an CGImage attached.
61 | guard let cgImage = self.cgImage else { return nil }
62 |
63 | // Create a CGContext to draw the rotated image to.
64 | guard let colorSpace = cgImage.colorSpace,
65 | let context = CGContext(
66 | data: nil,
67 | width: Int(size.width),
68 | height: Int(size.height),
69 | bitsPerComponent: cgImage.bitsPerComponent,
70 | bytesPerRow: 0,
71 | space: colorSpace,
72 | bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
73 | )
74 | else { return nil }
75 |
76 | var transform: CGAffineTransform = CGAffineTransform.identity
77 |
78 | // Calculate the transformation matrix that needed to bring the image orientation to .up
79 | switch imageOrientation {
80 | case .down, .downMirrored:
81 | transform = transform.translatedBy(x: size.width, y: size.height)
82 | transform = transform.rotated(by: CGFloat.pi)
83 | break
84 | case .left, .leftMirrored:
85 | transform = transform.translatedBy(x: size.width, y: 0)
86 | transform = transform.rotated(by: CGFloat.pi / 2.0)
87 | break
88 | case .right, .rightMirrored:
89 | transform = transform.translatedBy(x: 0, y: size.height)
90 | transform = transform.rotated(by: CGFloat.pi / -2.0)
91 | break
92 | case .up, .upMirrored:
93 | break
94 | @unknown default:
95 | break
96 | }
97 |
98 | // If the image is mirrored then flip it.
99 | switch imageOrientation {
100 | case .upMirrored, .downMirrored:
101 | transform.translatedBy(x: size.width, y: 0)
102 | transform.scaledBy(x: -1, y: 1)
103 | break
104 | case .leftMirrored, .rightMirrored:
105 | transform.translatedBy(x: size.height, y: 0)
106 | transform.scaledBy(x: -1, y: 1)
107 | case .up, .down, .left, .right:
108 | break
109 | @unknown default:
110 | break
111 | }
112 |
113 | // Apply transformation matrix to the CGContext.
114 | context.concatenate(transform)
115 |
116 | switch imageOrientation {
117 | case .left, .leftMirrored, .right, .rightMirrored:
118 | context.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))
119 | default:
120 | context.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
121 | break
122 | }
123 |
124 | // Create a CGImage from the context.
125 | guard let newCGImage = context.makeImage() else { return nil }
126 |
127 | // Convert it to UIImage.
128 | return UIImage.init(cgImage: newCGImage, scale: 1, orientation: .up)
129 | }
130 |
131 | // MARK: - Private
132 |
133 | /// The PNG or JPEG data representation of the image or `nil` if the conversion failed.
134 | private var data: Data? {
135 | return self.pngData() ?? self.jpegData(compressionQuality: Constant.jpegCompressionQuality)
136 | }
137 |
138 | static func normalizeImage(_ image: CGImage, resizingTo size: CGSize) -> CGImage? {
139 | // The TF Lite model expects images in the RGB color space.
140 | // Device-specific RGB color spaces should have the same number of colors as the standard
141 | // RGB color space so we probably don't have to redraw them.
142 | let colorSpace = CGColorSpaceCreateDeviceRGB()
143 | let cgImageSize = CGSize(width: image.width, height: image.height)
144 | if cgImageSize == size,
145 | (image.colorSpace?.name == colorSpace.name || image.colorSpace?.name == CGColorSpace.sRGB) {
146 | // Return the image if it is in the right format
147 | // to save a redraw operation.
148 | return image
149 | }
150 | let bitmapInfo = CGBitmapInfo(
151 | rawValue: CGBitmapInfo.byteOrder32Big.rawValue | CGImageAlphaInfo.premultipliedLast.rawValue
152 | )
153 | let width = Int(size.width)
154 | let scaledBytesPerRow = (image.bytesPerRow / image.width) * width
155 | guard
156 | let context = CGContext(
157 | data: nil,
158 | width: width,
159 | height: Int(size.height),
160 | bitsPerComponent: image.bitsPerComponent,
161 | bytesPerRow: scaledBytesPerRow,
162 | space: colorSpace,
163 | bitmapInfo: bitmapInfo.rawValue)
164 | else {
165 | return nil
166 | }
167 | context.draw(image, in: CGRect(origin: .zero, size: size))
168 | return context.makeImage()
169 | }
170 |
171 | static func normalizedData(from image: CGImage,
172 | resizingTo size: CGSize,
173 | quantum: T.Type) -> Data? where T: FloatingPoint {
174 | guard let normalizedImage = normalizeImage(image, resizingTo: size) else {
175 | return nil
176 | }
177 | guard let data = normalizedImage.dataProvider?.data as Data? else { return nil }
178 | // TF Lite expects an array of pixels in the form of floats normalized between 0 and 1.
179 | var floatArray: [T]
180 |
181 | // A full list of pixel formats is listed in this document under Table 2-1:
182 | // https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-CJBHBFFE
183 | // This code only handles pixel formats supported on iOS in the RGB color space.
184 | // If you're targeting macOS or macOS via Catalyst, you should support the macOS
185 | // pixel formats as well.
186 | switch normalizedImage.bitsPerPixel {
187 |
188 | // 16-bit pixel with no alpha channel. On iOS, this must have 5 bits per channel and
189 | // no alpha channel. The most significant bits are skipped.
190 | case 16:
191 | guard normalizedImage.bitsPerComponent == 5 else { return nil }
192 | guard normalizedImage.alphaInfo.rawValue & CGImageAlphaInfo.noneSkipFirst.rawValue != 0 else {
193 | return nil
194 | }
195 |
196 | // If this bool is false, assume little endian byte order.
197 | let bigEndian: Bool = {
198 | // Sometimes images have both littleEndian and bigEndian flags set. In this case, use the
199 | // non-default endianness because it seems to work best in empirical testing.
200 | let hasLittleEndian = normalizedImage.bitmapInfo.contains(.byteOrder16Little)
201 | let hasBigEndian = normalizedImage.bitmapInfo.contains(.byteOrder16Big)
202 | if !(hasLittleEndian && hasBigEndian) {
203 | return hasBigEndian
204 | }
205 | let currentByteOrder = CFByteOrderGetCurrent()
206 | switch currentByteOrder {
207 | case Int(CFByteOrderLittleEndian.rawValue):
208 | return true
209 | case Int(CFByteOrderBigEndian.rawValue):
210 | return false
211 | case _:
212 | // For unknown endianness, assume little endian since it's how most
213 | // Apple platforms are laid out nowadays.
214 | return false
215 | }
216 | }()
217 |
218 | let initializer: (inout UnsafeMutableBufferPointer, inout Int) -> () =
219 | { bufferPointer, initializedCount in
220 | let redMask: UInt16 = UInt16(0b0111110000000000)
221 | let greenMask: UInt16 = UInt16(0b0000001111100000)
222 | let blueMask: UInt16 = UInt16(0b0000000000011111)
223 |
224 | for byteIndex in stride(from: 0, to: data.count, by: 2) {
225 | // pixels are two bytes wide
226 | let pixelRange = byteIndex ..< byteIndex + 2
227 | let pixelData = data[pixelRange]
228 | let rawPixel = pixelData.withUnsafeBytes { $0.load(as: UInt16.self) }
229 | let pixel: UInt16
230 | if bigEndian {
231 | pixel = rawPixel.bigEndian
232 | } else {
233 | pixel = rawPixel.littleEndian
234 | }
235 | let redChannel = ((pixel & redMask) &>> 10)
236 | let greenChannel = ((pixel & greenMask) &>> 5)
237 | let blueChannel = ((pixel & blueMask) &>> 0)
238 |
239 | let maximumChannelValue = T(31) // 2 ^ 5 - 1
240 | let red = T(redChannel) / maximumChannelValue
241 | let green = T(greenChannel) / maximumChannelValue
242 | let blue = T(blueChannel) / maximumChannelValue
243 |
244 | let pixelIndex = byteIndex / 2
245 | let floatIndex = pixelIndex * 3
246 | bufferPointer[floatIndex] = red
247 | bufferPointer[floatIndex + 1] = green
248 | bufferPointer[floatIndex + 2] = blue
249 | }
250 |
251 | initializedCount = data.count / 2 * 3
252 | }
253 | floatArray = [T](unsafeUninitializedCapacity: data.count / 2 * 3,
254 | initializingWith: initializer)
255 |
256 | // We discard the image's alpha channel before running the TF Lite model, so we can treat
257 | // alpha and non-alpha images identically.
258 | case 32:
259 | guard normalizedImage.bitsPerComponent == 8 else { return nil }
260 | let alphaFirst =
261 | normalizedImage.alphaInfo == CGImageAlphaInfo.noneSkipFirst ||
262 | normalizedImage.alphaInfo == CGImageAlphaInfo.premultipliedFirst
263 | let alphaLast =
264 | normalizedImage.alphaInfo == CGImageAlphaInfo.noneSkipLast ||
265 | normalizedImage.alphaInfo == CGImageAlphaInfo.premultipliedLast
266 | let bigEndian = normalizedImage.bitmapInfo.contains(.byteOrder32Big)
267 | let littleEndian = normalizedImage.bitmapInfo.contains(.byteOrder32Little)
268 | guard alphaFirst || alphaLast else { return nil }
269 | guard bigEndian || littleEndian else { return nil }
270 |
271 | // Iterate over channels individually. Since the order of the channels in memory
272 | // may vary, we cannot add channels to the float buffer we pass to TF Lite in the
273 | // order that they are iterated over.
274 | let initializer: (inout UnsafeMutableBufferPointer, inout Int) -> () =
275 | { bufferPointer, initializedCount in
276 | let numberOfChannels = 4
277 | let alphaOffset: UInt8 = {
278 | if bigEndian {
279 | return alphaFirst ? 0 : 3
280 | } else {
281 | return alphaFirst ? 3 : 0
282 | }
283 | }()
284 | let redOffset: UInt8 = {
285 | if bigEndian {
286 | return alphaFirst ? 1 : 0
287 | } else {
288 | return alphaFirst ? 2 : 3
289 | }
290 | }()
291 | let greenOffset: UInt8 = {
292 | if bigEndian {
293 | return alphaFirst ? 2 : 1
294 | } else {
295 | return alphaFirst ? 1 : 2
296 | }
297 | }()
298 | let blueOffset: UInt8 = {
299 | if bigEndian {
300 | return alphaFirst ? 3 : 2
301 | } else {
302 | return alphaFirst ? 0 : 1
303 | }
304 | }()
305 |
306 | // Make sure we add the pixel components to the float array in the right
307 | // order regardless of pixel endianness.
308 | var rgbHolder: (red: T?, green: T?, blue: T?) = (nil, nil, nil)
309 | var floatIndex = 0
310 |
311 | func flushRGBs(_ rgbs: (red: T?, green: T?, blue: T?),
312 | to array: inout UnsafeMutableBufferPointer,
313 | at index: Int) {
314 | guard let red = rgbs.red, let green = rgbs.green, let blue = rgbs.blue else { return }
315 | array[index] = red
316 | array[index + 1] = green
317 | array[index + 2] = blue
318 | floatIndex += 3
319 | }
320 |
321 | let maximumChannelValue: T = 255 // 2 ^ 8 - 1
322 |
323 | func normalizeChannel(_ channel: UInt8) -> T {
324 | return T(
325 | bigEndian ? channel.bigEndian : channel.littleEndian
326 | ) / maximumChannelValue
327 | }
328 |
329 | for component in data.enumerated() {
330 | switch UInt8(component.offset % numberOfChannels) {
331 | case alphaOffset:
332 | // Ignore alpha channels
333 | break // Breaks from the switch, not the loop
334 |
335 | case redOffset:
336 | rgbHolder.red = normalizeChannel(component.element)
337 | case greenOffset:
338 | rgbHolder.green = normalizeChannel(component.element)
339 | case blueOffset:
340 | rgbHolder.blue = normalizeChannel(component.element)
341 |
342 | case _:
343 | fatalError("Unhandled offset: \(component.offset)")
344 | }
345 |
346 | // After every 4th channel (one full pixel), write the RGBs to
347 | // the float buffer in the correct order.
348 | if component.offset % 4 == 3 {
349 | flushRGBs(rgbHolder, to: &bufferPointer, at: floatIndex)
350 | rgbHolder.red = nil; rgbHolder.green = nil; rgbHolder.blue = nil
351 | }
352 | }
353 |
354 | initializedCount = floatIndex
355 | }
356 |
357 | floatArray = [T](unsafeUninitializedCapacity: data.count / 4 * 3,
358 | initializingWith: initializer)
359 |
360 | case _:
361 | print("Unsupported format from image: \(normalizedImage)")
362 | return nil
363 | }
364 |
365 | return Data(copyingBufferOf: floatArray)
366 | }
367 |
368 | }
369 |
370 | // MARK: - Data
371 |
372 | extension Data {
373 | /// Creates a new buffer by copying the buffer pointer of the given array.
374 | ///
375 | /// - Warning: The given array's element type `T` must be trivial in that it can be copied bit
376 | /// for bit with no indirection or reference-counting operations; otherwise, reinterpreting
377 | /// data from the resulting buffer has undefined behavior.
378 | /// - Parameter array: An array with elements of type `T`.
379 | init(copyingBufferOf array: [T]) {
380 | self = array.withUnsafeBufferPointer(Data.init)
381 | }
382 |
383 | /// Convert a Data instance to Array representation.
384 | func toArray(type: T.Type) -> [T] where T: AdditiveArithmetic {
385 | var array = [T](repeating: T.zero, count: self.count/MemoryLayout.stride)
386 | _ = array.withUnsafeMutableBytes { copyBytes(to: $0) }
387 | return array
388 | }
389 | }
390 |
391 | // MARK: - Constants
392 |
393 | private enum Constant {
394 | static let jpegCompressionQuality: CGFloat = 0.8
395 | }
396 |
397 |
--------------------------------------------------------------------------------
/ios/Ruse/UIKitExtension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Licensed under the Apache License, Version 2.0 (the "License");
3 | // you may not use this file except in compliance with the License.
4 | // You may obtain a copy of the License at
5 | //
6 | // http://www.apache.org/licenses/LICENSE-2.0
7 | //
8 | // Unless required by applicable law or agreed to in writing, software
9 | // distributed under the License is distributed on an "AS IS" BASIS,
10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | // See the License for the specific language governing permissions and
12 | // limitations under the License.
13 |
14 | import UIKit
15 |
16 | /// Helper functions for the UIImage class
17 | extension UIImage {
18 |
19 | /// Helper function to center-crop image.
20 | /// - Returns: Center-cropped copy of this image
21 | func cropCenter() -> UIImage? {
22 | // Don't do anything if the image is already square.
23 | guard size.height != size.width else {
24 | return self
25 | }
26 | let isPortrait = size.height > size.width
27 | let smallestDimension = min(size.width, size.height)
28 | let croppedSize = CGSize(width: smallestDimension, height: smallestDimension)
29 | let croppedRect = CGRect(origin: .zero, size: croppedSize)
30 |
31 | UIGraphicsBeginImageContextWithOptions(croppedSize, false, scale)
32 | let croppingOrigin = CGPoint(
33 | x: isPortrait ? 0 : floor((size.width - size.height) / 2),
34 | y: isPortrait ? floor((size.height - size.width) / 2) : 0
35 | )
36 | guard let cgImage = cgImage?.cropping(to: CGRect(origin: croppingOrigin, size: croppedSize))
37 | else { return nil }
38 | UIImage(cgImage: cgImage).draw(in: croppedRect)
39 | let croppedImage = UIGraphicsGetImageFromCurrentImageContext()
40 | UIGraphicsEndImageContext()
41 |
42 | return croppedImage
43 | }
44 |
45 | public func correctlyOrientedImage() -> UIImage {
46 | if self.imageOrientation == UIImage.Orientation.up {
47 | return self
48 | }
49 | UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
50 | self.draw(in: CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height))
51 | let normalizedImage = UIGraphicsGetImageFromCurrentImageContext()!;
52 | UIGraphicsEndImageContext();
53 |
54 | return normalizedImage;
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/ios/Ruse/UIUtilities.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2018 Google Inc.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 | //
16 |
17 | import AVFoundation
18 | import MLKit
19 | import UIKit
20 |
21 | /// Defines UI-related utilitiy methods for vision detection.
22 | public class UIUtilities {
23 |
24 | // MARK: - Public
25 |
26 | public static func addCircle(
27 | atPoint point: CGPoint,
28 | to view: UIView,
29 | color: UIColor,
30 | radius: CGFloat
31 | ) {
32 | let divisor: CGFloat = 2.0
33 | let xCoord = point.x - radius / divisor
34 | let yCoord = point.y - radius / divisor
35 | let circleRect = CGRect(x: xCoord, y: yCoord, width: radius, height: radius)
36 | guard circleRect.isValid() else { return }
37 | let circleView = UIView(frame: circleRect)
38 | circleView.layer.cornerRadius = radius / divisor
39 | circleView.alpha = Constants.circleViewAlpha
40 | circleView.backgroundColor = color
41 | view.addSubview(circleView)
42 | }
43 |
44 | public static func addLineSegment(
45 | fromPoint: CGPoint, toPoint: CGPoint, inView: UIView, color: UIColor, width: CGFloat
46 | ) {
47 | let path = UIBezierPath()
48 | path.move(to: fromPoint)
49 | path.addLine(to: toPoint)
50 | let lineLayer = CAShapeLayer()
51 | lineLayer.path = path.cgPath
52 | lineLayer.strokeColor = color.cgColor
53 | lineLayer.fillColor = nil
54 | lineLayer.opacity = 1.0
55 | lineLayer.lineWidth = width
56 | let lineView = UIView()
57 | lineView.layer.addSublayer(lineLayer)
58 | inView.addSubview(lineView)
59 | }
60 |
61 | public static func addRectangle(_ rectangle: CGRect, to view: UIView, color: UIColor) {
62 | guard rectangle.isValid() else { return }
63 | let rectangleView = UIView(frame: rectangle)
64 | rectangleView.layer.cornerRadius = Constants.rectangleViewCornerRadius
65 | rectangleView.alpha = Constants.rectangleViewAlpha
66 | // rectangleView.backgroundColor = color
67 | rectangleView.layer.borderWidth = 5
68 | rectangleView.layer.borderColor = color.cgColor
69 | view.addSubview(rectangleView)
70 | }
71 |
72 | public static func addShape(withPoints points: [NSValue]?, to view: UIView, color: UIColor) {
73 | guard let points = points else { return }
74 | let path = UIBezierPath()
75 | for (index, value) in points.enumerated() {
76 | let point = value.cgPointValue
77 | if index == 0 {
78 | path.move(to: point)
79 | } else {
80 | path.addLine(to: point)
81 | }
82 | if index == points.count - 1 {
83 | path.close()
84 | }
85 | }
86 | let shapeLayer = CAShapeLayer()
87 | shapeLayer.path = path.cgPath
88 | shapeLayer.fillColor = color.cgColor
89 | let rect = CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height)
90 | let shapeView = UIView(frame: rect)
91 | shapeView.alpha = Constants.shapeViewAlpha
92 | shapeView.layer.addSublayer(shapeLayer)
93 | view.addSubview(shapeView)
94 | }
95 |
96 | public static func imageOrientation(
97 | fromDevicePosition devicePosition: AVCaptureDevice.Position = .back
98 | ) -> UIImage.Orientation {
99 | var deviceOrientation = UIDevice.current.orientation
100 | if deviceOrientation == .faceDown || deviceOrientation == .faceUp
101 | || deviceOrientation
102 | == .unknown
103 | {
104 | deviceOrientation = currentUIOrientation()
105 | }
106 | switch deviceOrientation {
107 | case .portrait:
108 | return devicePosition == .front ? .leftMirrored : .right
109 | case .landscapeLeft:
110 | return devicePosition == .front ? .downMirrored : .up
111 | case .portraitUpsideDown:
112 | return devicePosition == .front ? .rightMirrored : .left
113 | case .landscapeRight:
114 | return devicePosition == .front ? .upMirrored : .down
115 | case .faceDown, .faceUp, .unknown:
116 | return .up
117 | @unknown default:
118 | fatalError()
119 | }
120 | }
121 |
122 |
123 |
124 | // MARK: - Private
125 |
126 | private static func currentUIOrientation() -> UIDeviceOrientation {
127 | let deviceOrientation = { () -> UIDeviceOrientation in
128 | switch UIApplication.shared.statusBarOrientation {
129 | case .landscapeLeft:
130 | return .landscapeRight
131 | case .landscapeRight:
132 | return .landscapeLeft
133 | case .portraitUpsideDown:
134 | return .portraitUpsideDown
135 | case .portrait, .unknown:
136 | return .portrait
137 | @unknown default:
138 | fatalError()
139 | }
140 | }
141 | guard Thread.isMainThread else {
142 | var currentOrientation: UIDeviceOrientation = .portrait
143 | DispatchQueue.main.sync {
144 | currentOrientation = deviceOrientation()
145 | }
146 | return currentOrientation
147 | }
148 | return deviceOrientation()
149 | }
150 | }
151 |
152 | // MARK: - Constants
153 |
154 | private enum Constants {
155 | static let circleViewAlpha: CGFloat = 0.7
156 | static let rectangleViewAlpha: CGFloat = 0.3
157 | static let shapeViewAlpha: CGFloat = 0.3
158 | static let rectangleViewCornerRadius: CGFloat = 10.0
159 | }
160 |
161 | // MARK: - Extension
162 |
163 | extension CGRect {
164 | /// Returns a `Bool` indicating whether the rectangle's values are valid`.
165 | func isValid() -> Bool {
166 | return
167 | !(origin.x.isNaN || origin.y.isNaN || width.isNaN || height.isNaN || width < 0 || height < 0
168 | || origin.x < 0 || origin.y < 0)
169 | }
170 | }
171 |
--------------------------------------------------------------------------------