How to Build a Lunge Counter App

Building an AI-Powered Lunge Counter App with Pose Estimation Using QuickPose iOS SDK

Are you looking to create an AI-powered Lunge counter for your fitness app? Lunges are a versatile lower-body exercise that targets multiple muscle groups, including the quadriceps, hamstrings, glutes, and calves. This compound movement also engages the core muscles, providing stability and balance throughout the exercise. Lunges are essential for building leg strength, improving balance, and enhancing overall functional fitness.

In this guide, you’ll learn how to develop a Lunge counter app using pose estimation with the QuickPose iOS SDK. Whether you’re creating a new fitness app or adding advanced features to an existing one, this tutorial will help you leverage AI and pose estimation technology to deliver a precise and engaging Lunge Counter that stands out in the competitive fitness app market.

Image shows AI Lunge Counter counting lunges of the left leg. The GIF shows a man standing up facing the camera, he performs a lunge by stepping forward one step with his left foot. The AI lunge counter automatically recognises this exercise and counts the rep. The man returns to standing.

Steps to integrate an AI Lunge Counter into your app:

Register an SDK Key with QuickPose

Get your free SDK key on https://dev.quickpose.ai, usage limits may apply. SDK Keys are linked to your bundle ID, please check Key before distributing to the App Store. 

This is a quick look to integrate the Lunge counter using the QuickPose iOS SDK. You can see the full documentation here: QuickPose iOS SDK Lunge Counter installation.

Activate Lunge Feature

				
					feature = .fitness(.lunges)
feature = .fitness(.lunges, style: customOrConditionalStyle)
				
			

Form Feedback

We recommend using the feature feedback to guide the user if an error occurs.

				
					quickPose.start(features: [.fitness(.lunges)], onFrame: { status, image, features, feedback, landmarks in
    switch status {
        case .success:
            overlayImage = image
            if let result = features.values.first  {
                feedbackText = "Lunges: \(Int(result.value * 100))%"
            } else if let feedback = feedback.values.first, feedback.isRequired  {
                feedbackText = feedback.displayString
            } else {
                feedbackText = nil
            } 
        case .noPersonFound:
            feedbackText = "Stand in view";
        case .sdkValidationError:
            feedbackText = "Be back soon";
    }
})
				
			

Body position: "Stand up"

				
					.body(feedback: standFacingOrSide, isRequired: true) 
				
			

Exercise Specific: "Move right knee back" "Move left knee back"

				
					.joint(action: .move, joint: QuickPose.Landmarks.Body.knee(side: .right), direction: .back, isRequired: true)
.joint(action: .move, joint: QuickPose.Landmarks.Body.knee(side: .left), direction: .back, isRequired: true)
				
			

Conditional Styling

To give user feedback consider using conditional styling so that when the user’s measurement goes above a threshold, here 0.8, a green highlight is shown.

				
					let greenHighlightStyle = QuickPose.Style(conditionalColors: [QuickPose.Style.ConditionalColor(min: 0.8, max: nil, color: UIColor.green)])
quickPose.start(features: [.fitness(.lunges, style: customOrConditionalStyle)], 
                onFrame: { status, image, features, feedback, landmarks in  ...
})
				
			

How to Count Lunges

Real-time lunge counting with AI

To count the Lunges declare a configurable threshold counter, which can be used to turn lots of our features into counts.

				
					@State private var counter = QuickPoseThresholdCounter()
				
			

Then pass QuickPose’s Lunge result to the counter, and display in the feedback text declared above.

				
					quickPose.start(features: [.fitness(.lunges)], onFrame: { status, image, features, feedback, landmarks in
    switch status {
        case .success:
            overlayImage = image
            if let result = features.values.first  {
                let counterState = counter.count(result.value)
                feedbackText = "\(counterState.count) Lunges"
            } else {
                feedbackText = nil
            }
            
        case .noPersonFound:
            feedbackText = "Stand in view";
        case .sdkValidationError:
            feedbackText = "Be back soon";
    }
})
				
			

Lunges Timer

To time the Lunges declare a configurable threshold timer, which can be used to turn lots of our features into timers. For Lunges, we suggest modifying the default threshold, taking account of expected camera positioning and tilt.

				
					@State private var timer = QuickPoseThresholdTimer(threshold: 0.2)
				
			

Then pass the result’s raw value to the timer, and display in the feedback text declared above.

				
					quickPose.start(features: [.fitness(.lunges)], onFrame: { status, image, features, feedback, landmarks in
    switch status {
        case .success:
            overlayImage = image
            if let result = features.values.first  {
                let timerState = timer.time(result.value)
                feedbackText = String(format: "%.1f", timerState.time) + "secs"
            } else {
                feedbackText = nil
            }
            
        case .noPersonFound:
            feedbackText = "Stand in view";
        case .sdkValidationError:
            feedbackText = "Be back soon";
    }
})
				
			

Need help building an AI project?

At QuickPose, our mission is to build smart Pose Estimation Solutions that elevate your product. Schedule a free consultation with us to discuss your project.