From 9dd395fc0a16f561e23bf9c19fa3c6a468f99f19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Pedersen?= Date: Wed, 4 Oct 2023 19:17:04 +0200 Subject: [PATCH] Deprecated old demo solution [no ci] --- AeroPath/__init__.py | 0 AeroPath/gui.py | 171 ------------------------------------------ AeroPath/inference.py | 103 ------------------------- AeroPath/utils.py | 67 ----------------- app.py | 41 ---------- 5 files changed, 382 deletions(-) delete mode 100644 AeroPath/__init__.py delete mode 100644 AeroPath/gui.py delete mode 100644 AeroPath/inference.py delete mode 100644 AeroPath/utils.py delete mode 100644 app.py diff --git a/AeroPath/__init__.py b/AeroPath/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/AeroPath/gui.py b/AeroPath/gui.py deleted file mode 100644 index 4f93af1..0000000 --- a/AeroPath/gui.py +++ /dev/null @@ -1,171 +0,0 @@ -import os - -import gradio as gr - -from .inference import run_model -from .utils import load_ct_to_numpy -from .utils import load_pred_volume_to_numpy -from .utils import nifti_to_glb - - -class WebUI: - def __init__( - self, - model_name: str = None, - cwd: str = "/home/user/app/", - share: int = 1, - ): - # global states - self.images = [] - self.pred_images = [] - - # @TODO: This should be dynamically set based on chosen volume size - self.nb_slider_items = 415 - - self.model_name = model_name - self.cwd = cwd - self.share = share - - self.class_name = "airways" # default - self.class_names = { - "airways": "CT_Airways", - "lungs": "CT_Lungs", - } - - self.result_names = { - "airways": "Airways", - "lungs": "Lungs", - } - - # define widgets not to be rendered immediately, but later on - self.slider = gr.Slider( - 1, - self.nb_slider_items, - value=1, - step=1, - label="Which 2D slice to show", - ) - self.volume_renderer = gr.Model3D( - clear_color=[0.0, 0.0, 0.0, 0.0], - label="3D Model", - visible=True, - elem_id="model-3d", - ).style(height=512) - - def set_class_name(self, value): - print("Changed task to:", value) - self.class_name = value - - def combine_ct_and_seg(self, img, pred): - return (img, [(pred, self.class_name)]) - - def upload_file(self, file): - return file.name - - def process(self, mesh_file_name): - path = mesh_file_name.name - run_model( - path, - model_path=os.path.join(self.cwd, "resources/models/"), - task=self.class_names[self.class_name], - name=self.result_names[self.class_name], - ) - nifti_to_glb("prediction.nii.gz") - - self.images = load_ct_to_numpy(path) - self.pred_images = load_pred_volume_to_numpy("./prediction.nii.gz") - return "./prediction.obj" - - def get_img_pred_pair(self, k): - k = int(k) - 1 - out = [gr.AnnotatedImage.update(visible=False)] * self.nb_slider_items - out[k] = gr.AnnotatedImage.update( - self.combine_ct_and_seg(self.images[k], self.pred_images[k]), - visible=True, - ) - return out - - def run(self): - css = """ - #model-3d { - height: 512px; - } - #model-2d { - height: 512px; - margin: auto; - } - #upload { - height: 120px; - } - """ - with gr.Blocks(css=css) as demo: - with gr.Row(): - file_output = gr.File(file_count="single", elem_id="upload") - file_output.upload(self.upload_file, file_output, file_output) - - model_selector = gr.Dropdown( - list(self.class_names.keys()), - label="Task", - info="Which task to perform - one model for" - "airways and lungs extraction", - multiselect=False, - size="sm", - ) - model_selector.input( - fn=lambda x: self.set_class_name(x), - inputs=model_selector, - outputs=None, - ) - - run_btn = gr.Button("Run analysis").style( - full_width=False, size="lg" - ) - run_btn.click( - fn=lambda x: self.process(x), - inputs=file_output, - outputs=self.volume_renderer, - ) - - with gr.Row(): - gr.Examples( - examples=[ - os.path.join(self.cwd, "test_thorax_CT_ds.nii"), - os.path.join(self.cwd, "test_thorax_CT_ds.nii"), - ], - inputs=file_output, - outputs=file_output, - fn=self.upload_file, - cache_examples=True, - ) - - with gr.Row(): - with gr.Box(): - with gr.Column(): - image_boxes = [] - for i in range(self.nb_slider_items): - visibility = True if i == 1 else False - t = gr.AnnotatedImage( - visible=visibility, elem_id="model-2d" - ).style( - color_map={self.class_name: "#ffae00"}, - height=512, - width=512, - ) - image_boxes.append(t) - - self.slider.input( - self.get_img_pred_pair, self.slider, image_boxes - ) - - self.slider.render() - - with gr.Box(): - self.volume_renderer.render() - - # sharing app publicly -> share=True: - # https://gradio.app/sharing-your-app/ - # inference times > 60 seconds -> need queue(): - # https://github.com/tloen/alpaca-lora/issues/60#issuecomment-1510006062 - demo.queue().launch( - server_name="0.0.0.0", server_port=7860, share=self.share - ) diff --git a/AeroPath/inference.py b/AeroPath/inference.py deleted file mode 100644 index 1395c45..0000000 --- a/AeroPath/inference.py +++ /dev/null @@ -1,103 +0,0 @@ -import configparser -import logging -import os -import shutil -import traceback - - -def run_model( - input_path: str, - model_path: str, - verbose: str = "info", - task: str = "CT_Airways", - name: str = "Airways", -): - logging.basicConfig() - logging.getLogger().setLevel(logging.WARNING) - - if verbose == "debug": - logging.getLogger().setLevel(logging.DEBUG) - elif verbose == "info": - logging.getLogger().setLevel(logging.INFO) - elif verbose == "error": - logging.getLogger().setLevel(logging.ERROR) - else: - raise ValueError("Unsupported verbose value provided:", verbose) - - # delete patient/result folder if they exist - if os.path.exists("./patient/"): - shutil.rmtree("./patient/") - if os.path.exists("./result/"): - shutil.rmtree("./result/") - - patient_directory = '' - output_path = '' - try: - # setup temporary patient directory - filename = input_path.split("/")[-1] - splits = filename.split(".") - extension = ".".join(splits[1:]) - patient_directory = "./patient/" - os.makedirs(patient_directory + "T0/", exist_ok=True) - shutil.copy( - input_path, - patient_directory + "T0/" + splits[0] + "-t1gd." + extension, - ) - - # define output directory to save results - output_path = "./result/prediction-" + splits[0] + "/" - os.makedirs(output_path, exist_ok=True) - - # Setting up the configuration file - rads_config = configparser.ConfigParser() - rads_config.add_section("Default") - rads_config.set("Default", "task", "mediastinum_diagnosis") - rads_config.set("Default", "caller", "") - rads_config.add_section("System") - rads_config.set("System", "gpu_id", "-1") - rads_config.set("System", "input_folder", patient_directory) - rads_config.set("System", "output_folder", output_path) - rads_config.set("System", "model_folder", model_path) - rads_config.set( - "System", - "pipeline_filename", - os.path.join(model_path, task, "pipeline.json"), - ) - rads_config.add_section("Runtime") - rads_config.set( - "Runtime", "reconstruction_method", "thresholding" - ) # thresholding, probabilities - rads_config.set("Runtime", "reconstruction_order", "resample_first") - rads_config.set("Runtime", "use_preprocessed_data", "False") - - with open("rads_config.ini", "w") as f: - rads_config.write(f) - - # finally, run inference - from raidionicsrads.compute import run_rads - - run_rads(config_filename="rads_config.ini") - - # rename and move final result - os.rename( - "./result/prediction-" - + splits[0] - + "/T0/" - + splits[0] - + "-t1gd_annotation-" - + name - + ".nii.gz", - "./prediction.nii.gz", - ) - # Clean-up - if os.path.exists(patient_directory): - shutil.rmtree(patient_directory) - if os.path.exists(output_path): - shutil.rmtree(output_path) - except Exception as e: - print(traceback.format_exc()) - # Clean-up - if os.path.exists(patient_directory): - shutil.rmtree(patient_directory) - if os.path.exists(output_path): - shutil.rmtree(output_path) diff --git a/AeroPath/utils.py b/AeroPath/utils.py deleted file mode 100644 index 69b9c50..0000000 --- a/AeroPath/utils.py +++ /dev/null @@ -1,67 +0,0 @@ -import nibabel as nib -import numpy as np -from nibabel.processing import resample_to_output -from skimage.measure import marching_cubes - - -def load_ct_to_numpy(data_path): - if type(data_path) != str: - data_path = data_path.name - - image = nib.load(data_path) - resampled = resample_to_output(image, None, order=0) - data = resampled.get_fdata() - - data = np.rot90(data, k=1, axes=(0, 1)) - - data[data < -1024] = -1024 - data[data > 1024] = 1024 - - data = data - np.amin(data) - data = data / np.amax(data) * 255 - data = data.astype("uint8") - - print(data.shape) - return [data[..., i] for i in range(data.shape[-1])] - - -def load_pred_volume_to_numpy(data_path): - if type(data_path) != str: - data_path = data_path.name - - image = nib.load(data_path) - resampled = resample_to_output(image, None, order=0) - data = resampled.get_fdata() - - data = np.rot90(data, k=1, axes=(0, 1)) - - data[data > 0] = 1 - data = data.astype("uint8") - - print(data.shape) - return [data[..., i] for i in range(data.shape[-1])] - - -def nifti_to_glb(path, output="prediction.obj"): - # load NIFTI into numpy array - image = nib.load(path) - resampled = resample_to_output(image, [1, 1, 1], order=1) - data = resampled.get_fdata().astype("uint8") - - # extract surface - verts, faces, normals, values = marching_cubes(data, 0) - faces += 1 - - with open(output, "w") as thefile: - for item in verts: - thefile.write("v {0} {1} {2}\n".format(item[0], item[1], item[2])) - - for item in normals: - thefile.write("vn {0} {1} {2}\n".format(item[0], item[1], item[2])) - - for item in faces: - thefile.write( - "f {0}//{0} {1}//{1} {2}//{2}\n".format( - item[0], item[1], item[2] - ) - ) \ No newline at end of file diff --git a/app.py b/app.py deleted file mode 100644 index c14169c..0000000 --- a/app.py +++ /dev/null @@ -1,41 +0,0 @@ -import os -from argparse import ArgumentParser - -from AeroPath.gui import WebUI - - -def main(): - parser = ArgumentParser() - parser.add_argument( - "--cwd", - type=str, - default="/home/user/app/", - help="Set current working directory (path to app.py).", - ) - parser.add_argument( - "--share", - type=int, - default=1, - help="Whether to enable the app to be accessible online" - "-> setups a public link which requires internet access.", - ) - args = parser.parse_args() - - print("Current working directory:", args.cwd) - - if not os.path.exists(args.cwd): - raise ValueError("Chosen 'cwd' is not a valid path!") - if args.share not in [0, 1]: - raise ValueError( - "The 'share' argument can only be set to 0 or 1, but was:", - args.share, - ) - - # initialize and run app - print("Launching demo...") - app = WebUI(cwd=args.cwd, share=args.share) - app.run() - - -if __name__ == "__main__": - main()