added documentation
This commit is contained in:
parent
a49d98c171
commit
1c0cde6627
|
@ -10,41 +10,17 @@ import LinderaDetection
|
|||
|
||||
class ViewController: UIViewController {
|
||||
|
||||
//MARK: - UI Elements
|
||||
|
||||
|
||||
@IBOutlet var liveView : UIView!
|
||||
@IBOutlet var showLandmarksButton: UIButton!
|
||||
@IBOutlet var chooseModelButton: UIButton!
|
||||
@IBOutlet var titleview: UIView!
|
||||
@IBOutlet var fpsLabel: UILabel!
|
||||
|
||||
func updateLandmarksButtonText(){
|
||||
if (lindera.areLandmarksShown()){
|
||||
showLandmarksButton.setTitle("LANDMARKS (ON)", for: UIControl.State.normal)
|
||||
}else{
|
||||
showLandmarksButton.setTitle("LANDMARKS (OFF)", for: UIControl.State.normal)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
func updateModelButtonText(){
|
||||
var text = "MODEL "
|
||||
switch(lindera.getModelComplexity()){
|
||||
|
||||
case 0:
|
||||
text += "(LITE)"
|
||||
break;
|
||||
case 1:
|
||||
text += "(FULL)"
|
||||
break;
|
||||
case 2:
|
||||
text += "(HEAVY)"
|
||||
break;
|
||||
|
||||
default:
|
||||
text += "(Unknown)"
|
||||
}
|
||||
chooseModelButton.setTitle(text, for: UIControl.State.normal)
|
||||
}
|
||||
//MARK: - UI Actions
|
||||
|
||||
@IBAction func setModelComplexity(){
|
||||
let alert = UIAlertController(
|
||||
|
@ -86,11 +62,9 @@ class ViewController: UIViewController {
|
|||
lindera.showLandmarks(value: !lindera.areLandmarksShown());
|
||||
updateLandmarksButtonText()
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
let lindera = Lindera()
|
||||
|
||||
// MARK: - LinderaDelegate
|
||||
|
||||
/// A simple LinderaDelegate implementation that prints nose coordinates if detected
|
||||
class LinderaDelegateImpl:LinderaDelegate{
|
||||
|
@ -103,25 +77,58 @@ class ViewController: UIViewController {
|
|||
|
||||
|
||||
}
|
||||
// MARK: - UI Text Modifications
|
||||
func updateLandmarksButtonText(){
|
||||
if (lindera.areLandmarksShown()){
|
||||
showLandmarksButton.setTitle("LANDMARKS (ON)", for: UIControl.State.normal)
|
||||
}else{
|
||||
showLandmarksButton.setTitle("LANDMARKS (OFF)", for: UIControl.State.normal)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func updateModelButtonText(){
|
||||
var text = "MODEL "
|
||||
switch(lindera.getModelComplexity()){
|
||||
|
||||
case 0:
|
||||
text += "(LITE)"
|
||||
break;
|
||||
case 1:
|
||||
text += "(FULL)"
|
||||
break;
|
||||
case 2:
|
||||
text += "(HEAVY)"
|
||||
break;
|
||||
|
||||
default:
|
||||
text += "(Unknown)"
|
||||
}
|
||||
chooseModelButton.setTitle(text, for: UIControl.State.normal)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: - State Objects
|
||||
|
||||
let lindera = Lindera()
|
||||
|
||||
let linderaDelegate = LinderaDelegateImpl()
|
||||
|
||||
|
||||
// MARK: - UI Setup
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// // Do any additional setup after loading the view.
|
||||
//
|
||||
//
|
||||
self.lindera.delegate = linderaDelegate
|
||||
|
||||
// add lindera camera view to our app's UIView i.e. liveView
|
||||
|
||||
// Expand our cameraView frame to liveView frame
|
||||
if let view = self.liveView{
|
||||
// add lindera camera view to our app's UIView i.e. liveView
|
||||
view.addSubview(lindera.cameraView)
|
||||
// Expand our cameraView frame to liveView frame
|
||||
self.lindera.cameraView.frame = view.bounds
|
||||
|
||||
// Setting Up Constraints (No necessary with above statement)
|
||||
self.lindera.cameraView.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
self.lindera.cameraView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
|
@ -131,20 +138,25 @@ class ViewController: UIViewController {
|
|||
])
|
||||
}
|
||||
|
||||
|
||||
lindera.startCamera()
|
||||
// This function is called whenver there is an fps update
|
||||
self.lindera.setFpsDelegate(fpsDelegate: {[weak self] fps in
|
||||
DispatchQueue.main.async {
|
||||
self?.fpsLabel.text = "\(Int(fps)) fps"
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
// Otherwise they are hidden
|
||||
self.liveView.bringSubviewToFront(titleview)
|
||||
self.liveView.bringSubviewToFront(fpsLabel)
|
||||
|
||||
// Make the Landmarks and Model button text reflect the state in lindera object
|
||||
updateLandmarksButtonText()
|
||||
updateModelButtonText()
|
||||
|
||||
lindera.startCamera()
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -6,34 +6,40 @@ import UIKit
|
|||
/// A helper class to run the Pose Tracking API
|
||||
/// TFLite models are also loaded when you initialize this class
|
||||
public final class Lindera{
|
||||
// initalize the PoseTracking api and load models
|
||||
var poseTracking:PoseTracking = PoseTracking(poseTrackingOptions: PoseTrackingOptions(showLandmarks: true,modelComplexity: 1))
|
||||
let fpsHelper = FPSHelper(smoothingFactor: 0.95)
|
||||
public func setFpsDelegate(fpsDelegate: @escaping (_ fps:Double)->Void){
|
||||
fpsHelper.onFpsUpdate = fpsDelegate;
|
||||
}
|
||||
|
||||
// attach Mediapipe camera helper to our class
|
||||
let cameraSource = MPPCameraInputSource()
|
||||
|
||||
|
||||
//MARK: - Public Class API
|
||||
|
||||
|
||||
// A delegate to handle results
|
||||
public weak var delegate: LinderaDelegate?
|
||||
|
||||
/// This function sets up your callback function to happen whenver there is an fps update
|
||||
public func setFpsDelegate(fpsDelegate: @escaping (_ fps:Double)->Void){
|
||||
fpsHelper.onFpsUpdate = fpsDelegate;
|
||||
}
|
||||
|
||||
// Get the camera UI View that may contain landmarks drawing
|
||||
public var cameraView: UIView {
|
||||
return self.linderaExerciseSession
|
||||
}
|
||||
|
||||
|
||||
// Show Landmarks - works instantaneously!
|
||||
public func showLandmarks(value:Bool){
|
||||
self.poseTracking.showLandmarks(value)
|
||||
}
|
||||
|
||||
|
||||
// Are landmarks already drawn?
|
||||
public func areLandmarksShown() -> Bool{
|
||||
return self.poseTracking.areLandmarksShown()
|
||||
}
|
||||
// Current Model Complexity 0 -> lite; 1 -> full ; 2 -> heavy
|
||||
public func getModelComplexity() -> Int {
|
||||
return Int(self.poseTracking.poseTrackingOptions.modelComplexity);
|
||||
}
|
||||
|
||||
// Set the model complexity and restart detection to load new models
|
||||
public func setModelComplexityNow(complexity:Int){
|
||||
let poseTrackingOptions = poseTracking.poseTrackingOptions
|
||||
|
||||
|
@ -43,54 +49,11 @@ public final class Lindera{
|
|||
startPoseTracking()
|
||||
startCamera()
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private lazy var linderaExerciseSession: UIView = {
|
||||
|
||||
// this will be the main camera view
|
||||
let liveView = UIView()
|
||||
|
||||
startPoseTracking()
|
||||
|
||||
|
||||
|
||||
return liveView
|
||||
|
||||
}()
|
||||
private func startPoseTracking(){
|
||||
// set camera preferences
|
||||
self.cameraSource.sessionPreset = AVCaptureSession.Preset.high.rawValue
|
||||
self.cameraSource.cameraPosition = AVCaptureDevice.Position.front
|
||||
self.cameraSource.orientation = AVCaptureVideoOrientation.portrait
|
||||
if (self.cameraSource.orientation == AVCaptureVideoOrientation.portrait){
|
||||
self.cameraSource.videoMirrored = true
|
||||
}
|
||||
// call LinderaDelegate on pose tracking results
|
||||
self.poseTracking.poseTrackingResultsListener = {[weak self] results in
|
||||
|
||||
|
||||
guard let self = self, let results = results else {
|
||||
return
|
||||
}
|
||||
|
||||
self.delegate?.lindera(self, didDetect: .init(pose: Asensei3DPose.init(results), timestamp: CMTimeGetSeconds(self.poseTracking.timeStamp)))
|
||||
}
|
||||
self.poseTracking.graphOutputStreamListener = {[weak self] in
|
||||
self?.fpsHelper.logTime()
|
||||
}
|
||||
|
||||
self.poseTracking.startGraph()
|
||||
// attach camera's output with poseTracking object and its videoQueue
|
||||
self.cameraSource.setDelegate(self.poseTracking, queue: self.poseTracking.videoQueue)
|
||||
}
|
||||
public required init(){
|
||||
|
||||
|
||||
startPoseTracking()
|
||||
}
|
||||
|
||||
|
||||
|
@ -120,6 +83,56 @@ public final class Lindera{
|
|||
|
||||
|
||||
}
|
||||
/// Choose front or back camera. Must restart camera after use if already started
|
||||
public func selectCamera(_ position: AVCaptureDevice.Position, _ completion: ((Result<Void, Error>) -> Void)? = nil) {
|
||||
self.poseTracking.videoQueue.async { [weak self] in
|
||||
self?.cameraSource.cameraPosition = position
|
||||
completion?(.success(Void()))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Private Class Functions
|
||||
|
||||
// Set your custom view heree
|
||||
private lazy var linderaExerciseSession: UIView = {
|
||||
|
||||
// this will be the main camera view; Change it to custom view class to get desired results
|
||||
let liveView = UIView()
|
||||
|
||||
return liveView
|
||||
|
||||
}()
|
||||
|
||||
|
||||
private func startPoseTracking(){
|
||||
// set camera preferences
|
||||
self.cameraSource.sessionPreset = AVCaptureSession.Preset.high.rawValue
|
||||
self.cameraSource.cameraPosition = AVCaptureDevice.Position.front
|
||||
self.cameraSource.orientation = AVCaptureVideoOrientation.portrait
|
||||
if (self.cameraSource.orientation == AVCaptureVideoOrientation.portrait){
|
||||
self.cameraSource.videoMirrored = true
|
||||
}
|
||||
// call LinderaDelegate on pose tracking results
|
||||
self.poseTracking.poseTrackingResultsListener = {[weak self] results in
|
||||
|
||||
|
||||
guard let self = self, let results = results else {
|
||||
return
|
||||
}
|
||||
|
||||
self.delegate?.lindera(self, didDetect: .init(pose: Asensei3DPose.init(results), timestamp: CMTimeGetSeconds(self.poseTracking.timeStamp)))
|
||||
}
|
||||
self.poseTracking.graphOutputStreamListener = {[weak self] in
|
||||
self?.fpsHelper.logTime()
|
||||
}
|
||||
|
||||
self.poseTracking.startGraph()
|
||||
// attach camera's output with poseTracking object and its videoQueue
|
||||
self.cameraSource.setDelegate(self.poseTracking, queue: self.poseTracking.videoQueue)
|
||||
}
|
||||
|
||||
|
||||
func stopCamera(){
|
||||
if (self.cameraSource.isRunning){
|
||||
|
@ -157,19 +170,23 @@ public final class Lindera{
|
|||
}
|
||||
}
|
||||
|
||||
/// Choose front or back camera. Must restart camera after use if already started
|
||||
public func selectCamera(_ position: AVCaptureDevice.Position, _ completion: ((Result<Void, Error>) -> Void)? = nil) {
|
||||
self.poseTracking.videoQueue.async { [weak self] in
|
||||
self?.cameraSource.cameraPosition = position
|
||||
completion?(.success(Void()))
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Private Class Objects
|
||||
// initalize the PoseTracking api and load models
|
||||
var poseTracking:PoseTracking = PoseTracking(poseTrackingOptions: PoseTrackingOptions(showLandmarks: true,modelComplexity: 1))
|
||||
|
||||
// Needed to get fps of model
|
||||
let fpsHelper = FPSHelper(smoothingFactor: 0.95)
|
||||
|
||||
// attach Mediapipe camera helper to our class
|
||||
let cameraSource = MPPCameraInputSource()
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
public protocol LinderaDelegate: AnyObject {
|
||||
|
||||
func lindera(_ lindera: Lindera, didDetect event: Asensei3DPose.Event)
|
||||
|
|
Loading…
Reference in New Issue
Block a user