Skip to main content

Data analysis using Julia

Benjamin Gallois

Benjamin Gallois

FastTrack creator
Copyright (C) FastTrack.
Permission is granted to copy, distribute and/or modify this document.This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Data analysis using Julia#

Introduction#

The Julia documentation and installation guide can be found at https://julialang.org/. We provide here a simple example that details how to import the tracking.txt file from FastTrack, and how to extract basic information like the number of objects, the number of images etc...

using DataFrames
using CSV
using PyPlot
using Plots
using StatsPlots

Importation#

We are going to import the tracking file in a DataFrames. Note that the user needs to provide the full path to the tracking.txt file.

data = CSV.read("tracking.txt", delim='\t', DataFrame)
display(data)

2,475 rows × 23 columns (omitted printing of 14 columns)

xHeadyHeadtHeadxTailyTailtTailxBodyyBodytBody
Float64Float64Float64Float64Float64Float64Float64Float64Float64
1514.327333.125.81619499.96327.7276.10226508.345330.8765.94395
2463.603327.0510.301279449.585330.3230.245547458.058328.3460.238877
323.9978287.7153.7064634.9722278.8363.9981929.2056283.5053.84844
4372.536230.1430.194641354.226231.6046.08737364.822230.7590.0515087
5480.58213.4821.28236478.125228.521.53303479.428220.5431.42567
6171.682143.556.09077155.507140.1166.1146164.913142.1136.08216
7498.151121.326.00177483.712119.2850.0223247492.683120.556.15298
8329.56123.4186.08726312.526119.0425.9098322.531121.6146.01722
9465.256115.0454.44359470.05799.9114.40559467.106109.2054.40862
10423.66366.37890.0888056409.10567.29716.12053417.61566.76230.0292602
11424.48740.42325.48198411.59430.39125.88869418.9636.11925.64923
12370.59135.21475.99688354.67229.56335.89121364.00732.87675.94008
13498.50220.25275.66339487.2549.194995.39497493.75815.57815.5026
14367.7915.030346.05933352.0766.756030.653641361.125.759040.152688
15512.965332.5755.86617499.435327.7596.052507.626330.6735.95102
16463.385324.6590.707451.431332.1930.246265458.959327.4430.542368
1719.4579293.0224.2886125.5579281.2064.1837921.8962288.3024.23379
18379.037230.5276.10571361.728229.6160.199343371.74230.1446.25939
19478.884206.7121.27832475.454221.7571.40929477.197214.1081.35472
20173.923143.0420.00732468157.261142.1826.00453167.066142.6896.20403
21498.561122.6875.83253486.357118.1966.13893493.718120.9065.95151
22328.812124.1346.05932312.848119.6055.98617322.331122.2946.00901
23461.738116.7314.47649466.371101.7364.40285463.615110.6564.41641
24428.63169.27155.87139415.66564.64446.13862423.21867.33645.96558
25425.82144.99425.59983414.8433.20285.37159421.24840.08975.461
26368.36235.62195.97427353.2230.46255.88261362.10933.48915.94605
27503.48422.72935.76026489.63216.63155.92136497.92420.28575.86668
28369.1845.840746.15994352.6224.253286.24787362.1445.167666.19236
29510.519331.4175.88883495.784327.3666.12889504.484329.7586.02088
30464.242323.5330.290639451.756328.1940.532686459.432325.3260.37736
⋮⋮⋮⋮⋮⋮⋮⋮⋮⋮

Basic information#

We are going to extract the basic tracking information:

  • Object's id
  • Number of objects
  • Number of images
  • Number of images with at least one object detected
objects = Set(data.id)
print("Objects id: ", objects)
Objects id: Set([10, 5, 9, 8, 13, 4, 1, 0, 12, 7, 11, 2, 3, 6])
numObjects = length(objects)
print("Number of objects: ", numObjects)
Number of objects: 14
numImages = maximum(data.imageNumber) + 1 # Image index starting at 0
print("Number of images: ", numImages)
Number of images: 200
numDetected = length(Set(data.imageNumber))
print("Number of images with at least one object detected: ", numDetected)
Number of images with at least one object detected: 200

Basic plots#

