Fly Tracker#

In this lab, you’ll use a Python script to track the position and velocity of a fruit fly from video recordings. The script will detect the fly against a light background, track its movement frame-by-frame, and output position and velocity data as CSV files, along with plots.

At the end of this notebook, you’ll be able to:

  • Run the fly tracker script on your own video recordings

  • Load and visualize tracking and velocity data in Python

  • Customize your plots for your lab report


Part 1: Running the Fly Tracker Script#

The fly tracker is a Python script that you’ll run from the command line. Follow the steps below to get set up.

Step 1: Open Anaconda Prompt#

  1. Click the Windows search bar (bottom-left of your screen) and type Anaconda.

  2. Click Anaconda Prompt to open it. You should see a black terminal window.

Note

This is intended to be run on the BIPN 145 Windows lab computers in York 1310. If you are running this on your own Mac computer, you can search for Python and run it that way. If you’re running it on your own Windows computer, you’ll need to download Python or Anaconda.

Step 2: Download the Scripts#

In the Anaconda Prompt, first use the following to move into your folder. Replace USER with the UCSD username (not including @ucsd.edu) for the person that is logged into the computer.

Tip

Copying and pasting into the Anaconda Prompt: You can copy commands from this page by clicking the copy button (📋) at the top-right of each code block and using Ctrl+V to paste into the command line.

cd C:\Users\USER

Then, run the following commands to download the setup and fly tracker scripts:

curl -o setup.py https://raw.githubusercontent.com/ajuavinett/fly_tracker/master/setup.py
curl -o BIPN145_flytrack.py https://raw.githubusercontent.com/ajuavinett/fly_tracker/master/BIPN145_flytrack.py

If you already have older versions of these scripts, this will overwrite them with the latest versions. You can also download the scripts manually from the fly_tracker GitHub page.

Step 3: Install Required Packages#

Run the setup script to install the required Python packages. You only need to do this once per computer:

python setup.py

You should see a message confirming that each package is installed. Once you see “Setup complete!”, you’re ready to go.

Step 4: Know Where Your Video File(s) Are#

Make sure you know where your fly video file(s) (.avi, .mp4, etc.) are saved on your computer. When you run the script, a file picker window will pop up and you can navigate to them from there.

Step 5: Run the Script#

In the Anaconda Prompt, type:

python BIPN145_flytrack.py

The script will walk you through everything interactively:

  1. Select your video(s) — A file picker dialog will pop up. Navigate to your video file(s), select them, and click Open.

  2. Draw the ROI — A window will pop up showing the first frame of your video. Click and drag to draw a rectangle around the dish/arena (and only around the dish – make it as snug as possible!), then press Enter or Space to confirm. Press C to cancel and redraw.

  3. Wait for tracking — The script will process each frame. You’ll see progress updates (10%, 20%, etc.). This will take a few minutes to run per video.

  4. View results — When done, plots will appear will save automatically along with CSV files containing the tracking results. You can either use the plots that are saved or modify them (see below).

Step 6: Find Your Output Files#

After the script finishes, you’ll find two CSV files for each video in the same folder as your video:

File

Contents

yourvideo_tracking.csv

Time (s), X position (cm), Y position (cm)

yourvideo_velocity.csv

Time (s), Velocity (mm/s)

If you need to edit them, you can open these in Excel, Google Sheets, or load them into Python (see below).


Part 2: Working with Your Data in Python (Optional)#

The cells below will help you load your CSV output files and create customized plots in case you need to adjust anything after running the tracking. This is useful if you want to adjust axis labels, colors, or combine data from multiple flies for your lab report.

Task: Run the cell below to import the packages we’ll need.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection

print('Packages imported!')
Packages imported!

Upload your CSV files#

If you’re running this notebook in Google Colab, run the cell below to upload your tracking and velocity CSV files. If you’re running locally, you can skip this cell and just set the file paths directly.

Task: Run the cell below and upload your _tracking.csv and _velocity.csv files.

try:
    from google.colab import files
    uploaded = files.upload()
    filenames = list(uploaded.keys())
    tracking_file = [f for f in filenames if 'tracking' in f][0]
    velocity_file = [f for f in filenames if 'velocity' in f][0]
    print(f'Tracking file: {tracking_file}')
    print(f'Velocity file: {velocity_file}')
except ImportError:
    # Running locally — set your file paths here
    tracking_file = 'yourvideo_tracking.csv'  # <-- change this
    velocity_file = 'yourvideo_velocity.csv'  # <-- change this
    print(f'Using local files: {tracking_file}, {velocity_file}')
Using local files: yourvideo_tracking.csv, yourvideo_velocity.csv

Load the data#

Now let’s load both CSV files using numpy. Each file has a header row, so we skip it with skiprows=1.

Task: Run the cell below to load your data.

# Load tracking data: columns are Time (s), X (cm), Y (cm)
tracking = np.loadtxt(tracking_file, delimiter=',', skiprows=1)
time_pos = tracking[:, 0]
x_pos = tracking[:, 1]
y_pos = tracking[:, 2]

# Load velocity data: columns are Time (s), Velocity (mm/s)
vel_data = np.loadtxt(velocity_file, delimiter=',', skiprows=1)
time_vel = vel_data[:, 0]
velocity = vel_data[:, 1]

print(f'Tracking data: {len(time_pos)} frames')
print(f'Velocity data: {len(time_vel)} time bins')
print(f'Mean velocity: {np.nanmean(velocity):.2f} mm/s')
print(f'SD velocity:   {np.nanstd(velocity):.2f} mm/s')
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
Cell In[3], line 2
      1 # Load tracking data: columns are Time (s), X (cm), Y (cm)
