Skip to content
Draft
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
3 changes: 3 additions & 0 deletions .github/workflows/test-and-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install .[test] --verbose
- name: Lint with Ruff
run: |
ruff check aicssegmentation
- name: Lint with flake8
run: |
flake8 aicssegmentation --count --verbose --show-source --statistics
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ test: ## run pytest with coverage report
pytest --cov=aicssegmentation --cov-report xml --cov-report term

lint: ## run a lint check / report
ruff check aicssegmentation
flake8 aicssegmentation --count --verbose --show-source --statistics
black --check --exclude vendor aicssegmentation

Expand Down
11 changes: 6 additions & 5 deletions aicssegmentation/bin/batch_processing.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import numpy as np
import os
import sys
import logging
import argparse
import traceback
import importlib
import logging
import os
import pathlib
import sys
import traceback
from glob import glob

import aicsimageio
import numpy as np

###############################################################################
# Global Objects
Expand Down
13 changes: 10 additions & 3 deletions aicssegmentation/core/MO_threshold.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from typing import Tuple, Union

import numpy as np
from skimage.morphology import remove_small_objects, ball, dilation
from skimage.filters import threshold_triangle, threshold_otsu
from skimage.filters import threshold_otsu, threshold_triangle
from skimage.measure import label
from skimage.morphology import ball, dilation, remove_small_objects


