Idealized Case 1: Tracking of a Test Blob in 2D
This tutorial shows the most important steps of tracking with tobac
using an idealized case:
Import Libraries
We start by importing tobac:
[1]:
import tobac
print('using tobac version', str(tobac.__version__))
# we add testing here to create test dataset (typically not needed in standard applications)
import tobac.testing
using tobac version 1.5.3
We will also need matplotlib in inline-mode for plotting and numpy:
[2]:
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
For a better readability of the graphs:
[3]:
import seaborn as sns
sns.set_context("talk")
Tobac works with a Python package called xarray
, which introduces DataArrays
. In a nutshell these are numpy
-arrays with labels. For a more extensive description have a look at the xarray Documentation.
1. Input Data
There are several utilities implemented in tobac to create simple examples of such arrays. In this tutorial we will use the function make_simple_sample_data_2D()
to create a moving test blob in 2D:
[4]:
test_data = tobac.testing.make_simple_sample_data_2D(data_type="xarray")
test_data
[4]:
<xarray.DataArray 'w' (time: 100, y: 50, x: 100)> Size: 4MB [500000 values with dtype=float64] Coordinates: * time (time) datetime64[ns] 800B 2000-01-01T12:00:00 ... 2000-01-01T... * y (y) float64 400B 0.0 1e+03 2e+03 ... 4.7e+04 4.8e+04 4.9e+04 * x (x) float64 800B 0.0 1e+03 2e+03 ... 9.7e+04 9.8e+04 9.9e+04 latitude (y, x) float64 40kB ... longitude (y, x) float64 40kB ... Attributes: units: m s-1
As you can see our generated data describes a field called ‘w’ with the unit m/s at 100, 50 and 100 datapoints of time, x and y. Additionally, the data contains the latitude and longitude coordinates of the field values. To access the values of ‘w’ in the first timeframe, we can use
[5]:
test_data.data[0]
[5]:
array([[3.67879441e+00, 4.04541885e+00, 4.40431655e+00, ...,
2.22319774e-16, 9.26766698e-17, 3.82489752e-17],
[4.04541885e+00, 4.44858066e+00, 4.84324569e+00, ...,
2.44475908e-16, 1.01912721e-16, 4.20608242e-17],
[4.40431655e+00, 4.84324569e+00, 5.27292424e+00, ...,
2.66165093e-16, 1.10954118e-16, 4.57923372e-17],
...,
[6.45813368e-03, 7.10174389e-03, 7.73178977e-03, ...,
3.90282972e-19, 1.62694148e-19, 6.71461808e-20],
[4.43860604e-03, 4.88095244e-03, 5.31397622e-03, ...,
2.68237303e-19, 1.11817944e-19, 4.61488502e-20],
[3.02025230e-03, 3.32124719e-03, 3.61589850e-03, ...,
1.82522243e-19, 7.60865909e-20, 3.14020145e-20]])
which is then just an array of numbers of the described shape.
To visualize the data, we can plot individual time frames using matplotlib per imshow:
[6]:
fig, axs = plt.subplots(ncols=1, nrows=3, figsize=(12, 16), sharey=True)
plt.subplots_adjust(hspace=0.5)
for i, itime in enumerate([0, 20, 40]):
# plot the 2D blob field in colors
test_data.isel(time=itime).plot(ax=axs[i])
axs[i].set_title(f"timeframe = {itime}")
This tells as that our data is a single moving blob, which is what we are going the analyze with tobac now.
2. Feature Detection
The first step of the general working routine of tobac is the identification of features. This essentially means finding the maxima or minima of the data.
To use the according functions of tobac we need to specify:
the thresholds below/above the features are detected
the spacing of our data
The spacing of the temporal and spatial dimension can be extracted from the data using a build-in utility:
[7]:
dxy, dt = tobac.get_spacings(test_data)
To get an idea of which order of magnitude our thresholds should be, we check the maximum of our data:
[8]:
test_data.max()
[8]:
<xarray.DataArray 'w' ()> Size: 8B array(10.)
Since we know that our data will only have one maximum it is reasoable to choose 9 as our threshold, but keep in mind that we could also add multiple values here if our data would be more complex.
[9]:
threshold = 9
Now we are ready to apply the feature detection algorithm. Notice that this is a minimal input. The function has several other option we will cover in later tutorials.
[10]:
%%capture
features = tobac.feature_detection_multithreshold(test_data, dxy, threshold)
Let’s inspect the resulting object:
[11]:
features
[11]:
frame | idx | hdim_1 | hdim_2 | num | threshold_value | feature | time | timestr | projection_y_coordinate | projection_x_coordinate | latitude | longitude | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 10.000000 | 10.000000 | 69 | 9 | 1 | 2000-01-01 12:00:00 | 2000-01-01 12:00:00 | 10000.000000 | 10000.000000 | 24.100000 | 150.100000 |
1 | 1 | 1 | 10.939394 | 11.848485 | 66 | 9 | 2 | 2000-01-01 12:01:00 | 2000-01-01 12:01:00 | 10939.393939 | 11848.484848 | 24.118485 | 150.109394 |
2 | 2 | 1 | 11.707692 | 13.661538 | 65 | 9 | 3 | 2000-01-01 12:02:00 | 2000-01-01 12:02:00 | 11707.692308 | 13661.538462 | 24.136615 | 150.117077 |
3 | 3 | 1 | 12.569231 | 15.353846 | 65 | 9 | 4 | 2000-01-01 12:03:00 | 2000-01-01 12:03:00 | 12569.230769 | 15353.846154 | 24.153538 | 150.125692 |
4 | 4 | 1 | 13.200000 | 17.107692 | 65 | 9 | 5 | 2000-01-01 12:04:00 | 2000-01-01 12:04:00 | 13200.000000 | 17107.692308 | 24.171077 | 150.132000 |
5 | 5 | 1 | 14.184615 | 19.000000 | 65 | 9 | 6 | 2000-01-01 12:05:00 | 2000-01-01 12:05:00 | 14184.615385 | 19000.000000 | 24.190000 | 150.141846 |
6 | 6 | 1 | 15.060606 | 20.848485 | 66 | 9 | 7 | 2000-01-01 12:06:00 | 2000-01-01 12:06:00 | 15060.606061 | 20848.484848 | 24.208485 | 150.150606 |
7 | 7 | 1 | 15.953125 | 22.765625 | 64 | 9 | 8 | 2000-01-01 12:07:00 | 2000-01-01 12:07:00 | 15953.125000 | 22765.625000 | 24.227656 | 150.159531 |
8 | 8 | 1 | 16.707692 | 24.338462 | 65 | 9 | 9 | 2000-01-01 12:08:00 | 2000-01-01 12:08:00 | 16707.692308 | 24338.461538 | 24.243385 | 150.167077 |
9 | 9 | 1 | 17.676923 | 26.169231 | 65 | 9 | 10 | 2000-01-01 12:09:00 | 2000-01-01 12:09:00 | 17676.923077 | 26169.230769 | 24.261692 | 150.176769 |
10 | 10 | 1 | 18.184615 | 28.000000 | 65 | 9 | 11 | 2000-01-01 12:10:00 | 2000-01-01 12:10:00 | 18184.615385 | 28000.000000 | 24.280000 | 150.181846 |
11 | 11 | 1 | 19.171875 | 29.828125 | 64 | 9 | 12 | 2000-01-01 12:11:00 | 2000-01-01 12:11:00 | 19171.875000 | 29828.125000 | 24.298281 | 150.191719 |
12 | 12 | 1 | 20.046875 | 31.765625 | 64 | 9 | 13 | 2000-01-01 12:12:00 | 2000-01-01 12:12:00 | 20046.875000 | 31765.625000 | 24.317656 | 150.200469 |
13 | 13 | 1 | 20.953125 | 33.234375 | 64 | 9 | 14 | 2000-01-01 12:13:00 | 2000-01-01 12:13:00 | 20953.125000 | 33234.375000 | 24.332344 | 150.209531 |
14 | 14 | 1 | 21.828125 | 35.171875 | 64 | 9 | 15 | 2000-01-01 12:14:00 | 2000-01-01 12:14:00 | 21828.125000 | 35171.875000 | 24.351719 | 150.218281 |
15 | 15 | 1 | 22.815385 | 37.000000 | 65 | 9 | 16 | 2000-01-01 12:15:00 | 2000-01-01 12:15:00 | 22815.384615 | 37000.000000 | 24.370000 | 150.228154 |
16 | 16 | 1 | 23.323077 | 38.830769 | 65 | 9 | 17 | 2000-01-01 12:16:00 | 2000-01-01 12:16:00 | 23323.076923 | 38830.769231 | 24.388308 | 150.233231 |
17 | 17 | 1 | 24.292308 | 40.661538 | 65 | 9 | 18 | 2000-01-01 12:17:00 | 2000-01-01 12:17:00 | 24292.307692 | 40661.538462 | 24.406615 | 150.242923 |
18 | 18 | 1 | 25.046875 | 42.234375 | 64 | 9 | 19 | 2000-01-01 12:18:00 | 2000-01-01 12:18:00 | 25046.875000 | 42234.375000 | 24.422344 | 150.250469 |
19 | 19 | 1 | 25.939394 | 44.151515 | 66 | 9 | 20 | 2000-01-01 12:19:00 | 2000-01-01 12:19:00 | 25939.393939 | 44151.515152 | 24.441515 | 150.259394 |
20 | 20 | 1 | 26.815385 | 46.000000 | 65 | 9 | 21 | 2000-01-01 12:20:00 | 2000-01-01 12:20:00 | 26815.384615 | 46000.000000 | 24.460000 | 150.268154 |
21 | 21 | 1 | 27.800000 | 47.892308 | 65 | 9 | 22 | 2000-01-01 12:21:00 | 2000-01-01 12:21:00 | 27800.000000 | 47892.307692 | 24.478923 | 150.278000 |
22 | 22 | 1 | 28.430769 | 49.646154 | 65 | 9 | 23 | 2000-01-01 12:22:00 | 2000-01-01 12:22:00 | 28430.769231 | 49646.153846 | 24.496462 | 150.284308 |
23 | 23 | 1 | 29.292308 | 51.338462 | 65 | 9 | 24 | 2000-01-01 12:23:00 | 2000-01-01 12:23:00 | 29292.307692 | 51338.461538 | 24.513385 | 150.292923 |
24 | 24 | 1 | 30.060606 | 53.151515 | 66 | 9 | 25 | 2000-01-01 12:24:00 | 2000-01-01 12:24:00 | 30060.606061 | 53151.515152 | 24.531515 | 150.300606 |
25 | 25 | 1 | 31.000000 | 55.000000 | 69 | 9 | 26 | 2000-01-01 12:25:00 | 2000-01-01 12:25:00 | 31000.000000 | 55000.000000 | 24.550000 | 150.310000 |
26 | 26 | 1 | 31.939394 | 56.848485 | 66 | 9 | 27 | 2000-01-01 12:26:00 | 2000-01-01 12:26:00 | 31939.393939 | 56848.484848 | 24.568485 | 150.319394 |
27 | 27 | 1 | 32.707692 | 58.661538 | 65 | 9 | 28 | 2000-01-01 12:27:00 | 2000-01-01 12:27:00 | 32707.692308 | 58661.538462 | 24.586615 | 150.327077 |
28 | 28 | 1 | 33.569231 | 60.353846 | 65 | 9 | 29 | 2000-01-01 12:28:00 | 2000-01-01 12:28:00 | 33569.230769 | 60353.846154 | 24.603538 | 150.335692 |
29 | 29 | 1 | 34.200000 | 62.107692 | 65 | 9 | 30 | 2000-01-01 12:29:00 | 2000-01-01 12:29:00 | 34200.000000 | 62107.692308 | 24.621077 | 150.342000 |
30 | 30 | 1 | 35.184615 | 64.000000 | 65 | 9 | 31 | 2000-01-01 12:30:00 | 2000-01-01 12:30:00 | 35184.615385 | 64000.000000 | 24.640000 | 150.351846 |
31 | 31 | 1 | 36.060606 | 65.848485 | 66 | 9 | 32 | 2000-01-01 12:31:00 | 2000-01-01 12:31:00 | 36060.606061 | 65848.484848 | 24.658485 | 150.360606 |
32 | 32 | 1 | 36.953125 | 67.765625 | 64 | 9 | 33 | 2000-01-01 12:32:00 | 2000-01-01 12:32:00 | 36953.125000 | 67765.625000 | 24.677656 | 150.369531 |
33 | 33 | 1 | 37.707692 | 69.338462 | 65 | 9 | 34 | 2000-01-01 12:33:00 | 2000-01-01 12:33:00 | 37707.692308 | 69338.461538 | 24.693385 | 150.377077 |
34 | 34 | 1 | 38.676923 | 71.169231 | 65 | 9 | 35 | 2000-01-01 12:34:00 | 2000-01-01 12:34:00 | 38676.923077 | 71169.230769 | 24.711692 | 150.386769 |
35 | 35 | 1 | 39.184615 | 73.000000 | 65 | 9 | 36 | 2000-01-01 12:35:00 | 2000-01-01 12:35:00 | 39184.615385 | 73000.000000 | 24.730000 | 150.391846 |
36 | 36 | 1 | 40.171875 | 74.828125 | 64 | 9 | 37 | 2000-01-01 12:36:00 | 2000-01-01 12:36:00 | 40171.875000 | 74828.125000 | 24.748281 | 150.401719 |
37 | 37 | 1 | 41.046875 | 76.765625 | 64 | 9 | 38 | 2000-01-01 12:37:00 | 2000-01-01 12:37:00 | 41046.875000 | 76765.625000 | 24.767656 | 150.410469 |
38 | 38 | 1 | 41.953125 | 78.234375 | 64 | 9 | 39 | 2000-01-01 12:38:00 | 2000-01-01 12:38:00 | 41953.125000 | 78234.375000 | 24.782344 | 150.419531 |
39 | 39 | 1 | 42.828125 | 80.171875 | 64 | 9 | 40 | 2000-01-01 12:39:00 | 2000-01-01 12:39:00 | 42828.125000 | 80171.875000 | 24.801719 | 150.428281 |
40 | 40 | 1 | 43.815385 | 82.000000 | 65 | 9 | 41 | 2000-01-01 12:40:00 | 2000-01-01 12:40:00 | 43815.384615 | 82000.000000 | 24.820000 | 150.438154 |
41 | 41 | 1 | 44.462687 | 83.820896 | 67 | 9 | 42 | 2000-01-01 12:41:00 | 2000-01-01 12:41:00 | 44462.686567 | 83820.895522 | 24.838209 | 150.444627 |
42 | 42 | 1 | 45.292308 | 85.661538 | 65 | 9 | 43 | 2000-01-01 12:42:00 | 2000-01-01 12:42:00 | 45292.307692 | 85661.538462 | 24.856615 | 150.452923 |
43 | 43 | 1 | 45.836066 | 87.278689 | 61 | 9 | 44 | 2000-01-01 12:43:00 | 2000-01-01 12:43:00 | 45836.065574 | 87278.688525 | 24.872787 | 150.458361 |
44 | 44 | 1 | 46.254545 | 89.145455 | 55 | 9 | 45 | 2000-01-01 12:44:00 | 2000-01-01 12:44:00 | 46254.545455 | 89145.454545 | 24.891455 | 150.462545 |
45 | 45 | 1 | 46.770833 | 91.000000 | 48 | 9 | 46 | 2000-01-01 12:45:00 | 2000-01-01 12:45:00 | 46770.833333 | 91000.000000 | 24.910000 | 150.467708 |
46 | 46 | 1 | 47.256410 | 93.000000 | 39 | 9 | 47 | 2000-01-01 12:46:00 | 2000-01-01 12:46:00 | 47256.410256 | 93000.000000 | 24.930000 | 150.472564 |
47 | 47 | 1 | 47.500000 | 94.764706 | 34 | 9 | 48 | 2000-01-01 12:47:00 | 2000-01-01 12:47:00 | 47500.000000 | 94764.705882 | 24.947647 | 150.475000 |
48 | 48 | 1 | 47.782609 | 96.130435 | 23 | 9 | 49 | 2000-01-01 12:48:00 | 2000-01-01 12:48:00 | 47782.608696 | 96130.434783 | 24.961304 | 150.477826 |
49 | 49 | 1 | 48.153846 | 97.230769 | 13 | 9 | 50 | 2000-01-01 12:49:00 | 2000-01-01 12:49:00 | 48153.846154 | 97230.769231 | 24.972308 | 150.481538 |
50 | 50 | 1 | 48.600000 | 98.200000 | 5 | 9 | 51 | 2000-01-01 12:50:00 | 2000-01-01 12:50:00 | 48600.000000 | 98200.000000 | 24.982000 | 150.486000 |
The ouputs tells us that features were found in 51 frames (index 0 to 50) of our data. The variable idx is 1 for every frames, which means that only 1 feature was found in every time step, as we expected. hdim_1 and hdim_2 are the position of this feature with respect to the y and x-indices.
We can plot the detected feature positions on top of the colored blob.
[12]:
fig, axs = plt.subplots(ncols=1, nrows=3, figsize=(12, 16), sharey=True)
plt.subplots_adjust(hspace=0.5)
for i, itime in enumerate([0, 20, 40]):
# plot the 2D blob field in colors
test_data.isel(time=itime).plot(ax=axs[i])
# plot the detected feature as black cross
f = features.loc[[itime]]
f.plot.scatter(
x="projection_x_coordinate",
y="projection_y_coordinate",
s=40,
ax=axs[i],
color="black",
marker="x",
)
axs[i].set_title(f"timeframe = {itime}")
The function has succesfully detected the maximum of our data in the individual timeframes.
3. Trajectory Linking
After we are done finding the features and associated segments for each frame it is necessary for further analysis to keep track of those elements troughout time. Linking is the tool for that. It connects the features of the timesteps which belong together. We are going to use the linking_trackpy() function here. The required inputs are the features, the two spacings and a maximum velocity of the features.
[13]:
trajectories = tobac.linking_trackpy(features, test_data, dt=dt, dxy=dxy, v_max=100)
Frame 50: 1 trajectories present.
fails without v_max
Unsurprisingly, one trajectory was found. The returned object is another Dataset:
[14]:
trajectories
[14]:
frame | idx | hdim_1 | hdim_2 | num | threshold_value | feature | time | timestr | projection_y_coordinate | projection_x_coordinate | latitude | longitude | cell | time_cell | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 10.000000 | 10.000000 | 69 | 9 | 1 | 2000-01-01 12:00:00 | 2000-01-01 12:00:00 | 10000.000000 | 10000.000000 | 24.100000 | 150.100000 | 1 | 0 days 00:00:00 |
1 | 1 | 1 | 10.939394 | 11.848485 | 66 | 9 | 2 | 2000-01-01 12:01:00 | 2000-01-01 12:01:00 | 10939.393939 | 11848.484848 | 24.118485 | 150.109394 | 1 | 0 days 00:01:00 |
2 | 2 | 1 | 11.707692 | 13.661538 | 65 | 9 | 3 | 2000-01-01 12:02:00 | 2000-01-01 12:02:00 | 11707.692308 | 13661.538462 | 24.136615 | 150.117077 | 1 | 0 days 00:02:00 |
3 | 3 | 1 | 12.569231 | 15.353846 | 65 | 9 | 4 | 2000-01-01 12:03:00 | 2000-01-01 12:03:00 | 12569.230769 | 15353.846154 | 24.153538 | 150.125692 | 1 | 0 days 00:03:00 |
4 | 4 | 1 | 13.200000 | 17.107692 | 65 | 9 | 5 | 2000-01-01 12:04:00 | 2000-01-01 12:04:00 | 13200.000000 | 17107.692308 | 24.171077 | 150.132000 | 1 | 0 days 00:04:00 |
5 | 5 | 1 | 14.184615 | 19.000000 | 65 | 9 | 6 | 2000-01-01 12:05:00 | 2000-01-01 12:05:00 | 14184.615385 | 19000.000000 | 24.190000 | 150.141846 | 1 | 0 days 00:05:00 |
6 | 6 | 1 | 15.060606 | 20.848485 | 66 | 9 | 7 | 2000-01-01 12:06:00 | 2000-01-01 12:06:00 | 15060.606061 | 20848.484848 | 24.208485 | 150.150606 | 1 | 0 days 00:06:00 |
7 | 7 | 1 | 15.953125 | 22.765625 | 64 | 9 | 8 | 2000-01-01 12:07:00 | 2000-01-01 12:07:00 | 15953.125000 | 22765.625000 | 24.227656 | 150.159531 | 1 | 0 days 00:07:00 |
8 | 8 | 1 | 16.707692 | 24.338462 | 65 | 9 | 9 | 2000-01-01 12:08:00 | 2000-01-01 12:08:00 | 16707.692308 | 24338.461538 | 24.243385 | 150.167077 | 1 | 0 days 00:08:00 |
9 | 9 | 1 | 17.676923 | 26.169231 | 65 | 9 | 10 | 2000-01-01 12:09:00 | 2000-01-01 12:09:00 | 17676.923077 | 26169.230769 | 24.261692 | 150.176769 | 1 | 0 days 00:09:00 |
10 | 10 | 1 | 18.184615 | 28.000000 | 65 | 9 | 11 | 2000-01-01 12:10:00 | 2000-01-01 12:10:00 | 18184.615385 | 28000.000000 | 24.280000 | 150.181846 | 1 | 0 days 00:10:00 |
11 | 11 | 1 | 19.171875 | 29.828125 | 64 | 9 | 12 | 2000-01-01 12:11:00 | 2000-01-01 12:11:00 | 19171.875000 | 29828.125000 | 24.298281 | 150.191719 | 1 | 0 days 00:11:00 |
12 | 12 | 1 | 20.046875 | 31.765625 | 64 | 9 | 13 | 2000-01-01 12:12:00 | 2000-01-01 12:12:00 | 20046.875000 | 31765.625000 | 24.317656 | 150.200469 | 1 | 0 days 00:12:00 |
13 | 13 | 1 | 20.953125 | 33.234375 | 64 | 9 | 14 | 2000-01-01 12:13:00 | 2000-01-01 12:13:00 | 20953.125000 | 33234.375000 | 24.332344 | 150.209531 | 1 | 0 days 00:13:00 |
14 | 14 | 1 | 21.828125 | 35.171875 | 64 | 9 | 15 | 2000-01-01 12:14:00 | 2000-01-01 12:14:00 | 21828.125000 | 35171.875000 | 24.351719 | 150.218281 | 1 | 0 days 00:14:00 |
15 | 15 | 1 | 22.815385 | 37.000000 | 65 | 9 | 16 | 2000-01-01 12:15:00 | 2000-01-01 12:15:00 | 22815.384615 | 37000.000000 | 24.370000 | 150.228154 | 1 | 0 days 00:15:00 |
16 | 16 | 1 | 23.323077 | 38.830769 | 65 | 9 | 17 | 2000-01-01 12:16:00 | 2000-01-01 12:16:00 | 23323.076923 | 38830.769231 | 24.388308 | 150.233231 | 1 | 0 days 00:16:00 |
17 | 17 | 1 | 24.292308 | 40.661538 | 65 | 9 | 18 | 2000-01-01 12:17:00 | 2000-01-01 12:17:00 | 24292.307692 | 40661.538462 | 24.406615 | 150.242923 | 1 | 0 days 00:17:00 |
18 | 18 | 1 | 25.046875 | 42.234375 | 64 | 9 | 19 | 2000-01-01 12:18:00 | 2000-01-01 12:18:00 | 25046.875000 | 42234.375000 | 24.422344 | 150.250469 | 1 | 0 days 00:18:00 |
19 | 19 | 1 | 25.939394 | 44.151515 | 66 | 9 | 20 | 2000-01-01 12:19:00 | 2000-01-01 12:19:00 | 25939.393939 | 44151.515152 | 24.441515 | 150.259394 | 1 | 0 days 00:19:00 |
20 | 20 | 1 | 26.815385 | 46.000000 | 65 | 9 | 21 | 2000-01-01 12:20:00 | 2000-01-01 12:20:00 | 26815.384615 | 46000.000000 | 24.460000 | 150.268154 | 1 | 0 days 00:20:00 |
21 | 21 | 1 | 27.800000 | 47.892308 | 65 | 9 | 22 | 2000-01-01 12:21:00 | 2000-01-01 12:21:00 | 27800.000000 | 47892.307692 | 24.478923 | 150.278000 | 1 | 0 days 00:21:00 |
22 | 22 | 1 | 28.430769 | 49.646154 | 65 | 9 | 23 | 2000-01-01 12:22:00 | 2000-01-01 12:22:00 | 28430.769231 | 49646.153846 | 24.496462 | 150.284308 | 1 | 0 days 00:22:00 |
23 | 23 | 1 | 29.292308 | 51.338462 | 65 | 9 | 24 | 2000-01-01 12:23:00 | 2000-01-01 12:23:00 | 29292.307692 | 51338.461538 | 24.513385 | 150.292923 | 1 | 0 days 00:23:00 |
24 | 24 | 1 | 30.060606 | 53.151515 | 66 | 9 | 25 | 2000-01-01 12:24:00 | 2000-01-01 12:24:00 | 30060.606061 | 53151.515152 | 24.531515 | 150.300606 | 1 | 0 days 00:24:00 |
25 | 25 | 1 | 31.000000 | 55.000000 | 69 | 9 | 26 | 2000-01-01 12:25:00 | 2000-01-01 12:25:00 | 31000.000000 | 55000.000000 | 24.550000 | 150.310000 | 1 | 0 days 00:25:00 |
26 | 26 | 1 | 31.939394 | 56.848485 | 66 | 9 | 27 | 2000-01-01 12:26:00 | 2000-01-01 12:26:00 | 31939.393939 | 56848.484848 | 24.568485 | 150.319394 | 1 | 0 days 00:26:00 |
27 | 27 | 1 | 32.707692 | 58.661538 | 65 | 9 | 28 | 2000-01-01 12:27:00 | 2000-01-01 12:27:00 | 32707.692308 | 58661.538462 | 24.586615 | 150.327077 | 1 | 0 days 00:27:00 |
28 | 28 | 1 | 33.569231 | 60.353846 | 65 | 9 | 29 | 2000-01-01 12:28:00 | 2000-01-01 12:28:00 | 33569.230769 | 60353.846154 | 24.603538 | 150.335692 | 1 | 0 days 00:28:00 |
29 | 29 | 1 | 34.200000 | 62.107692 | 65 | 9 | 30 | 2000-01-01 12:29:00 | 2000-01-01 12:29:00 | 34200.000000 | 62107.692308 | 24.621077 | 150.342000 | 1 | 0 days 00:29:00 |
30 | 30 | 1 | 35.184615 | 64.000000 | 65 | 9 | 31 | 2000-01-01 12:30:00 | 2000-01-01 12:30:00 | 35184.615385 | 64000.000000 | 24.640000 | 150.351846 | 1 | 0 days 00:30:00 |
31 | 31 | 1 | 36.060606 | 65.848485 | 66 | 9 | 32 | 2000-01-01 12:31:00 | 2000-01-01 12:31:00 | 36060.606061 | 65848.484848 | 24.658485 | 150.360606 | 1 | 0 days 00:31:00 |
32 | 32 | 1 | 36.953125 | 67.765625 | 64 | 9 | 33 | 2000-01-01 12:32:00 | 2000-01-01 12:32:00 | 36953.125000 | 67765.625000 | 24.677656 | 150.369531 | 1 | 0 days 00:32:00 |
33 | 33 | 1 | 37.707692 | 69.338462 | 65 | 9 | 34 | 2000-01-01 12:33:00 | 2000-01-01 12:33:00 | 37707.692308 | 69338.461538 | 24.693385 | 150.377077 | 1 | 0 days 00:33:00 |
34 | 34 | 1 | 38.676923 | 71.169231 | 65 | 9 | 35 | 2000-01-01 12:34:00 | 2000-01-01 12:34:00 | 38676.923077 | 71169.230769 | 24.711692 | 150.386769 | 1 | 0 days 00:34:00 |
35 | 35 | 1 | 39.184615 | 73.000000 | 65 | 9 | 36 | 2000-01-01 12:35:00 | 2000-01-01 12:35:00 | 39184.615385 | 73000.000000 | 24.730000 | 150.391846 | 1 | 0 days 00:35:00 |
36 | 36 | 1 | 40.171875 | 74.828125 | 64 | 9 | 37 | 2000-01-01 12:36:00 | 2000-01-01 12:36:00 | 40171.875000 | 74828.125000 | 24.748281 | 150.401719 | 1 | 0 days 00:36:00 |
37 | 37 | 1 | 41.046875 | 76.765625 | 64 | 9 | 38 | 2000-01-01 12:37:00 | 2000-01-01 12:37:00 | 41046.875000 | 76765.625000 | 24.767656 | 150.410469 | 1 | 0 days 00:37:00 |
38 | 38 | 1 | 41.953125 | 78.234375 | 64 | 9 | 39 | 2000-01-01 12:38:00 | 2000-01-01 12:38:00 | 41953.125000 | 78234.375000 | 24.782344 | 150.419531 | 1 | 0 days 00:38:00 |
39 | 39 | 1 | 42.828125 | 80.171875 | 64 | 9 | 40 | 2000-01-01 12:39:00 | 2000-01-01 12:39:00 | 42828.125000 | 80171.875000 | 24.801719 | 150.428281 | 1 | 0 days 00:39:00 |
40 | 40 | 1 | 43.815385 | 82.000000 | 65 | 9 | 41 | 2000-01-01 12:40:00 | 2000-01-01 12:40:00 | 43815.384615 | 82000.000000 | 24.820000 | 150.438154 | 1 | 0 days 00:40:00 |
41 | 41 | 1 | 44.462687 | 83.820896 | 67 | 9 | 42 | 2000-01-01 12:41:00 | 2000-01-01 12:41:00 | 44462.686567 | 83820.895522 | 24.838209 | 150.444627 | 1 | 0 days 00:41:00 |
42 | 42 | 1 | 45.292308 | 85.661538 | 65 | 9 | 43 | 2000-01-01 12:42:00 | 2000-01-01 12:42:00 | 45292.307692 | 85661.538462 | 24.856615 | 150.452923 | 1 | 0 days 00:42:00 |
43 | 43 | 1 | 45.836066 | 87.278689 | 61 | 9 | 44 | 2000-01-01 12:43:00 | 2000-01-01 12:43:00 | 45836.065574 | 87278.688525 | 24.872787 | 150.458361 | 1 | 0 days 00:43:00 |
44 | 44 | 1 | 46.254545 | 89.145455 | 55 | 9 | 45 | 2000-01-01 12:44:00 | 2000-01-01 12:44:00 | 46254.545455 | 89145.454545 | 24.891455 | 150.462545 | 1 | 0 days 00:44:00 |
45 | 45 | 1 | 46.770833 | 91.000000 | 48 | 9 | 46 | 2000-01-01 12:45:00 | 2000-01-01 12:45:00 | 46770.833333 | 91000.000000 | 24.910000 | 150.467708 | 1 | 0 days 00:45:00 |
46 | 46 | 1 | 47.256410 | 93.000000 | 39 | 9 | 47 | 2000-01-01 12:46:00 | 2000-01-01 12:46:00 | 47256.410256 | 93000.000000 | 24.930000 | 150.472564 | 1 | 0 days 00:46:00 |
47 | 47 | 1 | 47.500000 | 94.764706 | 34 | 9 | 48 | 2000-01-01 12:47:00 | 2000-01-01 12:47:00 | 47500.000000 | 94764.705882 | 24.947647 | 150.475000 | 1 | 0 days 00:47:00 |
48 | 48 | 1 | 47.782609 | 96.130435 | 23 | 9 | 49 | 2000-01-01 12:48:00 | 2000-01-01 12:48:00 | 47782.608696 | 96130.434783 | 24.961304 | 150.477826 | 1 | 0 days 00:48:00 |
49 | 49 | 1 | 48.153846 | 97.230769 | 13 | 9 | 50 | 2000-01-01 12:49:00 | 2000-01-01 12:49:00 | 48153.846154 | 97230.769231 | 24.972308 | 150.481538 | 1 | 0 days 00:49:00 |
50 | 50 | 1 | 48.600000 | 98.200000 | 5 | 9 | 51 | 2000-01-01 12:50:00 | 2000-01-01 12:50:00 | 48600.000000 | 98200.000000 | 24.982000 | 150.486000 | 1 | 0 days 00:50:00 |
The new variable cell now indexes the features in the different time steps. Therefore we can use it to create a mask for our moving feature:
[15]:
track_mask = trajectories["cell"] == 1.0
This mask can then be used - to select the track:
[16]:
track = trajectories.where(track_mask).dropna()
and to show the track in our plots:
[17]:
fig, axs = plt.subplots(ncols=1, nrows=3, figsize=(12, 16), sharey=True)
plt.subplots_adjust(hspace=0.5)
# fig.tight_layout()
for i, itime in enumerate([0, 20, 40]):
# plot the 2D blob field in colors
test_data.isel(time=itime).plot(ax=axs[i])
# plot the track
track.plot.scatter(
x="projection_x_coordinate",
y="projection_y_coordinate",
ax=axs[i],
color="red",
marker="o",
alpha=0.2,
)
# plot the detected feature as black cross
f = features.loc[[itime]]
f.plot.scatter(
x="projection_x_coordinate",
y="projection_y_coordinate",
s=40,
ax=axs[i],
color="black",
marker="x",
)
axs[i].set_title(f"timeframe = {itime}")
4. Segmentation
Another step after the feature detection and tracking is the segmentation of the data. This means we find the area surrounding our features belonging to the same cluster. Logically, we now need the already detected features as an additional input.
Just a small thought here:
We often introduce the different steps as 1.feature detection, 2.segmentation and 3. tracking, so having the segmenation step actually before the trajectory linking. The order actually does not matter since the current segmentation approach is based on the feature detection output only and can be done before or after the linking.
On further extensions:
Also, one could actually use the output of the segmentation (
segments
Dataset in the example below) as input for the tracking with the advantage that information on the area (ncells) is added in the dataframe.One could also use the output of the tracking in the segmentation (
trajectories
Dataset from above) with the advantage that mask will contain only the features that are also linked to trajectories.
These possibilities exist and could be utlized by you …
[18]:
%%capture
segment_labels, segments = tobac.segmentation_2D(features, test_data, dxy, threshold=9)
As the name implies, the first object returned is an array in which the segmented areas belonging to each feature have the same label value as that feature. The second output is a dataframe of the detected features updated with information about their segmented regions (currently only the number of pixels segmented)
[19]:
segment_labels
[19]:
<xarray.DataArray 'segmentation_mask' (time: 100, y: 50, x: 100)> Size: 2MB [500000 values with dtype=int32] Coordinates: * time (time) datetime64[ns] 800B 2000-01-01T12:00:00 ... 2000-01-01T... * y (y) float64 400B 0.0 1e+03 2e+03 ... 4.7e+04 4.8e+04 4.9e+04 * x (x) float64 800B 0.0 1e+03 2e+03 ... 9.7e+04 9.8e+04 9.9e+04 latitude (y, x) float64 40kB ... longitude (y, x) float64 40kB ... Attributes: long_name: segmentation_mask
Since True/False corresponds to 1/0 in python, we can visualize the segments with a simple contour plot:
[20]:
fig, axs = plt.subplots(ncols=1, nrows=3, figsize=(12, 16), sharey=True)
plt.subplots_adjust(hspace=0.5)
for i, itime in enumerate([0, 20, 40]):
# plot the 2D blob field in colors
test_data.isel(time=itime).plot(ax=axs[i])
# plot the mask outline
segment_labels.isel(time=itime).plot.contour(levels=[0.5], ax=axs[i], colors="k")
# plot the detected feature as black cross
f = features.loc[[itime]]
f.plot.scatter(
x="projection_x_coordinate",
y="projection_y_coordinate",
s=40,
ax=axs[i],
color="black",
marker="x",
)
axs[i].set_title(f"timeframe = {itime}")
Keep in mind that the area of the resulting segments crucially depends on the defined thresholds.
5. Statistical Analysis
Blob Velocity / Motion Speed
Now different functions of tobacs analysis module can be used to calculate or plot properties of the tracks and the features. For example the velocity of the feature along the track can be calulated via:
[21]:
vel = tobac.calculate_velocity(track)
The expected velocity (hard-coded in the test function) is:
[22]:
expected_velocity = np.sqrt(30**2 + 14**2)
Plotting the velocity vs the timeframe will give us
[23]:
plt.figure(figsize=(10, 5))
plt.tight_layout()
plt.plot(vel["frame"], vel["v"])
plt.xlabel("timeframe")
plt.ylabel("velocity [m/s]")
plt.grid()
plt.axhline(expected_velocity, color="darkgreen", lw=5, alpha=0.5)
sns.despine()
We can also create an histogram of the detected velocities:
[24]:
hist, edges = tobac.velocity_histogram(
track,
bin_edges=np.arange(14, 43, 3),
)
[25]:
width = 0.9 * (edges[1] - edges[0])
center = (edges[:-1] + edges[1:]) / 2
plt.tight_layout()
plt.bar(center, hist, width=width)
plt.ylabel("count")
plt.xlabel("velocity [m/s]")
plt.axvline(expected_velocity, color="darkgreen", lw=5, alpha=0.5)
sns.despine()
Area
The area of the features can also be calculated and plotted throughout time:
[26]:
area = tobac.calculate_area(features, segment_labels)
[27]:
blob_magitude = 10 # also hard-code in the test
blob_sigma = 10e3
normalized_circle_radius = np.sqrt(np.log(blob_magitude / threshold))
absolute_circle_radius = np.sqrt(2) * blob_sigma * normalized_circle_radius
expected_area = np.pi * absolute_circle_radius**2
[28]:
plt.figure(figsize=(10, 5))
plt.tight_layout()
plt.plot(area["frame"], area["area"])
plt.xlabel("timeframe")
plt.ylabel(r"area [$m^2$]")
plt.grid()
plt.axhline(expected_area, color="darkgreen", lw=5, alpha=0.5)
sns.despine()
Lifetime
Another interesting property for real data are the lifetimes of our features. Tobac can also produce a histogram of this:
[29]:
hist, bins, centers = tobac.lifetime_histogram(
track, bin_edges=np.arange(0, 200, 20)
)
[30]:
plt.tight_layout()
plt.bar(centers, hist, width=width)
plt.ylabel("count")
plt.xlabel("lifetime")
plt.show()
We can deduce, that our singular feature in the data has a lifetime of 50.