Skip to content

Commit

Permalink
Nuclei detection should be more stable across regions
Browse files Browse the repository at this point in the history
This does a better job of detecting the average color of the area being
analyzed to reduce individual tile effects.  It also fixes a potential
infinite loop in a degenerate case.
  • Loading branch information
manthey committed Feb 16, 2024
1 parent b0b1242 commit 4eaec6d
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 156 deletions.
28 changes: 25 additions & 3 deletions histomicstk/cli/NucleiDetection/NucleiDetection.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ def detect_nuclei_with_dask(ts, tile_fgnd_frac_list, it_kwargs, args,
return nuclei_list


def main(args):
def main(args): # noqa

# Flags
invert_image = False
Expand Down Expand Up @@ -220,7 +220,7 @@ def main(args):
'tile_size': {'width': args.analysis_tile_size},
'scale': {'magnification': args.analysis_mag},
'tile_overlap': {'x': tile_overlap, 'y': tile_overlap},
'style': {args.style},
'style': args.style,
}

# retrieve frame
Expand All @@ -230,7 +230,7 @@ def main(args):
msg = 'The given frame value is not an integer'
raise Exception(msg)
else:
it_kwargs['frame'] = args.frame
it_kwargs['frame'] = int(args.frame)

#
# Initiate Dask client
Expand Down Expand Up @@ -305,6 +305,28 @@ def main(args):
src_mu_lab, src_sigma_lab = compute_reinhard_norm(
args, invert_image=invert_image, default_img_inversion=default_img_inversion)

if src_mu_lab is None:
smallImage, _ = ts.getRegion(
output=dict(maxWidth=4096, maxHeight=4096), resample=False,
region=it_kwargs.get('region', {}),
format=large_image.tilesource.TILE_FORMAT_NUMPY,
frame=args.frame)
if len(smallImage.shape) == 2:
smallImage = np.resize(smallImage, (smallImage.shape[0], smallImage.shape[1], 1))
if smallImage.shape[2] < 3:
if default_img_inversion or invert_image:
smallImage = (np.iinfo(smallImage.dtype).max
if smallImage.dtype.kind == 'u'
else np.max(smallImage)) - smallImage
smallImage = np.repeat(smallImage[:, :, :1], 3, 2)
elif not default_img_inversion and invert_image:
smallImage = (np.iinfo(smallImage.dtype).max
if smallImage.dtype.kind == 'u'
else np.max(smallImage)) - smallImage
smallImage = smallImage[:, :, :3]
src_mu_lab, src_sigma_lab = htk_cnorm.reinhard_stats_rgb(smallImage)
print(src_mu_lab, src_sigma_lab)

#
# Detect nuclei in parallel using Dask
#
Expand Down
3 changes: 2 additions & 1 deletion histomicstk/preprocessing/color_normalization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from .deconvolution_based_normalization import \
deconvolution_based_normalization
from .reinhard import reinhard
from .reinhard_stats import reinhard_stats
from .reinhard_stats import reinhard_stats, reinhard_stats_rgb

# list out things that are available for public use
__all__ = (
Expand All @@ -15,5 +15,6 @@
'background_intensity',
'reinhard',
'reinhard_stats',
'reinhard_stats_rgb',
'deconvolution_based_normalization',
)
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,12 @@ def reinhard_stats(
stats = ReinhardStats(Mu, Sigma)

return stats


def reinhard_stats_rgb(rgb_image):
rgb_pixels = np.reshape(rgb_image, (1, rgb_image.shape[0] * rgb_image.shape[1], 3))

Mu, Sigma = color_conversion.lab_mean_std(rgb_pixels)

# build named tuple for output
return collections.namedtuple('ReinhardStats', ['Mu', 'Sigma'])(Mu, Sigma)
Loading

0 comments on commit 4eaec6d

Please sign in to comment.