Skip to content

Commit 5dbdd75

Browse files
authored
patch: Version 1.4.4 (#9)
* Updated Network Manager * Added FaceAnalysis and minor changes
1 parent e5fab16 commit 5dbdd75

6 files changed

Lines changed: 407 additions & 148 deletions

File tree

EasyCode.xcodeproj/project.pbxproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
BB3C76C82CFCDFDF005217AF /* View+extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB3C76C72CFCDFDF005217AF /* View+extension.swift */; };
3434
BB3C76CA2CFD502D005217AF /* UIApplication+extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB3C76C92CFD502D005217AF /* UIApplication+extension.swift */; };
3535
BB8A14152C36649C00F18CE8 /* UnmanagedWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8A14142C36649C00F18CE8 /* UnmanagedWrapper.swift */; };
36+
BBA9A1632DB77423002BC7FB /* FaceAnalysis.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBA9A1622DB77423002BC7FB /* FaceAnalysis.swift */; };
3637
BBAFFBC52C340D12005703B0 /* EasyCode.docc in Sources */ = {isa = PBXBuildFile; fileRef = BBAFFBC42C340D12005703B0 /* EasyCode.docc */; };
3738
BBAFFBCB2C340D12005703B0 /* EasyCode.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BBAFFBC02C340D12005703B0 /* EasyCode.framework */; };
3839
BBAFFBD02C340D12005703B0 /* EasyCodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBAFFBCF2C340D12005703B0 /* EasyCodeTests.swift */; };
@@ -145,6 +146,7 @@
145146
BB3C76C72CFCDFDF005217AF /* View+extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+extension.swift"; sourceTree = "<group>"; };
146147
BB3C76C92CFD502D005217AF /* UIApplication+extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+extension.swift"; sourceTree = "<group>"; };
147148
BB8A14142C36649C00F18CE8 /* UnmanagedWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnmanagedWrapper.swift; sourceTree = "<group>"; };
149+
BBA9A1622DB77423002BC7FB /* FaceAnalysis.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaceAnalysis.swift; sourceTree = "<group>"; };
148150
BBAFFBC02C340D12005703B0 /* EasyCode.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = EasyCode.framework; sourceTree = BUILT_PRODUCTS_DIR; };
149151
BBAFFBC32C340D12005703B0 /* EasyCode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EasyCode.h; sourceTree = "<group>"; };
150152
BBAFFBC42C340D12005703B0 /* EasyCode.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = EasyCode.docc; sourceTree = "<group>"; };
@@ -331,6 +333,7 @@
331333
children = (
332334
BBAFFBED2C341A98005703B0 /* Array+extension.swift */,
333335
BBBD51EF2C36A1E500557CD3 /* AVCaptureDevice+extension.swift */,
336+
BBE498C92D2D06930085233A /* BackgroundTaskManager.swift */,
334337
BBAFFBDB2C340DF2005703B0 /* BinaryFloatingPoint+extension.swift */,
335338
BBCC22D42C35208F00CC19C9 /* BiometricIDAuth.swift */,
336339
BBAFFC012C342126005703B0 /* Character+extension.swift */,
@@ -348,6 +351,7 @@
348351
BBAFFC292C342877005703B0 /* Encodable+extension.swift */,
349352
BBAFFBF12C341C31005703B0 /* EncryptionCryptoProtocol.swift */,
350353
BBAFFBF32C341C59005703B0 /* EncryptionError.swift */,
354+
BBA9A1622DB77423002BC7FB /* FaceAnalysis.swift */,
351355
BBAFFC152C342349005703B0 /* HostingView.swift */,
352356
BBBD51EB2C369B5700557CD3 /* ImageFilter.swift */,
353357
BBAFFBFD2C341FE4005703B0 /* JailbreakDetection.swift */,
@@ -395,7 +399,6 @@
395399
BB3B162F2CEC96910049F5F2 /* DI */,
396400
BB3B16422CEC979D0049F5F2 /* Keychain */,
397401
BB3C2EF92C6A0D3B00149867 /* Network Layer */,
398-
BBE498C92D2D06930085233A /* BackgroundTaskManager.swift */,
399402
);
400403
path = Source;
401404
sourceTree = "<group>";
@@ -607,6 +610,7 @@
607610
BB3C2F0B2C6A10F800149867 /* ServiceLocator.swift in Sources */,
608611
BBAFFBEE2C341A98005703B0 /* Array+extension.swift in Sources */,
609612
BB3B16312CEC96980049F5F2 /* DependencyInjector.swift in Sources */,
613+
BBA9A1632DB77423002BC7FB /* FaceAnalysis.swift in Sources */,
610614
BBAFFC182C342430005703B0 /* UIViewController+extension.swift in Sources */,
611615
BB3C76CA2CFD502D005217AF /* UIApplication+extension.swift in Sources */,
612616
BBBCE97C2CFCBAE70061DB04 /* Typealiases.swift in Sources */,