We are going to make basic plots using Plots, StatsPlots and the PyPlot (that require a valid matplotlib installation) modules. For more information about plotting see https://docs.juliaplots.org/latest/tutorial/.

objectsByImage = zeros(numImages)
for i in 1:numImages
objectsByImage[i] = length(Set(data.id[data.imageNumber .== i-1]))
end
Plots.plot(1:numImages, objectsByImage; title="Number of detected objects by frame", xlabel="Frames", ylabel="Objects", label=false)

svg

dataObject0 = data[data.id .== 0, :]
distance = sqrt.(diff(dataObject0.xBody).^2 + diff(dataObject0.yBody).^2)
framerate = 25
time = diff(dataObject0.imageNumber)/framerate
velocity = distance./time
fig, ax = PyPlot.subplots(1, 2)
fig.subplots_adjust(right = 2)
ax[1] = PyPlot.subplot(121)
plot = ax[1].scatter(dataObject0.xBody[1:end-1], dataObject0.yBody[1:end-1], c=velocity, s=40)
ax[1].set_title("Object displacement")
ax[1].set_xlabel("x-position")
ax[1].set_ylabel("y-position")
bar = fig.colorbar(plot)
bar.set_label("Velocity")
ax[2] = PyPlot.subplot(122, projection="polar")
ax[2].scatter(1:length(dataObject0.tBody), dataObject0.tBody, s=40)
ax[2].set_title("Object direction")

png

PyObject Text(0.5, 1.0715488215488216, 'Object direction')
velocities = Any[]
for i in 1:numObjects
distance = sqrt.(diff(data.xBody[data.id .== i-1]).^2 + diff(data.yBody[data.id .== i-1]).^2)
time = diff(data.imageNumber[data.id .== i-1])/25
velocity = distance./time
append!(velocities, [velocity])
end
StatsPlots.boxplot(velocities, label=false, title="Velocity distributions", ylabel="Velocity (px/s)", xlabel="Objects")

svg

Data analysis using Python

Benjamin Gallois

Benjamin Gallois

FastTrack creator
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# Import the data
data = pd.read_csv("tracking.txt", sep='\t')
data

2,475 rows × 23 columns (omitted printing of 14 columns)

xHeadyHeadtHeadxTailyTailtTailxBodyyBodytBody
Float64Float64Float64Float64Float64Float64Float64Float64Float64
1514.327333.125.81619499.96327.7276.10226508.345330.8765.94395
2463.603327.0510.301279449.585330.3230.245547458.058328.3460.238877
323.9978287.7153.7064634.9722278.8363.9981929.2056283.5053.84844
4372.536230.1430.194641354.226231.6046.08737364.822230.7590.0515087
5480.58213.4821.28236478.125228.521.53303479.428220.5431.42567
6171.682143.556.09077155.507140.1166.1146164.913142.1136.08216
7498.151121.326.00177483.712119.2850.0223247492.683120.556.15298
8329.56123.4186.08726312.526119.0425.9098322.531121.6146.01722
9465.256115.0454.44359470.05799.9114.40559467.106109.2054.40862
10423.66366.37890.0888056409.10567.29716.12053417.61566.76230.0292602
11424.48740.42325.48198411.59430.39125.88869418.9636.11925.64923
12370.59135.21475.99688354.67229.56335.89121364.00732.87675.94008
13498.50220.25275.66339487.2549.194995.39497493.75815.57815.5026
14367.7915.030346.05933352.0766.756030.653641361.125.759040.152688
15512.965332.5755.86617499.435327.7596.052507.626330.6735.95102
16463.385324.6590.707451.431332.1930.246265458.959327.4430.542368
1719.4579293.0224.2886125.5579281.2064.1837921.8962288.3024.23379
18379.037230.5276.10571361.728229.6160.199343371.74230.1446.25939
19478.884206.7121.27832475.454221.7571.40929477.197214.1081.35472
20173.923143.0420.00732468157.261142.1826.00453167.066142.6896.20403
21498.561122.6875.83253486.357118.1966.13893493.718120.9065.95151
22328.812124.1346.05932312.848119.6055.98617322.331122.2946.00901
23461.738116.7314.47649466.371101.7364.40285463.615110.6564.41641
24428.63169.27155.87139415.66564.64446.13862423.21867.33645.96558
25425.82144.99425.59983414.8433.20285.37159421.24840.08975.461
26368.36235.62195.97427353.2230.46255.88261362.10933.48915.94605
27503.48422.72935.76026489.63216.63155.92136497.92420.28575.86668
28369.1845.840746.15994352.6224.253286.24787362.1445.167666.19236
29510.519331.4175.88883495.784327.3666.12889504.484329.7586.02088
30464.242323.5330.290639451.756328.1940.532686459.432325.3260.37736
⋮⋮⋮⋮⋮⋮⋮⋮⋮⋮
# Count the number of detected object
objectNumber = len(set(data["id"].values))
objectNumber
2
# Count the number of image
imageNumber = np.max(data["imageNumber"]) + 1
imageNumber
2000
# Plot the number of objects detected by frame
objectByFrame = np.zeros(imageNumber)
for i in range(imageNumber):
objectByFrame[i] = data[data["imageNumber"] == i].shape[0]
plt.scatter(range(imageNumber), objectByFrame)
<matplotlib.collections.PathCollection at 0x7fa41831c6d8>

