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