Reputation: 39
So I want to access this variable squatCount from another class called PoseEstimator
and print it out in a UILabel in another viewController.But for some reason when I do that, the label prints out the value of squatCount
as zero even though the variable clearly increased.
I've read online and googled for hours the forums that I read through talking about implementing some global variable or singleton or some enum thingy I don't really understand it quite new to swift. So I'm trying to find out why this happens and solve it. Help is greatly appreciated.
PoseEstimator Code:
import AVFoundation
import Vision
import Combine
import SwiftUI
//declaring PoseEstimator as a class. We need to add AVCaptureVideoDataOutputSampleBufferDelegate and make it an obserable object to use PoseEsimation on our AVFoundation video output
class PoseEstimator: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, ObservableObject {
let sequenceHandler = VNSequenceRequestHandler()
@Published var bodyParts = [VNHumanBodyPoseObservation.JointName : VNRecognizedPoint]() //declare body parts as a published variable storing the coordinates of the body joints from Vision
var seconds = 0 //This variable will hold a starting value of seconds. It could be any amount above 0.
var timer = Timer()
var isTimerRunning = false//This will be used to make sure only one timer is created at a time.
var elaspedTime = Float()//we need it to declare it as a float because we would be storing numbers with decimals inside//This one is for Planks the one above is to fix a lunge bug
var halfburpee = 0
var halfLunge = 0
var halfPU = 0
var uprightLunge = false
var wasInBottomPosition = false //declare the position of the player as top or bottom
var wasInBottomLunge = false
var wasInTopJJ = false
var wasInBottomPU = false
var wasInDeadLift = false
var wasInMidBurpee = false
var wasInBottomBurpee = false
var wasinupright = false
var wasinalmostdoneBurpee = false
var down = false
var cooldown = true
@Published var squatCount = 0 //declare variables for no of exercises done
@Published var LungeCounter = 0
@Published var JumpingJackCounter = 0
@Published var PushupCounter = 0
@Published var BurpeesCounter = 0
@Published var SLDeadliftsCounter = 0
@Published var caloriesBurntSquats = Float(0)
@Published var caloriesBurntLunges = Float(0)
@Published var caloriesBurntJJ = Float()
@Published var caloriesBurntPushups = Float(0)
@Published var caloriesBurntBurpees = Float(0)
@Published var caloriesBurntSLD = Float(0)
@Published var isGoodSquatPosture = true
@Published var isGoodLungePosture = true
@Published var isGoodJJPosture = true
@Published var isGoodPUPosture = true
@Published var isGoodSLDPosture = true
var subscriptions = Set<AnyCancellable>() //for combine to store the proessed coordinates of Vision
func runTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(PoseEstimator.updateTimer)), userInfo: nil, repeats: true)
}
@objc func updateTimer() {
seconds += 1 //This will decrement(count down)the seconds.
if seconds >= 2{
self.cooldown = false
}else{
self.cooldown = true
}
}
override init() {
super.init()
$bodyParts
.dropFirst()
.sink(receiveValue: { bodyParts in self.Process(bodyParts: bodyParts)})
.store(in: &subscriptions)
}
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
let humanBodyRequest = VNDetectHumanBodyPoseRequest(completionHandler: detectedBodyPose)
do {
try sequenceHandler.perform( //sent request to detect human body joints from AVFoundation
[humanBodyRequest],
on: sampleBuffer,
orientation: .right)
} catch {
print(error.localizedDescription)
}
}
func detectedBodyPose(request: VNRequest, error: Error?) {
guard let bodyPoseResults = request.results as? [VNHumanBodyPoseObservation] //set bodyPoseResults as the the reuslts from Vision
else { return }
guard let bodyParts = try? bodyPoseResults.first?.recognizedPoints(.all) else { return }
DispatchQueue.main.async {
self.bodyParts = bodyParts //set bodyparts as the recognised points from vision
}
}
func Process(bodyParts: [VNHumanBodyPoseObservation.JointName : VNRecognizedPoint]) { // function for processing results from vision
let rightKnee = bodyParts[.rightKnee]!.location // declare variables that we need to determine if the player is doing exercise
let leftKnee = bodyParts[.leftKnee]!.location
let rightHip = bodyParts[.rightHip]!.location
let rightAnkle = bodyParts[.rightAnkle]!.location
let leftAnkle = bodyParts[.leftAnkle]!.location
let leftHip = bodyParts[.leftHip]!.location
let rightShoulder = bodyParts[.rightShoulder]!.location
let leftShoulder = bodyParts[.leftShoulder]!.location
let rightElbow = bodyParts[.rightElbow]!.location
let rightWrist = bodyParts[.rightWrist]!.location
let leftWrist = bodyParts[.leftWrist]!.location
let root = bodyParts[.root]!.location
let neck = bodyParts[.neck]!.location
//squats angles decleration
let firstAngle = atan2(rightHip.y - rightKnee.y, rightHip.x - rightKnee.x) // simple trigonometry to determine the angles required
let secondAngle = atan2(rightAnkle.y - rightKnee.y, rightAnkle.x - rightKnee.x)
var angleDiffRadians = firstAngle - secondAngle //calculate the difference between the angles
//Jumping Jacks angles decleration
let firstJJAngle = atan2(rightHip.y - rightShoulder.y, rightHip.x - rightShoulder.x)
let secondJJAngle = atan2(rightElbow.y - rightShoulder.y, rightElbow.x - rightShoulder.x) //calculate angles of armpit
var JJangleDiffRadians = firstJJAngle - secondJJAngle
let firstrootAngle = atan2(rightKnee.y - root.y, rightKnee.x - root.x)
let secondrootAngle = atan2(leftKnee.y - root.y, leftKnee.x - root.x)
var rootangleDiffRadians = firstrootAngle + secondrootAngle
//Pushups anlges decleration
let firstPUAngle = atan2(rightShoulder.y - rightElbow.y, rightShoulder.x - rightElbow.x)
let secondPUAngle = atan2(rightWrist.y - rightElbow.y, rightWrist.x - rightElbow.x)
var PUAngleDiffRadians = firstPUAngle - secondPUAngle
//SLDeadlifts angles decleration
let firstDeadliftAngle = atan2(neck.y - root.y, neck.x - root.x)
let secondDeadLiftAngle = atan2(rightKnee.y - root.y, rightKnee.x - root.x)
var SLDangleDiffRadians = firstDeadliftAngle - secondDeadLiftAngle
let firstLegAngle = atan2(leftAnkle.y - leftKnee.y, leftAnkle.x - leftKnee.x)
let secondLegAngle = atan2(leftHip.y - leftKnee.y, leftHip.x - leftKnee.x)
var legAngleDiffRadians = firstLegAngle - secondLegAngle
//Burpees angles decleration
let firstBurpeeAngle = atan2(rightElbow.y - rightShoulder.y, rightElbow.x - rightShoulder.x)
let secondBurpeeAngle = atan2(neck.y - rightShoulder.y, neck.x - rightShoulder.x)
var BurpeeAngleDiffRadians = firstBurpeeAngle - secondBurpeeAngle
//conversion of angles in radians to degrees
while angleDiffRadians < 0 {
angleDiffRadians += CGFloat(2 * Double.pi)
}
while JJangleDiffRadians < 0{
JJangleDiffRadians += CGFloat(2 * Double.pi)
}
while rootangleDiffRadians < 0{
rootangleDiffRadians += CGFloat(2 * Double.pi)
}
while PUAngleDiffRadians < 0{
PUAngleDiffRadians += CGFloat(2 * Double.pi)
}
while SLDangleDiffRadians < 0{
SLDangleDiffRadians += CGFloat(2 * Double.pi)
}
while legAngleDiffRadians < 0{
legAngleDiffRadians += CGFloat(2 * Double.pi)
}
while BurpeeAngleDiffRadians < 0{
BurpeeAngleDiffRadians += CGFloat(2 * Double.pi)
}
let angleDiffDegrees = Int(angleDiffRadians * 180 / .pi)
let rootangleDIffDegrees = Int(rootangleDiffRadians * 180 / .pi)
let JJangleDIffDegrees = Int(JJangleDiffRadians * 180 / .pi)
let PUAngleDiffDegrees = Int(PUAngleDiffRadians * 180 / .pi)
let SLDAngleDiffDegrees = Int(SLDangleDiffRadians * 180 / .pi)
let legAngleDiffDegrees = Int(legAngleDiffRadians * 180 / .pi)
let BurpeeAngleDiffDegrees = Int(BurpeeAngleDiffRadians * 180 / .pi)
//process whether the player is doing a squat
if angleDiffDegrees > 150 && self.wasInBottomPosition == true{ // determine if the player is doing a squat
self.wasInBottomPosition = false
self.squatCount += 1
self.caloriesBurntSquats = Float(self.squatCount) * 0.32
}
//process whether the player is doing a lunge
if angleDiffDegrees > 160 && wasInBottomLunge == true{
self.LungeCounter += 1
self.wasInBottomLunge = false
self.caloriesBurntLunges = Float(self.LungeCounter) * 0.3
}
//process whether the player is doing a Jumping Jack
if rootangleDIffDegrees < 180 && JJangleDIffDegrees <= 90 && self.wasInTopJJ{
self.JumpingJackCounter += 1
self.wasInTopJJ = false
self.caloriesBurntJJ = Float(self.JumpingJackCounter) * 0.2
}
//process whether the player is doing a pushup
if PUAngleDiffDegrees > 167 && wasInBottomPU{
halfPU += 1
wasInBottomPU = false
self.caloriesBurntPushups = Float(self.PushupCounter) * 0.6
}
if halfPU >= 2{
self.halfPU = 0
self.PushupCounter += 1
}
//process whether the player is doing a SLDeadlift
if SLDAngleDiffDegrees >= 215 && wasInDeadLift{
SLDeadliftsCounter += 1
wasInDeadLift = false
self.caloriesBurntSLD = Float(self.SLDeadliftsCounter) * 0.2
}
//process whether the player is doing a burpee
if self.wasinupright == true && self.wasinalmostdoneBurpee == true{
self.halfburpee += 1
self.wasinupright = false
self.wasInBottomBurpee = false
self.wasInMidBurpee = false
self.down = false
self.wasinalmostdoneBurpee = false
self.caloriesBurntBurpees = Float(self.BurpeesCounter) * 0.5
}
if self.halfburpee >= 2{
self.BurpeesCounter += 1
self.halfburpee = 0
}
//proess whether the player is in the top/bottom position of the exercise
let hipHeight = rightHip.y
let lefthipHeight = leftHip.y
let kneeHeight = rightKnee.y
let leftkneeHeight = leftKnee.y
let ankleheight = rightAnkle.y
let shoulderheight = rightShoulder.y
let elbowHeight = rightElbow.y
let wristheight = leftWrist.y
//squats
if hipHeight < kneeHeight { //determing if the user is in the bottom position of the squat
self.wasInBottomPosition = true
}
//lunges
if leftkneeHeight <= ankleheight {
self.wasInBottomLunge = true
}
if leftkneeHeight == kneeHeight {
self.uprightLunge = true
}else{
self.uprightLunge = false
}
//Jumping jacks
if elbowHeight > shoulderheight && JJangleDIffDegrees >= 130 && rootangleDIffDegrees > 175{ // determining if the user's arms are above the shoulders
self.wasInTopJJ = true
}
//Pushups
if PUAngleDiffDegrees <= 150 {
self.wasInBottomPU = true
}
//SL deadlifts
if hipHeight > lefthipHeight{
self.wasInDeadLift = true
}
//Burpees
if BurpeeAngleDiffDegrees >= 240{
self.down = true
}
if kneeHeight >= elbowHeight{
self.wasInMidBurpee = true
}
if kneeHeight < elbowHeight && self.wasInMidBurpee == true && self.down == true{
self.wasInBottomBurpee = true
}
if kneeHeight >= elbowHeight && self.wasInBottomBurpee == true{
self.wasinalmostdoneBurpee = true
}
if wristheight > ankleheight{
self.wasinupright = true
}else{
self.wasinupright = false
}
let kneeDistance = rightKnee.distance(to: leftKnee)
let ankleDistance = rightAnkle.distance(to: leftAnkle)
let wristDistance = rightWrist.distance(to: leftWrist)
let shoulderDistance = rightShoulder.distance(to: leftShoulder)
if ankleDistance > kneeDistance { //checking if the posture of the player doing squats is good
self.isGoodSquatPosture = false
} else {
self.isGoodSquatPosture = true
}
if rootangleDIffDegrees >= 180 && elbowHeight <= shoulderheight{ //posture check for Jumping Jacks
self.isGoodJJPosture = false
} else{
self.isGoodJJPosture = true
}
if (wristDistance - 1) != shoulderDistance { //posture check for PushUps
self.isGoodPUPosture = false
}else{
self.isGoodPUPosture = true
}
if legAngleDiffDegrees > 175{ //posture check for Single-Leg Deadlifts
self.isGoodSLDPosture = false
}else{
self.isGoodSLDPosture = true
}
}
}
my viewController code:
import SwiftUI
class DoneViewController: UIViewController {
var exerciseLoaded = false
var burntCalories = Float()
let poseEstimator = PoseEstimator()
@IBOutlet weak var repsDone: UILabel!
@IBOutlet weak var caloriesBurnt: UILabel!
@IBOutlet weak var exerciseMoreBtn: UIButton!
@IBOutlet weak var HomeWhenDone:
UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
exerciseMoreBtn.layer.cornerRadius = 10
HomeWhenDone.layer.cornerRadius = 10
repsDone.text = String(poseEstimator.squatCount)
}
Upvotes: 0
Views: 728
Reputation: 758
Add this singleton to the PoseEstimator class
static let shared = PoseEstimator()
then in DoneViewController change
let poseEstimator = PoseEstimator()
to:
let poseEstimator = PoseEstimator.shared
The reason why this works is because it will be a reference to the same object. Currently you are creating a new instance by calling init from the view controller. You could also pass a reference into the view controller during navigation or retrieve it, but this is the simplest way to get you going.
Upvotes: 2