Skip to content
This repository has been archived by the owner on Jul 3, 2024. It is now read-only.

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Mouwrice committed May 18, 2024
1 parent 9a49e3e commit 3ce75a5
Show file tree
Hide file tree
Showing 16 changed files with 28 additions and 40 deletions.
12 changes: 0 additions & 12 deletions figures.typ

This file was deleted.

5 changes: 5 additions & 0 deletions jitter_noise/jitter_noise.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
= Reducing Jitter and Noise

#include "jitter_noise_method.typ"

#include "jitter_noise_results.typ"
1 change: 1 addition & 0 deletions jitter_noise/jitter_noise_method.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
== Method
1 change: 1 addition & 0 deletions jitter_noise/jitter_noise_results.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
== Results
10 changes: 2 additions & 8 deletions main.typ
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,8 @@

#include "mediapipe_pose.typ"

#include "measurements.typ"
#include "measurements/measurements.typ"

= Improving accuracy and reducing jitter

== Methods

== Results
#include "jitter_noise/jitter_noise.typ"

#include "drum_application.typ"

#include "figures.typ"
20 changes: 10 additions & 10 deletions measurement_methods.typ → measurements/measurement_methods.typ
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#import "lib.typ": *
#import "../lib.typ": *

== Methods

Expand All @@ -8,7 +8,7 @@ To be able to get indications of the accuracy of MediaPipe Pose, a baseline, or

The setup is configured to be in line with the expected environment the drum application will be used in. Being, a single person sat on a chair facing a webcam, performing drumming motions. The person performing the movements has infrared reflective trackers positioned on the body as close as possible to where the MediaPipe Pose landmarks are located. This setup is shown in @front-still.

#figure(image("images/front_still.png"), caption: [A frame from the camera recording showing the measurement setup.]) <front-still>
#figure(image("../images/front_still.png"), caption: [A frame from the camera recording showing the measurement setup.]) <front-still>

The Qualisys Motion Capture setup first requires a calibration step, after which markers can be tracked with a precision of up to 0.3 mm. In later measurements, the accuracy has dropped to 0.8 mm, which can be attributed to calibrating a larger volume of space. For our use case, this level of accuracy is still sufficient. As we will see, the MediaPipe accuracy will not get close to an accuracy of 0.8 mm, so the Qualisys accuracy is more than sufficient.
After applying the reflective markers, motion capture can easily be performed using the Qualisys Track Manager (QTM) program #footnote()[#link("https://www.qualisys.com/software/qualisys-track-manager/")[https://www.qualisys.com/software/qualisys-track-manager/ #link-icon]]. After having labeled each marker and trajectory, these can be exported to a TSV (tab separated values) file @tsv.
Expand Down Expand Up @@ -65,7 +65,7 @@ Before describing how the outputs from the previous section are used to come up
@unaligned-time-series provides a visual representation of the problem. We have two time series to compare. Series $f$ in red and series $g$ in blue. Due to these points having a different frequency and unaligned time stamps, it is not possible to compare these just by iterating over both series at the same time. Where an iteration corresponds to jumping to the next point in time in both series.

#figure(caption: [Example of unaligned time series, the horizontal axis being time, the vertical axis would be some value])[
#image(width: 100%, "images/time_series_unaligned.png")
#image(width: 100%, "../images/time_series_unaligned.png")
] <unaligned-time-series>

One option would be to "walk" over the series based on the time difference between points.
Expand All @@ -78,7 +78,7 @@ Then there are three options:
The option that minimizes the time difference between the selected points is chosen. This method ensures that every point in both series is taken into account. The pairs of points that would be selected by this technique is displayed in @time-series-unaligned-compare-all by the dotted lines. The measured difference between both signals is then the sum of the length of all dotted lines (the difference between point pairs) divided by the amount of dotted lines (pairs).

#figure(caption: [Point pairs selected by walking over both series indicated by the dotted lines.])[
#image(width: 100%, "images/time_series_unaligned_compare_all.png")
#image(width: 100%, "../images/time_series_unaligned_compare_all.png")
] <time-series-unaligned-compare-all>

However, it is clear to see that this method will result in a higher deviation when comparing a high frequency signal against a lower frequency signal. Which is the case in our measurements. A high frequency Qualisys measurement (100Hz or more) is compared against a lower frequency MediaPipe measurement (30Hz). Because we know these differences in frequency, we are not interested in a difference method that takes these into account. The goal of the measurements is to find the average deviation of a point generated by MediaPipe with that of the corresponding point generated by the Qualisys capture. The following new method achieves that goal.
Expand All @@ -87,7 +87,7 @@ Instead of comparing every point in both series, a dominant series is selected.
For each point in the dominant time series, walk over the points of the other series until the difference in time stamps no longer decreases. Then we have found a new pair. Results of this procedure are displayed in @time-series-unaligned-compare-dominant.

