christ
This commit is contained in:
@@ -65,7 +65,8 @@ class EyeTrackingService: NSObject, ObservableObject {
|
||||
faceWidthScaleMax: 1.4,
|
||||
eyeBoundsHorizontalPadding: TrackingConfig.default.eyeBoundsHorizontalPadding,
|
||||
eyeBoundsVerticalPaddingUp: TrackingConfig.default.eyeBoundsVerticalPaddingUp,
|
||||
eyeBoundsVerticalPaddingDown: TrackingConfig.default.eyeBoundsVerticalPaddingDown
|
||||
eyeBoundsVerticalPaddingDown: TrackingConfig.default.eyeBoundsVerticalPaddingDown,
|
||||
eyeBoundsSmoothing: TrackingConfig.default.eyeBoundsSmoothing
|
||||
)
|
||||
|
||||
processor.updateConfig(config)
|
||||
|
||||
@@ -68,7 +68,8 @@ public struct TrackingConfig: Sendable {
|
||||
faceWidthScaleMax: Double,
|
||||
eyeBoundsHorizontalPadding: Double,
|
||||
eyeBoundsVerticalPaddingUp: Double,
|
||||
eyeBoundsVerticalPaddingDown: Double
|
||||
eyeBoundsVerticalPaddingDown: Double,
|
||||
eyeBoundsSmoothing: Double
|
||||
) {
|
||||
self.horizontalAwayThreshold = horizontalAwayThreshold
|
||||
self.verticalAwayThreshold = verticalAwayThreshold
|
||||
@@ -86,6 +87,7 @@ public struct TrackingConfig: Sendable {
|
||||
self.eyeBoundsHorizontalPadding = eyeBoundsHorizontalPadding
|
||||
self.eyeBoundsVerticalPaddingUp = eyeBoundsVerticalPaddingUp
|
||||
self.eyeBoundsVerticalPaddingDown = eyeBoundsVerticalPaddingDown
|
||||
self.eyeBoundsSmoothing = eyeBoundsSmoothing
|
||||
}
|
||||
|
||||
public let horizontalAwayThreshold: Double
|
||||
@@ -104,6 +106,7 @@ public struct TrackingConfig: Sendable {
|
||||
public let eyeBoundsHorizontalPadding: Double
|
||||
public let eyeBoundsVerticalPaddingUp: Double
|
||||
public let eyeBoundsVerticalPaddingDown: Double
|
||||
public let eyeBoundsSmoothing: Double
|
||||
|
||||
public static let `default` = TrackingConfig(
|
||||
horizontalAwayThreshold: 0.08,
|
||||
@@ -121,6 +124,7 @@ public struct TrackingConfig: Sendable {
|
||||
faceWidthScaleMax: 1.4,
|
||||
eyeBoundsHorizontalPadding: 0.1,
|
||||
eyeBoundsVerticalPaddingUp: 0.9,
|
||||
eyeBoundsVerticalPaddingDown: 0.4
|
||||
eyeBoundsVerticalPaddingDown: 0.4,
|
||||
eyeBoundsSmoothing: 0.2
|
||||
)
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ final class VisionGazeProcessor: @unchecked Sendable {
|
||||
private let baselineModel = GazeBaselineModel()
|
||||
private var faceWidthBaseline: Double?
|
||||
private var faceWidthSmoothed: Double?
|
||||
private var leftEyeFrameSmoothed: CGRect?
|
||||
private var rightEyeFrameSmoothed: CGRect?
|
||||
private var config: TrackingConfig
|
||||
|
||||
init(config: TrackingConfig) {
|
||||
@@ -46,6 +48,8 @@ final class VisionGazeProcessor: @unchecked Sendable {
|
||||
baselineModel.reset()
|
||||
faceWidthBaseline = nil
|
||||
faceWidthSmoothed = nil
|
||||
leftEyeFrameSmoothed = nil
|
||||
rightEyeFrameSmoothed = nil
|
||||
}
|
||||
|
||||
func process(analysis: VisionPipeline.FaceAnalysis) -> ObservationResult {
|
||||
@@ -77,13 +81,15 @@ final class VisionGazeProcessor: @unchecked Sendable {
|
||||
eye: landmarks.leftEye,
|
||||
pupil: landmarks.leftPupil,
|
||||
face: face,
|
||||
imageSize: analysis.imageSize
|
||||
imageSize: analysis.imageSize,
|
||||
smoothingRect: &leftEyeFrameSmoothed
|
||||
)
|
||||
let rightEye = makeEyeObservation(
|
||||
eye: landmarks.rightEye,
|
||||
pupil: landmarks.rightPupil,
|
||||
face: face,
|
||||
imageSize: analysis.imageSize
|
||||
imageSize: analysis.imageSize,
|
||||
smoothingRect: &rightEyeFrameSmoothed
|
||||
)
|
||||
|
||||
let eyesClosed = detectEyesClosed(left: leftEye, right: rightEye)
|
||||
@@ -126,7 +132,8 @@ final class VisionGazeProcessor: @unchecked Sendable {
|
||||
eye: VNFaceLandmarkRegion2D?,
|
||||
pupil: VNFaceLandmarkRegion2D?,
|
||||
face: VNFaceObservation,
|
||||
imageSize: CGSize
|
||||
imageSize: CGSize,
|
||||
smoothingRect: inout CGRect?
|
||||
) -> EyeObservation? {
|
||||
guard let eye else { return nil }
|
||||
|
||||
@@ -142,8 +149,10 @@ final class VisionGazeProcessor: @unchecked Sendable {
|
||||
pupilPoint = bounds.center
|
||||
}
|
||||
|
||||
let rawFrame = CGRect(x: bounds.minX, y: bounds.minY, width: bounds.size.width, height: bounds.size.height)
|
||||
let smoothedFrame = smoothRect(rawFrame, existing: &smoothingRect, smoothing: config.eyeBoundsSmoothing)
|
||||
let paddedFrame = expandRect(
|
||||
CGRect(x: bounds.minX, y: bounds.minY, width: bounds.size.width, height: bounds.size.height),
|
||||
smoothedFrame,
|
||||
horizontalPadding: config.eyeBoundsHorizontalPadding,
|
||||
verticalPaddingUp: config.eyeBoundsVerticalPaddingUp,
|
||||
verticalPaddingDown: config.eyeBoundsVerticalPaddingDown
|
||||
@@ -238,6 +247,26 @@ final class VisionGazeProcessor: @unchecked Sendable {
|
||||
)
|
||||
}
|
||||
|
||||
private func smoothRect(_ rect: CGRect, existing: inout CGRect?, smoothing: Double) -> CGRect {
|
||||
guard smoothing > 0, smoothing < 1 else {
|
||||
existing = rect
|
||||
return rect
|
||||
}
|
||||
|
||||
if let current = existing {
|
||||
let newOriginX = current.origin.x + (rect.origin.x - current.origin.x) * smoothing
|
||||
let newOriginY = current.origin.y + (rect.origin.y - current.origin.y) * smoothing
|
||||
let newWidth = current.size.width + (rect.size.width - current.size.width) * smoothing
|
||||
let newHeight = current.size.height + (rect.size.height - current.size.height) * smoothing
|
||||
let updated = CGRect(x: newOriginX, y: newOriginY, width: newWidth, height: newHeight)
|
||||
existing = updated
|
||||
return updated
|
||||
}
|
||||
|
||||
existing = rect
|
||||
return rect
|
||||
}
|
||||
|
||||
private func averageCoordinate(left: CGFloat?, right: CGFloat?, fallback: Double?) -> Double? {
|
||||
switch (left, right) {
|
||||
case let (left?, right?):
|
||||
|
||||
Reference in New Issue
Block a user