@@ -316,24 +316,22 @@ labeled_image, count = connected_components(filename="data/shapes-01.jpg", sigma
316
316
317
317
fig, ax = plt.subplots()
318
318
plt.imshow(labeled_image)
319
- plt.axis(" off" );
319
+ plt.axis(" off" )
320
320
```
321
321
322
322
:::::::::::::::: spoiler
323
323
324
- ## Color mappings
324
+ ## Do you see an empty image?
325
325
326
- Here you might get a warning
326
+ If you are using an old version of Matplotlib you might get a warning
327
327
` UserWarning: Low image data range; displaying image with stretched contrast. `
328
- or just see an all black image
329
- (Note: this behavior might change in future versions or
330
- not occur with a different image viewer).
328
+ or just see a visually empty image.
331
329
332
330
What went wrong?
333
- When you hover over the black image,
331
+ When you hover over the image,
334
332
the pixel values are shown as numbers in the lower corner of the viewer.
335
333
You can see that some pixels have values different from ` 0 ` ,
336
- so they are not actually pure black .
334
+ so they are not actually all the same value .
337
335
Let's find out more by examining ` labeled_image ` .
338
336
Properties that might be interesting in this context are ` dtype ` ,
339
337
the minimum and maximum value.
@@ -345,29 +343,36 @@ print("min:", np.min(labeled_image))
345
343
print (" max:" , np.max(labeled_image))
346
344
```
347
345
348
- Examining the output can give us a clue why the image appears black .
346
+ Examining the output can give us a clue why the image appears empty .
349
347
350
348
``` output
351
349
dtype: int32
352
350
min: 0
353
351
max: 11
354
352
```
355
353
356
- The ` dtype ` of ` labeled_image ` is ` int64 ` .
357
- This means that values in this image range from ` -2 ** 63 ` to ` 2 ** 63 - 1 ` .
354
+ The ` dtype ` of ` labeled_image ` is ` int32 ` .
355
+ This means that values in this image range from ` -2 ** 31 ` to ` 2 ** 31 - 1 ` .
358
356
Those are really big numbers.
359
357
From this available space we only use the range from ` 0 ` to ` 11 ` .
360
358
When showing this image in the viewer,
361
- it squeezes the complete range into 256 gray values.
362
- Therefore, the range of our numbers does not produce any visible change.
359
+ it may squeeze the complete range into 256 gray values.
360
+ Therefore, the range of our numbers does not produce any visible variation. One way to rectify this
361
+ is to explicitly specify the data range we want the colormap to cover:
363
362
364
- Fortunately, the scikit-image library has tools to cope with this situation.
363
+ ``` python
364
+ fig, ax = plt.subplots()
365
+ plt.imshow(labeled_image, vmin = np.min(labeled_image), vmax = np.max(labeled_image))
366
+ ```
367
+
368
+ Note this is the default behaviour for newer versions of ` matplotlib.pyplot.imshow ` .
369
+ Alternatively we could convert the image to RGB and then display it.
365
370
366
371
367
372
:::::::::::::::::::::::::
368
373
369
374
We can use the function ` ski.color.label2rgb() `
370
- to convert the colours in the image
375
+ to convert the 32-bit grayscale labeled image to standard RGB colour
371
376
(recall that we already used the ` ski.color.rgb2gray() ` function
372
377
to convert to grayscale).
373
378
With ` ski.color.label2rgb() ` ,
@@ -380,7 +385,7 @@ colored_label_image = ski.color.label2rgb(labeled_image, bg_label=0)
380
385
381
386
fig, ax = plt.subplots()
382
387
plt.imshow(colored_label_image)
383
- plt.axis(" off" );
388
+ plt.axis(" off" )
384
389
```
385
390
386
391
![ ] ( fig/shapes-01-labeled.png ) {alt='Labeled objects'}
@@ -402,7 +407,7 @@ How does changing the `sigma` and `threshold` values influence the result?
402
407
## Solution
403
408
404
409
As you might have guessed, the return value ` count ` already
405
- contains the number of found images . So it can simply be printed
410
+ contains the number of found objects in the image . So it can simply be printed
406
411
with
407
412
408
413
``` python
@@ -685,7 +690,7 @@ set the entries that belong to small objects to `0`.
685
690
object_areas = np.array([objf[" area" ] for objf in object_features])
686
691
object_labels = np.array([objf[" label" ] for objf in object_features])
687
692
small_objects = object_labels[object_areas < min_area]
688
- labeled_image[np.isin(labeled_image,small_objects)] = 0
693
+ labeled_image[np.isin(labeled_image, small_objects)] = 0
689
694
```
690
695
691
696
An even more elegant way to remove small objects from the image is
@@ -698,7 +703,7 @@ i.e, their pixel values are set to `False`.
698
703
We can then apply ` ski.measure.label ` to the masked image:
699
704
700
705
``` python
701
- object_mask = ski.morphology.remove_small_objects(binary_mask,min_area)
706
+ object_mask = ski.morphology.remove_small_objects(binary_mask, min_size = min_area)
702
707
labeled_image, n = ski.measure.label(object_mask,
703
708
connectivity = connectivity, return_num = True )
704
709
```
@@ -712,7 +717,7 @@ def enhanced_connected_components(filename, sigma=1.0, t=0.5, connectivity=2, mi
712
717
gray_image = ski.color.rgb2gray(image)
713
718
blurred_image = ski.filters.gaussian(gray_image, sigma = sigma)
714
719
binary_mask = blurred_image < t
715
- object_mask = ski.morphology.remove_small_objects(binary_mask,min_area)
720
+ object_mask = ski.morphology.remove_small_objects(binary_mask, min_size = min_area)
716
721
labeled_image, count = ski.measure.label(object_mask,
717
722
connectivity = connectivity, return_num = True )
718
723
return labeled_image, count
@@ -728,7 +733,7 @@ colored_label_image = ski.color.label2rgb(labeled_image, bg_label=0)
728
733
729
734
fig, ax = plt.subplots()
730
735
plt.imshow(colored_label_image)
731
- plt.axis(" off" );
736
+ plt.axis(" off" )
732
737
733
738
print (" Found" , count, " objects in the image." )
734
739
```
@@ -772,14 +777,16 @@ the area by indexing the `object_areas` with the label values in `labeled_image`
772
777
773
778
``` python
774
779
object_areas = np.array([objf[" area" ] for objf in ski.measure.regionprops(labeled_image)])
775
- object_areas = np.insert(0 ,1 ,object_areas)
780
+ # prepend zero to object_areas array for background pixels
781
+ object_areas = np.insert(0 , obj = 1 , values = object_areas)
782
+ # create image where the pixels in each object are equal to that object's area
776
783
colored_area_image = object_areas[labeled_image]
777
784
778
785
fig, ax = plt.subplots()
779
786
im = plt.imshow(colored_area_image)
780
787
cbar = fig.colorbar(im, ax = ax, shrink = 0.85 )
781
788
cbar.ax.set_title(" Area" )
782
- plt.axis(" off" );
789
+ plt.axis(" off" )
783
790
```
784
791
785
792
![ ] ( fig/shapes-01-objects-coloured-by-area.png ) {alt='Objects colored by area'}
0 commit comments