#figure(caption: [Point pairs selected by iterating over the dominant series ($g$, blue) and walking over the other ($f$, red), indicated by the dotted lines.])[
#image(width: 100%, "images/time_series_unaligned_compare_base.png")
#image(width: 100%, "../images/time_series_unaligned_compare_base.png")
] <time-series-unaligned-compare-dominant>

With this method, we have that the irrelevant points are no longer included in the measurements. Yet this does not mean that the points that are now taken into account are perfectly aligned with one another. However, due to the high frequency of the Qualisys captures, we note that the time difference will be relatively small, and thus the difference induced by this nonalignment will be quite small. For a Qualisys capture with a frame rate of 120Hz, there is a frame every $(1000 "ms") / (120 "Hz") = 8.33... $ milliseconds. In the worst case, the MediaPipe point lies exactly between two Qualisys frames, resulting in a maximum time difference of only 4.166... milliseconds.
Expand All @@ -110,7 +110,7 @@ The entire section uses a measurement of the vertical position of the left wrist
The output from the recordings are time series that can be plotted. The output from the measurements is read and without any processing plotted to a line plot in @left_wrist_axis_z_positions_base. On the horizontal axis is time in seconds. On the vertical axis is the value of the point in time in millimeter. @left_wrist_axis_z_positions_base shows a clear mismatch in axis. Plotted is the z-axis from both capture systems. But as mentioned, in MediaPipe the z-axis is the depth and not the vertical axis.

#figure(caption: [Plot of the MediaPipe (Blue) and Qualisys (Red) left wrist z-axis without processing.])[
#image(width: 100%, "images/left_wrist_axis_z_positions_base.svg")
#image(width: 100%, "../images/left_wrist_axis_z_positions_base.svg")
] <left_wrist_axis_z_positions_base>

The problem of mismatched axes is a very simple one to solve. Before plotting the MediaPipe signal we switch the axis so they match with the actual direction of axis in the Qualisys recording. The MediaPipe axes are thus mapped as follows:
Expand All @@ -124,19 +124,19 @@ Normally `Landmark` values would be in the range [0, 1], 0 being one side of the
Secondly, all MediaPipe values have been multiplied by 1000. As the Qualisys output is in millimeter we already prepare the MediaPipe signal by interpreting the incoming signal as meter and converting it to millimeter. The MediaPipe `Landmark` signal has no real unit of course but interpreting it as meters allows for a simpler interpretation when it comes to scaling, explained later in this section.

#figure(caption: [Plot of MediaPipe (Blue) and Qualisys (Red) left wrist z-axis after re-arranging the MediaPipe axes.])[
#image(width: 100%, "images/left_wrist_axis_z_positions_apply_axis_transformations.svg")
#image(width: 100%, "../images/left_wrist_axis_z_positions_apply_axis_transformations.svg")
] <left_wrist_axis_z_positions_apply_axis_transformations>

Now that the basics are out of the way we can start aligning the signal. A first step is removing the average offset the MediaPipe signal has to the Qualisys signal. The method of walking over the dominant series (MediaPipe in this case) and gathering pairs of points from both series as discussed in the previous section, @comparing-time-series, is used for this. For every pair of points we can simply take the difference between those points. The average of these differences is then the offset of the MediaPipe signal. The result of removing this offset from the MediaPipe signal is displayed in @left_wrist_axis_z_positions_remove_offset.

#figure(caption: [Plot of the MediaPipe (Blue) and Qualisys (Red) left wrist z-axis with the average offset removed.])[
#image(width: 100%, "images/left_wrist_axis_z_positions_remove_offset.svg")
#image(width: 100%, "../images/left_wrist_axis_z_positions_remove_offset.svg")
] <left_wrist_axis_z_positions_remove_offset>

With the two signals close together another problem becomes apparent. They are offset in time. This makes sense as both measurements cannot easily be started at exactly the same time. We need to introduce a starting offset. This starting offset should minimize the deviation between both signals. This is achieved by iteratively increasing a starting offset and capturing the offset that resulted in the least deviation. In @left_wrist_axis_z_positions_frame_offset it is shown that this method finds the most perfect offset. Both signals are perfectly aligned in time. After this offset operation the average vertical offset is computed again and subtracted from the signal.

#figure(caption: [Plot of the MediaPipe (Blue) and Qualisys (Red) left wrist z-axis with the time offset removed.])[
#image(width: 100%, "images/left_wrist_axis_z_positions_frame_offset.svg")
#image(width: 100%, "../images/left_wrist_axis_z_positions_frame_offset.svg")
] <left_wrist_axis_z_positions_frame_offset>


