diff --git a/.changeset/config.json b/.changeset/config.json index 417755c..b045dbb 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -9,6 +9,7 @@ [ "@capacitor-mlkit/barcode-scanning", "@capacitor-mlkit/face-detection", + "@capacitor-mlkit/selfie-segmentation", "@capacitor-mlkit/translation" ] ], diff --git a/.changeset/fifty-mirrors-teach.md b/.changeset/fifty-mirrors-teach.md new file mode 100644 index 0000000..f8de225 --- /dev/null +++ b/.changeset/fifty-mirrors-teach.md @@ -0,0 +1,5 @@ +--- +'@capacitor-mlkit/selfie-segmentation': minor +--- + +Initial release 🎉 diff --git a/README.md b/README.md index f202fa5..95f7dc4 100644 --- a/README.md +++ b/README.md @@ -47,11 +47,12 @@ Click on the name of the desired plugin under the [`Plugins`](#plugins) section ## Plugins -| Name | Package | Version | Downloads | -| ----------------------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [Barcode Scanning](./packages/barcode-scanning) | `@capacitor-mlkit/barcode-scanning` | [![npm badge](https://img.shields.io/npm/v/@capacitor-mlkit/barcode-scanning?style=flat-square)](https://www.npmjs.com/package/@capacitor-mlkit/barcode-scanning) | [![npm downloads](https://img.shields.io/npm/dw/@capacitor-mlkit/barcode-scanning?style=flat-square)](https://www.npmjs.com/package/@capacitor-mlkit/barcode-scanning) | -| [Face Detection](./packages/face-detection) | `@capacitor-mlkit/face-detection` | [![npm badge](https://img.shields.io/npm/v/@capacitor-mlkit/face-detection?style=flat-square)](https://www.npmjs.com/package/@capacitor-mlkit/face-detection) | [![npm downloads](https://img.shields.io/npm/dw/@capacitor-mlkit/face-detection?style=flat-square)](https://www.npmjs.com/package/@capacitor-mlkit/face-detection) | -| [Translation](./packages/translation) | `@capacitor-mlkit/translation` | [![npm badge](https://img.shields.io/npm/v/@capacitor-mlkit/translation?style=flat-square)](https://www.npmjs.com/package/@capacitor-mlkit/translation) | [![npm downloads](https://img.shields.io/npm/dw/@capacitor-mlkit/translation?style=flat-square)](https://www.npmjs.com/package/@capacitor-mlkit/translation) | +| Name | Package | Version | Downloads | +| ----------------------------------------------------- | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [Barcode Scanning](./packages/barcode-scanning) | `@capacitor-mlkit/barcode-scanning` | [![npm badge](https://img.shields.io/npm/v/@capacitor-mlkit/barcode-scanning?style=flat-square)](https://www.npmjs.com/package/@capacitor-mlkit/barcode-scanning) | [![npm downloads](https://img.shields.io/npm/dw/@capacitor-mlkit/barcode-scanning?style=flat-square)](https://www.npmjs.com/package/@capacitor-mlkit/barcode-scanning) | +| [Face Detection](./packages/face-detection) | `@capacitor-mlkit/face-detection` | [![npm badge](https://img.shields.io/npm/v/@capacitor-mlkit/face-detection?style=flat-square)](https://www.npmjs.com/package/@capacitor-mlkit/face-detection) | [![npm downloads](https://img.shields.io/npm/dw/@capacitor-mlkit/face-detection?style=flat-square)](https://www.npmjs.com/package/@capacitor-mlkit/face-detection) | +| [Selfie Segmentation](./packages/selfie-segmentation) | `@capacitor-mlkit/selfie-segmentation` | [![npm badge](https://img.shields.io/npm/v/@capacitor-mlkit/selfie-segmentation?style=flat-square)](https://www.npmjs.com/package/@capacitor-mlkit/selfie-segmentation) | [![npm downloads](https://img.shields.io/npm/dw/@capacitor-mlkit/selfie-segmentation?style=flat-square)](https://www.npmjs.com/package/@capacitor-mlkit/selfie-segmentation) | +| [Translation](./packages/translation) | `@capacitor-mlkit/translation` | [![npm badge](https://img.shields.io/npm/v/@capacitor-mlkit/translation?style=flat-square)](https://www.npmjs.com/package/@capacitor-mlkit/translation) | [![npm downloads](https://img.shields.io/npm/dw/@capacitor-mlkit/translation?style=flat-square)](https://www.npmjs.com/package/@capacitor-mlkit/translation) | ## Changelogs diff --git a/package-lock.json b/package-lock.json index de29fb8..1c182e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -78,6 +78,10 @@ "resolved": "packages/face-detection", "link": true }, + "node_modules/@capacitor-mlkit/selfie-segmentation": { + "resolved": "packages/selfie-segmentation", + "link": true + }, "node_modules/@capacitor-mlkit/translation": { "resolved": "packages/translation", "link": true @@ -6210,6 +6214,52 @@ "node": ">=10.13.0" } }, + "packages/selfie-segmentation": { + "name": "@capacitor-mlkit/selfie-segmentation", + "version": "5.1.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/capawesome-team/" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/capawesome" + } + ], + "license": "Apache-2.0", + "devDependencies": { + "@capacitor/android": "5.0.0", + "@capacitor/cli": "5.0.0", + "@capacitor/core": "5.0.0", + "@capacitor/docgen": "0.2.1", + "@capacitor/ios": "5.0.0", + "@ionic/eslint-config": "0.3.0", + "@ionic/swiftlint-config": "1.1.2", + "eslint": "7.32.0", + "prettier": "2.3.2", + "prettier-plugin-java": "1.0.2", + "rimraf": "3.0.2", + "rollup": "2.77.2", + "swiftlint": "1.0.1", + "typescript": "4.1.5" + }, + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } + }, + "packages/selfie-segmentation/node_modules/prettier": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", + "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, "packages/translation": { "name": "@capacitor-mlkit/translation", "version": "5.1.0", diff --git a/packages/barcode-scanning/README.md b/packages/barcode-scanning/README.md index fa49747..f0e1213 100644 --- a/packages/barcode-scanning/README.md +++ b/packages/barcode-scanning/README.md @@ -1,6 +1,6 @@ # @capacitor-mlkit/barcode-scanning -Unofficial Capacitor plugin for [ML Kit Barcode scanning](https://developers.google.com/ml-kit/vision/barcode-scanning).[^1][^2] +Unofficial Capacitor plugin for [ML Kit Barcode Scanning](https://developers.google.com/ml-kit/vision/barcode-scanning).[^1][^2] ## Features diff --git a/packages/barcode-scanning/package.json b/packages/barcode-scanning/package.json index e116602..a32dab9 100644 --- a/packages/barcode-scanning/package.json +++ b/packages/barcode-scanning/package.json @@ -1,7 +1,7 @@ { "name": "@capacitor-mlkit/barcode-scanning", "version": "5.1.0", - "description": "Capacitor plugin for ML Kit Barcode scanning.", + "description": "Capacitor plugin for ML Kit Barcode Scanning.", "main": "dist/plugin.cjs.js", "module": "dist/esm/index.js", "types": "dist/esm/index.d.ts", @@ -21,7 +21,7 @@ "url": "git+https://github.com/capawesome-team/capacitor-mlkit.git" }, "bugs": { - "url": "https://github.com/capawesome-team/capacitor-mlkit.git/issues" + "url": "https://github.com/capawesome-team/capacitor-mlkit/issues" }, "funding": [ { diff --git a/packages/face-detection/README.md b/packages/face-detection/README.md index 4edebbe..27c08ae 100644 --- a/packages/face-detection/README.md +++ b/packages/face-detection/README.md @@ -39,7 +39,7 @@ A working example can be found here: [robingenz/capacitor-mlkit-plugin-demo](htt import { FaceDetection, PerformanceMode, LandmarkMode, ContourMode, ClassificationMode } from '@capacitor-mlkit/face-detection'; const processImage = async () => { - await FaceDetection.processImage({ + const { faces } = await FaceDetection.processImage({ path: 'path/to/image.jpg', performanceMode: PerformanceMode.Fast, landmarkMode: LandmarkMode.All, @@ -48,6 +48,7 @@ const processImage = async () => { minFaceSize: 0.1, enableTracking: false, }); + return faces; }; ``` diff --git a/packages/face-detection/package.json b/packages/face-detection/package.json index b1ae361..1057c77 100644 --- a/packages/face-detection/package.json +++ b/packages/face-detection/package.json @@ -21,7 +21,7 @@ "url": "git+https://github.com/capawesome-team/capacitor-mlkit.git" }, "bugs": { - "url": "https://github.com/capawesome-team/capacitor-mlkit.git/issues" + "url": "https://github.com/capawesome-team/capacitor-mlkit/issues" }, "funding": [ { diff --git a/packages/selfie-segmentation/.eslintignore b/packages/selfie-segmentation/.eslintignore new file mode 100644 index 0000000..9d0b71a --- /dev/null +++ b/packages/selfie-segmentation/.eslintignore @@ -0,0 +1,2 @@ +build +dist diff --git a/packages/selfie-segmentation/.gitignore b/packages/selfie-segmentation/.gitignore new file mode 100644 index 0000000..70ccbf7 --- /dev/null +++ b/packages/selfie-segmentation/.gitignore @@ -0,0 +1,61 @@ +# node files +dist +node_modules + +# iOS files +Pods +Podfile.lock +Build +xcuserdata + +# macOS files +.DS_Store + + + +# Based on Android gitignore template: https://github.com/github/gitignore/blob/HEAD/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 + +# 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 diff --git a/packages/selfie-segmentation/.prettierignore b/packages/selfie-segmentation/.prettierignore new file mode 100644 index 0000000..9d0b71a --- /dev/null +++ b/packages/selfie-segmentation/.prettierignore @@ -0,0 +1,2 @@ +build +dist diff --git a/packages/selfie-segmentation/.prettierrc.js b/packages/selfie-segmentation/.prettierrc.js new file mode 100644 index 0000000..9414485 --- /dev/null +++ b/packages/selfie-segmentation/.prettierrc.js @@ -0,0 +1,2 @@ +const config = require('../../.prettierrc.js'); +module.exports = config; diff --git a/packages/selfie-segmentation/BREAKING.md b/packages/selfie-segmentation/BREAKING.md new file mode 100644 index 0000000..92d48a3 --- /dev/null +++ b/packages/selfie-segmentation/BREAKING.md @@ -0,0 +1,3 @@ +# Breaking Changes + +This is a comprehensive list of the breaking changes introduced in the major version releases of Capacitor ML Kit Selfie Segmentation plugin. diff --git a/packages/selfie-segmentation/CHANGELOG.md b/packages/selfie-segmentation/CHANGELOG.md new file mode 100644 index 0000000..825c32f --- /dev/null +++ b/packages/selfie-segmentation/CHANGELOG.md @@ -0,0 +1 @@ +# Changelog diff --git a/packages/selfie-segmentation/CONTRIBUTING.md b/packages/selfie-segmentation/CONTRIBUTING.md new file mode 100644 index 0000000..3f87518 --- /dev/null +++ b/packages/selfie-segmentation/CONTRIBUTING.md @@ -0,0 +1,52 @@ +# Contributing + +This guide provides instructions for contributing to this Capacitor plugin. + +## Developing + +### Local Setup + +1. Fork and clone the repo. +1. Install the dependencies. + + ```shell + npm install + ``` + +1. Install SwiftLint if you're on macOS. + + ```shell + brew install swiftlint + ``` + +### Scripts + +#### `npm run build` + +Build the plugin web assets and generate plugin API documentation using [`@capacitor/docgen`](https://github.com/ionic-team/capacitor-docgen). + +It will compile the TypeScript code from `src/` into ESM JavaScript in `dist/esm/`. These files are used in apps with bundlers when your plugin is imported. + +Then, Rollup will bundle the code into a single file at `dist/plugin.js`. This file is used in apps without bundlers by including it as a script in `index.html`. + +#### `npm run verify` + +Build and validate the web and native projects. + +This is useful to run in CI to verify that the plugin builds for all platforms. + +#### `npm run lint` / `npm run fmt` + +Check formatting and code quality, autoformat/autofix if possible. + +This template is integrated with ESLint, Prettier, and SwiftLint. Using these tools is completely optional, but the [Capacitor Community](https://github.com/capacitor-community/) strives to have consistent code style and structure for easier cooperation. + +## Publishing + +There is a `prepublishOnly` hook in `package.json` which prepares the plugin before publishing, so all you need to do is run: + +```shell +npm publish +``` + +> **Note**: The [`files`](https://docs.npmjs.com/cli/v7/configuring-npm/package-json#files) array in `package.json` specifies which files get published. If you rename files/directories or add files elsewhere, you may need to update it. diff --git a/packages/selfie-segmentation/CapacitorMlkitSelfieSegmentation.podspec b/packages/selfie-segmentation/CapacitorMlkitSelfieSegmentation.podspec new file mode 100644 index 0000000..2ff7a08 --- /dev/null +++ b/packages/selfie-segmentation/CapacitorMlkitSelfieSegmentation.podspec @@ -0,0 +1,19 @@ +require 'json' + +package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) + +Pod::Spec.new do |s| + s.name = 'CapacitorMlkitSelfieSegmentation' + s.version = package['version'] + s.summary = package['description'] + s.license = package['license'] + s.homepage = package['repository']['url'] + s.author = package['author'] + s.source = { :git => package['repository']['url'], :tag => s.version.to_s } + s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}' + s.ios.deployment_target = '13.0' + s.dependency 'Capacitor' + s.dependency 'GoogleMLKit/SegmentationSelfie', '4.0.0' + s.swift_version = '5.1' + s.static_framework = true +end diff --git a/packages/selfie-segmentation/LICENSE b/packages/selfie-segmentation/LICENSE new file mode 100644 index 0000000..99bab9d --- /dev/null +++ b/packages/selfie-segmentation/LICENSE @@ -0,0 +1,201 @@ + 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 2023 Robin Genz + + 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. diff --git a/packages/selfie-segmentation/README.md b/packages/selfie-segmentation/README.md new file mode 100644 index 0000000..4057186 --- /dev/null +++ b/packages/selfie-segmentation/README.md @@ -0,0 +1,112 @@ +# @capacitor-mlkit/selfie-segmentation + +Unofficial Capacitor plugin for [ML Kit Selfie Segmentation](https://developers.google.com/ml-kit/vision/selfie-segmentation).[^1] + +## Installation + +```bash +npm install @capacitor-mlkit/selfie-segmentation +npx cap sync +``` + +#### Variables + +This plugin will use the following project variables (defined in your app’s `variables.gradle` file): + +- `$mlkitSelfieSegmentationVersion` version of `com.google.mlkit:segmentation-selfie` (default: `16.0.0-beta4`) + +## Configuration + +No configuration required for this plugin. + +## Demo + +A working example can be found here: [robingenz/capacitor-mlkit-plugin-demo](https://github.com/robingenz/capacitor-mlkit-plugin-demo) + +## Usage + +```typescript +import { SelfieSegmentation } from '@capacitor-mlkit/selfie-segmentation'; + +const processImage = async () => { + const { path } = await SelfieSegmentation.processImage({ + path: 'path/to/image.jpg', + confidence: 0.7, + }); + return path; +}; +``` + +## API + + + +* [`processImage(...)`](#processimage) +* [Interfaces](#interfaces) + + + + + + +### processImage(...) + +```typescript +processImage(options: ProcessImageOptions) => Promise +``` + +Performs segmentation on an input image. + +Only available on Android and iOS. + +| Param | Type | +| ------------- | ------------------------------------------------------------------- | +| **`options`** | ProcessImageOptions | + +**Returns:** Promise<ProcessImageResult> + +**Since:** 5.2.0 + +-------------------- + + +### Interfaces + + +#### ProcessImageResult + +| Prop | Type | Description | Since | +| ------------ | ------------------- | ------------------------------------- | ----- | +| **`path`** | string | The path to the segmented image file. | 5.2.0 | +| **`width`** | number | Returns the width of the image file. | 5.2.0 | +| **`height`** | number | Returns the height of the image file. | 5.2.0 | + + +#### ProcessImageOptions + +| Prop | Type | Description | Default | Since | +| ---------------- | ------------------- | ----------------------------------------------------------------------------------------- | ---------------- | ----- | +| **`path`** | string | The local path to the image file. | | 5.2.0 | +| **`width`** | number | Scale the image to this width. If no `height` is given, it will respect the aspect ratio. | | 5.2.0 | +| **`height`** | number | Scale the image to this height. If no `width` is given, it will respect the aspect ratio. | | 5.2.0 | +| **`confidence`** | number | Sets the confidence threshold. | 0.9 | 5.2.0 | + + + +## Terms & Privacy + +This plugin uses the [Google ML Kit](https://developers.google.com/ml-kit): + +- [Terms & Privacy](https://developers.google.com/ml-kit/terms) +- [Android Data Disclosure](https://developers.google.com/ml-kit/android-data-disclosure) +- [iOS Data Disclosure](https://developers.google.com/ml-kit/ios-data-disclosure) + +## Changelog + +See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-mlkit/blob/main/packages/selfie-segmentation/CHANGELOG.md). + +## License + +See [LICENSE](https://github.com/capawesome-team/capacitor-mlkit/blob/main/packages/selfie-segmentation/LICENSE). + +[^1]: This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. diff --git a/packages/selfie-segmentation/android/.gitignore b/packages/selfie-segmentation/android/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/packages/selfie-segmentation/android/.gitignore @@ -0,0 +1 @@ +/build diff --git a/packages/selfie-segmentation/android/build.gradle b/packages/selfie-segmentation/android/build.gradle new file mode 100644 index 0000000..6df5175 --- /dev/null +++ b/packages/selfie-segmentation/android/build.gradle @@ -0,0 +1,60 @@ +ext { + junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2' + androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.6.1' + androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.5' + androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.5.1' + mlkitSelfieSegmentationVersion = project.hasProperty('mlkitSelfieSegmentationVersion') ? rootProject.ext.mlkitSelfieSegmentationVersion : '16.0.0-beta4' +} + +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:8.0.0' + } +} + +apply plugin: 'com.android.library' + +android { + namespace "io.capawesome.capacitorjs.plugins.mlkit.selfiesegmentation" + compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 33 + defaultConfig { + minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22 + targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 33 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + lintOptions { + abortOnError false + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } +} + +repositories { + google() + mavenCentral() +} + + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation project(':capacitor-android') + implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" + implementation "com.google.mlkit:segmentation-selfie:$mlkitSelfieSegmentationVersion" + testImplementation "junit:junit:$junitVersion" + androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" + androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" +} diff --git a/packages/selfie-segmentation/android/gradle.properties b/packages/selfie-segmentation/android/gradle.properties new file mode 100644 index 0000000..2e87c52 --- /dev/null +++ b/packages/selfie-segmentation/android/gradle.properties @@ -0,0 +1,22 @@ +# 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 diff --git a/packages/selfie-segmentation/android/gradle/wrapper/gradle-wrapper.jar b/packages/selfie-segmentation/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..ccebba7 Binary files /dev/null and b/packages/selfie-segmentation/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/packages/selfie-segmentation/android/gradle/wrapper/gradle-wrapper.properties b/packages/selfie-segmentation/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..761b8f0 --- /dev/null +++ b/packages/selfie-segmentation/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-all.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/selfie-segmentation/android/gradlew b/packages/selfie-segmentation/android/gradlew new file mode 100755 index 0000000..79a61d4 --- /dev/null +++ b/packages/selfie-segmentation/android/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# 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 ;; #( + MSYS* | 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" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/packages/selfie-segmentation/android/gradlew.bat b/packages/selfie-segmentation/android/gradlew.bat new file mode 100644 index 0000000..ead6177 --- /dev/null +++ b/packages/selfie-segmentation/android/gradlew.bat @@ -0,0 +1,90 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@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=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@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="-Xmx64m" "-Xms64m" + +@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 execute + +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 execute + +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 + +: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 %* + +: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 diff --git a/packages/selfie-segmentation/android/proguard-rules.pro b/packages/selfie-segmentation/android/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/packages/selfie-segmentation/android/proguard-rules.pro @@ -0,0 +1,21 @@ +# 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 diff --git a/packages/selfie-segmentation/android/settings.gradle b/packages/selfie-segmentation/android/settings.gradle new file mode 100644 index 0000000..6485002 --- /dev/null +++ b/packages/selfie-segmentation/android/settings.gradle @@ -0,0 +1,2 @@ +include ':capacitor-android' +project(':capacitor-android').projectDir = new File('../../../node_modules/@capacitor/android/capacitor') diff --git a/packages/selfie-segmentation/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java b/packages/selfie-segmentation/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java new file mode 100644 index 0000000..58020e1 --- /dev/null +++ b/packages/selfie-segmentation/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.getcapacitor.android; + +import static org.junit.Assert.*; + +import android.content.Context; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + assertEquals("com.getcapacitor.android", appContext.getPackageName()); + } +} diff --git a/packages/selfie-segmentation/android/src/main/AndroidManifest.xml b/packages/selfie-segmentation/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a2f47b6 --- /dev/null +++ b/packages/selfie-segmentation/android/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/packages/selfie-segmentation/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/ProcessImageResultCallback.java b/packages/selfie-segmentation/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/ProcessImageResultCallback.java new file mode 100644 index 0000000..f1febc2 --- /dev/null +++ b/packages/selfie-segmentation/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/ProcessImageResultCallback.java @@ -0,0 +1,11 @@ +package io.capawesome.capacitorjs.plugins.mlkit.selfiesegmentation; + +import io.capawesome.capacitorjs.plugins.mlkit.selfiesegmentation.classes.ProcessImageResult; + +public interface ProcessImageResultCallback { + void success(ProcessImageResult result); + + void cancel(); + + void error(Exception exception); +} diff --git a/packages/selfie-segmentation/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/SelfieSegmentation.java b/packages/selfie-segmentation/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/SelfieSegmentation.java new file mode 100644 index 0000000..3c7e6e5 --- /dev/null +++ b/packages/selfie-segmentation/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/SelfieSegmentation.java @@ -0,0 +1,132 @@ +package io.capawesome.capacitorjs.plugins.mlkit.selfiesegmentation; + +import android.annotation.SuppressLint; +import android.graphics.Bitmap; +import android.net.Uri; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.mlkit.vision.common.InputImage; +import com.google.mlkit.vision.segmentation.Segmentation; +import com.google.mlkit.vision.segmentation.Segmenter; +import com.google.mlkit.vision.segmentation.selfie.SelfieSegmenterOptions; +import io.capawesome.capacitorjs.plugins.mlkit.selfiesegmentation.classes.ProcessImageOptions; +import io.capawesome.capacitorjs.plugins.mlkit.selfiesegmentation.classes.ProcessImageResult; +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Objects; + +public class SelfieSegmentation { + + @NonNull + private final SelfieSegmentationPlugin plugin; + + public SelfieSegmentation(@NonNull SelfieSegmentationPlugin plugin) { + this.plugin = plugin; + } + + @Nullable + public InputImage createInputImageFromFilePath(@NonNull String path) { + try { + return InputImage.fromFilePath(this.plugin.getContext(), Uri.parse(path)); + } catch (Exception exception) { + return null; + } + } + + public void processImage(ProcessImageOptions options, ProcessImageResultCallback callback) { + InputImage inputImage = options.getInputImage(); + Float threshold = options.getConfidence(); + + SelfieSegmenterOptions.Builder builder = new SelfieSegmenterOptions.Builder(); + builder.setDetectorMode(SelfieSegmenterOptions.SINGLE_IMAGE_MODE); + SelfieSegmenterOptions selfieSegmenterOptions = builder.build(); + + final Segmenter segmenter = Segmentation.getClient(selfieSegmenterOptions); + + plugin + .getActivity() + .runOnUiThread( + () -> + segmenter + .process(inputImage) + .addOnSuccessListener( + segmentationMask -> { + segmenter.close(); + + ByteBuffer mask = segmentationMask.getBuffer(); + + Bitmap bitmap = inputImage.getBitmapInternal(); + Objects.requireNonNull(bitmap).setHasAlpha(true); + + ByteBuffer pixels = ByteBuffer.allocateDirect(bitmap.getAllocationByteCount()); + bitmap.copyPixelsToBuffer(pixels); + + final boolean bigEndian = pixels.order() == ByteOrder.BIG_ENDIAN; + final int ALPHA = bigEndian ? 3 : 0; + final int RED = bigEndian ? 2 : 1; + final int GREEN = bigEndian ? 1 : 2; + final int BLUE = bigEndian ? 0 : 3; + + for (int i = 0; i < pixels.capacity() >> 2; i++) { + float confidence = mask.getFloat(); + + if (confidence >= threshold) { + byte red = pixels.get((i << 2) + RED); + byte green = pixels.get((i << 2) + GREEN); + byte blue = pixels.get((i << 2) + BLUE); + + pixels.put((i << 2) + ALPHA, (byte) (0xff)); + pixels.put((i << 2) + RED, (byte) (red * confidence)); + pixels.put((i << 2) + GREEN, (byte) (green * confidence)); + pixels.put((i << 2) + BLUE, (byte) (blue * confidence)); + } else { + pixels.putInt(i << 2, 0x00000000); // transparent + } + } + + bitmap.copyPixelsFromBuffer(pixels.rewind()); + + // Create an image file name + @SuppressLint("SimpleDateFormat") + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); + String imageFileName = "PNG_" + timeStamp + "_"; + + try { + File image = File.createTempFile(imageFileName, ".png"); + + OutputStream stream = new FileOutputStream(image); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); + stream.close(); + + ProcessImageResult result = new ProcessImageResult( + image.getAbsolutePath(), + bitmap.getWidth(), + bitmap.getHeight() + ); + + callback.success(result); + } catch (Exception exception) { + callback.error(exception); + } + } + ) + .addOnCanceledListener( + () -> { + segmenter.close(); + callback.cancel(); + } + ) + .addOnFailureListener( + exception -> { + segmenter.close(); + callback.error(exception); + } + ) + ); + } +} diff --git a/packages/selfie-segmentation/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/SelfieSegmentationPlugin.java b/packages/selfie-segmentation/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/SelfieSegmentationPlugin.java new file mode 100644 index 0000000..fd16c45 --- /dev/null +++ b/packages/selfie-segmentation/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/SelfieSegmentationPlugin.java @@ -0,0 +1,88 @@ +package io.capawesome.capacitorjs.plugins.mlkit.selfiesegmentation; + +import com.getcapacitor.Logger; +import com.getcapacitor.Plugin; +import com.getcapacitor.PluginCall; +import com.getcapacitor.PluginMethod; +import com.getcapacitor.annotation.CapacitorPlugin; +import com.google.mlkit.vision.common.InputImage; +import io.capawesome.capacitorjs.plugins.mlkit.selfiesegmentation.classes.ProcessImageOptions; +import io.capawesome.capacitorjs.plugins.mlkit.selfiesegmentation.classes.ProcessImageResult; + +@CapacitorPlugin(name = "SelfieSegmentation") +public class SelfieSegmentationPlugin extends Plugin { + + public static final String TAG = "SelfieSegmentation"; + + public static final String ERROR_PROCESS_IMAGE_CANCELED = "processImage canceled."; + public static final String ERROR_PATH_MISSING = "path must be provided."; + public static final String ERROR_LOAD_IMAGE_FAILED = "The image could not be loaded."; + + public static final float CONFIDENCE = 0.9f; + + private SelfieSegmentation implementation; + + @Override + public void load() { + try { + implementation = new SelfieSegmentation(this); + } catch (Exception exception) { + Logger.error(TAG, exception.getMessage(), exception); + } + } + + @PluginMethod + public void processImage(PluginCall call) { + try { + String path = call.getString("path", null); + if (path == null) { + call.reject(ERROR_PATH_MISSING); + return; + } + + Integer width = call.getInt("width", null); + Integer height = call.getInt("height", null); + + Float confidence = call.getFloat("confidence", CONFIDENCE); + + InputImage image = implementation.createInputImageFromFilePath(path); + if (image == null) { + call.reject(ERROR_LOAD_IMAGE_FAILED); + return; + } + ProcessImageOptions options = new ProcessImageOptions(image, width, height, confidence); + + implementation.processImage( + options, + new ProcessImageResultCallback() { + @Override + public void success(ProcessImageResult result) { + try { + call.resolve(result.toJSObject()); + } catch (Exception exception) { + String message = exception.getMessage(); + Logger.error(TAG, message, exception); + call.reject(message); + } + } + + @Override + public void cancel() { + call.reject(ERROR_PROCESS_IMAGE_CANCELED); + } + + @Override + public void error(Exception exception) { + String message = exception.getMessage(); + Logger.error(TAG, message, exception); + call.reject(message); + } + } + ); + } catch (Exception exception) { + String message = exception.getMessage(); + Logger.error(TAG, message, exception); + call.reject(message); + } + } +} diff --git a/packages/selfie-segmentation/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/classes/ProcessImageOptions.java b/packages/selfie-segmentation/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/classes/ProcessImageOptions.java new file mode 100644 index 0000000..79755ab --- /dev/null +++ b/packages/selfie-segmentation/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/classes/ProcessImageOptions.java @@ -0,0 +1,47 @@ +package io.capawesome.capacitorjs.plugins.mlkit.selfiesegmentation.classes; + +import android.graphics.Bitmap; +import com.google.mlkit.vision.common.InputImage; +import java.util.Objects; + +public class ProcessImageOptions { + + private final InputImage inputImage; + + private final Float confidence; + + public ProcessImageOptions(InputImage inputImage, Integer width, Integer height, Float confidence) { + this.inputImage = scaledImage(inputImage, width, height); + + this.confidence = confidence; + } + + public InputImage getInputImage() { + return inputImage; + } + + public Float getConfidence() { + return confidence; + } + + private InputImage scaledImage(InputImage inputImage, Integer width, Integer height) { + float scaleX = (width != null) ? width * 1f / inputImage.getWidth() : 0f; + float scaleY = (height != null) ? height * 1f / inputImage.getHeight() : 0f; + + if (scaleX > 0f || scaleY > 0f) { + if (scaleX > 0f && scaleY == 0f) scaleY = scaleX; else if (scaleY > 0f && scaleX == 0f) scaleX = scaleY; + + return InputImage.fromBitmap( + Bitmap.createScaledBitmap( + Objects.requireNonNull(inputImage.getBitmapInternal()), + (int) (inputImage.getWidth() * scaleX), + (int) (inputImage.getHeight() * scaleY), + false + ), + inputImage.getRotationDegrees() + ); + } + + return inputImage; + } +} diff --git a/packages/selfie-segmentation/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/classes/ProcessImageResult.java b/packages/selfie-segmentation/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/classes/ProcessImageResult.java new file mode 100644 index 0000000..ffe3363 --- /dev/null +++ b/packages/selfie-segmentation/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/classes/ProcessImageResult.java @@ -0,0 +1,29 @@ +package io.capawesome.capacitorjs.plugins.mlkit.selfiesegmentation.classes; + +import com.getcapacitor.JSObject; + +public class ProcessImageResult { + + private final String imagePath; + + private final int width; + private final int height; + + public ProcessImageResult(String imagePath, int width, int height) { + this.imagePath = imagePath; + + this.width = width; + this.height = height; + } + + public JSObject toJSObject() { + JSObject result = new JSObject(); + + result.put("path", imagePath); + + result.put("width", width); + result.put("height", height); + + return result; + } +} diff --git a/packages/selfie-segmentation/android/src/main/res/.gitkeep b/packages/selfie-segmentation/android/src/main/res/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/packages/selfie-segmentation/android/src/test/java/com/getcapacitor/ExampleUnitTest.java b/packages/selfie-segmentation/android/src/test/java/com/getcapacitor/ExampleUnitTest.java new file mode 100644 index 0000000..a0fed0c --- /dev/null +++ b/packages/selfie-segmentation/android/src/test/java/com/getcapacitor/ExampleUnitTest.java @@ -0,0 +1,18 @@ +package com.getcapacitor; + +import static org.junit.Assert.*; + +import org.junit.Test; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} diff --git a/packages/selfie-segmentation/ios/Plugin.xcodeproj/project.pbxproj b/packages/selfie-segmentation/ios/Plugin.xcodeproj/project.pbxproj new file mode 100644 index 0000000..865a496 --- /dev/null +++ b/packages/selfie-segmentation/ios/Plugin.xcodeproj/project.pbxproj @@ -0,0 +1,645 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 03FC29A292ACC40490383A1F /* Pods_Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */; }; + 20C0B05DCFC8E3958A738AF2 /* Pods_PluginTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */; }; + 2F98D68224C9AAE500613A4C /* SelfieSegmentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F98D68124C9AAE400613A4C /* SelfieSegmentation.swift */; }; + 4A4E0DE52AA478A800BED263 /* ProcessImageOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A4E0DE32AA478A800BED263 /* ProcessImageOptions.swift */; }; + 4A4E0DE62AA478A800BED263 /* ProcessImageResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A4E0DE42AA478A800BED263 /* ProcessImageResult.swift */; }; + 50ADFF92201F53D600D50D53 /* Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ADFF88201F53D600D50D53 /* Plugin.framework */; }; + 50ADFF97201F53D600D50D53 /* SelfieSegmentationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFF96201F53D600D50D53 /* SelfieSegmentationTests.swift */; }; + 50ADFF99201F53D600D50D53 /* SelfieSegmentationPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 50ADFF8B201F53D600D50D53 /* SelfieSegmentationPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 50ADFFA42020D75100D50D53 /* Capacitor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ADFFA52020D75100D50D53 /* Capacitor.framework */; }; + 50ADFFA82020EE4F00D50D53 /* SelfieSegmentationPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFFA72020EE4F00D50D53 /* SelfieSegmentationPlugin.m */; }; + 50E1A94820377CB70090CE1A /* SelfieSegmentationPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E1A94720377CB70090CE1A /* SelfieSegmentationPlugin.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 50ADFF93201F53D600D50D53 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 50ADFF7F201F53D600D50D53 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 50ADFF87201F53D600D50D53; + remoteInfo = Plugin; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 2F98D68124C9AAE400613A4C /* SelfieSegmentation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelfieSegmentation.swift; sourceTree = ""; }; + 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4A4E0DE32AA478A800BED263 /* ProcessImageOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProcessImageOptions.swift; path = Classes/ProcessImageOptions.swift; sourceTree = ""; }; + 4A4E0DE42AA478A800BED263 /* ProcessImageResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProcessImageResult.swift; path = Classes/ProcessImageResult.swift; sourceTree = ""; }; + 50ADFF88201F53D600D50D53 /* Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 50ADFF8B201F53D600D50D53 /* SelfieSegmentationPlugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SelfieSegmentationPlugin.h; sourceTree = ""; }; + 50ADFF8C201F53D600D50D53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 50ADFF91201F53D600D50D53 /* PluginTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PluginTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 50ADFF96201F53D600D50D53 /* SelfieSegmentationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfieSegmentationTests.swift; sourceTree = ""; }; + 50ADFF98201F53D600D50D53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 50ADFFA52020D75100D50D53 /* Capacitor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Capacitor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 50ADFFA72020EE4F00D50D53 /* SelfieSegmentationPlugin.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SelfieSegmentationPlugin.m; sourceTree = ""; }; + 50E1A94720377CB70090CE1A /* SelfieSegmentationPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelfieSegmentationPlugin.swift; sourceTree = ""; }; + 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Plugin/Pods-Plugin.debug.xcconfig"; sourceTree = ""; }; + 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.release.xcconfig"; path = "Pods/Target Support Files/Pods-Plugin/Pods-Plugin.release.xcconfig"; sourceTree = ""; }; + 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PluginTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.debug.xcconfig"; sourceTree = ""; }; + F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PluginTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.release.xcconfig"; sourceTree = ""; }; + F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PluginTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 50ADFF84201F53D600D50D53 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFFA42020D75100D50D53 /* Capacitor.framework in Frameworks */, + 03FC29A292ACC40490383A1F /* Pods_Plugin.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50ADFF8E201F53D600D50D53 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFF92201F53D600D50D53 /* Plugin.framework in Frameworks */, + 20C0B05DCFC8E3958A738AF2 /* Pods_PluginTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4A4E0DE22AA4788400BED263 /* Classes */ = { + isa = PBXGroup; + children = ( + 4A4E0DE32AA478A800BED263 /* ProcessImageOptions.swift */, + 4A4E0DE42AA478A800BED263 /* ProcessImageResult.swift */, + ); + name = Classes; + sourceTree = ""; + }; + 50ADFF7E201F53D600D50D53 = { + isa = PBXGroup; + children = ( + 50ADFF8A201F53D600D50D53 /* Plugin */, + 50ADFF95201F53D600D50D53 /* PluginTests */, + 50ADFF89201F53D600D50D53 /* Products */, + 8C8E7744173064A9F6D438E3 /* Pods */, + A797B9EFA3DCEFEA1FBB66A9 /* Frameworks */, + ); + sourceTree = ""; + }; + 50ADFF89201F53D600D50D53 /* Products */ = { + isa = PBXGroup; + children = ( + 50ADFF88201F53D600D50D53 /* Plugin.framework */, + 50ADFF91201F53D600D50D53 /* PluginTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 50ADFF8A201F53D600D50D53 /* Plugin */ = { + isa = PBXGroup; + children = ( + 4A4E0DE22AA4788400BED263 /* Classes */, + 50E1A94720377CB70090CE1A /* SelfieSegmentationPlugin.swift */, + 2F98D68124C9AAE400613A4C /* SelfieSegmentation.swift */, + 50ADFF8B201F53D600D50D53 /* SelfieSegmentationPlugin.h */, + 50ADFFA72020EE4F00D50D53 /* SelfieSegmentationPlugin.m */, + 50ADFF8C201F53D600D50D53 /* Info.plist */, + ); + path = Plugin; + sourceTree = ""; + }; + 50ADFF95201F53D600D50D53 /* PluginTests */ = { + isa = PBXGroup; + children = ( + 50ADFF96201F53D600D50D53 /* SelfieSegmentationTests.swift */, + 50ADFF98201F53D600D50D53 /* Info.plist */, + ); + path = PluginTests; + sourceTree = ""; + }; + 8C8E7744173064A9F6D438E3 /* Pods */ = { + isa = PBXGroup; + children = ( + 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */, + 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */, + 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */, + F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + A797B9EFA3DCEFEA1FBB66A9 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 50ADFFA52020D75100D50D53 /* Capacitor.framework */, + 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */, + F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 50ADFF85201F53D600D50D53 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFF99201F53D600D50D53 /* SelfieSegmentationPlugin.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 50ADFF87201F53D600D50D53 /* Plugin */ = { + isa = PBXNativeTarget; + buildConfigurationList = 50ADFF9C201F53D600D50D53 /* Build configuration list for PBXNativeTarget "Plugin" */; + buildPhases = ( + AB5B3E54B4E897F32C2279DA /* [CP] Check Pods Manifest.lock */, + 50ADFF83201F53D600D50D53 /* Sources */, + 50ADFF84201F53D600D50D53 /* Frameworks */, + 50ADFF85201F53D600D50D53 /* Headers */, + 50ADFF86201F53D600D50D53 /* Resources */, + 322D095DAEE70E519D1CDC1C /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Plugin; + productName = Plugin; + productReference = 50ADFF88201F53D600D50D53 /* Plugin.framework */; + productType = "com.apple.product-type.framework"; + }; + 50ADFF90201F53D600D50D53 /* PluginTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 50ADFF9F201F53D600D50D53 /* Build configuration list for PBXNativeTarget "PluginTests" */; + buildPhases = ( + 0596884F929ED6F1DE134961 /* [CP] Check Pods Manifest.lock */, + 50ADFF8D201F53D600D50D53 /* Sources */, + 50ADFF8E201F53D600D50D53 /* Frameworks */, + 50ADFF8F201F53D600D50D53 /* Resources */, + 8E97F58B69A94C6503FC9C85 /* [CP] Embed Pods Frameworks */, + 6207E6C8C592D40A574BFFA7 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + 50ADFF94201F53D600D50D53 /* PBXTargetDependency */, + ); + name = PluginTests; + productName = PluginTests; + productReference = 50ADFF91201F53D600D50D53 /* PluginTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 50ADFF7F201F53D600D50D53 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1160; + ORGANIZATIONNAME = "Max Lynch"; + TargetAttributes = { + 50ADFF87201F53D600D50D53 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + }; + 50ADFF90201F53D600D50D53 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 50ADFF82201F53D600D50D53 /* Build configuration list for PBXProject "Plugin" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 50ADFF7E201F53D600D50D53; + productRefGroup = 50ADFF89201F53D600D50D53 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 50ADFF87201F53D600D50D53 /* Plugin */, + 50ADFF90201F53D600D50D53 /* PluginTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 50ADFF86201F53D600D50D53 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50ADFF8F201F53D600D50D53 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 0596884F929ED6F1DE134961 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-PluginTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 322D095DAEE70E519D1CDC1C /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Plugin/Pods-Plugin-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/MLKitSegmentationCommon/MLKitSegmentationCommonResources.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/MLKitSegmentationSelfie/MLKitSegmentationSelfieResources.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/MLKitXenoCommon/MLKitXenoResources.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MLKitSegmentationCommonResources.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MLKitSegmentationSelfieResources.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MLKitXenoResources.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Plugin/Pods-Plugin-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 6207E6C8C592D40A574BFFA7 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-PluginTests/Pods-PluginTests-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/MLKitSegmentationCommon/MLKitSegmentationCommonResources.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/MLKitSegmentationSelfie/MLKitSegmentationSelfieResources.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/MLKitXenoCommon/MLKitXenoResources.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MLKitSegmentationCommonResources.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MLKitSegmentationSelfieResources.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MLKitXenoResources.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PluginTests/Pods-PluginTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 8E97F58B69A94C6503FC9C85 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-PluginTests/Pods-PluginTests-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/Capacitor/Capacitor.framework", + "${BUILT_PRODUCTS_DIR}/CapacitorCordova/Cordova.framework", + "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", + "${BUILT_PRODUCTS_DIR}/GoogleDataTransport/GoogleDataTransport.framework", + "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac/GoogleToolboxForMac.framework", + "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", + "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", + "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Capacitor.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cordova.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleDataTransport.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleToolboxForMac.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PluginTests/Pods-PluginTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + AB5B3E54B4E897F32C2279DA /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Plugin-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 50ADFF83201F53D600D50D53 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4A4E0DE62AA478A800BED263 /* ProcessImageResult.swift in Sources */, + 50E1A94820377CB70090CE1A /* SelfieSegmentationPlugin.swift in Sources */, + 2F98D68224C9AAE500613A4C /* SelfieSegmentation.swift in Sources */, + 4A4E0DE52AA478A800BED263 /* ProcessImageOptions.swift in Sources */, + 50ADFFA82020EE4F00D50D53 /* SelfieSegmentationPlugin.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 50ADFF8D201F53D600D50D53 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 50ADFF97201F53D600D50D53 /* SelfieSegmentationTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 50ADFF94201F53D600D50D53 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 50ADFF87201F53D600D50D53 /* Plugin */; + targetProxy = 50ADFF93201F53D600D50D53 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 50ADFF9A201F53D600D50D53 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = ( + "\"${BUILT_PRODUCTS_DIR}/Capacitor\"", + "\"${BUILT_PRODUCTS_DIR}/CapacitorCordova\"", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 50ADFF9B201F53D600D50D53 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "\"${BUILT_PRODUCTS_DIR}/Capacitor\"", + "\"${BUILT_PRODUCTS_DIR}/CapacitorCordova\"", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 50ADFF9D201F53D600D50D53 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Plugin/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(FRAMEWORK_SEARCH_PATHS)\n$(FRAMEWORK_SEARCH_PATHS)\n$(FRAMEWORK_SEARCH_PATHS)"; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.Plugin; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 50ADFF9E201F53D600D50D53 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Plugin/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(FRAMEWORK_SEARCH_PATHS)"; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.Plugin; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 50ADFFA0201F53D600D50D53 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PluginTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.PluginTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 50ADFFA1201F53D600D50D53 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PluginTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.PluginTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 50ADFF82201F53D600D50D53 /* Build configuration list for PBXProject "Plugin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 50ADFF9A201F53D600D50D53 /* Debug */, + 50ADFF9B201F53D600D50D53 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 50ADFF9C201F53D600D50D53 /* Build configuration list for PBXNativeTarget "Plugin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 50ADFF9D201F53D600D50D53 /* Debug */, + 50ADFF9E201F53D600D50D53 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 50ADFF9F201F53D600D50D53 /* Build configuration list for PBXNativeTarget "PluginTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 50ADFFA0201F53D600D50D53 /* Debug */, + 50ADFFA1201F53D600D50D53 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 50ADFF7F201F53D600D50D53 /* Project object */; +} diff --git a/packages/selfie-segmentation/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/selfie-segmentation/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/packages/selfie-segmentation/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/selfie-segmentation/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/selfie-segmentation/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/packages/selfie-segmentation/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/selfie-segmentation/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme b/packages/selfie-segmentation/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme new file mode 100644 index 0000000..303f262 --- /dev/null +++ b/packages/selfie-segmentation/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/selfie-segmentation/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme b/packages/selfie-segmentation/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme new file mode 100644 index 0000000..3d8c88d --- /dev/null +++ b/packages/selfie-segmentation/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/selfie-segmentation/ios/Plugin.xcworkspace/contents.xcworkspacedata b/packages/selfie-segmentation/ios/Plugin.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..afad624 --- /dev/null +++ b/packages/selfie-segmentation/ios/Plugin.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/selfie-segmentation/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/selfie-segmentation/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/packages/selfie-segmentation/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/selfie-segmentation/ios/Plugin/Classes/ProcessImageOptions.swift b/packages/selfie-segmentation/ios/Plugin/Classes/ProcessImageOptions.swift new file mode 100644 index 0000000..a68f342 --- /dev/null +++ b/packages/selfie-segmentation/ios/Plugin/Classes/ProcessImageOptions.swift @@ -0,0 +1,64 @@ +import Foundation +import UIKit + +extension UIImage { + public func scaledImage(width: Int?, height: Int?) -> UIImage { + let newWidth: CGFloat + let newHeight: CGFloat + + if let width = width { + newWidth = CGFloat(width) + if let height = height { + newHeight = CGFloat(height) + } else { + let scaleFactor = newWidth / self.size.width + newHeight = self.size.height * scaleFactor + } + } else + if let height = height { + newHeight = CGFloat(height) + if let width = width { + newWidth = CGFloat(width) + } else { + let scaleFactor = newHeight / self.size.height + newWidth = self.size.width * scaleFactor + } + } else { + return self + } + + let newSize = CGSize(width: newWidth, height: newHeight) + + if newSize.width >= size.width && newSize.height >= size.height { + return self + } + + UIGraphicsBeginImageContextWithOptions(newSize, false, scale) + defer { UIGraphicsEndImageContext() } + draw(in: CGRect(origin: .zero, size: newSize)) + return UIGraphicsGetImageFromCurrentImageContext() ?? self + } +} + +@objc class ProcessImageOptions: NSObject { + private var image: UIImage + private var confidence: CGFloat + + init( + image: UIImage, + width: Int?, + height: Int?, + confidence: CGFloat + ) { + self.image = image.scaledImage(width: width, height: height) + self.confidence = confidence + } + + func getImage() -> UIImage { + return image + } + + func getConfidence() -> CGFloat { + return confidence + } +} diff --git a/packages/selfie-segmentation/ios/Plugin/Classes/ProcessImageResult.swift b/packages/selfie-segmentation/ios/Plugin/Classes/ProcessImageResult.swift new file mode 100644 index 0000000..626a5a7 --- /dev/null +++ b/packages/selfie-segmentation/ios/Plugin/Classes/ProcessImageResult.swift @@ -0,0 +1,30 @@ +import Foundation +import Capacitor + +@objc class ProcessImageResult: NSObject { + let image: UIImage + + init(image: UIImage) { + self.image = image + } + + func toJSObject() throws -> JSObject { + var result = JSObject() + + if let data = image.pngData() { + let uniqueFileNameWithExtension = UUID().uuidString + ".png" + var directory = URL(fileURLWithPath: NSTemporaryDirectory()) + if let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first { + directory = cachesDirectory + } + let url = directory.appendingPathComponent(uniqueFileNameWithExtension) + try data.write(to: url) + + result["path"] = url.absoluteString + result["width"] = Int(image.size.width) + result["height"] = Int(image.size.height) + } + + return result + } +} diff --git a/packages/selfie-segmentation/ios/Plugin/Info.plist b/packages/selfie-segmentation/ios/Plugin/Info.plist new file mode 100644 index 0000000..1007fd9 --- /dev/null +++ b/packages/selfie-segmentation/ios/Plugin/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/packages/selfie-segmentation/ios/Plugin/SelfieSegmentation.swift b/packages/selfie-segmentation/ios/Plugin/SelfieSegmentation.swift new file mode 100644 index 0000000..b536b62 --- /dev/null +++ b/packages/selfie-segmentation/ios/Plugin/SelfieSegmentation.swift @@ -0,0 +1,199 @@ +import Foundation +import MLKitVision +import MLKitSegmentationSelfie + +@objc public class SelfieSegmentation: NSObject { + public let plugin: SelfieSegmentationPlugin + + init(plugin: SelfieSegmentationPlugin) { + self.plugin = plugin + } + + @objc func createImageFromFilePath(_ path: String) -> UIImage? { + guard let url = URL.init(string: path) else { + return nil + } + if FileManager.default.fileExists(atPath: url.path) { + return UIImage.init(contentsOfFile: url.path) + } else { + return nil + } + } + + enum ProcessError: Error { + case createImageBuffer + } + + private var segmenter: Segmenter? + + @objc func processImage(_ options: ProcessImageOptions, completion: @escaping (ProcessImageResult?, Error?) -> Void) { + let image = options.getImage() + let threshold = options.getConfidence() + + let visionImage = VisionImage.init(image: image) + visionImage.orientation = image.imageOrientation + + let selfieSegmenterOptions: SelfieSegmenterOptions = SelfieSegmenterOptions() + selfieSegmenterOptions.segmenterMode = .singleImage + + segmenter = Segmenter.segmenter( + options: selfieSegmenterOptions + ) + + segmenter?.process(visionImage) { mask, error in + self.segmenter = nil + + guard error == nil, let mask = mask else { + return completion(nil, error) + } + + do { + guard let imageBuffer = self.createImageBuffer(from: image) else { + throw ProcessError.createImageBuffer + } + + self.applySegmentationMask( + mask: mask, to: imageBuffer, threshold: threshold + ) + + let image = self.createImage(from: imageBuffer) + let result = ProcessImageResult(image: image) + + completion(result, nil) + } catch { + completion(nil, error) + } + } + } + + func createImageBuffer(from image: UIImage) -> CVImageBuffer? { + guard let cgImage = image.cgImage else { return nil } + let width = cgImage.width + let height = cgImage.height + + var buffer: CVPixelBuffer? + CVPixelBufferCreate( + kCFAllocatorDefault, + width, + height, + kCVPixelFormatType_32BGRA, + nil, + &buffer) + guard let imageBuffer = buffer else { return nil } + + let flags = CVPixelBufferLockFlags(rawValue: 0) + CVPixelBufferLockBaseAddress(imageBuffer, flags) + let baseAddress = CVPixelBufferGetBaseAddress(imageBuffer) + let colorSpace = CGColorSpaceCreateDeviceRGB() + let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer) + let context = CGContext( + data: baseAddress, + width: width, + height: height, + bitsPerComponent: 8, + bytesPerRow: bytesPerRow, + space: colorSpace, + bitmapInfo: (CGImageAlphaInfo.premultipliedFirst.rawValue + | CGBitmapInfo.byteOrder32Little.rawValue)) + + if let context = context { + let rect = CGRect.init(x: 0, y: 0, width: width, height: height) + context.draw(cgImage, in: rect) + CVPixelBufferUnlockBaseAddress(imageBuffer, flags) + return imageBuffer + } else { + CVPixelBufferUnlockBaseAddress(imageBuffer, flags) + return nil + } + } + + // func createSampleBuffer(with imageBuffer: CVImageBuffer) -> CMSampleBuffer? { + // var timingInfo = CMSampleTimingInfo() + //// guard CMSampleBufferGetSampleTimingInfo(sampleBuffer, at: 0, timingInfoOut: &timingInfo) == 0 else { + //// return nil + //// } + // var outputSampleBuffer: CMSampleBuffer? + // var newFormatDescription: CMFormatDescription? + // CMVideoFormatDescriptionCreateForImageBuffer(allocator: nil, imageBuffer: imageBuffer, formatDescriptionOut: &newFormatDescription) + // guard let formatDescription = newFormatDescription else { + // return nil + // } + // CMSampleBufferCreateReadyWithImageBuffer(allocator: nil, imageBuffer: imageBuffer, formatDescription: formatDescription, sampleTiming: &timingInfo, sampleBufferOut: &outputSampleBuffer) + // guard let buffer = outputSampleBuffer else { + // return nil + // } + // return buffer + // } + + func createImage( + from imageBuffer: CVImageBuffer + ) -> UIImage { + let ciImage = CIImage(cvPixelBuffer: imageBuffer) + let context = CIContext(options: nil) + let cgImage = context.createCGImage(ciImage, from: ciImage.extent)! + return UIImage(cgImage: cgImage) + } + + func applySegmentationMask( + mask: SegmentationMask, to imageBuffer: CVImageBuffer, threshold: CGFloat + ) { + let bgraBytesPerPixel = 4 + + assert( + CVPixelBufferGetPixelFormatType(imageBuffer) == kCVPixelFormatType_32BGRA, + "Image buffer must have 32BGRA pixel format type") + + let width = CVPixelBufferGetWidth(mask.buffer) + let height = CVPixelBufferGetHeight(mask.buffer) + assert(CVPixelBufferGetWidth(imageBuffer) == width, "Width must match") + assert(CVPixelBufferGetHeight(imageBuffer) == height, "Height must match") + + let writeFlags = CVPixelBufferLockFlags(rawValue: 0) + CVPixelBufferLockBaseAddress(imageBuffer, writeFlags) + CVPixelBufferLockBaseAddress(mask.buffer, CVPixelBufferLockFlags.readOnly) + + let maskBytesPerRow = CVPixelBufferGetBytesPerRow(mask.buffer) + var maskAddress = + CVPixelBufferGetBaseAddress(mask.buffer)!.bindMemory( + to: Float32.self, capacity: maskBytesPerRow * height) + + let imageBytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer) + var imageAddress = CVPixelBufferGetBaseAddress(imageBuffer)!.bindMemory( + to: UInt8.self, capacity: imageBytesPerRow * height) + + for _ in 0...(height - 1) { + for col in 0...(width - 1) { + let pixelOffset = col * bgraBytesPerPixel + let blueOffset = pixelOffset + let greenOffset = pixelOffset + 1 + let redOffset = pixelOffset + 2 + let alphaOffset = pixelOffset + 3 + + let confidence: CGFloat = CGFloat(maskAddress[col]) + + if confidence >= threshold { + let red = CGFloat(imageAddress[redOffset]) + let green = CGFloat(imageAddress[greenOffset]) + let blue = CGFloat(imageAddress[blueOffset]) + // let alpha = CGFloat(imageAddress[alphaOffset]) + + imageAddress[redOffset] = UInt8(red * confidence) + imageAddress[greenOffset] = UInt8(green * confidence) + imageAddress[blueOffset] = UInt8(blue * confidence) + imageAddress[alphaOffset] = UInt8(0xff) + } else { + imageAddress[redOffset] = UInt8(0x00) + imageAddress[greenOffset] = UInt8(0x00) + imageAddress[blueOffset] = UInt8(0x00) + imageAddress[alphaOffset] = UInt8(0x00) + } + } + + imageAddress += imageBytesPerRow / MemoryLayout.size + maskAddress += maskBytesPerRow / MemoryLayout.size + } + + CVPixelBufferUnlockBaseAddress(imageBuffer, writeFlags) + CVPixelBufferUnlockBaseAddress(mask.buffer, CVPixelBufferLockFlags.readOnly) + } +} diff --git a/packages/selfie-segmentation/ios/Plugin/SelfieSegmentationPlugin.h b/packages/selfie-segmentation/ios/Plugin/SelfieSegmentationPlugin.h new file mode 100644 index 0000000..f2bd9e0 --- /dev/null +++ b/packages/selfie-segmentation/ios/Plugin/SelfieSegmentationPlugin.h @@ -0,0 +1,10 @@ +#import + +//! Project version number for Plugin. +FOUNDATION_EXPORT double PluginVersionNumber; + +//! Project version string for Plugin. +FOUNDATION_EXPORT const unsigned char PluginVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + diff --git a/packages/selfie-segmentation/ios/Plugin/SelfieSegmentationPlugin.m b/packages/selfie-segmentation/ios/Plugin/SelfieSegmentationPlugin.m new file mode 100644 index 0000000..6ec0c6b --- /dev/null +++ b/packages/selfie-segmentation/ios/Plugin/SelfieSegmentationPlugin.m @@ -0,0 +1,8 @@ +#import +#import + +// Define the plugin using the CAP_PLUGIN Macro, and +// each method the plugin supports using the CAP_PLUGIN_METHOD macro. +CAP_PLUGIN(SelfieSegmentationPlugin, "SelfieSegmentation", + CAP_PLUGIN_METHOD(processImage, CAPPluginReturnPromise); +) diff --git a/packages/selfie-segmentation/ios/Plugin/SelfieSegmentationPlugin.swift b/packages/selfie-segmentation/ios/Plugin/SelfieSegmentationPlugin.swift new file mode 100644 index 0000000..1aa7551 --- /dev/null +++ b/packages/selfie-segmentation/ios/Plugin/SelfieSegmentationPlugin.swift @@ -0,0 +1,61 @@ +import Foundation +import Capacitor + +/** + * Please read the Capacitor iOS Plugin Development Guide + * here: https://capacitorjs.com/docs/plugins/ios + */ +@objc(SelfieSegmentationPlugin) +public class SelfieSegmentationPlugin: CAPPlugin { + public let tag = "SelfieSegmentation" + + public let errorPathMissing = "path must be provided." + public let errorLoadImageFailed = "The image could not be loaded." + public let errorWriteFileFailed = "The result could not be created." + + public let defaultConfidence: Float = 0.9 + + private var implementation: SelfieSegmentation? + + override public func load() { + implementation = SelfieSegmentation(plugin: self) + } + + @objc func processImage(_ call: CAPPluginCall) { + guard let path = call.getString("path") else { + call.reject(errorPathMissing) + return + } + + let width = call.getInt("width") + let height = call.getInt("height") + + let confidence = call.getFloat("confidence", defaultConfidence) + + guard let image = implementation?.createImageFromFilePath(path) else { + call.reject(errorLoadImageFailed) + return + } + + let options = ProcessImageOptions(image: image, + width: width, + height: height, + confidence: CGFloat(confidence)) + + implementation?.processImage(options, completion: { result, error in + if let error = error { + CAPLog.print("[", self.tag, "] ", error) + call.reject(error.localizedDescription, nil, error) + return + } + + if let result = result { + do { + call.resolve(try result.toJSObject()) + } catch { + call.reject(self.errorWriteFileFailed) + } + } + }) + } +} diff --git a/packages/selfie-segmentation/ios/PluginTests/Info.plist b/packages/selfie-segmentation/ios/PluginTests/Info.plist new file mode 100644 index 0000000..6c40a6c --- /dev/null +++ b/packages/selfie-segmentation/ios/PluginTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/packages/selfie-segmentation/ios/PluginTests/SelfieSegmentationTests.swift b/packages/selfie-segmentation/ios/PluginTests/SelfieSegmentationTests.swift new file mode 100644 index 0000000..e82cd98 --- /dev/null +++ b/packages/selfie-segmentation/ios/PluginTests/SelfieSegmentationTests.swift @@ -0,0 +1,25 @@ +import XCTest +@testable import Plugin + +class SelfieSegmentationTests: XCTestCase { + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testEcho() { + // This is an example of a functional test case for a plugin. + // Use XCTAssert and related functions to verify your tests produce the correct results. + + let implementation = SelfieSegmentation() + let value = "Hello, World!" + let result = implementation.echo(value) + + XCTAssertEqual(value, result) + } +} diff --git a/packages/selfie-segmentation/ios/Podfile b/packages/selfie-segmentation/ios/Podfile new file mode 100644 index 0000000..c7b87b0 --- /dev/null +++ b/packages/selfie-segmentation/ios/Podfile @@ -0,0 +1,30 @@ +platform :ios, '13.0' + +def capacitor_pods + # Comment the next line if you're not using Swift and don't want to use dynamic frameworks + use_frameworks! + pod 'Capacitor', :path => '../../../node_modules/@capacitor/ios' + pod 'CapacitorCordova', :path => '../../../node_modules/@capacitor/ios' +end + +target 'Plugin' do + capacitor_pods + pod 'GoogleMLKit/SegmentationSelfie', '4.0.0' +end + +target 'PluginTests' do + capacitor_pods +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['ONLY_ACTIVE_ARCH'] = 'NO' + end + if target.respond_to?(:product_type) and target.product_type == "com.apple.product-type.bundle" + target.build_configurations.each do |config| + config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' + end + end + end +end diff --git a/packages/selfie-segmentation/package.json b/packages/selfie-segmentation/package.json new file mode 100644 index 0000000..712b639 --- /dev/null +++ b/packages/selfie-segmentation/package.json @@ -0,0 +1,91 @@ +{ + "name": "@capacitor-mlkit/selfie-segmentation", + "version": "5.1.0", + "description": "Capacitor plugin for ML Kit Selfie Segmentation.", + "main": "dist/plugin.cjs.js", + "module": "dist/esm/index.js", + "types": "dist/esm/index.d.ts", + "unpkg": "dist/plugin.js", + "files": [ + "android/src/main/", + "android/build.gradle", + "dist/", + "ios/Plugin/", + "CapacitorMlkitSelfieSegmentation.podspec" + ], + "author": "Robin Genz ", + "license": "Apache-2.0", + "homepage": "https://capawesome.io/", + "repository": { + "type": "git", + "url": "git+https://github.com/capawesome-team/capacitor-mlkit.git" + }, + "bugs": { + "url": "https://github.com/capawesome-team/capacitor-mlkit/issues" + }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/capawesome-team/" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/capawesome" + } + ], + "keywords": [ + "capacitor", + "plugin", + "native" + ], + "scripts": { + "verify": "npm run verify:ios && npm run verify:android && npm run verify:web", + "verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin -destination generic/platform=iOS && cd ..", + "verify:android": "cd android && ./gradlew clean build test && cd ..", + "verify:web": "npm run build", + "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint", + "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format", + "eslint": "eslint . --ext ts", + "prettier": "prettier \"**/*.{css,html,ts,js,java}\"", + "swiftlint": "node-swiftlint", + "docgen": "docgen --api SelfieSegmentationPlugin --output-readme README.md --output-json dist/docs.json", + "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.js", + "clean": "rimraf ./dist", + "watch": "tsc --watch", + "prepublishOnly": "npm run build" + }, + "devDependencies": { + "@capacitor/android": "5.0.0", + "@capacitor/cli": "5.0.0", + "@capacitor/core": "5.0.0", + "@capacitor/docgen": "0.2.1", + "@capacitor/ios": "5.0.0", + "@ionic/eslint-config": "0.3.0", + "@ionic/swiftlint-config": "1.1.2", + "eslint": "7.32.0", + "prettier": "2.3.2", + "prettier-plugin-java": "1.0.2", + "rimraf": "3.0.2", + "rollup": "2.77.2", + "swiftlint": "1.0.1", + "typescript": "4.1.5" + }, + "peerDependencies": { + "@capacitor/core": "^5.0.0" + }, + "swiftlint": "@ionic/swiftlint-config", + "eslintConfig": { + "extends": "@ionic/eslint-config/recommended" + }, + "capacitor": { + "ios": { + "src": "ios" + }, + "android": { + "src": "android" + } + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/selfie-segmentation/rollup.config.js b/packages/selfie-segmentation/rollup.config.js new file mode 100644 index 0000000..c7f622e --- /dev/null +++ b/packages/selfie-segmentation/rollup.config.js @@ -0,0 +1,22 @@ +export default { + input: 'dist/esm/index.js', + output: [ + { + file: 'dist/plugin.js', + format: 'iife', + name: 'capacitorSelfieSegmentation', + globals: { + '@capacitor/core': 'capacitorExports', + }, + sourcemap: true, + inlineDynamicImports: true, + }, + { + file: 'dist/plugin.cjs.js', + format: 'cjs', + sourcemap: true, + inlineDynamicImports: true, + }, + ], + external: ['@capacitor/core'], +}; diff --git a/packages/selfie-segmentation/src/definitions.ts b/packages/selfie-segmentation/src/definitions.ts new file mode 100644 index 0000000..5c0080f --- /dev/null +++ b/packages/selfie-segmentation/src/definitions.ts @@ -0,0 +1,69 @@ +export interface SelfieSegmentationPlugin { + /** + * Performs segmentation on an input image. + * + * Only available on Android and iOS. + * + * @since 5.2.0 + */ + processImage(options: ProcessImageOptions): Promise; +} + +/** + * @since 5.2.0 + */ +export interface ProcessImageOptions { + /** + * The local path to the image file. + * + * @since 5.2.0 + */ + path: string; + + /** + * Scale the image to this width. + * If no `height` is given, it will respect the aspect ratio. + * + * @since 5.2.0 + */ + width?: number; + /** + * Scale the image to this height. + * If no `width` is given, it will respect the aspect ratio. + * + * @since 5.2.0 + */ + height?: number; + + /** + * Sets the confidence threshold. + * + * @since 5.2.0 + * @default 0.9 + */ + confidence?: number; +} + +/** + * @since 5.2.0 + */ +export interface ProcessImageResult { + /** + * The path to the segmented image file. + * + * @since 5.2.0 + */ + path: string; + /** + * Returns the width of the image file. + * + * @since 5.2.0 + */ + width: number; + /** + * Returns the height of the image file. + * + * @since 5.2.0 + */ + height: number; +} diff --git a/packages/selfie-segmentation/src/index.ts b/packages/selfie-segmentation/src/index.ts new file mode 100644 index 0000000..e4501f2 --- /dev/null +++ b/packages/selfie-segmentation/src/index.ts @@ -0,0 +1,13 @@ +import { registerPlugin } from '@capacitor/core'; + +import type { SelfieSegmentationPlugin } from './definitions'; + +const SelfieSegmentation = registerPlugin( + 'SelfieSegmentation', + { + web: () => import('./web').then(m => new m.SelfieSegmentationWeb()), + }, +); + +export * from './definitions'; +export { SelfieSegmentation }; diff --git a/packages/selfie-segmentation/src/web.ts b/packages/selfie-segmentation/src/web.ts new file mode 100644 index 0000000..e581361 --- /dev/null +++ b/packages/selfie-segmentation/src/web.ts @@ -0,0 +1,25 @@ +import { CapacitorException, ExceptionCode, WebPlugin } from '@capacitor/core'; + +import type { + SelfieSegmentationPlugin, + ProcessImageOptions, + ProcessImageResult, +} from './definitions'; + +export class SelfieSegmentationWeb + extends WebPlugin + implements SelfieSegmentationPlugin +{ + public async processImage( + _options: ProcessImageOptions, + ): Promise { + throw this.createUnavailableException(); + } + + private createUnavailableException(): CapacitorException { + return new CapacitorException( + 'This Selfie Segmentation plugin method is not available on this platform.', + ExceptionCode.Unavailable, + ); + } +} diff --git a/packages/selfie-segmentation/tsconfig.json b/packages/selfie-segmentation/tsconfig.json new file mode 100644 index 0000000..f2e88e6 --- /dev/null +++ b/packages/selfie-segmentation/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "allowUnreachableCode": false, + "declaration": true, + "esModuleInterop": true, + "inlineSources": true, + "lib": ["dom", "es2017"], + "module": "esnext", + "moduleResolution": "node", + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "outDir": "dist/esm", + "pretty": true, + "sourceMap": true, + "strict": true, + "target": "es2017" + }, + "files": ["src/index.ts"] +} diff --git a/packages/translation/package.json b/packages/translation/package.json index 9a6710a..c15e11f 100644 --- a/packages/translation/package.json +++ b/packages/translation/package.json @@ -21,7 +21,7 @@ "url": "git+https://github.com/capawesome-team/capacitor-mlkit.git" }, "bugs": { - "url": "https://github.com/capawesome-team/capacitor-mlkit.git/issues" + "url": "https://github.com/capawesome-team/capacitor-mlkit/issues" }, "funding": [ {