@capacitor-mlkit/selfie-segmentation 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CapacitorMlkitSelfieSegmentation.podspec +19 -0
  2. package/LICENSE +201 -0
  3. package/README.md +112 -0
  4. package/android/build.gradle +60 -0
  5. package/android/src/main/AndroidManifest.xml +2 -0
  6. package/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/ProcessImageResultCallback.java +11 -0
  7. package/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/SelfieSegmentation.java +132 -0
  8. package/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/SelfieSegmentationPlugin.java +88 -0
  9. package/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/classes/ProcessImageOptions.java +47 -0
  10. package/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/selfiesegmentation/classes/ProcessImageResult.java +29 -0
  11. package/android/src/main/res/.gitkeep +0 -0
  12. package/dist/docs.json +156 -0
  13. package/dist/esm/definitions.d.ts +65 -0
  14. package/dist/esm/definitions.js +2 -0
  15. package/dist/esm/definitions.js.map +1 -0
  16. package/dist/esm/index.d.ts +4 -0
  17. package/dist/esm/index.js +7 -0
  18. package/dist/esm/index.js.map +1 -0
  19. package/dist/esm/web.d.ts +6 -0
  20. package/dist/esm/web.js +10 -0
  21. package/dist/esm/web.js.map +1 -0
  22. package/dist/plugin.cjs.js +26 -0
  23. package/dist/plugin.cjs.js.map +1 -0
  24. package/dist/plugin.js +29 -0
  25. package/dist/plugin.js.map +1 -0
  26. package/ios/Plugin/Classes/ProcessImageOptions.swift +64 -0
  27. package/ios/Plugin/Classes/ProcessImageResult.swift +30 -0
  28. package/ios/Plugin/Info.plist +24 -0
  29. package/ios/Plugin/SelfieSegmentation.swift +199 -0
  30. package/ios/Plugin/SelfieSegmentationPlugin.h +10 -0
  31. package/ios/Plugin/SelfieSegmentationPlugin.m +8 -0
  32. package/ios/Plugin/SelfieSegmentationPlugin.swift +61 -0
  33. package/package.json +91 -0