Expand All @@ -154,5 +154,5 @@ It is a technique for finding an extremum (minimum or maximum) of a function ins
Applying the Golden-section search method on our running example returns a scale factor of around 2 and results in a nice alignment between both signals (@left_wrist_axis_z_positions_stretch). The factor of 2 also makes sense. In the `Landmark` mode, the range of values lie between 0 and 1, reaching these outer values at the edges of the frame. As mentioned, the `Landmark` signal is interpreted to be in meter. As a consequence, the scaling factor is not only a scaling factor, it has become a measurement of the dimensions of what is visible in the frame. This means that the visible height in the video frame is 2 meters, at the location of the test subject, of course.

#figure(caption: [Plot of the MediaPipe (Blue) and Qualisys (Red) left wrist z-axis with the MediaPipe signal scaled to match.])[
#image(width: 100%, "images/left_wrist_axis_z_positions_stretch.svg")
#image(width: 100%, "../images/left_wrist_axis_z_positions_stretch.svg")
] <left_wrist_axis_z_positions_stretch>
16 changes: 8 additions & 8 deletions measurement_results.typ → measurements/measurement_results.typ
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#import "lib.typ": *
#import "../lib.typ": *

== Results

Expand All @@ -25,22 +25,22 @@ Lastly, the deviation and stability values are also made available by describing
caption: [The trajectory along the z-axis (vertical axis) of the `maurice_drum_regular` measurement. Model: `FULL`, Marker type: `Landmark`, Marker: `Left Wrist`.],
placement: none
)[
#image("measurements/maurice_drum_regular/Left Wrist_Axis.Z_positions.svg")
#image("../images/measurements/maurice_drum_regular/Left Wrist_Axis.Z_positions.svg")
] <maurice-drum-regular-left-wrist-full-landmark>


#figure(
caption: [Per axis deviation of the `maurice_drum_regular` measurement. Model: `FULL`, Marker type: `Landmark`, Marker: `Left Wrist`.],
placement: none,
)[
#image("measurements/maurice_drum_regular/Left Wrist_deviations_seperate.png")
#image("../images/measurements/maurice_drum_regular/Left Wrist_deviations_seperate.png")
] <maurice-drum-regular-left-wrist-full-landmark-deviation>

#figure(
caption: [Signal stability of the `maurice_drum_regular` measurement. Model: `FULL`, Marker type: `Landmark`, Marker: `Left Wrist`.],
placement: none
)[
#image("measurements/maurice_drum_regular/Left Wrist_signal_stability.png")
#image("../images/measurements/maurice_drum_regular/Left Wrist_signal_stability.png")
] <maurice-drum-regular-left-wrist-full-landmark-stability>

#show table.cell.where(x: 0): set text(weight: "bold")
Expand Down Expand Up @@ -385,7 +385,7 @@ One aspect that leads to a less stable signal is jitter. Jitter is the sudden, u
caption: [A case of jitter in the `maurice_drum_fast` measurement around the 20 seconds mark. Model: `LITE`, Marker type: `Landmark`, Marker: `Right Wrist`.],
placement: none,
)[
#image("measurements/maurice_drum_fast/LITE/Right_Wrist_y.svg")
#image("../images/measurements/maurice_drum_fast/LITE/Right_Wrist_y.svg")
] <jitter-example-right-wrist>


Expand Down Expand Up @@ -437,11 +437,11 @@ Another aspect that can lead to a less stable signal is noise. Noise is the rand
grid(
columns: (auto, auto),
rows: (auto, auto),
[#image("measurements/maurice_drum_regular/LITE/Right_Wrist_y.svg")
[#image("../images/measurements/maurice_drum_regular/LITE/Right_Wrist_y.svg")
],
[#image("measurements/maurice_drum_regular/FULL/Right_Wrist_y.svg")
[#image("../images/measurements/maurice_drum_regular/FULL/Right_Wrist_y.svg")
],
[#image("measurements/maurice_drum_regular/HEAVY/Right_Wrist_y.svg")
[#image("../images/measurements/maurice_drum_regular/HEAVY/Right_Wrist_y.svg")
]
),
caption: [A noisy signal in the `maurice_drum_regular` measurement. Models: `LITE` (top left), `FULL` (top right), `HEAVY` (bottom left). Marker type: `Landmark`. Marker: `Right Heel`.],
Expand Down
3 changes: 1 addition & 2 deletions measurements.typ → measurements/measurements.typ
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#import "lib.typ": *
#import "../lib.typ": *

= Measuring accuracy and deviation <measuring-accuracy-and-deviation>

Expand All @@ -8,4 +8,3 @@ All base measurements are taken from a Qualisys Oqus MRI #footnote()[The Oqus MR
#include("measurement_methods.typ")

#include("measurement_results.typ")

0 comments on commit 3ce75a5

Please sign in to comment.