You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Kinect-Project/exo2_subject.md

305 lines
12 KiB

[Home](./README.md)
| [Exercise 1 - Kinect Streams](./exo1_subject.md)
| **Exercise 2 - Introduction**
| [Exercise 2 part 1 - Postures](./exo2_1_subject.md)
| [Exercise 2 part 2 - Gestures](./exo2_2_subject.md)
| [Exercise 2 part 3 - Mapping](./exo2_3_subject.md)
| [Exercise 3 - Free App](./exo3_subject.md)
# Exercise 2: Gesture recognition
*Advised duration*: 4 hours
*Maximum advised duration*: 6 hours
This exercise can be splitted in three parts but they are all bound and you should do them in the right order.
The main objective is to develop a simple gesture recognition system. It is not based on AI or *Machine Learning*. It is rather made of **Q&D**¹ solutions.
# Just a glimpse on the work to be done
Our **gesture recognition** will be able to:
- recognize **postures** _ie_ static body poses (your body does not move, it is tested on a single frame),
- recognize **gestures** _ie_ dynamic body movements (your body does move, it is tested during several frames),
- do **mapping** of body movement on whatever computer thing (a cursor, an ellipse, a dinosaur...).
You will have to develop the following assemblies (except *Exercise3.WpfApp*, and the Nuget Packages *Microsoft.Kinect* and *CommunityToolkit.Mvvm*):
```mermaid
flowchart LR
subgraph Exercise2
MyGesturesBank
KinectUtils
Tests
end
MyGesturesBank -.-> KinectUtils
KinectUtils -.-> Microsoft.Kinect
KinectUtils -.-> CommunityToolkit.Mvvm
Tests -.-> MyGesturesBank
Tests -.-> KinectUtils
style Microsoft.Kinect fill:#eee,color:#111,stroke:#999
style CommunityToolkit.Mvvm fill:#eee,color:#111,stroke:#999
subgraph Exercise3
WpfApp
end
WpfApp -.-> MyGesturesBank
WpfApp -.-> KinectUtils
```
**KinectUtils** will contain all the base types to do gesture recognition and mapping. It will be updated in the three parts of this exercise.
**MyGesturesBank** will contain specific implementation of postures and gestures that could then be used in different applications.
**Tests** are only one or several *Console app* tests to verify that our system is working well.
Here is just a glimpse of the classes you will develop. More details will be given later or by your teacher on demand:
- ```BaseGesture``` will represent any gesture, _ie_ any ```Posture``` or ```Gesture``` (dynamic). To be tested, you will have to call ```TestGesture``` that will fire the event ```GestureRecognized``` if it is recognized.
- ```Posture``` is a specialization of ```BaseGesture``` for postures.
- ```Gesture``` is a specialization of ```BaseGesture``` for dynamic gestures.
- ```GestureManager``` is a static mediator for all gestures. It will ease the use of the gesture recognition system as the user will have to subscribe only once to the ```GestureManager.GestureRecognized``` event and not to every gesture event.
- ```BaseMapping<T>``` is a generic class that allow mapping a body on a ```T```. It will use ```BaseGesture``` to start or stop the mapping.
- To avoid plumbery of creating every gesture for every app or tests, we will finally add a ```IGestureFactory``` storing gestures to be used in an app.
```mermaid
classDiagram
class BaseGesture {
+TestGesture(Body body)*
+GestureRecognized : EventHandler~GestureRecognizedEventArgs~
#OnGestureRecognized()
+GestureName : string
}
class Gesture {
+IsRecognitionRunning : bool
#MinNbOfFrames : int
#MaxNbOfFrames : int
-currentFrameCount : int
#TestInitialConditions(Body body)* bool
#TestPosture(Body body)* bool
#TestRunningGesture(Body body)* bool
#TestEndConditions(Body body)* bool
+TestGesture(Body body)*
}
class Posture {
+TestGesture(Body body)*
#TestPosture(Body body)* bool
}
class IGestureFactory {
+CreateGestures() IEnumerable~BaseGesture~
}
<<interface>> IGestureFactory
class GestureManager {
<<static>>
+EventHandler~GestureRecognizedEventArgs~ GestureRecognized$
+AddGestures(IGestureFactory: factory)$
+AddGestures(params BaseGesture[])$
+RemoveGesture(BaseGesture)$
+StartAcquiringFrames(KinectManager)$
+StopAcquiringFrame()$
}
BaseGesture <|-- Gesture
BaseGesture <|-- Posture
Posture <|-- PostureRightHandUp
Posture <|-- PostureTwoHandsDragon
Gesture <|-- SwipeRightHand
Gesture <|-- ClapHands
IGestureFactory "1" <-- GestureManager : Factory
class BaseMapping~T~ {
+SubscribeToStartGesture(gesture: BaseGesture)
+SubscribeToEndGesture(gesture: BaseGesture)
+SubscribeToToggleGesture(gesture: BaseGesture)
#Mapping(Body body)* T
+TestMapping(Body body, out T ouput) bool
}
BaseMapping~T~ <|-- MapLeftHandToMouse : T <- Point
class MapLeftHandToMouse {
#Mapping(Body body)* Point
}
BaseGesture <.. BaseMapping~T~
BaseGesture "*" <-- GestureManager : +KnownGestures
IGestureFactory <|.. AllGesturesFactory
GestureManager --> "1" KinectManager : KinectManager
```
## Deeper informations
Here are more details about what we are going to do. **Do not hesitate to come back to this section later**.
- ```BaseGesture```: it represents the base class for every gesture, _ie_ a **posture** (static position of a body) or a **dynamic gesture** (a movement). It owns a name and sends and event ```GestureRecognized``` when the gesture is ... recognized. To test the gesture, the type owns an abstract method ```TestGesture``` that will be overriden in the different subclasses.
```mermaid
classDiagram
class BaseGesture {
<<abstract>>
+TestGesture(Body body)*
+GestureRecognized : EventHandler~GestureRecognizedEventArgs~
#OnGestureRecognized(Body)
+GestureName : string
}
```
- ```Posture```: this abstract subclass of ```BaseGesture``` is the base class for every **posture** (_ie_ every static position of a body). It overrides the ```TestGesture``` method that will send the event ```GestureRecognized``` event if the ```TestPosture``` abstract method returns ```true```. It is the base class for every other **posture** class of your coming application (_ie_ ```PostureRightHandUp```, ```PostureTwoHandsDragon```).
```mermaid
classDiagram
class BaseGesture {
<<abstract>>
+TestGesture(Body body)*
+GestureRecognized : EventHandler~GestureRecognizedEventArgs~
#OnGestureRecognized(Body)
+GestureName : string
}
class Posture {
<<abstract>>
+TestGesture(Body body)
#TestPosture(Body body)* bool
}
BaseGesture <|-- Posture
Posture <|-- RightHandUp
Posture <|-- TwoHandsDragon
```
- ```Gesture```: it represents a **dynamic gesture**. It is also a subclass of ```BaseGesture``` and overrides the ```TestGesture``` method to fire the event ```GestureRecognized``` event, but its algorithm is a little bit more complicated, and based on four abstract methods that will be explained later. But here is a summary:
- ```TestInitialConditions```: tests the body to see if the posture corresponds to a possible initial condition of this gesture
- ```TestPose```: tests the body to see if its current pose is valid for this gesture (like a picture or a pause in a movie)
- ```TestDynamicPoses```: tests the current pose but in comparison with the previous ones, to see if the dynamic of the movement corresponds to the gesture
- ```TestEndingConditions```: tests the current pose to see if it corresponds to the end of the gesture.
- Moreover, it also checks that the gesture is done in more than a minimum number of frames (otherwise it is just noise) or in less than a maximum number of frames (otherwise, it is too slow to be intentional).
> Note:
> This algorithm is far from being efficient, but it is simple...
Subclasses of ```Gesture``` will only have to override these methods.
```mermaid
classDiagram
class BaseGesture {
<<abstract>>
+TestGesture(Body body)*
+GestureRecognized : EventHandler~GestureRecognizedEventArgs~
#OnGestureRecognized(Body)
+GestureName : string
}
class Posture {
<<abstract>>
+TestGesture(Body body)
#TestPosture(Body body)* bool
}
class Gesture {
+IsRecognitionRunning : bool
#MinNbOfFrames : int
#MaxNbOfFrames : int
-mCurrentFrameCount : int
#TestInitialConditions(Body body)* bool
#TestPosture(Body body)* bool
#TestRunningGesture(Body body)* bool
#TestEndConditions(Body body)* bool
+TestGesture(Body body)*
}
BaseGesture <|-- Posture
BaseGesture <|-- Gesture
Gesture <|-- SwipeRightHand
Gesture <|-- ClapHands
```
- To simplify the creation of different gestures and postures, we will use **gesture factories**. Every gesture factory implements the interface ```IGestureFactory``` whose contract asks for just one method ```CreateGestures``` returning the collection of ```BaseGesture``` to be tested in your app. This way, you won't have to test a hundred colliding gestures if you need only a dozen.
```mermaid
classDiagram
direction TB
class BaseGesture {
<<abstract>>
+TestGesture(Body body)*
+GestureRecognized : EventHandler~GestureRecognizedEventArgs~
#OnGestureRecognized(Body)
+GestureName : string
}
class IGestureFactory {
<<interface>>
+CreateGestures() IEnumerable~BaseGesture~
}
BaseGesture <.. IGestureFactory
IGestureFactory <|.. AllGesturesFactory
```
- At last, to avoid the user to subscribe to multiple ```GestureRecognized``` events (one per gesture), the static class ```GestureManager``` plays the role of a mediator or a façade for our library, simplifying the subscription and use of the gestures. It also uses the previous factory.
One particular precious thing, is that it sends a static event ```GestureRecognized``` which you can subscribe to. This event is only one to which the user should subscribe. Its subscription will internally subscribes to all ```knownGestures```. Easier and beautiful.
```mermaid
classDiagram
class BaseGesture {
<<abstract>>
+TestGesture(Body body)*
+GestureRecognized : EventHandler~GestureRecognizedEventArgs~
#OnGestureRecognized(Body)
+GestureName : string
}
class IGestureFactory {
<<interface>>
+CreateGestures() IEnumerable~BaseGesture~
}
class GestureManager {
<<static>>
+EventHandler~GestureRecognizedEventArgs~ GestureRecognized$
+StartAcquiringFrames()$
+StopAcquiringFrames()$
}
BaseGesture "*" <-- GestureManager : -mKnownGestures
IGestureFactory "1" <-- GestureManager : Factory
```
# Plan
## Part1: Gesture Recognition (Postures)
The first part will focus on the basics of our gesture recognition system and postures.
In other words, you will have to code:
- ```BaseGesture```
- ```Posture```
- ```GestureManager```
- and some concrete postures to be tested in a test
## Part2: Gesture Recognition (Dynamic Gestures)
In the second part, you will code:
- ```Gesture```
- some concrete gestures to be tested in a console app
- *MyGesturesBank*
- ```IGestureFactory```
- a concrete factory with the previous gestures and postures
## Part3: Gesture Recognition (Mapping)
In this last part, you will have to code:
- ```BaseMapping<T>```
- a concrete mapping
- and a final test.
---
¹ *Q&D stands for Quick & Dirty in tribute to Seattle Computer Products and QDOS*
---
[Home](./README.md)
| [Exercise 1 - Kinect Streams](./exo1_subject.md)
| **Exercise 2 - Introduction**
| [Exercise 2 part 1 - Postures](./exo2_1_subject.md)
| [Exercise 2 part 2 - Gestures](./exo2_2_subject.md)
| [Exercise 2 part 3 - Mapping](./exo2_3_subject.md)
| [Exercise 3 - Free App](./exo3_subject.md)
---
Copyright © 2023-2024 Marc Chevaldonné
---
While preparing this practical works, I was listening to...
<table>
<tr>
<td>
<img src="./images/in_the_court_of_the_crimson_king.jpg" width="120"/>
</td>
<td>
<div>
<p><b>In The Court Of The Crimson King</b></p>
<p><i>King Crimson</i> (1969)</p>
</div>
</td>
</tr>
</table>