@insidepics/expo-apple-intelligence 0.1.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.
- package/LICENSE +21 -0
- package/README.md +178 -0
- package/build/ExpoAppleIntelligence.types.d.ts +177 -0
- package/build/ExpoAppleIntelligence.types.d.ts.map +1 -0
- package/build/ExpoAppleIntelligence.types.js +3 -0
- package/build/ExpoAppleIntelligence.types.js.map +1 -0
- package/build/ExpoAppleIntelligenceModule.d.ts +51 -0
- package/build/ExpoAppleIntelligenceModule.d.ts.map +1 -0
- package/build/ExpoAppleIntelligenceModule.js +3 -0
- package/build/ExpoAppleIntelligenceModule.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +3 -0
- package/build/index.js.map +1 -0
- package/expo-module.config.json +6 -0
- package/ios/ExpoAppleIntelligence.podspec +38 -0
- package/ios/ExpoAppleIntelligenceModule.swift +352 -0
- package/ios/VisionHelpers.swift +62 -0
- package/ios/VisionModern.swift +239 -0
- package/ios/VisionProcessors.swift +197 -0
- package/package.json +59 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import Vision
|
|
2
|
+
|
|
3
|
+
/// Raw passthrough processing for VNRequest results.
|
|
4
|
+
/// Returns Apple API values directly — no filtering, no coordinate conversion.
|
|
5
|
+
/// All coordinates are normalized (0-1, bottom-left origin) as Apple returns them.
|
|
6
|
+
/// Face landmark points are normalized to the face bounding box.
|
|
7
|
+
enum VisionProcessors {
|
|
8
|
+
|
|
9
|
+
// MARK: - Faces
|
|
10
|
+
|
|
11
|
+
static func processFaces(
|
|
12
|
+
landmarks landmarkObs: [VNFaceObservation],
|
|
13
|
+
quality qualityObs: [VNFaceObservation]
|
|
14
|
+
) -> [[String: Any]] {
|
|
15
|
+
return landmarkObs.map { face in
|
|
16
|
+
var dict: [String: Any] = [
|
|
17
|
+
"boundingBox": VisionHelpers.rawRect(face.boundingBox),
|
|
18
|
+
"confidence": Double(face.confidence),
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
if let roll = face.roll { dict["roll"] = roll.doubleValue }
|
|
22
|
+
if let yaw = face.yaw { dict["yaw"] = yaw.doubleValue }
|
|
23
|
+
if let pitch = face.pitch { dict["pitch"] = pitch.doubleValue }
|
|
24
|
+
|
|
25
|
+
if let lm = face.landmarks {
|
|
26
|
+
var landmarks: [String: Any] = [:]
|
|
27
|
+
if let r = lm.faceContour { landmarks["faceContour"] = rawLandmarkPoints(r) }
|
|
28
|
+
if let r = lm.leftEye { landmarks["leftEye"] = rawLandmarkPoints(r) }
|
|
29
|
+
if let r = lm.rightEye { landmarks["rightEye"] = rawLandmarkPoints(r) }
|
|
30
|
+
if let r = lm.leftEyebrow { landmarks["leftEyebrow"] = rawLandmarkPoints(r) }
|
|
31
|
+
if let r = lm.rightEyebrow { landmarks["rightEyebrow"] = rawLandmarkPoints(r) }
|
|
32
|
+
if let r = lm.nose { landmarks["nose"] = rawLandmarkPoints(r) }
|
|
33
|
+
if let r = lm.noseCrest { landmarks["noseCrest"] = rawLandmarkPoints(r) }
|
|
34
|
+
if let r = lm.medianLine { landmarks["medianLine"] = rawLandmarkPoints(r) }
|
|
35
|
+
if let r = lm.outerLips { landmarks["outerLips"] = rawLandmarkPoints(r) }
|
|
36
|
+
if let r = lm.innerLips { landmarks["innerLips"] = rawLandmarkPoints(r) }
|
|
37
|
+
if let r = lm.leftPupil { landmarks["leftPupil"] = rawLandmarkPoints(r) }
|
|
38
|
+
if let r = lm.rightPupil { landmarks["rightPupil"] = rawLandmarkPoints(r) }
|
|
39
|
+
dict["landmarks"] = landmarks
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let qualityMatch = qualityObs.first { $0.uuid == face.uuid }
|
|
43
|
+
if let q = qualityMatch?.faceCaptureQuality {
|
|
44
|
+
dict["quality"] = Double(q)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return dict
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private static func rawLandmarkPoints(_ region: VNFaceLandmarkRegion2D) -> [[String: Double]] {
|
|
52
|
+
(0..<region.pointCount).map { i in
|
|
53
|
+
let pt = region.normalizedPoints[i]
|
|
54
|
+
return ["x": Double(pt.x), "y": Double(pt.y)]
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// MARK: - Text
|
|
59
|
+
|
|
60
|
+
static func processText(_ observations: [VNRecognizedTextObservation]) -> [[String: Any]] {
|
|
61
|
+
return observations.compactMap { obs in
|
|
62
|
+
let candidates: [[String: Any]] = obs.topCandidates(10).map { candidate in
|
|
63
|
+
["string": candidate.string, "confidence": Double(candidate.confidence)]
|
|
64
|
+
}
|
|
65
|
+
if candidates.isEmpty { return nil }
|
|
66
|
+
return [
|
|
67
|
+
"boundingBox": VisionHelpers.rawRect(obs.boundingBox),
|
|
68
|
+
"confidence": Double(obs.confidence),
|
|
69
|
+
"candidates": candidates,
|
|
70
|
+
] as [String: Any]
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// MARK: - Classification
|
|
75
|
+
|
|
76
|
+
static func processClassification(_ observations: [VNClassificationObservation]) -> [[String: Any]] {
|
|
77
|
+
return observations.map {
|
|
78
|
+
["identifier": $0.identifier, "confidence": Double($0.confidence)]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// MARK: - Barcodes
|
|
83
|
+
|
|
84
|
+
static func processBarcodes(_ observations: [VNBarcodeObservation]) -> [[String: Any]] {
|
|
85
|
+
return observations.map { obs in
|
|
86
|
+
var dict: [String: Any] = [
|
|
87
|
+
"boundingBox": VisionHelpers.rawRect(obs.boundingBox),
|
|
88
|
+
"confidence": Double(obs.confidence),
|
|
89
|
+
"symbology": obs.symbology.rawValue,
|
|
90
|
+
"topLeft": VisionHelpers.rawPoint(obs.topLeft),
|
|
91
|
+
"topRight": VisionHelpers.rawPoint(obs.topRight),
|
|
92
|
+
"bottomLeft": VisionHelpers.rawPoint(obs.bottomLeft),
|
|
93
|
+
"bottomRight": VisionHelpers.rawPoint(obs.bottomRight),
|
|
94
|
+
]
|
|
95
|
+
if let payload = obs.payloadStringValue {
|
|
96
|
+
dict["payloadStringValue"] = payload
|
|
97
|
+
}
|
|
98
|
+
return dict
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// MARK: - Body pose
|
|
103
|
+
|
|
104
|
+
static func processBodyPoses(_ observations: [VNHumanBodyPoseObservation]) -> [[String: Any]] {
|
|
105
|
+
return observations.compactMap { obs in
|
|
106
|
+
guard let points = try? obs.recognizedPoints(.all) else { return nil }
|
|
107
|
+
let joints: [[String: Any]] = points.map { (key, point) in
|
|
108
|
+
["name": key.rawValue, "x": Double(point.location.x), "y": Double(point.location.y), "confidence": Double(point.confidence)]
|
|
109
|
+
}
|
|
110
|
+
return ["joints": joints]
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// MARK: - Hand pose
|
|
115
|
+
|
|
116
|
+
static func processHandPoses(_ observations: [VNHumanHandPoseObservation]) -> [[String: Any]] {
|
|
117
|
+
return observations.compactMap { obs in
|
|
118
|
+
guard let points = try? obs.recognizedPoints(.all) else { return nil }
|
|
119
|
+
let joints: [[String: Any]] = points.map { (key, point) in
|
|
120
|
+
["name": key.rawValue, "x": Double(point.location.x), "y": Double(point.location.y), "confidence": Double(point.confidence)]
|
|
121
|
+
}
|
|
122
|
+
return ["joints": joints]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// MARK: - Feature print
|
|
127
|
+
|
|
128
|
+
static func processFeaturePrint(_ obs: VNFeaturePrintObservation?) -> [String: Any]? {
|
|
129
|
+
guard let obs = obs else { return nil }
|
|
130
|
+
var data: [Double] = []
|
|
131
|
+
let count = obs.elementCount
|
|
132
|
+
if obs.elementType == .float {
|
|
133
|
+
var floats = [Float](repeating: 0, count: count)
|
|
134
|
+
_ = floats.withUnsafeMutableBytes { ptr in
|
|
135
|
+
obs.data.copyBytes(to: ptr)
|
|
136
|
+
}
|
|
137
|
+
data = floats.map { Double($0) }
|
|
138
|
+
} else {
|
|
139
|
+
var doubles = [Double](repeating: 0, count: count)
|
|
140
|
+
_ = doubles.withUnsafeMutableBytes { ptr in
|
|
141
|
+
obs.data.copyBytes(to: ptr)
|
|
142
|
+
}
|
|
143
|
+
data = doubles
|
|
144
|
+
}
|
|
145
|
+
return ["data": data as [Any], "elementType": obs.elementType == .float ? "float" : "double", "elementCount": Double(count)]
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// MARK: - Saliency
|
|
149
|
+
|
|
150
|
+
static func processSaliency(_ observations: [VNSaliencyImageObservation]) -> [[String: Any]] {
|
|
151
|
+
var regions: [[String: Any]] = []
|
|
152
|
+
for obs in observations {
|
|
153
|
+
guard let salientObjects = obs.salientObjects else { continue }
|
|
154
|
+
for obj in salientObjects {
|
|
155
|
+
regions.append(["boundingBox": VisionHelpers.rawRect(obj.boundingBox), "confidence": Double(obj.confidence)])
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return regions
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// MARK: - Animals
|
|
162
|
+
|
|
163
|
+
static func processAnimals(_ observations: [VNRecognizedObjectObservation]) -> [[String: Any]] {
|
|
164
|
+
return observations.map { obs in
|
|
165
|
+
let labels: [[String: Any]] = obs.labels.map {
|
|
166
|
+
["identifier": $0.identifier, "confidence": Double($0.confidence)]
|
|
167
|
+
}
|
|
168
|
+
return [
|
|
169
|
+
"boundingBox": VisionHelpers.rawRect(obs.boundingBox),
|
|
170
|
+
"confidence": Double(obs.confidence),
|
|
171
|
+
"labels": labels,
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// MARK: - Rectangles
|
|
177
|
+
|
|
178
|
+
static func processRectangles(_ observations: [VNRectangleObservation]) -> [[String: Any]] {
|
|
179
|
+
return observations.map { obs in
|
|
180
|
+
[
|
|
181
|
+
"boundingBox": VisionHelpers.rawRect(obs.boundingBox),
|
|
182
|
+
"topLeft": VisionHelpers.rawPoint(obs.topLeft),
|
|
183
|
+
"topRight": VisionHelpers.rawPoint(obs.topRight),
|
|
184
|
+
"bottomLeft": VisionHelpers.rawPoint(obs.bottomLeft),
|
|
185
|
+
"bottomRight": VisionHelpers.rawPoint(obs.bottomRight),
|
|
186
|
+
"confidence": Double(obs.confidence),
|
|
187
|
+
]
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// MARK: - Horizon
|
|
192
|
+
|
|
193
|
+
static func processHorizon(_ obs: VNHorizonObservation?) -> [String: Any]? {
|
|
194
|
+
guard let obs = obs else { return nil }
|
|
195
|
+
return ["angle": Double(obs.angle)]
|
|
196
|
+
}
|
|
197
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@insidepics/expo-apple-intelligence",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Expo module for Apple Intelligence: Vision, Foundation Models, SpeechAnalyzer, ImagePlayground",
|
|
5
|
+
"main": "build/index.js",
|
|
6
|
+
"types": "build/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "expo-module build",
|
|
9
|
+
"clean": "expo-module clean",
|
|
10
|
+
"lint": "expo-module lint",
|
|
11
|
+
"lint:check": "expo-module lint",
|
|
12
|
+
"test": "expo-module test --passWithNoTests",
|
|
13
|
+
"prepare": "expo-module prepare",
|
|
14
|
+
"expo-module": "expo-module",
|
|
15
|
+
"open:ios": "xed example/ios"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"react-native",
|
|
19
|
+
"expo",
|
|
20
|
+
"apple-intelligence",
|
|
21
|
+
"machine-learning",
|
|
22
|
+
"vision-framework",
|
|
23
|
+
"foundation-models",
|
|
24
|
+
"speech-analyzer",
|
|
25
|
+
"image-playground"
|
|
26
|
+
],
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/InsidePics/expo-apple-intelligence.git"
|
|
30
|
+
},
|
|
31
|
+
"bugs": "https://github.com/InsidePics/expo-apple-intelligence/issues",
|
|
32
|
+
"author": "INSP LLC",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"homepage": "https://github.com/InsidePics/expo-apple-intelligence#readme",
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"build",
|
|
40
|
+
"ios/*.swift",
|
|
41
|
+
"ios/*.podspec",
|
|
42
|
+
"expo-module.config.json",
|
|
43
|
+
"README.md",
|
|
44
|
+
"LICENSE"
|
|
45
|
+
],
|
|
46
|
+
"dependencies": {},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/react": "~19.2.10",
|
|
49
|
+
"expo": "^55.0.8",
|
|
50
|
+
"expo-module-scripts": "^55.0.2",
|
|
51
|
+
"expo-modules-test-core": "^55.0.2",
|
|
52
|
+
"react-native": "0.83.2"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"expo": "*",
|
|
56
|
+
"react": "*",
|
|
57
|
+
"react-native": "*"
|
|
58
|
+
}
|
|
59
|
+
}
|