Skip to content

Commit 2b87d83

Browse files
committed
save distance maps to NiFti format
1 parent 41158cd commit 2b87d83

File tree

2 files changed

+44
-17
lines changed

2 files changed

+44
-17
lines changed

calc_margin.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import numpy as np
44
import pandas as pd
55

6-
6+
import scripts.plot_ablation_margin_hist as pm
77
from customradiomics.margin import compute_distances
88
from utils.niftireader import load_image
99

@@ -36,33 +36,46 @@ def get_args():
3636
ap.add_argument("-l", "--liver", required=True, help="path to the liver segmentation")
3737
ap.add_argument("-i", "--lesion-id", required=True, help="lesion id")
3838
ap.add_argument("-p", "--patient-id", required=True, help="patient id from study")
39-
ap.add_argument("-o", "--OUTPUT", required=True, help="output file (csv)")
39+
ap.add_argument("-o", "--OUTPUT", required=True, help="output file (csv)")
4040
args = vars(ap.parse_args())
4141
return args
4242

4343

4444
if __name__ == '__main__':
4545
args = get_args()
46-
46+
# -t
47+
# "data\B04\01\B04_L01_Tumor.nii.gz" - a
48+
# "data\B04\01\B04_L01_Ablation.nii.gz" - l
49+
# "data\B04\01\B04_L01_Liver.nii.gz" - i
50+
# 1 - p
51+
# "B04" - o
52+
# "output111.csv"
4753
tumor_file = args['tumor']
4854
ablation_file = args['ablation']
4955
liver_file = args['liver']
5056
patient_id = args['patient_id']
5157
lesion_id = args['lesion_id']
5258
output_file = args['OUTPUT']
53-
59+
rootdir = r"C:\develop\segmentation-eval\figures"
5460
tumor, tumor_np = load_image(tumor_file)
5561
ablation, ablation_np = load_image(ablation_file)
5662
liver, liver_np = load_image(liver_file)
63+
affine_transform = tumor.affine
5764

5865
has_liver_segmented = np.sum(liver_np.astype(np.uint8)) > 0
5966

