Single-Pixel Fluorescence in Calcium Imaging
Authors
Setup
Import Libraries
import numpy as np
from tifffile import imread
import matplotlib.pyplot as plt
from scipy.ndimage import median_filter, gaussian_filter1dDownload Data
#| output: false
import owncloud
import os
if not os.path.exists('data'):
print('Creating directory for data')
os.mkdir('data')
if not os.path.exists('data/demoMovie.tif'):
oc = owncloud.Client.from_public_link('https://uni-bonn.sciebo.de/s/NwtdrIg5wGdeGcB')
oc.get_file('/', 'data/demoMovie.tif');Creating directory for dataIn this notebook we take a close look at how the brightness of a single pixel changes over time and what it tells us about underlying neural activity. We begin by visualising different frames and projections of a movie, then focus on extracting and plotting raw traces from selected pixels. From there we explore key processing steps such as neuropil correction, baseline estimation, and signal normalization using ΔF/F₀. These steps help isolate genuine activity from background noise and form the foundation for more advanced analyses later in the course.
Section 1: Flurorescence Signals from a Single Pixel.
In this section, let us look at the brightness (fluorescence) of a single pixel over time. This brightness changes when calcium levels in the cell change which is an indicator that the neuron is “active”. Plotting how the fluorescence changes shows us when “activity” might have happened in that pixel.
Exercises
Example: Plot the first frame as representative image.
data = imread('data/demoMovie.tif')
rep_image = data[0, :, :]
plt.imshow(rep_image, cmap='gray')Exercise: Plot the 116th frame as representative image.
Solution
data = imread('data/demoMovie.tif')
rep_image = data[115, :, :]
plt.imshow(rep_image, cmap='gray')Exercise: Plot the last frame as representative image.
Solution
data = imread('data/demoMovie.tif')
rep_image = data[-1, :, :]
plt.imshow(rep_image, cmap='gray')Example: Plot mean projection of all the frames as representative image.
data = imread('data/demoMovie.tif')
rep_image = data[:, :, :].mean(axis=0)
plt.imshow(rep_image, cmap='gray')Exercise: Plot the maximum projection of all frames as representative image.
Solution
data = imread('data/demoMovie.tif')
rep_image = data.max(axis=0)
plt.imshow(rep_image, cmap='gray')Exercise: Plot maximum projection from 100th to 500th frame only.
Solution
data = imread('data/demoMovie.tif')
rep_image = data[100:500, :, :].max(axis=0)
plt.imshow(rep_image, cmap='gray')Let us consider three cells located within rectangular box defined by:
| Cell | xmin | xmax | ymin | ymax |
|---|---|---|---|---|
| Cell 1 | 15 | 35 | 20 | 35 |
| Cell 2 | 55 | 75 | 23 | 40 |
| Cell 3 | 0 | 10 | 23 | 40 |
Example: Plot mean projection image cropped to Cell 1.
data = imread('data/demoMovie.tif')
rep_image = data[:, 20:35, 15:35].mean(axis=0)
plt.imshow(rep_image, cmap='gray')Exercise: Plot mean projection image cropped to Cell 2.
Solution
data = imread('data/demoMovie.tif')
rep_image = data[:, 23:40, 55:75].mean(axis=0)
plt.imshow(rep_image, cmap='gray')Exercise: Plot max projection of only the first 100 frames cropped to Cell 3.
Solution
data = imread('data/demoMovie.tif')
rep_image = data[0:100, 23:40, 0:10].max(axis=0)
plt.imshow(rep_image, cmap='gray')Example: Select a pixel from Cell 1.
rep_image = data[:, :, :].mean(axis=0)
plt.imshow(rep_image, cmap='gray')
plt.scatter(60, 30, color='r')Exercise: Select a pixel from Cell 2.
Solution
rep_image = data[:, :, :].mean(axis=0)
plt.imshow(rep_image, cmap='gray')
plt.scatter(39, 40, color='r')Exercise: Select a pixel from Cell 3.
Solution
rep_image = data[:, :, :].mean(axis=0)
plt.imshow(rep_image, cmap='gray')
plt.scatter(21, 25, color='r')Example: Plot calcium trace extracted from Cell 1.
rep_image = data[:, :, :].mean(axis=0)
trace = data[:, 25, 21]
plt.subplot(211)
plt.imshow(rep_image, cmap='gray')
plt.scatter(21, 25, color='r')
plt.subplot(212)
plt.plot(trace)Exercise: Plot calcium trace extracted from Cell 2.
Solution
rep_image = data[:, :, :].mean(axis=0)
trace = data[:, 40, 39]
plt.subplot(211)
plt.imshow(rep_image, cmap='gray')
plt.scatter(39, 40, color='r')
plt.subplot(212)
plt.plot(trace)Exercise: Plot calcium trace extracted from Cell 3.
Solution
rep_image = data[:, :, :].mean(axis=0)
trace = data[:, 25, 21]
plt.subplot(211)
plt.imshow(rep_image, cmap='gray')
plt.scatter(21, 25, color='r')
plt.subplot(212)
plt.plot(trace)Section 2: Neuropil Correction.
The brightness we see in a pixel is, usually, not just from the cell we care about. It also includes light from nearby tissue called neuropil. To get a cleaner signal, we subtract a portion of this surrounding signal. This process is called neuropil correction.
Exercises
data = imread('data/demoMovie.tif')
rep_image = np.mean(data, axis=0)Example: Take global mean of all frames and pixels to get neuropil trace.
npil_trace = np.mean(data, axis=(1,2))
plt.plot(npil_trace)Exercise: Take global median of all frames and pixels to get neuropil trace.
Solution
npil_trace = np.median(data, axis=(1,2))
plt.plot(npil_trace)Exercise: Take global mean of all frames and pixels to get neuropil trace.
Solution
npil_trace = np.min(data, axis=(1,2))
plt.plot(npil_trace)Example: Extract neuropil trace from just outside Cell 1.
plt.subplot(311)
plt.imshow(rep_image, cmap='gray')
plt.scatter(39, 40, color='r')
plt.scatter(39, 45, color='r', marker='x')
plt.subplot(312)
plt.plot(data[:, 40, 39])
plt.subplot(313)
plt.plot(data[:, 45, 39])Exercise: Extract neuropil trace from just outside Cell 2.
Solution
plt.subplot(211)
plt.imshow(rep_image, cmap='gray')
plt.scatter(60, 30, color='r')
plt.scatter(56, 33, color='r', marker='x')
plt.subplot(212)
plt.plot(data[:, 30, 60])
plt.plot(data[:, 33, 56])Exercise: Extract neuropil trace from just outside Cell 3.
Solution
plt.subplot(211)
plt.imshow(rep_image, cmap='gray')
plt.scatter(21, 25, color='r')
plt.scatter(20, 28, color='r', marker='x')
plt.subplot(212)
plt.plot(data[:, 25, 21])
plt.plot(data[:, 28, 20])Example: Subtract neuropil trace from Cell 3 calcium trace with a scaling factor of 1.0.
cell_trace = data[:, 25, 21]
neuropil_trace = data[:, 28, 20]
r = 1.0
F_corr = cell_trace - r*neuropil_trace
plt.subplot(211)
plt.plot(cell_trace)
plt.plot(neuropil_trace)
plt.subplot(212)
plt.plot(F_corr)Exercise: Subtract neuropil trace from Cell 2 calcium trace with a scaling factor of 0.7.
Solution
cell_trace = data[:, 30, 60]
neuropil_trace = data[:, 33, 56]
r = 0.7
F_corr = cell_trace - r*neuropil_trace
plt.subplot(211)
plt.plot(cell_trace)
plt.plot(neuropil_trace)
plt.subplot(212)
plt.plot(F_corr)Exercise: Subtract neuropil trace from Cell 1 calcium trace with a scaling factor of 1.0.
Solution
cell_trace = data[:, 40, 39]
neuropil_trace = data[:, 45, 39]
r = 0.2
F_corr = cell_trace - r*neuropil_trace
plt.subplot(211)
plt.plot(cell_trace)
plt.plot(neuropil_trace)
plt.subplot(212)
plt.plot(F_corr)Section 3: Estimating Baseline Fluorescence (F0).
Even when a neuron is not active, the signal is not zero because of slow change in signal or noise. This resting level of fluorescence is called baseline flurorescence or F0.
Exercises
cell1_corr_trace = data[:, 40, 39] - 0.7 * data[:, 45, 39]
cell2_corr_trace = data[:, 30, 60] - 0.7 * data[:, 33, 56]
cell3_corr_trace = data[:, 25, 21] - 0.7 * data[:, 28, 20]Example: Plot mean baseline activity of Cell 1.
F0 = np.mean(cell1_corr_trace)
plt.plot(cell1_corr_trace)
plt.axhline(F0, color='r')Exercise: Plot median baseline activity of Cell 2.
Solution
F0 = np.median(cell2_corr_trace)
plt.plot(cell2_corr_trace)
plt.axhline(F0, color='r')Exercise: Plot 10th percentile as baseline activity of Cell 3.
Solution
F0 = np.percentile(cell3_corr_trace, 10)
plt.plot(cell3_corr_trace)
plt.axhline(F0, color='r')Example: Use moving median window to estimate baseline activity of Cell 1 with window size of 501.
F0 = median_filter(cell1_corr_trace, 501)
plt.plot(cell1_corr_trace)
plt.plot(F0, color='r')Exercise: Use moving median window to estimate baseline activity of Cell 2 with window size of 501.
Solution
F0 = median_filter(cell2_corr_trace, 501)
plt.plot(cell2_corr_trace)
plt.plot(F0, color='r')Exercise: Use moving median window to estimate baseline activity of Cell 1 with an even larger window size.
Solution
F0 = median_filter(cell2_corr_trace, 1001)
plt.plot(cell2_corr_trace)
plt.plot(F0, color='r')Exercise: Use moving median window to estimate baseline activity of Cell 1 with a very small window size.
Solution
F0 = median_filter(cell2_corr_trace, 11)
plt.plot(cell2_corr_trace)
plt.plot(F0, color='r')Section 4: dF/F0 Normalization.
We usually want to quantify how much the fluorescence signal increases relative to that baseline so that we can compare signals from different neurons. This is done by computing ΔF/F₀, where:
Here, F is the fluorescence at each time point, and ΔF (delta F) is the difference between the current signal and the baseline. The resulting ΔF/F₀ value expresses the signal change as a fraction of the baseline, which is useful for comparing activity levels across different cells or imaging sessions.
Exercises
Example: Remove median baseline activity from Cell 1.
F0 = np.median(cell1_corr_trace)
df = cell1_corr_trace - F0
plt.plot(cell1_corr_trace)
plt.plot(df)Exercise: Remove baseline estimated from moving median filter with an appropriate size from the activity from Cell 3.
Solution
F0 = median_filter(cell3_corr_trace, 201)
df = cell3_corr_trace - F0
plt.plot(cell3_corr_trace)
plt.plot(df)Exercise: Remove baseline estimated from moving median filter with a very small size from the activity from Cell 3.
Solution
F0 = median_filter(cell3_corr_trace, 11)
df = cell3_corr_trace - F0
plt.plot(cell3_corr_trace)
plt.plot(df)Example: Plot df/f0 for Cell 1.
F0 = median_filter(cell1_corr_trace, 201)
dff = (cell1_corr_trace - F0) / F0
plt.plot(dff)Exercise: Plot df/f0 for Cell 2.
Solution
F0 = median_filter(cell2_corr_trace, 201)
dff = (cell2_corr_trace - F0) / F0
plt.plot(dff)Exercise: Plot df/f0 for Cell 3.
Solution
F0 = median_filter(cell3_corr_trace, 201)
dff = (cell3_corr_trace - F0) / F0
plt.plot(dff)