20.03.2021 Views

Deep-Learning-with-PyTorch

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

410 CHAPTER 14 End-to-end nodule analysis, and where to go next

We’ll break down the segmentCt, groupSegmentationOutput, and classifyCandidates

methods in the following sections.

14.3.1 Segmentation

First up, we are going to perform segmentation on every slice of the entire CT scan.

As we need to feed a given patient’s CT slice by slice, we build a Dataset that loads a

CT with a single series_uid and returns each slice, one per __getitem__ call.

NOTE The segmentation step in particular can take quite a while when executed

on the CPU. Even though we gloss over it here, the code will use the

GPU if available.

Other than the more expansive input, the main difference is what we do with the output.

Recall that the output is an array of per-pixel probabilities (that is, in the range

0…1) that the given pixel is part of a nodule. While iterating over the slices, we collect

the slice-wise predictions in a mask array that has the same shape as our CT input.

Afterward, we threshold the predictions to get a binary array. We will use a threshold

of 0.5, but if we wanted to, we could experiment with thresholding to trade getting

more true positives for an increase in false positives.

We also include a small cleanup step using the erosion operation from

scipy.ndimage.morphology. It deletes one layer of boundary voxels and only keeps

the inner ones—those for which all eight neighboring voxels in the axis direction are

also flagged. This makes the flagged area smaller and causes very small components

(smaller than 3 × 3 × 3 voxels) to vanish. Put together with the loop over the data

loader, which we instruct to feed us all slices from a single CT, we have the following.

Listing 14.2

nodule_analysis.py:384, .segmentCt

We do not need gradients here,

so we don’t build the graph.

… we run the

segmentation

model …

def segmentCt(self, ct, series_uid):

with torch.no_grad():

output_a = np.zeros_like(ct.hu_a, dtype=np.float32)

seg_dl = self.initSegmentationDl(series_uid) #

for input_t, _, _, slice_ndx_list in seg_dl:

input_g = input_t.to(self.device)

prediction_g = self.seg_model(input_g)

This array will hold

our output: a float

array of probability

annotations.

After moving the

input to the GPU …

for i, slice_ndx in enumerate(slice_ndx_list):

output_a[slice_ndx] = prediction_g[i].cpu().numpy()

mask_a = output_a > 0.5

mask_a = morphology.binary_erosion(mask_a, iterations=1)

We get a data

loader that lets

us loop over our

CT in batches.

… and copy each

element to the

output array.

return mask_a

Thresholds the probability outputs

to get a binary output, and then

applies binary erosion as cleanup

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!