@@ -584,27 +584,44 @@ def edge_1d_indexes_from(mask_2d: np.ndarray) -> np.ndarray:
584584 An edge pixel is defined as a pixel on the mask which is unmasked (has a `False`) value and at least one of its 8
585585 direct neighbors is masked (is `True`).
586586
587+ For example, for the following ``Mask2D``:
588+
589+ ::
590+ [[True, True, True, True, True],
591+ [True, False, False, False, True],
592+ [True, False, False, False, True],
593+ [True, False, False, False, True],
594+ [True, True, True, True, True]]
595+
596+ The `edge_slim` indexes (given via ``mask_2d.derive_indexes.edge_slim``) is given by:
597+
598+ ::
599+ [0, 1, 2, 3, 5, 6, 7, 8]
600+
601+ Note that index 4 is skipped, which corresponds to the ``False`` value in the centre of the mask, because it
602+ does not neighbor a ``True`` value in any one of the eight neighboring directions and is therefore not at
603+ an edge.
604+
587605 Parameters
588606 ----------
589607 mask_2d
590608 A 2D boolean array where `False` values indicate unmasked pixels.
591609
592610 Returns
593611 -------
594- np.ndarray
595- A 1D array of indexes of all edge pixels on the mask.
612+ A 1D array of indexes of all edge pixels on the mask.
596613
597614 Examples
598615 --------
599616 >>> mask = np.array([
600617 ... [True, True, True, True, True],
601- ... [True, False, False, True, True],
602618 ... [True, False, False, False, True],
603- ... [True, True, False, True, True],
619+ ... [True, False, False, False, True],
620+ ... [True, False, False, False, True],
604621 ... [True, True, True, True, True]
605622 ... ])
606623 >>> edge_1d_indexes_from(mask)
607- array([1, 2, 5, 7, 8, 9 ])
624+ array([0, 1, 2, 3, 5, 6, 7, 8 ])
608625 """
609626 # Pad the mask to handle edge cases without index errors
610627 padded_mask = np .pad (mask_2d , pad_width = 1 , mode = 'constant' , constant_values = True )
@@ -629,102 +646,12 @@ def edge_1d_indexes_from(mask_2d: np.ndarray) -> np.ndarray:
629646 return index_array [edge_mask ]
630647
631648
632- @numba_util .jit ()
633- def check_if_border_pixel (
634- mask_2d : np .ndarray , edge_pixel_slim : int , native_to_slim : np .ndarray
635- ) -> bool :
636- """
637- Checks if an input [y,x] pixel on the input `mask` is a border-pixel.
638-
639- A borders pixel is a pixel which:
640-
641- 1) is not fully surrounding by `False` mask values.
642- 2) Can reach the edge of the array without hitting a masked pixel in one of four directions (upwards, downwards,
643- left, right).
644-
645- The borders pixels are thus pixels which are on the exterior edge of the mask. For example, the inner ring of edge
646- pixels in an annular mask are edge pixels but not borders pixels.
647-
648- Parameters
649- ----------
650- mask_2d
651- The mask for which the input pixel is checked if it is a border pixel.
652- edge_pixel_slim
653- The edge pixel index in 1D that is checked if it is a border pixel (this 1D index is mapped to 2d via the
654- array `native_index_for_slim_index_2d`).
655- native_to_slim
656- An array describing the native 2D array index that every slimmed array index maps too.
657-
658- Returns
659- -------
660- bool
661- If `True` the pixel on the mask is a border pixel, else a `False` is returned because it is not.
662- """
663- edge_pixel_index = int (edge_pixel_slim )
664-
665- y = int (native_to_slim [edge_pixel_index , 0 ])
666- x = int (native_to_slim [edge_pixel_index , 1 ])
667-
668- if (
669- np .sum (mask_2d [0 :y , x ]) == y
670- or np .sum (mask_2d [y , x : mask_2d .shape [1 ]]) == mask_2d .shape [1 ] - x - 1
671- or np .sum (mask_2d [y : mask_2d .shape [0 ], x ]) == mask_2d .shape [0 ] - y - 1
672- or np .sum (mask_2d [y , 0 :x ]) == x
673- ):
674- return True
675- else :
676- return False
677-
678-
679- @numba_util .jit ()
680- def total_border_pixels_from (mask_2d , edge_pixels , native_to_slim ):
681- """
682- Returns the total number of border-pixels in a mask.
683-
684- A borders pixel is a pixel which:
685-
686- 1) is not fully surrounding by `False` mask values.
687- 2) Can reach the edge of the array without hitting a masked pixel in one of four directions (upwards, downwards,
688- left, right).
689-
690- The borders pixels are thus pixels which are on the exterior edge of the mask. For example, the inner ring of edge
691- pixels in an annular mask are edge pixels but not borders pixels.
692-
693- Parameters
694- ----------
695- mask_2d
696- The mask for which the total number of border pixels is computed.
697- edge_pixel_1d
698- The edge pixel index in 1D that is checked if it is a border pixel (this 1D index is mapped to 2d via the
699- array `native_index_for_slim_index_2d`).
700- native_to_slim
701- An array describing the 2D array index that every 1D array index maps too.
702-
703- Returns
704- -------
705- int
706- The total number of border pixels.
707- """
708-
709- border_pixel_total = 0
710-
711- for i in range (edge_pixels .shape [0 ]):
712- if check_if_border_pixel (mask_2d , edge_pixels [i ], native_to_slim ):
713- border_pixel_total += 1
714-
715- return border_pixel_total
716-
717-
718- @numba_util .jit ()
719649def border_slim_indexes_from (mask_2d : np .ndarray ) -> np .ndarray :
720650 """
721- Returns a slim array of shape [total_unmasked_border_pixels] listing all borders pixel indexes in the mask.
722-
723- A borders pixel is a pixel which:
651+ Returns a 1D array listing all border pixel indexes in the mask.
724652
725- 1) is not fully surrounding by `False` mask values.
726- 2) Can reach the edge of the array without hitting a masked pixel in one of four directions (upwards, downwards,
727- left, right).
653+ A border pixel is an unmasked pixel (`False` value) that can reach the edge of the mask without encountering
654+ a masked (`True`) pixel in any of the four cardinal directions (up, down, left, right).
728655
729656 The borders pixels are thus pixels which are on the exterior edge of the mask. For example, the inner ring of edge
730657 pixels in an annular mask are edge pixels but not borders pixels.
@@ -753,39 +680,53 @@ def border_slim_indexes_from(mask_2d: np.ndarray) -> np.ndarray:
753680 Parameters
754681 ----------
755682 mask_2d
756- The mask for which the slimmed border pixel indexes are calculated .
683+ A 2D boolean array where `False` values indicate unmasked pixels .
757684
758685 Returns
759686 -------
760- np.ndarray
761- The slimmed indexes of all border pixels on the mask.
762- """
687+ A 1D array of indexes of all border pixels on the mask.
763688
764- edge_pixels = edge_1d_indexes_from (mask_2d = mask_2d )
765- native_index_for_slim_index_2d = native_index_for_slim_index_2d_from (
766- mask_2d = mask_2d ,
767- )
689+ Examples
690+ --------
691+ >>> mask = np.array([
692+ ... [True, True, True, True, True, True, True, True, True],
693+ ... [True, False, False, False, False, False, False, False, True],
694+ ... [True, False, True, True, True, True, True, False, True],
695+ ... [True, False, True, False, False, False, True, False, True],
696+ ... [True, False, True, False, True, False, True, False, True],
697+ ... [True, False, True, False, False, False, True, False, True],
698+ ... [True, False, True, True, True, True, True, False, True],
699+ ... [True, False, False, False, False, False, False, False, True],
700+ ... [True, True, True, True, True, True, True, True, True]
701+ ... ])
702+ >>> border_slim_indexes_from(mask)
703+ array([0, 1, 2, 3, 5, 6, 7, 11, 12, 15, 16, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29])
704+ """
768705
769- border_pixel_total = total_border_pixels_from (
770- mask_2d = mask_2d ,
771- edge_pixels = edge_pixels ,
772- native_to_slim = native_index_for_slim_index_2d ,
773- )
706+ # Compute cumulative sums along each direction
707+ up_sums = np . cumsum ( mask_2d , axis = 0 )
708+ down_sums = np . cumsum ( mask_2d [:: - 1 , :], axis = 0 )[:: - 1 , :]
709+ left_sums = np . cumsum ( mask_2d , axis = 1 )
710+ right_sums = np . cumsum ( mask_2d [:, :: - 1 ], axis = 1 )[:, :: - 1 ]
774711
775- border_pixels = np .zeros (border_pixel_total )
712+ # Get mask dimensions
713+ height , width = mask_2d .shape
776714
777- border_pixel_index = 0
715+ # Identify border pixels: where the full length in any direction is True
716+ border_mask = (
717+ (up_sums == np .arange (height )[:, None ]) |
718+ (down_sums == np .arange (height - 1 , - 1 , - 1 )[:, None ]) |
719+ (left_sums == np .arange (width )[None , :]) |
720+ (right_sums == np .arange (width - 1 , - 1 , - 1 )[None , :])
721+ ) & ~ mask_2d
778722
779- for edge_pixel_index in range (edge_pixels .shape [0 ]):
780- if check_if_border_pixel (
781- mask_2d = mask_2d ,
782- edge_pixel_slim = edge_pixels [edge_pixel_index ],
783- native_to_slim = native_index_for_slim_index_2d ,
784- ):
785- border_pixels [border_pixel_index ] = edge_pixels [edge_pixel_index ]
786- border_pixel_index += 1
723+ # Create an index array where False entries get sequential 1D indices
724+ index_array = np .full (mask_2d .shape , fill_value = - 1 , dtype = int )
725+ false_indices = np .flatnonzero (~ mask_2d )
726+ index_array [~ mask_2d ] = np .arange (len (false_indices ))
787727
788- return border_pixels
728+ # Return the 1D indexes of the border pixels
729+ return index_array [border_mask ]
789730
790731
791732@numba_util .jit ()
0 commit comments