Mohammed Musharaf Hussain
Medical imaging coursework · Year 3, Queen Mary University of London
This was a lab in my third-year medical imaging module: outline the right atrium on an LGE MRI scan, then measure how close it came to the gold standard.
01The problemContext
The atria are the two upper chambers of the heart. In atrial fibrillation (AF), they beat irregularly. AF raises the risk of stroke and heart failure, so it matters to diagnose it early and plan treatment accurately.
Planning depends on measuring the atria, which needs segmentation: labelling which voxels of an MRI scan belong to a structure. A voxel is a three-dimensional pixel. The right atrium (RA) is hard to segment on LGE MRI, a scan that uses a contrast agent, because its walls are often unclear.
02The data and method3D Slicer
The scan comes from the Cardiac Atlas Project 2018 Atria Segmentation Challenge. I was given the MRI volume, a segmentation of the left atrium (LA), and a gold standard segmentation of the RA. The gold standard is the reference answer my outline is judged against.
In 3D Slicer I applied a threshold, which keeps voxels above a brightness value, and adjusted it until it covered the scan well enough. A threshold also picks up bright tissue nearby, so I painted the RA slice by slice and refined the edges.
03The codeMATLAB
The script's job is to measure how closely my outline matches the gold standard, and to report that as numbers. The scan is a 3D volume built from many 2D slices, like a stack of pages. The script works through that stack slice by slice: it first displays each slice in turn, so the scan and outline can be checked by eye, then counts the voxels where the two outlines agree and disagree across the whole stack. From those counts it works out two scores: precision, which measures how much of what I labelled is correct, and the Dice score, which measures how much the two outlines overlap.
It runs in four steps: it combines the two atria, measures precision on one slice (slice 44), measures precision across every slice, and calculates the Dice score. The four excerpts below are taken from the script, with plotting code omitted.
1 · combine the two atria · MATLAB
% Loading and reading in RA .nii file Segmentation = niftiread('Raendo.nii'); % Read in LA .nii file LA_segmentation = niftiread("laendo.nii"); % Combine RA (1) and LA (2) into one label map Both_Atria = Segmentation + 2.* LA_segmentation; % Write the combination into a .nii file niftiwrite(Both_Atria, 'Both_atria.nii');
The script merges the RA and LA into one file, with the RA as 1 and the LA as 2, so both atria can be viewed together.
2 · precision on slice 44 · MATLAB
% Part 2: slice 44 of both masks Segmentation=niftiread('raendo.nii'); Gold_standard=niftiread('raendo_gold.nii'); sliceno=44 Slice_RA=Segmentation(:, :, sliceno); Slice_gold_standard=Gold_standard(:, :, sliceno); % Precision on this slice true_positives=sum((Slice_gold_standard(:)==1) ... & (Slice_RA(:)==1)); false_positives=sum((Slice_gold_standard(:)==0) ... & (Slice_RA(:)==1)); precision_oneslice=true_positives/ ... (true_positives+false_positives)
Precision asks how much of what I labelled is correct. It is true positives (voxels labelled RA in both outlines) divided by all the voxels I labelled. The script checks one slice first, slice 44, near the middle of the scan.
3 · precision over the whole scan · MATLAB
% Precision over every voxel of the scan true_positives=sum((Gold_standard(:)==1) ... & (Segmentation(:)==1)); false_positives=sum((Gold_standard(:)==0) ... & (Segmentation(:)==1)); precision_allslice=true_positives/ ... (true_positives+false_positives)
The same calculation over every slice gives precision for the whole segmentation.
4 · Dice score · MATLAB
% Calculate True Positives (TP) true_positives = sum((Segmentation(:) == 1) ... & (Gold_standard(:) == 1)); % Calculate False Positives (FP) false_positives = sum((Segmentation(:) == 1) ... & (Gold_standard(:) == 0)); % Calculate False Negatives (FN) false_negatives = sum((Segmentation(:) == 0) ... & (Gold_standard(:) == 1)); % Compute the DICE coefficient DICE = (2 * true_positives) ... / (2 * true_positives + false_positives ... + false_negatives); % Print the results fprintf('DICE Coefficient: %.4f\n', DICE);
Dice measures overlap. It is twice the shared voxels divided by the total size of the two outlines. A missed voxel adds to the size of the gold standard but not to the shared count, so Dice falls whenever part of the atrium is missed, even if everything I labelled is correct.
Running the script prints these values:
output · MATLAB
Precision for one specified slice: 0.7632 Precision for entire segmentation: 0.8005 True Positives: 137218 False Positives: 34204 False Negatives: 167321 DICE Coefficient: 0.5766
04The resultPrecision, Dice
The script compares my outline with the reference outline, voxel by voxel, and gives two scores for how well they match. A voxel is one cell of the 3D scan.
The first score is precision: 0.8005, or about 80%. It means that when I marked a voxel as right atrium, it was almost always right atrium in the reference. On the one slice I checked in detail, slice 44, the figure was 0.7632.
The second score is the Dice score: 0.5766. It measures overall agreement on a scale from 0 to 1, where 1 would mean the two outlines are identical. This is a partial match rather than an exact one. The two outlines agreed on 137,218 voxels, and I marked 34,204 voxels that the reference does not include.
Figure 3 shows slice 44 with both outlines on the same grid. My outline is shaded and the reference outline is a dashed line. The two overlap closely, and my outline extends beyond the reference in places.
Put simply, the voxels I marked as atrium were usually correct, and the overall match was partial. These figures come from one scan, outlined by one person.
05Where segmentation is headingNeural networks
Manual painting depends on the observer, and the threshold depends on image quality. Neural networks are the main route past both. A neural network is a stack of simple calculations whose internal weights are adjusted during training until its output matches labelled examples. For this task, the examples would be scans with gold standard outlines like these, and the output would be a label for every voxel. Convolutional networks (CNNs) suit images because their filters learn local patterns such as edges.
Neural networks can reach higher accuracy than traditional methods, but they need large labelled datasets and substantial computing power. For now the practical approach is hybrid: a network drafts the outline and a person checks it. Li et al. (2020) used a multiscale CNN to segment scar on LGE MRI, and Matsumoto et al. (2022) showed a CNN could detect atrial fibrillation from chest radiographs. Both are related tasks rather than the same one.
Sources: Coursework report, Medical Imaging Lab 3; Cardiac Atlas Project 2018 Atria Segmentation Challenge; Li et al. (2020); Matsumoto et al. (2022).