Skip to content

Commit efa451b

Browse files
authored
Merge pull request #315 from bear-rsg/automated-thresholding-suggestions
Automated thresholding suggestions
2 parents 5224322 + 5109322 commit efa451b

File tree

1 file changed

+50
-21
lines changed

1 file changed

+50
-21
lines changed

episodes/07-thresholding.md

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -348,14 +348,28 @@ plt.xlim(0, 1.0)
348348

349349
![](fig/maize-root-cluster-histogram.png){alt='Grayscale histogram of the maize root image'}
350350

351-
The histogram has a significant peak around 0.2, and a second,
352-
smaller peak very near 1.0.
351+
The histogram has a significant peak around 0.2 and then a broader "hill" around 0.6 followed by a
352+
smaller peak near 1.0. Looking at the grayscale image, we can identify the peak at 0.2 with the
353+
background and the broader peak with the foreground.
353354
Thus, this image is a good candidate for thresholding with Otsu's method.
354355
The mathematical details of how this works are complicated (see
355356
[the scikit-image documentation](https://scikit-image.org/docs/dev/api/skimage.filters.html#threshold-otsu)
356357
if you are interested),
357-
but the outcome is that Otsu's method finds a threshold value between
358-
the two peaks of a grayscale histogram.
358+
but the outcome is that Otsu's method finds a threshold value between the two peaks of a grayscale
359+
histogram which might correspond well to the foreground and background depending on the data and
360+
application.
361+
362+
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: instructor
363+
364+
The histogram of the maize root image may prompt questions from learners about the interpretation
365+
of the peaks and the broader region around 0.6. The focus here is on the separation of background
366+
and foreground pixel values. We note that Otsu's method does not work well
367+
for the image with the shapes used earlier in this episode, as the foreground pixel values are more
368+
distributed. These examples could be augmented with a discussion of unimodal, bimodal, and multimodal
369+
histograms. While these points can lead to fruitful considerations, the text in this episode attempts
370+
to reduce cognitive load and deliberately simplifies the discussion.
371+
372+
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
359373

360374
The `ski.filters.threshold_otsu()` function can be used to determine
361375
the threshold automatically via Otsu's method.
@@ -463,10 +477,10 @@ def measure_root_mass(filename, sigma=1.0):
463477
binary_mask = blurred_image > t
464478

465479
# determine root mass ratio
466-
rootPixels = np.count_nonzero(binary_mask)
480+
root_pixels = np.count_nonzero(binary_mask)
467481
w = binary_mask.shape[1]
468482
h = binary_mask.shape[0]
469-
density = rootPixels / (w * h)
483+
density = root_pixels / (w * h)
470484

471485
return density
472486
```
@@ -613,9 +627,8 @@ and label from the image before applying Otsu's method.
613627
## Solution
614628

615629
We can apply a simple binary thresholding with a threshold
616-
`t=0.95` to remove the label and circle from the image. We use the
617-
binary mask to set the pixels in the blurred image to zero
618-
(black).
630+
`t=0.95` to remove the label and circle from the image. We can then use the
631+
binary mask to calculate the Otsu threshold without the pixels from the label and circle.
619632

620633
```python
621634
def enhanced_root_mass(filename, sigma):
@@ -628,21 +641,22 @@ def enhanced_root_mass(filename, sigma):
628641

629642
# perform binary thresholding to mask the white label and circle
630643
binary_mask = blurred_image < 0.95
631-
# use the mask to remove the circle and label from the blurred image
632-
blurred_image[~binary_mask] = 0
633-
634-
# perform automatic thresholding to produce a binary image
635-
t = ski.filters.threshold_otsu(blurred_image)
636-
binary_mask = blurred_image > t
644+
645+
# perform automatic thresholding using only the pixels with value True in the binary mask
646+
t = ski.filters.threshold_otsu(blurred_image[binary_mask])
647+
648+
# update binary mask to identify pixels which are both less than 0.95 and greater than t
649+
binary_mask = (blurred_image < 0.95) & (blurred_image > t)
637650

638651
# determine root mass ratio
639-
rootPixels = np.count_nonzero(binary_mask)
652+
root_pixels = np.count_nonzero(binary_mask)
640653
w = binary_mask.shape[1]
641654
h = binary_mask.shape[0]
642-
density = rootPixels / (w * h)
655+
density = root_pixels / (w * h)
643656

644657
return density
645658

659+
646660
all_files = glob.glob("data/trial-*.jpg")
647661
for filename in all_files:
648662
density = enhanced_root_mass(filename=filename, sigma=1.5)
@@ -654,11 +668,26 @@ The output of the improved program does illustrate that the white circles
654668
and labels were skewing our root mass ratios:
655669

656670
```output
657-
data/trial-016.jpg,0.045935837765957444
658-
data/trial-020.jpg,0.058800033244680854
659-
data/trial-216.jpg,0.13705003324468085
660-
data/trial-293.jpg,0.13164461436170213
671+
data/trial-016.jpg,0.046250166223404256
672+
data/trial-020.jpg,0.05886968085106383
673+
data/trial-216.jpg,0.13712117686170214
674+
data/trial-293.jpg,0.13190342420212767
661675
```
676+
:::::::::::::::::::::::::::::::::::::::::: spoiler
677+
678+
### What is `&` doing in the example above?
679+
680+
The `&` operator above means that we have defined a logical AND statement. This combines the two tests of pixel intensities in the blurred image such that both must be true for a pixel's position to be set to `True` in the resulting mask.
681+
682+
| Result of `t < blurred_image` | Result of `blurred_image < 0.95` | Resulting value in `binary_mask` |
683+
|----------|---------|---------|
684+
| False | True | False |
685+
| True | False | False |
686+
| True | True | True |
687+
688+
Knowing how to construct this kind of logical operation can be very helpful in image processing. The University of Minnesota Library's [guide to Boolean operators](https://libguides.umn.edu/BooleanOperators) is a good place to start if you want to learn more.
689+
690+
::::::::::::::::::::::::::::::::::::::::::::::::::
662691

663692
Here are the binary images produced by the additional thresholding.
664693
Note that we have not completely removed the offending white pixels.

0 commit comments

Comments
 (0)