6. Ejection-Fraction Predictionο
This chapter covers PredictEF.py β it predicts the ejection fraction (EF) of
the left ventricle directly from echocardiogram videos.
6.1. Configure the pathsο
Edit the configuration block at the top of PredictEF.py:
INPUT_DIR = r"...\EchoNet-dynamic\demo\video"
WEIGHTS = r"...\EchoNet-dynamic\stats\r2plus1d_18_32_2_pretrained.pt"
OUTPUT_CSV = r"...\EchoNet-dynamic\demo_output\EFprediction.csv"
MEAN = [0.128, 0.129, 0.130]
STD = [0.196, 0.196, 0.197]
Variable |
Meaning |
|---|---|
|
Folder scanned for |
|
R(2+1)D-18 checkpoint ( |
|
Output CSV file path (its folder is created automatically) |
|
Per-channel normalisation stats (0β1 scale) |
To run on your own videos, point INPUT_DIR at your folder.
Keep the default MEAN/STD for EchoNet-style data, or recompute them for your
own dataset β see Normalization Statistics.
The scriptβs main() also accepts a device_name argument (default 'cuda'),
which automatically falls back to CPU when no GPU is present.
6.2. Run the scriptο
conda activate echonet
python scripts/PredictEF.py
Typical output:
Using device: cuda for inference...
Successfully located 12 videos. Starting prediction...
[patient001.avi] -> contains 145 clips, overall predicted EF: 58.42% (Normal)
[patient002.avi] -> contains 98 clips, overall predicted EF: 34.10% (Low)
[patient003.avi] -> contains 120 clips, overall predicted EF: 73.80% (High)
...
Saving prediction results to: ...\EchoNet-dynamic\demo_output\EFprediction.csv
β
Save complete!
6.3. What happens under the hoodο
6.3.1. Modelο
model = torchvision.models.video.r2plus1d_18(pretrained=False)
model.fc = torch.nn.Linear(model.fc.in_features, 1) # 1 output = EF regression
This is a spatio-temporal network: it takes short video clips (not single frames) and regresses a single EF number.
6.3.2. Clip sampling (test-time augmentation)ο
Each video is turned into many overlapping clips. With defaults frames=32 and
period=2, a clip spans 32 Γ 2 = 64 raw frames, sampled every 2nd frame:
target_length = self.frames * self.period # 64
for start in range(T - target_length + 1): # one clip per start offset
clip = video_tensor[start : start + target_length : self.period]
So a long video produces hundreds of clips. If a video has fewer than 64 frames, it is padded and treated as a single clip.
Each clip is scaled to [0, 1] (divided by 255) and then normalised with the
configured MEAN/STD:
clips = np.array(clips).astype(np.float32) / 255.0
clips = (clips - self.mean) / self.std
6.3.3. Prediction and averagingο
For each video, clips are fed to the GPU in chunks (block_size = 10) to avoid
running out of memory, and the per-clip EF predictions are averaged into one
final value:
pred_ef = np.mean(pred_list)
if pred_ef < 50:
status = "Low"
elif pred_ef <= 70:
status = "Normal"
else:
status = "High"
Tune the chunk size for your GPU
block_size = 10 is conservative. If you have β₯12 GB VRAM you can raise it to 20
or 40 (in the main() function) to speed things up. Lower it if you hit
out-of-memory errors.
6.4. Outputο
A single CSV (OUTPUT_CSV) with one row per video:
Column |
Meaning |
|---|---|
|
Source video name |
|
Averaged EF prediction, rounded to 2 decimals |
|
|
The file is written with utf-8-sig encoding so it opens cleanly in Excel.
Clinical disclaimer
The Status label is a fixed-threshold screening heuristic (Low / Normal / High),
not a diagnosis. Always have predictions reviewed by a qualified clinician before
drawing conclusions.
Continue to Results & Troubleshooting.