EasyCode/Source/FaceAnalysis.swift

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//
2+
// FaceAnalysis.swift
3+
// EasyCode
4+
//
5+
// Created by Zhanibek Lukpanov on 22.04.2025.
6+
//
7+
8+
import Foundation
9+
import UIKit.UIImage
10+
import Vision
11+
import CoreImage
12+
13+
public class FaceAnalysis {
14+
15+
public init() {}
16+
17+
private let context = CIContext()
18+
private lazy var detector = CIDetector(
19+
ofType: CIDetectorTypeFace,
20+
context: context,
21+
options: [CIDetectorAccuracy: CIDetectorAccuracyHigh]
22+
)
23+
24+
public func isSmileDetected(in image: UIImage) -> Bool {
25+
guard let ciImage = CIImage(image: image), let detector else { return false }
26+
27+
let options: [String: Any] = [
28+
CIDetectorSmile: true,
29+
CIDetectorImageOrientation: NSNumber(value: image.imageOrientation.rawValue)
30+
]
31+
32+
guard let faceFeatures = detector.features(in: ciImage, options: options) as? [CIFaceFeature],
33+
let faceFeature = faceFeatures.first else {
34+
return false
35+
}
36+
37+
return faceFeature.hasSmile
38+
}
39+
40+
public func areEyesOpen(in image: UIImage) -> Bool {
41+
guard let ciImage = CIImage(image: image), let detector else { return false }
42+
43+
let options: [String: Any] = [
44+
CIDetectorImageOrientation: NSNumber(value: image.imageOrientation.rawValue),
45+
CIDetectorEyeBlink: true
46+
]
47+
48+
guard let faceFeatures = detector.features(in: ciImage, options: options) as? [CIFaceFeature],
49+
let face = faceFeatures.first else {
50+
return false
51+
}
52+
53+
return !face.leftEyeClosed && !face.rightEyeClosed
54+
}
55+
56+
open func isMouthOpen(in image: UIImage) -> Bool {
57+
guard let ciImage = CIImage(image: image) else { return false }
58+
59+
let request = VNDetectFaceLandmarksRequest()
60+
let handler = VNImageRequestHandler(ciImage: ciImage, options: [:])
61+
var isMouthOpen = false
62+
63+
do {
64+
try handler.perform([request])
65+
if let firstFace = request.results?.first as? VNFaceObservation,
66+
let landmarks = firstFace.landmarks,
67+
let outerLips = landmarks.outerLips {
68+
69+
let maxY = outerLips.normalizedPoints.max(by: { $0.y < $1.y })?.y ?? 0
70+
let minY = outerLips.normalizedPoints.min(by: { $0.y < $1.y })?.y ?? 0
71+
let maxX = outerLips.normalizedPoints.max(by: { $0.x < $1.x })?.x ?? 0
72+
let minX = outerLips.normalizedPoints.min(by: { $0.x < $1.x })?.x ?? 0
73+
74+
let verticalOpenAmount = maxY - minY
75+
let horizontalExpandAmount = maxX - minX
76+
77+
let verticalThreshold: CGFloat = 0.27
78+
let horizontalThreshold: CGFloat = 0.2
79+
80+
isMouthOpen = verticalOpenAmount > verticalThreshold && horizontalExpandAmount > horizontalThreshold
81+
}
82+
} catch {
83+
print("Face landmarks detection failed:", error.localizedDescription)
84+
}
85+
return isMouthOpen
86+
}
87+
}

0 commit comments

Comments
 (0)