Skip to content

Commit

Permalink
Documentation and minor bug fixes [skip ci]
Browse files Browse the repository at this point in the history
  • Loading branch information
dbouget committed Feb 16, 2023
1 parent 0a2d120 commit 816133b
Show file tree
Hide file tree
Showing 9 changed files with 94 additions and 46 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ segmentation and standardized reporting", which has been published in [Frontiers
Or
* git clone --single-branch --branch master https://github.com/dbouget/Raidionics-Slicer.git /path/to/folder/.

2.3 Download and install Docker (see Section 3).
2.3 Download and install Docker (see below).

2.3 Load the plugin into 3DSlicer:
∘ All Modules > Extension Wizard.
∘ Developer Tools > Extension Wizard.
∘ Select Extension > point to the folder (second Raidionics) and add it to the path (tick the small box at the bottom).
A restart of 3DSlicer is necessary after the initial launch with the plugin to have the proper Python environment.
:warning: A restart of 3DSlicer is necessary after the initial launch with the plugin to have the proper Python environment.
</details>

<details open>
Expand Down
Binary file modified Raidionics/Raidionics/Resources/Icons/Raidionics-Slicer.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 15 additions & 19 deletions Raidionics/Raidionics/src/RaidionicsLogic.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,6 @@ def thread_doit(self, model_parameters):
try:
self.main_queue_start()
self.logic_target_space = "neuro_diagnosis" if modelTarget == "Neuro" else "mediastinum_diagnosis"
# if model_parameters.json_dict['task'] == 'Diagnosis':
# if model_parameters.json_dict['organ'] == 'Brain':
# SharedResources.getInstance().user_diagnosis_configuration['Default']['task'] = 'neuro_diagnosis'
# elif model_parameters.json_dict['organ'] == 'Mediastinum':
# SharedResources.getInstance().user_diagnosis_configuration['Default']['task'] = 'mediastinum_diagnosis'