----> 2 tracking = np.loadtxt(tracking_file, delimiter=',', skiprows=1)
      3 time_pos = tracking[:, 0]
      4 x_pos = tracking[:, 1]

File /opt/miniconda3/lib/python3.12/site-packages/numpy/lib/_npyio_impl.py:1395, in loadtxt(fname, dtype, comments, delimiter, converters, skiprows, usecols, unpack, ndmin, encoding, max_rows, quotechar, like)
   1392 if isinstance(delimiter, bytes):
   1393     delimiter = delimiter.decode('latin1')
-> 1395 arr = _read(fname, dtype=dtype, comment=comment, delimiter=delimiter,
   1396             converters=converters, skiplines=skiprows, usecols=usecols,
   1397             unpack=unpack, ndmin=ndmin, encoding=encoding,
   1398             max_rows=max_rows, quote=quotechar)
   1400 return arr

File /opt/miniconda3/lib/python3.12/site-packages/numpy/lib/_npyio_impl.py:1022, in _read(fname, delimiter, comment, quote, imaginary_unit, usecols, skiplines, max_rows, converters, ndmin, unpack, dtype, encoding)
   1020     fname = os.fspath(fname)
   1021 if isinstance(fname, str):
-> 1022     fh = np.lib._datasource.open(fname, 'rt', encoding=encoding)
   1023     if encoding is None:
   1024         encoding = getattr(fh, 'encoding', 'latin1')

File /opt/miniconda3/lib/python3.12/site-packages/numpy/lib/_datasource.py:192, in open(path, mode, destpath, encoding, newline)
    155 """
    156 Open `path` with `mode` and return the file object.
    157 
   (...)
    188 
    189 """
    191 ds = DataSource(destpath)
--> 192 return ds.open(path, mode, encoding=encoding, newline=newline)

File /opt/miniconda3/lib/python3.12/site-packages/numpy/lib/_datasource.py:529, in DataSource.open(self, path, mode, encoding, newline)
    526     return _file_openers[ext](found, mode=mode,
    527                               encoding=encoding, newline=newline)
    528 else:
--> 529     raise FileNotFoundError(f"{path} not found.")

FileNotFoundError: yourvideo_tracking.csv not found.

Plot the fly path#

The plot below shows the fly’s path through the arena, color-coded by time. Earlier positions are shown in purple/blue, and later positions in yellow/green.

Task: Run the cell below to plot the fly path. Edit the axis labels, title, or colormap (cmap) if you’d like to customize it.

# Set the diameter of your dish (should match what you used in the script)
diameter = 4  # cm

fig, ax = plt.subplots(figsize=(6, 6))

# Create color-coded path
valid = ~np.isnan(x_pos) & ~np.isnan(y_pos)
points = np.array([x_pos[valid], y_pos[valid]]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)

lc = LineCollection(segments, cmap='viridis', linewidth=2)
lc.set_array(time_pos[valid][:-1])
ax.add_collection(lc)

ax.set_xlim(0, diameter)
ax.set_ylim(diameter, 0)  # Inverted to match video orientation
ax.set_aspect('equal')

# --- Customize these labels for your report ---
ax.set_xlabel('X-coordinate (cm)', fontsize=12)
ax.set_ylabel('Y-coordinate (cm)', fontsize=12)
ax.set_title('Fly Path', fontsize=14)

cbar = fig.colorbar(lc, ax=ax, orientation='horizontal', pad=0.1)
cbar.set_label('Time (s)')

plt.tight_layout()
plt.show()

Plot velocity over time#

The plot below shows how the fly’s velocity changes over the course of the recording.

Task: Run the cell below to plot the velocity trace. Edit the labels, colors, or axis limits as needed.

fig, ax = plt.subplots(figsize=(10, 4))

ax.plot(time_vel, velocity, linewidth=1.5, color='steelblue')

# --- Customize these for your report ---
ax.set_xlabel('Time (s)', fontsize=12)
ax.set_ylabel('Velocity (mm/s)', fontsize=12)
ax.set_title('Fly Velocity Over Time', fontsize=14)

# Uncomment the line below to set custom axis limits:
# ax.set_xlim(0, 60)   # e.g., first 60 seconds
# ax.set_ylim(0, 20)   # e.g., max 20 mm/s

plt.tight_layout()
plt.show()

print(f'Mean velocity: {np.nanmean(velocity):.2f} mm/s')
print(f'SD velocity:   {np.nanstd(velocity):.2f} mm/s')

Compare multiple flies (optional)#

If you have velocity data from multiple flies, you can plot them together and compute summary statistics.

Task: Upload additional velocity CSV files and add their filenames to the list below.

# Add your velocity CSV filenames here
velocity_files = [
    'fly1_velocity.csv',   # <-- change these
    'fly2_velocity.csv',
    # 'fly3_velocity.csv',
]

fig, ax = plt.subplots(figsize=(10, 5))
all_means = []

for i, vf in enumerate(velocity_files):
    data = np.loadtxt(vf, delimiter=',', skiprows=1)
    t = data[:, 0]
    v = data[:, 1]
    ax.plot(t, v, linewidth=1.5, label=f'Fly {i + 1}')
    all_means.append(np.nanmean(v))

ax.set_xlabel('Time (s)', fontsize=12)
ax.set_ylabel('Velocity (mm/s)', fontsize=12)
ax.set_title('Velocity Comparison', fontsize=14)
ax.legend()
plt.tight_layout()
plt.show()

print(f'\nMean velocity across flies: {np.mean(all_means):.2f} mm/s')
print(f'SD across flies: {np.std(all_means):.2f} mm/s')

About this Notebook#

This notebook was created by Ashley Juavinett for classes at UC San Diego. The fly tracker script is based on MATLAB code by Jeff Stafford.