diff --git a/README.md b/README.md index 345f6c8..6c74e09 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,21 @@ You may pre-process the image with a Gaussian filter and contrast adjustments. D This process creates 2 additional layers (Keep track of these): - Pre-processed Data: An enhanced version of the dataset that sharpens cell borders and brightens the interior of blobs to help identify multiple cells within a single blob. -- Cloud Binary: A binary image representing the region of interest with the highest probability of containing cells. + +### Create binary mask + +To improve the precision of our cell detection efforts within a specific channel, it is essential to create a binary mask. A binary mask is a black-and-white image that defines regions of interest where there is a higher likelihood of locating cells. By combining the segmentation results with this binary mask, we can effectively filter out noise generated during the segmentation process. This approach ensures that the analysis focuses exclusively on potential cells within the specified regions, improving both accuracy and efficiency in cell detection. + + bimary mask widget + + +Use the dropdown menu to select the channel from which you want to create the binary mask. The threshold setting specifies that only pixels with an intensity greater than the defined percentage of the channel's maximum intensity will be included in the mask. This approach helps isolate regions with a higher likelihood of containing cells, enabling a more targeted and precise analysis by focusing on areas of interest while minimizing irrelevant data. Click on the "Create binary Mask" to add the result to the napari viewer + + + Example binary mask + ### Data Segmentation diff --git a/images/README/binary_mask_widget.png b/images/README/binary_mask_widget.png new file mode 100644 index 0000000..a54d52b Binary files /dev/null and b/images/README/binary_mask_widget.png differ diff --git a/images/README/example_binary_mask.png b/images/README/example_binary_mask.png new file mode 100644 index 0000000..3d568b8 Binary files /dev/null and b/images/README/example_binary_mask.png differ diff --git a/images/README/pipeline.png b/images/README/pipeline.png index 44b24f2..ad3525b 100644 Binary files a/images/README/pipeline.png and b/images/README/pipeline.png differ diff --git a/jaworski_cellseg/binary_maps_widget.py b/jaworski_cellseg/binary_maps_widget.py new file mode 100644 index 0000000..3a7c531 --- /dev/null +++ b/jaworski_cellseg/binary_maps_widget.py @@ -0,0 +1,50 @@ +from magicgui import magicgui +import napari +import numpy as np +from typing import Optional +from skimage.morphology import remove_small_objects + + +def create_binary_map_widget( + viewer: napari.Viewer, +): + """ + Widget to create biary map from the multiple channels + in the dataset + """ + + @magicgui( + call_button="Create Binary Mask", + auto_call=False, + channel_name={ + "widget_type": "ComboBox", + "choices": [], + }, + binary_map_threshold={"label": "Threshold", "min": 0.0, "max": 100.0}, + ) + def binary_map_widget( + channel_name: Optional[str] = None, + binary_map_threshold: float = 90.0, + ): + cloud_channel = viewer.layers[channel_name].data + cloud_percentile_float = np.percentile( + cloud_channel.astype(np.float64), binary_map_threshold + ) + + # Create a binary mask using the percentile as a threshold + binary_mask = cloud_channel > cloud_percentile_float + binary_mask = remove_small_objects(binary_mask, min_size=100) + viewer.add_image( + binary_mask, name=f"{channel_name} binary mask perc={binary_map_threshold}" + ) + + def update_channel_names_dropdown(event=None): + channel_layer_names = [] + for layer in viewer.layers: + if "Channel" in layer.name: + channel_layer_names.append(layer.name) + + binary_map_widget.channel_name.choices = channel_layer_names + + viewer.layers.events.inserted.connect(update_channel_names_dropdown) + return binary_map_widget diff --git a/jaworski_cellseg/jwski_widget.py b/jaworski_cellseg/jwski_widget.py index 5ce2efc..02c6f93 100644 --- a/jaworski_cellseg/jwski_widget.py +++ b/jaworski_cellseg/jwski_widget.py @@ -7,6 +7,7 @@ from .label_counting_widget import create_label_counting_widget from .subregion_selection_widget import create_region_selection_widget from .configuration_widget import create_configuration_widget +from .binary_maps_widget import create_binary_map_widget import yaml from pathlib import Path @@ -44,6 +45,7 @@ def init_ui(self): self.pre_process_data_widget = create_pre_process_data_widget( self.viewer, self.inferer_widget, self.current_config ) + self.binary_map_widget = create_binary_map_widget(self.viewer) self.count_widget = create_label_counting_widget( self.viewer, self.physical_sizes ) @@ -56,6 +58,7 @@ def init_ui(self): "config_widget", "data_widget", "pre_process_data_widget", + "binary_map_widget", "count_widget", "region_selection_widget", "inferer_widget", diff --git a/jaworski_cellseg/label_counting_widget.py b/jaworski_cellseg/label_counting_widget.py index e6afb63..1293a1b 100644 --- a/jaworski_cellseg/label_counting_widget.py +++ b/jaworski_cellseg/label_counting_widget.py @@ -86,7 +86,9 @@ def cell_counting_widget( count = len(filtered_labels_regions) cell_counting_widget.total_count.value = str(count) - viewer.add_labels(result_image, name=f"{count} Filtered Labled layer", opacity=1.0) + viewer.add_labels( + result_image, name=f"{count} Filtered Labled layer", opacity=1.0 + ) file_path = cell_counting_widget.save_results.value if file_path: @@ -113,7 +115,7 @@ def update_layer_dropdown(event=None): # Get names of binary masks layers in the viewer binary_masks_layer_names = [ - layer.name for layer in viewer.layers if "cloud binary" in layer.name + layer.name for layer in viewer.layers if "binary mask" in layer.name ] # Update the dropdown choices for label_layer_name cell_counting_widget.binary_layer_name.choices = binary_masks_layer_names diff --git a/jaworski_cellseg/pre_process_data_widget.py b/jaworski_cellseg/pre_process_data_widget.py index 3f811cd..2eea78b 100644 --- a/jaworski_cellseg/pre_process_data_widget.py +++ b/jaworski_cellseg/pre_process_data_widget.py @@ -16,7 +16,6 @@ def pre_process_bio_data( contrast_adjustment, contrast_lower, contrast_upper, - binary_map_threshold, ): try: channel_data = napari_viewer.layers["Channel 0"].data @@ -37,15 +36,8 @@ def pre_process_bio_data( print(f"Applying Gaussian fileter sigma {gaussian_factor}") img_rescale = gaussian_filter(img_rescale, sigma=gaussian_factor) - cloud_channel = napari_viewer.layers["Channel 2"].data - cloud_percentile_float = np.percentile( - cloud_channel.astype(np.float64), binary_map_threshold - ) + return img_rescale - # Create a binary mask using the percentile as a threshold - binary_mask = cloud_channel > cloud_percentile_float - binary_mask = remove_small_objects(binary_mask, min_size=100) - return img_rescale, binary_mask except Exception as e: print(e) @@ -61,7 +53,6 @@ def create_pre_process_data_widget( contrast_adjustment={"label": "Contrast Adjustment (Percentiles)"}, contrast_lower={"label": "min", "min": 0.0, "max": 100.0}, contrast_upper={"label": "max", "min": 0.0, "max": 100.0}, - binary_map_threshold={"label": "Binary Threshold", "min": 0.0, "max": 100.0}, ) def pre_process_data_widget( gaussian_checkbox: bool = config.get("gausianFilter", True), @@ -69,20 +60,15 @@ def pre_process_data_widget( contrast_adjustment: bool = config.get("contrast_adjustment", True), contrast_lower: float = config.get("contrast_min", 5.0), contrast_upper: float = config.get("contrast_max", 95.0), - binary_map_threshold: float = config.get("binary_map_threshold", 90.0), ) -> "napari.types.LabelsData": - result_image, binary_mask = pre_process_bio_data( + result_image = pre_process_bio_data( viewer, gaussian_checkbox, gaussian_factor, contrast_adjustment, contrast_lower, contrast_upper, - binary_map_threshold, - ) - viewer.add_image( - binary_mask, name=f"cloud binary mask perc={binary_map_threshold}" ) viewer.add_image(result_image, name="Pre processed data") ## In case more layers are added, lets set the Tiff layer by default diff --git a/jaworski_cellseg/subregion_selection_widget.py b/jaworski_cellseg/subregion_selection_widget.py index aff2e55..578537d 100644 --- a/jaworski_cellseg/subregion_selection_widget.py +++ b/jaworski_cellseg/subregion_selection_widget.py @@ -45,7 +45,7 @@ def region_selection_widget( modified_data = binary_layer.data * mask_3d - viewer.add_image(modified_data, name="cloud binary region") + viewer.add_image(modified_data, name=f"{shape_layer_name} binary region") # Update dropdown menu options in the `region_selection_widget` to display # available shape and binary layers in the Napari viewer. @@ -56,7 +56,7 @@ def update_binary_layers_dropdown(event=None): for layer in viewer.layers: if isinstance(layer, Shapes): shape_layer_names.append(layer.name) - if "cloud binary" in layer.name: + if "binary mask" in layer.name: binary_layer_names.append(layer.name) # Update the dropdown choices for label_layer_name