self.executeDocker(dockerName, modelName, dataPath, iodict, inputs, outputs, params, widgets)
if not self.abort:
Expand Down Expand Up @@ -368,20 +363,21 @@ def executeDocker(self, dockerName, modelName, dataPath, iodict, inputs, outputs
if iodict[item]["type"] == "volume":
# print(inputs[item])
input_node_name = inputs[item].GetName()
#try:
img = sitk.ReadImage(sitkUtils.GetSlicerITKReadWriteAddress(input_node_name))
input_sequence_type = iodict[item]["sequence_type"]
fileName = 'input_' + input_sequence_type + self.file_extension_docker
# @TODO. hard-coding to improve.
if input_sequence_type == "T1-CE":
fileName = 'input_t1gd' + self.file_extension_docker
inputDict[item] = fileName
input_timestamp_order = iodict[item]["timestamp_order"]
os.makedirs(str(os.path.join(SharedResources.getInstance().data_path, "T" + input_timestamp_order)))
sitk.WriteImage(img, str(os.path.join(SharedResources.getInstance().data_path,
"T" + input_timestamp_order, fileName)))
#except Exception as e:
# print(e.message)
try:
img = sitk.ReadImage(sitkUtils.GetSlicerITKReadWriteAddress(input_node_name))
input_sequence_type = iodict[item]["sequence_type"]
fileName = 'input_' + input_sequence_type + self.file_extension_docker
# @TODO. hard-coding to improve.
if input_sequence_type == "T1-CE":
fileName = 'input_t1gd' + self.file_extension_docker
inputDict[item] = fileName
input_timestamp_order = iodict[item]["timestamp_order"]
os.makedirs(str(os.path.join(SharedResources.getInstance().data_path, "T" + input_timestamp_order)))
sitk.WriteImage(img, str(os.path.join(SharedResources.getInstance().data_path,
"T" + input_timestamp_order, fileName)))
except Exception as e:
print("Issue preparing input volume.")
print(traceback.format_exc())
elif iodict[item]["type"] == "configuration":
generate_backend_config(SharedResources.getInstance().data_path,
iodict, self.logic_target_space, modelName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,7 @@ def populate_local_diagnosis(self):
self.local_diagnosis_selector_combobox.addItem(name, idx + 1)

def on_diagnosis_selection(self, index):
if index < 1 or self.local_diagnosis_selector_combobox.count == 1:
return

jsonIndex = self.local_diagnosis_selector_combobox.itemData(index)
selected_model = self.local_diagnosis_selector_combobox.currentText
selected_diagnosis = self.local_diagnosis_selector_combobox.currentText
if SharedResources.getInstance().global_active_model_update:
dl_req = check_local_diagnosis_for_update(selected_diagnosis)
Expand All @@ -167,11 +164,11 @@ def on_diagnosis_selection(self, index):
diag.exec()

self.diagnosis_model_parameters.destroy()
json_model = self.json_diagnoses[jsonIndex - 1]
json_model = self.find_json_model(selected_model_name=selected_model)
self.diagnosis_model_parameters.create(json_model)

if "briefdescription" in self.json_diagnoses[jsonIndex - 1]:
tip = self.json_diagnoses[jsonIndex - 1]["briefdescription"]
if "briefdescription" in json_model:
tip = json_model["briefdescription"]
tip = tip.rstrip()
self.local_diagnosis_selector_combobox.setToolTip(tip)
else:
Expand All @@ -189,7 +186,7 @@ def on_diagnosis_selection(self, index):
if new_docker_status:
self.diagnosis_available_signal.emit(True)
else:
tip = 'The required Docker image could not be downloaded, maybe because of read/write access rights or because the image is private:\n'
tip = 'The required Docker image could not be downloaded, because of inadequet access rights or missing Docker installation.\n'
tip += ' * Open the command line editor (On Windows, type \'cmd\' in the search bar.)\n'
tip += ' * Copy and execute: docker image pull {}\n'.format(self.model_parameters.dockerImageName)
tip += ' * Wait for the download to be complete, then exit the popup.\n'
Expand Down Expand Up @@ -239,3 +236,11 @@ def on_cloud_diagnosis_download_selected(self):
# if True: #success:
self.populate_local_diagnosis()
self.populate_cloud_diagnosis()

def find_json_model(self, selected_model_name):
json_model = None
for m in self.jsonModels:
if m['name'] == selected_model_name:
json_model = m
break
return json_model
2 changes: 2 additions & 0 deletions Raidionics/Raidionics/src/gui/RaidionicsWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ def setup_user_interactions_widget(self):
self.tasks_tabwidget.addTab(self.base_segmentation_widget, 'Segmentation')
self.base_diagnosis_widget = BaseDiagnosisWidget(self.parent)
self.tasks_tabwidget.addTab(self.base_diagnosis_widget, 'Reporting (RADS)')
self.base_diagnosis_widget.setEnabled(False)
self.base_diagnosis_widget.setToolTip("Currently disabled for maintenance, please use Raidionics in the meantime.")
self.logging_textedit = qt.QTextEdit()
#self.logging_textedit.setEnabled(False)
self.logging_textedit.setReadOnly(True)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,10 @@ def populate_cloud_models(self):
self.cloud_model_download_pushbutton.setEnabled(False)

def on_model_selection(self, index):
# @TODO. Maybe need a flush method to remove old models, in case the users don't know how to do it themselves?
if index < 1 or self.local_model_selector_combobox.count == 1:
return

"""
Updates the model parameters GUI part based on the currently selected model.
The index parameter is currently not used, as the current combobox text is directly retrieved and used.
"""
selected_model = self.local_model_selector_combobox.currentText
# @TODO. Should also check if the files are still on disk, before sending the OK signal?
# @TODO. Should also check for an update of the docker image by comparing sha numbers?
Expand All @@ -157,12 +157,14 @@ def on_model_selection(self, index):
diag.set_model_name(selected_model)
diag.exec()
self.model_parameters.destroy()
jsonIndex = self.local_model_selector_combobox.itemData(index)
json_model = self.jsonModels[jsonIndex - 1]
json_model = self.find_json_model(selected_model_name=selected_model)
if not json_model:
return

self.model_parameters.create(json_model)

if "briefdescription" in self.jsonModels[jsonIndex - 1]:
tip = self.jsonModels[jsonIndex - 1]["briefdescription"]
if "briefdescription" in json_model:
tip = json_model["briefdescription"]
tip = tip.rstrip()
self.local_model_selector_combobox.setToolTip(tip)
else:
Expand All @@ -180,7 +182,7 @@ def on_model_selection(self, index):
if new_docker_status:
self.segmentation_available_signal.emit(True)
else:
tip = 'The required Docker image could not be downloaded, maybe because of read/write access rights or because the image is private:\n'
tip = 'The required Docker image could not be downloaded, because of inadequate access rights or missing Docker installation.\n'
tip += ' * Open the command line editor (On Windows, type \'cmd\' in the search bar.)\n'
tip += ' * Copy and execute: docker image pull {}\n'.format(self.model_parameters.dockerImageName)
tip += ' * Wait for the download to be complete, then exit the popup.\n'
Expand Down Expand Up @@ -276,3 +278,11 @@ def on_model_details_selected(self):
popup.setWindowTitle('Exhaustive description for {}'.format(self.local_model_selector_combobox.currentText))
popup.setText(tip)
x = popup.exec_()

def find_json_model(self, selected_model_name):
json_model = None
for m in self.jsonModels:
if m['name'] == selected_model_name:
json_model = m
break
return json_model
17 changes: 16 additions & 1 deletion Raidionics/Raidionics/src/utils/backend_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,21 @@
from src.utils.resources import SharedResources


def generate_backend_config(input_folder, parameters, logic_target_space, model_name):
def generate_backend_config(input_folder: str, parameters, logic_target_space: str, model_name: str) -> None:
"""
Preparing the configuration file to be used as input by raidionics_rads_lib (processing backend).
Parameters
----------
input_folder: str
Folder to be used as input by the backend.
parameters: ?
Not used anymore
logic_target_space: str
Description of the targeted medical specialty, between neuro and mediastinum for now.
model_name: str
Name of the model to be executed in the backend.
"""
try:
rads_config = configparser.ConfigParser()
rads_config.add_section('Default')
Expand All @@ -19,6 +33,7 @@ def generate_backend_config(input_folder, parameters, logic_target_space, model_
rads_config.set('System', 'model_folder', '/home/ubuntu/resources/models')
rads_config.set('System', 'pipeline_filename', '/home/ubuntu/resources/models/' + model_name + '/pipeline.json')
rads_config.add_section('Runtime')
# @TODO. The backend disregards those parameters after the latest RADS lib update, has to be fixed
rads_config.set('Runtime', 'reconstruction_method',
SharedResources.getInstance().user_configuration['Predictions']['reconstruction_method'])
rads_config.set('Runtime', 'reconstruction_order',
Expand Down
30 changes: 26 additions & 4 deletions Raidionics/Raidionics/src/utils/io_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import csv
import threading
import hashlib
from typing import List
import json
import datetime
import zipfile
Expand All @@ -27,7 +28,15 @@
from src.utils.resources import SharedResources


def get_available_cloud_models_list():
def get_available_cloud_models_list() -> List[List[str]]:
"""
Collects a csv file from Google Drive, summarizing all available models.
Returns
------
List of all available models on the cloud, each expressed as a List[str].
Each model list element corresponds to the following headers: Item,link,dependencies,sum.
"""
cloud_models_list = []
# cloud_models_list_url = 'https://drive.google.com/uc?id=1wVjqpQ7S3xTcNJyV2Sp_hSyKglcxfQLe'
cloud_models_list_url = 'https://drive.google.com/uc?id=1uibFBPBQywX7EGK5G_Oc6CXlDSiOePKF'
Expand Down Expand Up @@ -56,6 +65,19 @@ def download_cloud_model_thread(selected_model):


def download_cloud_model(selected_model):
"""
Legacy, but still needed, model download method (use recursively from within the DownloadWorker thread.
@TODO. Should be removed and the recursive download should just happen inside the worker.
Parameters
----------
selected_model: str
Unique name identifier of the model to be downloaded.
Returns
-------
Boolean to indicate if the download operation succeeded or failed.
"""
model_url = ''
model_dependencies = []
model_checksum = None
Expand Down Expand Up @@ -126,9 +148,10 @@ def download_cloud_model(selected_model):

def check_local_model_for_update(selected_model):
"""
Compares the existing local model with the remote ones, to identify if a new version is available for download,
by checking the checksums.
:param selected_model:
:return:
"""
model_url = ''
model_dependencies = []
Expand Down Expand Up @@ -263,7 +286,6 @@ class DownloadWorker(qt.QObject): #qt.QThread

def __init__(self):
super(qt.QObject, self).__init__()
# self.finished_signal = qt.Signal(bool) #WorkerFinishedSignal() #qt.Signal() #qt.Signal(name='workerFinished')

def onWorkerStart(self, model=None, diagnosis=None, docker_image=None):
try:
Expand Down
4 changes: 1 addition & 3 deletions Raidionics/Raidionics/src/utils/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def set_environment(self):
self.global_active_model_update = False

def __set_runtime_parameters(self):
# Most likely deprecated, as we moved from the seg backend to the rads one!
# Set of variables sent to the docker images as runtime config, manually chosen by the user.
self.user_configuration = configparser.ConfigParser()
self.user_configuration['Predictions'] = {}
Expand All @@ -94,6 +95,3 @@ def __set_runtime_parameters(self):
self.user_diagnosis_configuration['Neuro']['tumor_segmentation_filename'] = ''
self.user_diagnosis_configuration['Neuro']['brain_segmentation_filename'] = ''
self.user_diagnosis_configuration['Neuro']['tumor_type'] = ''
# @TODO. Give the option to the user to decide what to include in the standardized report generation
self.user_diagnosis_configuration['Neuro']['compute_cortical_structures'] = 'True'
self.user_diagnosis_configuration['Neuro']['compute_subcortical_structures'] = 'True'

0 comments on commit 816133b

Please sign in to comment.