AdritRao commited on
Commit
ad6e633
1 Parent(s): 9c13d49

Update Comp2Comp-main/comp2comp/aaa/aaa.py

Browse files
Files changed (1) hide show
  1. Comp2Comp-main/comp2comp/aaa/aaa.py +343 -165
Comp2Comp-main/comp2comp/aaa/aaa.py CHANGED
@@ -1,24 +1,46 @@
1
- import math
2
- import operator
3
  import os
4
  import zipfile
5
  from pathlib import Path
6
  from time import time
7
- from tkinter import Tcl
8
  from typing import Union
 
9
 
 
 
 
10
  import cv2
11
- import matplotlib.pyplot as plt
 
 
 
 
 
 
12
  import moviepy.video.io.ImageSequenceClip
13
- import nibabel as nib
14
- import numpy as np
15
  import pandas as pd
16
- import pydicom
17
- import wget
18
- from totalsegmentator.libs import nostdout
19
 
20
- from comp2comp.inference_class_base import InferenceClass
 
 
 
 
 
 
 
 
21
 
 
 
 
 
 
 
 
 
 
 
22
 
23
  class AortaSegmentation(InferenceClass):
24
  """Spine segmentation."""
@@ -42,16 +64,16 @@ class AortaSegmentation(InferenceClass):
42
  self.output_dir_segmentations + "spine.nii.gz",
43
  inference_pipeline.model_dir,
44
  )
45
-
46
  seg = seg.get_fdata()
47
  medical_volume = mv.get_fdata()
48
-
49
  axial_masks = []
50
  ct_image = []
51
 
52
  for i in range(seg.shape[2]):
53
  axial_masks.append(seg[:, :, i])
54
-
55
  for i in range(medical_volume.shape[2]):
56
  ct_image.append(medical_volume[:, :, i])
57
 
@@ -68,13 +90,13 @@ class AortaSegmentation(InferenceClass):
68
 
69
  model_dir = Path(model_dir)
70
  config_dir = model_dir / Path("." + self.model_name)
71
- (config_dir / "nnunet/results/nnUNet/3d_fullres").mkdir(
72
- exist_ok=True, parents=True
73
- )
74
  (config_dir / "nnunet/results/nnUNet/2d").mkdir(exist_ok=True, parents=True)
75
  weights_dir = config_dir / "nnunet/results"
76
  self.weights_dir = weights_dir
77
 
 
 
78
  os.environ["nnUNet_raw_data_base"] = str(
79
  weights_dir
80
  ) # not needed, just needs to be an existing directory
@@ -98,9 +120,7 @@ class AortaSegmentation(InferenceClass):
98
  "https://huggingface.co/AdritRao/aaa_test/resolve/main/fold_0.zip",
99
  out=os.path.join(download_dir, "fold_0.zip"),
100
  )
101
- with zipfile.ZipFile(
102
- os.path.join(download_dir, "fold_0.zip"), "r"
103
- ) as zip_ref:
104
  zip_ref.extractall(download_dir)
105
  os.remove(os.path.join(download_dir, "fold_0.zip"))
