Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Salvia changes #3

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added data/input/CHGJ007/gt/CHGJ007_ct_gtvt.nii.gz
Binary file not shown.
Binary file added data/input/CHGJ007/pet/CHGJ007_pt.nii.gz
Binary file not shown.
Binary file added data/input/CHGJ010/gt/CHGJ010_ct_gtvt.nii.gz
Binary file not shown.
Binary file added data/input/CHGJ010/pet/CHGJ010_pt.nii.gz
Binary file not shown.
Binary file added data/input/CHGJ015/gt/CHGJ015_ct_gtvt.nii.gz
Binary file not shown.
Binary file added data/input/CHGJ015/pet/CHGJ015_pt.nii.gz
Binary file not shown.
Binary file not shown.
Binary file added data/predicted/CHGJ015/CHGJ015_image.nii
Binary file not shown.
Binary file added data/predicted/CHGJ015/CHGJ015_predicted.nii
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added src/LFBNet/__pycache__/__init__.cpython-310.pyc
Binary file not shown.
Binary file not shown.
44 changes: 24 additions & 20 deletions src/LFBNet/data_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,26 +146,30 @@ def get_nii_files_path(data_directory: str) -> List[ndarray]:
for path in list(nii_paths):
# get the base name: means the file name
identifier_base_name = str(os.path.basename(path)).split('.')[0]
if "pet_sagittal" == str(identifier_base_name):
pet_saggital = np.asanyarray(nib.load(path).dataobj)
pet_saggital = np.expand_dims(pet_saggital, axis=0)

elif "pet_coronal" == str(identifier_base_name):
pet_coronal = np.asanyarray(nib.load(path).dataobj)
pet_coronal = np.expand_dims(pet_coronal, axis=0)

if "ground_truth_sagittal" == str(identifier_base_name):
gt_saggital = np.asanyarray(nib.load(path).dataobj)
gt_saggital = np.expand_dims(gt_saggital, axis=0)

elif "ground_truth_coronal" == str(identifier_base_name):
gt_coronal = np.asanyarray(nib.load(path).dataobj)
gt_coronal = np.expand_dims(gt_coronal, axis=0)

# concatenate coronal and sagita images
# show
pet = np.concatenate((pet_saggital, pet_coronal), axis=0)
gt = np.concatenate((gt_saggital, gt_coronal), axis=0)
# if "pet_sagittal" == str(identifier_base_name):
# pet_saggital = np.asanyarray(nib.load(path).dataobj)
# pet_saggital = np.expand_dims(pet_saggital, axis=0)

# elif "pet_coronal" == str(identifier_base_name):
# pet_coronal = np.asanyarray(nib.load(path).dataobj)
# pet_coronal = np.expand_dims(pet_coronal, axis=0)

# if "ground_truth_sagittal" == str(identifier_base_name):
# gt_saggital = np.asanyarray(nib.load(path).dataobj)
# gt_saggital = np.expand_dims(gt_saggital, axis=0)

# elif "ground_truth_coronal" == str(identifier_base_name):
# gt_coronal = np.asanyarray(nib.load(path).dataobj)
# gt_coronal = np.expand_dims(gt_coronal, axis=0)

if "pet" == str(identifier_base_name):
pet = np.asanyarray(nib.load(path).dataobj)
pet= np.expand_dims(pet, axis=0)

elif "ground_truth" == str(identifier_base_name):
gt = np.asanyarray(nib.load(path).dataobj)
gt = np.expand_dims(gt, axis=0)

return [pet, gt]

@staticmethod
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
6 changes: 3 additions & 3 deletions src/LFBNet/network_architecture/get_conv_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,12 +212,12 @@ class UpConvLayer:
"""

def __init__(self, stage_input: ndarray, num_output_features: int, kernel_size: int = None, strides: int = None,
conv_upsampling: str = '2D'):
conv_upsampling: str = '3D'):########2D

if strides is None:
self.strides = [2, 2]
self.strides = [2, 2,2] #############[2,2]
if kernel_size is None:
self.kernel_size = [2, 2]
self.kernel_size = [2, 2,2] #########[2,2]

if conv_upsampling == "3D":
self.conv_upsampling = Conv3DTranspose
Expand Down
53 changes: 35 additions & 18 deletions src/LFBNet/network_architecture/lfbnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@
# CUDA_VISIBLE_DEVICES = 1
# os.environ["CUDA_VISIBLE_DEVICES"] = "1"

# local import
# # local import
# from get_conv_blocks import StackedConvLayerABlock, UpConvLayer
# from losses import LossMetric
from src.LFBNet.network_architecture.get_conv_blocks import StackedConvLayerABlock, UpConvLayer
from src.LFBNet.losses.losses import LossMetric


# function to set/configure default parameters for lfbnet.
def get_default_config(dimension: int = 2, dropout_ratio: float = 0.5, non_linear_activation: str = 'elu',
def get_default_config(dimension: int = 3, dropout_ratio: float = 0.5, non_linear_activation: str = 'elu',
batch_norm: bool = True, strides: int = 1, pooling: bool = True, pool_size:int =2, default_skips: bool = True,
kernel_size: int = 3, kernel_initializer: str = 'he_normal', use_bias: bool = False, padding: str = 'same',
num_conv_per_block: int = 2, skip_encoder=None, use_residual: bool = True,
Expand Down Expand Up @@ -126,9 +128,11 @@ def __init__(self, input_image_shape: ndarray = None, num_output_class: int = 1,
skipped_input: skipped values from the encoder and to be connected to the decoder.
num_conv_per_block: a series of consecutive convolution, batch normalization, activation operations.
"""