def MO_low_level(
Expand Down Expand Up @@ -47,6 +49,11 @@ def MO_low_level(
global_tri = threshold_triangle(structure_img_smooth)
global_median = np.percentile(structure_img_smooth, 50)
th_low_level = (global_tri + global_median) / 2
else:
raise ValueError(
f"Unsupported global_thresh_method {global_thresh_method!r}. "
"Valid options: 'tri', 'triangle', 'med', 'median', 'ave', 'ave_tri_med'."
)

bw_low_level = structure_img_smooth > th_low_level
bw_low_level = remove_small_objects(bw_low_level, min_size=object_minArea, connectivity=1, out=bw_low_level)
Expand Down Expand Up @@ -115,7 +122,7 @@ def MO(
local_adjust: float = 0.98,
return_object: bool = False,
dilate: bool = False,
):
) -> Union[Tuple[np.ndarray, np.ndarray], np.ndarray]:
"""
Implementation of "Masked Object Thresholding" algorithm. Specifically, the
algorithm is a hybrid thresholding method combining two levels of thresholds.
Expand Down
97 changes: 97 additions & 0 deletions aicssegmentation/core/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""aicssegmentation.core — image processing primitives.

Public re-exports allow callers to do::

from aicssegmentation.core import intensity_normalization, filament_3d_wrapper
"""

from .hessian import absolute_3d_hessian_eigenvalues, compute_3d_hessian_matrix
from .MO_threshold import MO, MO_high_level, MO_low_level
from .output_utils import generate_segmentation_contour, output_hook, save_segmentation
from .pre_processing_utils import (
edge_preserving_smoothing_3d,
image_smoothing_gaussian_3d,
image_smoothing_gaussian_slice_by_slice,
intensity_normalization,
suggest_normalization_param,
)
from .seg_dot import dot_2d_slice_by_slice_wrapper, dot_3d_wrapper
from .utils import (
absolute_eigenvaluesh,
cell_local_adaptive_threshold,
divide_nonzero,
get_3dseed_from_mid_frame,
get_middle_frame,
get_seed_for_objects,
histogram_otsu,
hole_filling,
peak_local_max_wrapper,
prune_z_slices,
remove_hot_pixel,
remove_index_object,
segmentation_intersection,
segmentation_union,
segmentation_xor,
size_filter,
sortbyabs,
topology_preserving_thinning,
watershed_wrapper,
)
from .vessel import (
filament_2d_wrapper,
filament_3d_wrapper,
vesselness2D,
vesselness2D_single_slice,
vesselness3D,
vesselnessSliceBySlice,
)

__all__ = [
# hessian
"absolute_3d_hessian_eigenvalues",
"compute_3d_hessian_matrix",
# MO threshold
"MO",
"MO_high_level",
"MO_low_level",
# output
"generate_segmentation_contour",
"output_hook",
"save_segmentation",
# pre-processing
"edge_preserving_smoothing_3d",
"image_smoothing_gaussian_3d",
"image_smoothing_gaussian_slice_by_slice",
"intensity_normalization",
"suggest_normalization_param",
# spot / dot filters
"dot_2d_slice_by_slice_wrapper",
"dot_3d_wrapper",
# utilities
"absolute_eigenvaluesh",
"cell_local_adaptive_threshold",
"divide_nonzero",
"get_3dseed_from_mid_frame",
"get_middle_frame",
"get_seed_for_objects",
"histogram_otsu",
"hole_filling",
"peak_local_max_wrapper",
"prune_z_slices",
"remove_hot_pixel",
"remove_index_object",
"segmentation_intersection",
"segmentation_union",
"segmentation_xor",
"size_filter",
"sortbyabs",
"topology_preserving_thinning",
"watershed_wrapper",
# vessel / filament filters
"filament_2d_wrapper",
"filament_3d_wrapper",
"vesselness2D",
"vesselness2D_single_slice",
"vesselness3D",
"vesselnessSliceBySlice",
]
12 changes: 6 additions & 6 deletions aicssegmentation/core/hessian.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from itertools import combinations_with_replacement
from typing import List

import numpy as np
from scipy import ndimage as ndi
Expand All @@ -21,11 +22,11 @@ def compute_3d_hessian_matrix(
nd_array: np.ndarray
nd array from which to compute the hessian matrix.
sigma: float
Standard deviation used for the Gaussian kernel to smooth the array. Defaul is 1
Standard deviation used for the Gaussian kernel to smooth the array. Default is 1
scale: bool
whether the hessian elements will be scaled by sigma squared. Default is True
whiteonblack: boolean
image is white objects on black blackground or not. Default is True
image is white objects on black background or not. Default is True


Return:
Expand Down Expand Up @@ -54,7 +55,6 @@ def compute_3d_hessian_matrix(

# create hessian matrix from hessian elements
hessian_full = [[()] * ndim for x in range(ndim)]
# hessian_full = [[None] * ndim] * ndim

for index, (ax0, ax1) in enumerate(combinations_with_replacement(range(ndim), 2)):
element = hessian_elements[index]
Expand All @@ -76,7 +76,7 @@ def absolute_3d_hessian_eigenvalues(
sigma: float = 1,
scale: bool = True,
whiteonblack: bool = True,
):
) -> List[np.ndarray]:
"""
Eigenvalues of the hessian matrix calculated from the input array sorted by
absolute value.
Expand All @@ -86,11 +86,11 @@ def absolute_3d_hessian_eigenvalues(
nd_array: np.ndarray
nd array from which to compute the hessian matrix.
sigma: float
Standard deviation used for the Gaussian kernel to smooth the array. Defaul is 1
Standard deviation used for the Gaussian kernel to smooth the array. Default is 1
scale: bool
whether the hessian elements will be scaled by sigma squared. Default is True
whiteonblack: boolean
image is white objects on black blackground or not. Default is True
image is white objects on black background or not. Default is True

Return:
------------
Expand Down
19 changes: 14 additions & 5 deletions aicssegmentation/core/output_utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from pathlib import Path
from typing import Sequence

import numpy as np
from skimage.morphology import erosion, ball
from aicsimageio.writers import OmeTiffWriter
from skimage.morphology import ball, erosion


def save_segmentation(
Expand All @@ -10,7 +12,7 @@ def save_segmentation(
output_path: Path,
fn: str,
suffix: str = "_struct_segmentation",
):
) -> None:
"""save the segmentation into a tiff file

Parameters:
Expand All @@ -37,7 +39,7 @@ def save_segmentation(
OmeTiffWriter.save(data=bd, uri=out_fn, dim_order="ZYX")


def generate_segmentation_contour(im):
def generate_segmentation_contour(im: np.ndarray) -> np.ndarray:
"""generate the contour of the segmentation"""

bd = np.logical_xor(erosion(im > 0, footprint=ball(1)), im > 0)
Expand All @@ -48,9 +50,16 @@ def generate_segmentation_contour(im):
return bd


def output_hook(im, names, out_flag, output_path, fn):
def output_hook(
im: Sequence[np.ndarray],
names: Sequence[str],
out_flag: Sequence[bool],
output_path: Path,
fn: str,
) -> None:
"""general hook for cutomized output"""
assert len(im) == len(names) and len(names) == len(out_flag)
if not (len(im) == len(names) == len(out_flag)):
raise ValueError("im, names, and out_flag must have the same length")

for i in range(len(out_flag)):
if out_flag[i]:
Expand Down
Loading
Loading