@@ -0,0 +1,199 @@
1
+ import Foundation
2
+ import MLKitVision
3
+ import MLKitSegmentationSelfie
4
+
5
+ @objc public class SelfieSegmentation: NSObject {
6
+ public let plugin: SelfieSegmentationPlugin
7
+
8
+ init(plugin: SelfieSegmentationPlugin) {
9
+ self.plugin = plugin
10
+ }
11
+
12
+ @objc func createImageFromFilePath(_ path: String) -> UIImage? {
13
+ guard let url = URL.init(string: path) else {
14
+ return nil
15
+ }
16
+ if FileManager.default.fileExists(atPath: url.path) {
17
+ return UIImage.init(contentsOfFile: url.path)
18
+ } else {
19
+ return nil
20
+ }
21
+ }
22
+
23
+ enum ProcessError: Error {
24
+ case createImageBuffer
25
+ }
26
+
27
+ private var segmenter: Segmenter?
28
+
29
+ @objc func processImage(_ options: ProcessImageOptions, completion: @escaping (ProcessImageResult?, Error?) -> Void) {
30
+ let image = options.getImage()
31
+ let threshold = options.getConfidence()
32
+
33
+ let visionImage = VisionImage.init(image: image)
34
+ visionImage.orientation = image.imageOrientation
35
+
36
+ let selfieSegmenterOptions: SelfieSegmenterOptions = SelfieSegmenterOptions()
37
+ selfieSegmenterOptions.segmenterMode = .singleImage
38
+
39
+ segmenter = Segmenter.segmenter(
40
+ options: selfieSegmenterOptions
41
+ )
42
+
43
+ segmenter?.process(visionImage) { mask, error in
44
+ self.segmenter = nil
45
+
46
+ guard error == nil, let mask = mask else {
47
+ return completion(nil, error)
48
+ }
49
+
50
+ do {
51
+ guard let imageBuffer = self.createImageBuffer(from: image) else {
52
+ throw ProcessError.createImageBuffer
53
+ }
54
+
55
+ self.applySegmentationMask(
56
+ mask: mask, to: imageBuffer, threshold: threshold
57
+ )
58
+
59
+ let image = self.createImage(from: imageBuffer)
60
+ let result = ProcessImageResult(image: image)
61
+
62
+ completion(result, nil)
63
+ } catch {
64
+ completion(nil, error)
65
+ }
66
+ }
67
+ }
68
+
69
+ func createImageBuffer(from image: UIImage) -> CVImageBuffer? {
70
+ guard let cgImage = image.cgImage else { return nil }
71
+ let width = cgImage.width
72
+ let height = cgImage.height
73
+
74
+ var buffer: CVPixelBuffer?
75
+ CVPixelBufferCreate(
76
+ kCFAllocatorDefault,
77
+ width,
78
+ height,
79
+ kCVPixelFormatType_32BGRA,
80
+ nil,
81
+ &buffer)
82
+ guard let imageBuffer = buffer else { return nil }
83
+
84
+ let flags = CVPixelBufferLockFlags(rawValue: 0)
85
+ CVPixelBufferLockBaseAddress(imageBuffer, flags)
86
+ let baseAddress = CVPixelBufferGetBaseAddress(imageBuffer)
87
+ let colorSpace = CGColorSpaceCreateDeviceRGB()
88
+ let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer)
89
+ let context = CGContext(
90
+ data: baseAddress,
91
+ width: width,
92
+ height: height,
93
+ bitsPerComponent: 8,
94
+ bytesPerRow: bytesPerRow,
95
+ space: colorSpace,
96
+ bitmapInfo: (CGImageAlphaInfo.premultipliedFirst.rawValue
97
+ | CGBitmapInfo.byteOrder32Little.rawValue))
98
+
99
+ if let context = context {
100
+ let rect = CGRect.init(x: 0, y: 0, width: width, height: height)
101
+ context.draw(cgImage, in: rect)
102
+ CVPixelBufferUnlockBaseAddress(imageBuffer, flags)
103
+ return imageBuffer
104
+ } else {
105
+ CVPixelBufferUnlockBaseAddress(imageBuffer, flags)
106
+ return nil
107
+ }
108
+ }
109
+
110
+ // func createSampleBuffer(with imageBuffer: CVImageBuffer) -> CMSampleBuffer? {
111
+ // var timingInfo = CMSampleTimingInfo()
112
+ //// guard CMSampleBufferGetSampleTimingInfo(sampleBuffer, at: 0, timingInfoOut: &timingInfo) == 0 else {
113
+ //// return nil
114
+ //// }
115
+ // var outputSampleBuffer: CMSampleBuffer?
116
+ // var newFormatDescription: CMFormatDescription?
117
+ // CMVideoFormatDescriptionCreateForImageBuffer(allocator: nil, imageBuffer: imageBuffer, formatDescriptionOut: &newFormatDescription)
118
+ // guard let formatDescription = newFormatDescription else {
119
+ // return nil
120
+ // }
121
+ // CMSampleBufferCreateReadyWithImageBuffer(allocator: nil, imageBuffer: imageBuffer, formatDescription: formatDescription, sampleTiming: &timingInfo, sampleBufferOut: &outputSampleBuffer)
122
+ // guard let buffer = outputSampleBuffer else {
123
+ // return nil
124
+ // }
125
+ // return buffer
126
+ // }
127
+
128
+ func createImage(
129
+ from imageBuffer: CVImageBuffer
130
+ ) -> UIImage {
131
+ let ciImage = CIImage(cvPixelBuffer: imageBuffer)
132
+ let context = CIContext(options: nil)
133
+ let cgImage = context.createCGImage(ciImage, from: ciImage.extent)!
134
+ return UIImage(cgImage: cgImage)
135
+ }
136
+
137
+ func applySegmentationMask(
138
+ mask: SegmentationMask, to imageBuffer: CVImageBuffer, threshold: CGFloat
139
+ ) {
140
+ let bgraBytesPerPixel = 4
141
+
142
+ assert(
143
+ CVPixelBufferGetPixelFormatType(imageBuffer) == kCVPixelFormatType_32BGRA,
144
+ "Image buffer must have 32BGRA pixel format type")
145
+
146
+ let width = CVPixelBufferGetWidth(mask.buffer)
147
+ let height = CVPixelBufferGetHeight(mask.buffer)
148
+ assert(CVPixelBufferGetWidth(imageBuffer) == width, "Width must match")
149
+ assert(CVPixelBufferGetHeight(imageBuffer) == height, "Height must match")
150
+
151
+ let writeFlags = CVPixelBufferLockFlags(rawValue: 0)
152
+ CVPixelBufferLockBaseAddress(imageBuffer, writeFlags)
153
+ CVPixelBufferLockBaseAddress(mask.buffer, CVPixelBufferLockFlags.readOnly)
154
+
155
+ let maskBytesPerRow = CVPixelBufferGetBytesPerRow(mask.buffer)
156
+ var maskAddress =
157
+ CVPixelBufferGetBaseAddress(mask.buffer)!.bindMemory(
158
+ to: Float32.self, capacity: maskBytesPerRow * height)
159
+
160
+ let imageBytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer)
161
+ var imageAddress = CVPixelBufferGetBaseAddress(imageBuffer)!.bindMemory(
162
+ to: UInt8.self, capacity: imageBytesPerRow * height)
163
+
164
+ for _ in 0...(height - 1) {
165
+ for col in 0...(width - 1) {
166
+ let pixelOffset = col * bgraBytesPerPixel
167
+ let blueOffset = pixelOffset
168
+ let greenOffset = pixelOffset + 1
169
+ let redOffset = pixelOffset + 2
170
+ let alphaOffset = pixelOffset + 3
171
+
172
+ let confidence: CGFloat = CGFloat(maskAddress[col])
173
+
174
+ if confidence >= threshold {
175
+ let red = CGFloat(imageAddress[redOffset])
176
+ let green = CGFloat(imageAddress[greenOffset])
177
+ let blue = CGFloat(imageAddress[blueOffset])
178
+ // let alpha = CGFloat(imageAddress[alphaOffset])
179
+
180
+ imageAddress[redOffset] = UInt8(red * confidence)
181
+ imageAddress[greenOffset] = UInt8(green * confidence)
182
+ imageAddress[blueOffset] = UInt8(blue * confidence)
183
+ imageAddress[alphaOffset] = UInt8(0xff)
184
+ } else {
185
+ imageAddress[redOffset] = UInt8(0x00)
186
+ imageAddress[greenOffset] = UInt8(0x00)
187
+ imageAddress[blueOffset] = UInt8(0x00)
188
+ imageAddress[alphaOffset] = UInt8(0x00)
189
+ }
190
+ }
191
+
192
+ imageAddress += imageBytesPerRow / MemoryLayout<UInt8>.size
193
+ maskAddress += maskBytesPerRow / MemoryLayout<Float32>.size
194
+ }
195
+
196
+ CVPixelBufferUnlockBaseAddress(imageBuffer, writeFlags)
197
+ CVPixelBufferUnlockBaseAddress(mask.buffer, CVPixelBufferLockFlags.readOnly)
198
+ }
199
+ }
@@ -0,0 +1,10 @@
1
+ #import <UIKit/UIKit.h>
2
+
3
+ //! Project version number for Plugin.
4
+ FOUNDATION_EXPORT double PluginVersionNumber;
5
+
6
+ //! Project version string for Plugin.
7
+ FOUNDATION_EXPORT const unsigned char PluginVersionString[];
8
+
9
+ // In this header, you should import all the public headers of your framework using statements like #import <Plugin/PublicHeader.h>
10
+
@@ -0,0 +1,8 @@
1
+ #import <Foundation/Foundation.h>
2
+ #import <Capacitor/Capacitor.h>
3
+
4
+ // Define the plugin using the CAP_PLUGIN Macro, and
5
+ // each method the plugin supports using the CAP_PLUGIN_METHOD macro.
6
+ CAP_PLUGIN(SelfieSegmentationPlugin, "SelfieSegmentation",
7
+ CAP_PLUGIN_METHOD(processImage, CAPPluginReturnPromise);
8
+ )
@@ -0,0 +1,61 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ /**
5
+ * Please read the Capacitor iOS Plugin Development Guide
6
+ * here: https://capacitorjs.com/docs/plugins/ios
7
+ */
8
+ @objc(SelfieSegmentationPlugin)
9
+ public class SelfieSegmentationPlugin: CAPPlugin {
10
+ public let tag = "SelfieSegmentation"
11
+
12
+ public let errorPathMissing = "path must be provided."
13
+ public let errorLoadImageFailed = "The image could not be loaded."
14
+ public let errorWriteFileFailed = "The result could not be created."
15
+
16
+ public let defaultConfidence: Float = 0.9
17
+
18
+ private var implementation: SelfieSegmentation?
19
+
20
+ override public func load() {
21
+ implementation = SelfieSegmentation(plugin: self)
22
+ }
23
+
24
+ @objc func processImage(_ call: CAPPluginCall) {
25
+ guard let path = call.getString("path") else {
26
+ call.reject(errorPathMissing)
27
+ return
28
+ }
29
+
30
+ let width = call.getInt("width")
31
+ let height = call.getInt("height")
32
+
33
+ let confidence = call.getFloat("confidence", defaultConfidence)
34
+
35
+ guard let image = implementation?.createImageFromFilePath(path) else {
36
+ call.reject(errorLoadImageFailed)
37
+ return
38
+ }
39
+
40
+ let options = ProcessImageOptions(image: image,
41
+ width: width,
42
+ height: height,
43
+ confidence: CGFloat(confidence))
44
+
45
+ implementation?.processImage(options, completion: { result, error in
46
+ if let error = error {
47
+ CAPLog.print("[", self.tag, "] ", error)
48
+ call.reject(error.localizedDescription, nil, error)
49
+ return
50
+ }
51
+
52
+ if let result = result {
53
+ do {
54
+ call.resolve(try result.toJSObject())
55
+ } catch {
56
+ call.reject(self.errorWriteFileFailed)
57
+ }
58
+ }
59
+ })
60
+ }
61
+ }
package/package.json ADDED
@@ -0,0 +1,91 @@
1
+ {
2
+ "name": "@capacitor-mlkit/selfie-segmentation",
3
+ "version": "5.2.0",
4
+ "description": "Capacitor plugin for ML Kit Selfie Segmentation.",
5
+ "main": "dist/plugin.cjs.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/esm/index.d.ts",
8
+ "unpkg": "dist/plugin.js",
9
+ "files": [
10
+ "android/src/main/",
11
+ "android/build.gradle",
12
+ "dist/",
13
+ "ios/Plugin/",
14
+ "CapacitorMlkitSelfieSegmentation.podspec"
15
+ ],
16
+ "author": "Robin Genz <mail@robingenz.dev>",
17
+ "license": "Apache-2.0",
18
+ "homepage": "https://capawesome.io/",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/capawesome-team/capacitor-mlkit.git"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/capawesome-team/capacitor-mlkit/issues"
25
+ },
26
+ "funding": [
27
+ {
28
+ "type": "github",
29
+ "url": "https://github.com/sponsors/capawesome-team/"
30
+ },
31
+ {
32
+ "type": "opencollective",
33
+ "url": "https://opencollective.com/capawesome"
34
+ }
35
+ ],
36
+ "keywords": [
37
+ "capacitor",
38
+ "plugin",
39
+ "native"
40
+ ],
41
+ "scripts": {
42
+ "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
43
+ "verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin -destination generic/platform=iOS && cd ..",
44
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
45
+ "verify:web": "npm run build",
46
+ "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
47
+ "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
48
+ "eslint": "eslint . --ext ts",
49
+ "prettier": "prettier \"**/*.{css,html,ts,js,java}\"",
50
+ "swiftlint": "node-swiftlint",
51
+ "docgen": "docgen --api SelfieSegmentationPlugin --output-readme README.md --output-json dist/docs.json",
52
+ "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.js",
53
+ "clean": "rimraf ./dist",
54
+ "watch": "tsc --watch",
55
+ "prepublishOnly": "npm run build"
56
+ },
57
+ "devDependencies": {
58
+ "@capacitor/android": "5.0.0",
59
+ "@capacitor/cli": "5.0.0",
60
+ "@capacitor/core": "5.0.0",
61
+ "@capacitor/docgen": "0.2.1",
62
+ "@capacitor/ios": "5.0.0",
63
+ "@ionic/eslint-config": "0.3.0",
64
+ "@ionic/swiftlint-config": "1.1.2",
65
+ "eslint": "7.32.0",
66
+ "prettier": "2.3.2",
67
+ "prettier-plugin-java": "1.0.2",
68
+ "rimraf": "3.0.2",
69
+ "rollup": "2.77.2",
70
+ "swiftlint": "1.0.1",
71
+ "typescript": "4.1.5"
72
+ },
73
+ "peerDependencies": {
74
+ "@capacitor/core": "^5.0.0"
75
+ },
76
+ "swiftlint": "@ionic/swiftlint-config",
77
+ "eslintConfig": {
78
+ "extends": "@ionic/eslint-config/recommended"
79
+ },
80
+ "capacitor": {
81
+ "ios": {
82
+ "src": "ios"
83
+ },
84
+ "android": {
85
+ "src": "android"
86
+ }
87
+ },
88
+ "publishConfig": {
89
+ "access": "public"
90
+ }
91
+ }