png

# Plot the trajectory of the first object and its orientation
dataObject0 = data[data["id"] == 0]
distance = np.sqrt(np.diff(dataObject0["xBody"].values)**2 + np.diff(dataObject0["yBody"].values)**2)
framerate = 50
time = np.diff(dataObject0["imageNumber"].values)/framerate
velocity = distance/time
fig, ax = plt.subplots(1, 2)
fig.subplots_adjust(right = 2)
ax[0] = plt.subplot(121)
plot = ax[0].scatter(dataObject0["xBody"][0:-1], dataObject0["yBody"][0:-1], c = velocity, s = 1)
ax[0].set_xlabel("x-position")
ax[0].set_ylabel("y-position")
bar = fig.colorbar(plot)
bar.set_label("Velocity")
ax[1] = plt.subplot(122, projection='polar')
ax[1].scatter(range(dataObject0["tBody"].shape[0]), dataObject0["tBody"], s = 0.4)
ax[1].set_title("Object direction")
Text(0.5, 1.05, 'Object direction')

png

Introducing feature based registration

Benjamin Gallois

Benjamin Gallois

Fast Track creator

Fast Track is now integration feature-based registration alongside the other registration methods. Feature-based registration consists of finding stable points in an image, these points are called key points and their descriptors. Then the same key points are found in the second image. From these key points, a homography is computed between the two images and the transformation is applied to all the pixels of the image to register.

Feature automatic detection#

Fast Track used an automatic algorithm to find the key points and the descriptors (~500) in the two images. This algorithm is called ORB feature detector and was brought up by Ethan Rublee, Vincent Rabaud, Kurt Konolige and Gary R. Bradski in 2011.

Feature matching#

The key points are matched pairwise between the two images using the Hamming distance. The Hamming distance measures the minimum number of errors that could transform one feature descriptor in another one.

Compute the homography#

The homography is computed between the matching key points. It is possible that more than 30% of the features matched are incorrect. To reduce errors when finding the homography, Fast Track used Random Sample Consensus RANSAC estimation technic brought up by Fischler and Bolles in 1981.

This new registration method will be available in the 4.8 Fast Track release. It can be tested in the nightly build and on the dev branch on GitLab.

Introducing ECC registration

Benjamin Gallois

Benjamin Gallois

Fast Track creator

Fast Track is now integrating a new method of registration: the so-called ECC registration. This method was developed by Georgios D. Evangelidis and Emmanouil Z. Psarakis. It consists of maximizing the Enhanced Correlation Coefficient function to find the parameters that described the best transformation between the two images. This is done by solving iteratively a sequence of nonlinear optimization problems.

This method has several advantages:

  • Invariant with respect to photometric distortion, ie, in contrast, and brightness changes.
  • The optimization problem solution is linear, ie, the computing time is acceptable.
  • This method performs well in noisy conditions.

For the moment, only one mode of registration is integrated into Fast Track. The euclidian mode can correct the translation and rotation of the image. For example, this mode can correct small camera vibrations.

Currently in testing, this registration mode will be available in the 4.8.0 version. You can test this feature in the nightly release or in the dev branch on the GitLab.