From 5f4e28243e67f3ac5f1a6202b9d8f30dcc6efe35 Mon Sep 17 00:00:00 2001 From: Louis Blankemeier Date: Mon, 25 Sep 2023 11:56:19 -0700 Subject: [PATCH] formatting and autoformat (#112) --- .github/workflows/ci.yaml | 38 --- bin/C2C | 9 +- comp2comp/aaa/aaa.py | 309 ++++++++++-------- .../aortic_calcium_visualization.py | 45 ++- comp2comp/contrast_phase/contrast_inf.py | 37 ++- comp2comp/contrast_phase/contrast_phase.py | 4 +- comp2comp/hip/hip_utils.py | 12 +- comp2comp/io/io.py | 22 +- comp2comp/muscle_adipose_tissue/data.py | 8 +- comp2comp/spine/spine.py | 38 ++- comp2comp/utils/run.py | 4 +- .../visualization/detectron_visualizer.py | 1 - docs/source/conf.py | 8 +- setup.py | 8 +- 14 files changed, 303 insertions(+), 240 deletions(-) delete mode 100644 .github/workflows/ci.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml deleted file mode 100644 index 6ef17a5..0000000 --- a/.github/workflows/ci.yaml +++ /dev/null @@ -1,38 +0,0 @@ -name: CI - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -jobs: - - Linting: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.7", "3.8", "3.9"] - - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - - uses: actions/cache@v2 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip - - - name: Install Dependencies - run: | - python -m pip install --upgrade pip - make dev-lint - - name: Lint with isort, black, docformatter, flake8 - run: | - make lint \ No newline at end of file diff --git a/bin/C2C b/bin/C2C index 9be02b0..3ec9d8f 100755 --- a/bin/C2C +++ b/bin/C2C @@ -5,7 +5,11 @@ import os os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" os.environ["TF_FORCE_GPU_ALLOW_GROWTH"] = "true" -from comp2comp.aortic_calcium import aortic_calcium, aortic_calcium_visualization +from comp2comp.aaa import aaa +from comp2comp.aortic_calcium import ( + aortic_calcium, + aortic_calcium_visualization, +) from comp2comp.contrast_phase.contrast_phase import ContrastPhaseDetection from comp2comp.hip import hip from comp2comp.inference_pipeline import InferencePipeline @@ -18,13 +22,10 @@ from comp2comp.muscle_adipose_tissue import ( muscle_adipose_tissue, muscle_adipose_tissue_visualization, ) -from comp2comp.muscle_adipose_tissue import muscle_adipose_tissue_visualization from comp2comp.spine import spine from comp2comp.utils.orientation import ToCanonical from comp2comp.utils.process import process_2d, process_3d -from comp2comp.aaa import aaa - os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID" ### AAA Pipeline diff --git a/comp2comp/aaa/aaa.py b/comp2comp/aaa/aaa.py index 88b2047..09837e0 100644 --- a/comp2comp/aaa/aaa.py +++ b/comp2comp/aaa/aaa.py @@ -1,36 +1,26 @@ +import math +import operator import os import zipfile from pathlib import Path from time import time +from tkinter import Tcl from typing import Union -import matplotlib.pyplot as plt -import dosma -import numpy as np -import wget import cv2 -import scipy.misc -from PIL import Image - -import dicom2nifti -import math -import pydicom -import operator +import matplotlib.pyplot as plt import moviepy.video.io.ImageSequenceClip -from tkinter import Tcl +import nibabel as nib +import numpy as np import pandas as pd -import warnings - +import pydicom +import wget from totalsegmentator.libs import ( - download_pretrained_weights, nostdout, - setup_nnunet, ) from comp2comp.inference_class_base import InferenceClass -from comp2comp.models.models import Models -from comp2comp.spine import spine_utils -import nibabel as nib + class AortaSegmentation(InferenceClass): """Spine segmentation.""" @@ -54,16 +44,16 @@ def __call__(self, inference_pipeline): self.output_dir_segmentations + "spine.nii.gz", inference_pipeline.model_dir, ) - + seg = seg.get_fdata() medical_volume = mv.get_fdata() - + axial_masks = [] ct_image = [] for i in range(seg.shape[2]): axial_masks.append(seg[:, :, i]) - + for i in range(medical_volume.shape[2]): ct_image.append(medical_volume[:, :, i]) @@ -80,13 +70,13 @@ def setup_nnunet_c2c(self, model_dir: Union[str, Path]): model_dir = Path(model_dir) config_dir = model_dir / Path("." + self.model_name) - (config_dir / "nnunet/results/nnUNet/3d_fullres").mkdir(exist_ok=True, parents=True) + (config_dir / "nnunet/results/nnUNet/3d_fullres").mkdir( + exist_ok=True, parents=True + ) (config_dir / "nnunet/results/nnUNet/2d").mkdir(exist_ok=True, parents=True) weights_dir = config_dir / "nnunet/results" self.weights_dir = weights_dir - - os.environ["nnUNet_raw_data_base"] = str( weights_dir ) # not needed, just needs to be an existing directory @@ -110,7 +100,9 @@ def download_spine_model(self, model_dir: Union[str, Path]): "https://huggingface.co/AdritRao/aaa_test/resolve/main/fold_0.zip", out=os.path.join(download_dir, "fold_0.zip"), ) - with zipfile.ZipFile(os.path.join(download_dir, "fold_0.zip"), "r") as zip_ref: + with zipfile.ZipFile( + os.path.join(download_dir, "fold_0.zip"), "r" + ) as zip_ref: zip_ref.extractall(download_dir) os.remove(os.path.join(download_dir, "fold_0.zip")) wget.download( @@ -121,7 +113,9 @@ def download_spine_model(self, model_dir: Union[str, Path]): else: print("Spine model already downloaded.") - def spine_seg(self, input_path: Union[str, Path], output_path: Union[str, Path], model_dir): + def spine_seg( + self, input_path: Union[str, Path], output_path: Union[str, Path], model_dir + ): """Run spine segmentation. Args: @@ -141,7 +135,7 @@ def spine_seg(self, input_path: Union[str, Path], output_path: Union[str, Path], trainer = "nnUNetTrainerV2_ep4000_nomirror" crop_path = None task_id = [253] - + self.setup_nnunet_c2c(model_dir) self.download_spine_model(model_dir) @@ -180,8 +174,8 @@ def spine_seg(self, input_path: Union[str, Path], output_path: Union[str, Path], return seg, img -class AortaDiameter(InferenceClass): +class AortaDiameter(InferenceClass): def __init__(self): super().__init__() @@ -196,10 +190,14 @@ def normalize_img(self, img: np.ndarray) -> np.ndarray: def __call__(self, inference_pipeline): - axial_masks = inference_pipeline.axial_masks # list of 2D numpy arrays of shape (512, 512) - ct_img = inference_pipeline.ct_image # 3D numpy array of shape (512, 512, num_axial_slices) + axial_masks = ( + inference_pipeline.axial_masks + ) # list of 2D numpy arrays of shape (512, 512) + ct_img = ( + inference_pipeline.ct_image + ) # 3D numpy array of shape (512, 512, num_axial_slices) - # image output directory + # image output directory output_dir = inference_pipeline.output_dir output_dir_slices = os.path.join(output_dir, "images/slices/") if not os.path.exists(output_dir_slices): @@ -211,11 +209,11 @@ def __call__(self, inference_pipeline): os.makedirs(output_dir_summary) DICOM_PATH = inference_pipeline.dicom_series_path - dicom = pydicom.dcmread(DICOM_PATH+"/"+os.listdir(DICOM_PATH)[0]) - - dicom.PhotometricInterpretation = 'YBR_FULL' + dicom = pydicom.dcmread(DICOM_PATH + "/" + os.listdir(DICOM_PATH)[0]) + + dicom.PhotometricInterpretation = "YBR_FULL" pixel_conversion = dicom.PixelSpacing - print("Pixel conversion: "+str(pixel_conversion)) + print("Pixel conversion: " + str(pixel_conversion)) RATIO_PIXEL_TO_MM = pixel_conversion[0] SLICE_COUNT = dicom["InstanceNumber"].value @@ -223,10 +221,10 @@ def __call__(self, inference_pipeline): SLICE_COUNT = len(ct_img) diameterDict = {} - + for i in range(len(ct_img)): - mask = axial_masks[i].astype('uint8') + mask = axial_masks[i].astype("uint8") img = ct_img[i] @@ -239,116 +237,133 @@ def __call__(self, inference_pipeline): if len(contours) != 0: - areas = [cv2.contourArea(c) for c in contours] - sorted_areas = np.sort(areas) - - contours = contours[areas.index(sorted_areas[-1])] - - overlay = img.copy() - - back = img.copy() - cv2.drawContours(back, [contours], 0, (0,255,0), -1) - - alpha = 0.25 - img = cv2.addWeighted(img, 1-alpha, back, alpha, 0) - - ellipse = cv2.fitEllipse(contours) - (xc,yc),(d1,d2),angle = ellipse - - cv2.ellipse(img, ellipse, (0, 255, 0), 1) - - xc, yc = ellipse[0] - cv2.circle(img, (int(xc),int(yc)), 5, (0, 0, 255), -1) - - rmajor = max(d1,d2)/2 - rminor = min(d1,d2)/2 - - ### Draw major axes - - if angle > 90: - angle = angle - 90 - else: - angle = angle + 90 - print(angle) - xtop = xc + math.cos(math.radians(angle))*rmajor - ytop = yc + math.sin(math.radians(angle))*rmajor - xbot = xc + math.cos(math.radians(angle+180))*rmajor - ybot = yc + math.sin(math.radians(angle+180))*rmajor - cv2.line(img, (int(xtop),int(ytop)), (int(xbot),int(ybot)), (0, 0, 255), 3) - - ### Draw minor axes - - if angle > 90: - angle = angle - 90 - else: - angle = angle + 90 - print(angle) - x1 = xc + math.cos(math.radians(angle))*rminor - y1 = yc + math.sin(math.radians(angle))*rminor - x2 = xc + math.cos(math.radians(angle+180))*rminor - y2 = yc + math.sin(math.radians(angle+180))*rminor - cv2.line(img, (int(x1),int(y1)), (int(x2),int(y2)), (255, 0, 0), 3) - - # pixel_length = math.sqrt( (x1-x2)**2 + (y1-y2)**2 ) - pixel_length = rminor*2 - - print("Pixel_length_minor: "+str(pixel_length)) - - area_px = cv2.contourArea(contours) - area_mm = round(area_px*RATIO_PIXEL_TO_MM) - area_cm = area_mm/10 - - diameter_mm = round((pixel_length)*RATIO_PIXEL_TO_MM) - diameter_cm = diameter_mm/10 - - diameterDict[(SLICE_COUNT-(i))] = diameter_cm - - img = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE) - - h,w,c = img.shape - 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))] - offset = 0 - font = cv2.FONT_HERSHEY_SIMPLEX - - scale = 0.03 - fontScale = min(w,h)/(25/scale) - - cv2.putText(img, lbls[0], (10, 40), font, fontScale, (0, 255, 0), 2) - - cv2.putText(img, lbls[1], (10, 70), font, fontScale, (0, 255, 0), 2) - - cv2.putText(img, lbls[2], (10, 100), font, fontScale, (0, 255, 0), 2) - - cv2.putText(img, lbls[3], (10, 130), font, fontScale, (0, 255, 0), 2) - - cv2.putText(img, lbls[4], (10, 160), font, fontScale, (0, 255, 0), 2) - - cv2.imwrite(output_dir_slices+"slice"+str(SLICE_COUNT-(i))+".png", img) - - plt.bar(list(diameterDict.keys()), diameterDict.values(), color='b') + areas = [cv2.contourArea(c) for c in contours] + sorted_areas = np.sort(areas) - plt.title(r"$\bf{Diameter}$" + " " + r"$\bf{Progression}$") + contours = contours[areas.index(sorted_areas[-1])] + img.copy() - plt.xlabel('Slice Number') + back = img.copy() + cv2.drawContours(back, [contours], 0, (0, 255, 0), -1) - plt.ylabel('Diameter Measurement (cm)') - plt.savefig(output_dir_summary+"diameter_graph.png", dpi=500) + alpha = 0.25 + img = cv2.addWeighted(img, 1 - alpha, back, alpha, 0) + + ellipse = cv2.fitEllipse(contours) + (xc, yc), (d1, d2), angle = ellipse + + cv2.ellipse(img, ellipse, (0, 255, 0), 1) + + xc, yc = ellipse[0] + cv2.circle(img, (int(xc), int(yc)), 5, (0, 0, 255), -1) + + rmajor = max(d1, d2) / 2 + rminor = min(d1, d2) / 2 + + ### Draw major axes + + if angle > 90: + angle = angle - 90 + else: + angle = angle + 90 + print(angle) + xtop = xc + math.cos(math.radians(angle)) * rmajor + ytop = yc + math.sin(math.radians(angle)) * rmajor + xbot = xc + math.cos(math.radians(angle + 180)) * rmajor + ybot = yc + math.sin(math.radians(angle + 180)) * rmajor + cv2.line( + img, (int(xtop), int(ytop)), (int(xbot), int(ybot)), (0, 0, 255), 3 + ) + + ### Draw minor axes + + if angle > 90: + angle = angle - 90 + else: + angle = angle + 90 + print(angle) + x1 = xc + math.cos(math.radians(angle)) * rminor + y1 = yc + math.sin(math.radians(angle)) * rminor + x2 = xc + math.cos(math.radians(angle + 180)) * rminor + y2 = yc + math.sin(math.radians(angle + 180)) * rminor + cv2.line(img, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 3) + + # pixel_length = math.sqrt( (x1-x2)**2 + (y1-y2)**2 ) + pixel_length = rminor * 2 + + print("Pixel_length_minor: " + str(pixel_length)) + + area_px = cv2.contourArea(contours) + area_mm = round(area_px * RATIO_PIXEL_TO_MM) + area_cm = area_mm / 10 + + diameter_mm = round((pixel_length) * RATIO_PIXEL_TO_MM) + diameter_cm = diameter_mm / 10 + + diameterDict[(SLICE_COUNT - (i))] = diameter_cm + + img = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE) + + h, w, c = img.shape + 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)), + ] + font = cv2.FONT_HERSHEY_SIMPLEX + + scale = 0.03 + fontScale = min(w, h) / (25 / scale) + + cv2.putText(img, lbls[0], (10, 40), font, fontScale, (0, 255, 0), 2) + + cv2.putText(img, lbls[1], (10, 70), font, fontScale, (0, 255, 0), 2) + + cv2.putText(img, lbls[2], (10, 100), font, fontScale, (0, 255, 0), 2) + + cv2.putText(img, lbls[3], (10, 130), font, fontScale, (0, 255, 0), 2) + + cv2.putText(img, lbls[4], (10, 160), font, fontScale, (0, 255, 0), 2) + + cv2.imwrite( + output_dir_slices + "slice" + str(SLICE_COUNT - (i)) + ".png", img + ) + + plt.bar(list(diameterDict.keys()), diameterDict.values(), color="b") + + plt.title(r"$\bf{Diameter}$" + " " + r"$\bf{Progression}$") + + plt.xlabel("Slice Number") + + plt.ylabel("Diameter Measurement (cm)") + plt.savefig(output_dir_summary + "diameter_graph.png", dpi=500) print(diameterDict) print(max(diameterDict.items(), key=operator.itemgetter(1))[0]) print(diameterDict[max(diameterDict.items(), key=operator.itemgetter(1))[0]]) - inference_pipeline.max_diameter = diameterDict[max(diameterDict.items(), key=operator.itemgetter(1))[0]] + inference_pipeline.max_diameter = diameterDict[ + max(diameterDict.items(), key=operator.itemgetter(1))[0] + ] - img = ct_img[SLICE_COUNT-(max(diameterDict.items(), key=operator.itemgetter(1))[0])] + img = ct_img[ + SLICE_COUNT - (max(diameterDict.items(), key=operator.itemgetter(1))[0]) + ] img = np.clip(img, -300, 1800) img = self.normalize_img(img) * 255.0 img = img.reshape((img.shape[0], img.shape[1], 1)) img2 = np.tile(img, (1, 1, 3)) img2 = cv2.rotate(img2, cv2.ROTATE_90_COUNTERCLOCKWISE) - img1 = cv2.imread(output_dir_slices+'slice'+str(max(diameterDict.items(), key=operator.itemgetter(1))[0])+'.png') + img1 = cv2.imread( + output_dir_slices + + "slice" + + str(max(diameterDict.items(), key=operator.itemgetter(1))[0]) + + ".png" + ) border_size = 3 img1 = cv2.copyMakeBorder( @@ -358,7 +373,7 @@ def __call__(self, inference_pipeline): left=border_size, right=border_size, borderType=cv2.BORDER_CONSTANT, - value=[0, 244, 0] + value=[0, 244, 0], ) img2 = cv2.copyMakeBorder( img2, @@ -367,19 +382,23 @@ def __call__(self, inference_pipeline): left=border_size, right=border_size, borderType=cv2.BORDER_CONSTANT, - value=[244, 0, 0] + value=[244, 0, 0], ) vis = np.concatenate((img2, img1), axis=1) - cv2.imwrite(output_dir_summary+'out.png', vis) - - image_folder=output_dir_slices - fps=20 - image_files = [os.path.join(image_folder,img) - for img in Tcl().call('lsort', '-dict', os.listdir(image_folder)) - if img.endswith(".png")] - clip = moviepy.video.io.ImageSequenceClip.ImageSequenceClip(image_files, fps=fps) - clip.write_videofile(output_dir_summary+'aaa.mp4') + cv2.imwrite(output_dir_summary + "out.png", vis) + + image_folder = output_dir_slices + fps = 20 + image_files = [ + os.path.join(image_folder, img) + for img in Tcl().call("lsort", "-dict", os.listdir(image_folder)) + if img.endswith(".png") + ] + clip = moviepy.video.io.ImageSequenceClip.ImageSequenceClip( + image_files, fps=fps + ) + clip.write_videofile(output_dir_summary + "aaa.mp4") return {} @@ -405,5 +424,5 @@ def save_results(self): """Save results to a CSV file.""" _, filename = os.path.split(self.dicom_series_path) data = [[filename, str(self.max_diameter)]] - df = pd.DataFrame(data, columns=['Filename', 'Max Diameter']) - df.to_csv(os.path.join(self.csv_output_dir, "aorta_metrics.csv"), index=False) \ No newline at end of file + df = pd.DataFrame(data, columns=["Filename", "Max Diameter"]) + df.to_csv(os.path.join(self.csv_output_dir, "aorta_metrics.csv"), index=False) diff --git a/comp2comp/aortic_calcium/aortic_calcium_visualization.py b/comp2comp/aortic_calcium/aortic_calcium_visualization.py index a248a23..c11a427 100644 --- a/comp2comp/aortic_calcium/aortic_calcium_visualization.py +++ b/comp2comp/aortic_calcium/aortic_calcium_visualization.py @@ -29,20 +29,29 @@ def __call__(self, inference_pipeline): metrics = inference_pipeline.metrics - inference_pipeline.csv_output_dir = os.path.join(inference_pipeline.output_dir, "metrics") + inference_pipeline.csv_output_dir = os.path.join( + inference_pipeline.output_dir, "metrics" + ) os.makedirs(inference_pipeline.csv_output_dir, exist_ok=True) with open( - os.path.join(inference_pipeline.csv_output_dir, "aortic_calcification.csv"), "w" + os.path.join(inference_pipeline.csv_output_dir, "aortic_calcification.csv"), + "w", ) as f: f.write("Volume (cm^3),Mean HU,Median HU,Max HU\n") for vol, mean, median, max in zip( - metrics["volume"], metrics["mean_hu"], metrics["median_hu"], metrics["max_hu"] + metrics["volume"], + metrics["mean_hu"], + metrics["median_hu"], + metrics["max_hu"], ): f.write("{},{:.1f},{:.1f},{:.1f}\n".format(vol, mean, median, max)) with open( - os.path.join(inference_pipeline.csv_output_dir, "aortic_calcification_total.csv"), "w" + os.path.join( + inference_pipeline.csv_output_dir, "aortic_calcification_total.csv" + ), + "w", ) as f: f.write("Total number,{}\n".format(metrics["num_calc"])) f.write("Total volume (cm^3),{}\n".format(metrics["volume_total"])) @@ -54,10 +63,17 @@ def __call__(self, inference_pipeline): else: print("Statistics on aortic calcifications:") print("{:<{}}{}".format("Total number:", distance, metrics["num_calc"])) - print("{:<{}}{:.3f}".format("Total volume (cm³):", distance, metrics["volume_total"])) + print( + "{:<{}}{:.3f}".format( + "Total volume (cm³):", distance, metrics["volume_total"] + ) + ) print( "{:<{}}{:.1f}+/-{:.1f}".format( - "Mean HU:", distance, np.mean(metrics["mean_hu"]), np.std(metrics["mean_hu"]) + "Mean HU:", + distance, + np.mean(metrics["mean_hu"]), + np.std(metrics["mean_hu"]), ) ) print( @@ -70,7 +86,10 @@ def __call__(self, inference_pipeline): ) print( "{:<{}}{:.1f}+/-{:.1f}".format( - "Max HU:", distance, np.mean(metrics["max_hu"]), np.std(metrics["max_hu"]) + "Max HU:", + distance, + np.mean(metrics["max_hu"]), + np.std(metrics["max_hu"]), ) ) print( @@ -86,8 +105,16 @@ def __call__(self, inference_pipeline): "Median volume (cm³):", distance, np.median(metrics["volume"]) ) ) - print("{:<{}}{:.3f}".format("Max volume (cm³):", distance, np.max(metrics["volume"]))) - print("{:<{}}{:.3f}".format("Min volume (cm³):", distance, np.min(metrics["volume"]))) + print( + "{:<{}}{:.3f}".format( + "Max volume (cm³):", distance, np.max(metrics["volume"]) + ) + ) + print( + "{:<{}}{:.3f}".format( + "Min volume (cm³):", distance, np.min(metrics["volume"]) + ) + ) print("\n") diff --git a/comp2comp/contrast_phase/contrast_inf.py b/comp2comp/contrast_phase/contrast_inf.py index a062030..4676d24 100644 --- a/comp2comp/contrast_phase/contrast_inf.py +++ b/comp2comp/contrast_phase/contrast_inf.py @@ -184,11 +184,15 @@ def getFeatures(TSArray, scanArray): # Erode vessels to get only the center of the vessels struct2 = np.ones((3, 3, 3)) - aortaMaskEroded = ndi.binary_erosion(aortaMask, structure=struct2).astype(aortaMask.dtype) + aortaMaskEroded = ndi.binary_erosion(aortaMask, structure=struct2).astype( + aortaMask.dtype + ) IVCMaskEroded = ndi.binary_erosion(IVCMask, structure=struct2).astype(IVCMask.dtype) struct3 = np.ones((1, 1, 1)) - portalMaskEroded = ndi.binary_erosion(portalMask, structure=struct3).astype(portalMask.dtype) + portalMaskEroded = ndi.binary_erosion(portalMask, structure=struct3).astype( + portalMask.dtype + ) # If portalMaskEroded has less then 500 values, use the original portalMask if np.count_nonzero(portalMaskEroded) < 500: portalMaskEroded = portalMask @@ -208,7 +212,9 @@ def getFeatures(TSArray, scanArray): kidneyLHull = kidneyLHull * (kidneyLMask == 0) # erode the kidneyHull to remove the edges struct = np.ones((3, 3, 3)) - kidneyLHull = ndi.binary_erosion(kidneyLHull, structure=struct).astype(kidneyLHull.dtype) + kidneyLHull = ndi.binary_erosion(kidneyLHull, structure=struct).astype( + kidneyLHull.dtype + ) # keep the values of the scanArray that are in the Left Convex Hull pelvisLArray = keep_masked_values(scanArray, kidneyLHull) @@ -218,7 +224,9 @@ def getFeatures(TSArray, scanArray): kidneyRHull = kidneyRHull * (kidneyRMask == 0) # erode the kidneyHull to remove the edges struct = np.ones((3, 3, 3)) - kidneyRHull = ndi.binary_erosion(kidneyRHull, structure=struct).astype(kidneyRHull.dtype) + kidneyRHull = ndi.binary_erosion(kidneyRHull, structure=struct).astype( + kidneyRHull.dtype + ) # keep the values of the scanArray that are in the Right Convex Hull pelvisRArray = keep_masked_values(scanArray, kidneyRHull) @@ -315,7 +323,14 @@ def getFeatures(TSArray, scanArray): ) # Add the stats for the IVCArray to the list stats.extend( - [IVC_max_val, IVC_min_val, IVC_mean_val, IVC_median_val, IVC_std_val, IVC_variance_val] + [ + IVC_max_val, + IVC_min_val, + IVC_mean_val, + IVC_median_val, + IVC_std_val, + IVC_variance_val, + ] ) # Add the stats for the portalArray to the list stats.extend( @@ -434,10 +449,18 @@ def predict_phase(TS_path, scan_path, outputPath=None, save_sample=False): parser.add_argument("--TS_path", type=str, required=True, help="Input image") parser.add_argument("--scan_path", type=str, required=True, help="Input image") parser.add_argument( - "--output_dir", type=str, required=False, help="Output .txt prediction", default=None + "--output_dir", + type=str, + required=False, + help="Output .txt prediction", + default=None, ) parser.add_argument( - "--save_sample", type=bool, required=False, help="Save jpeg sample ", default=False + "--save_sample", + type=bool, + required=False, + help="Save jpeg sample ", + default=False, ) args = parser.parse_args() predict_phase(args.TS_path, args.scan_path, args.output_dir, args.save_sample) diff --git a/comp2comp/contrast_phase/contrast_phase.py b/comp2comp/contrast_phase/contrast_phase.py index fcd660e..1b505d6 100644 --- a/comp2comp/contrast_phase/contrast_phase.py +++ b/comp2comp/contrast_phase/contrast_phase.py @@ -35,7 +35,9 @@ def __call__(self, inference_pipeline): # segArray, imgArray = self.convertNibToNumpy(seg, img) - imgNiftiPath = os.path.join(self.output_dir_segmentations, "converted_dcm.nii.gz") + imgNiftiPath = os.path.join( + self.output_dir_segmentations, "converted_dcm.nii.gz" + ) segNiftPath = os.path.join(self.output_dir_segmentations, "s01.nii.gz") predict_phase(segNiftPath, imgNiftiPath, outputPath=self.output_dir) diff --git a/comp2comp/hip/hip_utils.py b/comp2comp/hip/hip_utils.py index 60a776d..005d85a 100644 --- a/comp2comp/hip/hip_utils.py +++ b/comp2comp/hip/hip_utils.py @@ -41,11 +41,7 @@ def compute_rois(medical_volume, segmentation, model, output_dir, save=False): ) = get_femural_head_roi( right_femur_mask, medical_volume, output_dir, "right_intertrochanter" ) - ( - left_neck_roi, - left_neck_centroid, - left_neck_hu, - ) = get_femural_neck_roi( + (left_neck_roi, left_neck_centroid, left_neck_hu,) = get_femural_neck_roi( left_femur_mask, medical_volume, left_intertrochanter_roi, @@ -54,11 +50,7 @@ def compute_rois(medical_volume, segmentation, model, output_dir, save=False): left_head_centroid, output_dir, ) - ( - right_neck_roi, - right_neck_centroid, - right_neck_hu, - ) = get_femural_neck_roi( + (right_neck_roi, right_neck_centroid, right_neck_hu,) = get_femural_neck_roi( right_femur_mask, medical_volume, right_intertrochanter_roi, diff --git a/comp2comp/io/io.py b/comp2comp/io/io.py index 0055e21..f555dd8 100644 --- a/comp2comp/io/io.py +++ b/comp2comp/io/io.py @@ -22,7 +22,9 @@ def __init__(self, input_path: Union[str, Path]): self.dr = dm.DicomReader() def __call__(self, inference_pipeline) -> Dict: - medical_volume = self.dr.load(self.dicom_dir, group_by=None, sort_by="InstanceNumber")[0] + medical_volume = self.dr.load( + self.dicom_dir, group_by=None, sort_by="InstanceNumber" + )[0] return {"medical_volume": medical_volume} @@ -34,7 +36,9 @@ def __init__(self): # self.output_dir = Path(output_path) self.nw = dm.NiftiWriter() - def __call__(self, inference_pipeline, medical_volume: dm.MedicalVolume) -> Dict[str, Path]: + def __call__( + self, inference_pipeline, medical_volume: dm.MedicalVolume + ) -> Dict[str, Path]: nifti_file = inference_pipeline.output_dir self.nw.write(medical_volume, nifti_file) return {"nifti_file": nifti_file} @@ -73,7 +77,9 @@ def __init__(self, input_path: Union[str, Path], save=True): def __call__(self, inference_pipeline): if os.path.exists( - os.path.join(inference_pipeline.output_dir, "segmentations", "converted_dcm.nii.gz") + os.path.join( + inference_pipeline.output_dir, "segmentations", "converted_dcm.nii.gz" + ) ): return {} if hasattr(inference_pipeline, "medical_volume"): @@ -86,17 +92,21 @@ def __call__(self, inference_pipeline): if self.input_path.is_dir(): dicom_series_to_nifti( self.input_path, - output_file=os.path.join(segmentations_output_dir, "converted_dcm.nii.gz"), + output_file=os.path.join( + segmentations_output_dir, "converted_dcm.nii.gz" + ), reorient_nifti=False, ) inference_pipeline.dicom_series_path = str(self.input_path) elif str(self.input_path).endswith(".nii"): shutil.copy( - self.input_path, os.path.join(segmentations_output_dir, "converted_dcm.nii") + self.input_path, + os.path.join(segmentations_output_dir, "converted_dcm.nii"), ) elif str(self.input_path).endswith(".nii.gz"): shutil.copy( - self.input_path, os.path.join(segmentations_output_dir, "converted_dcm.nii.gz") + self.input_path, + os.path.join(segmentations_output_dir, "converted_dcm.nii.gz"), ) return {} diff --git a/comp2comp/muscle_adipose_tissue/data.py b/comp2comp/muscle_adipose_tissue/data.py index a33041a..54843bf 100644 --- a/comp2comp/muscle_adipose_tissue/data.py +++ b/comp2comp/muscle_adipose_tissue/data.py @@ -88,7 +88,9 @@ def __getitem__(self, idx): xs = [(x.pixel_array + int(x.RescaleIntercept)).astype("float32") for x in dcms] - params = [{"spacing": header.PixelSpacing, "image": x} for header, x in zip(dcms, xs)] + params = [ + {"spacing": header.PixelSpacing, "image": x} for header, x in zip(dcms, xs) + ] # Preprocess xs via windowing. xs = np.stack(xs, axis=0) @@ -186,7 +188,9 @@ def predict( """ if num_workers > 0: - enqueuer = OrderedEnqueuer(dataset, use_multiprocessing=use_multiprocessing, shuffle=False) + enqueuer = OrderedEnqueuer( + dataset, use_multiprocessing=use_multiprocessing, shuffle=False + ) enqueuer.start(workers=num_workers, max_queue_size=max_queue_size) output_generator = enqueuer.get() else: diff --git a/comp2comp/spine/spine.py b/comp2comp/spine/spine.py index 28b74e0..047b787 100644 --- a/comp2comp/spine/spine.py +++ b/comp2comp/spine/spine.py @@ -59,7 +59,9 @@ def setup_nnunet_c2c(self, model_dir: Union[str, Path]): model_dir = Path(model_dir) config_dir = model_dir / Path("." + self.model_name) - (config_dir / "nnunet/results/nnUNet/3d_fullres").mkdir(exist_ok=True, parents=True) + (config_dir / "nnunet/results/nnUNet/3d_fullres").mkdir( + exist_ok=True, parents=True + ) (config_dir / "nnunet/results/nnUNet/2d").mkdir(exist_ok=True, parents=True) weights_dir = config_dir / "nnunet/results" self.weights_dir = weights_dir @@ -86,7 +88,9 @@ def download_spine_model(self, model_dir: Union[str, Path]): "https://huggingface.co/louisblankemeier/spine_v1/resolve/main/fold_0.zip", out=os.path.join(download_dir, "fold_0.zip"), ) - with zipfile.ZipFile(os.path.join(download_dir, "fold_0.zip"), "r") as zip_ref: + with zipfile.ZipFile( + os.path.join(download_dir, "fold_0.zip"), "r" + ) as zip_ref: zip_ref.extractall(download_dir) os.remove(os.path.join(download_dir, "fold_0.zip")) wget.download( @@ -97,7 +101,9 @@ def download_spine_model(self, model_dir: Union[str, Path]): else: print("Spine model already downloaded.") - def spine_seg(self, input_path: Union[str, Path], output_path: Union[str, Path], model_dir): + def spine_seg( + self, input_path: Union[str, Path], output_path: Union[str, Path], model_dir + ): """Run spine segmentation. Args: @@ -202,24 +208,34 @@ def __call__(self, inference_pipeline): """ segmentation = inference_pipeline.segmentation segmentation_data = segmentation.get_fdata() - upper_level_index = np.where(segmentation_data == self.upper_level_index)[2].max() - lower_level_index = np.where(segmentation_data == self.lower_level_index)[2].min() + upper_level_index = np.where(segmentation_data == self.upper_level_index)[ + 2 + ].max() + lower_level_index = np.where(segmentation_data == self.lower_level_index)[ + 2 + ].min() segmentation = segmentation.slicer[:, :, lower_level_index:upper_level_index] inference_pipeline.segmentation = segmentation medical_volume = inference_pipeline.medical_volume - medical_volume = medical_volume.slicer[:, :, lower_level_index:upper_level_index] + medical_volume = medical_volume.slicer[ + :, :, lower_level_index:upper_level_index + ] inference_pipeline.medical_volume = medical_volume if self.save: nib.save( segmentation, - os.path.join(inference_pipeline.output_dir, "segmentations", "spine.nii.gz"), + os.path.join( + inference_pipeline.output_dir, "segmentations", "spine.nii.gz" + ), ) nib.save( medical_volume, os.path.join( - inference_pipeline.output_dir, "segmentations", "converted_dcm.nii.gz" + inference_pipeline.output_dir, + "segmentations", + "converted_dcm.nii.gz", ), ) return {} @@ -338,7 +354,9 @@ def __call__(self, inference_pipeline): coronal_image = inference_pipeline.spine_vis_coronal # concatenate these numpy arrays laterally img = np.concatenate((coronal_image, sagittal_image), axis=1) - output_path = os.path.join(inference_pipeline.output_dir, "images", "spine_report") + output_path = os.path.join( + inference_pipeline.output_dir, "images", "spine_report" + ) if self.format == "png": im = Image.fromarray(img) im.save(output_path + ".png") @@ -404,4 +422,4 @@ def generate_panel(self, image_dir: Union[str, Path]): new_im.save(os.path.join(image_dir, "spine_muscle_adipose_tissue_report.png")) im_cor.close() im_sag.close() - new_im.close() \ No newline at end of file + new_im.close() diff --git a/comp2comp/utils/run.py b/comp2comp/utils/run.py index 05c7b8b..f2f6bb8 100644 --- a/comp2comp/utils/run.py +++ b/comp2comp/utils/run.py @@ -108,7 +108,9 @@ def _get_files(depth: int, dir_name: str): output_path = format_output_path(possible_dir) if not exist_ok and os.path.isfile(output_path): logger.info( - "Skipping {} - results exist at {}".format(possible_dir, output_path) + "Skipping {} - results exist at {}".format( + possible_dir, output_path + ) ) continue ret_files.append(possible_dir) diff --git a/comp2comp/visualization/detectron_visualizer.py b/comp2comp/visualization/detectron_visualizer.py index 44ce5ff..d2d3746 100644 --- a/comp2comp/visualization/detectron_visualizer.py +++ b/comp2comp/visualization/detectron_visualizer.py @@ -2,7 +2,6 @@ import colorsys import logging import math -import os from enum import Enum, unique from pathlib import Path diff --git a/docs/source/conf.py b/docs/source/conf.py index b115499..f374c38 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -6,9 +6,9 @@ # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information -project = 'comp2comp' -copyright = '2023, StanfordMIMI' -author = 'StanfordMIMI' +project = "comp2comp" +copyright = "2023, StanfordMIMI" +author = "StanfordMIMI" # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration @@ -37,7 +37,7 @@ bibtex_bibfiles = ["references.bib"] -templates_path = ['_templates'] +templates_path = ["_templates"] exclude_patterns = [] diff --git a/setup.py b/setup.py index e3811b4..18fdbe1 100644 --- a/setup.py +++ b/setup.py @@ -8,9 +8,13 @@ def get_version(): - init_py_path = path.join(path.abspath(path.dirname(__file__)), "comp2comp", "__init__.py") + init_py_path = path.join( + path.abspath(path.dirname(__file__)), "comp2comp", "__init__.py" + ) init_py = open(init_py_path, "r").readlines() - version_line = [line.strip() for line in init_py if line.startswith("__version__")][0] + version_line = [line.strip() for line in init_py if line.startswith("__version__")][ + 0 + ] version = version_line.split("=")[-1].strip().strip("'\"") # The following is used to build release packages.