Repository: fritzlabs/fritz-examples
Branch: master
Commit: 3a74ba7fd269
Files: 701
Total size: 1.9 MB
Directory structure:
gitextract_je7ujmkl/
├── .gitattributes
├── .gitignore
├── .swiftlint.yml
├── Android/
│ ├── .gitignore
│ ├── AnimatedSkyApp/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── .gitignore
│ │ │ ├── build.gradle
│ │ │ ├── proguard-rules.pro
│ │ │ └── src/
│ │ │ └── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java/
│ │ │ │ └── ai/
│ │ │ │ └── fritz/
│ │ │ │ └── animatedSky/
│ │ │ │ ├── AutoFitTextureView.java
│ │ │ │ ├── BaseCameraActivity.java
│ │ │ │ ├── CameraConnectionFragment.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── OverlayView.java
│ │ │ └── res/
│ │ │ ├── drawable/
│ │ │ │ ├── ic_close.xml
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ └── round_button.xml
│ │ │ ├── drawable-v24/
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── layout/
│ │ │ │ ├── activity_main.xml
│ │ │ │ └── sky_fragment.xml
│ │ │ ├── mipmap-anydpi-v26/
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ └── values/
│ │ │ ├── colors.xml
│ │ │ ├── dimen.xml
│ │ │ ├── fritz.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── build.gradle
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ └── settings.gradle
│ ├── BackgroundReplacementApp/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── .gitignore
│ │ │ ├── build.gradle
│ │ │ ├── proguard-rules.pro
│ │ │ └── src/
│ │ │ └── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java/
│ │ │ │ └── ai/
│ │ │ │ └── fritz/
│ │ │ │ └── replaceBackground/
│ │ │ │ ├── AutoFitTextureView.java
│ │ │ │ ├── BaseCameraActivity.java
│ │ │ │ ├── CameraConnectionFragment.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── OverlayView.java
│ │ │ └── res/
│ │ │ ├── drawable/
│ │ │ │ ├── ic_close.xml
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ └── round_button.xml
│ │ │ ├── drawable-v24/
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── layout/
│ │ │ │ ├── activity_main.xml
│ │ │ │ └── camera_connection_fragment_background_replace.xml
│ │ │ ├── mipmap-anydpi-v26/
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ └── values/
│ │ │ ├── colors.xml
│ │ │ ├── dimen.xml
│ │ │ ├── fritz.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── build.gradle
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ └── settings.gradle
│ ├── CameraBoilerplateApp/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── .gitignore
│ │ │ ├── build.gradle
│ │ │ ├── proguard-rules.pro
│ │ │ └── src/
│ │ │ ├── androidTest/
│ │ │ │ └── java/
│ │ │ │ └── ai/
│ │ │ │ └── fritz/
│ │ │ │ └── camera/
│ │ │ │ └── ExampleInstrumentedTest.java
│ │ │ ├── main/
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ ├── java/
│ │ │ │ │ └── ai/
│ │ │ │ │ └── fritz/
│ │ │ │ │ └── camera/
│ │ │ │ │ ├── AutoFitTextureView.java
│ │ │ │ │ ├── BaseCameraActivity.java
│ │ │ │ │ ├── CameraConnectionFragment.java
│ │ │ │ │ ├── LiveCameraActivity.java
│ │ │ │ │ ├── MainActivity.java
│ │ │ │ │ └── OverlayView.java
│ │ │ │ └── res/
│ │ │ │ ├── drawable/
│ │ │ │ │ ├── ic_close.xml
│ │ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ │ └── round_button.xml
│ │ │ │ ├── drawable-v24/
│ │ │ │ │ └── ic_launcher_foreground.xml
│ │ │ │ ├── layout/
│ │ │ │ │ ├── activity_main.xml
│ │ │ │ │ ├── camera_connection_fragment.xml
│ │ │ │ │ └── camera_connection_fragment_stylize.xml
│ │ │ │ ├── mipmap-anydpi-v26/
│ │ │ │ │ ├── ic_launcher.xml
│ │ │ │ │ └── ic_launcher_round.xml
│ │ │ │ └── values/
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── fritz.xml
│ │ │ │ ├── strings.xml
│ │ │ │ └── styles.xml
│ │ │ └── test/
│ │ │ └── java/
│ │ │ └── ai/
│ │ │ └── fritz/
│ │ │ └── camera/
│ │ │ └── ExampleUnitTest.java
│ │ ├── build.gradle
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ └── settings.gradle
│ ├── FritzAIStudio/
│ │ ├── LICENSE.md
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── build.gradle
│ │ │ ├── proguard-rules.pro
│ │ │ └── src/
│ │ │ └── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── assets/
│ │ │ │ ├── coco_labels_list.txt
│ │ │ │ └── imagenet_comp_graph_label_strings.txt
│ │ │ ├── java/
│ │ │ │ └── ai/
│ │ │ │ └── fritz/
│ │ │ │ └── aistudio/
│ │ │ │ ├── Navigation.java
│ │ │ │ ├── PredictorType.java
│ │ │ │ ├── activities/
│ │ │ │ │ ├── BaseCameraActivity.java
│ │ │ │ │ ├── BaseLiveVideoActivity.java
│ │ │ │ │ ├── BaseRecordingActivity.java
│ │ │ │ │ ├── MainActivity.java
│ │ │ │ │ ├── SplashActivity.java
│ │ │ │ │ ├── custommodel/
│ │ │ │ │ │ ├── CustomTFLiteActivity.java
│ │ │ │ │ │ └── ml/
│ │ │ │ │ │ ├── Classifier.java
│ │ │ │ │ │ └── MnistClassifier.java
│ │ │ │ │ └── vision/
│ │ │ │ │ ├── ImageLabelingActivity.java
│ │ │ │ │ ├── ImageSegmentationActivity.java
│ │ │ │ │ ├── ObjectDetectionActivity.java
│ │ │ │ │ ├── PoseEstimationActivity.java
│ │ │ │ │ └── StyleTransferActivity.java
│ │ │ │ ├── adapters/
│ │ │ │ │ ├── DemoAdapter.java
│ │ │ │ │ └── DemoItem.java
│ │ │ │ ├── fragments/
│ │ │ │ │ └── CameraConnectionFragment.java
│ │ │ │ ├── ui/
│ │ │ │ │ ├── AutoFitTextureView.java
│ │ │ │ │ ├── ChooseModelDialog.java
│ │ │ │ │ ├── DrawModel.java
│ │ │ │ │ ├── DrawRenderer.java
│ │ │ │ │ ├── DrawView.java
│ │ │ │ │ ├── OverlayView.java
│ │ │ │ │ ├── RecognitionScoreView.java
│ │ │ │ │ ├── ResultsView.java
│ │ │ │ │ └── SeparatorDecoration.java
│ │ │ │ └── utils/
│ │ │ │ └── VideoProcessingQueue.java
│ │ │ └── res/
│ │ │ ├── drawable/
│ │ │ │ ├── circle.xml
│ │ │ │ ├── circle_white.xml
│ │ │ │ ├── ic_close.xml
│ │ │ │ ├── ic_heartbeat_logo.xml
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ ├── round_button.xml
│ │ │ │ └── splash_bg.xml
│ │ │ ├── drawable-v24/
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── font/
│ │ │ │ ├── sf_display.xml
│ │ │ │ ├── sf_ui_display_bold.otf
│ │ │ │ ├── sf_ui_display_light.otf
│ │ │ │ └── sf_ui_display_regular.otf
│ │ │ ├── layout/
│ │ │ │ ├── activity_camera.xml
│ │ │ │ ├── activity_fritz_vision.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── activity_mnist.xml
│ │ │ │ ├── activity_tfmobile_camera.xml
│ │ │ │ ├── app_bar.xml
│ │ │ │ ├── camera_connection_fragment.xml
│ │ │ │ ├── camera_connection_fragment_recording.xml
│ │ │ │ ├── camera_connection_fragment_stylize.xml
│ │ │ │ ├── camera_connection_fragment_tracking.xml
│ │ │ │ ├── camera_connection_snapshot.xml
│ │ │ │ ├── list_item_demo.xml
│ │ │ │ └── list_text_item.xml
│ │ │ ├── mipmap-anydpi-v26/
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ └── values/
│ │ │ ├── colors.xml
│ │ │ ├── custom_models.xml
│ │ │ ├── dimens.xml
│ │ │ ├── fritz.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── build.gradle
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ └── settings.gradle
│ ├── FritzVisionVideoApp/
│ │ ├── .gitignore
│ │ ├── app/
│ │ │ ├── .gitignore
│ │ │ ├── build.gradle
│ │ │ ├── gradlew
│ │ │ ├── gradlew.bat
│ │ │ ├── proguard-rules.pro
│ │ │ └── src/
│ │ │ ├── androidTest/
│ │ │ │ └── java/
│ │ │ │ └── ai/
│ │ │ │ └── fritz/
│ │ │ │ └── fritzvisionvideo/
│ │ │ │ └── ExampleInstrumentedTest.java
│ │ │ ├── main/
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ ├── java/
│ │ │ │ │ └── ai/
│ │ │ │ │ └── fritz/
│ │ │ │ │ └── fritzvisionvideo/
│ │ │ │ │ ├── MainActivity.java
│ │ │ │ │ ├── VideoActivity.java
│ │ │ │ │ ├── strategies/
│ │ │ │ │ │ ├── MaskCutStrategy.java
│ │ │ │ │ │ ├── ObjectPoseStrategy.java
│ │ │ │ │ │ ├── PoseDoubleMaskStrategy.java
│ │ │ │ │ │ ├── StylizeBackgroundStrategy.java
│ │ │ │ │ │ ├── StylizeHairStrategy.java
│ │ │ │ │ │ ├── VideoFilterStrategy.java
│ │ │ │ │ │ └── customfilters/
│ │ │ │ │ │ └── StylizeHairFilter.java
│ │ │ │ │ └── ui/
│ │ │ │ │ ├── DemoAdapter.java
│ │ │ │ │ ├── DemoItem.java
│ │ │ │ │ └── SeparatorDecoration.java
│ │ │ │ └── res/
│ │ │ │ ├── drawable/
│ │ │ │ │ └── ic_launcher_background.xml
│ │ │ │ ├── drawable-v24/
│ │ │ │ │ └── ic_launcher_foreground.xml
│ │ │ │ ├── font/
│ │ │ │ │ ├── sf_display.xml
│ │ │ │ │ ├── sf_ui_display_bold.otf
│ │ │ │ │ ├── sf_ui_display_light.otf
│ │ │ │ │ └── sf_ui_display_regular.otf
│ │ │ │ ├── layout/
│ │ │ │ │ ├── activity_main.xml
│ │ │ │ │ ├── activity_video.xml
│ │ │ │ │ ├── app_bar.xml
│ │ │ │ │ └── list_item_demo.xml
│ │ │ │ ├── menu/
│ │ │ │ │ └── video_menu.xml
│ │ │ │ ├── mipmap-anydpi-v26/
│ │ │ │ │ ├── ic_launcher.xml
│ │ │ │ │ └── ic_launcher_round.xml
│ │ │ │ └── values/
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── fritz.xml
│ │ │ │ ├── strings.xml
│ │ │ │ └── styles.xml
│ │ │ └── test/
│ │ │ └── java/
│ │ │ └── ai/
│ │ │ └── fritz/
│ │ │ └── fritzvisionvideo/
│ │ │ └── ExampleUnitTest.java
│ │ ├── build.gradle
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ └── settings.gradle
│ ├── HairColoringApp/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── .gitignore
│ │ │ ├── build.gradle
│ │ │ ├── proguard-rules.pro
│ │ │ └── src/
│ │ │ ├── androidTest/
│ │ │ │ └── java/
│ │ │ │ └── ai/
│ │ │ │ └── fritz/
│ │ │ │ └── haircoloring/
│ │ │ │ └── ExampleInstrumentedTest.java
│ │ │ ├── main/
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ ├── java/
│ │ │ │ │ └── ai/
│ │ │ │ │ └── fritz/
│ │ │ │ │ └── haircoloring/
│ │ │ │ │ ├── activities/
│ │ │ │ │ │ ├── BaseCameraActivity.java
│ │ │ │ │ │ ├── BaseLiveGPUActivity.java
│ │ │ │ │ │ ├── LiveHairColorActivity.java
│ │ │ │ │ │ ├── MainActivity.java
│ │ │ │ │ │ └── VideoHairColorActivity.java
│ │ │ │ │ ├── ui/
│ │ │ │ │ │ ├── DemoAdapter.java
│ │ │ │ │ │ ├── DemoItem.java
│ │ │ │ │ │ └── SeparatorDecoration.java
│ │ │ │ │ └── views/
│ │ │ │ │ ├── AutoFitTextureView.java
│ │ │ │ │ ├── CameraConnectionFragment.java
│ │ │ │ │ └── OverlayView.java
│ │ │ │ └── res/
│ │ │ │ ├── drawable/
│ │ │ │ │ ├── ic_close.xml
│ │ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ │ └── round_button.xml
│ │ │ │ ├── drawable-v24/
│ │ │ │ │ └── ic_launcher_foreground.xml
│ │ │ │ ├── font/
│ │ │ │ │ ├── sf_display.xml
│ │ │ │ │ ├── sf_ui_display_bold.otf
│ │ │ │ │ ├── sf_ui_display_light.otf
│ │ │ │ │ └── sf_ui_display_regular.otf
│ │ │ │ ├── layout/
│ │ │ │ │ ├── activity_camera.xml
│ │ │ │ │ ├── activity_main.xml
│ │ │ │ │ ├── activity_video.xml
│ │ │ │ │ ├── camera_color_slider.xml
│ │ │ │ │ ├── camera_connection_fragment.xml
│ │ │ │ │ └── list_item_demo.xml
│ │ │ │ ├── menu/
│ │ │ │ │ └── video_menu.xml
│ │ │ │ ├── mipmap-anydpi-v26/
│ │ │ │ │ ├── ic_launcher.xml
│ │ │ │ │ └── ic_launcher_round.xml
│ │ │ │ └── values/
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── fritz.xml
│ │ │ │ ├── strings.xml
│ │ │ │ └── styles.xml
│ │ │ └── test/
│ │ │ └── java/
│ │ │ └── ai/
│ │ │ └── fritz/
│ │ │ └── haircoloring/
│ │ │ └── ExampleUnitTest.java
│ │ ├── build.gradle
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ └── settings.gradle
│ ├── ImageLabelingApp/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── .gitignore
│ │ │ ├── build.gradle
│ │ │ ├── proguard-rules.pro
│ │ │ └── src/
│ │ │ └── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── assets/
│ │ │ │ ├── image_labeling_quantized_model.tflite
│ │ │ │ └── label_recording_model.json
│ │ │ ├── java/
│ │ │ │ └── ai/
│ │ │ │ └── fritz/
│ │ │ │ └── camera/
│ │ │ │ ├── AutoFitTextureView.java
│ │ │ │ ├── BaseCameraActivity.java
│ │ │ │ ├── CameraConnectionFragment.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── OverlayView.java
│ │ │ └── res/
│ │ │ ├── drawable/
│ │ │ │ ├── ic_close.xml
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ └── round_button.xml
│ │ │ ├── drawable-v24/
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── layout/
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── camera_connection_fragment.xml
│ │ │ │ ├── main_camera.xml
│ │ │ │ ├── snapshot_button.xml
│ │ │ │ └── snapshot_overlay_frame.xml
│ │ │ ├── mipmap-anydpi-v26/
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ └── values/
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── fritz.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── build.gradle
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ └── settings.gradle
│ ├── ObjectDetectionApp/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── .gitignore
│ │ │ ├── build.gradle
│ │ │ ├── proguard-rules.pro
│ │ │ └── src/
│ │ │ └── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── assets/
│ │ │ │ ├── detect.tflite
│ │ │ │ └── object_recording_model.json
│ │ │ ├── java/
│ │ │ │ └── ai/
│ │ │ │ └── fritz/
│ │ │ │ └── camera/
│ │ │ │ ├── AutoFitTextureView.java
│ │ │ │ ├── BaseCameraActivity.java
│ │ │ │ ├── CameraConnectionFragment.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── OverlayView.java
│ │ │ └── res/
│ │ │ ├── drawable/
│ │ │ │ ├── ic_close.xml
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ └── round_button.xml
│ │ │ ├── drawable-v24/
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── layout/
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── camera_connection_fragment.xml
│ │ │ │ ├── main_camera.xml
│ │ │ │ ├── snapshot_button.xml
│ │ │ │ └── snapshot_overlay_frame.xml
│ │ │ ├── mipmap-anydpi-v26/
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ └── values/
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── fritz.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── build.gradle
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ └── settings.gradle
│ ├── PetMonitoringApp/
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── .gitignore
│ │ │ ├── build.gradle
│ │ │ ├── proguard-rules.pro
│ │ │ └── src/
│ │ │ └── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java/
│ │ │ │ └── ai/
│ │ │ │ └── fritz/
│ │ │ │ └── petdetector/
│ │ │ │ ├── AutoFitTextureView.java
│ │ │ │ ├── BaseCameraActivity.java
│ │ │ │ ├── CameraConnectionFragment.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── OverlayView.java
│ │ │ └── res/
│ │ │ ├── drawable/
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ └── ic_warning.xml
│ │ │ ├── drawable-v24/
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── layout/
│ │ │ │ ├── activity_main.xml
│ │ │ │ └── camera_connection_fragment_stylize.xml
│ │ │ ├── mipmap-anydpi-v26/
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ └── values/
│ │ │ ├── colors.xml
│ │ │ ├── fritz.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── build.gradle
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ └── settings.gradle
│ ├── PetStickerApp/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── .gitignore
│ │ │ ├── build.gradle
│ │ │ ├── proguard-rules.pro
│ │ │ └── src/
│ │ │ └── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java/
│ │ │ │ └── ai/
│ │ │ │ └── fritz/
│ │ │ │ └── petSticker/
│ │ │ │ ├── AutoFitTextureView.java
│ │ │ │ ├── BaseCameraActivity.java
│ │ │ │ ├── CameraConnectionFragment.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── OverlayView.java
│ │ │ └── res/
│ │ │ ├── drawable/
│ │ │ │ ├── ic_close.xml
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ └── round_button.xml
│ │ │ ├── drawable-v24/
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── layout/
│ │ │ │ ├── activity_main.xml
│ │ │ │ └── camera_connection_fragment_pet_sticker.xml
│ │ │ ├── mipmap-anydpi-v26/
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ └── values/
│ │ │ ├── colors.xml
│ │ │ ├── dimen.xml
│ │ │ ├── fritz.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── build.gradle
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ └── settings.gradle
│ ├── PoseEstimationApp/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── .gitignore
│ │ │ ├── build.gradle
│ │ │ ├── proguard-rules.pro
│ │ │ └── src/
│ │ │ └── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── assets/
│ │ │ │ ├── PoseMobilenet353x25758Large1565627685.tflite
│ │ │ │ └── pose_recording_model.json
│ │ │ ├── java/
│ │ │ │ └── ai/
│ │ │ │ └── fritz/
│ │ │ │ └── camera/
│ │ │ │ ├── AutoFitTextureView.java
│ │ │ │ ├── BaseCameraActivity.java
│ │ │ │ ├── CameraConnectionFragment.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── OverlayView.java
│ │ │ └── res/
│ │ │ ├── drawable/
│ │ │ │ ├── ic_close.xml
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ └── round_button.xml
│ │ │ ├── drawable-v24/
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── layout/
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── camera_connection_fragment.xml
│ │ │ │ ├── main_camera.xml
│ │ │ │ ├── snapshot_button.xml
│ │ │ │ └── snapshot_overlay_frame.xml
│ │ │ ├── mipmap-anydpi-v26/
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ └── values/
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── fritz.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── build.gradle
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ └── settings.gradle
│ └── README.md
├── LICENSE
├── README.md
├── SnapLensStudio/
│ └── README.md
└── iOS/
├── .gitignore
├── FritzAIStudio/
│ ├── FritzAIStudio/
│ │ ├── Media/
│ │ │ ├── Assets.xcassets/
│ │ │ │ ├── AppIcon.appiconset/
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Contents.json
│ │ │ │ ├── SettingsIcon.imageset/
│ │ │ │ │ └── Contents.json
│ │ │ │ └── fritzLogo.imageset/
│ │ │ │ └── Contents.json
│ │ │ └── Base.lproj/
│ │ │ └── LaunchScreen.storyboard
│ │ ├── Source/
│ │ │ ├── Delegate/
│ │ │ │ └── AppDelegate.swift
│ │ │ ├── FritzCamera/
│ │ │ │ ├── FritzCamera/
│ │ │ │ │ ├── Delegates/
│ │ │ │ │ │ ├── CameraAVCaptureVideoDelegate.swift
│ │ │ │ │ │ └── CapturePhotoDelgate.swift
│ │ │ │ │ ├── Extensions/
│ │ │ │ │ │ ├── AVPhotoExtension.swift
│ │ │ │ │ │ ├── CameraResolutionExtension.swift
│ │ │ │ │ │ ├── CameraSessionExtensions.swift
│ │ │ │ │ │ ├── FritzCamera+CaptureDeviceExtensions.swift
│ │ │ │ │ │ ├── FritzCamera+Focus.swift
│ │ │ │ │ │ └── PhotoCaptureExtensions.swift
│ │ │ │ │ └── FritzCamera.swift
│ │ │ │ └── FritzCameraUI/
│ │ │ │ ├── CameraDelegateImplmentation.swift
│ │ │ │ ├── Extensions/
│ │ │ │ │ ├── ButtonFunctionality.swift
│ │ │ │ │ ├── CameraInterfaceHandler.swift
│ │ │ │ │ ├── DebugImageExtensions.swift
│ │ │ │ │ ├── FocusExtension.swift
│ │ │ │ │ └── GestureReconizerDelegateExtension.swift
│ │ │ │ ├── FritzCameraButton.swift
│ │ │ │ ├── FritzCameraControllerDelegate.swift
│ │ │ │ ├── FritzCameraViewController.swift
│ │ │ │ ├── FritzTextPromptView.swift
│ │ │ │ ├── Media.xcassets/
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ ├── cameraFocus.imageset/
│ │ │ │ │ │ └── Contents.json
│ │ │ │ │ ├── cameraSwitch.imageset/
│ │ │ │ │ │ └── Contents.json
│ │ │ │ │ ├── cameraTorchAuto.imageset/
│ │ │ │ │ │ └── Contents.json
│ │ │ │ │ ├── cameraTorchOff.imageset/
│ │ │ │ │ │ └── Contents.json
│ │ │ │ │ └── cameraTorchOn.imageset/
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Shaders/
│ │ │ │ ├── DepthToGrayscale.metal
│ │ │ │ ├── Mixer.metal
│ │ │ │ ├── PassThrough.metal
│ │ │ │ └── RosyEffect.metal
│ │ │ └── Modules/
│ │ │ ├── Common/
│ │ │ │ ├── AIStudioFeaturePredictors.swift
│ │ │ │ ├── AIStudioImagePredictor.swift
│ │ │ │ ├── FritzModelDetails.swift
│ │ │ │ ├── ImagePredictorProtocol.swift
│ │ │ │ ├── Logging/
│ │ │ │ │ ├── Locks.swift
│ │ │ │ │ ├── LogHandler.swift
│ │ │ │ │ └── Logging.swift
│ │ │ │ ├── ModelGroupManager.swift
│ │ │ │ ├── PredictorOptionTypes.swift
│ │ │ │ └── Utils/
│ │ │ │ ├── FritzDeviceUtil.swift
│ │ │ │ └── FritzLogger.swift
│ │ │ ├── Demos/
│ │ │ │ ├── Cells/
│ │ │ │ │ ├── DemoTableViewCell.swift
│ │ │ │ │ └── LinkTableViewCell.swift
│ │ │ │ ├── Demos.storyboard
│ │ │ │ ├── DemosViewController.swift
│ │ │ │ └── NavigationController.swift
│ │ │ ├── FeatureController/
│ │ │ │ ├── ChooseModelController.swift
│ │ │ │ ├── ConfigureModelCells/
│ │ │ │ │ ├── ChooseColorCell.swift
│ │ │ │ │ ├── ChooseColorCell.xib
│ │ │ │ │ ├── ChooseModelCell.swift
│ │ │ │ │ ├── ChooseModelCell.xib
│ │ │ │ │ ├── RangeSliderCell.swift
│ │ │ │ │ ├── RangeSliderCell.xib
│ │ │ │ │ ├── SegmentSliderCell.swift
│ │ │ │ │ └── SegmentSliderCell.xib
│ │ │ │ ├── ConfigurePopover.swift
│ │ │ │ ├── Extensions/
│ │ │ │ │ ├── NavigationBarCustomization.swift
│ │ │ │ │ └── SettingsButtonInteractionExtension.swift
│ │ │ │ ├── FeatureContainer.xib
│ │ │ │ ├── FeatureOptions.swift
│ │ │ │ ├── FeatureViewController.swift
│ │ │ │ ├── ModelOptions.storyboard
│ │ │ │ └── SettingsButton.swift
│ │ │ ├── ImageSegmentation/
│ │ │ │ └── ImageSegFeature.swift
│ │ │ ├── ModelExtensions/
│ │ │ │ ├── ImageSegmentationFeature.swift
│ │ │ │ └── PoseEstimation+ImagePredictor.swift
│ │ │ ├── PredictorControllers/
│ │ │ │ ├── DetectObjects/
│ │ │ │ │ ├── DetectObjectsStoryboard.storyboard
│ │ │ │ │ └── DetectObjectsViewController.swift
│ │ │ │ ├── HairColor+ColorSlider.swift
│ │ │ │ ├── HairColorViewController.swift
│ │ │ │ ├── ImageSegmentationViewController.swift
│ │ │ │ ├── LabelImages/
│ │ │ │ │ ├── FritzVisionLabelViewController.swift
│ │ │ │ │ └── LabelImages.storyboard
│ │ │ │ ├── PoseEstimationViewController.swift
│ │ │ │ └── StyleTransferViewController.swift
│ │ │ └── Root/
│ │ │ ├── Root.storyboard
│ │ │ └── RootViewController.swift
│ │ └── Supporting Files/
│ │ └── Info.plist
│ ├── FritzAIStudio.xcodeproj/
│ │ ├── project.pbxproj
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── FritzAIStudio.xcscheme
│ ├── LICENSE
│ ├── Podfile
│ ├── README.md
│ └── fastlane/
│ ├── Appfile
│ ├── Deliverfile
│ └── Fastfile
├── FritzARKitDemo/
│ ├── ARKIt+Utilities.swift
│ ├── FritzARKitDemo/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ ├── ObjectDetectionViewController.swift
│ │ ├── SCNVector+Extensions.swift
│ │ ├── Utils.swift
│ │ ├── ViewController.swift
│ │ └── art.scnassets/
│ │ ├── box.scn
│ │ └── ship.scn
│ ├── FritzARKitDemo.xcodeproj/
│ │ ├── project.pbxproj
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── FritzARKitDemo.xcscheme
│ ├── Podfile
│ └── README.md
├── FritzHairColorDemo/
│ ├── FritzHairColorDemo/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Controllers/
│ │ │ ├── HairPredictor.swift
│ │ │ ├── LiveHairViewController.swift
│ │ │ ├── NavigationController.swift
│ │ │ ├── VideoHairViewController.swift
│ │ │ └── ViewController.swift
│ │ ├── Info.plist
│ │ ├── Storyboards/
│ │ │ ├── Base.lproj/
│ │ │ │ ├── LaunchScreen.storyboard
│ │ │ │ └── LiveHairColorStoryboard.storyboard
│ │ │ ├── Main.storyboard
│ │ │ └── VideoHairViewStoryboard.storyboard
│ │ └── UI/
│ │ ├── DemoTableViewCell.swift
│ │ ├── LinkTableViewCell.swift
│ │ └── VideoPicker.swift
│ ├── FritzHairColorDemo.xcodeproj/
│ │ └── project.pbxproj
│ ├── Podfile
│ └── README.md
├── FritzHandPoseEstimationDemo/
│ ├── FritzHandPoseEstimationDemo/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── CameraButton.swift
│ │ ├── DraggableKeypoint.swift
│ │ ├── HandPose.mlmodel
│ │ ├── HandPoseModel.swift
│ │ ├── Info.plist
│ │ ├── UIImageView+Transformations.swift
│ │ └── ViewController.swift
│ ├── FritzHandPoseEstimationDemo.xcodeproj/
│ │ ├── project.pbxproj
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── FritzHandPoseEstimationDemo.xcscheme
│ ├── Podfile
│ └── README.md
├── FritzImageLabelingDemo/
│ ├── FritzImageLabelingDemo/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ ├── SceneDelegate.swift
│ │ └── ViewController.swift
│ ├── FritzImageLabelingDemo.xcodeproj/
│ │ ├── project.pbxproj
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── FritzImageLabelingDemo.xcscheme
│ ├── Podfile
│ └── README.md
├── FritzImageSegmentationDemo/
│ ├── FritzImageSegmentationDemo/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── CustomBlurView.swift
│ │ ├── Info.plist
│ │ └── ViewController.swift
│ ├── FritzImageSegmentationDemo.xcodeproj/
│ │ └── project.pbxproj
│ ├── Podfile
│ └── README.md
├── FritzMaskRecognitionDemo/
│ ├── FritzMaskRecognitionDemo/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ ├── MaskRecognition.mlmodel
│ │ ├── SceneDelegate.swift
│ │ └── ViewController.swift
│ ├── FritzMaskRecognitionDemo.xcodeproj/
│ │ ├── project.pbxproj
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── FritzMaskRecognitionDemo.xcscheme
│ ├── Podfile
│ └── README.md
├── FritzObjectDetectionDemo/
│ ├── FritzObjectDetectionDemo/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ ├── SceneDelegate.swift
│ │ └── ViewController.swift
│ ├── FritzObjectDetectionDemo.xcodeproj/
│ │ ├── project.pbxproj
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── FritzObjectDetectionDemo.xcscheme
│ └── Podfile
├── FritzPetStickerDemo/
│ ├── FritzPetStickerDemo/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── ViewController.swift
│ ├── FritzPetStickerDemo.xcodeproj/
│ │ ├── project.pbxproj
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── FritzPetStickerDemo.xcscheme
│ ├── Podfile
│ └── README.md
├── FritzPizzaDetectorDemo/
│ ├── Final/
│ │ ├── FritzPizzaDetectorDemo/
│ │ │ ├── AppDelegate.swift
│ │ │ ├── Assets.xcassets/
│ │ │ │ ├── AppIcon.appiconset/
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Contents.json
│ │ │ │ └── pizza.imageset/
│ │ │ │ └── Contents.json
│ │ │ ├── Base.lproj/
│ │ │ │ ├── LaunchScreen.storyboard
│ │ │ │ └── Main.storyboard
│ │ │ ├── Info.plist
│ │ │ └── ViewController.swift
│ │ ├── FritzPizzaDetectorDemo.xcodeproj/
│ │ │ └── project.pbxproj
│ │ └── Podfile
│ ├── README.md
│ └── Starter/
│ ├── FritzPizzaDetectorDemo/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ ├── Contents.json
│ │ │ └── pizza.imageset/
│ │ │ └── Contents.json
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── ViewController.swift
│ ├── FritzPizzaDetectorDemo.xcodeproj/
│ │ └── project.pbxproj
│ └── Podfile
├── FritzPoseEstimationDemo/
│ ├── FritzPoseEstimationDemo/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── ViewController.swift
│ ├── FritzPoseEstimationDemo.xcodeproj/
│ │ └── project.pbxproj
│ ├── Podfile
│ └── README.md
├── FritzSkyReplacementDemo/
│ ├── FritzSkyReplacementDemo/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── ViewController.swift
│ ├── FritzSkyReplacementDemo.xcodeproj/
│ │ └── project.pbxproj
│ ├── Podfile
│ └── README.md
├── FritzStyleTransferDemo/
│ ├── FritzStyleTransferDemo/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── ViewController.swift
│ ├── FritzStyleTransferDemo.xcodeproj/
│ │ └── project.pbxproj
│ ├── Podfile
│ └── README.md
├── FritzVisionVideoDemo/
│ ├── FritzVisionVideoDemo/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ ├── Contents.json
│ │ │ └── ExportIcon.imageset/
│ │ │ └── Contents.json
│ │ ├── Controllers/
│ │ │ ├── NavigationController.swift
│ │ │ ├── SingleScreenViewController.swift
│ │ │ ├── SplitScreenViewController.swift
│ │ │ ├── Strategies/
│ │ │ │ ├── DoubleStyleStrategy.swift
│ │ │ │ ├── FemmesStrategy.swift
│ │ │ │ ├── ObjectPoseStrategy.swift
│ │ │ │ ├── PoseDoubleMaskStrategy.swift
│ │ │ │ ├── ScreamStrategy.swift
│ │ │ │ ├── StyleOptionStrategy.swift
│ │ │ │ ├── StylizeBackgroundStrategy.swift
│ │ │ │ ├── StylizeHairStrategy.swift
│ │ │ │ └── VideoOptionStrategy.swift
│ │ │ └── ViewController.swift
│ │ ├── Info.plist
│ │ ├── SceneDelegate.swift
│ │ ├── Storyboards/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ ├── Main.storyboard
│ │ │ ├── SingleScreenStoryboard.storyboard
│ │ │ └── SplitScreenStoryboard.storyboard
│ │ └── Utils/
│ │ └── StylizeHairMaskFilter.swift
│ ├── FritzVisionVideoDemo.xcodeproj/
│ │ └── project.pbxproj
│ ├── Podfile
│ └── README.md
└── README.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
*.zip filter=lfs diff=lfs merge=lfs -text
================================================
FILE: .gitignore
================================================
#### CocoaPods
Pods/
*.generated.swift
#### Fastlane
fastlane/report.xml
fastlane/README.md
#### macOS
.DS_Store
#### Xcode
xcuserdata/
*.xcworkspace
iOS/*.zip
*.ipa
# For rswift build workaround
rswift.workaround.file
*.Framework/
[Ll]ibrary/
[Tt]emp/
[Oo]bj/
[Bb]uild/
[Bb]uilds/
Assets/AssetStoreTools*
# Visual Studio cache directory
.vs/
# Autogenerated VS/MD/Consulo solution and project files
ExportedObj/
.consulo/
*.csproj
*.unityproj
*.sln
*.suo
*.tmp
*.user
*.userprefs
*.pidb
*.booproj
*.svd
*.pdb
*.opendb
# Unity3D generated meta files
*.pidb.meta
*.pdb.meta
# Unity3D Generated File On Crash Reports
sysinfo.txt
# Builds
*.apk
*.unitypackage
*.Framework/
*.framework.meta
.DS_Store
FritzConfig.asset
FritzConfig.asset.meta
# excluding local python version
.python-version
================================================
FILE: .swiftlint.yml
================================================
included:
- Heartbeat/Source
excluded:
- Pods
- Supporting Files
opt_in_rules:
- attributes
- closure_end_indentation
- empty_count
- explicit_top_level_acl
- force_unwrapping
disabled_rules:
- explicit_top_level_acl
- line_length
================================================
FILE: Android/.gitignore
================================================
# Built application files
*.apk
*.ap_
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Gradle files
.gradle/
build/
gradle/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# Intellij
*.iml
.idea
# Keystore files
*.jks
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
# Google Services (e.g. APIs or Firebase)
google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
.DS_Store
================================================
FILE: Android/AnimatedSkyApp/.gitignore
================================================
# Built application files
*.apk
*.ap_
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.idea
# Keystore files
# Uncomment the following line if you do not want to check your keystore files in.
#*.jks
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
# Google Services (e.g. APIs or Firebase)
google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
================================================
FILE: Android/AnimatedSkyApp/README.md
================================================
# Animations with Sky Segmentation
[ ](https://app.codeship.com/projects/297281)
[](http://twitter.com/fritzlabs)
In this app, we create a simple photo app to replace the sky with an animation.

This example app uses the on-device Image Segmentation API with the Sky Segmentation Model for Android.
- [Overview](https://www.fritz.ai/features/image-segmentation.html?utm_source=github&utm_campaign=fritz-examples)
- [Documentation](https://docs.fritz.ai/develop/vision/image-segmentation/android.html?utm_source=github&utm_campaign=fritz-examples)
## Fritz AI
Fritz AI is the machine learning platform for iOS and Android developers. Teach your mobile apps to see, hear, sense, and think. Start with our ready-to-use feature APIs or connect and deploy your own custom models.
## Requirements
- Android Studio 3.2 or above
- Android device in developer model (USB debugging enabled)
## Getting Started
**Step 1: Choose a Fritz AI Plan**
[Sign up](https://www.fritz.ai/pricing/?utm_source=github&utm_campaign=fritz-examples) for a Fritz AI plan to get started.
Register the Android app in your Fritz account with the package id "ai.fritz.animatedSky". During registration, you'll receive an API key for the app. Save this for later. To find it in the webapp, you can go to Project Settings > Apps > Your App > Show API Key.
**Step 2: Clone / Fork the fritz-examples repository and open the AnimatedSkyApp app in Android Studio**
```
git clone https://github.com/fritzlabs/fritz-examples.git
```
In Android Studio, choose "Open an existing Android Studio project" and select `AnimatedSkyApp`.
**Step 3: Edit the fritz.xml file with your API Key**
In app/src/main/res/values/fritz.xml, change the fritz_api_key attribute with the one you received in step 1.
**Step 4: Build the Android Studio Project**
Select "Build > Make Project" from the top nav. Download any missing libraries if applicable. This should sync the gradle dependencies so give the build a second to complete.
**Step 5: Install the app onto your device**
With your Android device connected, select `Run > Run App` from the top nav. When running the app for the first time, you'll have to give permissions to access the camera. After the app is installed and running, take a picture outside and watch as we replace the sky with an animated image.
## Official Documentation
[SDK Documentation](https://docs.fritz.ai/?utm_source=github&utm_campaign=fritz-examples)
[Android API Docs](https://docs.fritz.ai/android/latest/index.html?utm_source=github&utm_campaign=fritz-examples)
## Join the community
[Heartbeat](https://heartbeat.fritz.ai/?utm_source=github&utm_campaign=fritz-examples) is a community of developers interested in the intersection of mobile and machine learning. [Chat with us in Slack](https://www.fritz.ai/slack?utm_source=github&utm_campaign=fritz-examples) and stay up to date on the latest mobile ML news with our [Newsletter](https://www.fritz.ai/newsletter?utm_source=github&utm_campaign=fritz-examples).
## Help
For any questions or issues, you can:
- Submit an issue on this repo
- Go to [Support](https://support.fritz.ai/?utm_source=github&utm_campaign=fritz-examples)
- Message us directly in [Slack](https://www.fritz.ai/slack?utm_source=github&utm_campaign=fritz-examples)
================================================
FILE: Android/AnimatedSkyApp/app/.gitignore
================================================
/build
================================================
FILE: Android/AnimatedSkyApp/app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
// MUST MATCH THE APPLICATION YOU CREATE IN FRITZ
applicationId "ai.fritz.animatedSky"
minSdkVersion 24
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
debuggable true
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
aaptOptions {
noCompress "tflite"
}
lintOptions {
abortOnError false
}
}
dependencies {
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation "ai.fritz:vision:6.0.3"
implementation "ai.fritz:vision-sky-segmentation-model-fast:3.0.0"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
================================================
FILE: Android/AnimatedSkyApp/app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: Android/AnimatedSkyApp/app/src/main/AndroidManifest.xml
================================================
================================================
FILE: Android/AnimatedSkyApp/app/src/main/java/ai/fritz/animatedSky/AutoFitTextureView.java
================================================
package ai.fritz.animatedSky;
import android.content.Context;
import android.util.AttributeSet;
import android.view.TextureView;
/**
* A {@link TextureView} that can be adjusted to a specified aspect ratio.
*/
public class AutoFitTextureView extends TextureView {
private int mRatioWidth = 0;
private int mRatioHeight = 0;
public AutoFitTextureView(Context context) {
this(context, null);
}
public AutoFitTextureView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* Sets the aspect ratio for this view. The size of the view will be measured based on the ratio
* calculated from the parameters. Note that the actual sizes of parameters don't matter, that
* is, calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result.
*
* @param width Relative horizontal size
* @param height Relative vertical size
*/
public void setAspectRatio(int width, int height) {
if (width < 0 || height < 0) {
throw new IllegalArgumentException("Size cannot be negative.");
}
mRatioWidth = width;
mRatioHeight = height;
requestLayout();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (0 == mRatioWidth || 0 == mRatioHeight) {
setMeasuredDimension(width, height);
return;
}
if (width < height * mRatioWidth / mRatioHeight) {
setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
return;
}
setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
}
}
================================================
FILE: Android/AnimatedSkyApp/app/src/main/java/ai/fritz/animatedSky/BaseCameraActivity.java
================================================
package ai.fritz.animatedSky;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.ImageReader.OnImageAvailableListener;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Size;
import android.view.KeyEvent;
import android.view.WindowManager;
import android.widget.Toast;
public abstract class BaseCameraActivity extends AppCompatActivity implements OnImageAvailableListener {
private static final String TAG = BaseCameraActivity.class.getSimpleName();
private static int MAX_WIDTH = 500;
private static final int PERMISSIONS_REQUEST = 1;
private static final String PERMISSION_CAMERA = Manifest.permission.CAMERA;
private static final String PERMISSION_STORAGE = Manifest.permission.WRITE_EXTERNAL_STORAGE;
private boolean useCamera2API;
private boolean debug = false;
private Handler handler;
private HandlerThread handlerThread;
protected String cameraId;
protected int cameraFacingDirection = CameraCharacteristics.LENS_FACING_BACK;
@Override
protected void onCreate(final Bundle savedInstanceState) {
Log.d(TAG, "onCreate " + this);
super.onCreate(null);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_main);
if (hasPermission()) {
setFragment();
} else {
requestPermission();
}
}
@Override
public synchronized void onStart() {
Log.d(TAG, "onStart " + this);
super.onStart();
}
@Override
public synchronized void onResume() {
Log.d(TAG, "onResume " + this);
super.onResume();
handlerThread = new HandlerThread("inference");
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
}
@Override
public synchronized void onPause() {
Log.d(TAG, "onPause " + this);
handlerThread.quitSafely();
try {
handlerThread.join();
handlerThread = null;
handler = null;
} catch (final InterruptedException e) {
Log.e(TAG, "Exception!" + e);
}
super.onPause();
}
@Override
public synchronized void onStop() {
Log.d(TAG, "onStop " + this);
super.onStop();
}
@Override
public synchronized void onDestroy() {
Log.d(TAG, "onDestroy " + this);
super.onDestroy();
}
protected int getCameraFacingDirection() {
return cameraFacingDirection;
}
protected synchronized void runInBackground(final Runnable r) {
if (handler != null) {
handler.post(r);
}
}
@Override
public void onRequestPermissionsResult(
final int requestCode, final String[] permissions, final int[] grantResults) {
switch (requestCode) {
case PERMISSIONS_REQUEST: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
setFragment();
} else {
requestPermission();
}
}
}
}
private boolean hasPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
boolean cameraPermission = checkSelfPermission(PERMISSION_CAMERA) == PackageManager.PERMISSION_GRANTED;
return cameraPermission;
} else {
return true;
}
}
private void requestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (shouldShowRequestPermissionRationale(PERMISSION_CAMERA) || shouldShowRequestPermissionRationale(PERMISSION_STORAGE)) {
Toast.makeText(BaseCameraActivity.this, "Camera AND storage permission are required for this demo", Toast.LENGTH_LONG).show();
}
requestPermissions(new String[]{PERMISSION_CAMERA, PERMISSION_STORAGE}, PERMISSIONS_REQUEST);
}
}
protected void setFragment() {
cameraId = chooseCamera();
final CameraConnectionFragment fragment =
CameraConnectionFragment.newInstance(
new CameraConnectionFragment.ConnectionCallback() {
@Override
public void onPreviewSizeChosen(final Size previewSize, final Size cameraViewSize, final int rotation) {
BaseCameraActivity.this.onPreviewSizeChosen(previewSize, cameraViewSize, rotation);
}
},
this,
getLayoutId(),
getDesiredPreviewFrameSize());
fragment.setCamera(cameraId);
getFragmentManager()
.beginTransaction()
.replace(R.id.camera_container, fragment)
.commit();
}
protected void toggleCameraFacingDirection() {
if (cameraFacingDirection == CameraCharacteristics.LENS_FACING_FRONT) {
cameraFacingDirection = CameraCharacteristics.LENS_FACING_BACK;
} else {
cameraFacingDirection = CameraCharacteristics.LENS_FACING_FRONT;
}
setFragment();
}
private String chooseCamera() {
final CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
for (final String cameraId : manager.getCameraIdList()) {
final CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
// We don't use a front facing camera in this sample.
final Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
if (facing != null && facing != cameraFacingDirection) {
continue;
}
final StreamConfigurationMap map =
characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
continue;
}
// Fallback to camera1 API for internal cameras that don't have full support.
// This should help with legacy situations where using the camera2 API causes
// distorted or otherwise broken previews.
useCamera2API = (facing == CameraCharacteristics.LENS_FACING_EXTERNAL)
|| isHardwareLevelSupported(characteristics,
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
Log.i(TAG, "Camera API lv2?: " + useCamera2API);
return cameraId;
}
} catch (CameraAccessException e) {
Log.e(TAG, "Not allowed to access camera: " + e);
}
return null;
}
// Returns true if the device supports the required hardware level, or better.
private boolean isHardwareLevelSupported(
CameraCharacteristics characteristics, int requiredLevel) {
int deviceLevel = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (deviceLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
return requiredLevel == deviceLevel;
}
// deviceLevel is not LEGACY, can use numerical sort
return requiredLevel <= deviceLevel;
}
public boolean isDebug() {
return debug;
}
public void requestRender() {
final OverlayView overlay = (OverlayView) findViewById(R.id.debug_overlay);
if (overlay != null) {
overlay.postInvalidate();
}
}
public void setCallback(final OverlayView.DrawCallback callback) {
final OverlayView overlay = (OverlayView) findViewById(R.id.debug_overlay);
if (overlay != null) {
overlay.setCallback(callback);
}
}
public void onSetDebug(final boolean debug) {
}
@Override
public boolean onKeyDown(final int keyCode, final KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
debug = !debug;
requestRender();
onSetDebug(debug);
return true;
}
return super.onKeyDown(keyCode, event);
}
protected abstract void onPreviewSizeChosen(final Size previewSize, final Size cameraViewSize, final int rotation);
protected abstract int getLayoutId();
protected Size getDesiredPreviewFrameSize() {
DisplayMetrics metrics = getResources().getDisplayMetrics();
float ratio = (float) metrics.heightPixels / metrics.widthPixels;
return new Size(MAX_WIDTH, (int) ratio * MAX_WIDTH);
}
}
================================================
FILE: Android/AnimatedSkyApp/app/src/main/java/ai/fritz/animatedSky/CameraConnectionFragment.java
================================================
package ai.fritz.animatedSky;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.ImageReader;
import android.media.ImageReader.OnImageAvailableListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.text.TextUtils;
import android.util.Log;
import android.util.Size;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class CameraConnectionFragment extends Fragment {
private static final String TAG = CameraConnectionFragment.class.getSimpleName();
public CameraConnectionFragment() {
}
/**
* The camera preview size will be chosen to be the smallest frame by pixel size capable of
* containing a DESIRED_SIZE x DESIRED_SIZE square.
*/
private static final int MINIMUM_PREVIEW_SIZE = 320;
/**
* Conversion from screen rotation to JPEG orientation.
*/
private static final String FRAGMENT_DIALOG = "dialog";
/**
* {@link android.view.TextureView.SurfaceTextureListener} handles several lifecycle events on a
* {@link TextureView}.
*/
private final TextureView.SurfaceTextureListener surfaceTextureListener =
new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(
final SurfaceTexture texture, final int width, final int height) {
openCamera(width, height);
}
@Override
public void onSurfaceTextureSizeChanged(
final SurfaceTexture texture, final int width, final int height) {
configureTransform(width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(final SurfaceTexture texture) {
return true;
}
@Override
public void onSurfaceTextureUpdated(final SurfaceTexture texture) {
}
};
/**
* Callback for Activities to use to initialize their data once the
* selected preview size is known.
*/
public interface ConnectionCallback {
void onPreviewSizeChosen(Size size, Size cameraViewSize, int cameraRotation);
}
/**
* ID of the current {@link CameraDevice}.
*/
private String cameraId;
/**
* An {@link AutoFitTextureView} for camera preview.
*/
private AutoFitTextureView textureView;
/**
* A {@link CameraCaptureSession } for camera preview.
*/
private CameraCaptureSession captureSession;
/**
* A reference to the opened {@link CameraDevice}.
*/
private CameraDevice cameraDevice;
/**
* The rotation in degrees of the camera sensor from the display.
*/
private Integer sensorOrientation;
/**
* The {@link android.util.Size} of camera preview.
*/
private Size previewSize;
/**
* {@link android.hardware.camera2.CameraDevice.StateCallback}
* is called when {@link CameraDevice} changes its state.
*/
private final CameraDevice.StateCallback stateCallback =
new CameraDevice.StateCallback() {
@Override
public void onOpened(final CameraDevice cd) {
// This method is called when the camera is opened. We start camera preview here.
cameraOpenCloseLock.release();
cameraDevice = cd;
createCameraPreviewSession();
}
@Override
public void onDisconnected(final CameraDevice cd) {
cameraOpenCloseLock.release();
cd.close();
cameraDevice = null;
}
@Override
public void onError(final CameraDevice cd, final int error) {
cameraOpenCloseLock.release();
cd.close();
cameraDevice = null;
final Activity activity = getActivity();
if (null != activity) {
activity.finish();
}
}
};
/**
* An additional thread for running tasks that shouldn't block the UI.
*/
private HandlerThread backgroundThread;
/**
* A {@link Handler} for running tasks in the background.
*/
private Handler backgroundHandler;
/**
* An {@link ImageReader} that handles preview frame capture.
*/
private ImageReader previewReader;
/**
* {@link android.hardware.camera2.CaptureRequest.Builder} for the camera preview
*/
private CaptureRequest.Builder previewRequestBuilder;
/**
* {@link CaptureRequest} generated by {@link #previewRequestBuilder}
*/
private CaptureRequest previewRequest;
/**
* A {@link Semaphore} to prevent the app from exiting before closing the camera.
*/
private final Semaphore cameraOpenCloseLock = new Semaphore(1);
/**
* A {@link OnImageAvailableListener} to receive frames as they are available.
*/
private OnImageAvailableListener imageListener = null;
/**
* The input size in pixels desired by TensorFlow (width and height of a square bitmap).
*/
private Size inputSize = null;
/**
* The layout identifier to inflate for this Fragment.
*/
private int layout = -1;
private ConnectionCallback cameraConnectionCallback = null;
private CameraConnectionFragment(
final ConnectionCallback connectionCallback,
final OnImageAvailableListener imageListener,
final int layout,
final Size inputSize) {
this.cameraConnectionCallback = connectionCallback;
this.imageListener = imageListener;
this.layout = layout;
this.inputSize = inputSize;
}
/**
* Shows a {@link Toast} on the UI thread.
*
* @param text The message to show
*/
private void showToast(final String text) {
final Activity activity = getActivity();
if (activity != null) {
activity.runOnUiThread(
new Runnable() {
@Override
public void run() {
Toast.makeText(activity, text, Toast.LENGTH_SHORT).show();
}
});
}
}
/**
* Given {@code choices} of {@code Size}s supported by a camera, chooses the smallest one whose
* width and height are at least as large as the minimum of both, or an exact match if possible.
*
* @param choices The list of sizes that the camera supports for the intended output class
* @param width The minimum desired width
* @param height The minimum desired height
* @return The optimal {@code Size}, or an arbitrary one if none were big enough
*/
protected static Size chooseOptimalSize(final Size[] choices, final int width, final int height) {
final int minSize = Math.max(Math.min(width, height), MINIMUM_PREVIEW_SIZE);
final Size desiredSize = new Size(width, height);
// Collect the supported resolutions that are at least as big as the preview Surface
boolean exactSizeFound = false;
final List bigEnough = new ArrayList();
final List tooSmall = new ArrayList();
for (final Size option : choices) {
if (option.equals(desiredSize)) {
// Set the size but don't return yet so that remaining sizes will still be logged.
exactSizeFound = true;
}
if (option.getHeight() >= minSize && option.getWidth() >= minSize) {
bigEnough.add(option);
} else {
tooSmall.add(option);
}
}
Log.d(TAG, "Desired size: " + desiredSize + ", min size: " + minSize + "x" + minSize);
Log.d(TAG, "Valid preview sizes: [" + TextUtils.join(", ", bigEnough) + "]");
Log.d(TAG, "Rejected preview sizes: [" + TextUtils.join(", ", tooSmall) + "]");
if (exactSizeFound) {
Log.d(TAG, "Exact size match found.");
return desiredSize;
}
// Pick the smallest of those, assuming we found any
if (bigEnough.size() > 0) {
final Size chosenSize = Collections.min(bigEnough, new CompareSizesByArea());
Log.d(TAG, "Chosen size: " + chosenSize.getWidth() + "x" + chosenSize.getHeight());
return chosenSize;
} else {
Log.e(TAG, "Couldn't find any suitable preview size");
return choices[0];
}
}
public static CameraConnectionFragment newInstance(
final ConnectionCallback callback,
final OnImageAvailableListener imageListener,
final int layout,
final Size inputSize) {
return new CameraConnectionFragment(callback, imageListener, layout, inputSize);
}
@Override
public View onCreateView(
final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
return inflater.inflate(layout, container, false);
}
@Override
public void onViewCreated(final View view, final Bundle savedInstanceState) {
textureView = (AutoFitTextureView) view.findViewById(R.id.texture);
}
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
startBackgroundThread();
// When the screen is turned off and turned back on, the SurfaceTexture is ai.fritz.heartbeat.ui.AutoFitTextureViewalready
// available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open
// a camera and start preview from here (otherwise, we wait until the surface is ready in
// the SurfaceTextureListener).
if (textureView.isAvailable()) {
openCamera(textureView.getWidth(), textureView.getHeight());
} else {
textureView.setSurfaceTextureListener(surfaceTextureListener);
}
}
@Override
public void onPause() {
closeCamera();
stopBackgroundThread();
super.onPause();
}
public void setCamera(String cameraId) {
this.cameraId = cameraId;
}
/**
* Sets up member variables related to camera.
*/
private void setUpCameraOutputs() {
final Activity activity = getActivity();
final CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
try {
final CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
final StreamConfigurationMap map =
characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
// For still image captures, we use the largest available size.
final Size largest =
Collections.max(
Arrays.asList(map.getOutputSizes(ImageFormat.YUV_420_888)),
new CompareSizesByArea());
sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
// Danger, W.R.! Attempting to use too large a preview size could exceed the camera
// bus' bandwidth limitation, resulting in gorgeous previews but the storage of
// garbage capture data.
previewSize =
chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
inputSize.getWidth(),
inputSize.getHeight());
} catch (final CameraAccessException e) {
Log.e(TAG, "Exception!" + e);
} catch (final NullPointerException e) {
// Currently an NPE is thrown when the Camera2API is used but not supported on the
// device this code runs.
// TODO(andrewharp): abstract ErrorDialog/RuntimeException handling out into new method and
// reuse throughout app.
ErrorDialog.newInstance(getString(R.string.camera_error))
.show(getChildFragmentManager(), FRAGMENT_DIALOG);
throw new RuntimeException(getString(R.string.camera_error));
}
Size textureViewSize = new Size(textureView.getWidth(), textureView.getHeight());
cameraConnectionCallback.onPreviewSizeChosen(previewSize, textureViewSize, sensorOrientation);
}
/**
* Opens the camera specified by {@link CameraConnectionFragment#cameraId}.
*/
private void openCamera(final int width, final int height) {
setUpCameraOutputs();
configureTransform(width, height);
final Activity activity = getActivity();
final CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
try {
if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Time out waiting to lock camera opening.");
}
manager.openCamera(cameraId, stateCallback, backgroundHandler);
} catch (final CameraAccessException | SecurityException e) {
Log.e(TAG, "Exception!" + e);
} catch (final InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
}
}
/**
* Closes the current {@link CameraDevice}.
*/
private void closeCamera() {
try {
cameraOpenCloseLock.acquire();
if (null != captureSession) {
captureSession.close();
captureSession = null;
}
if (null != cameraDevice) {
cameraDevice.close();
cameraDevice = null;
}
if (null != previewReader) {
previewReader.close();
previewReader = null;
}
} catch (final InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
} finally {
cameraOpenCloseLock.release();
}
}
/**
* Starts a background thread and its {@link Handler}.
*/
private void startBackgroundThread() {
backgroundThread = new HandlerThread("ImageListener");
backgroundThread.start();
backgroundHandler = new Handler(backgroundThread.getLooper());
}
/**
* Stops the background thread and its {@link Handler}.
*/
private void stopBackgroundThread() {
backgroundThread.quitSafely();
try {
backgroundThread.join();
backgroundThread = null;
backgroundHandler = null;
} catch (final InterruptedException e) {
Log.e(TAG, "Exception!" + e);
}
}
private final CameraCaptureSession.CaptureCallback captureCallback =
new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureProgressed(
final CameraCaptureSession session,
final CaptureRequest request,
final CaptureResult partialResult) {
}
@Override
public void onCaptureCompleted(
final CameraCaptureSession session,
final CaptureRequest request,
final TotalCaptureResult result) {
}
};
/**
* Creates a new {@link CameraCaptureSession} for camera preview.
*/
private void createCameraPreviewSession() {
try {
final SurfaceTexture texture = textureView.getSurfaceTexture();
assert texture != null;
// We configure the size of default buffer to be the size of camera preview we want.
texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
// This is the output Surface we need to start preview.
final Surface surface = new Surface(texture);
// We set up a CaptureRequest.Builder with the output Surface.
previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewRequestBuilder.addTarget(surface);
Log.i(TAG, "Opening camera preview: " + previewSize.getWidth() + "x" + previewSize.getHeight());
// Create the reader for the preview frames.
previewReader =
ImageReader.newInstance(
previewSize.getWidth(), previewSize.getHeight(), ImageFormat.YUV_420_888, 2);
previewReader.setOnImageAvailableListener(imageListener, backgroundHandler);
previewRequestBuilder.addTarget(previewReader.getSurface());
// Here, we create a CameraCaptureSession for camera preview.
cameraDevice.createCaptureSession(
Arrays.asList(surface, previewReader.getSurface()),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(final CameraCaptureSession cameraCaptureSession) {
// The camera is already closed
if (null == cameraDevice) {
return;
}
// When the session is ready, we start displaying the preview.
captureSession = cameraCaptureSession;
try {
// Auto focus should be continuous for camera preview.
previewRequestBuilder.set(
CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// Flash is automatically enabled when necessary.
previewRequestBuilder.set(
CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
// Finally, we start displaying the camera preview.
previewRequest = previewRequestBuilder.build();
captureSession.setRepeatingRequest(
previewRequest, captureCallback, backgroundHandler);
} catch (final CameraAccessException e) {
Log.e(TAG, "Exception!" + e);
}
}
@Override
public void onConfigureFailed(final CameraCaptureSession cameraCaptureSession) {
showToast("Failed");
}
},
null);
} catch (final CameraAccessException e) {
Log.e(TAG, "Exception!" + e);
}
}
/**
* Configures the necessary {@link android.graphics.Matrix} transformation to `mTextureView`.
* This method should be called after the camera preview size is determined in
* setUpCameraOutputs and also the size of `mTextureView` is fixed.
*
* @param viewWidth The width of `mTextureView`
* @param viewHeight The height of `mTextureView`
*/
private void configureTransform(final int viewWidth, final int viewHeight) {
final Activity activity = getActivity();
if (null == textureView || null == previewSize || null == activity) {
return;
}
final int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
final Matrix matrix = new Matrix();
final RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
final RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth());
final float centerX = viewRect.centerX();
final float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
final float scale =
Math.max(
(float) viewHeight / previewSize.getHeight(),
(float) viewWidth / previewSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
} else if (Surface.ROTATION_180 == rotation) {
matrix.postRotate(180, centerX, centerY);
}
textureView.setTransform(matrix);
}
/**
* Compares two {@code Size}s based on their areas.
*/
static class CompareSizesByArea implements Comparator {
@Override
public int compare(final Size lhs, final Size rhs) {
// We cast here to ensure the multiplications won't overflow
return Long.signum(
(long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight());
}
}
/**
* Shows an error message dialog.
*/
public static class ErrorDialog extends DialogFragment {
private static final String ARG_MESSAGE = "message";
public static ErrorDialog newInstance(final String message) {
final ErrorDialog dialog = new ErrorDialog();
final Bundle args = new Bundle();
args.putString(ARG_MESSAGE, message);
dialog.setArguments(args);
return dialog;
}
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
final Activity activity = getActivity();
return new AlertDialog.Builder(activity)
.setMessage(getArguments().getString(ARG_MESSAGE))
.setPositiveButton(
android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialogInterface, final int i) {
activity.finish();
}
})
.create();
}
}
}
================================================
FILE: Android/AnimatedSkyApp/app/src/main/java/ai/fritz/animatedSky/MainActivity.java
================================================
package ai.fritz.animatedSky;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.util.Size;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicBoolean;
import ai.fritz.core.Fritz;
import ai.fritz.vision.FritzVision;
import ai.fritz.vision.FritzVisionImage;
import ai.fritz.vision.FritzVisionModels;
import ai.fritz.vision.FritzVisionOrientation;
import ai.fritz.vision.ImageOrientation;
import ai.fritz.vision.ImageRotation;
import ai.fritz.vision.ModelVariant;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationPredictor;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationPredictorOptions;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationResult;
import ai.fritz.vision.imagesegmentation.MaskClass;
import ai.fritz.vision.imagesegmentation.SegmentationOnDeviceModel;
public class MainActivity extends BaseCameraActivity implements ImageReader.OnImageAvailableListener {
private static final String TAG = MainActivity.class.getSimpleName();
private AtomicBoolean shouldSample = new AtomicBoolean(true);
private FritzVisionSegmentationPredictor predictor;
private ImageOrientation orientation;
private static final int DURATION = 5000;
private ValueAnimator mCurrentAnimator;
private Matrix mMatrix = new Matrix();
private ImageView mImageView;
private float mScaleFactor;
private RectF mDisplayRect = new RectF();
private FritzVisionSegmentationResult segmentResult;
private FritzVisionImage visionImage;
Button snapshotButton;
RelativeLayout previewLayout;
RelativeLayout snapshotLayout;
OverlayView snapshotOverlay;
ProgressBar snapshotProcessingSpinner;
Button closeButton;
FritzVisionSegmentationPredictorOptions options;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fritz.configure(getApplicationContext());
SegmentationOnDeviceModel onDeviceModel = FritzVisionModels.getSkySegmentationOnDeviceModel(ModelVariant.FAST);
options = new FritzVisionSegmentationPredictorOptions();
options.confidenceThreshold = .6f;
predictor = FritzVision.ImageSegmentation.getPredictor(onDeviceModel, options);
}
@Override
protected int getLayoutId() {
return R.layout.sky_fragment;
}
@Override
public void onPreviewSizeChosen(final Size size, final Size cameraSize, final int rotation) {
orientation = FritzVisionOrientation.getImageOrientationFromCamera(this, cameraId);
snapshotButton = findViewById(R.id.take_picture_btn);
previewLayout = findViewById(R.id.preview_frame);
snapshotLayout = findViewById(R.id.snapshot_frame);
snapshotOverlay = findViewById(R.id.snapshot_view);
closeButton = findViewById(R.id.close_btn);
snapshotProcessingSpinner = findViewById(R.id.snapshotProcessingSpinner);
mImageView = findViewById(R.id.backgroundImgView);
snapshotOverlay.setCallback(new OverlayView.DrawCallback() {
@Override
public void drawCallback(final Canvas canvas) {
// If there's no result, just return
if (segmentResult == null) {
return;
}
// Create a bitmap for undetected items. Scale it up for the camera.
Bitmap notSkyMask = segmentResult.buildSingleClassMask(MaskClass.NONE);
Bitmap notSkyBitmap = visionImage.mask(notSkyMask);
// Scale the non-sky bitmap (scale up from preview size (size of the original image)
// to fill the view (cameraSize)).
float scaleWidth = ((float) cameraSize.getWidth()) / notSkyBitmap.getWidth();
float scaleHeight = ((float) cameraSize.getHeight()) / notSkyBitmap.getHeight();
final Matrix matrix = new Matrix();
float scale = Math.min(scaleWidth, scaleHeight);
matrix.postScale(scale, scale);
Bitmap scaledNonSkyBitmap = Bitmap.createBitmap(notSkyBitmap, 0, 0, notSkyBitmap.getWidth(), notSkyBitmap.getHeight(), matrix, false);
// Start the animation
mImageView.post(new Runnable() {
@Override
public void run() {
mScaleFactor = (float) mImageView.getHeight() / (float) mImageView.getDrawable().getIntrinsicHeight();
mMatrix.postScale(mScaleFactor, mScaleFactor);
mImageView.setImageMatrix(mMatrix);
startAnimation();
}
});
// Draw the non-sky bitmap on the bottom center.
canvas.drawBitmap(scaledNonSkyBitmap, (cameraSize.getWidth() - scaledNonSkyBitmap.getWidth()) / 2, cameraSize.getHeight() - scaledNonSkyBitmap.getHeight(), new Paint());
}
});
snapshotButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (!shouldSample.compareAndSet(true, false)) {
return;
}
snapshotOverlay.postInvalidate();
runInBackground(
new Runnable() {
@Override
public void run() {
showSpinner();
segmentResult = predictor.predict(visionImage);
showSnapshotLayout();
hideSpinner();
snapshotOverlay.postInvalidate();
}
});
}
});
closeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
showPreviewLayout();
shouldSample.set(true);
mCurrentAnimator.end();
mMatrix = new Matrix();
mImageView.setImageMatrix(mMatrix);
mDisplayRect = new RectF();
}
});
}
// For more information on how this animation works:
// http://old.flavienlaurent.com/blog/2013/08/05/make-your-background-moving-like-on-play-music-app/
// In short, use displayRect to maintain the real size and position of the bg.
// Animate the background by applying a translation.
private void startAnimation() {
int width = mImageView.getDrawable().getIntrinsicWidth();
int height = mImageView.getDrawable().getIntrinsicHeight();
mDisplayRect.set(0, 0, width, height);
mMatrix.mapRect(mDisplayRect);
animate(mDisplayRect.left, mDisplayRect.left - (mDisplayRect.right - mImageView.getWidth()));
}
private void animate(float from, float to) {
mCurrentAnimator = ValueAnimator.ofFloat(from, to);
mCurrentAnimator.setRepeatCount(ValueAnimator.INFINITE);
mCurrentAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (Float) animation.getAnimatedValue();
mMatrix.reset();
mMatrix.postScale(mScaleFactor, mScaleFactor);
mMatrix.postTranslate(value, 0);
mImageView.setImageMatrix(mMatrix);
}
});
mCurrentAnimator.setDuration(DURATION);
mCurrentAnimator.start();
}
private void showSpinner() {
runOnUiThread(new Runnable() {
@Override
public void run() {
snapshotProcessingSpinner.setVisibility(View.VISIBLE);
}
});
}
private void hideSpinner() {
runOnUiThread(new Runnable() {
@Override
public void run() {
snapshotProcessingSpinner.setVisibility(View.GONE);
}
});
}
private void showSnapshotLayout() {
runOnUiThread(new Runnable() {
@Override
public void run() {
previewLayout.setVisibility(View.GONE);
snapshotLayout.setVisibility(View.VISIBLE);
}
});
}
private void showPreviewLayout() {
previewLayout.setVisibility(View.VISIBLE);
snapshotLayout.setVisibility(View.GONE);
}
@Override
public void onImageAvailable(final ImageReader reader) {
Image image = reader.acquireLatestImage();
if (image == null) {
return;
}
if (!shouldSample.get()) {
image.close();
return;
}
// Feel free to uncomment if you'd like to try it out with a static image
// Bitmap testImage = getBitmapForAsset(this, "climbing.png");
// visionImage = FritzVisionImage.fromBitmap(testImage, ImageRotation.ROTATE_0);
// Using the image from the camera
visionImage = FritzVisionImage.fromMediaImage(image, orientation);
image.close();
}
public static Bitmap getBitmapForAsset(Context context, String path) {
AssetManager assetManager = context.getAssets();
InputStream inputStream;
Bitmap bitmap = null;
try {
inputStream = assetManager.open(path);
bitmap = BitmapFactory.decodeStream(inputStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
return bitmap;
}
}
================================================
FILE: Android/AnimatedSkyApp/app/src/main/java/ai/fritz/animatedSky/OverlayView.java
================================================
package ai.fritz.animatedSky;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
/**
* A simple View providing a render callback to other classes.
*/
public class OverlayView extends View {
private DrawCallback callback;
public OverlayView(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
/**
* Interface defining the callback for client classes.
*/
public interface DrawCallback {
public void drawCallback(final Canvas canvas);
}
public void setCallback(final DrawCallback callback) {
this.callback = callback;
}
@Override
public synchronized void draw(final Canvas canvas) {
super.draw(canvas);
if(callback != null) {
callback.drawCallback(canvas);
}
}
}
================================================
FILE: Android/AnimatedSkyApp/app/src/main/res/drawable/ic_close.xml
================================================
================================================
FILE: Android/AnimatedSkyApp/app/src/main/res/drawable/ic_launcher_background.xml
================================================
================================================
FILE: Android/AnimatedSkyApp/app/src/main/res/drawable/round_button.xml
================================================
================================================
FILE: Android/AnimatedSkyApp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
================================================
FILE: Android/AnimatedSkyApp/app/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: Android/AnimatedSkyApp/app/src/main/res/layout/sky_fragment.xml
================================================
================================================
FILE: Android/AnimatedSkyApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
================================================
FILE: Android/AnimatedSkyApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
================================================
FILE: Android/AnimatedSkyApp/app/src/main/res/values/colors.xml
================================================
#3F51B5#303F9F#FF4081
================================================
FILE: Android/AnimatedSkyApp/app/src/main/res/values/dimen.xml
================================================
4dp8dp16dp32dp64dp@dimen/margin_md@dimen/margin_md
================================================
FILE: Android/AnimatedSkyApp/app/src/main/res/values/fritz.xml
================================================
Your API Key
================================================
FILE: Android/AnimatedSkyApp/app/src/main/res/values/strings.xml
================================================
Sky AnimationThis device doesn\'t support Camera2 API.
================================================
FILE: Android/AnimatedSkyApp/app/src/main/res/values/styles.xml
================================================
================================================
FILE: Android/AnimatedSkyApp/build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
// ADD FOR FRITZ DEPENDENCIES
maven { url "https://fritz.mycloudrepo.io/public/repositories/android" }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: Android/AnimatedSkyApp/gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: Android/AnimatedSkyApp/gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: Android/AnimatedSkyApp/gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: Android/AnimatedSkyApp/settings.gradle
================================================
include ':app'
================================================
FILE: Android/BackgroundReplacementApp/.gitignore
================================================
# Built application files
*.apk
*.ap_
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.idea
# Keystore files
# Uncomment the following line if you do not want to check your keystore files in.
#*.jks
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
# Google Services (e.g. APIs or Firebase)
google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
================================================
FILE: Android/BackgroundReplacementApp/README.md
================================================
# Automatic Background Replacement with People Segmentation
[ ](https://app.codeship.com/projects/297281)
[](http://twitter.com/fritzlabs)
In this app, we create a mask of a person and then replace the background with any picture in the camera gallery.

For the full tutorial, visit [our post on Heartbeat](https://heartbeat.fritz.ai/image-segmentation-for-android-smart-background-replacement-with-fritz-a09d8b0592a4).
This example app uses the on-device People Segmentation API for Android.
- [Overview](https://www.fritz.ai/features/image-segmentation.html)
- [Documentation](https://docs.fritz.ai/develop/vision/image-segmentation/android.html)
## Fritz AI
Fritz AI is the machine learning platform for iOS and Android developers. Teach your mobile apps to see, hear, sense, and think. Start with our ready-to-use feature APIs or connect and deploy your own custom models.
## Requirements
- Android Studio 3.2 or above
- Android device in developer model (USB debugging enabled)
## Getting Started
**Step 1: Choose a Fritz AI Plan**
[Sign up](https://www.fritz.ai/pricing/?utm_source=github&utm_campaign=fritz-examples) for a Fritz AI plan to get started.
Register the Android app in your Fritz account with the package id "ai.fritz.backgroundChanger". During registration, you'll receive an API key for the app. Save this for later. To find it in the webapp, you can go to Project Settings > Apps > Your App > Show API Key.
**Step 2: Clone / Fork the fritz-examples repository and open the BackgroundReplacementApp app in Android Studio**
```
git clone https://github.com/fritzlabs/fritz-examples.git
```
In Android Studio, choose "Open an existing Android Studio project" and select `BackgroundReplacementApp`.
**Step 3: Edit the fritz.xml file with your API Key**
In app/src/main/res/values/fritz.xml, change the fritz_api_key attribute with the one you received in step 1.
**Step 4: Build the Android Studio Project**
Select "Build > Make Project" from the top nav. Download any missing libraries if applicable. This should sync the gradle dependencies so give the build a second to complete.
**Step 5: Install the app onto your device**
With your Android device connected, select `Run > Run App` from the top nav. When running the app for the first time, you'll have to give permissions to access the camera. After the app is installed and running, take a picture of a person and then after that mask is created, select a photo from your camera gallery to replace the background.
## Official Documentation
[SDK Documentation](https://docs.fritz.ai/?utm_source=github&utm_campaign=fritz-examples)
[Android API Docs](https://docs.fritz.ai/android/latest/index.html?utm_source=github&utm_campaign=fritz-examples)
## Join the community
[Heartbeat](https://heartbeat.fritz.ai/?utm_source=github&utm_campaign=fritz-examples) is a community of developers interested in the intersection of mobile and machine learning. [Chat with us in Slack](https://www.fritz.ai/slack) and stay up to date on the latest mobile ML news with our [Newsletter](https://www.fritz.ai/newsletter?utm_source=github&utm_campaign=fritz-examples).
## Help
For any questions or issues, you can:
- Submit an issue on this repo
- Go to [Support](https://support.fritz.ai/?utm_source=github&utm_campaign=fritz-examples)
- Message us directly in [Slack](https://www.fritz.ai/slack)
================================================
FILE: Android/BackgroundReplacementApp/app/.gitignore
================================================
/build
================================================
FILE: Android/BackgroundReplacementApp/app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
// MUST MATCH THE APPLICATION YOU CREATE IN FRITZ
applicationId "ai.fritz.backgroundChanger"
minSdkVersion 24
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
debuggable true
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
aaptOptions {
noCompress "tflite"
}
lintOptions {
abortOnError false
}
}
dependencies {
implementation 'com.android.support:appcompat-v7:28.0.0-rc02'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation "ai.fritz:core:6.0.3"
implementation "ai.fritz:vision:6.0.3"
implementation "ai.fritz:vision-people-segmentation-model-accurate:3.1.0"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
================================================
FILE: Android/BackgroundReplacementApp/app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: Android/BackgroundReplacementApp/app/src/main/AndroidManifest.xml
================================================
================================================
FILE: Android/BackgroundReplacementApp/app/src/main/java/ai/fritz/replaceBackground/AutoFitTextureView.java
================================================
package ai.fritz.replaceBackground;
import android.content.Context;
import android.util.AttributeSet;
import android.view.TextureView;
/**
* A {@link TextureView} that can be adjusted to a specified aspect ratio.
*/
public class AutoFitTextureView extends TextureView {
private int ratioWidth = 0;
private int ratioHeight = 0;
public AutoFitTextureView(final Context context) {
this(context, null);
}
public AutoFitTextureView(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public AutoFitTextureView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
}
/**
* Sets the aspect ratio for this view. The size of the view will be measured based on the ratio
* calculated from the parameters. Note that the actual sizes of parameters don't matter, that
* is, calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result.
*
* @param width Relative horizontal size
* @param height Relative vertical size
*/
public void setAspectRatio(final int width, final int height) {
if (width < 0 || height < 0) {
throw new IllegalArgumentException("Size cannot be negative.");
}
ratioWidth = width;
ratioHeight = height;
requestLayout();
}
}
================================================
FILE: Android/BackgroundReplacementApp/app/src/main/java/ai/fritz/replaceBackground/BaseCameraActivity.java
================================================
package ai.fritz.replaceBackground;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.ImageReader.OnImageAvailableListener;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Size;
import android.view.KeyEvent;
import android.view.WindowManager;
import android.widget.Toast;
public abstract class BaseCameraActivity extends AppCompatActivity implements OnImageAvailableListener {
private static final String TAG = BaseCameraActivity.class.getSimpleName();
private static int MAX_WIDTH = 500;
private static final int PERMISSIONS_REQUEST = 1;
private static final String PERMISSION_CAMERA = Manifest.permission.CAMERA;
private static final String PERMISSION_STORAGE = Manifest.permission.WRITE_EXTERNAL_STORAGE;
private boolean useCamera2API;
private boolean debug = false;
private Handler handler;
private HandlerThread handlerThread;
protected String cameraId;
@Override
protected void onCreate(final Bundle savedInstanceState) {
Log.d(TAG, "onCreate " + this);
super.onCreate(null);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_main);
if (hasPermission()) {
setFragment();
} else {
requestPermission();
}
}
@Override
public synchronized void onStart() {
Log.d(TAG, "onStart " + this);
super.onStart();
}
@Override
public synchronized void onResume() {
Log.d(TAG, "onResume " + this);
super.onResume();
handlerThread = new HandlerThread("inference");
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
}
@Override
public synchronized void onPause() {
Log.d(TAG, "onPause " + this);
handlerThread.quitSafely();
try {
handlerThread.join();
handlerThread = null;
handler = null;
} catch (final InterruptedException e) {
Log.e(TAG, "Exception!" + e);
}
super.onPause();
}
@Override
public synchronized void onStop() {
Log.d(TAG, "onStop " + this);
super.onStop();
}
@Override
public synchronized void onDestroy() {
Log.d(TAG, "onDestroy " + this);
super.onDestroy();
}
protected synchronized void runInBackground(final Runnable r) {
if (handler != null) {
handler.post(r);
}
}
@Override
public void onRequestPermissionsResult(
final int requestCode, final String[] permissions, final int[] grantResults) {
switch (requestCode) {
case PERMISSIONS_REQUEST: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
setFragment();
} else {
requestPermission();
}
}
}
}
private boolean hasPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return checkSelfPermission(PERMISSION_CAMERA) == PackageManager.PERMISSION_GRANTED && checkSelfPermission(PERMISSION_STORAGE) == PackageManager.PERMISSION_GRANTED;
} else {
return true;
}
}
private void requestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (shouldShowRequestPermissionRationale(PERMISSION_CAMERA) || shouldShowRequestPermissionRationale(PERMISSION_STORAGE)) {
Toast.makeText(BaseCameraActivity.this, "Camera AND storage permission are required for this demo", Toast.LENGTH_LONG).show();
}
requestPermissions(new String[]{PERMISSION_CAMERA, PERMISSION_STORAGE}, PERMISSIONS_REQUEST);
}
}
protected void setFragment() {
cameraId = chooseCamera();
final CameraConnectionFragment fragment =
CameraConnectionFragment.newInstance(
new CameraConnectionFragment.ConnectionCallback() {
@Override
public void onPreviewSizeChosen(final Size previewSize, final Size cameraViewSize, final int rotation) {
BaseCameraActivity.this.onPreviewSizeChosen(previewSize, cameraViewSize, rotation);
}
},
this,
getLayoutId(),
getDesiredPreviewFrameSize());
fragment.setCamera(cameraId);
getFragmentManager()
.beginTransaction()
.replace(R.id.camera_container, fragment)
.commit();
}
private String chooseCamera() {
final CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
for (final String cameraId : manager.getCameraIdList()) {
final CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
// We don't use a front facing camera in this sample.
final Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
continue;
}
final StreamConfigurationMap map =
characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
continue;
}
// Fallback to camera1 API for internal cameras that don't have full support.
// This should help with legacy situations where using the camera2 API causes
// distorted or otherwise broken previews.
useCamera2API = (facing == CameraCharacteristics.LENS_FACING_EXTERNAL)
|| isHardwareLevelSupported(characteristics,
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
Log.i(TAG, "Camera API lv2?: " + useCamera2API);
return cameraId;
}
} catch (CameraAccessException e) {
Log.e(TAG, "Not allowed to access camera: " + e);
}
return null;
}
// Returns true if the device supports the required hardware level, or better.
private boolean isHardwareLevelSupported(
CameraCharacteristics characteristics, int requiredLevel) {
int deviceLevel = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (deviceLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
return requiredLevel == deviceLevel;
}
// deviceLevel is not LEGACY, can use numerical sort
return requiredLevel <= deviceLevel;
}
public boolean isDebug() {
return debug;
}
public void requestRender() {
final OverlayView overlay = (OverlayView) findViewById(R.id.debug_overlay);
if (overlay != null) {
overlay.postInvalidate();
}
}
public void setCallback(final OverlayView.DrawCallback callback) {
final OverlayView overlay = (OverlayView) findViewById(R.id.debug_overlay);
if (overlay != null) {
overlay.setCallback(callback);
}
}
public void onSetDebug(final boolean debug) {
}
@Override
public boolean onKeyDown(final int keyCode, final KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
debug = !debug;
requestRender();
onSetDebug(debug);
return true;
}
return super.onKeyDown(keyCode, event);
}
protected abstract void onPreviewSizeChosen(final Size previewSize, final Size cameraViewSize, final int rotation);
protected abstract int getLayoutId();
protected Size getDesiredPreviewFrameSize() {
DisplayMetrics metrics = getResources().getDisplayMetrics();
float ratio = (float) metrics.heightPixels / metrics.widthPixels;
return new Size(MAX_WIDTH, (int) ratio * MAX_WIDTH);
}
}
================================================
FILE: Android/BackgroundReplacementApp/app/src/main/java/ai/fritz/replaceBackground/CameraConnectionFragment.java
================================================
package ai.fritz.replaceBackground;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.ImageReader;
import android.media.ImageReader.OnImageAvailableListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.text.TextUtils;
import android.util.Log;
import android.util.Size;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class CameraConnectionFragment extends Fragment {
private static final String TAG = CameraConnectionFragment.class.getSimpleName();
public CameraConnectionFragment() {
}
/**
* The camera preview size will be chosen to be the smallest frame by pixel size capable of
* containing a DESIRED_SIZE x DESIRED_SIZE square.
*/
private static final int MINIMUM_PREVIEW_SIZE = 320;
/**
* Conversion from screen rotation to JPEG orientation.
*/
private static final String FRAGMENT_DIALOG = "dialog";
/**
* {@link android.view.TextureView.SurfaceTextureListener} handles several lifecycle events on a
* {@link TextureView}.
*/
private final TextureView.SurfaceTextureListener surfaceTextureListener =
new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(
final SurfaceTexture texture, final int width, final int height) {
openCamera(width, height);
}
@Override
public void onSurfaceTextureSizeChanged(
final SurfaceTexture texture, final int width, final int height) {
configureTransform(width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(final SurfaceTexture texture) {
return true;
}
@Override
public void onSurfaceTextureUpdated(final SurfaceTexture texture) {
}
};
/**
* Callback for Activities to use to initialize their data once the
* selected preview size is known.
*/
public interface ConnectionCallback {
void onPreviewSizeChosen(Size size, Size cameraViewSize, int cameraRotation);
}
/**
* ID of the current {@link CameraDevice}.
*/
private String cameraId;
/**
* An {@link AutoFitTextureView} for camera preview.
*/
private AutoFitTextureView textureView;
/**
* A {@link CameraCaptureSession } for camera preview.
*/
private CameraCaptureSession captureSession;
/**
* A reference to the opened {@link CameraDevice}.
*/
private CameraDevice cameraDevice;
/**
* The rotation in degrees of the camera sensor from the display.
*/
private Integer sensorOrientation;
/**
* The {@link android.util.Size} of camera preview.
*/
private Size previewSize;
/**
* {@link android.hardware.camera2.CameraDevice.StateCallback}
* is called when {@link CameraDevice} changes its state.
*/
private final CameraDevice.StateCallback stateCallback =
new CameraDevice.StateCallback() {
@Override
public void onOpened(final CameraDevice cd) {
// This method is called when the camera is opened. We start camera preview here.
cameraOpenCloseLock.release();
cameraDevice = cd;
createCameraPreviewSession();
}
@Override
public void onDisconnected(final CameraDevice cd) {
cameraOpenCloseLock.release();
cd.close();
cameraDevice = null;
}
@Override
public void onError(final CameraDevice cd, final int error) {
cameraOpenCloseLock.release();
cd.close();
cameraDevice = null;
final Activity activity = getActivity();
if (null != activity) {
activity.finish();
}
}
};
/**
* An additional thread for running tasks that shouldn't block the UI.
*/
private HandlerThread backgroundThread;
/**
* A {@link Handler} for running tasks in the background.
*/
private Handler backgroundHandler;
/**
* An {@link ImageReader} that handles preview frame capture.
*/
private ImageReader previewReader;
/**
* {@link android.hardware.camera2.CaptureRequest.Builder} for the camera preview
*/
private CaptureRequest.Builder previewRequestBuilder;
/**
* {@link CaptureRequest} generated by {@link #previewRequestBuilder}
*/
private CaptureRequest previewRequest;
/**
* A {@link Semaphore} to prevent the app from exiting before closing the camera.
*/
private final Semaphore cameraOpenCloseLock = new Semaphore(1);
/**
* A {@link OnImageAvailableListener} to receive frames as they are available.
*/
private OnImageAvailableListener imageListener = null;
/**
* The input size in pixels desired by TensorFlow (width and height of a square bitmap).
*/
private Size inputSize = null;
/**
* The layout identifier to inflate for this Fragment.
*/
private int layout = -1;
private ConnectionCallback cameraConnectionCallback = null;
private CameraConnectionFragment(
final ConnectionCallback connectionCallback,
final OnImageAvailableListener imageListener,
final int layout,
final Size inputSize) {
this.cameraConnectionCallback = connectionCallback;
this.imageListener = imageListener;
this.layout = layout;
this.inputSize = inputSize;
}
/**
* Shows a {@link Toast} on the UI thread.
*
* @param text The message to show
*/
private void showToast(final String text) {
final Activity activity = getActivity();
if (activity != null) {
activity.runOnUiThread(
new Runnable() {
@Override
public void run() {
Toast.makeText(activity, text, Toast.LENGTH_SHORT).show();
}
});
}
}
/**
* Given {@code choices} of {@code Size}s supported by a camera, chooses the smallest one whose
* width and height are at least as large as the minimum of both, or an exact match if possible.
*
* @param choices The list of sizes that the camera supports for the intended output class
* @param width The minimum desired width
* @param height The minimum desired height
* @return The optimal {@code Size}, or an arbitrary one if none were big enough
*/
protected static Size chooseOptimalSize(final Size[] choices, final int width, final int height) {
final int minSize = Math.max(Math.min(width, height), MINIMUM_PREVIEW_SIZE);
final Size desiredSize = new Size(width, height);
// Collect the supported resolutions that are at least as big as the preview Surface
boolean exactSizeFound = false;
final List bigEnough = new ArrayList();
final List tooSmall = new ArrayList();
for (final Size option : choices) {
if (option.equals(desiredSize)) {
// Set the size but don't return yet so that remaining sizes will still be logged.
exactSizeFound = true;
}
if (option.getHeight() >= minSize && option.getWidth() >= minSize) {
bigEnough.add(option);
} else {
tooSmall.add(option);
}
}
Log.d(TAG, "Desired size: " + desiredSize + ", min size: " + minSize + "x" + minSize);
Log.d(TAG, "Valid preview sizes: [" + TextUtils.join(", ", bigEnough) + "]");
Log.d(TAG, "Rejected preview sizes: [" + TextUtils.join(", ", tooSmall) + "]");
if (exactSizeFound) {
Log.d(TAG, "Exact size match found.");
return desiredSize;
}
// Pick the smallest of those, assuming we found any
if (bigEnough.size() > 0) {
final Size chosenSize = Collections.min(bigEnough, new CompareSizesByArea());
Log.d(TAG, "Chosen size: " + chosenSize.getWidth() + "x" + chosenSize.getHeight());
return chosenSize;
} else {
Log.e(TAG, "Couldn't find any suitable preview size");
return choices[0];
}
}
public static CameraConnectionFragment newInstance(
final ConnectionCallback callback,
final OnImageAvailableListener imageListener,
final int layout,
final Size inputSize) {
return new CameraConnectionFragment(callback, imageListener, layout, inputSize);
}
@Override
public View onCreateView(
final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
return inflater.inflate(layout, container, false);
}
@Override
public void onViewCreated(final View view, final Bundle savedInstanceState) {
textureView = (AutoFitTextureView) view.findViewById(R.id.texture);
}
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
startBackgroundThread();
// When the screen is turned off and turned back on, the SurfaceTexture is already
// available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open
// a camera and start preview from here (otherwise, we wait until the surface is ready in
// the SurfaceTextureListener).
if (textureView.isAvailable()) {
openCamera(textureView.getWidth(), textureView.getHeight());
} else {
textureView.setSurfaceTextureListener(surfaceTextureListener);
}
}
@Override
public void onPause() {
closeCamera();
stopBackgroundThread();
super.onPause();
}
public void setCamera(String cameraId) {
this.cameraId = cameraId;
}
/**
* Sets up member variables related to camera.
*/
private void setUpCameraOutputs() {
final Activity activity = getActivity();
final CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
try {
final CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
final StreamConfigurationMap map =
characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
// For still image captures, we use the largest available size.
final Size largest =
Collections.max(
Arrays.asList(map.getOutputSizes(ImageFormat.YUV_420_888)),
new CompareSizesByArea());
sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
// Danger, W.R.! Attempting to use too large a preview size could exceed the camera
// bus' bandwidth limitation, resulting in gorgeous previews but the storage of
// garbage capture data.
previewSize =
chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
inputSize.getWidth(),
inputSize.getHeight());
} catch (final CameraAccessException e) {
Log.e(TAG, "Exception!" + e);
} catch (final NullPointerException e) {
// Currently an NPE is thrown when the Camera2API is used but not supported on the
// device this code runs.
// TODO(andrewharp): abstract ErrorDialog/RuntimeException handling out into new method and
// reuse throughout app.
ErrorDialog.newInstance(getString(R.string.camera_error))
.show(getChildFragmentManager(), FRAGMENT_DIALOG);
throw new RuntimeException(getString(R.string.camera_error));
}
Size textureViewSize = new Size(textureView.getWidth(), textureView.getHeight());
cameraConnectionCallback.onPreviewSizeChosen(previewSize, textureViewSize, sensorOrientation);
}
/**
* Opens the camera specified by {@link CameraConnectionFragment#cameraId}.
*/
private void openCamera(final int width, final int height) {
setUpCameraOutputs();
configureTransform(width, height);
final Activity activity = getActivity();
final CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
try {
if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Time out waiting to lock camera opening.");
}
manager.openCamera(cameraId, stateCallback, backgroundHandler);
} catch (final CameraAccessException | SecurityException e) {
Log.e(TAG, "Exception!" + e);
} catch (final InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
}
}
/**
* Closes the current {@link CameraDevice}.
*/
private void closeCamera() {
try {
cameraOpenCloseLock.acquire();
if (null != captureSession) {
captureSession.close();
captureSession = null;
}
if (null != cameraDevice) {
cameraDevice.close();
cameraDevice = null;
}
if (null != previewReader) {
previewReader.close();
previewReader = null;
}
} catch (final InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
} finally {
cameraOpenCloseLock.release();
}
}
/**
* Starts a background thread and its {@link Handler}.
*/
private void startBackgroundThread() {
backgroundThread = new HandlerThread("ImageListener");
backgroundThread.start();
backgroundHandler = new Handler(backgroundThread.getLooper());
}
/**
* Stops the background thread and its {@link Handler}.
*/
private void stopBackgroundThread() {
backgroundThread.quitSafely();
try {
backgroundThread.join();
backgroundThread = null;
backgroundHandler = null;
} catch (final InterruptedException e) {
Log.e(TAG, "Exception!" + e);
}
}
private final CameraCaptureSession.CaptureCallback captureCallback =
new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureProgressed(
final CameraCaptureSession session,
final CaptureRequest request,
final CaptureResult partialResult) {
}
@Override
public void onCaptureCompleted(
final CameraCaptureSession session,
final CaptureRequest request,
final TotalCaptureResult result) {
}
};
/**
* Creates a new {@link CameraCaptureSession} for camera preview.
*/
private void createCameraPreviewSession() {
try {
final SurfaceTexture texture = textureView.getSurfaceTexture();
assert texture != null;
// We configure the size of default buffer to be the size of camera preview we want.
texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
// This is the output Surface we need to start preview.
final Surface surface = new Surface(texture);
// We set up a CaptureRequest.Builder with the output Surface.
previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewRequestBuilder.addTarget(surface);
Log.i(TAG, "Opening camera preview: " + previewSize.getWidth() + "x" + previewSize.getHeight());
// Create the reader for the preview frames.
previewReader =
ImageReader.newInstance(
previewSize.getWidth(), previewSize.getHeight(), ImageFormat.YUV_420_888, 2);
previewReader.setOnImageAvailableListener(imageListener, backgroundHandler);
previewRequestBuilder.addTarget(previewReader.getSurface());
// Here, we create a CameraCaptureSession for camera preview.
cameraDevice.createCaptureSession(
Arrays.asList(surface, previewReader.getSurface()),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(final CameraCaptureSession cameraCaptureSession) {
// The camera is already closed
if (null == cameraDevice) {
return;
}
// When the session is ready, we start displaying the preview.
captureSession = cameraCaptureSession;
try {
// Auto focus should be continuous for camera preview.
previewRequestBuilder.set(
CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// Flash is automatically enabled when necessary.
previewRequestBuilder.set(
CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
// Finally, we start displaying the camera preview.
previewRequest = previewRequestBuilder.build();
captureSession.setRepeatingRequest(
previewRequest, captureCallback, backgroundHandler);
} catch (final CameraAccessException e) {
Log.e(TAG, "Exception!" + e);
}
}
@Override
public void onConfigureFailed(final CameraCaptureSession cameraCaptureSession) {
showToast("Failed");
}
},
null);
} catch (final CameraAccessException e) {
Log.e(TAG, "Exception!" + e);
}
}
/**
* Configures the necessary {@link android.graphics.Matrix} transformation to `mTextureView`.
* This method should be called after the camera preview size is determined in
* setUpCameraOutputs and also the size of `mTextureView` is fixed.
*
* @param viewWidth The width of `mTextureView`
* @param viewHeight The height of `mTextureView`
*/
private void configureTransform(final int viewWidth, final int viewHeight) {
final Activity activity = getActivity();
if (null == textureView || null == previewSize || null == activity) {
return;
}
final int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
final Matrix matrix = new Matrix();
final RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
final RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth());
final float centerX = viewRect.centerX();
final float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
final float scale =
Math.max(
(float) viewHeight / previewSize.getHeight(),
(float) viewWidth / previewSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
} else if (Surface.ROTATION_180 == rotation) {
matrix.postRotate(180, centerX, centerY);
}
textureView.setTransform(matrix);
}
/**
* Compares two {@code Size}s based on their areas.
*/
static class CompareSizesByArea implements Comparator {
@Override
public int compare(final Size lhs, final Size rhs) {
// We cast here to ensure the multiplications won't overflow
return Long.signum(
(long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight());
}
}
/**
* Shows an error message dialog.
*/
public static class ErrorDialog extends DialogFragment {
private static final String ARG_MESSAGE = "message";
public static ErrorDialog newInstance(final String message) {
final ErrorDialog dialog = new ErrorDialog();
final Bundle args = new Bundle();
args.putString(ARG_MESSAGE, message);
dialog.setArguments(args);
return dialog;
}
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
final Activity activity = getActivity();
return new AlertDialog.Builder(activity)
.setMessage(getArguments().getString(ARG_MESSAGE))
.setPositiveButton(
android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialogInterface, final int i) {
activity.finish();
}
})
.create();
}
}
}
================================================
FILE: Android/BackgroundReplacementApp/app/src/main/java/ai/fritz/replaceBackground/MainActivity.java
================================================
package ai.fritz.replaceBackground;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.media.ExifInterface;
import android.media.Image;
import android.media.ImageReader;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.util.Size;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.Toast;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicBoolean;
import ai.fritz.core.Fritz;
import ai.fritz.core.utils.BitmapUtils;
import ai.fritz.vision.FritzVision;
import ai.fritz.vision.FritzVisionImage;
import ai.fritz.vision.FritzVisionModels;
import ai.fritz.vision.FritzVisionOrientation;
import ai.fritz.vision.ImageOrientation;
import ai.fritz.vision.ImageRotation;
import ai.fritz.vision.ModelVariant;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationPredictor;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationPredictorOptions;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationResult;
import ai.fritz.vision.imagesegmentation.MaskClass;
import ai.fritz.vision.imagesegmentation.SegmentationOnDeviceModel;
public class MainActivity extends BaseCameraActivity implements ImageReader.OnImageAvailableListener {
private static final String TAG = MainActivity.class.getSimpleName();
private static final int SELECT_IMAGE = 1;
private AtomicBoolean shouldSample = new AtomicBoolean(true);
private FritzVisionSegmentationPredictor predictor;
private ImageOrientation orientation;
private FritzVisionSegmentationResult segmentResult;
private FritzVisionImage visionImage;
Button snapshotButton;
Button selectBackgroundBtn;
RelativeLayout previewLayout;
RelativeLayout snapshotLayout;
OverlayView snapshotOverlay;
ProgressBar snapshotProcessingSpinner;
Button closeButton;
private Bitmap backgroundBitmap;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fritz.configure(getApplicationContext(), "bbe75c73f8b24e63bc05bf81ed9d2829");
SegmentationOnDeviceModel onDeviceModel = FritzVisionModels.getPeopleSegmentationOnDeviceModel(ModelVariant.ACCURATE);
FritzVisionSegmentationPredictorOptions options = new FritzVisionSegmentationPredictorOptions();
options.confidenceThreshold = .4f;
predictor = FritzVision.ImageSegmentation.getPredictor(onDeviceModel, options);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode != SELECT_IMAGE) {
return;
}
if (resultCode == Activity.RESULT_CANCELED) {
Toast.makeText(this, "Canceled", Toast.LENGTH_SHORT).show();
return;
}
if (resultCode == Activity.RESULT_OK) {
if (data == null) {
return;
}
try {
Uri selectedPicture = data.getData();
Log.d(TAG, "IMAGE CHOSEN: " + selectedPicture);
InputStream inputStream = getContentResolver().openInputStream(selectedPicture);
ExifInterface exif = new ExifInterface(inputStream);
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
backgroundBitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), selectedPicture);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
backgroundBitmap = BitmapUtils.rotate(backgroundBitmap, 0);
case ExifInterface.ORIENTATION_ROTATE_180:
backgroundBitmap = BitmapUtils.rotate(backgroundBitmap, 270);
case ExifInterface.ORIENTATION_ROTATE_270:
backgroundBitmap = BitmapUtils.rotate(backgroundBitmap, 180);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Override
protected int getLayoutId() {
return R.layout.camera_connection_fragment_background_replace;
}
@Override
public void onPreviewSizeChosen(final Size size, final Size cameraSize, final int rotation) {
orientation = FritzVisionOrientation.getImageOrientationFromCamera(this, cameraId);
snapshotButton = findViewById(R.id.take_picture_btn);
previewLayout = findViewById(R.id.preview_frame);
snapshotLayout = findViewById(R.id.snapshot_frame);
snapshotOverlay = findViewById(R.id.snapshot_view);
closeButton = findViewById(R.id.close_btn);
snapshotProcessingSpinner = findViewById(R.id.snapshotProcessingSpinner);
selectBackgroundBtn = findViewById(R.id.selectBackgroundBtn);
snapshotOverlay.setCallback(new OverlayView.DrawCallback() {
@Override
public void drawCallback(final Canvas canvas) {
// If the prediction has not run
if (segmentResult == null) {
return;
}
// Show the people segmentation result when the background hasn't been chosen.
if (backgroundBitmap == null) {
Bitmap personMask = segmentResult.buildSingleClassMask(MaskClass.PERSON, 180, .5f, .5f);
Bitmap result = visionImage.overlay(personMask);
Bitmap scaledBitmap = BitmapUtils.resize(result, cameraSize.getWidth(), cameraSize.getHeight());
canvas.drawBitmap(scaledBitmap, new Matrix(), new Paint());
return;
}
// Show the background replacement
Bitmap scaledBackgroundBitmap = BitmapUtils.resize(backgroundBitmap, cameraSize.getWidth(), cameraSize.getHeight());
canvas.drawBitmap(scaledBackgroundBitmap, new Matrix(), new Paint());
// Draw the masked bitmap
long startTime = System.currentTimeMillis();
// Use a max alpha of 255 so that there isn't any transparency in the mask.
Bitmap maskedBitmap = segmentResult.buildSingleClassMask(MaskClass.PERSON, 255, .5f, .5f);
Bitmap croppedMask = visionImage.mask(maskedBitmap, true);
Log.d(TAG, "Masked bitmap took " + (System.currentTimeMillis() - startTime) + "ms to create.");
if (croppedMask != null) {
// Scale the result
float scaleWidth = ((float) cameraSize.getWidth()) / croppedMask.getWidth();
float scaleHeight = ((float) cameraSize.getWidth()) / croppedMask.getHeight();
final Matrix matrix = new Matrix();
float scale = Math.min(scaleWidth, scaleHeight);
matrix.postScale(scale, scale);
Bitmap scaledMaskBitmap = Bitmap.createBitmap(croppedMask, 0, 0, croppedMask.getWidth(), croppedMask.getHeight(), matrix, false);
// Print the background bitmap with the masked bitmap
// Center the masked bitmap at the bottom of the image.
canvas.drawBitmap(scaledMaskBitmap, (cameraSize.getWidth() - scaledMaskBitmap.getWidth()) / 2, cameraSize.getHeight() - scaledMaskBitmap.getHeight(), new Paint());
}
}
});
snapshotButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (!shouldSample.compareAndSet(true, false)) {
return;
}
snapshotOverlay.postInvalidate();
runInBackground(
new Runnable() {
@Override
public void run() {
showSpinner();
segmentResult = predictor.predict(visionImage);
showSnapshotLayout();
hideSpinner();
snapshotOverlay.postInvalidate();
}
});
}
});
selectBackgroundBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select Picture"), SELECT_IMAGE);
}
});
closeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
showPreviewLayout();
shouldSample.set(true);
backgroundBitmap = null;
}
});
}
private void showSpinner() {
runOnUiThread(new Runnable() {
@Override
public void run() {
snapshotProcessingSpinner.setVisibility(View.VISIBLE);
}
});
}
private void hideSpinner() {
runOnUiThread(new Runnable() {
@Override
public void run() {
snapshotProcessingSpinner.setVisibility(View.GONE);
}
});
}
private void showSnapshotLayout() {
runOnUiThread(new Runnable() {
@Override
public void run() {
previewLayout.setVisibility(View.GONE);
snapshotLayout.setVisibility(View.VISIBLE);
}
});
}
private void showPreviewLayout() {
previewLayout.setVisibility(View.VISIBLE);
snapshotLayout.setVisibility(View.GONE);
}
@Override
public void onImageAvailable(final ImageReader reader) {
Image image = reader.acquireLatestImage();
if (image == null) {
return;
}
if (!shouldSample.get()) {
image.close();
return;
}
visionImage = FritzVisionImage.fromMediaImage(image, orientation);
image.close();
}
}
================================================
FILE: Android/BackgroundReplacementApp/app/src/main/java/ai/fritz/replaceBackground/OverlayView.java
================================================
package ai.fritz.replaceBackground;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
/**
* A simple View providing a render callback to other classes.
*/
public class OverlayView extends View {
private DrawCallback callback;
public OverlayView(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
/**
* Interface defining the callback for client classes.
*/
public interface DrawCallback {
public void drawCallback(final Canvas canvas);
}
public void setCallback(final DrawCallback callback) {
this.callback = callback;
}
@Override
public synchronized void draw(final Canvas canvas) {
super.draw(canvas);
if(callback != null) {
callback.drawCallback(canvas);
}
}
}
================================================
FILE: Android/BackgroundReplacementApp/app/src/main/res/drawable/ic_close.xml
================================================
================================================
FILE: Android/BackgroundReplacementApp/app/src/main/res/drawable/ic_launcher_background.xml
================================================
================================================
FILE: Android/BackgroundReplacementApp/app/src/main/res/drawable/round_button.xml
================================================
================================================
FILE: Android/BackgroundReplacementApp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
================================================
FILE: Android/BackgroundReplacementApp/app/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: Android/BackgroundReplacementApp/app/src/main/res/layout/camera_connection_fragment_background_replace.xml
================================================
================================================
FILE: Android/BackgroundReplacementApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
================================================
FILE: Android/BackgroundReplacementApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
================================================
FILE: Android/BackgroundReplacementApp/app/src/main/res/values/colors.xml
================================================
#3F51B5#303F9F#FF4081
================================================
FILE: Android/BackgroundReplacementApp/app/src/main/res/values/dimen.xml
================================================
4dp8dp16dp32dp64dp@dimen/margin_md@dimen/margin_md
================================================
FILE: Android/BackgroundReplacementApp/app/src/main/res/values/fritz.xml
================================================
Your API Key
================================================
FILE: Android/BackgroundReplacementApp/app/src/main/res/values/strings.xml
================================================
Background ChangerThis device doesn\'t support Camera2 API.
================================================
FILE: Android/BackgroundReplacementApp/app/src/main/res/values/styles.xml
================================================
================================================
FILE: Android/BackgroundReplacementApp/build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
// ADD FOR FRITZ DEPENDENCIES
maven { url "https://fritz.mycloudrepo.io/public/repositories/android" }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: Android/BackgroundReplacementApp/gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: Android/BackgroundReplacementApp/gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: Android/BackgroundReplacementApp/gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: Android/BackgroundReplacementApp/settings.gradle
================================================
include ':app'
================================================
FILE: Android/CameraBoilerplateApp/.gitignore
================================================
# Built application files
*.apk
*.ap_
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.idea
# Keystore files
# Uncomment the following line if you do not want to check your keystore files in.
#*.jks
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
# Google Services (e.g. APIs or Firebase)
google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
================================================
FILE: Android/CameraBoilerplateApp/README.md
================================================
# Camera Boilerplate
[ ](https://app.codeship.com/projects/297281)
[](http://twitter.com/fritzlabs)
We've created this simple camera app to make it easier for developers to try out ML models using the Camera 2 API.
For a full list of tutorials, please visit our [examples and tutorials page](https://www.fritz.ai/resources/tutorials.html).
## Fritz AI
Fritz AI is the machine learning platform for iOS and Android developers. Teach your mobile apps to see, hear, sense, and think. Start with our ready-to-use feature APIs or connect and deploy your own custom models.
## Requirements
- Android Studio 3.2 or above
- Android device in developer model (USB debugging enabled)
## Getting Started
**Step 1: Choose a Fritz AI Plan**
[Sign up](https://www.fritz.ai/pricing/?utm_source=github&utm_campaign=fritz-examples) for a Fritz AI plan to get started.
Register the Android app in your Fritz account with the package id "ai.fritz.camera". During registration, you'll receive an API key for the app. Save this for later. To find it in the webapp, you can go to Project Settings > Apps > Your App > Show API Key.
**Step 2: Clone / Fork the fritz-examples repository and open the camera app in Android Studio**
```
git clone https://github.com/fritzlabs/fritz-examples.git
```
In Android Studio, choose "Open an existing Android Studio project" and select `skeleton-live-video-app`.
**Step 3: Edit the fritz.xml file with your API Key**
In app/src/main/res/values/fritz.xml, change the fritz_api_key attribute with the one you received in step 1.
**Step 4: Build the Android Studio Project**
Select "Build > Make Project" from the top nav. Download any missing libraries if applicable. This should sync the gradle dependencies so give the build a second to complete.
**Step 5: Install the app onto your device**
With your Android device connected, select `Run > Run App` from the top nav. You should see a simple camera app for you to play around with.
In future tutorials, we'll use this as a starting point in order to run the model on each camera frame and show the result.
## Official Documentation
[SDK Documentation](https://docs.fritz.ai/?utm_source=github&utm_campaign=fritz-examples)
[Android API Docs](https://docs.fritz.ai/android/latest/index.html?utm_source=github&utm_campaign=fritz-examples)
## Join the community
[Heartbeat](https://heartbeat.fritz.ai/?utm_source=github&utm_campaign=fritz-examples) is a community of developers interested in the intersection of mobile and machine learning. [Chat with us in Slack](https://www.fritz.ai/slack?utm_source=github&utm_campaign=fritz-examples) and stay up to date on the latest mobile ML news with our [Newsletter](https://www.fritz.ai/newsletter?utm_source=github&utm_campaign=fritz-examples).
## Help
For any questions or issues, you can:
- Submit an issue on this repo
- Go to [Support](https://support.fritz.ai/?utm_source=github&utm_campaign=fritz-examples)
- Message us directly in [Slack](https://www.fritz.ai/slack?utm_source=github&utm_campaign=fritz-examples)
================================================
FILE: Android/CameraBoilerplateApp/app/.gitignore
================================================
/build
================================================
FILE: Android/CameraBoilerplateApp/app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
// MUST MATCH THE APPLICATION YOU CREATE IN FRITZ
applicationId "ai.fritz.camera"
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
debuggable true
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
aaptOptions {
noCompress "tflite"
}
lintOptions {
abortOnError false
}
}
dependencies {
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation "ai.fritz:core:6.0.3"
implementation "ai.fritz:vision:6.0.3"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
================================================
FILE: Android/CameraBoilerplateApp/app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: Android/CameraBoilerplateApp/app/src/androidTest/java/ai/fritz/camera/ExampleInstrumentedTest.java
================================================
package ai.fritz.camera;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see Testing documentation
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("ai.fritz.camera", appContext.getPackageName());
}
}
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/AndroidManifest.xml
================================================
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/java/ai/fritz/camera/AutoFitTextureView.java
================================================
package ai.fritz.camera;
import android.content.Context;
import android.util.AttributeSet;
import android.view.TextureView;
/**
* A {@link TextureView} that can be adjusted to a specified aspect ratio.
*/
public class AutoFitTextureView extends TextureView {
private int ratioWidth = 0;
private int ratioHeight = 0;
public AutoFitTextureView(final Context context) {
this(context, null);
}
public AutoFitTextureView(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public AutoFitTextureView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
}
/**
* Sets the aspect ratio for this view. The size of the view will be measured based on the ratio
* calculated from the parameters. Note that the actual sizes of parameters don't matter, that
* is, calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result.
*
* @param width Relative horizontal size
* @param height Relative vertical size
*/
public void setAspectRatio(final int width, final int height) {
if (width < 0 || height < 0) {
throw new IllegalArgumentException("Size cannot be negative.");
}
ratioWidth = width;
ratioHeight = height;
requestLayout();
}
}
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/java/ai/fritz/camera/BaseCameraActivity.java
================================================
package ai.fritz.camera;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.ImageReader.OnImageAvailableListener;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Size;
import android.view.KeyEvent;
import android.view.WindowManager;
import android.widget.Toast;
public abstract class BaseCameraActivity extends AppCompatActivity implements OnImageAvailableListener {
private static final String TAG = BaseCameraActivity.class.getSimpleName();
private static int MAX_WIDTH = 500;
private static final int PERMISSIONS_REQUEST = 1;
private static final String PERMISSION_CAMERA = Manifest.permission.CAMERA;
private boolean useCamera2API;
private boolean debug = false;
private Handler handler;
private HandlerThread handlerThread;
protected String cameraId;
@Override
protected void onCreate(final Bundle savedInstanceState) {
Log.d(TAG, "onCreate " + this);
super.onCreate(null);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_main);
if (hasPermission()) {
setFragment();
} else {
requestPermission();
}
}
@Override
public synchronized void onStart() {
Log.d(TAG, "onStart " + this);
super.onStart();
}
@Override
public synchronized void onResume() {
Log.d(TAG, "onResume " + this);
super.onResume();
handlerThread = new HandlerThread("inference");
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
}
@Override
public synchronized void onPause() {
Log.d(TAG, "onPause " + this);
if (!isFinishing()) {
Log.d(TAG, "Requesting finish");
finish();
}
handlerThread.quitSafely();
try {
handlerThread.join();
handlerThread = null;
handler = null;
} catch (final InterruptedException e) {
Log.e(TAG, "Exception!" + e);
}
super.onPause();
}
@Override
public synchronized void onStop() {
Log.d(TAG, "onStop " + this);
super.onStop();
}
@Override
public synchronized void onDestroy() {
Log.d(TAG, "onDestroy " + this);
super.onDestroy();
}
protected synchronized void runInBackground(final Runnable r) {
if (handler != null) {
handler.post(r);
}
}
@Override
public void onRequestPermissionsResult(
final int requestCode, final String[] permissions, final int[] grantResults) {
switch (requestCode) {
case PERMISSIONS_REQUEST: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
setFragment();
} else {
requestPermission();
}
}
}
}
private boolean hasPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int permission = checkSelfPermission(PERMISSION_CAMERA);
return permission == PackageManager.PERMISSION_GRANTED;
} else {
return true;
}
}
private void requestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (shouldShowRequestPermissionRationale(PERMISSION_CAMERA)) {
Toast.makeText(BaseCameraActivity.this, "Camera permission are required for this demo", Toast.LENGTH_LONG).show();
}
requestPermissions(new String[]{PERMISSION_CAMERA}, PERMISSIONS_REQUEST);
}
}
protected void setFragment() {
cameraId = chooseCamera();
final CameraConnectionFragment fragment =
CameraConnectionFragment.newInstance(
new CameraConnectionFragment.ConnectionCallback() {
@Override
public void onPreviewSizeChosen(final Size previewSize, final Size cameraViewSize, final int rotation) {
BaseCameraActivity.this.onPreviewSizeChosen(previewSize, cameraViewSize, rotation);
}
},
this,
getLayoutId(),
getDesiredPreviewFrameSize());
fragment.setCamera(cameraId);
getFragmentManager()
.beginTransaction()
.replace(R.id.camera_container, fragment)
.commit();
}
private String chooseCamera() {
final CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
for (final String cameraId : manager.getCameraIdList()) {
final CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
// We don't use a front facing camera in this sample.
final Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
continue;
}
final StreamConfigurationMap map =
characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
continue;
}
// Fallback to camera1 API for internal cameras that don't have full support.
// This should help with legacy situations where using the camera2 API causes
// distorted or otherwise broken previews.
useCamera2API = (facing == CameraCharacteristics.LENS_FACING_EXTERNAL)
|| isHardwareLevelSupported(characteristics,
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
Log.i(TAG, "Camera API lv2?: " + useCamera2API);
return cameraId;
}
} catch (CameraAccessException e) {
Log.e(TAG, "Not allowed to access camera: " + e);
}
return null;
}
// Returns true if the device supports the required hardware level, or better.
private boolean isHardwareLevelSupported(
CameraCharacteristics characteristics, int requiredLevel) {
int deviceLevel = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (deviceLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
return requiredLevel == deviceLevel;
}
// deviceLevel is not LEGACY, can use numerical sort
return requiredLevel <= deviceLevel;
}
public boolean isDebug() {
return debug;
}
public void requestRender() {
final OverlayView overlay = (OverlayView) findViewById(R.id.debug_overlay);
if (overlay != null) {
overlay.postInvalidate();
}
}
public void setCallback(final OverlayView.DrawCallback callback) {
final OverlayView overlay = (OverlayView) findViewById(R.id.debug_overlay);
if (overlay != null) {
overlay.setCallback(callback);
}
}
public void onSetDebug(final boolean debug) {
}
@Override
public boolean onKeyDown(final int keyCode, final KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
debug = !debug;
requestRender();
onSetDebug(debug);
return true;
}
return super.onKeyDown(keyCode, event);
}
protected abstract void onPreviewSizeChosen(final Size previewSize, final Size cameraViewSize, final int rotation);
protected abstract int getLayoutId();
protected Size getDesiredPreviewFrameSize() {
DisplayMetrics metrics = getResources().getDisplayMetrics();
float ratio = (float) metrics.heightPixels / metrics.widthPixels;
return new Size(MAX_WIDTH, (int) ratio * MAX_WIDTH);
}
}
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/java/ai/fritz/camera/CameraConnectionFragment.java
================================================
package ai.fritz.camera;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.ImageReader;
import android.media.ImageReader.OnImageAvailableListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.text.TextUtils;
import android.util.Log;
import android.util.Size;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class CameraConnectionFragment extends Fragment {
private static final String TAG = CameraConnectionFragment.class.getSimpleName();
public CameraConnectionFragment() {
}
/**
* The camera preview size will be chosen to be the smallest frame by pixel size capable of
* containing a DESIRED_SIZE x DESIRED_SIZE square.
*/
private static final int MINIMUM_PREVIEW_SIZE = 320;
/**
* Conversion from screen rotation to JPEG orientation.
*/
private static final String FRAGMENT_DIALOG = "dialog";
/**
* {@link android.view.TextureView.SurfaceTextureListener} handles several lifecycle events on a
* {@link TextureView}.
*/
private final TextureView.SurfaceTextureListener surfaceTextureListener =
new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(
final SurfaceTexture texture, final int width, final int height) {
openCamera(width, height);
}
@Override
public void onSurfaceTextureSizeChanged(
final SurfaceTexture texture, final int width, final int height) {
configureTransform(width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(final SurfaceTexture texture) {
return true;
}
@Override
public void onSurfaceTextureUpdated(final SurfaceTexture texture) {
}
};
/**
* Callback for Activities to use to initialize their data once the
* selected preview size is known.
*/
public interface ConnectionCallback {
void onPreviewSizeChosen(Size size, Size cameraViewSize, int cameraRotation);
}
/**
* ID of the current {@link CameraDevice}.
*/
private String cameraId;
/**
* An {@link AutoFitTextureView} for camera preview.
*/
private AutoFitTextureView textureView;
/**
* A {@link CameraCaptureSession } for camera preview.
*/
private CameraCaptureSession captureSession;
/**
* A reference to the opened {@link CameraDevice}.
*/
private CameraDevice cameraDevice;
/**
* The rotation in degrees of the camera sensor from the display.
*/
private Integer sensorOrientation;
/**
* The {@link android.util.Size} of camera preview.
*/
private Size previewSize;
/**
* {@link android.hardware.camera2.CameraDevice.StateCallback}
* is called when {@link CameraDevice} changes its state.
*/
private final CameraDevice.StateCallback stateCallback =
new CameraDevice.StateCallback() {
@Override
public void onOpened(final CameraDevice cd) {
// This method is called when the camera is opened. We start camera preview here.
cameraOpenCloseLock.release();
cameraDevice = cd;
createCameraPreviewSession();
}
@Override
public void onDisconnected(final CameraDevice cd) {
cameraOpenCloseLock.release();
cd.close();
cameraDevice = null;
}
@Override
public void onError(final CameraDevice cd, final int error) {
cameraOpenCloseLock.release();
cd.close();
cameraDevice = null;
final Activity activity = getActivity();
if (null != activity) {
activity.finish();
}
}
};
/**
* An additional thread for running tasks that shouldn't block the UI.
*/
private HandlerThread backgroundThread;
/**
* A {@link Handler} for running tasks in the background.
*/
private Handler backgroundHandler;
/**
* An {@link ImageReader} that handles preview frame capture.
*/
private ImageReader previewReader;
/**
* {@link android.hardware.camera2.CaptureRequest.Builder} for the camera preview
*/
private CaptureRequest.Builder previewRequestBuilder;
/**
* {@link CaptureRequest} generated by {@link #previewRequestBuilder}
*/
private CaptureRequest previewRequest;
/**
* A {@link Semaphore} to prevent the app from exiting before closing the camera.
*/
private final Semaphore cameraOpenCloseLock = new Semaphore(1);
/**
* A {@link OnImageAvailableListener} to receive frames as they are available.
*/
private OnImageAvailableListener imageListener = null;
/**
* The input size in pixels desired by TensorFlow (width and height of a square bitmap).
*/
private Size inputSize = null;
/**
* The layout identifier to inflate for this Fragment.
*/
private int layout = -1;
private ConnectionCallback cameraConnectionCallback = null;
private CameraConnectionFragment(
final ConnectionCallback connectionCallback,
final OnImageAvailableListener imageListener,
final int layout,
final Size inputSize) {
this.cameraConnectionCallback = connectionCallback;
this.imageListener = imageListener;
this.layout = layout;
this.inputSize = inputSize;
}
/**
* Shows a {@link Toast} on the UI thread.
*
* @param text The message to show
*/
private void showToast(final String text) {
final Activity activity = getActivity();
if (activity != null) {
activity.runOnUiThread(
new Runnable() {
@Override
public void run() {
Toast.makeText(activity, text, Toast.LENGTH_SHORT).show();
}
});
}
}
/**
* Given {@code choices} of {@code Size}s supported by a camera, chooses the smallest one whose
* width and height are at least as large as the minimum of both, or an exact match if possible.
*
* @param choices The list of sizes that the camera supports for the intended output class
* @param width The minimum desired width
* @param height The minimum desired height
* @return The optimal {@code Size}, or an arbitrary one if none were big enough
*/
protected static Size chooseOptimalSize(final Size[] choices, final int width, final int height) {
final int minSize = Math.max(Math.min(width, height), MINIMUM_PREVIEW_SIZE);
final Size desiredSize = new Size(width, height);
// Collect the supported resolutions that are at least as big as the preview Surface
boolean exactSizeFound = false;
final List bigEnough = new ArrayList();
final List tooSmall = new ArrayList();
for (final Size option : choices) {
if (option.equals(desiredSize)) {
// Set the size but don't return yet so that remaining sizes will still be logged.
exactSizeFound = true;
}
if (option.getHeight() >= minSize && option.getWidth() >= minSize) {
bigEnough.add(option);
} else {
tooSmall.add(option);
}
}
Log.d(TAG, "Desired size: " + desiredSize + ", min size: " + minSize + "x" + minSize);
Log.d(TAG, "Valid preview sizes: [" + TextUtils.join(", ", bigEnough) + "]");
Log.d(TAG, "Rejected preview sizes: [" + TextUtils.join(", ", tooSmall) + "]");
if (exactSizeFound) {
Log.d(TAG, "Exact size match found.");
return desiredSize;
}
// Pick the smallest of those, assuming we found any
if (bigEnough.size() > 0) {
final Size chosenSize = Collections.min(bigEnough, new CompareSizesByArea());
Log.d(TAG, "Chosen size: " + chosenSize.getWidth() + "x" + chosenSize.getHeight());
return chosenSize;
} else {
Log.e(TAG, "Couldn't find any suitable preview size");
return choices[0];
}
}
public static CameraConnectionFragment newInstance(
final ConnectionCallback callback,
final OnImageAvailableListener imageListener,
final int layout,
final Size inputSize) {
return new CameraConnectionFragment(callback, imageListener, layout, inputSize);
}
@Override
public View onCreateView(
final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
return inflater.inflate(layout, container, false);
}
@Override
public void onViewCreated(final View view, final Bundle savedInstanceState) {
textureView = (AutoFitTextureView) view.findViewById(R.id.texture);
}
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
startBackgroundThread();
// When the screen is turned off and turned back on, the SurfaceTexture is already
// available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open
// a camera and start preview from here (otherwise, we wait until the surface is ready in
// the SurfaceTextureListener).
if (textureView.isAvailable()) {
openCamera(textureView.getWidth(), textureView.getHeight());
} else {
textureView.setSurfaceTextureListener(surfaceTextureListener);
}
}
@Override
public void onPause() {
closeCamera();
stopBackgroundThread();
super.onPause();
}
public void setCamera(String cameraId) {
this.cameraId = cameraId;
}
/**
* Sets up member variables related to camera.
*/
private void setUpCameraOutputs() {
final Activity activity = getActivity();
final CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
try {
final CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
final StreamConfigurationMap map =
characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
// For still image captures, we use the largest available size.
final Size largest =
Collections.max(
Arrays.asList(map.getOutputSizes(ImageFormat.YUV_420_888)),
new CompareSizesByArea());
sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
// Danger, W.R.! Attempting to use too large a preview size could exceed the camera
// bus' bandwidth limitation, resulting in gorgeous previews but the storage of
// garbage capture data.
previewSize =
chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
inputSize.getWidth(),
inputSize.getHeight());
} catch (final CameraAccessException e) {
Log.e(TAG, "Exception!" + e);
} catch (final NullPointerException e) {
// Currently an NPE is thrown when the Camera2API is used but not supported on the
// device this code runs.
// TODO(andrewharp): abstract ErrorDialog/RuntimeException handling out into new method and
// reuse throughout app.
ErrorDialog.newInstance(getString(R.string.camera_error))
.show(getChildFragmentManager(), FRAGMENT_DIALOG);
throw new RuntimeException(getString(R.string.camera_error));
}
Size textureViewSize = new Size(textureView.getWidth(), textureView.getHeight());
cameraConnectionCallback.onPreviewSizeChosen(previewSize, textureViewSize, sensorOrientation);
}
/**
* Opens the camera specified by {@link CameraConnectionFragment#cameraId}.
*/
private void openCamera(final int width, final int height) {
setUpCameraOutputs();
configureTransform(width, height);
final Activity activity = getActivity();
final CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
try {
if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Time out waiting to lock camera opening.");
}
manager.openCamera(cameraId, stateCallback, backgroundHandler);
} catch (final CameraAccessException | SecurityException e) {
Log.e(TAG, "Exception!" + e);
} catch (final InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
}
}
/**
* Closes the current {@link CameraDevice}.
*/
private void closeCamera() {
try {
cameraOpenCloseLock.acquire();
if (null != captureSession) {
captureSession.close();
captureSession = null;
}
if (null != cameraDevice) {
cameraDevice.close();
cameraDevice = null;
}
if (null != previewReader) {
previewReader.close();
previewReader = null;
}
} catch (final InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
} finally {
cameraOpenCloseLock.release();
}
}
/**
* Starts a background thread and its {@link Handler}.
*/
private void startBackgroundThread() {
backgroundThread = new HandlerThread("ImageListener");
backgroundThread.start();
backgroundHandler = new Handler(backgroundThread.getLooper());
}
/**
* Stops the background thread and its {@link Handler}.
*/
private void stopBackgroundThread() {
backgroundThread.quitSafely();
try {
backgroundThread.join();
backgroundThread = null;
backgroundHandler = null;
} catch (final InterruptedException e) {
Log.e(TAG, "Exception!" + e);
}
}
private final CameraCaptureSession.CaptureCallback captureCallback =
new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureProgressed(
final CameraCaptureSession session,
final CaptureRequest request,
final CaptureResult partialResult) {
}
@Override
public void onCaptureCompleted(
final CameraCaptureSession session,
final CaptureRequest request,
final TotalCaptureResult result) {
}
};
/**
* Creates a new {@link CameraCaptureSession} for camera preview.
*/
private void createCameraPreviewSession() {
try {
final SurfaceTexture texture = textureView.getSurfaceTexture();
assert texture != null;
// We configure the size of default buffer to be the size of camera preview we want.
texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
// This is the output Surface we need to start preview.
final Surface surface = new Surface(texture);
// We set up a CaptureRequest.Builder with the output Surface.
previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewRequestBuilder.addTarget(surface);
Log.i(TAG, "Opening camera preview: " + previewSize.getWidth() + "x" + previewSize.getHeight());
// Create the reader for the preview frames.
previewReader =
ImageReader.newInstance(
previewSize.getWidth(), previewSize.getHeight(), ImageFormat.YUV_420_888, 2);
previewReader.setOnImageAvailableListener(imageListener, backgroundHandler);
previewRequestBuilder.addTarget(previewReader.getSurface());
// Here, we create a CameraCaptureSession for camera preview.
cameraDevice.createCaptureSession(
Arrays.asList(surface, previewReader.getSurface()),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(final CameraCaptureSession cameraCaptureSession) {
// The camera is already closed
if (null == cameraDevice) {
return;
}
// When the session is ready, we start displaying the preview.
captureSession = cameraCaptureSession;
try {
// Auto focus should be continuous for camera preview.
previewRequestBuilder.set(
CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// Flash is automatically enabled when necessary.
previewRequestBuilder.set(
CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
// Finally, we start displaying the camera preview.
previewRequest = previewRequestBuilder.build();
captureSession.setRepeatingRequest(
previewRequest, captureCallback, backgroundHandler);
} catch (final CameraAccessException e) {
Log.e(TAG, "Exception!" + e);
}
}
@Override
public void onConfigureFailed(final CameraCaptureSession cameraCaptureSession) {
showToast("Failed");
}
},
null);
} catch (final CameraAccessException e) {
Log.e(TAG, "Exception!" + e);
}
}
/**
* Configures the necessary {@link android.graphics.Matrix} transformation to `mTextureView`.
* This method should be called after the camera preview size is determined in
* setUpCameraOutputs and also the size of `mTextureView` is fixed.
*
* @param viewWidth The width of `mTextureView`
* @param viewHeight The height of `mTextureView`
*/
private void configureTransform(final int viewWidth, final int viewHeight) {
final Activity activity = getActivity();
if (null == textureView || null == previewSize || null == activity) {
return;
}
final int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
final Matrix matrix = new Matrix();
final RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
final RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth());
final float centerX = viewRect.centerX();
final float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
final float scale =
Math.max(
(float) viewHeight / previewSize.getHeight(),
(float) viewWidth / previewSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
} else if (Surface.ROTATION_180 == rotation) {
matrix.postRotate(180, centerX, centerY);
}
textureView.setTransform(matrix);
}
/**
* Compares two {@code Size}s based on their areas.
*/
static class CompareSizesByArea implements Comparator {
@Override
public int compare(final Size lhs, final Size rhs) {
// We cast here to ensure the multiplications won't overflow
return Long.signum(
(long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight());
}
}
/**
* Shows an error message dialog.
*/
public static class ErrorDialog extends DialogFragment {
private static final String ARG_MESSAGE = "message";
public static ErrorDialog newInstance(final String message) {
final ErrorDialog dialog = new ErrorDialog();
final Bundle args = new Bundle();
args.putString(ARG_MESSAGE, message);
dialog.setArguments(args);
return dialog;
}
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
final Activity activity = getActivity();
return new AlertDialog.Builder(activity)
.setMessage(getArguments().getString(ARG_MESSAGE))
.setPositiveButton(
android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialogInterface, final int i) {
activity.finish();
}
})
.create();
}
}
}
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/java/ai/fritz/camera/LiveCameraActivity.java
================================================
package ai.fritz.camera;
import android.graphics.Canvas;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.util.Size;
import java.util.concurrent.atomic.AtomicBoolean;
public abstract class LiveCameraActivity extends BaseCameraActivity implements ImageReader.OnImageAvailableListener {
private static final String TAG = MainActivity.class.getSimpleName();
private static final Size DESIRED_PREVIEW_SIZE = new Size(1280, 960);
private AtomicBoolean computing = new AtomicBoolean(false);
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initializeFritz();
setupPredictor();
}
protected abstract void initializeFritz();
protected abstract void setupPredictor();
protected abstract void setupImageForPrediction(Image image);
protected abstract void runInference();
protected abstract void showResult(Canvas canvas, Size cameraViewSize);
@Override
protected int getLayoutId() {
return R.layout.camera_connection_fragment;
}
@Override
protected Size getDesiredPreviewFrameSize() {
return DESIRED_PREVIEW_SIZE;
}
@Override
public void onPreviewSizeChosen(final Size previewSize, final Size cameraViewSize, final int rotation) {
// Callback draws a canvas on the OverlayView
setCallback(
new OverlayView.DrawCallback() {
@Override
public void drawCallback(final Canvas canvas) {
showResult(canvas, cameraViewSize);
}
});
}
@Override
public void onImageAvailable(final ImageReader reader) {
Image image = reader.acquireLatestImage();
if (image == null) {
return;
}
if (!computing.compareAndSet(false, true)) {
image.close();
return;
}
setupImageForPrediction(image);
image.close();
runInBackground(
new Runnable() {
@Override
public void run() {
runInference();
// Fire callback to change the OverlayView
requestRender();
computing.set(false);
}
});
}
}
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/java/ai/fritz/camera/MainActivity.java
================================================
package ai.fritz.camera;
import android.graphics.Canvas;
import android.media.Image;
import android.util.Size;
public class MainActivity extends LiveCameraActivity {
@Override
protected void initializeFritz() {
// TODO: Uncomment this and modify your api key in fritz.xml.
// Fritz.configure(this);
}
@Override
protected void setupPredictor() {
// STEP 1: Get the predictor and set the options.
// ----------------------------------------------
// A FritzOnDeviceModel object is available when a model has been
// successfully downloaded and included with the app.
// TODO: Create a predictor
// ----------------------------------------------
// END STEP 1
}
@Override
protected void setupImageForPrediction(Image image) {
// STEP 2: Create the FritzVisionImage object from media.Image
// ------------------------------------------------------------------------
// TODO: Add code for creating FritzVisionImage from a media.Image object
// ------------------------------------------------------------------------
// END STEP 2
}
@Override
protected void runInference() {
// STEP 3: Run predict on the image
// ---------------------------------------------------
// TODO: Add code for running prediction on the image
// ----------------------------------------------------
// END STEP 3
}
@Override
protected void showResult(Canvas canvas, Size cameraSize) {
// STEP 4: Draw the prediction result
// ----------------------------------
// TODO: Draw the result.
// ----------------------------------
// END STEP 4
}
}
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/java/ai/fritz/camera/OverlayView.java
================================================
package ai.fritz.camera;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
/**
* A simple View providing a render callback to other classes.
*/
public class OverlayView extends View {
private DrawCallback callback;
public OverlayView(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
/**
* Interface defining the callback for client classes.
*/
public interface DrawCallback {
void drawCallback(final Canvas canvas);
}
public void setCallback(final DrawCallback callback) {
this.callback = callback;
}
@Override
public synchronized void draw(final Canvas canvas) {
super.draw(canvas);
if(callback != null) {
callback.drawCallback(canvas);
}
}
}
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/res/drawable/ic_close.xml
================================================
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/res/drawable/ic_launcher_background.xml
================================================
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/res/drawable/round_button.xml
================================================
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/res/layout/camera_connection_fragment.xml
================================================
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/res/layout/camera_connection_fragment_stylize.xml
================================================
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/res/values/colors.xml
================================================
#3F51B5#303F9F#FF4081
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/res/values/dimens.xml
================================================
4dp8dp16dp32dp64dp@dimen/margin_md@dimen/margin_md
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/res/values/fritz.xml
================================================
Your API Key
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/res/values/strings.xml
================================================
Fritz Camera TemplateThis device doesn\'t support Camera2 API.
================================================
FILE: Android/CameraBoilerplateApp/app/src/main/res/values/styles.xml
================================================
================================================
FILE: Android/CameraBoilerplateApp/app/src/test/java/ai/fritz/camera/ExampleUnitTest.java
================================================
package ai.fritz.camera;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see Testing documentation
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: Android/CameraBoilerplateApp/build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
// ADD FOR FRITZ DEPENDENCIES
maven { url "https://fritz.mycloudrepo.io/public/repositories/android" }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: Android/CameraBoilerplateApp/gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: Android/CameraBoilerplateApp/gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: Android/CameraBoilerplateApp/gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: Android/CameraBoilerplateApp/settings.gradle
================================================
include ':app'
================================================
FILE: Android/FritzAIStudio/LICENSE.md
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: Android/FritzAIStudio/README.md
================================================
# Fritz AI Studio App
[ ](https://app.codeship.com/projects/297281)
[](http://twitter.com/fritzlabs)
Fritz AI is the machine learning platform for iOS and Android developers. Teach your mobile apps to see, hear, sense, and think. Start with our ready-to-use feature APIs or connect and deploy your own custom models.
**Vision API: Prebuilt models that you can simply drop into your apps:**
- [Image Segmentation](https://www.fritz.ai/features/image-segmentation.html): Create pixel level masks of different objects in a scene ([code](app/src/main/java/ai/fritz/aistudio/activities/vision/ImageSegmentationActivity.java))
- [Image Labeling](https://www.fritz.ai/features/image-labeling.html): Classify different objects in an video or image ([code](app/src/main/java/ai/fritz/aistudio/activities/vision/ImageLabelingActivity.java))
- [Pose Estimation](https://www.fritz.ai/features/pose-estimation.html): Identify and track a person's body position ([code](app/src/main/java/ai/fritz/aistudio/activities/vision/PoseEstimationActivity.java))
- [Object Detection](https://www.fritz.ai/features/object-detection.html): Detect multiple objects and track their location ([code](app/src/main/java/ai/fritz/aistudio/activities/vision/ObjectDetectionActivity.java))
- [Style Transfer](https://www.fritz.ai/features/style-transfer.html): Transform photos and videos into artistic masterpieces ([code](app/src/main/java/ai/fritz/aistudio/activities/vision/StyleTransferActivity.java))
**Custom Models: Deploy, Monitor, and Update your own models:**
We currently support both TensorFlow Lite and TensorFlow Mobile for Android.
- [Analytics and Monitoring](https://www.fritz.ai/features/analytics-monitoring.html): Monitor machine learning models running on-device with Fritz AI
- [Model Management](https://www.fritz.ai/features/model-management.html): Iterate on your ML models over-the-air, without having to release your app
- [Model Protection](https://www.fritz.ai/features/model-protection.html): Use model protection to keep models from being tampered-with or stolen
## Requirements
- Android Studio 3.2 or above
- Android device in developer model (USB debugging enabled)
## Getting Started
**Step 1: Choose a Fritz AI Plan**
[Sign up](https://www.fritz.ai/pricing/?utm_source=github&utm_campaign=fritz-examples) for a Fritz AI plan to get started.
Register the Android app in your Fritz account with the package id "ai.fritz.aistudiobuild". During registration, you'll receive an API key for the app. Save this for later. To find it in the webapp, you can go to Project Settings > Apps > Your App > Show API Key.
**Step 2: Clone / Fork the fritz-examples repository and open the demo app in Android Studio**
```
git clone https://github.com/fritzlabs/fritz-examples.git
```
In Android Studio, choose "Open an existing Android Studio project" and select `FritzAIStudio`.
**Step 3: Edit the fritz.xml file with your API Key**
In app/src/main/res/values/fritz.xml, change the fritz_api_key attribute with the one you received in step 1.
**Step 4: Build the Android Studio Project**
Select "Build > Make Project" from the top nav. Download any missing libraries if applicable. This should sync the gradle dependencies so give the build a second to complete.
**Step 5: Install the app onto your device**
With your Android device connected, select `Run > Run App` from the top nav. After it's installed, select any of the options to try out the different ML features. When running the app for the first time, you'll have to give permissions to access the camera.
## Official Documentation
[SDK Documentation](https://docs.fritz.ai/?utm_source=github&utm_campaign=fritz-examples)
[Android API Docs](https://docs.fritz.ai/android/latest/index.html?utm_source=github&utm_campaign=fritz-examples)
## Join the community
[Heartbeat](https://heartbeat.fritz.ai/?utm_source=github&utm_campaign=fritz-examples) is a community of developers interested in the intersection of mobile and machine learning. [Chat with us in Slack](https://www.fritz.ai/slack?utm_source=github&utm_campaign=fritz-examples) and stay up to date on the latest mobile ML news with our [Newsletter](https://www.fritz.ai/newsletter?utm_source=github&utm_campaign=fritz-examples).
## Help
For any questions or issues, you can:
- Submit an issue on this repo
- Go to [Support](https://support.fritz.ai/?utm_source=github&utm_campaign=fritz-examples)
- Message us directly in [Slack](https://www.fritz.ai/slack?utm_source=github&utm_campaign=fritz-examples)
================================================
FILE: Android/FritzAIStudio/app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
configurations.all {
resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
}
compileSdkVersion 28
defaultConfig {
applicationId "ai.fritz.aistudiobuild"
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
debuggable true
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
aaptOptions {
noCompress "tflite"
}
lintOptions {
abortOnError false
}
productFlavors {
}
}
dependencies {
implementation 'com.jakewharton:butterknife:8.8.1'
implementation "ai.fritz:core:6.0.3"
implementation "ai.fritz:vision:6.0.3"
implementation "ai.fritz:vision-labeling-model-fast:3.0.0"
implementation "ai.fritz:vision-object-detection-model-fast:3.0.0"
implementation "ai.fritz:vision-style-painting-models:3.0.0"
implementation "ai.fritz:vision-pose-estimation-model-fast:3.0.0"
implementation "ai.fritz:vision-hair-segmentation-model-fast:3.0.0"
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.android.support:appcompat-v7:28.0.0-rc02'
implementation 'com.android.support:recyclerview-v7:28.0.0-rc02'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
================================================
FILE: Android/FritzAIStudio/app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: Android/FritzAIStudio/app/src/main/AndroidManifest.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/assets/coco_labels_list.txt
================================================
???
person
bicycle
car
motorcycle
airplane
bus
train
truck
boat
traffic light
fire hydrant
???
stop sign
parking meter
bench
bird
cat
dog
horse
sheep
cow
elephant
bear
zebra
giraffe
???
backpack
umbrella
???
???
handbag
tie
suitcase
frisbee
skis
snowboard
sports ball
kite
baseball bat
baseball glove
skateboard
surfboard
tennis racket
bottle
???
wine glass
cup
fork
knife
spoon
bowl
banana
apple
sandwich
orange
broccoli
carrot
hot dog
pizza
donut
cake
chair
couch
potted plant
bed
???
dining table
???
???
toilet
???
tv
laptop
mouse
remote
keyboard
cell phone
microwave
oven
toaster
sink
refrigerator
???
book
clock
vase
scissors
teddy bear
hair drier
toothbrush
================================================
FILE: Android/FritzAIStudio/app/src/main/assets/imagenet_comp_graph_label_strings.txt
================================================
dummy
kit fox
English setter
Siberian husky
Australian terrier
English springer
grey whale
lesser panda
Egyptian cat
ibex
Persian cat
cougar
gazelle
porcupine
sea lion
malamute
badger
Great Dane
Walker hound
Welsh springer spaniel
whippet
Scottish deerhound
killer whale
mink
African elephant
Weimaraner
soft-coated wheaten terrier
Dandie Dinmont
red wolf
Old English sheepdog
jaguar
otterhound
bloodhound
Airedale
hyena
meerkat
giant schnauzer
titi
three-toed sloth
sorrel
black-footed ferret
dalmatian
black-and-tan coonhound
papillon
skunk
Staffordshire bullterrier
Mexican hairless
Bouvier des Flandres
weasel
miniature poodle
Cardigan
malinois
bighorn
fox squirrel
colobus
tiger cat
Lhasa
impala
coyote
Yorkshire terrier
Newfoundland
brown bear
red fox
Norwegian elkhound
Rottweiler
hartebeest
Saluki
grey fox
schipperke
Pekinese
Brabancon griffon
West Highland white terrier
Sealyham terrier
guenon
mongoose
indri
tiger
Irish wolfhound
wild boar
EntleBucher
zebra
ram
French bulldog
orangutan
basenji
leopard
Bernese mountain dog
Maltese dog
Norfolk terrier
toy terrier
vizsla
cairn
squirrel monkey
groenendael
clumber
Siamese cat
chimpanzee
komondor
Afghan hound
Japanese spaniel
proboscis monkey
guinea pig
white wolf
ice bear
gorilla
borzoi
toy poodle
Kerry blue terrier
ox
Scotch terrier
Tibetan mastiff
spider monkey
Doberman
Boston bull
Greater Swiss Mountain dog
Appenzeller
Shih-Tzu
Irish water spaniel
Pomeranian
Bedlington terrier
warthog
Arabian camel
siamang
miniature schnauzer
collie
golden retriever
Irish terrier
affenpinscher
Border collie
hare
boxer
silky terrier
beagle
Leonberg
German short-haired pointer
patas
dhole
baboon
macaque
Chesapeake Bay retriever
bull mastiff
kuvasz
capuchin
pug
curly-coated retriever
Norwich terrier
flat-coated retriever
hog
keeshond
Eskimo dog
Brittany spaniel
standard poodle
Lakeland terrier
snow leopard
Gordon setter
dingo
standard schnauzer
hamster
Tibetan terrier
Arctic fox
wire-haired fox terrier
basset
water buffalo
American black bear
Angora
bison
howler monkey
hippopotamus
chow
giant panda
American Staffordshire terrier
Shetland sheepdog
Great Pyrenees
Chihuahua
tabby
marmoset
Labrador retriever
Saint Bernard
armadillo
Samoyed
bluetick
redbone
polecat
marmot
kelpie
gibbon
llama
miniature pinscher
wood rabbit
Italian greyhound
lion
cocker spaniel
Irish setter
dugong
Indian elephant
beaver
Sussex spaniel
Pembroke
Blenheim spaniel
Madagascar cat
Rhodesian ridgeback
lynx
African hunting dog
langur
Ibizan hound
timber wolf
cheetah
English foxhound
briard
sloth bear
Border terrier
German shepherd
otter
koala
tusker
echidna
wallaby
platypus
wombat
revolver
umbrella
schooner
soccer ball
accordion
ant
starfish
chambered nautilus
grand piano
laptop
strawberry
airliner
warplane
airship
balloon
space shuttle
fireboat
gondola
speedboat
lifeboat
canoe
yawl
catamaran
trimaran
container ship
liner
pirate
aircraft carrier
submarine
wreck
half track
tank
missile
bobsled
dogsled
bicycle-built-for-two
mountain bike
freight car
passenger car
barrow
shopping cart
motor scooter
forklift
electric locomotive
steam locomotive
amphibian
ambulance
beach wagon
cab
convertible
jeep
limousine
minivan
Model T
racer
sports car
go-kart
golfcart
moped
snowplow
fire engine
garbage truck
pickup
tow truck
trailer truck
moving van
police van
recreational vehicle
streetcar
snowmobile
tractor
mobile home
tricycle
unicycle
horse cart
jinrikisha
oxcart
bassinet
cradle
crib
four-poster
bookcase
china cabinet
medicine chest
chiffonier
table lamp
file
park bench
barber chair
throne
folding chair
rocking chair
studio couch
toilet seat
desk
pool table
dining table
entertainment center
wardrobe
Granny Smith
orange
lemon
fig
pineapple
banana
jackfruit
custard apple
pomegranate
acorn
hip
ear
rapeseed
corn
buckeye
organ
upright
chime
drum
gong
maraca
marimba
steel drum
banjo
cello
violin
harp
acoustic guitar
electric guitar
cornet
French horn
trombone
harmonica
ocarina
panpipe
bassoon
oboe
sax
flute
daisy
yellow lady's slipper
cliff
valley
alp
volcano
promontory
sandbar
coral reef
lakeside
seashore
geyser
hatchet
cleaver
letter opener
plane
power drill
lawn mower
hammer
corkscrew
can opener
plunger
screwdriver
shovel
plow
chain saw
cock
hen
ostrich
brambling
goldfinch
house finch
junco
indigo bunting
robin
bulbul
jay
magpie
chickadee
water ouzel
kite
bald eagle
vulture
great grey owl
black grouse
ptarmigan
ruffed grouse
prairie chicken
peacock
quail
partridge
African grey
macaw
sulphur-crested cockatoo
lorikeet
coucal
bee eater
hornbill
hummingbird
jacamar
toucan
drake
red-breasted merganser
goose
black swan
white stork
black stork
spoonbill
flamingo
American egret
little blue heron
bittern
crane
limpkin
American coot
bustard
ruddy turnstone
red-backed sandpiper
redshank
dowitcher
oystercatcher
European gallinule
pelican
king penguin
albatross
great white shark
tiger shark
hammerhead
electric ray
stingray
barracouta
coho
tench
goldfish
eel
rock beauty
anemone fish
lionfish
puffer
sturgeon
gar
loggerhead
leatherback turtle
mud turtle
terrapin
box turtle
banded gecko
common iguana
American chameleon
whiptail
agama
frilled lizard
alligator lizard
Gila monster
green lizard
African chameleon
Komodo dragon
triceratops
African crocodile
American alligator
thunder snake
ringneck snake
hognose snake
green snake
king snake
garter snake
water snake
vine snake
night snake
boa constrictor
rock python
Indian cobra
green mamba
sea snake
horned viper
diamondback
sidewinder
European fire salamander
common newt
eft
spotted salamander
axolotl
bullfrog
tree frog
tailed frog
whistle
wing
paintbrush
hand blower
oxygen mask
snorkel
loudspeaker
microphone
screen
mouse
electric fan
oil filter
strainer
space heater
stove
guillotine
barometer
rule
odometer
scale
analog clock
digital clock
wall clock
hourglass
sundial
parking meter
stopwatch
digital watch
stethoscope
syringe
magnetic compass
binoculars
projector
sunglasses
loupe
radio telescope
bow
cannon [ground]
assault rifle
rifle
projectile
computer keyboard
typewriter keyboard
crane
lighter
abacus
cash machine
slide rule
desktop computer
hand-held computer
notebook
web site
harvester
thresher
printer
slot
vending machine
sewing machine
joystick
switch
hook
car wheel
paddlewheel
pinwheel
potter's wheel
gas pump
carousel
swing
reel
radiator
puck
hard disc
sunglass
pick
car mirror
solar dish
remote control
disk brake
buckle
hair slide
knot
combination lock
padlock
nail
safety pin
screw
muzzle
seat belt
ski
candle
jack-o'-lantern
spotlight
torch
neck brace
pier
tripod
maypole
mousetrap
spider web
trilobite
harvestman
scorpion
black and gold garden spider
barn spider
garden spider
black widow
tarantula
wolf spider
tick
centipede
isopod
Dungeness crab
rock crab
fiddler crab
king crab
American lobster
spiny lobster
crayfish
hermit crab
tiger beetle
ladybug
ground beetle
long-horned beetle
leaf beetle
dung beetle
rhinoceros beetle
weevil
fly
bee
grasshopper
cricket
walking stick
cockroach
mantis
cicada
leafhopper
lacewing
dragonfly
damselfly
admiral
ringlet
monarch
cabbage butterfly
sulphur butterfly
lycaenid
jellyfish
sea anemone
brain coral
flatworm
nematode
conch
snail
slug
sea slug
chiton
sea urchin
sea cucumber
iron
espresso maker
microwave
Dutch oven
rotisserie
toaster
waffle iron
vacuum
dishwasher
refrigerator
washer
Crock Pot
frying pan
wok
caldron
coffeepot
teapot
spatula
altar
triumphal arch
patio
steel arch bridge
suspension bridge
viaduct
barn
greenhouse
palace
monastery
library
apiary
boathouse
church
mosque
stupa
planetarium
restaurant
cinema
home theater
lumbermill
coil
obelisk
totem pole
castle
prison
grocery store
bakery
barbershop
bookshop
butcher shop
confectionery
shoe shop
tobacco shop
toyshop
fountain
cliff dwelling
yurt
dock
brass
megalith
bannister
breakwater
dam
chainlink fence
picket fence
worm fence
stone wall
grille
sliding door
turnstile
mountain tent
scoreboard
honeycomb
plate rack
pedestal
beacon
mashed potato
bell pepper
head cabbage
broccoli
cauliflower
zucchini
spaghetti squash
acorn squash
butternut squash
cucumber
artichoke
cardoon
mushroom
shower curtain
jean
carton
handkerchief
sandal
ashcan
safe
plate
necklace
croquet ball
fur coat
thimble
pajama
running shoe
cocktail shaker
chest
manhole cover
modem
tub
tray
balance beam
bagel
prayer rug
kimono
hot pot
whiskey jug
knee pad
book jacket
spindle
ski mask
beer bottle
crash helmet
bottlecap
tile roof
mask
maillot
Petri dish
football helmet
bathing cap
teddy bear
holster
pop bottle
photocopier
vestment
crossword puzzle
golf ball
trifle
suit
water tower
feather boa
cloak
red wine
drumstick
shield
Christmas stocking
hoopskirt
menu
stage
bonnet
meat loaf
baseball
face powder
scabbard
sunscreen
beer glass
hen-of-the-woods
guacamole
lampshade
wool
hay
bow tie
mailbag
water jug
bucket
dishrag
soup bowl
eggnog
mortar
trench coat
paddle
chain
swab
mixing bowl
potpie
wine bottle
shoji
bulletproof vest
drilling platform
binder
cardigan
sweatshirt
pot
birdhouse
hamper
ping-pong ball
pencil box
pay-phone
consomme
apron
punching bag
backpack
groom
bearskin
pencil sharpener
broom
mosquito net
abaya
mortarboard
poncho
crutch
Polaroid camera
space bar
cup
racket
traffic light
quill
radio
dough
cuirass
military uniform
lipstick
shower cap
monitor
oscilloscope
mitten
brassiere
French loaf
vase
milk can
rugby ball
paper towel
earthstar
envelope
miniskirt
cowboy hat
trolleybus
perfume
bathtub
hotdog
coral fungus
bullet train
pillow
toilet tissue
cassette
carpenter's kit
ladle
stinkhorn
lotion
hair spray
academic gown
dome
crate
wig
burrito
pill bottle
chain mail
theater curtain
window shade
barrel
washbasin
ballpoint
basketball
bath towel
cowboy boot
gown
window screen
agaric
cellular telephone
nipple
barbell
mailbox
lab coat
fire screen
minibus
packet
maze
pole
horizontal bar
sombrero
pickelhaube
rain barrel
wallet
cassette player
comic book
piggy bank
street sign
bell cote
fountain pen
Windsor tie
volleyball
overskirt
sarong
purse
bolo tie
bib
parachute
sleeping bag
television
swimming trunks
measuring cup
espresso
pizza
breastplate
shopping basket
wooden spoon
saltshaker
chocolate sauce
ballplayer
goblet
gyromitra
stretcher
water bottle
dial telephone
soap dispenser
jersey
school bus
jigsaw puzzle
plastic bag
reflex camera
diaper
Band Aid
ice lolly
velvet
tennis ball
gasmask
doormat
Loafer
ice cream
pretzel
quilt
maillot
tape player
clog
iPod
bolete
scuba diver
pitcher
matchstick
bikini
sock
CD player
lens cap
thatch
vault
beaker
bubble
cheeseburger
parallel bars
flagpole
coffee mug
rubber eraser
stole
carbonara
dumbbell
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/Navigation.java
================================================
package ai.fritz.aistudio;
import android.content.Context;
import android.content.Intent;
import ai.fritz.aistudio.activities.custommodel.CustomTFLiteActivity;
import ai.fritz.aistudio.activities.vision.ImageLabelingActivity;
import ai.fritz.aistudio.activities.vision.ImageSegmentationActivity;
import ai.fritz.aistudio.activities.vision.ObjectDetectionActivity;
import ai.fritz.aistudio.activities.vision.PoseEstimationActivity;
import ai.fritz.aistudio.activities.vision.StyleTransferActivity;
/**
* Navigation is a helper class for common links throughout the app.
*/
public class Navigation {
public static void goToTFLite(Context context) {
Intent tflite = new Intent(context, CustomTFLiteActivity.class);
context.startActivity(tflite);
}
public static void goToLabelingActivity(Context context) {
Intent labelActivity = new Intent(context, ImageLabelingActivity.class);
context.startActivity(labelActivity);
}
public static void goToStyleTransfer(Context context) {
Intent styleActivity = new Intent(context, StyleTransferActivity.class);
context.startActivity(styleActivity);
}
public static void goToImageSegmentation(Context context) {
Intent imgSegActivity = new Intent(context, ImageSegmentationActivity.class);
context.startActivity(imgSegActivity);
}
public static void goToObjectDetection(Context context) {
Intent objectDetection = new Intent(context, ObjectDetectionActivity.class);
context.startActivity(objectDetection);
}
public static void goToPoseEstimation(Context context) {
Intent poseEstimation = new Intent(context, PoseEstimationActivity.class);
context.startActivity(poseEstimation);
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/PredictorType.java
================================================
package ai.fritz.aistudio;
public enum PredictorType {
POSE_ESTIMATION,
STYLE_TRANSFER,
IMAGE_LABELING,
OBJECT_DETECTION,
PEOPLE_SEGMENTATION,
LIVING_ROOM_SEGMENTATION,
OUTDOOR_SEGMENTATION,
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/activities/BaseCameraActivity.java
================================================
package ai.fritz.aistudio.activities;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.ImageReader.OnImageAvailableListener;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.util.Size;
import android.view.KeyEvent;
import android.view.WindowManager;
import android.widget.Toast;
import ai.fritz.aistudio.R;
import ai.fritz.aistudio.fragments.CameraConnectionFragment;
import ai.fritz.aistudio.ui.OverlayView;
public abstract class BaseCameraActivity extends AppCompatActivity implements OnImageAvailableListener {
private static final String TAG = BaseCameraActivity.class.getSimpleName();
private static final int PERMISSIONS_REQUEST = 1;
private static final String PERMISSION_CAMERA = Manifest.permission.CAMERA;
private static final String PERMISSION_STORAGE = Manifest.permission.WRITE_EXTERNAL_STORAGE;
private boolean useCamera2API;
private boolean debug = false;
private Handler handler;
private HandlerThread handlerThread;
protected String cameraId;
@Override
protected void onCreate(final Bundle savedInstanceState) {
Log.d(TAG, "onCreate " + this);
super.onCreate(null);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_camera);
if (hasPermission()) {
setFragment();
} else {
requestPermission();
}
}
@Override
public synchronized void onStart() {
Log.d(TAG, "onStart " + this);
super.onStart();
}
@Override
public synchronized void onResume() {
Log.d(TAG, "onResume " + this);
super.onResume();
handlerThread = new HandlerThread("inference");
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
}
@Override
public synchronized void onPause() {
Log.d(TAG, "onPause " + this);
handlerThread.quitSafely();
try {
handlerThread.join();
handlerThread = null;
handler = null;
} catch (final InterruptedException e) {
Log.e(TAG, "Exception!" + e);
}
super.onPause();
}
@Override
public synchronized void onStop() {
Log.d(TAG, "onStop " + this);
super.onStop();
}
@Override
public synchronized void onDestroy() {
Log.d(TAG, "onDestroy " + this);
super.onDestroy();
}
protected synchronized void runInBackground(final Runnable r) {
if (handler != null) {
handler.post(r);
}
}
@Override
public void onRequestPermissionsResult(
final int requestCode, final String[] permissions, final int[] grantResults) {
switch (requestCode) {
case PERMISSIONS_REQUEST: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
setFragment();
} else {
requestPermission();
}
}
}
}
private boolean hasPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return checkSelfPermission(PERMISSION_CAMERA) == PackageManager.PERMISSION_GRANTED && checkSelfPermission(PERMISSION_STORAGE) == PackageManager.PERMISSION_GRANTED;
} else {
return true;
}
}
private void requestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (shouldShowRequestPermissionRationale(PERMISSION_CAMERA) || shouldShowRequestPermissionRationale(PERMISSION_STORAGE)) {
Toast.makeText(BaseCameraActivity.this, "Camera AND storage permission are required for this demo", Toast.LENGTH_LONG).show();
}
requestPermissions(new String[]{PERMISSION_CAMERA, PERMISSION_STORAGE}, PERMISSIONS_REQUEST);
}
}
protected void setFragment() {
cameraId = chooseCamera();
final CameraConnectionFragment fragment =
CameraConnectionFragment.newInstance(
new CameraConnectionFragment.ConnectionCallback() {
@Override
public void onPreviewSizeChosen(final Size previewSize, final Size cameraViewSize, final int rotation) {
BaseCameraActivity.this.onPreviewSizeChosen(previewSize, cameraViewSize, rotation);
}
},
this,
getLayoutId(),
getDesiredPreviewFrameSize());
fragment.setCamera(cameraId);
getFragmentManager()
.beginTransaction()
.replace(R.id.camera_container, fragment)
.commit();
}
private String chooseCamera() {
final CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
for (final String cameraId : manager.getCameraIdList()) {
final CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
// We don't use a front facing camera in this sample.
final Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
continue;
}
final StreamConfigurationMap map =
characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
continue;
}
// Fallback to camera1 API for internal cameras that don't have full support.
// This should help with legacy situations where using the camera2 API causes
// distorted or otherwise broken previews.
useCamera2API = (facing == CameraCharacteristics.LENS_FACING_EXTERNAL)
|| isHardwareLevelSupported(characteristics,
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
Log.i(TAG, "Camera API lv2?: " + useCamera2API);
return cameraId;
}
} catch (CameraAccessException e) {
Log.e(TAG, "Not allowed to access camera: " + e);
}
return null;
}
// Returns true if the device supports the required hardware level, or better.
private boolean isHardwareLevelSupported(
CameraCharacteristics characteristics, int requiredLevel) {
int deviceLevel = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (deviceLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
return requiredLevel == deviceLevel;
}
// deviceLevel is not LEGACY, can use numerical sort
return requiredLevel <= deviceLevel;
}
public boolean isDebug() {
return debug;
}
public void requestRender() {
final OverlayView overlay = (OverlayView) findViewById(R.id.debug_overlay);
if (overlay != null) {
overlay.postInvalidate();
}
}
public void setCallback(final OverlayView.DrawCallback callback) {
final OverlayView overlay = (OverlayView) findViewById(R.id.debug_overlay);
if (overlay != null) {
overlay.setCallback(callback);
}
}
public void onSetDebug(final boolean debug) {
}
@Override
public boolean onKeyDown(final int keyCode, final KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
debug = !debug;
requestRender();
onSetDebug(debug);
return true;
}
return super.onKeyDown(keyCode, event);
}
protected abstract void onPreviewSizeChosen(final Size previewSize, final Size cameraViewSize, final int rotation);
protected abstract int getLayoutId();
protected Size getDesiredPreviewFrameSize() {
return new Size(640, 480);
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/activities/BaseLiveVideoActivity.java
================================================
package ai.fritz.aistudio.activities;
import android.graphics.Canvas;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.util.Size;
import android.widget.Button;
import java.util.concurrent.atomic.AtomicBoolean;
import ai.fritz.aistudio.R;
import ai.fritz.aistudio.ui.OverlayView;
import ai.fritz.vision.FritzVisionImage;
import ai.fritz.vision.FritzVisionOrientation;
import ai.fritz.vision.ImageOrientation;
public abstract class BaseLiveVideoActivity extends BaseCameraActivity implements ImageReader.OnImageAvailableListener {
private static final String TAG = BaseLiveVideoActivity.class.getSimpleName();
private AtomicBoolean computing = new AtomicBoolean(false);
protected FritzVisionImage fritzVisionImage;
private ImageOrientation orientation;
protected Button chooseModelBtn;
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onPreviewSizeChosen(final Size size, final Size cameraSize, final int rotation) {
orientation = FritzVisionOrientation.getImageOrientationFromCamera(this, cameraId);
chooseModelBtn = findViewById(R.id.chose_model_btn);
setCallback(
new OverlayView.DrawCallback() {
@Override
public void drawCallback(final Canvas canvas) {
handleDrawingResult(canvas, cameraSize);
}
});
onCameraSetup(cameraSize);
}
@Override
public void onImageAvailable(final ImageReader reader) {
Image image = reader.acquireLatestImage();
if (image == null) {
return;
}
if (!computing.compareAndSet(false, true)) {
image.close();
return;
}
fritzVisionImage = FritzVisionImage.fromMediaImage(image, orientation);
image.close();
runInBackground(
new Runnable() {
@Override
public void run() {
runInference(fritzVisionImage);
requestRender();
computing.set(false);
}
});
}
@Override
protected int getLayoutId() {
return R.layout.camera_connection_fragment_tracking;
}
@Override
public void onSetDebug(final boolean debug) {
}
protected abstract void onCameraSetup(Size cameraSize);
protected abstract void handleDrawingResult(Canvas canvas, Size cameraSize);
protected abstract void runInference(FritzVisionImage fritzVisionImage);
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/activities/BaseRecordingActivity.java
================================================
package ai.fritz.aistudio.activities;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.media.Image;
import android.media.ImageReader;
import android.media.ImageReader.OnImageAvailableListener;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.util.Log;
import android.util.Size;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import ai.fritz.aistudio.R;
import ai.fritz.aistudio.ui.ChooseModelDialog;
import ai.fritz.aistudio.ui.OverlayView;
import ai.fritz.aistudio.utils.VideoProcessingQueue;
import ai.fritz.vision.FritzVisionImage;
import ai.fritz.vision.FritzVisionOrientation;
import ai.fritz.vision.ImageOrientation;
import ai.fritz.vision.ImageRotation;
public abstract class BaseRecordingActivity extends BaseCameraActivity implements OnImageAvailableListener {
private static final String TAG = BaseRecordingActivity.class.getSimpleName();
private static final long TIME_BETWEEN_FRAMES_MS = 100;
private static final long TIME_BUFFER_FRAMES_MS = 50;
private static final long MAX_RECORDING_TIME_MS = TimeUnit.SECONDS.toMillis(5);
private static final long TIME_BETWEEN_RECORDING_INTERVAL_MS = 50;
private static final int NUM_PROGRESS_INTERVALS = (int) (MAX_RECORDING_TIME_MS / TIME_BETWEEN_RECORDING_INTERVAL_MS);
private AtomicBoolean isRecording = new AtomicBoolean(false);
private OverlayView overlayView;
private ImageOrientation orientation;
private ChooseModelDialog imageSegDialog;
private Button takeVideoBtn;
private Button closeBtn;
private Button chooseModelBtn;
private ProgressBar processingVideoProgress;
private ProgressBar videoRecordingProgress;
private ProgressBar loadingModelSpinner;
private VideoProcessingQueue videoProcessingQueue;
private LinkedBlockingQueue processedBitmaps = new LinkedBlockingQueue<>();
private AtomicLong lastRecordedFrameAt = new AtomicLong(0);
private CountDownTimer mCountDownTimer;
private Handler playBackHandler = new Handler();
private int processingProgress = 0;
protected abstract int getModelOptionsTextId();
protected abstract Bitmap runPrediction(FritzVisionImage visionImage, Size cameraViewSize);
protected abstract void loadPredictor(int choice);
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected int getLayoutId() {
return R.layout.camera_connection_fragment_recording;
}
@Override
public void onPreviewSizeChosen(final Size previewSize, final Size cameraViewSize, final int rotation) {
overlayView = findViewById(R.id.debug_overlay);
takeVideoBtn = findViewById(R.id.take_video_btn);
processingVideoProgress = findViewById(R.id.processingVideoProgress);
videoRecordingProgress = findViewById(R.id.videoRecordingProgress);
closeBtn = findViewById(R.id.close_btn);
chooseModelBtn = findViewById(R.id.choose_model_btn);
loadingModelSpinner = findViewById(R.id.loadingModelSpinner);
showCameraViews();
// Create a predictor
loadPredictor(0);
chooseModelBtn.setText(getModelText(0));
videoProcessingQueue = new VideoProcessingQueue(new VideoProcessingQueue.Listener() {
@Override
public void processVisionImage(FritzVisionImage visionImage) {
Bitmap result = runPrediction(visionImage, cameraViewSize);
processedBitmaps.add(result);
Log.d(TAG, "Processed Frame #: " + processedBitmaps.size());
if (!isRecording.get()) {
processingVideoProgress.setProgress(++processingProgress);
}
}
@Override
public void finishedProcessing() {
Log.d(TAG, "Finished processing video clips");
finishProcessing();
}
});
orientation = FritzVisionOrientation.getImageOrientationFromCamera(this, cameraId);
// Dialog for image seg choice;
imageSegDialog = new ChooseModelDialog(getModelOptionsTextId(), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// The 'which' argument contains the index position
// of the selected item.
loadPredictor(which);
chooseModelBtn.setText(getModelText(which));
}
});
closeBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
showCameraViews();
}
});
final Paint paint = new Paint();
setCallback(
new OverlayView.DrawCallback() {
@Override
public void drawCallback(final Canvas canvas) {
if (!processedBitmaps.isEmpty()) {
Bitmap bitmap = processedBitmaps.poll();
canvas.drawBitmap(bitmap, null, new RectF(0, 0, cameraViewSize.getWidth(), cameraViewSize.getHeight()), null);
}
}
});
takeVideoBtn.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
isRecording.compareAndSet(false, true);
lastRecordedFrameAt.set(0);
showStartRecordingViews();
return true;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
isRecording.compareAndSet(true, false);
showFinishRecordingViews();
return true;
}
return false;
}
});
chooseModelBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
imageSegDialog.show(getSupportFragmentManager(), ChooseModelDialog.TAG);
}
});
}
protected void showPredictorReadyViews() {
takeVideoBtn.setVisibility(View.VISIBLE);
chooseModelBtn.setVisibility(View.VISIBLE);
loadingModelSpinner.setVisibility(View.GONE);
}
protected void showPredictorNotReadyViews() {
takeVideoBtn.setVisibility(View.GONE);
chooseModelBtn.setVisibility(View.GONE);
loadingModelSpinner.setVisibility(View.VISIBLE);
}
private void showStartRecordingViews() {
videoRecordingProgress.setVisibility(View.VISIBLE);
videoRecordingProgress.setMax(NUM_PROGRESS_INTERVALS);
videoRecordingProgress.setProgress(0);
chooseModelBtn.setVisibility(View.GONE);
mCountDownTimer = new CountDownTimer(MAX_RECORDING_TIME_MS, TIME_BETWEEN_RECORDING_INTERVAL_MS) {
@Override
public void onTick(long millisUntilFinished) {
int progress = (int) (NUM_PROGRESS_INTERVALS - millisUntilFinished / TIME_BETWEEN_RECORDING_INTERVAL_MS);
videoRecordingProgress.setProgress(progress);
}
@Override
public void onFinish() {
// finish recording when MAX_RECORDING_TIME_MS is met
if (isRecording.compareAndSet(true, false)) {
videoRecordingProgress.setProgress(NUM_PROGRESS_INTERVALS);
showFinishRecordingViews();
}
}
};
mCountDownTimer.start();
}
private void showCameraViews() {
overlayView.setVisibility(View.GONE);
closeBtn.setVisibility(View.GONE);
takeVideoBtn.setVisibility(View.VISIBLE);
chooseModelBtn.setVisibility(View.VISIBLE);
videoRecordingProgress.setProgress(0);
videoRecordingProgress.setVisibility(View.GONE);
}
private void showFinishRecordingViews() {
videoRecordingProgress.setProgress(0);
takeVideoBtn.setVisibility(View.GONE);
overlayView.setVisibility(View.VISIBLE);
videoRecordingProgress.setVisibility(View.INVISIBLE);
processingVideoProgress.setVisibility(View.VISIBLE);
processingVideoProgress.setMax(videoProcessingQueue.getNumFramesToProcess());
processingVideoProgress.setProgress(0);
}
private void showStyleResults() {
processingVideoProgress.setVisibility(View.GONE);
closeBtn.setVisibility(View.VISIBLE);
overlayView.setVisibility(View.VISIBLE);
}
private void finishProcessing() {
runOnUiThread(new Runnable() {
@Override
public void run() {
showStyleResults();
}
});
processingProgress = 0;
// Redraw the overlay view
overlayView.postInvalidate();
// This is a bit hacky but re-render the
// overlay view with a new bitmap result after
// a certain amount of time until all the bitmaps are shown.
Runnable runnable = new Runnable() {
@Override
public void run() {
if (processedBitmaps.isEmpty()) {
return;
}
overlayView.postInvalidate();
playBackHandler.postDelayed(this, TIME_BETWEEN_FRAMES_MS + TIME_BUFFER_FRAMES_MS);
}
};
runnable.run();
}
public String getModelText(int choice) {
String[] options = getResources().getStringArray(getModelOptionsTextId());
return options[choice];
}
@Override
public void onImageAvailable(final ImageReader reader) {
Image image = reader.acquireLatestImage();
if (image == null) {
return;
}
// Save Images when we're recording
if (!isRecording.get()) {
image.close();
return;
}
// Only grab a frame every 100ms
if (System.currentTimeMillis() - lastRecordedFrameAt.get() < TIME_BETWEEN_FRAMES_MS) {
image.close();
return;
}
// Add the frame to a queue to process
lastRecordedFrameAt.set(System.currentTimeMillis());
final FritzVisionImage fritzImage = FritzVisionImage.fromMediaImage(image, orientation);
videoProcessingQueue.addVisionImage(fritzImage);
image.close();
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/activities/MainActivity.java
================================================
package ai.fritz.aistudio.activities;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import ai.fritz.core.Fritz;
import ai.fritz.aistudio.PredictorType;
import ai.fritz.aistudio.R;
import ai.fritz.aistudio.adapters.DemoAdapter;
import ai.fritz.aistudio.adapters.DemoItem;
import ai.fritz.aistudio.ui.SeparatorDecoration;
import ai.fritz.aistudio.Navigation;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* The primary activity that shows the different model demos.
*/
public class MainActivity extends AppCompatActivity {
private static final String FRITZ_URL = "https://fritz.ai";
private Logger logger = Logger.getLogger(this.getClass().getName());
@BindView(R.id.demo_list_view)
RecyclerView recyclerView;
@BindView(R.id.app_toolbar)
Toolbar appBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTitle(R.string.demo_title);
ButterKnife.bind(this);
// customize the action bar
setSupportActionBar(appBar);
// Initialize Fritz
Fritz.configure(this);
// Setup the recycler view
recyclerView.setHasFixedSize(true);
LinearLayoutManager rvLinearLayoutMgr = new LinearLayoutManager(this);
recyclerView.setLayoutManager(rvLinearLayoutMgr);
// Add a divider
SeparatorDecoration decoration = new SeparatorDecoration(this, Color.GRAY, 1);
recyclerView.addItemDecoration(decoration);
// Add the adapter
DemoAdapter adapter = new DemoAdapter(getDemoItems());
recyclerView.setAdapter(adapter);
recyclerView.setClickable(true);
}
private List getDemoItems() {
// Add different demo items here
List demoItems = new ArrayList<>();
demoItems.add(new DemoItem(
getString(R.string.fritz_vision_title),
getString(R.string.fritz_vision_description_live_video),
new View.OnClickListener() {
@Override
public void onClick(View v) {
logger.info("FRITZ VISION LIVE VIDEO");
Navigation.goToLabelingActivity(v.getContext());
}
}));
demoItems.add(new DemoItem(
getString(R.string.fritz_object_detection_title),
getString(R.string.fritz_object_detection_description),
new View.OnClickListener() {
@Override
public void onClick(View v) {
Navigation.goToObjectDetection(v.getContext());
}
}));
demoItems.add(new DemoItem(
getString(R.string.fritz_vision_style_transfer),
getString(R.string.fritz_vision_style_transfer_description),
new View.OnClickListener() {
@Override
public void onClick(View v) {
Navigation.goToStyleTransfer(v.getContext());
}
}));
demoItems.add(new DemoItem(
getString(R.string.fritz_vision_img_seg_title),
getString(R.string.fritz_vision_img_seg_description),
new View.OnClickListener() {
@Override
public void onClick(View v) {
Navigation.goToImageSegmentation(v.getContext());
}
}));
demoItems.add(new DemoItem(
getString(R.string.fritz_pose_estimation_title),
getString(R.string.fritz_pose_estimation_description),
new View.OnClickListener() {
@Override
public void onClick(View v) {
Navigation.goToPoseEstimation(v.getContext());
}
}));
demoItems.add(new DemoItem(
getString(R.string.fritz_customtflite_title),
getString(R.string.fritz_customtflite_description),
new View.OnClickListener() {
@Override
public void onClick(View v) {
Navigation.goToTFLite(v.getContext());
}
}));
demoItems.add(new DemoItem(
getString(R.string.fritz_info_title),
getString(R.string.fritz_info_description),
new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(FRITZ_URL));
startActivity(i);
}
}));
return demoItems;
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/activities/SplashActivity.java
================================================
package ai.fritz.aistudio.activities;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import ai.fritz.aistudio.activities.MainActivity;
/**
* The splash activity is the entry point for the rest of the app.
*/
public class SplashActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
finish();
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/activities/custommodel/CustomTFLiteActivity.java
================================================
package ai.fritz.aistudio.activities.custommodel;
import android.graphics.PointF;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import ai.fritz.aistudio.R;
import ai.fritz.aistudio.activities.custommodel.ml.MnistClassifier;
import ai.fritz.aistudio.ui.DrawModel;
import ai.fritz.aistudio.ui.DrawView;
import butterknife.BindView;
import butterknife.ButterKnife;
public class CustomTFLiteActivity extends AppCompatActivity {
private final String TAG = this.getClass().getSimpleName();
private static final int PIXEL_WIDTH = 28;
private float lastX;
private float lastY;
private DrawModel drawModel;
private PointF fPoint = new PointF();
private MnistClassifier mnistClassifier;
@BindView(R.id.view_draw)
DrawView mDrawView;
@BindView(R.id.button_detect)
View detectButton;
@BindView(R.id.button_clear)
View clearButton;
@BindView(R.id.text_result)
TextView mResultText;
@BindView(R.id.app_toolbar)
Toolbar appBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mnist);
setTitle(R.string.app_name);
ButterKnife.bind(this);
setSupportActionBar(appBar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mnistClassifier = new MnistClassifier(this);
drawModel = new DrawModel(PIXEL_WIDTH, PIXEL_WIDTH);
mDrawView.setModel(drawModel);
mDrawView.setOnTouchListener(new DrawOnTouchListener());
detectButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onDetectClicked();
}
});
clearButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onClearClicked();
}
});
}
@Override
protected void onResume() {
mDrawView.onResume();
super.onResume();
}
@Override
protected void onPause() {
mDrawView.onPause();
super.onPause();
}
private void onDetectClicked() {
int digit = mnistClassifier.classify(mDrawView.getDrawnBitmap());
if (digit >= 0) {
Log.d(TAG, "Found Digit = " + digit);
mResultText.setText(getString(R.string.found_digits, String.valueOf(digit)));
} else {
mResultText.setText(getString(R.string.not_detected));
}
}
private void onClearClicked() {
drawModel.clear();
mDrawView.reset();
mDrawView.invalidate();
mResultText.setText("");
}
/**
* DrawOnTouchListener to handle drawing actions.
*/
public class DrawOnTouchListener implements View.OnTouchListener {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction() & MotionEvent.ACTION_MASK;
if (action == MotionEvent.ACTION_DOWN) {
processTouchDown(event);
return true;
} else if (action == MotionEvent.ACTION_MOVE) {
processTouchMove(event);
return true;
} else if (action == MotionEvent.ACTION_UP) {
processTouchUp();
return true;
}
return false;
}
private void processTouchDown(MotionEvent event) {
lastX = event.getX();
lastY = event.getY();
mDrawView.calcPos(lastX, lastY, fPoint);
float lastConvX = fPoint.x;
float lastConvY = fPoint.y;
drawModel.startLine(lastConvX, lastConvY);
}
private void processTouchMove(MotionEvent event) {
float x = event.getX();
float y = event.getY();
mDrawView.calcPos(x, y, fPoint);
float newConvX = fPoint.x;
float newConvY = fPoint.y;
drawModel.addLineElem(newConvX, newConvY);
lastX = x;
lastY = y;
mDrawView.invalidate();
}
private void processTouchUp() {
drawModel.endLine();
}
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/activities/custommodel/ml/Classifier.java
================================================
package ai.fritz.aistudio.activities.custommodel.ml;
/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
import android.graphics.Bitmap;
import android.graphics.RectF;
import java.util.List;
/**
* Generic interface for interacting with different recognition engines.
*/
public interface Classifier {
/**
* An immutable result returned by a Classifier describing what was recognized.
*/
public class Recognition {
/**
* A unique identifier for what has been recognized. Specific to the class, not the instance of
* the object.
*/
private final String id;
/**
* Display name for the recognition.
*/
private final String title;
/**
* A sortable score for how good the recognition is relative to others. Higher should be better.
*/
private final Float confidence;
/**
* Optional location within the source image for the location of the recognized object.
*/
private RectF location;
public Recognition(
final String id, final String title, final Float confidence, final RectF location) {
this.id = id;
this.title = title;
this.confidence = confidence;
this.location = location;
}
public String getId() {
return id;
}
public String getTitle() {
return title;
}
public Float getConfidence() {
return confidence;
}
public RectF getLocation() {
return new RectF(location);
}
public void setLocation(RectF location) {
this.location = location;
}
@Override
public String toString() {
String resultString = "";
if (id != null) {
resultString += "[" + id + "] ";
}
if (title != null) {
resultString += title + " ";
}
if (confidence != null) {
resultString += String.format("(%.1f%%) ", confidence * 100.0f);
}
if (location != null) {
resultString += location + " ";
}
return resultString.trim();
}
}
List recognizeImage(Bitmap bitmap);
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/activities/custommodel/ml/MnistClassifier.java
================================================
package ai.fritz.aistudio.activities.custommodel.ml;
import android.app.Activity;
import android.graphics.Bitmap;
import android.os.SystemClock;
import android.util.Log;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import ai.fritz.aistudio.R;
import ai.fritz.core.FritzManagedModel;
import ai.fritz.core.FritzOnDeviceModel;
import ai.fritz.core.FritzTFLiteInterpreter;
import ai.fritz.core.ModelReadyListener;
import ai.fritz.core.utils.FritzModelManager;
public class MnistClassifier {
private final String TAG = this.getClass().getSimpleName();
// The tensorflow lite file
private FritzTFLiteInterpreter tflite;
// Input byte buffer
private ByteBuffer imgData = null;
// Output array [batch_size, 10]
private float[][] mnistOutput = null;
// Name of the file in the assets folder
// Specify the output size
private static final int NUMBER_LENGTH = 10;
// Specify the input size
private static final int DIM_BATCH_SIZE = 1;
private static final int DIM_IMG_SIZE_X = 28;
private static final int DIM_IMG_SIZE_Y = 28;
private static final int DIM_PIXEL_SIZE = 1;
// Number of bytes to hold a float (32 bits / float) / (8 bits / byte) = 4 bytes / float
private static final int BYTE_SIZE_OF_FLOAT = 4;
public MnistClassifier(Activity activity) {
/**
* This MNIST model provided is used to demonstrate custom models with TensorFlow Lite
* and should not be used in production.
*/
FritzManagedModel managedModel = new FritzManagedModel(activity.getString(R.string.tflite_model_id));
FritzModelManager modelManager = new FritzModelManager(managedModel);
modelManager.loadModel(new ModelReadyListener() {
@Override
public void onModelReady(FritzOnDeviceModel onDeviceModel) {
tflite = new FritzTFLiteInterpreter(onDeviceModel);
Log.d(TAG, "Interpreter is now ready to use");
}
});
imgData =
ByteBuffer.allocateDirect(
BYTE_SIZE_OF_FLOAT * DIM_BATCH_SIZE * DIM_IMG_SIZE_X * DIM_IMG_SIZE_Y * DIM_PIXEL_SIZE);
imgData.order(ByteOrder.nativeOrder());
mnistOutput = new float[DIM_BATCH_SIZE][NUMBER_LENGTH];
Log.d(TAG, "Created a Tensorflow Lite MNIST Classifier.");
}
/**
* Run the TFLite model
*/
protected void runInference() {
long startTime = SystemClock.uptimeMillis();
tflite.run(imgData, mnistOutput);
long endTime = SystemClock.uptimeMillis();
Log.d(TAG, "Timecost to run model inference: " + Long.toString(endTime - startTime));
}
/**
* Classifies the number with the mnist model.
*
* @param bitmap
* @return the identified number
*/
public int classify(Bitmap bitmap) {
if (tflite == null) {
Log.e(TAG, "Image classifier has not been initialized; Skipped.");
return -1;
}
convertBitmapToByteBuffer(bitmap);
runInference();
return getResult();
}
/**
* Go through the output and find the number that was identified.
*
* @return the number that was identified (returns -1 if one wasn't found)
*/
private int getResult() {
for (int i = 0; i < mnistOutput[0].length; i++) {
float value = mnistOutput[0][i];
Log.d(TAG, "Output for " + Integer.toString(i) + ": " + Float.toString(value));
if (value == 1f) {
return i;
}
}
return -1;
}
/**
* Converts it into the Byte Buffer to feed into the model
*
* @param bitmap
*/
private void convertBitmapToByteBuffer(Bitmap bitmap) {
if (bitmap == null || imgData == null) {
return;
}
// Reset the image data
imgData.rewind();
int width = bitmap.getWidth();
int height = bitmap.getHeight();
long startTime = SystemClock.uptimeMillis();
// The bitmap shape should be 28 x 28
int[] pixels = new int[width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
for (int i = 0; i < pixels.length; ++i) {
// Set 0 for white and 255 for black pixels
int pixel = pixels[i];
int b = pixel & 0xff;
imgData.putFloat(0xff - b);
}
long endTime = SystemClock.uptimeMillis();
Log.d(TAG, "Time cost to put values into ByteBuffer: " + Long.toString(endTime - startTime));
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/activities/vision/ImageLabelingActivity.java
================================================
package ai.fritz.aistudio.activities.vision;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.util.Size;
import java.util.concurrent.atomic.AtomicBoolean;
import ai.fritz.aistudio.R;
import ai.fritz.aistudio.activities.BaseCameraActivity;
import ai.fritz.aistudio.ui.ResultsView;
import ai.fritz.vision.FritzVision;
import ai.fritz.vision.FritzVisionImage;
import ai.fritz.vision.FritzVisionModels;
import ai.fritz.vision.FritzVisionOrientation;
import ai.fritz.vision.ImageOrientation;
import ai.fritz.vision.imagelabeling.FritzVisionLabelPredictor;
import ai.fritz.vision.imagelabeling.FritzVisionLabelResult;
import ai.fritz.vision.imagelabeling.LabelingOnDeviceModel;
import butterknife.BindView;
import butterknife.ButterKnife;
public class ImageLabelingActivity extends BaseCameraActivity implements ImageReader.OnImageAvailableListener {
private static final String TAG = ImageLabelingActivity.class.getSimpleName();
/**
* Requests for the size of the preview depending on the camera results. We will try to match the closest
* in terms of size and aspect ratio.
*/
private static final Size DESIRED_PREVIEW_SIZE = new Size(640, 480);
private AtomicBoolean computing = new AtomicBoolean(false);
private FritzVisionLabelPredictor predictor;
private FritzVisionLabelResult labelResult;
private ImageOrientation orientation;
@BindView(R.id.app_toolbar)
Toolbar appBar;
ResultsView resultsView;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ButterKnife.bind(this);
setTitle(R.string.fritz_vision_title);
}
@Override
protected int getLayoutId() {
return R.layout.camera_connection_fragment;
}
@Override
protected Size getDesiredPreviewFrameSize() {
return DESIRED_PREVIEW_SIZE;
}
@Override
public void onPreviewSizeChosen(final Size size, final Size cameraSize, final int rotation) {
orientation = FritzVisionOrientation.getImageOrientationFromCamera(this, cameraId);
LabelingOnDeviceModel onDeviceModel = FritzVisionModels.getImageLabelingOnDeviceModel();
predictor = FritzVision.ImageLabeling.getPredictor(onDeviceModel);
}
@Override
public void onImageAvailable(final ImageReader reader) {
Image image = reader.acquireLatestImage();
if (image == null) {
return;
}
if (!computing.compareAndSet(false, true)) {
image.close();
return;
}
final FritzVisionImage fritzImage = FritzVisionImage.fromMediaImage(image, orientation);
image.close();
runInBackground(
new Runnable() {
@Override
public void run() {
final long startTime = SystemClock.uptimeMillis();
labelResult = predictor.predict(fritzImage);
labelResult.logResult();
if (resultsView == null) {
resultsView = findViewById(R.id.results);
}
resultsView.setResult(labelResult.getVisionLabels());
Log.d(TAG, "INFERENCE TIME:" + (SystemClock.uptimeMillis() - startTime));
requestRender();
computing.set(false);
}
});
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/activities/vision/ImageSegmentationActivity.java
================================================
package ai.fritz.aistudio.activities.vision;
import android.graphics.Bitmap;
import android.media.ImageReader.OnImageAvailableListener;
import android.os.Bundle;
import android.util.Log;
import android.util.Size;
import ai.fritz.aistudio.R;
import ai.fritz.aistudio.activities.BaseRecordingActivity;
import ai.fritz.core.FritzOnDeviceModel;
import ai.fritz.core.utils.FritzModelManager;
import ai.fritz.vision.FritzVision;
import ai.fritz.vision.FritzVisionImage;
import ai.fritz.vision.FritzVisionModels;
import ai.fritz.vision.ModelVariant;
import ai.fritz.vision.PredictorStatusListener;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationPredictor;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationPredictorOptions;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationResult;
import ai.fritz.vision.imagesegmentation.SegmentationManagedModel;
import ai.fritz.vision.imagesegmentation.SegmentationOnDeviceModel;
public class ImageSegmentationActivity extends BaseRecordingActivity implements OnImageAvailableListener {
private static final String TAG = ImageSegmentationActivity.class.getSimpleName();
private FritzVisionSegmentationPredictor predictor;
private FritzVisionSegmentationPredictorOptions options;
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
options = new FritzVisionSegmentationPredictorOptions();
}
@Override
protected int getModelOptionsTextId() {
return R.array.img_seg_model_options;
}
@Override
protected Bitmap runPrediction(FritzVisionImage visionImage, Size cameraViewSize) {
FritzVisionSegmentationResult segmentResult = predictor.predict(visionImage);
Bitmap bitmap = segmentResult.buildMultiClassMask();
return visionImage.overlay(bitmap);
}
@Override
protected void loadPredictor(int choice) {
SegmentationManagedModel managedModel = getManagedModel(choice);
FritzOnDeviceModel activeOnDeviceModel = FritzModelManager.getActiveOnDeviceModel(managedModel.getModelId());
if (activeOnDeviceModel != null) {
showPredictorReadyViews();
SegmentationOnDeviceModel onDeviceModel = new SegmentationOnDeviceModel(activeOnDeviceModel, managedModel);
predictor = FritzVision.ImageSegmentation.getPredictor(onDeviceModel, options);
} else {
showPredictorNotReadyViews();
FritzVision.ImageSegmentation.loadPredictor(managedModel, new PredictorStatusListener() {
@Override
public void onPredictorReady(FritzVisionSegmentationPredictor segmentPredictor) {
Log.d(TAG, "Segmentation predictor is ready");
predictor = segmentPredictor;
showPredictorReadyViews();
}
});
}
}
private SegmentationManagedModel getManagedModel(int choice) {
switch (choice) {
case (1):
return FritzVisionModels.getLivingRoomSegmentationManagedModel(ModelVariant.FAST);
case (2):
return FritzVisionModels.getOutdoorSegmentationManagedModel(ModelVariant.FAST);
default:
return FritzVisionModels.getPeopleSegmentationManagedModel(ModelVariant.FAST);
}
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/activities/vision/ObjectDetectionActivity.java
================================================
package ai.fritz.aistudio.activities.vision;
import android.graphics.Canvas;
import android.util.Size;
import java.util.List;
import ai.fritz.aistudio.activities.BaseLiveVideoActivity;
import ai.fritz.vision.FritzVision;
import ai.fritz.vision.FritzVisionImage;
import ai.fritz.vision.FritzVisionModels;
import ai.fritz.vision.FritzVisionObject;
import ai.fritz.vision.objectdetection.FritzVisionObjectPredictor;
import ai.fritz.vision.objectdetection.FritzVisionObjectResult;
import ai.fritz.vision.objectdetection.ObjectDetectionOnDeviceModel;
public class ObjectDetectionActivity extends BaseLiveVideoActivity {
private FritzVisionObjectPredictor objectPredictor;
private FritzVisionObjectResult objectResult;
@Override
protected void onCameraSetup(final Size cameraSize) {
ObjectDetectionOnDeviceModel onDeviceModel = FritzVisionModels.getObjectDetectionOnDeviceModel();
objectPredictor = FritzVision.ObjectDetection.getPredictor(onDeviceModel);
}
@Override
protected void handleDrawingResult(Canvas canvas, Size cameraSize) {
if (objectResult != null) {
List visionObjects = objectResult.getObjects();
for (FritzVisionObject object : visionObjects) {
object.draw(canvas);
}
}
}
@Override
protected void runInference(FritzVisionImage fritzVisionImage) {
objectResult = objectPredictor.predict(fritzVisionImage);
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/activities/vision/PoseEstimationActivity.java
================================================
package ai.fritz.aistudio.activities.vision;
import android.graphics.Canvas;
import android.util.Size;
import java.util.List;
import ai.fritz.aistudio.activities.BaseLiveVideoActivity;
import ai.fritz.vision.FritzVision;
import ai.fritz.vision.FritzVisionImage;
import ai.fritz.vision.FritzVisionModels;
import ai.fritz.vision.ModelVariant;
import ai.fritz.vision.filter.OneEuroFilterMethod;
import ai.fritz.vision.poseestimation.FritzVisionPosePredictor;
import ai.fritz.vision.poseestimation.FritzVisionPosePredictorOptions;
import ai.fritz.vision.poseestimation.FritzVisionPoseResult;
import ai.fritz.vision.poseestimation.Pose;
import ai.fritz.vision.poseestimation.PoseOnDeviceModel;
public class PoseEstimationActivity extends BaseLiveVideoActivity {
private FritzVisionPosePredictor posePredictor;
private FritzVisionPoseResult poseResult;
private FritzVisionPosePredictorOptions options;
@Override
protected void onCameraSetup(final Size cameraSize) {
PoseOnDeviceModel onDeviceModel = FritzVisionModels.getHumanPoseEstimationOnDeviceModel(ModelVariant.FAST);
options = new FritzVisionPosePredictorOptions();
options.smoothingOptions = new OneEuroFilterMethod();
posePredictor = FritzVision.PoseEstimation.getPredictor(onDeviceModel);
}
@Override
protected void handleDrawingResult(Canvas canvas, Size cameraSize) {
if (poseResult != null) {
List poseList = poseResult.getPoses();
for (Pose pose : poseList) {
pose.draw(canvas);
}
}
}
@Override
protected void runInference(FritzVisionImage fritzVisionImage) {
poseResult = posePredictor.predict(fritzVisionImage);
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/activities/vision/StyleTransferActivity.java
================================================
package ai.fritz.aistudio.activities.vision;
import android.graphics.Bitmap;
import android.media.ImageReader.OnImageAvailableListener;
import android.os.Bundle;
import android.util.Size;
import ai.fritz.core.FritzOnDeviceModel;
import ai.fritz.aistudio.R;
import ai.fritz.aistudio.activities.BaseRecordingActivity;
import ai.fritz.vision.FritzVision;
import ai.fritz.vision.FritzVisionImage;
import ai.fritz.vision.FritzVisionModels;
import ai.fritz.vision.styletransfer.FritzVisionStylePredictor;
import ai.fritz.vision.styletransfer.FritzVisionStylePredictorOptions;
import ai.fritz.vision.styletransfer.FritzVisionStyleResult;
public class StyleTransferActivity extends BaseRecordingActivity implements OnImageAvailableListener {
private FritzVisionStylePredictor predictor;
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected int getModelOptionsTextId() {
return R.array.style_transfer_options;
}
@Override
protected Bitmap runPrediction(FritzVisionImage visionImage, Size cameraViewSize) {
FritzVisionStyleResult styleResult = predictor.predict(visionImage);
return styleResult.toBitmap();
}
@Override
protected void loadPredictor(int choice) {
FritzOnDeviceModel onDeviceModel = getModel(choice);
FritzVisionStylePredictorOptions options = new FritzVisionStylePredictorOptions();
predictor = FritzVision.StyleTransfer.getPredictor(onDeviceModel, options);
}
private FritzOnDeviceModel getModel(int choice) {
FritzOnDeviceModel[] styles = FritzVisionModels.getPaintingStyleModels().getAll();
return styles[choice];
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/adapters/DemoAdapter.java
================================================
package ai.fritz.aistudio.adapters;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import ai.fritz.aistudio.R;
/**
* Demo items adapter to manage the list of models.
*/
public class DemoAdapter extends RecyclerView.Adapter {
private List demoItems = new ArrayList<>();
/**
* View holder for each demo item to display in the list.
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
public LinearLayout linearLayout;
public TextView titleView;
public TextView descriptionView;
public ViewHolder(LinearLayout v) {
super(v);
linearLayout = v;
titleView = linearLayout.findViewById(R.id.title);
descriptionView = linearLayout.findViewById(R.id.description);
}
}
/**
* Initialize the adapter with a list of demo items.
*
* @param demoItems
*/
public DemoAdapter(List demoItems) {
this.demoItems = demoItems;
}
/**
* Get the DemoItem and binds it to the recycled view.
*
* Also sets the click actions here.
*
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(DemoAdapter.ViewHolder holder, int position) {
DemoItem demoItem = demoItems.get(position);
holder.titleView.setText(demoItem.getTitle());
holder.descriptionView.setText(demoItem.getDescription());
holder.linearLayout.setOnClickListener(demoItem.getOnClickListener());
}
/**
* Get number of demo items.
*
* @return number of items in the list
*/
@Override
public int getItemCount() {
return demoItems.size();
}
/**
* Create a view holder for the DemoItems
*
* @param parent
* @param viewType
* @return
*/
@Override
public DemoAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
LinearLayout v = (LinearLayout) LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item_demo, parent, false);
ViewHolder holder = new ViewHolder(v);
return holder;
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/adapters/DemoItem.java
================================================
package ai.fritz.aistudio.adapters;
import android.view.View;
/**
* Helper class to show demo items for the DemoAdapter
*/
public class DemoItem {
private String title;
private String description;
private View.OnClickListener onClickListener;
public DemoItem(String title, String description, View.OnClickListener clickListener) {
this.title = title;
this.description = description;
this.onClickListener = clickListener;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public View.OnClickListener getOnClickListener() {
return onClickListener;
}
public void setOnClickListener(View.OnClickListener onClickListener) {
this.onClickListener = onClickListener;
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/fragments/CameraConnectionFragment.java
================================================
package ai.fritz.aistudio.fragments;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.ImageReader;
import android.media.ImageReader.OnImageAvailableListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.text.TextUtils;
import android.util.Log;
import android.util.Size;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import ai.fritz.aistudio.R;
import ai.fritz.aistudio.ui.AutoFitTextureView;
public class CameraConnectionFragment extends Fragment {
private static final String TAG = CameraConnectionFragment.class.getSimpleName();
public CameraConnectionFragment() {
}
/**
* The camera preview size will be chosen to be the smallest frame by pixel size capable of
* containing a DESIRED_SIZE x DESIRED_SIZE square.
*/
private static final int MINIMUM_PREVIEW_SIZE = 320;
/**
* Conversion from screen rotation to JPEG orientation.
*/
private static final String FRAGMENT_DIALOG = "dialog";
/**
* {@link android.view.TextureView.SurfaceTextureListener} handles several lifecycle events on a
* {@link TextureView}.
*/
private final TextureView.SurfaceTextureListener surfaceTextureListener =
new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(
final SurfaceTexture texture, final int width, final int height) {
openCamera(width, height);
}
@Override
public void onSurfaceTextureSizeChanged(
final SurfaceTexture texture, final int width, final int height) {
configureTransform(width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(final SurfaceTexture texture) {
return true;
}
@Override
public void onSurfaceTextureUpdated(final SurfaceTexture texture) {
}
};
/**
* Callback for Activities to use to initialize their data once the
* selected preview size is known.
*/
public interface ConnectionCallback {
void onPreviewSizeChosen(Size size, Size cameraViewSize, int cameraRotation);
}
/**
* ID of the current {@link CameraDevice}.
*/
private String cameraId;
/**
* An {@link AutoFitTextureView} for camera preview.
*/
private AutoFitTextureView textureView;
/**
* A {@link CameraCaptureSession } for camera preview.
*/
private CameraCaptureSession captureSession;
/**
* A reference to the opened {@link CameraDevice}.
*/
private CameraDevice cameraDevice;
/**
* The rotation in degrees of the camera sensor from the display.
*/
private Integer sensorOrientation;
/**
* The {@link android.util.Size} of camera preview.
*/
private Size previewSize;
/**
* {@link android.hardware.camera2.CameraDevice.StateCallback}
* is called when {@link CameraDevice} changes its state.
*/
private final CameraDevice.StateCallback stateCallback =
new CameraDevice.StateCallback() {
@Override
public void onOpened(final CameraDevice cd) {
// This method is called when the camera is opened. We start camera preview here.
cameraOpenCloseLock.release();
cameraDevice = cd;
createCameraPreviewSession();
}
@Override
public void onDisconnected(final CameraDevice cd) {
cameraOpenCloseLock.release();
cd.close();
cameraDevice = null;
}
@Override
public void onError(final CameraDevice cd, final int error) {
cameraOpenCloseLock.release();
cd.close();
cameraDevice = null;
final Activity activity = getActivity();
if (null != activity) {
activity.finish();
}
}
};
/**
* An additional thread for running tasks that shouldn't block the UI.
*/
private HandlerThread backgroundThread;
/**
* A {@link Handler} for running tasks in the background.
*/
private Handler backgroundHandler;
/**
* An {@link ImageReader} that handles preview frame capture.
*/
private ImageReader previewReader;
/**
* {@link android.hardware.camera2.CaptureRequest.Builder} for the camera preview
*/
private CaptureRequest.Builder previewRequestBuilder;
/**
* {@link CaptureRequest} generated by {@link #previewRequestBuilder}
*/
private CaptureRequest previewRequest;
/**
* A {@link Semaphore} to prevent the app from exiting before closing the camera.
*/
private final Semaphore cameraOpenCloseLock = new Semaphore(1);
/**
* A {@link OnImageAvailableListener} to receive frames as they are available.
*/
private OnImageAvailableListener imageListener = null;
/**
* The input size in pixels desired by TensorFlow (width and height of a square bitmap).
*/
private Size inputSize = null;
/**
* The layout identifier to inflate for this Fragment.
*/
private int layout = -1;
private ConnectionCallback cameraConnectionCallback = null;
private CameraConnectionFragment(
final ConnectionCallback connectionCallback,
final OnImageAvailableListener imageListener,
final int layout,
final Size inputSize) {
this.cameraConnectionCallback = connectionCallback;
this.imageListener = imageListener;
this.layout = layout;
this.inputSize = inputSize;
}
/**
* Shows a {@link Toast} on the UI thread.
*
* @param text The message to show
*/
private void showToast(final String text) {
final Activity activity = getActivity();
if (activity != null) {
activity.runOnUiThread(
new Runnable() {
@Override
public void run() {
Toast.makeText(activity, text, Toast.LENGTH_SHORT).show();
}
});
}
}
/**
* Given {@code choices} of {@code Size}s supported by a camera, chooses the smallest one whose
* width and height are at least as large as the minimum of both, or an exact match if possible.
*
* @param choices The list of sizes that the camera supports for the intended output class
* @param width The minimum desired width
* @param height The minimum desired height
* @return The optimal {@code Size}, or an arbitrary one if none were big enough
*/
protected static Size chooseOptimalSize(final Size[] choices, final int width, final int height) {
final int minSize = Math.max(Math.min(width, height), MINIMUM_PREVIEW_SIZE);
final Size desiredSize = new Size(width, height);
// Collect the supported resolutions that are at least as big as the preview Surface
boolean exactSizeFound = false;
final List bigEnough = new ArrayList();
final List tooSmall = new ArrayList();
for (final Size option : choices) {
if (option.equals(desiredSize)) {
// Set the size but don't return yet so that remaining sizes will still be logged.
exactSizeFound = true;
}
if (option.getHeight() >= minSize && option.getWidth() >= minSize) {
bigEnough.add(option);
} else {
tooSmall.add(option);
}
}
Log.d(TAG, "Desired size: " + desiredSize + ", min size: " + minSize + "x" + minSize);
Log.d(TAG, "Valid preview sizes: [" + TextUtils.join(", ", bigEnough) + "]");
Log.d(TAG, "Rejected preview sizes: [" + TextUtils.join(", ", tooSmall) + "]");
if (exactSizeFound) {
Log.d(TAG, "Exact size match found.");
return desiredSize;
}
// Pick the smallest of those, assuming we found any
if (bigEnough.size() > 0) {
final Size chosenSize = Collections.min(bigEnough, new CompareSizesByArea());
Log.d(TAG, "Chosen size: " + chosenSize.getWidth() + "x" + chosenSize.getHeight());
return chosenSize;
} else {
Log.e(TAG, "Couldn't find any suitable preview size");
return choices[0];
}
}
public static CameraConnectionFragment newInstance(
final ConnectionCallback callback,
final OnImageAvailableListener imageListener,
final int layout,
final Size inputSize) {
return new CameraConnectionFragment(callback, imageListener, layout, inputSize);
}
@Override
public View onCreateView(
final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
return inflater.inflate(layout, container, false);
}
@Override
public void onViewCreated(final View view, final Bundle savedInstanceState) {
textureView = (AutoFitTextureView) view.findViewById(R.id.texture);
}
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
startBackgroundThread();
// When the screen is turned off and turned back on, the SurfaceTexture is already
// available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open
// a camera and start preview from here (otherwise, we wait until the surface is ready in
// the SurfaceTextureListener).
if (textureView.isAvailable()) {
openCamera(textureView.getWidth(), textureView.getHeight());
} else {
textureView.setSurfaceTextureListener(surfaceTextureListener);
}
}
@Override
public void onPause() {
closeCamera();
stopBackgroundThread();
super.onPause();
}
public void setCamera(String cameraId) {
this.cameraId = cameraId;
}
/**
* Sets up member variables related to camera.
*/
private void setUpCameraOutputs() {
final Activity activity = getActivity();
final CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
try {
final CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
final StreamConfigurationMap map =
characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
// For still image captures, we use the largest available size.
final Size largest =
Collections.max(
Arrays.asList(map.getOutputSizes(ImageFormat.YUV_420_888)),
new CompareSizesByArea());
sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
// Danger, W.R.! Attempting to use too large a preview size could exceed the camera
// bus' bandwidth limitation, resulting in gorgeous previews but the storage of
// garbage capture data.
previewSize =
chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
inputSize.getWidth(),
inputSize.getHeight());
} catch (final CameraAccessException e) {
Log.e(TAG, e.toString());
} catch (final NullPointerException e) {
// Currently an NPE is thrown when the Camera2API is used but not supported on the
// device this code runs.
// TODO(andrewharp): abstract ErrorDialog/RuntimeException handling out into new method and
// reuse throughout app.
ErrorDialog.newInstance(getString(R.string.camera_error))
.show(getChildFragmentManager(), FRAGMENT_DIALOG);
throw new RuntimeException(getString(R.string.camera_error));
}
Size textureViewSize = new Size(textureView.getWidth(), textureView.getHeight());
cameraConnectionCallback.onPreviewSizeChosen(previewSize, textureViewSize, sensorOrientation);
}
/**
* Opens the camera specified by {@link CameraConnectionFragment#cameraId}.
*/
private void openCamera(final int width, final int height) {
setUpCameraOutputs();
configureTransform(width, height);
final Activity activity = getActivity();
final CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
try {
if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Time out waiting to lock camera opening.");
}
manager.openCamera(cameraId, stateCallback, backgroundHandler);
} catch (final CameraAccessException | SecurityException e) {
Log.e(TAG, e.toString());
} catch (final InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
}
}
/**
* Closes the current {@link CameraDevice}.
*/
private void closeCamera() {
try {
cameraOpenCloseLock.acquire();
if (null != captureSession) {
captureSession.close();
captureSession = null;
}
if (null != cameraDevice) {
cameraDevice.close();
cameraDevice = null;
}
if (null != previewReader) {
previewReader.close();
previewReader = null;
}
} catch (final InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
} finally {
cameraOpenCloseLock.release();
}
}
/**
* Starts a background thread and its {@link Handler}.
*/
private void startBackgroundThread() {
backgroundThread = new HandlerThread("ImageListener");
backgroundThread.start();
backgroundHandler = new Handler(backgroundThread.getLooper());
}
/**
* Stops the background thread and its {@link Handler}.
*/
private void stopBackgroundThread() {
backgroundThread.quitSafely();
try {
backgroundThread.join();
backgroundThread = null;
backgroundHandler = null;
} catch (final InterruptedException e) {
Log.e(TAG, e.toString());
}
}
private final CameraCaptureSession.CaptureCallback captureCallback =
new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureProgressed(
final CameraCaptureSession session,
final CaptureRequest request,
final CaptureResult partialResult) {
}
@Override
public void onCaptureCompleted(
final CameraCaptureSession session,
final CaptureRequest request,
final TotalCaptureResult result) {
}
};
/**
* Creates a new {@link CameraCaptureSession} for camera preview.
*/
private void createCameraPreviewSession() {
try {
final SurfaceTexture texture = textureView.getSurfaceTexture();
assert texture != null;
// We configure the size of default buffer to be the size of camera preview we want.
texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
// This is the output Surface we need to start preview.
final Surface surface = new Surface(texture);
// We set up a CaptureRequest.Builder with the output Surface.
previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewRequestBuilder.addTarget(surface);
Log.d(TAG, "Opening camera preview: " + previewSize.getWidth() + "x" + previewSize.getHeight());
// Create the reader for the preview frames.
previewReader =
ImageReader.newInstance(
previewSize.getWidth(), previewSize.getHeight(), ImageFormat.YUV_420_888, 2);
previewReader.setOnImageAvailableListener(imageListener, backgroundHandler);
previewRequestBuilder.addTarget(previewReader.getSurface());
// Here, we create a CameraCaptureSession for camera preview.
cameraDevice.createCaptureSession(
Arrays.asList(surface, previewReader.getSurface()),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(final CameraCaptureSession cameraCaptureSession) {
// The camera is already closed
if (null == cameraDevice) {
return;
}
// When the session is ready, we start displaying the preview.
captureSession = cameraCaptureSession;
try {
// Auto focus should be continuous for camera preview.
previewRequestBuilder.set(
CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// Flash is automatically enabled when necessary.
previewRequestBuilder.set(
CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
// Finally, we start displaying the camera preview.
previewRequest = previewRequestBuilder.build();
captureSession.setRepeatingRequest(
previewRequest, captureCallback, backgroundHandler);
} catch (final CameraAccessException e) {
Log.e(TAG, e.toString());
}
}
@Override
public void onConfigureFailed(final CameraCaptureSession cameraCaptureSession) {
showToast("Failed");
}
},
null);
} catch (final CameraAccessException e) {
Log.e(TAG, e.toString());
}
}
/**
* Configures the necessary {@link android.graphics.Matrix} transformation to `mTextureView`.
* This method should be called after the camera preview size is determined in
* setUpCameraOutputs and also the size of `mTextureView` is fixed.
*
* @param viewWidth The width of `mTextureView`
* @param viewHeight The height of `mTextureView`
*/
private void configureTransform(final int viewWidth, final int viewHeight) {
final Activity activity = getActivity();
if (null == textureView || null == previewSize || null == activity) {
return;
}
final int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
final Matrix matrix = new Matrix();
final RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
final RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth());
final float centerX = viewRect.centerX();
final float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
final float scale =
Math.max(
(float) viewHeight / previewSize.getHeight(),
(float) viewWidth / previewSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
} else if (Surface.ROTATION_180 == rotation) {
matrix.postRotate(180, centerX, centerY);
}
textureView.setTransform(matrix);
}
/**
* Compares two {@code Size}s based on their areas.
*/
static class CompareSizesByArea implements Comparator {
@Override
public int compare(final Size lhs, final Size rhs) {
// We cast here to ensure the multiplications won't overflow
return Long.signum(
(long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight());
}
}
/**
* Shows an error message dialog.
*/
public static class ErrorDialog extends DialogFragment {
private static final String ARG_MESSAGE = "message";
public static ErrorDialog newInstance(final String message) {
final ErrorDialog dialog = new ErrorDialog();
final Bundle args = new Bundle();
args.putString(ARG_MESSAGE, message);
dialog.setArguments(args);
return dialog;
}
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
final Activity activity = getActivity();
return new AlertDialog.Builder(activity)
.setMessage(getArguments().getString(ARG_MESSAGE))
.setPositiveButton(
android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialogInterface, final int i) {
activity.finish();
}
})
.create();
}
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/ui/AutoFitTextureView.java
================================================
/*
* Copyright 2016 The TensorFlow Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ai.fritz.aistudio.ui;
import android.content.Context;
import android.util.AttributeSet;
import android.view.TextureView;
/**
* A {@link TextureView} that can be adjusted to a specified aspect ratio.
*/
public class AutoFitTextureView extends TextureView {
private int ratioWidth = 0;
private int ratioHeight = 0;
public AutoFitTextureView(final Context context) {
this(context, null);
}
public AutoFitTextureView(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public AutoFitTextureView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
}
/**
* Sets the aspect ratio for this view. The size of the view will be measured based on the ratio
* calculated from the parameters. Note that the actual sizes of parameters don't matter, that
* is, calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result.
*
* @param width Relative horizontal size
* @param height Relative vertical size
*/
public void setAspectRatio(final int width, final int height) {
if (width < 0 || height < 0) {
throw new IllegalArgumentException("Size cannot be negative.");
}
ratioWidth = width;
ratioHeight = height;
requestLayout();
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/ui/ChooseModelDialog.java
================================================
package ai.fritz.aistudio.ui;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import ai.fritz.aistudio.R;
public class ChooseModelDialog extends DialogFragment {
public static final String TAG = ChooseModelDialog.class.getSimpleName();
private int choice = 0;
private int itemsResourceId;
private DialogInterface.OnClickListener listener;
public ChooseModelDialog(int itemsResourceId, DialogInterface.OnClickListener listener) {
this.itemsResourceId = itemsResourceId;
this.listener = listener;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.CustomDialog);
builder.setTitle(R.string.available_models)
.setItems(itemsResourceId, listener);
// Create the AlertDialog object and return it
return builder.create();
}
public int getChoice() {
return choice;
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/ui/DrawModel.java
================================================
package ai.fritz.aistudio.ui;
/*
Copyright 2016 Narrative Nights Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import java.util.ArrayList;
import java.util.List;
public class DrawModel {
public static class LineElem {
public float x;
public float y;
private LineElem(float x, float y) {
this.x = x;
this.y = y;
}
}
public static class Line {
private List elems = new ArrayList<>();
private Line() {
}
private void addElem(LineElem elem) {
elems.add(elem);
}
public int getElemSize() {
return elems.size();
}
public LineElem getElem(int index) {
return elems.get(index);
}
}
private Line mCurrentLine;
private int mWidth; // pixel width = 28
private int mHeight; // pixel height = 28
private List mLines = new ArrayList<>();
public DrawModel(int width, int height) {
this.mWidth = width;
this.mHeight = height;
}
public int getWidth() {
return mWidth;
}
public int getHeight() {
return mHeight;
}
public void startLine(float x, float y) {
mCurrentLine = new Line();
mCurrentLine.addElem(new LineElem(x, y));
mLines.add(mCurrentLine);
}
public void endLine() {
mCurrentLine = null;
}
public void addLineElem(float x, float y) {
if (mCurrentLine != null) {
mCurrentLine.addElem(new LineElem(x, y));
}
}
public int getLineSize() {
return mLines.size();
}
public Line getLine(int index) {
return mLines.get(index);
}
public void clear() {
mLines.clear();
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/ui/DrawRenderer.java
================================================
package ai.fritz.aistudio.ui;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
public class DrawRenderer {
/**
* Draw lines to canvas
*/
public static void renderModel(Canvas canvas, DrawModel model, Paint paint,
int startLineIndex) {
paint.setAntiAlias(true);
int lineSize = model.getLineSize();
for (int i = startLineIndex; i < lineSize; ++i) {
DrawModel.Line line = model.getLine(i);
paint.setColor(Color.BLACK);
int elemSize = line.getElemSize();
if (elemSize < 1) {
continue;
}
DrawModel.LineElem elem = line.getElem(0);
float lastX = elem.x;
float lastY = elem.y;
for (int j = 0; j < elemSize; ++j) {
elem = line.getElem(j);
float x = elem.x;
float y = elem.y;
canvas.drawLine(lastX, lastY, x, y, paint);
lastX = x;
lastY = y;
}
}
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/ui/DrawView.java
================================================
package ai.fritz.aistudio.ui;
/*
Copyright 2016 Narrative Nights Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
public class DrawView extends View {
private Paint mPaint = new Paint();
private DrawModel mModel;
// 28x28 pixel Bitmap
private Bitmap mOffscreenBitmap;
private Canvas mOffscreenCanvas;
private Matrix mMatrix = new Matrix();
private Matrix mInvMatrix = new Matrix();
private int mDrawnLineSize = 0;
private boolean mSetuped = false;
private float mTmpPoints[] = new float[2];
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setModel(DrawModel model) {
this.mModel = model;
}
public void reset() {
mDrawnLineSize = 0;
if (mOffscreenBitmap != null) {
mPaint.setColor(Color.WHITE);
int width = mOffscreenBitmap.getWidth();
int height = mOffscreenBitmap.getHeight();
mOffscreenCanvas.drawRect(new Rect(0, 0, width, height), mPaint);
}
}
private void setup() {
mSetuped = true;
// View size
float width = getWidth();
float height = getHeight();
// Model (bitmap) size
float modelWidth = mModel.getWidth();
float modelHeight = mModel.getHeight();
float scaleW = width / modelWidth;
float scaleH = height / modelHeight;
float scale = scaleW;
if (scale > scaleH) {
scale = scaleH;
}
float newCx = modelWidth * scale / 2;
float newCy = modelHeight * scale / 2;
float dx = width / 2 - newCx;
float dy = height / 2 - newCy;
mMatrix.setScale(scale, scale);
mMatrix.postTranslate(dx, dy);
mMatrix.invert(mInvMatrix);
mSetuped = true;
}
@Override
public void onDraw(Canvas canvas) {
if (mModel == null) {
return;
}
if (!mSetuped) {
setup();
}
if (mOffscreenBitmap == null) {
return;
}
int startIndex = mDrawnLineSize - 1;
if (startIndex < 0) {
startIndex = 0;
}
DrawRenderer.renderModel(mOffscreenCanvas, mModel, mPaint, startIndex);
canvas.drawBitmap(mOffscreenBitmap, mMatrix, mPaint);
mDrawnLineSize = mModel.getLineSize();
}
/**
* Convert screen position to local pos (pos in bitmap)
*/
public void calcPos(float x, float y, PointF out) {
mTmpPoints[0] = x;
mTmpPoints[1] = y;
mInvMatrix.mapPoints(mTmpPoints);
out.x = mTmpPoints[0];
out.y = mTmpPoints[1];
}
public void onResume() {
createBitmap();
}
public void onPause() {
releaseBitmap();
}
private void createBitmap() {
if (mOffscreenBitmap != null) {
mOffscreenBitmap.recycle();
}
mOffscreenBitmap = Bitmap.createBitmap(mModel.getWidth(), mModel.getHeight(), Bitmap.Config.ARGB_8888);
mOffscreenCanvas = new Canvas(mOffscreenBitmap);
reset();
}
private void releaseBitmap() {
if (mOffscreenBitmap != null) {
mOffscreenBitmap.recycle();
mOffscreenBitmap = null;
mOffscreenCanvas = null;
}
reset();
}
public Bitmap getDrawnBitmap() {
return mOffscreenBitmap;
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/ui/OverlayView.java
================================================
package ai.fritz.aistudio.ui;
/* Copyright 2016 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
/**
* A simple View providing a render callback to other classes.
*/
public class OverlayView extends View {
private DrawCallback callback;
public OverlayView(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
/**
* Interface defining the callback for client classes.
*/
public interface DrawCallback {
public void drawCallback(final Canvas canvas);
}
public void setCallback(final DrawCallback callback) {
this.callback = callback;
}
@Override
public synchronized void draw(final Canvas canvas) {
super.draw(canvas);
if(callback != null) {
callback.drawCallback(canvas);
}
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/ui/RecognitionScoreView.java
================================================
package ai.fritz.aistudio.ui;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
import ai.fritz.aistudio.activities.custommodel.ml.Classifier.Recognition;
import ai.fritz.vision.FritzVisionLabel;
public class RecognitionScoreView extends View implements ResultsView {
private static final float TEXT_SIZE_DIP = 24;
private List results = new ArrayList<>();
private List labels = new ArrayList<>();
private final float textSizePx;
private final Paint fgPaint;
private final Paint bgPaint;
public RecognitionScoreView(final Context context, final AttributeSet set) {
super(context, set);
textSizePx =
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, TEXT_SIZE_DIP, getResources().getDisplayMetrics());
fgPaint = new Paint();
fgPaint.setTextSize(textSizePx);
bgPaint = new Paint();
bgPaint.setColor(0xcc4285f4);
}
@Override
public void setResults(final List results) {
this.results = results;
postInvalidate();
}
@Override
public void setResult(final List labels) {
this.labels = labels;
postInvalidate();
}
@Override
public void onDraw(final Canvas canvas) {
final int x = 10;
int y = (int) (fgPaint.getTextSize() * 1.5f);
canvas.drawPaint(bgPaint);
if (results.size() > 0) {
for (final Recognition recog : results) {
canvas.drawText(recog.getTitle() + ": " + recog.getConfidence(), x, y, fgPaint);
y += fgPaint.getTextSize() * 1.5f;
}
}
if (labels.size() > 0) {
for (final FritzVisionLabel label : labels) {
canvas.drawText(label.getText() + ": " + label.getConfidence(), x, y, fgPaint);
y += fgPaint.getTextSize() * 1.5f;
}
}
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/ui/ResultsView.java
================================================
package ai.fritz.aistudio.ui;
import java.util.List;
import ai.fritz.aistudio.activities.custommodel.ml.Classifier.Recognition;
import ai.fritz.vision.FritzVisionLabel;
public interface ResultsView {
void setResults(final List results);
void setResult(final List labels);
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/ui/SeparatorDecoration.java
================================================
package ai.fritz.aistudio.ui;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.ColorInt;
import android.support.annotation.FloatRange;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.util.TypedValue;
import android.view.View;
/**
* Taken from https://github.com/bleeding182/recyclerviewItemDecorations
*/
public class SeparatorDecoration extends RecyclerView.ItemDecoration {
private final Paint mPaint;
/**
* Create a decoration that draws a line in the given color and width between the items in the view.
*
* @param context a context to access the resources.
* @param color the color of the separator to draw.
* @param heightDp the height of the separator in dp.
*/
public SeparatorDecoration(@NonNull Context context, @ColorInt int color,
@FloatRange(from = 0, fromInclusive = false) float heightDp) {
mPaint = new Paint();
mPaint.setColor(color);
final float thickness = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
heightDp, context.getResources().getDisplayMetrics());
mPaint.setStrokeWidth(thickness);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
// we want to retrieve the position in the list
final int position = params.getViewAdapterPosition();
// and add a separator to any view but the last one
if (position < state.getItemCount()) {
outRect.set(0, 0, 0, (int) mPaint.getStrokeWidth()); // left, top, right, bottom
} else {
outRect.setEmpty(); // 0, 0, 0, 0
}
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
// we set the stroke width before, so as to correctly draw the line we have to offset by width / 2
final int offset = (int) (mPaint.getStrokeWidth() / 2);
// this will iterate over every visible view
for (int i = 0; i < parent.getChildCount(); i++) {
// get the view
final View view = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
// get the position
final int position = params.getViewAdapterPosition();
// and finally draw the separator
if (position < state.getItemCount()) {
c.drawLine(view.getLeft(), view.getBottom() + offset, view.getRight(), view.getBottom() + offset, mPaint);
}
}
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/java/ai/fritz/aistudio/utils/VideoProcessingQueue.java
================================================
package ai.fritz.aistudio.utils;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import ai.fritz.vision.FritzVisionImage;
public class VideoProcessingQueue {
private static final String TAG = VideoProcessingQueue.class.getSimpleName();
public interface Listener {
void processVisionImage(FritzVisionImage visionImage);
void finishedProcessing();
}
private AtomicBoolean isProcessingQueue = new AtomicBoolean(false);
private LinkedBlockingQueue recordedImages;
private Listener listener;
private Handler handler;
public VideoProcessingQueue(Listener listener) {
recordedImages = new LinkedBlockingQueue<>();
this.listener = listener;
HandlerThread handlerThread = new HandlerThread("Video Processing Thread");
handlerThread.start();
this.handler = new Handler(handlerThread.getLooper());
}
public void addVisionImage(FritzVisionImage visionImage) {
recordedImages.add(visionImage);
if (isProcessingQueue.compareAndSet(false, true)) {
runQueueProcessor();
}
}
public int getNumFramesToProcess() {
return recordedImages.size();
}
private void runQueueProcessor() {
handler.post(new Runnable() {
@Override
public void run() {
while (!recordedImages.isEmpty()) {
FritzVisionImage visionImage = recordedImages.poll();
Log.d(TAG, "Processing Image");
listener.processVisionImage(visionImage);
}
listener.finishedProcessing();
isProcessingQueue.set(false);
}
});
}
}
================================================
FILE: Android/FritzAIStudio/app/src/main/res/drawable/circle.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/drawable/circle_white.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/drawable/ic_close.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/drawable/ic_heartbeat_logo.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/drawable/ic_launcher_background.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/drawable/round_button.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/drawable/splash_bg.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/font/sf_display.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/layout/activity_camera.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/layout/activity_fritz_vision.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/layout/activity_mnist.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/layout/activity_tfmobile_camera.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/layout/app_bar.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/layout/camera_connection_fragment.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/layout/camera_connection_fragment_recording.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/layout/camera_connection_fragment_stylize.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/layout/camera_connection_fragment_tracking.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/layout/camera_connection_snapshot.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/layout/list_item_demo.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/layout/list_text_item.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
================================================
FILE: Android/FritzAIStudio/app/src/main/res/values/colors.xml
================================================
#000#FF4081#fff#aaa#000#666#9F8D36
================================================
FILE: Android/FritzAIStudio/app/src/main/res/values/custom_models.xml
================================================
08c1cea18756489cb244952aa7b218a07bf64aad64b7420f94f9c106a5e96cbd
================================================
FILE: Android/FritzAIStudio/app/src/main/res/values/dimens.xml
================================================
4dp8dp16dp32dp64dp@dimen/margin_md@dimen/margin_md
================================================
FILE: Android/FritzAIStudio/app/src/main/res/values/fritz.xml
================================================
Your API Key
================================================
FILE: Android/FritzAIStudio/app/src/main/res/values/ic_launcher_background.xml
================================================
#000000
================================================
FILE: Android/FritzAIStudio/app/src/main/res/values/strings.xml
================================================
Fritz AI StudioFritz AI StudioStyle TransferRun style transfer on live video feed.Image LabelingLabel a still image.Label video framesCustom TFMobile ModelDemonstrates using a TFMobile model to label images. Note, this is not a prebuilt model provided by the Vision API.Custom TFLite ModelDemonstrates using a custom TFLite model to identify handwritten numbers. Note, this is not a prebuilt model provided by the Vision API.Handwriting DetectionDetects numbers from 0-9. Draw a number on the canvas and guess what it is.Learn More About Fritz AITeach your mobile app to see, hear, sense, and think.Object DetectionDetect objects in an image and label themImage SegmentationTake a picture and identify people in the photo (highlighted in blue).Pose EstimationDetect body position and keypoints.People SegmentationCreate a mask on people in an image.Living Room SegmentationCreate a mask on living room objects.Outdoor SegmentationCreate a mask on outdoor objects in an image.MNISTClassic MNIST handwriting recognizer for digits 0–9PictureInfoThis sample needs camera permission.This device doesn\'t support Camera2 API.NN:OnNN:OffUse NNAPIClearSelect PhotoDetectNot detectedDetected = %1$sAvailable ModelsHair ColoringChange your hair color with Image Segmentation.PeopleLiving RoomOutdoorBicentennial PrintFemmesHead of ClownHorses on SeashoreKaleidoscopePink Blue RhombusPoppy FieldRitmo PlasticoStarry NightThe ScreamThe Trail
================================================
FILE: Android/FritzAIStudio/app/src/main/res/values/styles.xml
================================================
================================================
FILE: Android/FritzAIStudio/build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
mavenCentral()
maven {
url 'https://maven.google.com/'
}
maven { url 'https://fritz.mycloudrepo.io/public/repositories/android' }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: Android/FritzAIStudio/gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: Android/FritzAIStudio/gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: Android/FritzAIStudio/gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: Android/FritzAIStudio/settings.gradle
================================================
include ':app'
================================================
FILE: Android/FritzVisionVideoApp/.gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
================================================
FILE: Android/FritzVisionVideoApp/app/.gitignore
================================================
/build
================================================
FILE: Android/FritzVisionVideoApp/app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
defaultConfig {
// MUST MATCH THE APPLICATION YOU CREATE IN FRITZ
applicationId "ai.fritz.fritzvisionvideo"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
debuggable true
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
aaptOptions {
noCompress "tflite"
}
lintOptions {
abortOnError false
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation "ai.fritz:vision:6.0.3"
implementation "ai.fritz:vision-hair-segmentation-model-fast:3.0.0"
implementation "ai.fritz:vision-people-segmentation-model-fast:3.1.0"
implementation "ai.fritz:vision-object-detection-model-fast:3.0.0"
implementation "ai.fritz:vision-pose-estimation-model-fast:3.0.0"
implementation "ai.fritz:vision-style-painting-models:3.0.0"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
================================================
FILE: Android/FritzVisionVideoApp/app/gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: Android/FritzVisionVideoApp/app/gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: Android/FritzVisionVideoApp/app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: Android/FritzVisionVideoApp/app/src/androidTest/java/ai/fritz/fritzvisionvideo/ExampleInstrumentedTest.java
================================================
package ai.fritz.fritzvisionvideo;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see Testing documentation
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("ai.fritz.fritzvisionvideo", appContext.getPackageName());
}
}
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/AndroidManifest.xml
================================================
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/java/ai/fritz/fritzvisionvideo/MainActivity.java
================================================
package ai.fritz.fritzvisionvideo;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
import ai.fritz.core.Fritz;
import ai.fritz.fritzvisionvideo.strategies.MaskCutStrategy;
import ai.fritz.fritzvisionvideo.strategies.ObjectPoseStrategy;
import ai.fritz.fritzvisionvideo.strategies.PoseDoubleMaskStrategy;
import ai.fritz.fritzvisionvideo.strategies.StylizeBackgroundStrategy;
import ai.fritz.fritzvisionvideo.strategies.StylizeHairStrategy;
import ai.fritz.fritzvisionvideo.ui.DemoAdapter;
import ai.fritz.fritzvisionvideo.ui.DemoItem;
import ai.fritz.fritzvisionvideo.ui.SeparatorDecoration;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fritz.configure(this);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.demo_list_view);
recyclerView.setHasFixedSize(true);
LinearLayoutManager rvLinearLayoutMgr = new LinearLayoutManager(this);
recyclerView.setLayoutManager(rvLinearLayoutMgr);
// Add a divider
SeparatorDecoration decoration = new SeparatorDecoration(this, Color.GRAY, 1);
recyclerView.addItemDecoration(decoration);
// Add the adapter
DemoAdapter adapter = new DemoAdapter(getDemoItems());
recyclerView.setAdapter(adapter);
recyclerView.setClickable(true);
}
private List getDemoItems() {
List demoItems = new ArrayList<>();
demoItems.add(new DemoItem(
getString(R.string.starry_night_hair_title),
getString(R.string.starry_night_hair_description),
new View.OnClickListener() {
@Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(context, VideoActivity.class);
// Send a VideoFilterStrategy as a Parcelable
intent.putExtra(getResources().getString(R.string.filter_strategy_key), new StylizeHairStrategy());
context.startActivity(intent);
}
}));
demoItems.add(new DemoItem(
getString(R.string.stylize_background_title),
getString(R.string.stylize_background_description),
new View.OnClickListener() {
@Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(context, VideoActivity.class);
// Send a VideoFilterStrategy as a Parcelable
intent.putExtra(getResources().getString(R.string.filter_strategy_key), new StylizeBackgroundStrategy());
context.startActivity(intent);
}
}));
demoItems.add(new DemoItem(
getString(R.string.object_pose_title),
getString(R.string.object_pose_description),
new View.OnClickListener() {
@Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(context, VideoActivity.class);
// Send a VideoFilterStrategy as a Parcelable
intent.putExtra(getResources().getString(R.string.filter_strategy_key), new ObjectPoseStrategy());
context.startActivity(intent);
}
}));
demoItems.add(new DemoItem(
getString(R.string.pose_double_mask_title),
getString(R.string.pose_double_mask_description),
new View.OnClickListener() {
@Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(context, VideoActivity.class);
// Send a VideoFilterStrategy as a Parcelable
intent.putExtra(getResources().getString(R.string.filter_strategy_key), new PoseDoubleMaskStrategy());
context.startActivity(intent);
}
}));
demoItems.add(new DemoItem(
getString(R.string.mask_cut_title),
getString(R.string.mask_cut_description),
new View.OnClickListener() {
@Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(context, VideoActivity.class);
// Send a VideoFilterStrategy as a Parcelable
intent.putExtra(getResources().getString(R.string.filter_strategy_key), new MaskCutStrategy());
context.startActivity(intent);
}
}));
return demoItems;
}
}
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/java/ai/fritz/fritzvisionvideo/VideoActivity.java
================================================
package ai.fritz.fritzvisionvideo;
import android.app.Activity;
import android.content.Intent;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.VideoView;
import androidx.appcompat.app.AppCompatActivity;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.Locale;
import ai.fritz.fritzvisionvideo.strategies.VideoFilterStrategy;
import ai.fritz.vision.video.ExportVideoOptions;
import ai.fritz.vision.video.FritzVisionImageFilter;
import ai.fritz.vision.video.FritzVisionVideo;
public class VideoActivity extends AppCompatActivity {
private static final int REQUEST_CODE = 1;
private VideoView videoView;
private ProgressBar progressBar;
private TextView progressText;
private MenuItem exportButton;
private VideoFilterStrategy strategy;
private File exportFile = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video);
videoView = findViewById(R.id.video_view);
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.setLooping(true);
mediaPlayer.setVolume(1, 1);
mediaPlayer.start();
}
});
progressBar = findViewById(R.id.export_progress);
progressText = findViewById(R.id.progress_text);
// Retrieve the VideoFilterStrategy
strategy = getIntent().getParcelableExtra(getResources().getString(R.string.filter_strategy_key));
// Display the video picker
Intent filePicker = new Intent(Intent.ACTION_PICK, MediaStore.Video.Media.EXTERNAL_CONTENT_URI);
filePicker.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
startActivityForResult(filePicker, 1);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Show a top menu bar with an export button
getMenuInflater().inflate(R.menu.video_menu, menu);
exportButton = menu.findItem(R.id.export_button);
exportButton.setVisible(false);
exportButton.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
try {
saveProcessedVideo();
} catch (IOException e) {
throw new IllegalStateException("Unable to save video.");
}
menuItem.setEnabled(false);
return true;
}
});
return super.onCreateOptionsMenu(menu);
}
@Override
protected void onDestroy() {
super.onDestroy();
File cacheDir = getCacheDir();
File[] cachedFiles = cacheDir.listFiles();
// Delete any cached files
if (cachedFiles != null) {
for (File file : cachedFiles) {
file.delete();
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {
// Process the video after a video is selected
startProcessing(data.getData());
} else {
finish();
}
super.onActivityResult(requestCode, resultCode, data);
}
/**
* Starts processing a video.
*
* @param videoUri The URI of the video to process.
*/
private void startProcessing(Uri videoUri) {
try {
// Create a temporary file to write the processed video to
exportFile = File.createTempFile("tempExport", ".mp4", getCacheDir());
} catch (IOException e) {
throw new IllegalStateException("Unable to create a destination file.");
}
// Get filters to apply on the video
FritzVisionImageFilter[] filters = strategy.getFilters();
// Create the FritzVisionVideo object with the filter and selected video URI
FritzVisionVideo fritzVideo = new FritzVisionVideo(videoUri, filters);
// Create and set the processing parameters
// Every third frame of the video will be processed
ExportVideoOptions options = new ExportVideoOptions();
options.frameInterval = 3;
// Uncomment following line to enable audio at the cost of increased processing time
// options.copyAudio = true;
final String exportPath = exportFile.getAbsolutePath();
// Start exporting and processing the video
fritzVideo.export(exportPath, options, new FritzVisionVideo.ExportProgressCallback() {
@Override
public void onProgress(Float response) {
// Update progress
int progress = (int) (response * 100);
updateProgress(progress);
}
@Override
public void onComplete() {
// Play the processed video
Uri uri = Uri.fromFile(exportFile);
displayVideo(uri);
}
});
}
/**
* Updates progress when processing.
*/
private void updateProgress(final int progress) {
runOnUiThread(new Runnable() {
@Override
public void run() {
progressBar.setProgress(progress);
progressText.setText(String.format(Locale.US, "Processing %d%%", progress));
}
});
}
/**
* Changes visibility of views when processing is complete.
*/
private void displayVideo(final Uri videoUri) {
runOnUiThread(new Runnable() {
@Override
public void run() {
progressBar.setVisibility(View.INVISIBLE);
progressText.setVisibility(View.INVISIBLE);
exportButton.setVisible(true);
videoView.setVideoURI(videoUri);
}
});
}
/**
* Write the contents of the processed video to Photos.
*
* @throws IOException If there is a file error.
*/
private void saveProcessedVideo() throws IOException {
// Get the directory to save to
File file = getExternalFilesDir(Environment.DIRECTORY_MOVIES);
// Create the file to write to
String exportPath = (file.getAbsolutePath() + "/" + System.currentTimeMillis() + ".mp4");
File destFile = new File(exportPath);
// Write contents of the processed video file to the destination file
try (FileChannel source = new FileInputStream(exportFile).getChannel();
FileChannel destination = new FileOutputStream(destFile).getChannel()) {
destination.transferFrom(source, 0, source.size());
}
// Notify the user that the video has been saved
Toast.makeText(
VideoActivity.this,
"Saved video to " + exportPath, Toast.LENGTH_LONG
).show();
}
}
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/java/ai/fritz/fritzvisionvideo/strategies/MaskCutStrategy.java
================================================
package ai.fritz.fritzvisionvideo.strategies;
import android.os.Parcel;
import ai.fritz.vision.FritzVision;
import ai.fritz.vision.FritzVisionModels;
import ai.fritz.vision.ModelVariant;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationPredictor;
import ai.fritz.vision.imagesegmentation.MaskClass;
import ai.fritz.vision.imagesegmentation.SegmentationOnDeviceModel;
import ai.fritz.vision.video.FritzVisionImageFilter;
import ai.fritz.vision.video.filters.imagesegmentation.MaskCutOutOverlayFilter;
import ai.fritz.vision.video.filters.imagesegmentation.MaskOverlayFilter;
public class MaskCutStrategy extends VideoFilterStrategy {
private SegmentationOnDeviceModel peopleModel = FritzVisionModels.getPeopleSegmentationOnDeviceModel(ModelVariant.FAST);
private SegmentationOnDeviceModel hairModel = FritzVisionModels.getHairSegmentationOnDeviceModel(ModelVariant.FAST);
public MaskCutStrategy() {
super();
}
public MaskCutStrategy(Parcel in) {
super(in);
}
public static final Creator CREATOR = new Creator() {
@Override
public VideoFilterStrategy createFromParcel(Parcel in) {
return new MaskCutStrategy(in);
}
@Override
public VideoFilterStrategy[] newArray(int size) {
return new MaskCutStrategy[size];
}
};
@Override
public FritzVisionImageFilter[] getFilters() {
// Create predictors for the filter
FritzVisionSegmentationPredictor personPredictor = FritzVision.ImageSegmentation.getPredictor(peopleModel);
FritzVisionSegmentationPredictor hairPredictor = FritzVision.ImageSegmentation.getPredictor(hairModel);
return new FritzVisionImageFilter[]{
new MaskOverlayFilter(personPredictor, MaskClass.PERSON),
new MaskCutOutOverlayFilter(hairPredictor, MaskClass.HAIR)
};
}
}
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/java/ai/fritz/fritzvisionvideo/strategies/ObjectPoseStrategy.java
================================================
package ai.fritz.fritzvisionvideo.strategies;
import android.os.Parcel;
import ai.fritz.vision.FritzVision;
import ai.fritz.vision.FritzVisionModels;
import ai.fritz.vision.ModelVariant;
import ai.fritz.vision.objectdetection.FritzVisionObjectPredictor;
import ai.fritz.vision.objectdetection.ObjectDetectionOnDeviceModel;
import ai.fritz.vision.poseestimation.FritzVisionPosePredictor;
import ai.fritz.vision.poseestimation.PoseOnDeviceModel;
import ai.fritz.vision.video.FritzVisionImageFilter;
import ai.fritz.vision.video.filters.DrawBoxesCompoundFilter;
import ai.fritz.vision.video.filters.DrawSkeletonCompoundFilter;
public class ObjectPoseStrategy extends VideoFilterStrategy {
private ObjectDetectionOnDeviceModel objectModel = FritzVisionModels.getObjectDetectionOnDeviceModel();
private PoseOnDeviceModel poseModel = FritzVisionModels.getHumanPoseEstimationOnDeviceModel(ModelVariant.FAST);
public ObjectPoseStrategy() {
super();
}
public ObjectPoseStrategy(Parcel in) {
super(in);
}
public static final Creator CREATOR = new Creator() {
@Override
public VideoFilterStrategy createFromParcel(Parcel in) {
return new ObjectPoseStrategy(in);
}
@Override
public VideoFilterStrategy[] newArray(int size) {
return new ObjectPoseStrategy[size];
}
};
@Override
public FritzVisionImageFilter[] getFilters() {
// Create predictors for the filters
FritzVisionObjectPredictor objectPredictor = FritzVision.ObjectDetection.getPredictor(objectModel);
FritzVisionPosePredictor posePredictor = FritzVision.PoseEstimation.getPredictor(poseModel);
return new FritzVisionImageFilter[]{
new DrawBoxesCompoundFilter(objectPredictor), // Applied first
new DrawSkeletonCompoundFilter(posePredictor) // Applied second
};
}
}
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/java/ai/fritz/fritzvisionvideo/strategies/PoseDoubleMaskStrategy.java
================================================
package ai.fritz.fritzvisionvideo.strategies;
import android.os.Parcel;
import ai.fritz.vision.FritzVision;
import ai.fritz.vision.FritzVisionModels;
import ai.fritz.vision.ModelVariant;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationMaskOptions;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationPredictor;
import ai.fritz.vision.imagesegmentation.MaskClass;
import ai.fritz.vision.imagesegmentation.SegmentationOnDeviceModel;
import ai.fritz.vision.poseestimation.FritzVisionPosePredictor;
import ai.fritz.vision.poseestimation.PoseOnDeviceModel;
import ai.fritz.vision.video.FritzVisionImageFilter;
import ai.fritz.vision.video.filters.DrawSkeletonCompoundFilter;
import ai.fritz.vision.video.filters.imagesegmentation.MaskOverlayFilter;
public class PoseDoubleMaskStrategy extends VideoFilterStrategy {
private PoseOnDeviceModel poseModel = FritzVisionModels.getHumanPoseEstimationOnDeviceModel(ModelVariant.FAST);
private SegmentationOnDeviceModel peopleModel = FritzVisionModels.getPeopleSegmentationOnDeviceModel(ModelVariant.FAST);
private SegmentationOnDeviceModel hairModel = FritzVisionModels.getHairSegmentationOnDeviceModel(ModelVariant.FAST);
private FritzVisionSegmentationMaskOptions peopleOptions = new FritzVisionSegmentationMaskOptions();
public PoseDoubleMaskStrategy() {
super();
}
public PoseDoubleMaskStrategy(Parcel in) {
super(in);
}
public static final Creator CREATOR = new Creator() {
@Override
public VideoFilterStrategy createFromParcel(Parcel in) {
return new PoseDoubleMaskStrategy(in);
}
@Override
public VideoFilterStrategy[] newArray(int size) {
return new PoseDoubleMaskStrategy[size];
}
};
@Override
public FritzVisionImageFilter[] getFilters() {
// Reduce the alpha of the people mask
peopleOptions.maxAlpha = 50;
// Create predictors for the filters
FritzVisionPosePredictor posePredictor = FritzVision.PoseEstimation.getPredictor(poseModel);
FritzVisionSegmentationPredictor peoplePredictor = FritzVision.ImageSegmentation.getPredictor(peopleModel);
FritzVisionSegmentationPredictor hairPredictor = FritzVision.ImageSegmentation.getPredictor(hairModel);
return new FritzVisionImageFilter[]{
new DrawSkeletonCompoundFilter(posePredictor), // Applied first
new MaskOverlayFilter(peoplePredictor, peopleOptions, MaskClass.PERSON), // Applied second
new MaskOverlayFilter(hairPredictor, MaskClass.HAIR) // Applied last
};
}
}
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/java/ai/fritz/fritzvisionvideo/strategies/StylizeBackgroundStrategy.java
================================================
package ai.fritz.fritzvisionvideo.strategies;
import android.os.Parcel;
import ai.fritz.core.FritzOnDeviceModel;
import ai.fritz.vision.FritzVision;
import ai.fritz.vision.FritzVisionModels;
import ai.fritz.vision.ModelVariant;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationPredictor;
import ai.fritz.vision.imagesegmentation.MaskClass;
import ai.fritz.vision.imagesegmentation.SegmentationOnDeviceModel;
import ai.fritz.vision.styletransfer.FritzVisionStylePredictor;
import ai.fritz.vision.video.FritzVisionImageFilter;
import ai.fritz.vision.video.filters.StylizeImageCompoundFilter;
import ai.fritz.vision.video.filters.imagesegmentation.MaskCutOutOverlayFilter;
public class StylizeBackgroundStrategy extends VideoFilterStrategy {
private FritzOnDeviceModel styleModel = FritzVisionModels.getPaintingStyleModels().getTheScream();
private SegmentationOnDeviceModel peopleModel = FritzVisionModels.getPeopleSegmentationOnDeviceModel(ModelVariant.FAST);
public StylizeBackgroundStrategy() {
super();
}
public StylizeBackgroundStrategy(Parcel in) {
super(in);
}
public static final Creator CREATOR = new Creator() {
@Override
public VideoFilterStrategy createFromParcel(Parcel in) {
return new StylizeBackgroundStrategy(in);
}
@Override
public VideoFilterStrategy[] newArray(int size) {
return new StylizeBackgroundStrategy[size];
}
};
@Override
public FritzVisionImageFilter[] getFilters() {
// Create predictors for the filters
FritzVisionStylePredictor stylePredictor = FritzVision.StyleTransfer.getPredictor(styleModel);
FritzVisionSegmentationPredictor segmentationPredictor = FritzVision.ImageSegmentation.getPredictor(peopleModel);
return new FritzVisionImageFilter[]{
new StylizeImageCompoundFilter(stylePredictor), // Applied first
new MaskCutOutOverlayFilter(segmentationPredictor, MaskClass.PERSON) // Applied second
};
}
}
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/java/ai/fritz/fritzvisionvideo/strategies/StylizeHairStrategy.java
================================================
package ai.fritz.fritzvisionvideo.strategies;
import android.os.Parcel;
import ai.fritz.core.FritzOnDeviceModel;
import ai.fritz.fritzvisionvideo.strategies.customfilters.StylizeHairFilter;
import ai.fritz.vision.FritzVision;
import ai.fritz.vision.FritzVisionModels;
import ai.fritz.vision.ModelVariant;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationPredictor;
import ai.fritz.vision.imagesegmentation.MaskClass;
import ai.fritz.vision.imagesegmentation.SegmentationOnDeviceModel;
import ai.fritz.vision.styletransfer.FritzVisionStylePredictor;
import ai.fritz.vision.video.FritzVisionImageFilter;
public class StylizeHairStrategy extends VideoFilterStrategy {
private FritzOnDeviceModel styleModel = FritzVisionModels.getPaintingStyleModels().getStarryNight();
private SegmentationOnDeviceModel hairModel = FritzVisionModels.getHairSegmentationOnDeviceModel(ModelVariant.FAST);
public StylizeHairStrategy() {
super();
}
public StylizeHairStrategy(Parcel in) {
super(in);
}
public static final Creator CREATOR = new Creator() {
@Override
public VideoFilterStrategy createFromParcel(Parcel in) {
return new StylizeHairStrategy(in);
}
@Override
public VideoFilterStrategy[] newArray(int size) {
return new StylizeHairStrategy[size];
}
};
@Override
public FritzVisionImageFilter[] getFilters() {
// Create predictors for the filter
FritzVisionSegmentationPredictor hairPredictor = FritzVision.ImageSegmentation.getPredictor(hairModel);
FritzVisionStylePredictor stylePredictor = FritzVision.StyleTransfer.getPredictor(styleModel);
return new FritzVisionImageFilter[]{
new StylizeHairFilter(stylePredictor, hairPredictor, MaskClass.HAIR)
};
}
}
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/java/ai/fritz/fritzvisionvideo/strategies/VideoFilterStrategy.java
================================================
package ai.fritz.fritzvisionvideo.strategies;
import android.os.Parcel;
import android.os.Parcelable;
import ai.fritz.vision.video.FritzVisionImageFilter;
public abstract class VideoFilterStrategy implements Parcelable {
protected VideoFilterStrategy() {
}
protected VideoFilterStrategy(Parcel in) {
}
@Override
public void writeToParcel(Parcel dest, int flags) {
}
@Override
public int describeContents() {
return 0;
}
/**
* Retrieve created filters.
*
* @return A filter array.
*/
public abstract FritzVisionImageFilter[] getFilters();
}
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/java/ai/fritz/fritzvisionvideo/strategies/customfilters/StylizeHairFilter.java
================================================
package ai.fritz.fritzvisionvideo.strategies.customfilters;
import android.graphics.Bitmap;
import ai.fritz.vision.FritzVisionImage;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationMaskOptions;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationPredictor;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationResult;
import ai.fritz.vision.imagesegmentation.MaskClass;
import ai.fritz.vision.styletransfer.FritzVisionStylePredictor;
import ai.fritz.vision.styletransfer.FritzVisionStyleResult;
import ai.fritz.vision.video.filters.imagesegmentation.FritzVisionSegmentationFilter;
public class StylizeHairFilter extends FritzVisionSegmentationFilter {
private FritzVisionStylePredictor stylePredictor;
public StylizeHairFilter(
FritzVisionStylePredictor stylePredictor,
FritzVisionSegmentationPredictor model,
MaskClass segmentationMask
) {
super(model, segmentationMask);
this.stylePredictor = stylePredictor;
}
public StylizeHairFilter(
FritzVisionStylePredictor stylePredictor,
FritzVisionSegmentationPredictor predictor,
FritzVisionSegmentationMaskOptions options,
MaskClass segmentationMask
) {
super(predictor, options, segmentationMask);
this.stylePredictor = stylePredictor;
}
@Override
public FilterCompositionMode getCompositionMode() {
return FilterCompositionMode.OVERLAY_ON_ORIGINAL_IMAGE;
}
@Override
public FritzVisionImage processImage(FritzVisionImage image) {
FritzVisionSegmentationResult hairResult = predictor.predict(image);
FritzVisionStyleResult styleResult = stylePredictor.predict(image);
FritzVisionImage stylizedImage = FritzVisionImage.fromBitmap(styleResult.toBitmap());
Bitmap mask = hairResult.buildSingleClassMask(segmentationMask, options);
return FritzVisionImage.fromBitmap(stylizedImage.mask(mask));
}
}
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/java/ai/fritz/fritzvisionvideo/ui/DemoAdapter.java
================================================
package ai.fritz.fritzvisionvideo.ui;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import ai.fritz.fritzvisionvideo.R;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
/**
* Demo items adapter to manage the list of models.
*/
public class DemoAdapter extends RecyclerView.Adapter {
private List demoItems = new ArrayList<>();
/**
* View holder for each demo item to display in the list.
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
public LinearLayout linearLayout;
public TextView titleView;
public TextView descriptionView;
public ViewHolder(LinearLayout v) {
super(v);
linearLayout = v;
titleView = linearLayout.findViewById(R.id.title);
descriptionView = linearLayout.findViewById(R.id.description);
}
}
/**
* Initialize the adapter with a list of demo items.
*
* @param demoItems
*/
public DemoAdapter(List demoItems) {
this.demoItems = demoItems;
}
/**
* Get the DemoItem and binds it to the recycled view.
*
* Also sets the click actions here.
*
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(DemoAdapter.ViewHolder holder, int position) {
DemoItem demoItem = demoItems.get(position);
holder.titleView.setText(demoItem.getTitle());
holder.descriptionView.setText(demoItem.getDescription());
holder.linearLayout.setOnClickListener(demoItem.getOnClickListener());
}
/**
* Get number of demo items.
*
* @return number of items in the list
*/
@Override
public int getItemCount() {
return demoItems.size();
}
/**
* Create a view holder for the DemoItems
*
* @param parent
* @param viewType
* @return
*/
@Override
public DemoAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
LinearLayout v = (LinearLayout) LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item_demo, parent, false);
ViewHolder holder = new ViewHolder(v);
return holder;
}
}
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/java/ai/fritz/fritzvisionvideo/ui/DemoItem.java
================================================
package ai.fritz.fritzvisionvideo.ui;
import android.view.View;
/**
* Helper class to show demo items for the DemoAdapter
*/
public class DemoItem {
private String title;
private String description;
private View.OnClickListener onClickListener;
public DemoItem(String title, String description, View.OnClickListener clickListener) {
this.title = title;
this.description = description;
this.onClickListener = clickListener;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public View.OnClickListener getOnClickListener() {
return onClickListener;
}
public void setOnClickListener(View.OnClickListener onClickListener) {
this.onClickListener = onClickListener;
}
}
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/java/ai/fritz/fritzvisionvideo/ui/SeparatorDecoration.java
================================================
package ai.fritz.fritzvisionvideo.ui;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import androidx.annotation.ColorInt;
import androidx.annotation.FloatRange;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import android.util.TypedValue;
import android.view.View;
/**
* Taken from https://github.com/bleeding182/recyclerviewItemDecorations
*/
public class SeparatorDecoration extends RecyclerView.ItemDecoration {
private final Paint mPaint;
/**
* Create a decoration that draws a line in the given color and width between the items in the view.
*
* @param context a context to access the resources.
* @param color the color of the separator to draw.
* @param heightDp the height of the separator in dp.
*/
public SeparatorDecoration(@NonNull Context context, @ColorInt int color,
@FloatRange(from = 0, fromInclusive = false) float heightDp) {
mPaint = new Paint();
mPaint.setColor(color);
final float thickness = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
heightDp, context.getResources().getDisplayMetrics());
mPaint.setStrokeWidth(thickness);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
// we want to retrieve the position in the list
final int position = params.getViewAdapterPosition();
// and add a separator to any view but the last one
if (position < state.getItemCount()) {
outRect.set(0, 0, 0, (int) mPaint.getStrokeWidth()); // left, top, right, bottom
} else {
outRect.setEmpty(); // 0, 0, 0, 0
}
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
// we set the stroke width before, so as to correctly draw the line we have to offset by width / 2
final int offset = (int) (mPaint.getStrokeWidth() / 2);
// this will iterate over every visible view
for (int i = 0; i < parent.getChildCount(); i++) {
// get the view
final View view = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
// get the position
final int position = params.getViewAdapterPosition();
// and finally draw the separator
if (position < state.getItemCount()) {
c.drawLine(view.getLeft(), view.getBottom() + offset, view.getRight(), view.getBottom() + offset, mPaint);
}
}
}
}
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/res/drawable/ic_launcher_background.xml
================================================
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/res/font/sf_display.xml
================================================
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/res/layout/activity_video.xml
================================================
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/res/layout/app_bar.xml
================================================
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/res/layout/list_item_demo.xml
================================================
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/res/menu/video_menu.xml
================================================
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/res/values/colors.xml
================================================
#000#000#9E8D37#fff
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/res/values/dimens.xml
================================================
4dp8dp16dp32dp64dp@dimen/margin_md@dimen/margin_md
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/res/values/fritz.xml
================================================
Your API Key
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/res/values/strings.xml
================================================
Video ProcessingThis device doesn\'t support Camera2 API.Starry Night HairStylize your hair with Starry Night by Vincent van Gogh.Stylize BackgroundCut out people and paste them on top of a background stylized with Ritmo Plastico by Gino Severini.Object Detection and Pose EstimationDetect and draw boxes around objects while identifying and tracking the body position of a person.Pose Estimation with People and Hair MaskIdentify and track the body position of a person while putting a mask over people and hair.People Mask with Original HairPut a mask over people while retaining their original hair.videostrategy
================================================
FILE: Android/FritzVisionVideoApp/app/src/main/res/values/styles.xml
================================================
================================================
FILE: Android/FritzVisionVideoApp/app/src/test/java/ai/fritz/fritzvisionvideo/ExampleUnitTest.java
================================================
package ai.fritz.fritzvisionvideo;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see Testing documentation
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: Android/FritzVisionVideoApp/build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
maven { url 'https://jitpack.io' }
// ADD FOR FRITZ DEPENDENCIES
maven {
url "https://fritz.mycloudrepo.io/public/repositories/android"
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: Android/FritzVisionVideoApp/gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
================================================
FILE: Android/FritzVisionVideoApp/gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: Android/FritzVisionVideoApp/gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: Android/FritzVisionVideoApp/settings.gradle
================================================
include ':app'
rootProject.name='FritzVisionVideoDemo'
================================================
FILE: Android/HairColoringApp/.gitignore
================================================
# Built application files
*.apk
*.ap_
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.idea
# Keystore files
# Uncomment the following line if you do not want to check your keystore files in.
#*.jks
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
# Google Services (e.g. APIs or Firebase)
google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
================================================
FILE: Android/HairColoringApp/README.md
================================================
# Styling and Coloring with Hair Segmentation
[ ](https://app.codeship.com/projects/297281)
[](http://twitter.com/fritzlabs)
In this app, we automatically blend a color mask with the user's hair, transforming it to red.

For the full tutorial, visit [our post on Heartbeat](https://heartbeat.fritz.ai/embrace-your-new-look-with-hair-segmentation-by-fritz-now-available-for-android-developers-f20f5b4e9ae1).
This example app uses the on-device Hair Segmentation API for Android.
- [Overview](https://www.fritz.ai/features/image-segmentation.html)
- [Documentation](https://docs.fritz.ai/develop/vision/image-segmentation/android.html)
## Fritz AI
Fritz AI is the machine learning platform for iOS and Android developers. Teach your mobile apps to see, hear, sense, and think. Start with our ready-to-use feature APIs or connect and deploy your own custom models.
## Requirements
- Android Studio 3.2 or above
- Android device in developer model (USB debugging enabled)
## Getting Started
**Step 1: Choose a Fritz AI Plan**
[Sign up](https://www.fritz.ai/pricing/?utm_source=github&utm_campaign=fritz-examples) for a Fritz AI plan to get started.
Register the Android app in your Fritz account with the package id "ai.fritz.haircoloring". During registration, you'll receive an API key for the app. Save this for later. To find it in the webapp, you can go to Project Settings > Apps > Your App > Show API Key.
**Step 2: Clone / Fork the fritz-examples repository and open the HairColoringApp in Android Studio**
```
git clone https://github.com/fritzlabs/fritz-examples.git
```
In Android Studio, choose "Open an existing Android Studio project" and select `HairColoringApp`.
**Step 3: Edit the fritz.xml file with your API Key**
In app/src/main/res/values/fritz.xml, change the fritz_api_key attribute with the one you received in step 1.
**Step 4: Build the Android Studio Project**
Select "Build > Make Project" from the top nav. Download any missing libraries if applicable. This should sync the gradle dependencies so give the build a second to complete.
**Step 5: Install the app onto your device**
With your Android device connected, select `Run > Run App` from the top nav. When running the app for the first time, you'll have to give permissions to access the camera. After the app is installed and running, point your camera at someone's hair to automatically color it.
## Official Documentation
[SDK Documentation](https://docs.fritz.ai/?utm_source=github&utm_campaign=fritz-examples)
[Android API Docs](https://docs.fritz.ai/android/latest/index.html?utm_source=github&utm_campaign=fritz-examples)
## Join the community
[Heartbeat](https://heartbeat.fritz.ai/?utm_source=github&utm_campaign=fritz-examples) is a community of developers interested in the intersection of mobile and machine learning. [Chat with us in Slack](https://www.fritz.ai/slack?utm_source=github&utm_campaign=fritz-examples) and stay up to date on the latest mobile ML news with our [Newsletter](https://www.fritz.ai/newsletter?utm_source=github&utm_campaign=fritz-examples).
## Help
For any questions or issues, you can:
- Submit an issue on this repo
- Go to [Support](https://support.fritz.ai/?utm_source=github&utm_campaign=fritz-examples)
- Message us directly in [Slack](https://www.fritz.ai/slack?utm_source=github&utm_campaign=fritz-examples)
================================================
FILE: Android/HairColoringApp/app/.gitignore
================================================
/build
================================================
FILE: Android/HairColoringApp/app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
// MUST MATCH THE APPLICATION YOU CREATE IN FRITZ
applicationId "ai.fritz.haircoloring"
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
debuggable true
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
aaptOptions {
noCompress "tflite"
}
lintOptions {
abortOnError false
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'com.github.veritas1:Android-VerticalSlideColorPicker:1.0.0'
implementation "ai.fritz:vision:6.0.3"
implementation "ai.fritz:vision-hair-segmentation-model-fast:3.0.0"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
}
================================================
FILE: Android/HairColoringApp/app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: Android/HairColoringApp/app/src/androidTest/java/ai/fritz/haircoloring/ExampleInstrumentedTest.java
================================================
package ai.fritz.haircoloring;
import android.content.Context;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see Testing documentation
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("ai.fritz.camera", appContext.getPackageName());
}
}
================================================
FILE: Android/HairColoringApp/app/src/main/AndroidManifest.xml
================================================
================================================
FILE: Android/HairColoringApp/app/src/main/java/ai/fritz/haircoloring/activities/BaseCameraActivity.java
================================================
package ai.fritz.haircoloring.activities;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.ImageReader.OnImageAvailableListener;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.util.Size;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.Toast;
import ai.fritz.haircoloring.R;
import ai.fritz.haircoloring.views.CameraConnectionFragment;
import androidx.appcompat.app.AppCompatActivity;
import ai.fritz.haircoloring.views.OverlayView;
public abstract class BaseCameraActivity extends AppCompatActivity implements OnImageAvailableListener {
private static final String TAG = BaseCameraActivity.class.getSimpleName();
private static final int PERMISSIONS_REQUEST = 1;
private static final long DELAY_OVERLAY_VISIBLE = 500;
private static final String PERMISSION_CAMERA = Manifest.permission.CAMERA;
private static final String PERMISSION_STORAGE = Manifest.permission.WRITE_EXTERNAL_STORAGE;
private boolean useCamera2API;
private boolean debug = false;
private Handler handler;
private HandlerThread handlerThread;
protected String cameraId;
protected int cameraFacingDirection = CameraCharacteristics.LENS_FACING_BACK;
CameraConnectionFragment fragment;
@Override
protected void onCreate(final Bundle savedInstanceState) {
Log.d(TAG, "onCreate " + this);
super.onCreate(null);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_camera);
if (hasPermission()) {
setFragment();
} else {
requestPermission();
}
}
@Override
public synchronized void onStart() {
Log.d(TAG, "onStart " + this);
super.onStart();
}
@Override
public synchronized void onResume() {
Log.d(TAG, "onResume " + this);
super.onResume();
handlerThread = new HandlerThread("inference");
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
}
@Override
public synchronized void onPause() {
Log.d(TAG, "onPause " + this);
handlerThread.quitSafely();
try {
handlerThread.join();
handlerThread = null;
handler = null;
} catch (final InterruptedException e) {
Log.e(TAG, "Exception!" + e);
}
super.onPause();
}
@Override
public synchronized void onStop() {
Log.d(TAG, "onStop " + this);
super.onStop();
}
@Override
public synchronized void onDestroy() {
Log.d(TAG, "onDestroy " + this);
super.onDestroy();
}
protected int getCameraFacingDirection() {
return cameraFacingDirection;
}
protected void setCameraFacingDirection(int cameraFacingDirection) {
this.cameraFacingDirection = cameraFacingDirection;
}
protected synchronized void runInBackground(final Runnable r) {
if (handler != null) {
handler.post(r);
}
}
@Override
public void onRequestPermissionsResult(
final int requestCode, final String[] permissions, final int[] grantResults) {
switch (requestCode) {
case PERMISSIONS_REQUEST: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
setFragment();
} else {
requestPermission();
}
}
}
}
private boolean hasPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return checkSelfPermission(PERMISSION_CAMERA) == PackageManager.PERMISSION_GRANTED && checkSelfPermission(PERMISSION_STORAGE) == PackageManager.PERMISSION_GRANTED;
} else {
return true;
}
}
private void requestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (shouldShowRequestPermissionRationale(PERMISSION_CAMERA) || shouldShowRequestPermissionRationale(PERMISSION_STORAGE)) {
Toast.makeText(BaseCameraActivity.this, "Camera AND storage permission are required for this demo", Toast.LENGTH_LONG).show();
}
requestPermissions(new String[]{PERMISSION_CAMERA, PERMISSION_STORAGE}, PERMISSIONS_REQUEST);
}
}
protected void setFragment() {
cameraId = chooseCamera();
fragment =
CameraConnectionFragment.newInstance(
new CameraConnectionFragment.ConnectionCallback() {
@Override
public void onPreviewSizeChosen(final Size previewSize, final Size cameraViewSize, final int rotation) {
BaseCameraActivity.this.onPreviewSizeChosen(previewSize, cameraViewSize, rotation);
}
},
this,
getLayoutId(),
getDesiredPreviewFrameSize(),
new CameraConnectionFragment.CameraOpenListener() {
@Override
public void onCameraOpened() {
final Handler handler = new Handler();
handler.postDelayed(
new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
OverlayView overlay = findViewById(R.id.debug_overlay);
if (overlay != null) {
overlay.setVisibility(View.VISIBLE);
}
}
});
}
}, DELAY_OVERLAY_VISIBLE);
}
});
getFragmentManager()
.beginTransaction()
.replace(R.id.camera_container, fragment)
.commit();
fragment.setCamera(cameraId);
}
protected void toggleCameraFacingDirection() {
final OverlayView overlay = findViewById(R.id.debug_overlay);
if (overlay != null) {
overlay.setVisibility(View.INVISIBLE);
}
if (cameraFacingDirection == CameraCharacteristics.LENS_FACING_FRONT) {
cameraFacingDirection = CameraCharacteristics.LENS_FACING_BACK;
} else {
cameraFacingDirection = CameraCharacteristics.LENS_FACING_FRONT;
}
cameraId = chooseCamera();
fragment.switchCamera(cameraId);
}
private String chooseCamera() {
final CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
for (final String cameraId : manager.getCameraIdList()) {
final CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
// We don't use a front facing camera in this sample.
final Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
if (facing != null && facing != cameraFacingDirection) {
continue;
}
final StreamConfigurationMap map =
characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
continue;
}
// Fallback to camera1 API for internal cameras that don't have full support.
// This should help with legacy situations where using the camera2 API causes
// distorted or otherwise broken previews.
useCamera2API = (facing == CameraCharacteristics.LENS_FACING_EXTERNAL)
|| isHardwareLevelSupported(characteristics,
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
Log.i(TAG, "Camera API lv2?: " + useCamera2API);
return cameraId;
}
} catch (CameraAccessException e) {
Log.e(TAG, "Not allowed to access camera: " + e);
}
return null;
}
// Returns true if the device supports the required hardware level, or better.
private boolean isHardwareLevelSupported(
CameraCharacteristics characteristics, int requiredLevel) {
int deviceLevel = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (deviceLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
return requiredLevel == deviceLevel;
}
// deviceLevel is not LEGACY, can use numerical sort
return requiredLevel <= deviceLevel;
}
public boolean isDebug() {
return debug;
}
public void requestRender() {
final OverlayView overlay = (OverlayView) findViewById(R.id.debug_overlay);
if (overlay != null) {
overlay.postInvalidate();
}
}
public void setCallback(final OverlayView.DrawCallback callback) {
final OverlayView overlay = (OverlayView) findViewById(R.id.debug_overlay);
if (overlay != null) {
overlay.setCallback(callback);
}
}
public void onSetDebug(final boolean debug) {
}
@Override
public boolean onKeyDown(final int keyCode, final KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
debug = !debug;
requestRender();
onSetDebug(debug);
return true;
}
return super.onKeyDown(keyCode, event);
}
protected abstract void onPreviewSizeChosen(final Size previewSize, final Size cameraViewSize, final int rotation);
protected abstract int getLayoutId();
protected Size getDesiredPreviewFrameSize() {
return new Size(640, 480);
}
}
================================================
FILE: Android/HairColoringApp/app/src/main/java/ai/fritz/haircoloring/activities/BaseLiveGPUActivity.java
================================================
package ai.fritz.haircoloring.activities;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.util.Size;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import java.util.concurrent.atomic.AtomicBoolean;
import ai.fritz.haircoloring.R;
import ai.fritz.vision.FritzSurfaceView;
import ai.fritz.vision.FritzVisionImage;
import ai.fritz.vision.FritzVisionOrientation;
import ai.fritz.vision.ImageOrientation;
public abstract class BaseLiveGPUActivity extends BaseCameraActivity implements ImageReader.OnImageAvailableListener {
private static final String TAG = BaseLiveGPUActivity.class.getSimpleName();
private AtomicBoolean computing = new AtomicBoolean(false);
private ImageOrientation orientation;
protected Button chooseModelBtn;
protected ImageButton cameraSwitchBtn;
protected FritzVisionImage fritzVisionImage;
protected FritzSurfaceView fritzSurfaceView;
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onPreviewSizeChosen(final Size size, final Size cameraSize, final int rotation) {
orientation = FritzVisionOrientation.getImageOrientationFromCamera(this, cameraId);
chooseModelBtn = findViewById(R.id.chose_model_btn);
cameraSwitchBtn = findViewById(R.id.camera_switch_btn);
fritzSurfaceView = findViewById(R.id.gpuimageview);
cameraSwitchBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
toggleCameraFacingDirection();
}
});
}
@Override
public void onImageAvailable(final ImageReader reader) {
final Image image = reader.acquireLatestImage();
if (image == null) {
return;
}
if (!computing.compareAndSet(false, true)) {
image.close();
return;
}
fritzVisionImage = FritzVisionImage.fromMediaImage(image, orientation);
runInference(fritzVisionImage);
image.close();
computing.set(false);
}
protected abstract int getLayoutId();
@Override
public void onSetDebug(final boolean debug) {
}
protected abstract void runInference(FritzVisionImage fritzVisionImage);
}
================================================
FILE: Android/HairColoringApp/app/src/main/java/ai/fritz/haircoloring/activities/LiveHairColorActivity.java
================================================
package ai.fritz.haircoloring.activities;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.hardware.camera2.CameraCharacteristics;
import android.os.Bundle;
import android.util.Size;
import com.github.veritas1.verticalslidecolorpicker.VerticalSlideColorPicker;
import ai.fritz.haircoloring.R;
import ai.fritz.vision.FritzVision;
import ai.fritz.vision.FritzVisionImage;
import ai.fritz.vision.FritzVisionModels;
import ai.fritz.vision.ModelVariant;
import ai.fritz.vision.imagesegmentation.BlendMode;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationPredictor;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationPredictorOptions;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationResult;
import ai.fritz.vision.imagesegmentation.MaskClass;
import ai.fritz.vision.imagesegmentation.SegmentationOnDeviceModel;
public class LiveHairColorActivity extends BaseLiveGPUActivity {
private int maskColor = Color.RED;
private static final int HAIR_ALPHA = 180;
private static final BlendMode BLEND_MODE = BlendMode.SOFT_LIGHT;
private static final boolean RUN_ON_GPU = true;
private FritzVisionSegmentationPredictor hairPredictor;
private FritzVisionSegmentationResult hairResult;
private FritzVisionSegmentationPredictorOptions options;
private SegmentationOnDeviceModel onDeviceModel;
public void onCreate(final Bundle savedInstanceState) {
setCameraFacingDirection(CameraCharacteristics.LENS_FACING_FRONT);
super.onCreate(savedInstanceState);
// Create the segmentation options
options = new FritzVisionSegmentationPredictorOptions();
options.useGPU = RUN_ON_GPU;
// Set the on-device model
onDeviceModel = FritzVisionModels.getHairSegmentationOnDeviceModel(ModelVariant.FAST);
// Load the predictor when the activity is created (iff not running on the GPU)
if (!RUN_ON_GPU) {
hairPredictor = FritzVision.ImageSegmentation.getPredictor(onDeviceModel, options);
}
}
@Override
public void onPreviewSizeChosen(final Size size, final Size cameraSize, final int rotation) {
super.onPreviewSizeChosen(size, cameraSize, rotation);
VerticalSlideColorPicker colorPicker = findViewById(R.id.color_picker);
// Change the mask color upon using the slider
colorPicker.setOnColorChangeListener(new VerticalSlideColorPicker.OnColorChangeListener() {
@Override
public void onColorChange(int selectedColor) {
if (selectedColor != Color.TRANSPARENT) {
maskColor = selectedColor;
}
}
});
}
@Override
protected int getLayoutId() {
return R.layout.camera_color_slider;
}
@Override
protected void runInference(FritzVisionImage fritzVisionImage) {
// If you're using the GPU, it MUST run on the same thread
if (RUN_ON_GPU && hairPredictor == null) {
hairPredictor = FritzVision.ImageSegmentation.getPredictor(onDeviceModel, options);
}
hairResult = hairPredictor.predict(fritzVisionImage);
Bitmap alphaMask = hairResult.buildSingleClassMask(MaskClass.HAIR, HAIR_ALPHA, options.confidenceThreshold, options.confidenceThreshold, maskColor);
fritzSurfaceView.drawBlendedMask(fritzVisionImage, alphaMask, BLEND_MODE, getCameraFacingDirection() == CameraCharacteristics.LENS_FACING_FRONT);
}
}
================================================
FILE: Android/HairColoringApp/app/src/main/java/ai/fritz/haircoloring/activities/MainActivity.java
================================================
package ai.fritz.haircoloring.activities;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
import ai.fritz.core.Fritz;
import ai.fritz.haircoloring.R;
import ai.fritz.haircoloring.ui.DemoAdapter;
import ai.fritz.haircoloring.ui.DemoItem;
import ai.fritz.haircoloring.ui.SeparatorDecoration;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fritz.configure(this);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.demo_list_view);
recyclerView.setHasFixedSize(true);
LinearLayoutManager rvLinearLayoutMgr = new LinearLayoutManager(this);
recyclerView.setLayoutManager(rvLinearLayoutMgr);
// Add a divider
SeparatorDecoration decoration = new SeparatorDecoration(this, Color.GRAY, 1);
recyclerView.addItemDecoration(decoration);
// Add the adapter
DemoAdapter adapter = new DemoAdapter(getDemoItems());
recyclerView.setAdapter(adapter);
recyclerView.setClickable(true);
}
private List getDemoItems() {
List demoItems = new ArrayList<>();
demoItems.add(new DemoItem(
getString(R.string.fritz_hair_color_title),
getString(R.string.fritz_hair_color_description),
new View.OnClickListener() {
@Override
public void onClick(View v) {
Context context = v.getContext();
Intent liveHair = new Intent(context, LiveHairColorActivity.class);
context.startActivity(liveHair);
}
}));
demoItems.add(new DemoItem(
getString(R.string.fritz_video_hair_color_title),
getString(R.string.fritz_video_hair_color_description),
new View.OnClickListener() {
@Override
public void onClick(View v) {
Context context = v.getContext();
Intent selectVideo = new Intent(context, VideoHairColorActivity.class);
context.startActivity(selectVideo);
}
}));
return demoItems;
}
}
================================================
FILE: Android/HairColoringApp/app/src/main/java/ai/fritz/haircoloring/activities/VideoHairColorActivity.java
================================================
package ai.fritz.haircoloring.activities;
import android.app.Activity;
import android.content.Intent;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.VideoView;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.Locale;
import ai.fritz.haircoloring.R;
import ai.fritz.vision.FritzVision;
import ai.fritz.vision.FritzVisionModels;
import ai.fritz.vision.ModelVariant;
import ai.fritz.vision.imagesegmentation.BlendMode;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationMaskOptions;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationPredictor;
import ai.fritz.vision.imagesegmentation.FritzVisionSegmentationPredictorOptions;
import ai.fritz.vision.imagesegmentation.MaskClass;
import ai.fritz.vision.imagesegmentation.SegmentationOnDeviceModel;
import ai.fritz.vision.video.ExportVideoOptions;
import ai.fritz.vision.video.FritzVisionImageFilter;
import ai.fritz.vision.video.FritzVisionVideo;
import ai.fritz.vision.video.filters.imagesegmentation.MaskBlendCompoundFilter;
import androidx.appcompat.app.AppCompatActivity;
public class VideoHairColorActivity extends AppCompatActivity {
private static final int REQUEST_CODE = 1;
private static final int HAIR_ALPHA = 180;
private static final float HAIR_CONFIDENCE_THRESHOLD = .5f;
private VideoView videoView;
private ProgressBar progressBar;
private TextView progressText;
private MenuItem exportButton;
private FritzVisionSegmentationPredictor hairPredictor;
private FritzVisionSegmentationMaskOptions maskOptions;
private File exportFile = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video);
videoView = findViewById(R.id.video_view);
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.setLooping(true);
mediaPlayer.setVolume(1, 1);
mediaPlayer.start();
}
});
progressBar = findViewById(R.id.export_progress);
progressText = findViewById(R.id.progress_text);
// Create the segmentation options
FritzVisionSegmentationPredictorOptions options = new FritzVisionSegmentationPredictorOptions();
options.confidenceThreshold = HAIR_CONFIDENCE_THRESHOLD;
// Set the on-device model
SegmentationOnDeviceModel onDeviceModel = FritzVisionModels.getHairSegmentationOnDeviceModel(ModelVariant.FAST);
// Create the predictor
hairPredictor = FritzVision.ImageSegmentation.getPredictor(onDeviceModel, options);
maskOptions = new FritzVisionSegmentationMaskOptions();
maskOptions.maxAlpha = HAIR_ALPHA;
// Display the video picker
Intent filePicker = new Intent(Intent.ACTION_PICK, MediaStore.Video.Media.EXTERNAL_CONTENT_URI);
filePicker.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
startActivityForResult(filePicker, 1);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Show a top menu bar with an export button
getMenuInflater().inflate(R.menu.video_menu, menu);
exportButton = menu.findItem(R.id.export_button);
exportButton.setVisible(false);
exportButton.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
try {
saveProcessedVideo();
} catch (IOException e) {
throw new IllegalStateException("Unable to save video.");
}
menuItem.setEnabled(false);
return true;
}
});
return super.onCreateOptionsMenu(menu);
}
@Override
protected void onDestroy() {
super.onDestroy();
File cacheDir = getCacheDir();
File[] cachedFiles = cacheDir.listFiles();
// Delete any cached files
if (cachedFiles != null) {
for (File file : cachedFiles) {
file.delete();
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {
// Process the video after a video is selected
startProcessing(data.getData());
}
else {
finish();
}
}
/**
* Starts processing a video.
*
* @param videoUri The URI of the video to process.
*/
private void startProcessing(Uri videoUri) {
try {
// Create a temporary file to write the processed video to
exportFile = File.createTempFile("tempExport", ".mp4", getCacheDir());
} catch (IOException e) {
throw new IllegalStateException("Unable to create a destination file.");
}
// Create the filter to apply on the video
FritzVisionImageFilter[] filters = {
new MaskBlendCompoundFilter(hairPredictor, maskOptions, MaskClass.HAIR, BlendMode.SOFT_LIGHT)
};
// Create the FritzVisionVideo object with the filter and selected video URI
FritzVisionVideo fritzVideo = new FritzVisionVideo(videoUri, filters);
// Create and set the processing parameters
// Every second frame of the video will be processed
// Enable audio to be copied
// Disable to decrease processing time
ExportVideoOptions options = new ExportVideoOptions();
options.copyAudio = true;
options.frameInterval = 2;
final String exportPath = exportFile.getAbsolutePath();
// Start exporting and processing the whole length of the video while skipping every other frame
fritzVideo.export(exportPath, options, new FritzVisionVideo.ExportProgressCallback() {
@Override
public void onProgress(Float response) {
// Update the export progress
int progress = (int) (response * 100);
updateProgress(progress);
}
@Override
public void onComplete() {
// Close the predictor
hairPredictor.close();
// Play the processed video
Uri uri = Uri.fromFile(exportFile);
displayVideo(uri);
}
});
}
/**
* Updates progress when processing.
*/
private void updateProgress(final int progress) {
runOnUiThread(new Runnable() {
@Override
public void run() {
progressBar.setProgress(progress);
progressText.setText(String.format(Locale.US, "Processing %d%%", progress));
}
});
}
/**
* Changes visibility of views when processing is complete.
*/
private void displayVideo(final Uri videoUri) {
runOnUiThread(new Runnable() {
@Override
public void run() {
progressBar.setVisibility(View.INVISIBLE);
progressText.setVisibility(View.INVISIBLE);
exportButton.setVisible(true);
videoView.setVideoURI(videoUri);
}
});
}
/**
* Write the contents of the processed video to Photos.
*
* @throws IOException If there is a file error.
*/
private void saveProcessedVideo() throws IOException {
// Get the directory to save to
File file = getExternalFilesDir(Environment.DIRECTORY_MOVIES);
// Create the file to write to
String exportPath = (file.getAbsolutePath() + "/" + System.currentTimeMillis() + ".mp4");
File destFile = new File(exportPath);
// Write contents of the processed video file to the destination file
try (FileChannel source = new FileInputStream(exportFile).getChannel();
FileChannel destination = new FileOutputStream(destFile).getChannel()) {
destination.transferFrom(source, 0, source.size());
}
// Notify the user that the video has been saved
Toast.makeText(
VideoHairColorActivity.this,
"Saved video to " + exportPath, Toast.LENGTH_LONG
).show();
}
}
================================================
FILE: Android/HairColoringApp/app/src/main/java/ai/fritz/haircoloring/ui/DemoAdapter.java
================================================
package ai.fritz.haircoloring.ui;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import ai.fritz.haircoloring.R;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
/**
* Demo items adapter to manage the list of models.
*/
public class DemoAdapter extends RecyclerView.Adapter {
private List demoItems = new ArrayList<>();
/**
* View holder for each demo item to display in the list.
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
public LinearLayout linearLayout;
public TextView titleView;
public TextView descriptionView;
public ViewHolder(LinearLayout v) {
super(v);
linearLayout = v;
titleView = linearLayout.findViewById(R.id.title);
descriptionView = linearLayout.findViewById(R.id.description);
}
}
/**
* Initialize the adapter with a list of demo items.
*
* @param demoItems
*/
public DemoAdapter(List demoItems) {
this.demoItems = demoItems;
}
/**
* Get the DemoItem and binds it to the recycled view.
*