From 8aa8524cb5d071df7921375742d533b7a456b659 Mon Sep 17 00:00:00 2001 From: SumanthDevarasetty <74096507+SumanthDevarasetty@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:29:03 -0400 Subject: [PATCH 1/3] Add files via upload Initial Commit --- .../FeatureExtraction-HG.py | 183 ++++++++++++++++++ .../FeatureExtraction-HG.xml | 94 +++++++++ 2 files changed, 277 insertions(+) create mode 100644 multic/cli/FeatureExtraction-HG/FeatureExtraction-HG.py create mode 100644 multic/cli/FeatureExtraction-HG/FeatureExtraction-HG.xml diff --git a/multic/cli/FeatureExtraction-HG/FeatureExtraction-HG.py b/multic/cli/FeatureExtraction-HG/FeatureExtraction-HG.py new file mode 100644 index 0000000..a8634b7 --- /dev/null +++ b/multic/cli/FeatureExtraction-HG/FeatureExtraction-HG.py @@ -0,0 +1,183 @@ +import sys +import os, girder_client +import numpy as np +import pandas as pd +from tiffslide import TiffSlide +from ctk_cli import CLIArgumentParser +import cv2 +import csv +from skimage import measure +from skimage.feature import graycomatrix, graycoprops +from skimage.segmentation import find_boundaries +from skimage.measure import regionprops +from skimage.color import rgb2gray + + +sys.path.append("..") + + +def calculate_color_features(image, compartment_mask): + + feature_values = {} + compartment_names = ['Luminal space compartment', 'PAS compartment', 'Nuclei compartment'] + + for compartment in range(3): # As there are 3 compartments + + compartment_pixels = image[compartment_mask == (compartment + 1)] + + if len(compartment_pixels) > 0: + + # Mean Color + + mean_color = np.mean(compartment_pixels, axis=0) + + for i, channel_value in enumerate(mean_color): + + feature_values[f"Mean {['Red', 'Green', 'Blue'][i]} {compartment_names[compartment]}"] = channel_value + + # Standard Deviation Color + + std_dev_color = np.std(compartment_pixels, axis=0) + + for i, channel_value in enumerate(std_dev_color): + + feature_values[f"Standard Deviation {['Red', 'Green', 'Blue'][i]} {compartment_names[compartment]}"] = channel_value + + else: + + # If compartment has no pixels, set values to zero + + for i in range(3): + + feature_values[f"Mean {['Red', 'Green', 'Blue'][i]} {compartment_names[compartment]}"] = 0.0 + + feature_values[f"Standard Deviation {['Red', 'Green', 'Blue'][i]} {compartment_names[compartment]}"] = 0.0 + + return feature_values + + + +# Texture Features + +def calculate_texture_features(image, compartment_mask): + + feature_values = {} + + texture_feature_names = ['Contrast', 'Homogeneity', 'Correlation', 'Energy'] + + compartment_names = ['Luminal space compartment', 'PAS compartment', 'Nuclei compartment'] + + for compartment in range(3): # As there are 3 compartments + + compartment_pixels = (compartment_mask == compartment).astype(np.uint8) + + compartment_image = cv2.bitwise_and(image, image, mask=compartment_pixels) + + compartment_image_gray = rgb2gray(compartment_image) + + compartment_image_gray_uint = (compartment_image_gray * 255).astype(np.uint8) + + texture_matrix = graycomatrix(compartment_image_gray_uint, [1], [0], levels=256, symmetric=True, normed=True) + + for i, texture_name in enumerate(texture_feature_names): + + texture_feature_value = graycoprops(texture_matrix, texture_name.lower()) + + feature_values[f"{texture_name} {compartment_names[compartment]}"] = texture_feature_value[0][0] + + return feature_values + + + +def calculate_features(image, compartment_mask): + + color_features_dict = calculate_color_features(image, compartment_mask) + + texture_features_dict = calculate_texture_features(image, compartment_mask) + + all_features = { + + #"Distance Transform Features": distance_transform_features_list, + + "Color Features": color_features_dict, + + "Texture Features": texture_features_dict, + + #"Morphological Features": morphological_features_list + + } + + return all_features + + +def main(args): + # Load the compartment mask and rgb image + + compartment_mask = cv2.imread(args.sub_compartment_mask, cv2.IMREAD_GRAYSCALE) + + image = cv2.imread(args.input_file, cv2.IMREAD_COLOR) + + # Assigning custom labels to compartments. For easier calculations + + # 29 - blue = nuclei, + + # 76- red = PAS + + # 149 - green = Luminal space + + compartment_mask[compartment_mask == 29] = 3 + + compartment_mask[compartment_mask == 76] = 2 + + compartment_mask[compartment_mask == 149] = 1 + + compartment_mask[compartment_mask > 3] = 0 + + all_features = calculate_features(image, compartment_mask) + + # Print the color features dictionary + + print(all_features) + + # Write the color features to a CSV file + + csv_filename = args.basedir+"/tmp/features_output.csv" + + os.makedirs(args.basedir+"/tmp/") + + folder = args.basedir + + gc_folder_id = folder.split('/')[-2] + + gc = girder_client.GirderClient(apiUrl = args.girderApiUrl) + + gc.setToken(args.girderToken) + + f = open(csv_filename, 'w') + f.close() + + with open(csv_filename, mode='w', newline='') as csv_file: + + writer = csv.writer(csv_file) + + for feature_name, value in all_features.items(): + + writer.writerow([feature_name, ""]) + + if isinstance(value, dict): + + for sub_feature_name, sub_value in value.items(): + + writer.writerow(["", f"{sub_feature_name}", sub_value]) + + file_name = csv_filename.split('/')[-1] + + print("Uploading to DSA") + gc.uploadFileToFolder(gc_folder_id, csv_filename, name = file_name) + print("Done") + + + + + + diff --git a/multic/cli/FeatureExtraction-HG/FeatureExtraction-HG.xml b/multic/cli/FeatureExtraction-HG/FeatureExtraction-HG.xml new file mode 100644 index 0000000..eee74f3 --- /dev/null +++ b/multic/cli/FeatureExtraction-HG/FeatureExtraction-HG.xml @@ -0,0 +1,94 @@ + + + HistomicsTK + Extract Features + Extracts a variety of features an annotated whole-slide image + 0.1.0 + https://github.com/SarderLab/deeplab-WSI + Apache 2.0 + Sayat Mimar (UFL) + This work is part of efforts in digital pathology by the Sarder Lab: University Of Florida. + + + Input/output parameters + + base_dir + + Select the folder containing the annotated slide to be analyzed + input + 0 + + + input_file + + input file + input + 1 + + + downsample_factor + + downsample_factor + 1.0 + input + 2 + + + output_filename + + Select the name and location of the Excel file produced. By default this will be saved in your Private folder. + output + 3 + + + sub_compartment_mask + + sub_compartment mask + input + 4 + + + + + Deconvolution Thresholds + + h_threshold + + h-threhshold for glomeruli and sclerotic glomeruli + h_threshold + 160 + + + saturation_threshold + + Satruation Threshold for glomeruli and sclerotic glomeruli + saturation_threshold + 0.3 + + + whitespace_threshold + + Whitespace Threshold for tubules + whitespace_threshold + 0.88 + + + + + A Girder API URL and token for Girder client + + girderApiUrl + api-url + + A Girder API URL (e.g., https://girder.example.com:443/api/v1) + + + + girderToken + token + + A Girder token + + + + \ No newline at end of file From 3cc25d86996d2a50f8f063175bd488565cebddcd Mon Sep 17 00:00:00 2001 From: SumanthDevarasetty <74096507+SumanthDevarasetty@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:30:23 -0400 Subject: [PATCH 2/3] Add files via upload updated slicer_cli --- multic/cli/slicer_cli_list.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/multic/cli/slicer_cli_list.json b/multic/cli/slicer_cli_list.json index 1189b00..6822a23 100644 --- a/multic/cli/slicer_cli_list.json +++ b/multic/cli/slicer_cli_list.json @@ -4,5 +4,8 @@ }, "FeatureExtraction": { "type" : "python" + }, + "FeatureExtraction-HG": { + "type" : "python" } } From 5aa5f7e1b760e83e6849ffd41ba29fae53da3a9e Mon Sep 17 00:00:00 2001 From: SumanthDevarasetty <74096507+SumanthDevarasetty@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:31:22 -0400 Subject: [PATCH 3/3] Add files via upload Updated Dockerfile --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index 4ca5afb..34e6df6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -118,6 +118,7 @@ RUN apt-get update && \ COPY . $mc_path/ WORKDIR $mc_path +RUN pip cache purge RUN pip install --no-cache-dir --upgrade --ignore-installed pip setuptools && \ pip install --no-cache-dir . && \ pip install --no-cache-dir tensorboard cmake onnx && \ @@ -134,6 +135,7 @@ WORKDIR $mc_path/multic/cli RUN python -m slicer_cli_web.cli_list_entrypoint --list_cli RUN python -m slicer_cli_web.cli_list_entrypoint MultiCompartmentSegment --help RUN python -m slicer_cli_web.cli_list_entrypoint FeatureExtraction --help +RUN python -m slicer_cli_web.cli_list_entrypoint FeatureExtraction-HG --help ENTRYPOINT ["/bin/bash", "docker-entrypoint.sh"]