- 
                Notifications
    You must be signed in to change notification settings 
- Fork 3
Sumanth mtc #23
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
base: main
Are you sure you want to change the base?
Sumanth mtc #23
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,183 @@ | ||
| import sys | ||
| import os, girder_client | ||
| import numpy as np | ||
| import pandas as pd | ||
| from tiffslide import TiffSlide | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 
 | ||
| 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("..") | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not needed please remove | ||
|  | ||
|  | ||
| 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") | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please remove extra space end of the file, just leave 1 extra line | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <executable> | ||
| <category>HistomicsTK</category> | ||
| <title>Extract Features</title> | ||
| <description>Extracts a variety of features an annotated whole-slide image</description> | ||
| <version>0.1.0</version> | ||
| <documentation-url>https://github.com/SarderLab/deeplab-WSI</documentation-url> | ||
| <license>Apache 2.0</license> | ||
| <contributor>Sayat Mimar (UFL)</contributor> | ||
| <acknowledgements>This work is part of efforts in digital pathology by the Sarder Lab: University Of Florida.</acknowledgements> | ||
| <parameters> | ||
| <label>IO</label> | ||
| <description>Input/output parameters</description> | ||
| <directory> | ||
| <name>base_dir</name> | ||
| <label>WSI folder</label> | ||
| <description>Select the folder containing the annotated slide to be analyzed</description> | ||
| <channel>input</channel> | ||
| <index>0</index> | ||
| </directory> | ||
| <image> | ||
| <name>input_file</name> | ||
| <label>input_file</label> | ||
| <description>input file</description> | ||
| <channel>input</channel> | ||
| <index>1</index> | ||
| </image> | ||
| <double> | ||
| <name>downsample_factor</name> | ||
| <label>downsample_factor</label> | ||
| <description>downsample_factor</description> | ||
| <default>1.0</default> | ||
| <channel>input</channel> | ||
| <index>2</index> | ||
| </double> | ||
| <file fileExtensions=".xlsx" reference="output"> | ||
| <name>output_filename</name> | ||
| <label>Output Excel Filename</label> | ||
| <description>Select the name and location of the Excel file produced. By default this will be saved in your Private folder.</description> | ||
| <channel>output</channel> | ||
| <index>3</index> | ||
| </file> | ||
| <file> | ||
| <name>sub_compartment_mask</name> | ||
| <label>sub_compartment_mask</label> | ||
| <description>sub_compartment mask</description> | ||
| <channel>input</channel> | ||
| <index>4</index> | ||
| </file> | ||
| </parameters> | ||
| <parameters advanced="true"> | ||
| <label>Deconvolution Thresholds</label> | ||
| <description>Deconvolution Thresholds</description> | ||
| <integer> | ||
| <name>h_threshold</name> | ||
| <label>h-threhsold</label> | ||
| <description>h-threhshold for glomeruli and sclerotic glomeruli</description> | ||
| <longflag>h_threshold</longflag> | ||
| <default>160</default> | ||
| </integer> | ||
| <double> | ||
| <name>saturation_threshold</name> | ||
| <label>Saturation Threshold</label> | ||
| <description>Satruation Threshold for glomeruli and sclerotic glomeruli</description> | ||
| <longflag>saturation_threshold</longflag> | ||
| <default>0.3</default> | ||
| </double> | ||
| <double> | ||
| <name>whitespace_threshold</name> | ||
| <label>Whitespace Threshold</label> | ||
| <description>Whitespace Threshold for tubules</description> | ||
| <longflag>whitespace_threshold</longflag> | ||
| <default>0.88</default> | ||
| </double> | ||
| </parameters> | ||
| <parameters advanced="true"> | ||
| <label>Girder API URL and Key</label> | ||
| <description>A Girder API URL and token for Girder client</description> | ||
| <string> | ||
| <name>girderApiUrl</name> | ||
| <longflag>api-url</longflag> | ||
| <label>Girder API URL</label> | ||
| <description>A Girder API URL (e.g., https://girder.example.com:443/api/v1)</description> | ||
| <default></default> | ||
| </string> | ||
| <string> | ||
| <name>girderToken</name> | ||
| <longflag>token</longflag> | ||
| <label>Girder API Token</label> | ||
| <description>A Girder token</description> | ||
| <default></default> | ||
| </string> | ||
| </parameters> | ||
| </executable> | 
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -4,5 +4,8 @@ | |
| }, | ||
| "FeatureExtraction": { | ||
| "type" : "python" | ||
| }, | ||
| "FeatureExtraction-HG": { | ||
| "type" : "python" | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why we need this?