Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move tutorials to Quarto #638

Merged
merged 15 commits into from
Jul 11, 2023
8 changes: 6 additions & 2 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ if Base.active_project() != joinpath(@__DIR__, "Project.toml")
end

# (b) Did someone say render? Then we render!
if "--quarto" ∈ ARGS
if true || "--quarto" ∈ ARGS
mateuszbaran marked this conversation as resolved.
Show resolved Hide resolved
using CondaPkg
CondaPkg.withenv() do
@info "Rendering Quarto"
Expand Down Expand Up @@ -87,7 +87,11 @@ makedocs(
sitename="Manifolds.jl",
pages=[
"Home" => "index.md",
"How to..." => ["🚀 Get Started with `Manifolds.jl`" => "tutorials/getstarted.md"],
"How to..." => [
"🚀 Get Started with `Manifolds.jl`" => "tutorials/getstarted.md",
"Hand gesture analysis" => "tutorials/hand-gestures.md",
"Working in charts" => "tutorials/working-in-charts.md",
mateuszbaran marked this conversation as resolved.
Show resolved Hide resolved
],
"Manifolds" => [
"Basic manifolds" => [
"Centered matrices" => "manifolds/centeredmatrices.md",
Expand Down
10 changes: 10 additions & 0 deletions tutorials/Project.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
[deps]
BoundaryValueDiffEq = "764a87c0-6b3e-53db-9096-fe964310641d"
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
DiffEqCallbacks = "459566f4-90b8-5000-8ac3-15dfb0a30def"
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a"
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
Manifolds = "1cead3c2-87b3-11e9-0ccd-23c62b72b94e"
ManifoldsBase = "3362f125-f0bb-47a3-aa74-596ffd7ef2fb"
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
MultivariateStats = "6f286f6a-111f-5878-ab1e-185364afe411"
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd"

[compat]
kellertuer marked this conversation as resolved.
Show resolved Hide resolved
IJulia = "1"
Expand Down
71 changes: 0 additions & 71 deletions tutorials/hand-gestures.jl

This file was deleted.

114 changes: 114 additions & 0 deletions tutorials/hand-gestures.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
title: Hand gesture analysis
---

In this tutorial we will learn how to use Kendall's shape space to analyze hand gesture data.

```{julia}
#| echo: false
#| code-fold: true
#| output: false
using Pkg;
cd(@__DIR__)
Pkg.activate("."); # for reproducibility use the local tutorial environment.
using Markdown
```


```{julia}
using Manifolds
using CSV
using DataFrames
using Plots
using MultivariateStats
mateuszbaran marked this conversation as resolved.
Show resolved Hide resolved

kellertuer marked this conversation as resolved.
Show resolved Hide resolved
function load_hands()
hands_url = "https://raw.githubusercontent.com/geomstats/geomstats/master/geomstats/datasets/data/hands/hands.txt"
hand_labels_url = "https://raw.githubusercontent.com/geomstats/geomstats/master/geomstats/datasets/data/hands/labels.txt"

hands = Matrix(CSV.read(download(hands_url), DataFrame, header=false))
hands = reshape(hands, size(hands, 1), 3, 22)
hand_labels = CSV.read(download(hand_labels_url), DataFrame, header=false).Column1
return hands, hand_labels
end
```

Let's load dataset of hand gestures, described [here](https://geomstats.github.io/notebooks/14_real_world_applications__hand_poses_analysis_in_kendall_shape_space.html)
and plot a sample gesture as a 3D scatter plot of points.
```{julia}
hands, hand_labels = load_hands()
scatter3d(hands[1, 1, :], hands[1, 2, :], hands[1, 3, :])
```

Each gesture is represented by 22 landmarks in $ℝ³$, so we use the appropriate Kendall's shape space
```{julia}
Mshape = KendallsShapeSpace(3, 22)
```

Hands read from the dataset are projected to the shape space to remove translation
and scaling variability. Rotational variability is then handled using quotient
mateuszbaran marked this conversation as resolved.
Show resolved Hide resolved
structure of ``[`KendallsShapeSpace`](@ref)``{=commonmark}
```{julia}
#| output: false
hands_projected = [project(Mshape, hands[i, :, :]) for i in axes(hands, 1)]
```

Now let's do tangent space PCA. This starts with computing a mean point and computing
logithmic maps at mean to each point in the dataset.
```{julia}
#| output: false
mean_hand = mean(Mshape, hands_projected)
hand_logs = [log(Mshape, mean_hand, p) for p in hands_projected]
```

For tangent PCA, we need coordinates in a basis.
mateuszbaran marked this conversation as resolved.
Show resolved Hide resolved
Some libraries skip this step because the representation of tangent vectors
forms a linear subspace of an Euclidean space so PCA automatically detects
which directions have no variance but this is a more principled way to solve
mateuszbaran marked this conversation as resolved.
Show resolved Hide resolved
this issue.
```{julia}
#| output: false
B = get_basis(Mshape, mean_hand, ProjectedOrthonormalBasis(:svd))
hand_log_coordinates = [get_coordinates(Mshape, mean_hand, X, B) for X in hand_logs]
```

This code prepares data for MultivariateStats -- `mean=0` is set because we've centered
the data geometrically to `mean_hand` in the code above.
```{julia}

mateuszbaran marked this conversation as resolved.
Show resolved Hide resolved
red_coords = reduce(hcat, hand_log_coordinates)
fp = fit(PCA, red_coords; mean=0)
```

Now let's show explained variance of each principal component.
```{julia}

plot(principalvars(fp), title="explained variance", label="Tangent PCA")

```
mateuszbaran marked this conversation as resolved.
Show resolved Hide resolved

Also, let's look at how projections on the first two pricipal components look like.
```{julia}


fig = plot(; title="coordinates per gesture of the first two principal components")
for label_num in [0, 1]
mask = hand_labels .== label_num
cur_hand_logs = red_coords[:, mask]
cur_t = MultivariateStats.transform(fp, cur_hand_logs)
scatter!(fig, cur_t[1, :], cur_t[2, :], label="gesture " * string(label_num))
end
xlabel!(fig, "principal component 1")
ylabel!(fig, "principal component 2")
fig
```

Now let's compute pairwise distances between gestures
kellertuer marked this conversation as resolved.
Show resolved Hide resolved
we can use them for clustering, classification, etc.
```{julia}
hand_distances = [
distance(Mshape, hands_projected[i], hands_projected[j]) for
i in eachindex(hands_projected), j in eachindex(hands_projected)
]
heatmap(hand_distances, aspect_ratio=:equal)
```
Loading
Loading