Skip to content

Commit e962155

Browse files
authored
Nuclei detection should be more stable across regions (#1100)
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. Closes #1002.
1 parent b0b1242 commit e962155

File tree

8 files changed

+200
-160
lines changed

8 files changed

+200
-160
lines changed

histomicstk/cli/NucleiDetection/NucleiDetection.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ def detect_nuclei_with_dask(ts, tile_fgnd_frac_list, it_kwargs, args,
187187
return nuclei_list
188188

189189

190-
def main(args):
190+
def main(args): # noqa
191191

192192
# Flags
193193
invert_image = False
@@ -220,7 +220,7 @@ def main(args):
220220
'tile_size': {'width': args.analysis_tile_size},
221221
'scale': {'magnification': args.analysis_mag},
222222
'tile_overlap': {'x': tile_overlap, 'y': tile_overlap},
223-
'style': {args.style},
223+
'style': args.style,
224224
}
225225

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

235235
#
236236
# Initiate Dask client
@@ -305,6 +305,27 @@ def main(args):
305305
src_mu_lab, src_sigma_lab = compute_reinhard_norm(
306306
args, invert_image=invert_image, default_img_inversion=default_img_inversion)
307307

308+
if src_mu_lab is None:
309+
smallImage, _ = ts.getRegion(
310+
output=dict(maxWidth=4096, maxHeight=4096), resample=False,
311+
region=it_kwargs.get('region', {}),
312+
format=large_image.tilesource.TILE_FORMAT_NUMPY,
313+
frame=args.frame)
314+
if len(smallImage.shape) == 2:
315+
smallImage = np.resize(smallImage, (smallImage.shape[0], smallImage.shape[1], 1))
316+
if smallImage.shape[2] < 3:
317+
if default_img_inversion or invert_image:
318+
smallImage = (np.iinfo(smallImage.dtype).max
319+
if smallImage.dtype.kind == 'u'
320+
else np.max(smallImage)) - smallImage
321+
smallImage = np.repeat(smallImage[:, :, :1], 3, 2)
322+
elif not default_img_inversion and invert_image:
323+
smallImage = (np.iinfo(smallImage.dtype).max
324+
if smallImage.dtype.kind == 'u'
325+
else np.max(smallImage)) - smallImage
326+
smallImage = smallImage[:, :, :3]
327+
src_mu_lab, src_sigma_lab = htk_cnorm.reinhard_stats_rgb(smallImage)
328+
308329
#
309330
# Detect nuclei in parallel using Dask
310331
#

histomicstk/preprocessing/color_normalization/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from .deconvolution_based_normalization import \
77
deconvolution_based_normalization
88
from .reinhard import reinhard
9-
from .reinhard_stats import reinhard_stats
9+
from .reinhard_stats import reinhard_stats, reinhard_stats_rgb
1010

1111
# list out things that are available for public use
1212
__all__ = (
@@ -15,5 +15,6 @@
1515
'background_intensity',
1616
'reinhard',
1717
'reinhard_stats',
18+
'reinhard_stats_rgb',
1819
'deconvolution_based_normalization',
1920
)

histomicstk/preprocessing/color_normalization/reinhard_stats.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,12 @@ def reinhard_stats(
8282
stats = ReinhardStats(Mu, Sigma)
8383

8484
return stats
85+
86+
87+
def reinhard_stats_rgb(rgb_image):
88+
rgb_pixels = np.reshape(rgb_image, (1, rgb_image.shape[0] * rgb_image.shape[1], 3))
89+
90+
Mu, Sigma = color_conversion.lab_mean_std(rgb_pixels)
91+
92+
# build named tuple for output
93+
return collections.namedtuple('ReinhardStats', ['Mu', 'Sigma'])(Mu, Sigma)

0 commit comments

Comments
 (0)