if input_image_shape is None:
input_image_shape = [128, 256, 1]
input_image_shape = [64, 64, 64,1]
#######[128, 128, 256,1]
##################[128, 256, 1]

self.img_shape = input_image_shape
self.channels_out = num_output_class
Expand All @@ -141,7 +145,9 @@ def __init__(self, input_image_shape: ndarray = None, num_output_class: int = 1,
# add the at last the number of features : base_num_features * latent_dim_input_ratio in feature space
self.latent_dim[-1] = int(base_num_features * latent_dim_input_ratio)

self.optimizer = tf.keras.optimizers.Adam(lr=3e-4)
# self.optimizer = tf.keras.optimizers.Adam(lr=3e-4)
self.optimizer = tf.keras.optimizers.legacy.Adam(learning_rate=3e-4)


# if conv_config is not given: take the default values
if conv_config is None:
Expand Down Expand Up @@ -175,10 +181,12 @@ def __init__(self, input_image_shape: ndarray = None, num_output_class: int = 1,
for stage in range(num_layers):
skipped_input.append(
[int(decoder_input_shape[0] * (2 ** stage)), int(decoder_input_shape[1] * (2 ** stage)),
int(decoder_input_shape[2] * (2 ** stage)), #############I added this
int(base_num_features * (2 ** (num_layers - (1 + stage))))])


# print("skipped_connections setup")
# print(skipped_input)
print("skipped_connections setup")
print(skipped_input)

self.skipped_input = skipped_input

Expand Down Expand Up @@ -232,13 +240,13 @@ def __init__(self, input_image_shape: ndarray = None, num_output_class: int = 1,
encoder_output.insert(1, img_input_latent)
decoder_output = self.forward_decoder([encoder_output[i] for i in range(len(encoder_output))])


"""

"""

# combined model training both encoder and decoder together
self.combine_and_train = Model(inputs=[img_input, img_input_latent], outputs=[decoder_output])

# print('Forward Encoder and decoder network combined summary: \n ')
# self.combine_and_train.summary()

Expand All @@ -258,10 +266,10 @@ def __init__(self, input_image_shape: ndarray = None, num_output_class: int = 1,

self.feedback_latent = Model(inputs=[self.fcn_feedback.input],
outputs=[self.fcn_feedback.get_layer('latent_space_fcn').output])

"""

"""

"""
"""

def define_forward_encoder(self):
""" forward system's encoder model.
Expand Down Expand Up @@ -309,21 +317,20 @@ def define_forward_decoder(self):
# set the two inputs from the two encoders
inputs_forward_encoder = inputs
inputs_feedback_encoder = Input(shape=self.latent_dim, name='input_from_feedback')

# change the input dimension into input tensors
skip_input = []
for skip in range(len(self.skipped_input) - 1):
skip_input.append(
Input(shape=np.asarray(self.skipped_input[skip + 1]), name='input_from_encoder' + str(skip)))

'''
The bottleneck need to do the feedback connection:
To use U-net-based segmentation jump or comment this block
'''

concatenate_encoder_feedback = self.conv_config['merging_strategy'](
[inputs_forward_encoder, inputs_feedback_encoder])

fused_bottle_neck = StackedConvLayerABlock(concatenate_encoder_feedback,
int(self.base_num_features * (2 ** (self.num_layers - 1))),
conv_config=self.conv_config, num_conv_per_block=self.conv_config[
Expand All @@ -339,14 +346,15 @@ def define_forward_decoder(self):

# apply drop out at the bottleneck
fused_bottle_neck = Dropout(self.conv_config['dropout_ratio'])(fused_bottle_neck)

current_up_conv = fused_bottle_neck

for decoder_stage in range(self.num_layers - 1):
# decrease the number of features per block: (self.num_decoder_stage-decoder_stage)
num_output_features = int(self.base_num_features * (2 ** (self.num_layers - (2 + decoder_stage))))
current_up_conv = UpConvLayer(current_up_conv, num_output_features=num_output_features,
conv_upsampling="2D").Up_conv_layer()
conv_upsampling="3D").Up_conv_layer() #####2D

# Need skipp connections:
if self.conv_config['merging_strategy'] is not None:
skipped_ = skip_input[decoder_stage]
Expand All @@ -370,7 +378,6 @@ def define_forward_decoder(self):
current_up_conv = self.conv_config['conv'](self.num_classes, kernel_size=1, activation=activation,
kernel_initializer='he_normal', use_bias=False, padding='same',
name='final_output_layer')(current_up_conv)

skip_input.insert(0, inputs)
skip_input.insert(1, inputs_feedback_encoder)

Expand Down Expand Up @@ -406,7 +413,7 @@ def define_feedback_fcn_network(self):

# up convolution block
current_up_conv = UpConvLayer(current_up_conv, num_output_features=num_output_features,
conv_upsampling="2D").Up_conv_layer()
conv_upsampling="3D").Up_conv_layer() ####2D

# convolution blocks
current_up_conv = StackedConvLayerABlock(current_up_conv, num_output_features, conv_config=self.conv_config,
Expand Down Expand Up @@ -437,5 +444,15 @@ def define_feedback_fcn_network(self):
props = get_default_config()
model = LfbNet()
print("network summary \n")
print(f"{model.combine_and_train.summary()}\n")


print(f"{model.forward_encoder.summary()}\n")

print(f"{model.forward_decoder.summary()}\n")

print(f"{model.feedback_latent.summary()}\n")

print(f"{model.fcn_feedback.summary()}\n")


Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
16 changes: 10 additions & 6 deletions src/LFBNet/preprocessing/preprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ def crop_nii_to_desired_resolution(data: ndarray = None, cropped_resolution: Lis
pass

if cropped_resolution is not None:
cropped_resolution = [128, 128, 256]
cropped_resolution = [64,64,64]#[54,54,54]#[128, 128, 256]

print("\n Initial data size \t Cropped data size ")
print(data.shape, "\t", end=" ")
Expand Down Expand Up @@ -392,7 +392,7 @@ def read_pet_gt_resize_crop_save_as_3d_andor_mip(
data_path: str = None, data_name: str = None, saving_dir: str = None, save_3D: bool = False, crop: bool = True,
output_resolution: List[int] = None, desired_spacing: List[float] = None, generate_mip: bool = False
):
""" Read pet and ground truth images from teh input data path. It also apply resize, and cropping operations.
""" Read pet and ground truth images from the input data path. It also apply resize, and cropping operations.

Args:
data_path: directory to the raw 3D pet and ground truth .nii fies.
Expand All @@ -413,7 +413,7 @@ def read_pet_gt_resize_crop_save_as_3d_andor_mip(
rows, columns, depths = output_resolution
else: # default values
# output resized and cropped image resolution=
output_resolution = [128, 128, 256]
output_resolution = [128, 128, 256]#######################

if data_name is None:
data_name = "unknown_data"
Expand Down Expand Up @@ -465,6 +465,7 @@ def create_directory(directory_to_create: list):

# all patient ids
case_ids = os.listdir(data_path)
# print(f"===== {data_path} case_ids {case_ids}")

if not len(case_ids): # reise exception if the directory is empty
raise Exception("Directory %s is empty" % data_path)
Expand Down Expand Up @@ -606,7 +607,9 @@ def create_directory(directory_to_create: list):
[pet_mip], affine, path_save=saving_dir_mip, identifier=str(image_name),
name=['pet_' + str(naming_)]
)
return saving_dir_mip

###################return saving_dir_mip
return saving_dir_mip if generate_mip else saving_dir_3d


# Read .nii files using itk
Expand All @@ -615,9 +618,10 @@ def create_directory(directory_to_create: list):
# input_path = r"F:\Data\Remarc\REMARC/"
# data_ = "remarc"
#
input_path = r"F:\Data\Vienna\No_ground_truth/"
input_path = 'data/input'#r"F:\Data\Vienna\No_ground_truth/"
data_ = "LNH"
saving_dir_mip = read_pet_gt_resize_crop_save_as_3d_andor_mip(
data_path=input_path, data_name=data_, saving_dir=None, save_3D=True, crop=True,
output_resolution=[128, 128, 256], desired_spacing=None, generate_mip=True
output_resolution=[128, 128, 256], desired_spacing=None, generate_mip=False
#################output_resolution=[128, 128, 256], desired_spacing=None, generate_mip=True
)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added src/run/__pycache__/__init__.cpython-310.pyc
Binary file not shown.
Binary file not shown.
Binary file added src/run/__pycache__/trainer.cpython-310.pyc
Binary file not shown.
23 changes: 17 additions & 6 deletions src/run/trainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@


def default_training_parameters(
num_epochs: int = 5000, batch_size: int = 16, early_stop: int = None, fold_number: int = None,
num_epochs: int = 4, batch_size: int = 1, early_stop: int = None, fold_number: int = None,
model_name_save: List[str] = None, loss: str = None, metric: str = None
) -> dict:
""" Configure default parameters for training.
Expand Down Expand Up @@ -213,7 +213,8 @@ def __init__(
self.latent_dim = self.model.latent_dim
self.h_at_zero_time = np.zeros(
(int(self.config_trainer['batch_size']), int(self.latent_dim[0]), int(self.latent_dim[1]),
int(self.latent_dim[2])), np.float32
int(self.latent_dim[2]), ############i added
int(self.latent_dim[3])), np.float32 ##########
)

@staticmethod
Expand Down Expand Up @@ -261,6 +262,7 @@ def train(self):
# training
if self.task == 'train':
# training

for current_epoch in range(self.config_trainer['num_epochs']):
feedback_loss_dice = []
forward_loss_dice = []
Expand All @@ -280,12 +282,13 @@ def train(self):

batch_input_data, batch_output_data = self.load_dataset(
directory_=self.folder_preprocessed_train, ids_to_read=kk
)
)

assert len(batch_input_data) > 0, "batch of data not loaded correctly"

# shuffle within the batch
index_batch = np.random.permutation(int(batch_input_data.shape[0]))

batch_input_data = batch_input_data[index_batch]
batch_output_data = batch_output_data[index_batch]

Expand All @@ -300,10 +303,13 @@ def train(self):
# Train forward models
if current_epoch % 2 == 0:
# step 1: train the forward network encoder and decoder

loss, dice = self.model.combine_and_train.train_on_batch(
[batch_input, self.h_at_zero_time], [batch_output]
) # self.h_at_zero_time

forward_loss_dice.append([loss, dice])


else:
predicted_decoder = self.model.combine_and_train.predict(
Expand All @@ -325,6 +331,7 @@ def train(self):
[output for output in forward_encoder_output], [batch_output]
)
forward_decoder_loss_dice.append([loss, dice])


forward_loss_dice = np.array(forward_loss_dice)
feedback_loss_dice = np.array(feedback_loss_dice)
Expand All @@ -336,11 +343,13 @@ def train(self):
'Training_forward_system: >%d, '
' fwd_loss = %.3f, fwd_dice=%0.3f, ' % (current_epoch, loss, dice)
)


else:

loss_forward, dice_forward = np.mean(forward_decoder_loss_dice, axis=0)
loss_feedback, dice_feedback = np.mean(feedback_loss_dice, axis=0)

print(
'Training_forward_decoder_and_feedback_system: >%d, '
'fwd_decoder_loss=%03f, '
Expand Down Expand Up @@ -505,7 +514,7 @@ def evaluation(
# latent feedback variable h0
# replace the first number of batches with the number of input images from the first channel
h0_input = np.zeros(
(len(input_image), int(self.latent_dim[0]), int(self.latent_dim[1]), int(self.latent_dim[2])), np.float32
(len(input_image), int(self.latent_dim[0]), int(self.latent_dim[1]), int(self.latent_dim[2]), int(self.latent_dim[3])), np.float32
)

# step 0:
Expand Down Expand Up @@ -554,7 +563,7 @@ def evaluation(
binary.specificity(NetworkTrainer.threshold_image(predicted), NetworkTrainer.threshold_image(ground_truth))
)
# all = np.concatenate((ground_truth, predicted, input_image), axis=0)
# display_image(all)
# NetworkTrainer.display_image(all)

# Sometimes save predictions
if self.save_all:
Expand Down Expand Up @@ -591,6 +600,8 @@ def display_image(im_display: ndarray):
plt.subplot(3, 2, n + 1)
plt.imshow(im) # chart formatting
plt.show()
# temp_file_path = 'temp_plot.png'
# plt.savefig(temp_file_path, bbox_inches='tight', pad_inches=0.5)

@staticmethod
# binary.dc, sen, and specificty works only on binary images
Expand Down
Loading