/// <summary>
/// Common base class for all exercise services.
/// </summary>
- public abstract class BaseExerciseService : INotifyPropertyChanged
+ public abstract class BaseExerciseService
{
private readonly PoseDetector poseDetector = new PoseDetector();
private int isInferencing = 0;
- private Landmark[,] poseLandmarks;
-
- /// <summary>
- /// Occurs when a property value changes.
- /// </summary>
- public event PropertyChangedEventHandler PropertyChanged;
-
- /// <summary>
- /// Gets the pose landmark property.
- /// </summary>
- public Landmark[,] PoseLandmarks
- {
- get => poseLandmarks;
- private set
- {
- if (value != poseLandmarks)
- {
- poseLandmarks = value;
- RaisePropertyChanged();
- }
- }
- }
/// <summary>
/// Detects performing the exercise based on given preview image data.
var landmarks = await poseDetector.Detect(plane, height, width);
DetectExercise(landmarks);
- NUIContext.InvokeOnMainThread(() =>
- {
- PoseLandmarks = landmarks;
- });
}
}
catch (System.Exception exception)
/// </summary>
/// <param name="landmarks">Body landmarks.</param>
protected abstract void DetectExercise(Landmark[,] landmarks);
-
- /// <summary>
- /// Raises PropertyChanged event.
- /// </summary>
- /// <param name="propertyName">Property name.</param>
- protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
}
}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tizen.Multimedia.Vision;
+
+namespace Fitness.Services
+{
+ /// <summary>
+ /// Class holding exercise event data.
+ /// </summary>
+ public class ExerciseEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Gets the pose landmark property.
+ /// </summary>
+ public Landmark[,] PoseLandmarks { get; internal set; }
+
+ /// <summary>
+ /// Gets the property specifying the time of the exercise performed - values ranging from 0 to 5.
+ /// </summary>
+ public int Hold { get; internal set; }
+
+ /// <summary>
+ /// Gets the property specifying the number of repetitions of the exercise.
+ /// </summary>
+ public int Count { get; internal set; }
+
+ /// <summary>
+ /// Gets the property specifying the correctness of the exercise - values ranging from 0 to 100.
+ /// </summary>
+ public int Score { get; internal set; }
+ }
+}
public interface IExerciseService
{
/// <summary>
- /// Gets the pose landmark property.
+ /// Event raised when exercise status is updated.
/// </summary>
- Landmark[,] PoseLandmarks { get; }
+ event EventHandler<ExerciseEventArgs> ExerciseStateUpdated;
/// <summary>
/// Gets or sets the hold time threshold specifying the time of a single exercise.
/// </summary>
long HoldTimeThreshold { get; set; }
- /// <summary>
- /// Gets the property specifying the time of the exercise performed - values ranging from 0 to 5.
- /// </summary>
- int Hold { get; }
-
- /// <summary>
- /// Gets the property specifying the number of repetitions of the exercise.
- /// </summary>
- int Count { get; }
-
- /// <summary>
- /// Gets the property specifying the correctness of the exercise - values ranging from 0 to 100.
- /// </summary>
- int Score { get; }
-
/// <summary>
/// Detects performing the exercise based on given preview image data.
/// </summary>
-using System.ComponentModel;
+using System;
+using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Timers;
private int hold;
private int count;
private int score;
- private int newHold;
- private int newCount;
- private int newScore;
- private float fscore = 0;
+ private float currentSquatSimilarity = 0;
private Stopwatch stopwatch = new Stopwatch();
private System.Timers.Timer holdTimer;
private long holdTimeThreshold;
- public int Hold
- {
- get => hold;
- set
- {
- if (value != hold)
- {
- hold = value;
- RaisePropertyChanged();
- }
- }
- }
-
- public int Count
- {
- get => count;
- set
- {
- if (value != count)
- {
- count = value;
- RaisePropertyChanged();
- }
- }
- }
-
- public int Score
- {
- get => score;
- set
- {
- if (value != score)
- {
- score = value;
- RaisePropertyChanged();
- }
- }
- }
+ /// <inheritdoc />
+ public event EventHandler<ExerciseEventArgs> ExerciseStateUpdated;
+ /// <inheritdoc />
public long HoldTimeThreshold
{
get => holdTimeThreshold;
}
}
+ /// <inheritdoc />
protected override void DetectExercise(Landmark[,] landmarks)
{
int numberOfBodyParts = landmarks.GetLength(1);
NUIContext.InvokeOnMainThread(() =>
{
- Hold = newHold;
- Count = newCount;
- Score = newScore;
+ ExerciseStateUpdated.Invoke(this, new ExerciseEventArgs()
+ {
+ PoseLandmarks = landmarks,
+ Hold = hold,
+ Count = count,
+ Score = score,
+ });
});
}
private void HoldTimer_Elapsed(object sender, ElapsedEventArgs e)
{
- if (Hold < HoldCount && fscore >= SquatDetectedThreshold)
+ if (hold < HoldCount && currentSquatSimilarity >= SquatDetectedThreshold)
{
- newHold++;
+ hold++;
}
}
private void DetectSquat(float squatSimilarity)
{
- var previousScore = fscore;
- fscore = squatSimilarity;
- newScore = (int)System.Math.Round((float)(100 * fscore));
+ var previousSquatSimilarity = currentSquatSimilarity;
+ currentSquatSimilarity = squatSimilarity;
+ score = (int)System.Math.Round((float)(100 * currentSquatSimilarity));
- if (previousScore < SquatDetectedThreshold)
+ if (previousSquatSimilarity < SquatDetectedThreshold)
{
- if (fscore >= SquatDetectedThreshold)
+ if (currentSquatSimilarity >= SquatDetectedThreshold)
{
stopwatch.Start();
holdTimer.Start();
}
}
- else if (fscore < SquatDetectedThreshold)
+ else if (currentSquatSimilarity < SquatDetectedThreshold)
{
stopwatch.Stop();
holdTimer.Stop();
if (stopwatch.ElapsedMilliseconds >= holdTimeThreshold)
{
- newCount++;
+ count++;
}
- newHold = 0;
+ hold = 0;
stopwatch.Reset();
}
}
using System.Windows.Input;
using Fitness.Models;
using Fitness.Services;
+using Tizen.Multimedia.Vision;
using Tizen.NUI.Binding;
namespace Fitness.ViewModels
{
private WorkoutState state;
private IExerciseService squatService;
+ private int score;
+ private int hold;
+ private int repetitions;
+ private Landmark[,] poseLandmarks;
public ExercisingViewModel(WorkoutViewModel workoutViewModel)
{
{
HoldTimeThreshold = 1200,
};
+ squatService.ExerciseStateUpdated += SquatService_ExerciseStateUpdated;
}
/// <summary>
- /// Current score
+ /// Gets the pose landmark property.
/// </summary>
- public int Score { get; private set; } = 95;
+ public Landmark[,] PoseLandmarks
+ {
+ get => poseLandmarks;
+ private set
+ {
+ if (value != poseLandmarks)
+ {
+ poseLandmarks = value;
+ RaisePropertyChanged();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Current score.
+ /// </summary>
+ public int Score
+ {
+ get => score;
+ set
+ {
+ if (value != score)
+ {
+ score = value;
+ RaisePropertyChanged();
+ }
+ }
+ }
/// <summary>
/// Repetitions made in current Workout
/// </summary>
- public int Repetitions { get; private set; } = 3;
+ public int Repetitions
+ {
+ get => repetitions;
+ set
+ {
+ if (value != repetitions)
+ {
+ repetitions = value;
+ RaisePropertyChanged();
+ }
+ }
+ }
/// <summary>
/// Time left in current body pose to accept repetition.
/// </summary>
- public TimeSpan Hold { get; private set; } = TimeSpan.FromSeconds(5);
+ public int Hold
+ {
+ get => hold;
+ set
+ {
+ if (value != hold)
+ {
+ hold = value;
+ RaisePropertyChanged();
+ }
+ }
+ }
/// <summary>
/// TimeLeft in workout.
public string PauseResumeLabel { get; private set; } = "Pause";
/// <summary>
- /// Gets or sets the <see cref="SquatService"/> property.
+ /// Gets the <see cref="SquatService"/> property.
/// </summary>
- public IExerciseService SquatService
- {
- get => squatService;
- set
- {
- if (value != squatService)
- {
- squatService = value;
- RaisePropertyChanged();
- }
- }
- }
+ public IExerciseService SquatService { get; private set; }
protected override void GoPrevious()
{
Services.NavigationService.Instance.NavigateToSummaryView(SummaryType.ChangeToNextWorkout, CurrentWorkout);
}
+ private void SquatService_ExerciseStateUpdated(object sender, ExerciseEventArgs e)
+ {
+ PoseLandmarks = e.PoseLandmarks;
+ Score = e.Score;
+ Repetitions = e.Count;
+ Hold = e.Hold;
+ }
+
private void TriggerPauseResumeWorkout()
{
if (State == WorkoutState.Paused)
using System.Windows.Input;
using Fitness.Services;
+using Tizen.Multimedia.Vision;
using Tizen.NUI.Binding;
namespace Fitness.ViewModels
public class ScanningViewModel : BaseViewModel
{
private IExerciseService squatService;
+ private int count;
+ private int hold;
+ private int score;
+ private Landmark[,] poseLandmarks;
/// <summary>
/// Initializes a new instance of the <see cref="ScanningViewModel"/> class.
{
HoldTimeThreshold = 1200,
};
+ squatService.ExerciseStateUpdated += SquatService_ExerciseStateUpdated;
}
/// <summary>
- /// Gets title.
+ /// Gets the pose landmark property.
/// </summary>
- public string Title { get; private set; } = "ScanningView";
+ public Landmark[,] PoseLandmarks
+ {
+ get => poseLandmarks;
+ private set
+ {
+ if (value != poseLandmarks)
+ {
+ poseLandmarks = value;
+ RaisePropertyChanged();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the property specifying the correctness of the exercise - values ranging from 0 to 100.
+ /// </summary>
+ public int Score
+ {
+ get => score;
+ set
+ {
+ if (value != score)
+ {
+ score = value;
+ RaisePropertyChanged();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the property specifying the number of repetitions of the exercise.
+ /// </summary>
+ public int Count
+ {
+ get => count;
+ set
+ {
+ if (value != count)
+ {
+ count = value;
+ RaisePropertyChanged();
+ }
+ }
+ }
/// <summary>
- /// Gets or sets the <see cref="SquatService"/> property.
+ /// Gets or sets the property specifying the time of the exercise performed - values ranging from 0 to 5.
/// </summary>
- public IExerciseService SquatService
+ public int Hold
{
- get => squatService;
+ get => hold;
set
{
- if (value != squatService)
+ if (value != hold)
{
- squatService = value;
+ hold = value;
RaisePropertyChanged();
}
}
}
+ /// <summary>
+ /// Gets title.
+ /// </summary>
+ public string Title { get; private set; } = "ScanningView";
+
+ /// <summary>
+ /// Gets the <see cref="SquatService"/> property.
+ /// </summary>
+ public IExerciseService SquatService { get; private set; }
+
/// <summary>
/// Gets EndWorkout Command.
/// </summary>
public ICommand CloseScanningView { get; private set; }
+
+ private void SquatService_ExerciseStateUpdated(object sender, ExerciseEventArgs e)
+ {
+ PoseLandmarks = e.PoseLandmarks;
+ Score = e.Score;
+ Count = e.Count;
+ Hold = e.Hold;
+ }
}
}
WidthResizePolicy="FillToParent"
HeightResizePolicy="FillToParent"
BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
- PoseLandmarks="{Binding SquatService.PoseLandmarks}">
+ PoseLandmarks="{Binding PoseLandmarks}">
<x:Arguments>
<Size2D>1124, 632</Size2D>
</x:Arguments>
</View.Layout>
<TextLabel BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
- Text="{Binding SquatService.Hold}"
+ Text="{Binding Hold}"
TextColor="#000C2B"
PixelSize="40"
HorizontalAlignment="Center"
Size="{views:SizeInUnits Width=36, Height=16}"/>
<TextLabel BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
- Text="{Binding SquatService.Count}"
+ Text="{Binding Repetitions}"
TextColor="#000C2B"
PixelSize="40"
HorizontalAlignment="Center"
Size="{views:SizeInUnits Width=36, Height=16}"/>
<TextLabel BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
- Text="{Binding SquatService.Score}"
+ Text="{Binding Score}"
TextColor="#000C2B"
PixelSize="40"
HorizontalAlignment="Center"
<ctrl:Overlay WidthSpecification="{Static LayoutParamPolicies.MatchParent}"
HeightSpecification="{Static LayoutParamPolicies.MatchParent}"
BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
- PoseLandmarks="{Binding SquatService.PoseLandmarks}">
+ PoseLandmarks="{Binding PoseLandmarks}">
<x:Arguments>
<Size2D>1280, 960</Size2D>
</x:Arguments>
</View.Layout>
<TextLabel BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
- Text="{Binding SquatService.Hold, StringFormat='Hold: {0}'}"
+ Text="{Binding Hold, StringFormat='Hold: {0}'}"
PixelSize="40"
TextColor="Black" />
<TextLabel BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
- Text="{Binding SquatService.Count, StringFormat='Count: {0}'}"
+ Text="{Binding Count, StringFormat='Count: {0}'}"
PixelSize="40"
TextColor="Black" />
<TextLabel BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
- Text="{Binding SquatService.Score, StringFormat='Score: {0}'}"
+ Text="{Binding Score, StringFormat='Score: {0}'}"
PixelSize="40"
TextColor="Black"/>