6067
pixdim = liver.header['pixdim']
6168
spacing = (pixdim[1], pixdim[2], pixdim[3])
62-
surface_distance = compute_distances(tumor_np, ablation_np,
69+
# compute_distances(mask_gt, mask_pred, exclusion_zone, spacing_mm, connectivity=1, crop=True, exclusion_distance=5)
70+
surface_distance = compute_distances(tumor_np, ablation_np, affine_transform,
6371
exclusion_zone=liver_np if has_liver_segmented else None,
6472
spacing_mm=spacing, connectivity=1)
6573

74+
pm.plot_histogram_surface_distances(patient_id, lesion_id, rootdir, distance_map=surface_distance['distances_gt_to_pred'],
75+
num_voxels=len(surface_distance['distances_gt_to_pred']),
76+
title='SurfaceDistance_Histogram_B04_L1', ablation_date="20160101",
77+
flag_to_plot=True)
78+
6679
df = pd.DataFrame(data={
6780
'Patient': [patient_id] * len(surface_distance['distances_gt_to_pred']),
6881
'Lesion': [lesion_id] * len(surface_distance['distances_gt_to_pred']),

customradiomics/margin.py

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import numpy as np
22
from scipy import ndimage
3-
3+
import matplotlib.pyplot as plt
4+
import nibabel as nib
45

56
def compute_bounding_box(mask_gt, mask_pred, exclusion_zone):
67
mask_all = mask_gt | mask_pred
@@ -27,25 +28,26 @@ def compute_bounding_box(mask_gt, mask_pred, exclusion_zone):
2728
bbox_min[2] = np.min(idx_nonzero_2)
2829
bbox_max[2] = np.max(idx_nonzero_2)
2930

30-
# crop the processing subvolume.
31-
# we need to zeropad the cropped region with 1 voxel at the lower,
32-
# the right and the back side. This is required to obtain the "full"
33-
# convolution result with the 2x2x2 kernel
34-
# cropmask = np.zeros((bbox_max - bbox_min)+2, np.uint8)
3531

3632
return bbox_min, bbox_max
3733

3834

3935
def crop_mask(mask, bbox_min, bbox_max):
36+
4037
cropmask = np.zeros((bbox_max - bbox_min) + 2)
4138

4239
cropmask[0:-1, 0:-1, 0:-1] = mask[bbox_min[0]:bbox_max[0] + 1,
4340
bbox_min[1]:bbox_max[1] + 1,
4441
bbox_min[2]:bbox_max[2] + 1]
42+
# TODO: This is correct only if the object is interior to the bounding box. Must perform testing.
43+
# crop the processing subvolume.
44+
# we need to zeropad the cropped region with 1 voxel at the lower,
45+
# the right and the back side. This is required to obtain the "full"
46+
# convolution result with the 2x2x2 kernel
4547
return cropmask
4648

4749

48-
def compute_distances(mask_gt, mask_pred, exclusion_zone, spacing_mm, connectivity=1, crop=True, exclusion_distance=5):
50+
def compute_distances(mask_gt, mask_pred, affine_transform, exclusion_zone, spacing_mm, connectivity=1, crop=True, exclusion_distance=5):
4951
if crop:
5052
if exclusion_zone is not None:
5153
bbox_min, bbox_max = compute_bounding_box(mask_gt, mask_pred, exclusion_zone)
@@ -58,16 +60,24 @@ def compute_distances(mask_gt, mask_pred, exclusion_zone, spacing_mm, connectivi
5860
mask_pred = crop_mask(mask_pred, bbox_min, bbox_max).astype(np.bool)
5961

6062
border_inside = ndimage.binary_erosion(mask_gt, structure=ndimage.generate_binary_structure(3, connectivity))
61-
borders_gt = mask_gt ^ border_inside
63+
ni_img = nib.Nifti1Image(border_inside.astype(np.uint8), affine_transform)
64+
nib.save(ni_img, 'border_inside_tumor.nii.gz')
65+
borders_gt = mask_gt ^ border_inside # contours
66+
ni_img = nib.Nifti1Image(borders_gt.astype(np.uint8), affine_transform)
67+
nib.save(ni_img, 'contour_tumor.nii.gz')
6268
border_inside = ndimage.binary_erosion(mask_pred, structure=ndimage.generate_binary_structure(3, connectivity))
63-
borders_pred = mask_pred ^ border_inside
64-
65-
# compute the distance transform (closest distance of each voxel to the
66-
# surface voxels)
69+
borders_pred = mask_pred ^ border_inside # contours
70+
ni_img = nib.Nifti1Image(borders_pred.astype(np.uint8), affine_transform)
71+
nib.save(ni_img, 'contour_ablation.nii.gz')
72+
# compute the distance transform (closest distance of each voxel to the surface voxels)
6773
if borders_gt.any():
6874
distmap_gt = ndimage.morphology.distance_transform_edt(
6975
~borders_gt, sampling=spacing_mm)
76+
ni_img = nib.Nifti1Image(distmap_gt.astype(np.uint8), affine_transform)
77+
nib.save(ni_img, 'distmap_tumor.nii.gz')
7078
distmask_gt = mask_gt.astype(np.int8)
79+
# todo: visualize with matplotlib
80+
# mark outside zones with -1
7181
distmask_gt[distmask_gt == 0] = -1
7282
distmap_gt *= distmask_gt
7383
else:
@@ -76,7 +86,10 @@ def compute_distances(mask_gt, mask_pred, exclusion_zone, spacing_mm, connectivi
7686
if borders_pred.any():
7787
distmap_pred = ndimage.morphology.distance_transform_edt(
7888
~borders_pred, sampling=spacing_mm)
89+
ni_img = nib.Nifti1Image(distmap_pred, affine_transform)
90+
nib.save(ni_img, 'distmap_ablation.nii.gz')
7991
distmask_pred = mask_pred.astype(np.int8)
92+
# mark outside zones with -1
8093
distmask_pred[distmask_pred == 0] = -1
8194
distmap_pred *= distmask_pred
8295
else:
@@ -90,6 +103,7 @@ def compute_distances(mask_gt, mask_pred, exclusion_zone, spacing_mm, connectivi
90103
~borders_exclusion, sampling=spacing_mm)
91104

92105
distmask_exclusion = exclusion_zone.astype(np.int8)
106+
# mark outside zones with -1
93107
distmask_exclusion[distmask_exclusion == 0] = -1
94108
distmap_exclusion *= distmask_exclusion
95109

0 commit comments

Comments
 (0)