Skip to content

Commit

Permalink
Merge pull request #18 from brown-ccv/feat-binary-maps
Browse files Browse the repository at this point in the history
[Feat] Binary map widget
  • Loading branch information
kmilo9999 authored Nov 28, 2024
2 parents 31e90c0 + 74ad4a2 commit c96dcbe
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 21 deletions.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<img src="images/README/binary_mask_widget.png" alt="bimary mask widget"
width="320" height="150">


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


<img src="images/README/example_binary_mask.png" alt="Example binary mask"
width="320" height="300">



### Data Segmentation
Expand Down
Binary file added images/README/binary_mask_widget.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/README/example_binary_mask.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/README/pipeline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 50 additions & 0 deletions jaworski_cellseg/binary_maps_widget.py
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions jaworski_cellseg/jwski_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
)
Expand All @@ -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",
Expand Down
6 changes: 4 additions & 2 deletions jaworski_cellseg/label_counting_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand Down
18 changes: 2 additions & 16 deletions jaworski_cellseg/pre_process_data_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)

Expand All @@ -61,28 +53,22 @@ 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),
gaussian_factor: float = config.get("gaussian_sigma", 1.0),
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
Expand Down
4 changes: 2 additions & 2 deletions jaworski_cellseg/subregion_selection_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down

0 comments on commit c96dcbe

Please sign in to comment.