SarcGraph Tutorial
All demos are availble on GitHub at https://github.com/Sarc-Graph/sarcgraph/tree/main/tutorials.
To run demos with jupyter notebook check Installation Guide.
SarcGraph incorporates functions to automatically detect and track z-discs and sarcomeres in movies of beating human induced pluripotent stem cell-derived cardiomyocytes (hiPSC-CMs). In addition, SarcGraph includes the tools which enable the recovery of basic sarcomere characteristics and the ability to run further high level analysis.
In this notebook we provide a tutorial on how to use the SarcGraph package using demos and examples. The focus if on the SarcGraph class in the sarcgraph.sg module.
SarcGraph on a Sample Video
In this section we show how to use SarcGraph to detect and track z-discs and sarcomeres in a sample movie of beating hiPSC-CM.
To showcase this we use samples/sample_0.avi.
Import Modules
The first step is to import SarcGraph class from sarcgraph.sg module:
Note: If you are accessing this tutorial through Binder, please uncomment and run this cell.
[ ]:
# !pip install -e ../.
[1]:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sarcgraph.sg import SarcGraph
SarcGraph Initialization
Next we need to create an instance of SarcGraph class:
SarcGraph class takes two optional inputs: - output_dir: the directory to save the results - default: 'test-run' - file_type: whether the sample is a video or an image - default: 'video'
[2]:
sg = SarcGraph(output_dir='../tutorial-results', file_type='video')
Run Detection
The simplest way to perform sarcomere detection on the video sample is to run SarcGraph().sarcomere_detection function:
[3]:
sarcomeres, _ = sg.sarcomere_detection(file_path='../samples/sample_0.avi')
Frame 79: 81 trajectories present.
SarcGraph().sarcomere_detection automatically runs in three steps:
Z-disc segmentation: load the video, filter frames, and detect z-discs
Z-disc tracking: track detected z-discs
Sarcomere detection: detect sarcomeres using tracked z-discs information
And, this is how the output looks like:
Note
sarcomeres.sample(5) randomly picks 5 samples to display, and running the cell multiple times should output different results.
[4]:
sarcomeres.sample(5)
[4]:
| frame | sarc_id | x | y | length | width | angle | zdiscs | |
|---|---|---|---|---|---|---|---|---|
| 220 | 60 | 2 | 278.646713 | 267.714240 | 11.397091 | 14.080775 | 0.000067 | -1,26 |
| 4877 | 77 | 60 | 116.730978 | 214.462941 | 12.949554 | 14.099212 | 2.805399 | 59,79 |
| 2910 | 30 | 36 | 264.642071 | 218.846934 | 13.029619 | 13.280433 | 0.318365 | 31,35 |
| 3830 | 70 | 47 | 113.105042 | 267.712294 | 11.226317 | 13.896309 | 0.002197 | 44,60 |
| 219 | 59 | 2 | 280.124606 | 270.096786 | 10.119645 | 14.683792 | 0.003224 | -1,26 |
By dafault save_output=True in sg.sarcomere_detection() and the following information will be saved in sg.output_dir:
raw video frames (grayscale)
filtered video frames
zdisc contours
segmented zdiscs information
tracked zdiscs information
detected sarcomeres information
Simple Visualization
We briefly show how to use the saved information to visualize z-discs and sarcomeres in one frame of the video.
[5]:
# load the information
raw_frame = np.load(f"{sg.output_dir}/raw-frames.npy")[0, :, :, 0]
tracked_zdiscs = pd.read_csv(f"{sg.output_dir}/tracked-zdiscs.csv", index_col=[0])
[6]:
sarcs_frame = sarcomeres[sarcomeres.frame == 0]
zdiscs_frame = tracked_zdiscs[tracked_zdiscs.frame == 0]
plt.figure(figsize=(7,7))
plt.axes().set_title('Sarcomeres and Z-Discs in Frame 0')
plt.axis('off')
plt.imshow(raw_frame, cmap='gray')
plt.plot(zdiscs_frame.y, zdiscs_frame.x, 'r.')
plt.plot(sarcs_frame.y, sarcs_frame.x, 'b.', ms=10)
[6]:
[<matplotlib.lines.Line2D at 0x285ad65b160>]
Alternative Way to Run SarcGraph
As mentioned in section Run Detection, sg.sarcomere_detection performs three tasks consecutively. Each task can also be run separately. Note that the results will not be saved since we have set save_output=False. We show how this can be done below:
Z-disc Segmentation
Run sg.zdisc_segmentation().
Input could be either
file_pathorraw_frames.
Notes
raw_framesmust be a 4d numpy array withshape=(frames, dim 1, dim 2, channels), for example, for a grayscale video with 20 frames and resolution of 400x400 the shape ofraw_framesis(20, 400, 400, 1)SarcGraph uses
scikitlibrary to load video/image files and can handle most formats. Yet, we recommend loading your sample video/image into a numpy array and pass the array as the input toSarcGraphfunctions, especially if the video/image is saved as aTIFFfile.
[7]:
zdiscs = sg.zdisc_segmentation(file_path='../samples/sample_0.avi', save_output=False)
Z-disc Tracking
Run sg.zdisc_tracking().
If the input is either
file_pathorraw_frames, this function runssg.zdisc_segmentationfirst and then applies tracking.If the input is
segmented_zdiscs,sg.zdisc_trackingonly runs tracking.
[8]:
tracked_zdiscs = sg.zdisc_tracking(segmented_zdiscs=zdiscs, save_output=False)
Frame 79: 81 trajectories present.
Let’s check the information stored in tracked_zdiscs:
[9]:
tracked_zdiscs.sample(10)
[9]:
| frame | x | y | p1_x | p1_y | p2_x | p2_y | particle | freq | |
|---|---|---|---|---|---|---|---|---|---|
| 3270 | 40 | 279.114063 | 166.565872 | 275.000000 | 160.430515 | 283.000000 | 172.113936 | 24 | 80 |
| 1661 | 20 | 184.597237 | 169.178027 | 182.000000 | 163.418879 | 187.000000 | 174.990273 | 39 | 80 |
| 770 | 9 | 173.353513 | 217.482265 | 174.000000 | 212.414066 | 172.000000 | 222.388170 | 62 | 80 |
| 968 | 11 | 141.369782 | 146.787244 | 144.000000 | 141.701820 | 139.000000 | 152.430764 | 76 | 80 |
| 5919 | 72 | 138.771012 | 76.135783 | 142.000000 | 70.795997 | 135.000000 | 81.007262 | 77 | 80 |
| 4286 | 52 | 159.956104 | 278.157040 | 160.000000 | 270.661829 | 160.000000 | 285.328813 | 66 | 80 |
| 488 | 6 | 232.340605 | 210.864295 | 234.000000 | 204.086232 | 232.000000 | 217.365572 | 1 | 80 |
| 1356 | 16 | 121.575798 | 159.141550 | 118.000000 | 152.555463 | 125.000000 | 165.887396 | 42 | 80 |
| 1321 | 16 | 279.379684 | 166.246376 | 274.901385 | 160.000000 | 283.047762 | 172.000000 | 24 | 80 |
| 3214 | 39 | 100.737593 | 127.985250 | 98.000000 | 122.160055 | 103.000000 | 132.979194 | 51 | 80 |
Sarcomere Detection Step
Run sg.sarcomere_detection().
If the input is either
file_pathorraw_frames, this function runssg.zdisc_segmentationthensg.zdisc_trackingbefore applying sarcomere detection.If the input is
segmented_zdiscs, this function runssg.zdisc_trackingfirst and then sarcomere detection.If the input is
tracked_zdiscs, this function only runs sarcomere detection.
[10]:
sarcs, _ = sg.sarcomere_detection(tracked_zdiscs=tracked_zdiscs)
SarcGraph on a Sample Image
To analyze a single frame we can follow the steps with a few changes:
When we are working on single-frame images and not videos, the tracking part cannot be done! Therefore, during SarcGraph Initialization we should set
file_type="image".If rather than running
SarcGraph().sarcomere_detection, we need to process the image step by step, we should still runSarcGraph().zdiscs_trackingalthough it does not do much.
Import SarcGraph class first:
[11]:
from sarcgraph.sg import SarcGraph
Create a new instance of SarcGraph class and set file_type='image'
[12]:
sg_img = SarcGraph(output_dir='../tutorial-results-image', file_type='image')
Let’s load a sample image to a numpy array and pass it to sg_img.sarcomere_detection() rather than specifying file_path:
Note
We used matplotlib.image to load the image here.
[13]:
import matplotlib
img = matplotlib.image.imread('../samples/sample_5.png')
[14]:
plt.figure(figsize=(7,7))
plt.axes().set_title('Sample Image Visualized')
plt.axis('off')
plt.imshow(img, cmap='gray')
[14]:
<matplotlib.image.AxesImage at 0x285ad6bd420>
Our sample image has the shape (368, 368) which needs to be reshaped to (1, 368, 368, 1) - [frames, dimension 1, dimension 2, color channels].
[15]:
img = img[None, :, :, None]
Now we can run sg_img.sarcomere_detection():
[16]:
sarcs_img, _ = sg_img.sarcomere_detection(raw_frames=img)
Similar to the video sample, we will visualize detected sarcomeres in this image:
[17]:
segmented_zdiscs = pd.read_csv(f"{sg_img.output_dir}/segmented-zdiscs.csv", index_col=[0])
plt.figure(figsize=(7,7))
plt.axes().set_title('Detected Z-Discs and Sarcomeres')
plt.axis('off')
plt.imshow(img[0, :, :, 0], cmap='gray')
plt.plot(sarcs_img.y, sarcs_img.x, 'r.')
plt.plot(segmented_zdiscs.y, segmented_zdiscs.x, 'b.', ms=10)
[17]:
[<matplotlib.lines.Line2D at 0x285ad34d090>]
[ ]: