Skip to main content

Yoga Custom Exercises

Detect if the user is in the correct yoga pose, such as Downward Dog or Warrior pose

This page describes a number of different techniques designed to creating custom Yoga exercises using QuickPose

Features​

To show multiple features you'll need to extend your quickpose call as described in the Getting Started Guide:

// Downward Dog
let ddRightShoulder = QuickPose.Feature.rangeOfMotion(.shoulder(side: .right, clockwiseDirection: false), style: yogaStyle)
let ddRightHip = QuickPose.Feature.rangeOfMotion(.hip(side: .right, clockwiseDirection: false), style: ninetyDegreeStyle)
let ddRightKnee = QuickPose.Feature.rangeOfMotion(.knee(side: .right, clockwiseDirection: false), style: yogaStyle)

// Warrior Pose
let warriorLeftShoulder = QuickPose.Feature.rangeOfMotion(.shoulder(side: .left, clockwiseDirection: false), style: ninetyDegreeStyle)
let warriorRightShoulder = QuickPose.Feature.rangeOfMotion(.shoulder(side: .right, clockwiseDirection: true), style: ninetyDegreeStyle)
let warriorRightKnee = QuickPose.Feature.rangeOfMotion(.knee(side: .right, clockwiseDirection: false), style: yogaStyle)
let warriorLeftKnee = QuickPose.Feature.rangeOfMotion(.knee(side: .left, clockwiseDirection: false), style: ninetyDegreeStyle)

Basic Implementation​​

To show results you'll need to modify your view ZStack, which we assume is setup as described in the Getting Started Guide:

ZStack(alignment: .top) {
QuickPoseCameraView(useFrontCamera: true, delegate: quickPose)
QuickPoseOverlayView(overlayImage: $overlayImage)
}

The basic implementation will require displaying some text to the screen, start with declaring this value in your SwiftUI view.

@State private var feedbackText: String? = nil

And show this feedback text as an overlay to the view in your branding.

ZStack(alignment: .top) {
QuickPoseCameraView(useFrontCamera: true, delegate: quickPose)
QuickPoseOverlayView(overlayImage: $overlayImage)
}
.overlay(alignment: .center) {
if let feedbackText = feedbackText {
Text(feedbackText)
.font(.system(size: 20, weight: .semibold)).foregroundColor(.white).multilineTextAlignment(.center)
.padding(12)
.background(RoundedRectangle(cornerRadius: 8).foregroundColor(Color("AccentColor").opacity(0.8)))
.padding(.bottom, 40)
}
}

Note the above use of alignment in .overlay(alignment: .center), you can modify this to move the overlay around easily to say the bottom: .overlay(alignment: .bottom).

Pose Detection​

To detect if the user is in the correct yoga pose, check the angle ranges for the relevant joints.

For Downward Dog:

  • Right shoulder angle between 160-190 degrees
  • Right hip angle between 60-100 degrees
  • Right knee angle between 160-190 degrees

For Warrior Pose:

  • Left and right shoulder angles between 80-130 degrees
  • Right knee angle between 80-120 degrees
  • Left knee angle between 160-190 degrees
quickPose.start(features: selectedFeatures, onFrame: { status, image, features, feedback, landmarks in
switch status {
case .success:
overlayImage = image

if selectedFeatures == QuickPose.Feature.allDemoFeatures(component: "Yoga").first {
// Handle downward dog pose detection
var isDownwardDog = true

if let yogaFeatures = QuickPose.Feature.allDemoFeatures(component: "Yoga").first {
for feature in yogaFeatures {
if let result = features[feature] {
let angle = result.value

if case .rangeOfMotion(let joint, _) = feature {
switch joint {
case .shoulder(side: .right, _):
isDownwardDog = isDownwardDog && (angle >= 160 && angle <= 190)
case .hip(side: .right, _):
isDownwardDog = isDownwardDog && (angle >= 60 && angle <= 100)
case .knee(side: .right, _):
isDownwardDog = isDownwardDog && (angle >= 160 && angle <= 190)
default:
break
}
}
}
}
}

// Update feedback based on pose
if isDownwardDog {
feedbackText = "✅ Yes Downward Dog!"
} else {
feedbackText = "Not downward dog"
}

} else if selectedFeatures == QuickPose.Feature.allDemoFeatures(component: "Yoga")[1] {
// Handle warrior pose detection
var isWarrior = true

let yogaFeatures = QuickPose.Feature.allDemoFeatures(component: "Yoga")[1]
for feature in yogaFeatures {
if let result = features[feature] {
let angle = result.value

if case .rangeOfMotion(let joint, _) = feature {
switch joint {
case .shoulder(side: .left, _), .shoulder(side: .right, _):
isWarrior = isWarrior && (angle >= 80 && angle <= 130)
case .knee(side: .right, _):
isWarrior = isWarrior && (angle >= 80 && angle <= 120)
case .knee(side: .left, _):
isWarrior = isWarrior && (angle >= 160 && angle <= 190)
default:
break
}
}
}
}

// Update feedback based on pose
if isWarrior {
feedbackText = "✅ Yes Warrior Pose!"
} else {
feedbackText = "Not in warrior pose"
}
}

case .noPersonFound:
feedbackText = "Stand in view"
case .sdkValidationError:
feedbackText = "Be back soon"
}
})

Conditional Styling​

To give the user visual feedback on their pose, use conditional styling to highlight joints in green when they are in the correct angle range.

let yogaStyle = QuickPose.Style(conditionalColors: [
QuickPose.Style.ConditionalColor(min: 170, max: 190, color: UIColor.green) // For straight limbs (180 degrees)
])
let ninetyDegreeStyle = QuickPose.Style(conditionalColors: [
QuickPose.Style.ConditionalColor(min: 80, max: 100, color: UIColor.green) // For 90 degree angles
])

Apply the styles when creating the yoga pose features:

// Downward Dog features
let ddRightShoulder = QuickPose.Feature.rangeOfMotion(.shoulder(side: .right, clockwiseDirection: false), style: yogaStyle)
let ddRightHip = QuickPose.Feature.rangeOfMotion(.hip(side: .right, clockwiseDirection: false), style: ninetyDegreeStyle)
let ddRightKnee = QuickPose.Feature.rangeOfMotion(.knee(side: .right, clockwiseDirection: false), style: yogaStyle)

// Warrior pose features
let warriorLeftShoulder = QuickPose.Feature.rangeOfMotion(.shoulder(side: .left, clockwiseDirection: false), style: ninetyDegreeStyle)
let warriorRightShoulder = QuickPose.Feature.rangeOfMotion(.shoulder(side: .right, clockwiseDirection: true), style: ninetyDegreeStyle)
let warriorRightKnee = QuickPose.Feature.rangeOfMotion(.knee(side: .right, clockwiseDirection: false), style: yogaStyle)
let warriorLeftKnee = QuickPose.Feature.rangeOfMotion(.knee(side: .left, clockwiseDirection: false), style: ninetyDegreeStyle)

This will highlight the user's joints in green when they are in the correct alignment for the yoga pose.