106
  wget.download(
@@ -111,9 +131,7 @@ class AortaSegmentation(InferenceClass):
111
  else:
112
  print("Spine model already downloaded.")
113
 
114
- def spine_seg(
115
- self, input_path: Union[str, Path], output_path: Union[str, Path], model_dir
116
- ):
117
  """Run spine segmentation.
118
 
119
  Args:
@@ -133,13 +151,14 @@ class AortaSegmentation(InferenceClass):
133
  trainer = "nnUNetTrainerV2_ep4000_nomirror"
134
  crop_path = None
135
  task_id = [253]
136
-
137
  self.setup_nnunet_c2c(model_dir)
138
  self.download_spine_model(model_dir)
139
 
140
  from totalsegmentator.nnunet import nnUNet_predict_image
141
 
142
  with nostdout():
 
143
  img, seg = nnUNet_predict_image(
144
  input_path,
145
  output_path,
@@ -171,8 +190,8 @@ class AortaSegmentation(InferenceClass):
171
 
172
  return seg, img
173
 
174
-
175
  class AortaDiameter(InferenceClass):
 
176
  def __init__(self):
177
  super().__init__()
178
 
@@ -186,14 +205,11 @@ class AortaDiameter(InferenceClass):
186
  return (img - img.min()) / (img.max() - img.min())
187
 
188
  def __call__(self, inference_pipeline):
189
- axial_masks = (
190
- inference_pipeline.axial_masks
191
- ) # list of 2D numpy arrays of shape (512, 512)
192
- ct_img = (
193
- inference_pipeline.ct_image
194
- ) # 3D numpy array of shape (512, 512, num_axial_slices)
195
-
196
- # image output directory
197
  output_dir = inference_pipeline.output_dir
198
  output_dir_slices = os.path.join(output_dir, "images/slices/")
199
  if not os.path.exists(output_dir_slices):
@@ -205,11 +221,11 @@ class AortaDiameter(InferenceClass):
205
  os.makedirs(output_dir_summary)
206
 
207
  DICOM_PATH = inference_pipeline.dicom_series_path
208
- dicom = pydicom.dcmread(DICOM_PATH + "/" + os.listdir(DICOM_PATH)[0])
209
-
210
- dicom.PhotometricInterpretation = "YBR_FULL"
211
  pixel_conversion = dicom.PixelSpacing
212
- print("Pixel conversion: " + str(pixel_conversion))
213
  RATIO_PIXEL_TO_MM = pixel_conversion[0]
214
 
215
  SLICE_COUNT = dicom["InstanceNumber"].value
@@ -217,9 +233,10 @@ class AortaDiameter(InferenceClass):
217
 
218
  SLICE_COUNT = len(ct_img)
219
  diameterDict = {}
220
-
221
  for i in range(len(ct_img)):
222
- mask = axial_masks[i].astype("uint8")
 
223
 
224
  img = ct_img[i]
225
 
@@ -231,170 +248,331 @@ class AortaDiameter(InferenceClass):
231
  contours, _ = cv2.findContours(mask, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
232
 
233
  if len(contours) != 0:
234
- areas = [cv2.contourArea(c) for c in contours]
235
- sorted_areas = np.sort(areas)
236
 
237
- areas = [cv2.contourArea(c) for c in contours]
238
- sorted_areas = np.sort(areas)
239
- contours = contours[areas.index(sorted_areas[-1])]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
 
241
- img.copy()
242
 
243
- back = img.copy()
244
- cv2.drawContours(back, [contours], 0, (0, 255, 0), -1)
245
 
246
- alpha = 0.25
247
- img = cv2.addWeighted(img, 1 - alpha, back, alpha, 0)
248
 
249
- ellipse = cv2.fitEllipse(contours)
250
- (xc, yc), (d1, d2), angle = ellipse
251
 
252
- cv2.ellipse(img, ellipse, (0, 255, 0), 1)
 
 
253
 
254
- xc, yc = ellipse[0]
255
- cv2.circle(img, (int(xc), int(yc)), 5, (0, 0, 255), -1)
256
 
257
- rmajor = max(d1, d2) / 2
258
- rminor = min(d1, d2) / 2
 
 
 
 
259
 
260
- ### Draw major axes
261
 
262
- if angle > 90:
263
- angle = angle - 90
264
- else:
265
- angle = angle + 90
266
- print(angle)
267
- xtop = xc + math.cos(math.radians(angle)) * rmajor
268
- ytop = yc + math.sin(math.radians(angle)) * rmajor
269
- xbot = xc + math.cos(math.radians(angle + 180)) * rmajor
270
- ybot = yc + math.sin(math.radians(angle + 180)) * rmajor
271
- cv2.line(
272
- img, (int(xtop), int(ytop)), (int(xbot), int(ybot)), (0, 0, 255), 3
273
- )
 
 
 
 
 
 
 
274
 
275
- ### Draw minor axes
 
276
 
277
- if angle > 90:
278
- angle = angle - 90
279
- else:
280
- angle = angle + 90
281
- print(angle)
282
- x1 = xc + math.cos(math.radians(angle)) * rminor
283
- y1 = yc + math.sin(math.radians(angle)) * rminor
284
- x2 = xc + math.cos(math.radians(angle + 180)) * rminor
285
- y2 = yc + math.sin(math.radians(angle + 180)) * rminor
286
- cv2.line(img, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 3)
287
 
288
- # pixel_length = math.sqrt( (x1-x2)**2 + (y1-y2)**2 )
289
- pixel_length = rminor * 2
290
 
291
- print("Pixel_length_minor: " + str(pixel_length))
 
 
 
 
 
292
 
293
- area_px = cv2.contourArea(contours)
294
- area_mm = round(area_px * RATIO_PIXEL_TO_MM)
295
- area_cm = area_mm / 10
296
 
297
- diameter_mm = round((pixel_length) * RATIO_PIXEL_TO_MM)
298
- diameter_cm = diameter_mm / 10
 
 
299
 
300
- diameterDict[(SLICE_COUNT - (i))] = diameter_cm
301
 
302
- img = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE)
 
 
 
303
 
304
- h, w, c = img.shape
305
- lbls = [
306
- "Area (mm): " + str(area_mm) + "mm",
307
- "Area (cm): " + str(area_cm) + "cm",
308
- "Diameter (mm): " + str(diameter_mm) + "mm",
309
- "Diameter (cm): " + str(diameter_cm) + "cm",
310
- "Slice: " + str(SLICE_COUNT - (i)),
311
- ]
312
- font = cv2.FONT_HERSHEY_SIMPLEX
313
 
314
- scale = 0.03
315
- fontScale = min(w, h) / (25 / scale)
 
 
 
 
 
 
316
 
317
- cv2.putText(img, lbls[0], (10, 40), font, fontScale, (0, 255, 0), 2)
318
 
319
- cv2.putText(img, lbls[1], (10, 70), font, fontScale, (0, 255, 0), 2)
 
 
 
320
 
321
- cv2.putText(img, lbls[2], (10, 100), font, fontScale, (0, 255, 0), 2)
322
 
323
- cv2.putText(img, lbls[3], (10, 130), font, fontScale, (0, 255, 0), 2)
 
 
 
324
 
325
- cv2.putText(img, lbls[4], (10, 160), font, fontScale, (0, 255, 0), 2)
326
 
327
- cv2.imwrite(
328
- output_dir_slices + "slice" + str(SLICE_COUNT - (i)) + ".png", img
329
- )
330
 
331
- plt.bar(list(diameterDict.keys()), diameterDict.values(), color="b")
 
 
332
 
333
- plt.title(r"$\bf{Diameter}$" + " " + r"$\bf{Progression}$")
334
 
335
- plt.xlabel("Slice Number")
 
 
 
 
336
 
337
- plt.ylabel("Diameter Measurement (cm)")
338
- plt.savefig(output_dir_summary + "diameter_graph.png", dpi=500)
339
 
340
- print(diameterDict)
341
- print(max(diameterDict.items(), key=operator.itemgetter(1))[0])
342
- print(diameterDict[max(diameterDict.items(), key=operator.itemgetter(1))[0]])
343
 
344
- inference_pipeline.max_diameter = diameterDict[
345
- max(diameterDict.items(), key=operator.itemgetter(1))[0]
346
- ]
347
 
348
- img = ct_img[
349
- SLICE_COUNT - (max(diameterDict.items(), key=operator.itemgetter(1))[0])
350
- ]
351
- img = np.clip(img, -300, 1800)
352
- img = self.normalize_img(img) * 255.0
353
- img = img.reshape((img.shape[0], img.shape[1], 1))
354
- img2 = np.tile(img, (1, 1, 3))
355
- img2 = cv2.rotate(img2, cv2.ROTATE_90_COUNTERCLOCKWISE)
356
 
357
- img1 = cv2.imread(
358
- output_dir_slices
359
- + "slice"
360
- + str(max(diameterDict.items(), key=operator.itemgetter(1))[0])
361
- + ".png"
362
- )
363
 
364
- border_size = 3
365
- img1 = cv2.copyMakeBorder(
366
- img1,
367
- top=border_size,
368
- bottom=border_size,
369
- left=border_size,
370
- right=border_size,
371
- borderType=cv2.BORDER_CONSTANT,
372
- value=[0, 244, 0],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373
  )
374
- img2 = cv2.copyMakeBorder(
375
- img2,
376
- top=border_size,
377
- bottom=border_size,
378
- left=border_size,
379
- right=border_size,
380
- borderType=cv2.BORDER_CONSTANT,
381
- value=[244, 0, 0],
382
  )
383
 
384
- vis = np.concatenate((img2, img1), axis=1)
385
- cv2.imwrite(output_dir_summary + "out.png", vis)
386
-
387
- image_folder = output_dir_slices
388
- fps = 20
389
- image_files = [
390
- os.path.join(image_folder, img)
391
- for img in Tcl().call("lsort", "-dict", os.listdir(image_folder))
392
- if img.endswith(".png")
393
- ]
394
- clip = moviepy.video.io.ImageSequenceClip.ImageSequenceClip(
395
- image_files, fps=fps
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396
  )
397
- clip.write_videofile(output_dir_summary + "aaa.mp4")
398
 
399
  return {}
400
 
@@ -420,5 +598,5 @@ class AortaMetricsSaver(InferenceClass):
420
  """Save results to a CSV file."""
421
  _, filename = os.path.split(self.dicom_series_path)
422
  data = [[filename, str(self.max_diameter)]]
423
- df = pd.DataFrame(data, columns=["Filename", "Max Diameter"])
424
- df.to_csv(os.path.join(self.csv_output_dir, "aorta_metrics.csv"), index=False)
 
 
 
1
  import os
2
  import zipfile
3
  from pathlib import Path
4
  from time import time
 
5
  from typing import Union
6
+ import matplotlib.pyplot as plt
7
 
8
+ import dosma
9
+ import numpy as np
10
+ import wget
11
  import cv2
12
+ import scipy.misc
13
+ from PIL import Image
14
+
15
+ import dicom2nifti
16
+ import math
17
+ import pydicom
18
+ import operator
19
  import moviepy.video.io.ImageSequenceClip
20
+ from tkinter import Tcl
 
21
  import pandas as pd
22
+ import warnings
 
 
23
 
24
+ import numpy as np
25
+ from skimage.morphology import skeletonize_3d
26
+ from scipy.spatial.distance import pdist, squareform
27
+ from scipy.interpolate import splprep, splev
28
+ import nibabel as nib
29
+ from nibabel.processing import resample_to_output
30
+
31
+ import matplotlib.pyplot as plt
32
+ from scipy.interpolate import interp1d
33
 
34
+ from totalsegmentator.libs import (
35
+ download_pretrained_weights,
36
+ nostdout,
37
+ setup_nnunet,
38
+ )
39
+
40
+ from comp2comp.inference_class_base import InferenceClass
41
+ from comp2comp.models.models import Models
42
+ from comp2comp.spine import spine_utils
43
+ import nibabel as nib
44
 
45
  class AortaSegmentation(InferenceClass):
46
  """Spine segmentation."""
 
64
  self.output_dir_segmentations + "spine.nii.gz",
65
  inference_pipeline.model_dir,
66
  )
67
+
68
  seg = seg.get_fdata()
69
  medical_volume = mv.get_fdata()
70
+
71
  axial_masks = []
72
  ct_image = []
73
 
74
  for i in range(seg.shape[2]):
75
  axial_masks.append(seg[:, :, i])
76
+
77
  for i in range(medical_volume.shape[2]):
78
  ct_image.append(medical_volume[:, :, i])
79
 
 
90
 
91
  model_dir = Path(model_dir)
92
  config_dir = model_dir / Path("." + self.model_name)
93
+ (config_dir / "nnunet/results/nnUNet/3d_fullres").mkdir(exist_ok=True, parents=True)
 
 
94
  (config_dir / "nnunet/results/nnUNet/2d").mkdir(exist_ok=True, parents=True)
95
  weights_dir = config_dir / "nnunet/results"
96
  self.weights_dir = weights_dir
97
 
98
+
99
+
100
  os.environ["nnUNet_raw_data_base"] = str(
101
  weights_dir
102
  ) # not needed, just needs to be an existing directory
 
120
  "https://huggingface.co/AdritRao/aaa_test/resolve/main/fold_0.zip",
121
  out=os.path.join(download_dir, "fold_0.zip"),
122
  )
123
+ with zipfile.ZipFile(os.path.join(download_dir, "fold_0.zip"), "r") as zip_ref:
 
 
124
  zip_ref.extractall(download_dir)
125
  os.remove(os.path.join(download_dir, "fold_0.zip"))
126
  wget.download(
 
131
  else:
132
  print("Spine model already downloaded.")
133
 
134
+ def spine_seg(self, input_path: Union[str, Path], output_path: Union[str, Path], model_dir):
 
 
135
  """Run spine segmentation.
136
 
137
  Args:
 
151
  trainer = "nnUNetTrainerV2_ep4000_nomirror"
152
  crop_path = None
153
  task_id = [253]
154
+
155
  self.setup_nnunet_c2c(model_dir)
156
  self.download_spine_model(model_dir)
157
 
158
  from totalsegmentator.nnunet import nnUNet_predict_image
159
 
160
  with nostdout():
161
+
162
  img, seg = nnUNet_predict_image(
163
  input_path,
164
  output_path,
 
190
 
191
  return seg, img
192
 
 
193
  class AortaDiameter(InferenceClass):
194
+
195
  def __init__(self):
196
  super().__init__()
197
 
 
205
  return (img - img.min()) / (img.max() - img.min())
206
 
207
  def __call__(self, inference_pipeline):
208
+
209
+ axial_masks = inference_pipeline.axial_masks # list of 2D numpy arrays of shape (512, 512)
210
+ ct_img = inference_pipeline.ct_image # 3D numpy array of shape (512, 512, num_axial_slices)
211
+
212
+ # image output directory
 
 
 
213
  output_dir = inference_pipeline.output_dir
214
  output_dir_slices = os.path.join(output_dir, "images/slices/")
215
  if not os.path.exists(output_dir_slices):
 
221
  os.makedirs(output_dir_summary)
222
 
223
  DICOM_PATH = inference_pipeline.dicom_series_path
224
+ dicom = pydicom.dcmread(DICOM_PATH+"/"+os.listdir(DICOM_PATH)[0])
225
+
226
+ dicom.PhotometricInterpretation = 'YBR_FULL'
227
  pixel_conversion = dicom.PixelSpacing
228
+ print("Pixel conversion: "+str(pixel_conversion))
229
  RATIO_PIXEL_TO_MM = pixel_conversion[0]
230
 
231
  SLICE_COUNT = dicom["InstanceNumber"].value
 
233
 
234
  SLICE_COUNT = len(ct_img)
235
  diameterDict = {}
236
+
237
  for i in range(len(ct_img)):
238
+
239
+ mask = axial_masks[i].astype('uint8')
240
 
241
  img = ct_img[i]
242
 
 
248
  contours, _ = cv2.findContours(mask, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
249
 
250
  if len(contours) != 0:
 
 
251
 
252
+ areas = [cv2.contourArea(c) for c in contours]
253
+ sorted_areas = np.sort(areas)
254
+
255
+ contours = contours[areas.index(sorted_areas[-1])]
256
+
257
+ overlay = img.copy()
258
+
259
+ back = img.copy()
260
+ cv2.drawContours(back, [contours], 0, (0,255,0), -1)
261
+
262
+ alpha = 0.25
263
+ img = cv2.addWeighted(img, 1-alpha, back, alpha, 0)
264
+
265
+ ellipse = cv2.fitEllipse(contours)
266
+ (xc,yc),(d1,d2),angle = ellipse
267
+
268
+ cv2.ellipse(img, ellipse, (0, 255, 0), 1)
269
+
270
+ xc, yc = ellipse[0]
271
+ cv2.circle(img, (int(xc),int(yc)), 5, (0, 0, 255), -1)
272
+
273
+ rmajor = max(d1,d2)/2
274
+ rminor = min(d1,d2)/2
275
+
276
+ ### Draw major axes
277
+
278
+ if angle > 90:
279
+ angle = angle - 90
280
+ else:
281
+ angle = angle + 90
282
+ print(angle)
283
+ xtop = xc + math.cos(math.radians(angle))*rmajor
284
+ ytop = yc + math.sin(math.radians(angle))*rmajor
285
+ xbot = xc + math.cos(math.radians(angle+180))*rmajor
286
+ ybot = yc + math.sin(math.radians(angle+180))*rmajor
287
+ cv2.line(img, (int(xtop),int(ytop)), (int(xbot),int(ybot)), (0, 0, 255), 3)
288
+
289
+ ### Draw minor axes
290
+
291
+ if angle > 90:
292
+ angle = angle - 90
293
+ else:
294
+ angle = angle + 90
295
+ print(angle)
296
+ x1 = xc + math.cos(math.radians(angle))*rminor
297
+ y1 = yc + math.sin(math.radians(angle))*rminor
298
+ x2 = xc + math.cos(math.radians(angle+180))*rminor
299
+ y2 = yc + math.sin(math.radians(angle+180))*rminor
300
+ cv2.line(img, (int(x1),int(y1)), (int(x2),int(y2)), (255, 0, 0), 3)
301
+
302
+ # pixel_length = math.sqrt( (x1-x2)**2 + (y1-y2)**2 )
303
+ pixel_length = rminor*2
304
+
305
+ print("Pixel_length_minor: "+str(pixel_length))
306
+
307
+ area_px = cv2.contourArea(contours)
308
+ area_mm = round(area_px*RATIO_PIXEL_TO_MM)
309
+ area_cm = area_mm/10
310
+
311
+ diameter_mm = round((pixel_length)*RATIO_PIXEL_TO_MM)
312
+ diameter_cm = diameter_mm/10
313
+
314
+ diameterDict[(SLICE_COUNT-(i))] = diameter_cm
315
+
316
+ img = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE)
317
+
318
+ h,w,c = img.shape
319
+ lbls = ["Area (mm): "+str(area_mm)+"mm", "Area (cm): "+str(area_cm)+"cm", "Diameter (mm): "+str(diameter_mm)+"mm", "Diameter (cm): "+str(diameter_cm)+"cm", "Slice: "+str(SLICE_COUNT-(i))]
320
+ offset = 0
321
+ font = cv2.FONT_HERSHEY_SIMPLEX
322
+
323
+ scale = 0.03
324
+ fontScale = min(w,h)/(25/scale)
325
+
326
+ cv2.putText(img, lbls[0], (10, 40), font, fontScale, (0, 255, 0), 2)
327
+
328
+ cv2.putText(img, lbls[1], (10, 70), font, fontScale, (0, 255, 0), 2)
329
+
330
+ cv2.putText(img, lbls[2], (10, 100), font, fontScale, (0, 255, 0), 2)
331
+
332
+ cv2.putText(img, lbls[3], (10, 130), font, fontScale, (0, 255, 0), 2)
333
+
334
+ cv2.putText(img, lbls[4], (10, 160), font, fontScale, (0, 255, 0), 2)
335
+
336
+ cv2.imwrite(output_dir_slices+"slice"+str(SLICE_COUNT-(i))+".png", img)
337
+
338
+ plt.bar(list(diameterDict.keys()), diameterDict.values(), color='b')
339
 
340
+ plt.title(r"$\bf{Diameter}$" + " " + r"$\bf{Progression}$")
341
 
 
 
342
 
343
+ plt.xlabel('Slice Number')
 
344
 
345
+ plt.ylabel('Diameter Measurement (cm)')
346
+ plt.savefig(output_dir_summary+"diameter_graph.png", dpi=500)
347
 
348
+ print(diameterDict)
349
+ print(max(diameterDict.items(), key=operator.itemgetter(1))[0])
350
+ print(diameterDict[max(diameterDict.items(), key=operator.itemgetter(1))[0]])
351
 
352
+ inference_pipeline.max_diameter = diameterDict[max(diameterDict.items(), key=operator.itemgetter(1))[0]]
 
353
 
354
+ img = ct_img[SLICE_COUNT-(max(diameterDict.items(), key=operator.itemgetter(1))[0])]
355
+ img = np.clip(img, -300, 1800)
356
+ img = self.normalize_img(img) * 255.0
357
+ img = img.reshape((img.shape[0], img.shape[1], 1))
358
+ img2 = np.tile(img, (1, 1, 3))
359
+ img2 = cv2.rotate(img2, cv2.ROTATE_90_COUNTERCLOCKWISE)
360
 
361
+ img1 = cv2.imread(output_dir_slices+'slice'+str(max(diameterDict.items(), key=operator.itemgetter(1))[0])+'.png')
362
 
363
+ border_size = 3
364
+ img1 = cv2.copyMakeBorder(
365
+ img1,
366
+ top=border_size,
367
+ bottom=border_size,
368
+ left=border_size,
369
+ right=border_size,
370
+ borderType=cv2.BORDER_CONSTANT,
371
+ value=[0, 244, 0]
372
+ )
373
+ img2 = cv2.copyMakeBorder(
374
+ img2,
375
+ top=border_size,
376
+ bottom=border_size,
377
+ left=border_size,
378
+ right=border_size,
379
+ borderType=cv2.BORDER_CONSTANT,
380
+ value=[244, 0, 0]
381
+ )
382
 
383
+ vis = np.concatenate((img2, img1), axis=1)
384
+ cv2.imwrite(output_dir_summary+'out.png', vis)
385
 
386
+ image_folder=output_dir_slices
387
+ fps=20
388
+ image_files = [os.path.join(image_folder,img)
389
+ for img in Tcl().call('lsort', '-dict', os.listdir(image_folder))
390
+ if img.endswith(".png")]
391
+ clip = moviepy.video.io.ImageSequenceClip.ImageSequenceClip(image_files, fps=fps)
392
+ clip.write_videofile(output_dir_summary+'aaa.mp4')
 
 
 
393
 
 
 
394
 
395
+ def compute_centerline_3d(aorta_segmentation):
396
+ skeleton = skeletonize_3d(aorta_segmentation)
397
+ z, y, x = np.where(skeleton)
398
+ centerline_points = np.vstack((x, y, z)).T
399
+ centerline_points = centerline_points[centerline_points[:, 0].argsort()]
400
+ return centerline_points
401
 
 
 
 
402
 
403
+ def fit_bspline(centerline_points, smoothness=1e8):
404
+ x, y, z = centerline_points.T
405
+ tck, _ = splprep([x, y, z], s=smoothness)
406
+ return tck
407
 
 
408
 
409
+ def evaluate_bspline(tck, num_points=1000):
410
+ u = np.linspace(0, 1, num_points)
411
+ x, y, z = splev(u, tck)
412
+ return np.vstack((x, y, z)).T
413
 
 
 
 
 
 
 
 
 
 
414
 
415
+ def interpolate_points(data, num_points=32):
416
+ x = data[:, 0]
417
+ y = data[:, 1:]
418
+ f_y = interp1d(x, y, kind="nearest", fill_value="extrapolate", axis=0)
419
+ new_x = np.arange(0, num_points)
420
+ new_y = f_y(new_x)
421
+ new_data = np.round(np.hstack((new_x.reshape(-1, 1), new_y)))
422
+ return new_data
423
 
 
424
 
425
+ def compute_orthogonal_planes(tck, num_points=100):
426
+ u = np.linspace(0, 1, num_points)
427
+ points = np.vstack(splev(u, tck)).T
428
+ tangents = np.vstack(splev(u, tck, der=1)).T
429
 
430
+ normals = tangents / np.linalg.norm(tangents, axis=1)[:, np.newaxis]
431
 
432
+ planes = []
433
+ for point, normal in zip(points, normals):
434
+ d = -np.dot(point, normal)
435
+ planes.append((normal, d))
436
 
437
+ return planes
438
 
 
 
 
439
 
440
+ def compute_maximum_diameter(aorta_segmentation, planes):
441
+ z, y, x = np.where(aorta_segmentation)
442
+ aorta_points = np.vstack((x, y, z)).T
443
 
 
444
 
445
+ max_diameters = []
446
+ intersecting_points_list = []
447
+ for normal, d in planes:
448
+ distances = np.dot(aorta_points, normal) + d
449
+ intersecting_points = aorta_points[np.abs(distances) < 0.5]
450
 
451
+ if len(intersecting_points) < 2:
452
+ continue
453
 
454
+ dist_matrix = squareform(pdist(intersecting_points))
455
+ intersecting_points_list.append(intersecting_points)
 
456
 
457
+ max_diameter = np.max(dist_matrix)
458
+ max_diameters.append(max_diameter)
 
459
 
460
+ max_diameter_index = np.argmax(max_diameters)
461
+ max_diameter_in_pixels = max_diameters[max_diameter_index]
462
+ print(f'Maximum Diameter in Pixels: {max_diameter_in_pixels}')
 
 
 
 
 
463
 
464
+ diameter_mm = round((max_diameter_in_pixels)*RATIO_PIXEL_TO_MM)
465
+ print(f'Maximum Diameter in mm: {diameter_mm}')
 
 
 
 
466
 
467
+ max_diameters = np.array(max_diameters) * 0.15
468
+ max_diameter_index = np.argmax(max_diameters)
469
+ max_diameter_normal, max_diameter_point = planes[max_diameter_index]
470
+ max_intersecting_points = intersecting_points_list[max_diameter_index]
471
+ print("max_diameter_normal type:", type(max_diameter_normal))
472
+ print("max_diameter_normal shape:", np.shape(max_diameter_normal))
473
+ print("max_diameter_point type:", type(max_diameter_point))
474
+ print("max_diameter_point shape:", np.shape(max_diameter_point))
475
+
476
+ print("max intersecting points type:", type(max_intersecting_points))
477
+ print("max intersecting points shape:", np.shape(max_intersecting_points))
478
+ print("max intersecting points:", max_intersecting_points)
479
+
480
+ return (
481
+ max_diameters,
482
+ max_diameter_point,
483
+ max_diameter_normal,
484
+ max_intersecting_points,
485
+ )
486
+
487
+
488
+ def plot_2d_planar_reconstruction(
489
+ image,
490
+ segmentation,
491
+ interpolated_points,
492
+ max_diameter_point,
493
+ max_diameter_normal,
494
+ max_intersecting_points,
495
+ ):
496
+ fig, axs = plt.subplots(nrows=2, ncols=1, figsize=(15, 10))
497
+
498
+ sagittal_index = interpolated_points[:, 2].astype(int)
499
+ image_2d = image[sagittal_index, :, range(image.shape[2])]
500
+ seg_2d = segmentation[sagittal_index, :, range(image.shape[2])]
501
+
502
+ # axs[0].imshow(image_2d, cmap="gray")
503
+ # axs[0].imshow(seg_2d, cmap="jet", alpha=0.3)
504
+ axs[0].scatter(
505
+ interpolated_points[:, 1].astype(int),
506
+ interpolated_points[:, 0].astype(int),
507
+ color="red",
508
+ s=1,
509
+ )
510
+ axs[0].plot(
511
+ max_intersecting_points[:, 1].astype(int),
512
+ max_intersecting_points[:, 0].astype(int),
513
+ color="blue",
514
+ )
515
+
516
+ coronal_index = interpolated_points[:, 1].astype(int)
517
+ image_2d = image[:, coronal_index, range(image.shape[2])].T
518
+ seg_2d = segmentation[:, coronal_index, range(image.shape[2])].T
519
+
520
+ # axs[1].imshow(image_2d, cmap="gray")
521
+ # axs[1].imshow(seg_2d, cmap="jet", alpha=0.3)
522
+ axs[1].scatter(
523
+ interpolated_points[:, 2].astype(int),
524
+ interpolated_points[:, 0].astype(int),
525
+ color="red",
526
+ s=1,
527
+ )
528
+ axs[1].plot(
529
+ max_intersecting_points[:, 2].astype(int),
530
+ max_intersecting_points[:, 0].astype(int),
531
+ color="blue",
532
+ )
533
+
534
+ plt.savefig(output_dir_summary+"planar_reconstruction.png")
535
+
536
+ output_dir = inference_pipeline.output_dir_segmentations
537
+
538
+ segmentation = nib.load(
539
+ os.path.join(output_dir, "converted_dcm.nii.gz")
540
  )
541
+ image = nib.load(
542
+ os.path.join(output_dir, "spine.nii.gz")
 
 
 
 
 
 
543
  )
544
 
545
+ image = resample_to_output(image, (1.5, 1.5, 1.5))
546
+ segmentation = resample_to_output(segmentation, (1.5, 1.5, 1.5), order=0)
547
+ image = image.get_fdata()
548
+ segmentation = segmentation.get_fdata()
549
+
550
+ segmentation[segmentation == 42] = 1
551
+
552
+ print(segmentation.shape)
553
+ print(np.unique(segmentation))
554
+ centerline_points = compute_centerline_3d(segmentation)
555
+ print(centerline_points)
556
+ tck = fit_bspline(centerline_points)
557
+ evaluated_points = evaluate_bspline(tck)
558
+ print(evaluated_points)
559
+ interpolated_points = interpolate_points(evaluated_points, image.shape[2])
560
+ print(interpolated_points)
561
+ planes = compute_orthogonal_planes(tck)
562
+ (
563
+ cmax_diameters,
564
+ max_diameter_point,
565
+ max_diameter_normal,
566
+ max_intersecting_points,
567
+ ) = compute_maximum_diameter(segmentation, planes)
568
+ plot_2d_planar_reconstruction(
569
+ image,
570
+ segmentation,
571
+ interpolated_points,
572
+ max_diameter_point,
573
+ max_diameter_normal,
574
+ max_intersecting_points,
575
  )
 
576
 
577
  return {}
578
 
 
598
  """Save results to a CSV file."""
599
  _, filename = os.path.split(self.dicom_series_path)
600
  data = [[filename, str(self.max_diameter)]]
601
+ df = pd.DataFrame(data, columns=['Filename', 'Max Diameter'])
602
+ df.to_csv(os.path.join(self.csv_output_dir, "aorta_metrics.csv"), index=False)