@@ -225,6 +225,83 @@ def get_peaks(image, threshold=None, ordered=True, mask=None, include_border=Fal
225225 return X , Y , heights
226226
227227
228+ def get_peaks_sphere (image , nside , threshold = None , ordered = True , mask = None ):
229+ """
230+ Identify peaks in a spherical HEALPix map using a vectorized approach.
231+
232+ A peak is defined as a pixel with a value greater than all of its neighbors.
233+ The neighborhood is determined by the healpy's `get_all_neighbors` function.
234+
235+ Parameters
236+ ----------
237+ image : array_like
238+ HEALPix map (1D array).
239+ nside : int
240+ Nside parameter of the HEALPix map.
241+ threshold : float, optional
242+ Minimum pixel amplitude to be considered as a peak. If not provided,
243+ the default value is set to the minimum of `image`.
244+ ordered : bool, optional
245+ If True, return peaks in decreasing order according to height.
246+ mask : array_like (same shape as `image`), optional
247+ Boolean array identifying which pixels of `image` to consider/exclude
248+ in finding peaks. A numerical array will be converted to binary, where
249+ only zero values are considered masked.
250+
251+ Returns
252+ -------
253+ X, heights : tuple of 1D numpy arrays
254+ Pixel indices of peak positions and their associated heights.
255+ """
256+ image = np .atleast_1d (image )
257+ npix = hp .nside2npix (nside )
258+
259+ if mask is None :
260+ mask = np .ones (npix , dtype = bool )
261+ else :
262+ mask = mask .astype (bool )
263+
264+ # Handle threshold like the original implementation
265+ if threshold is None :
266+ threshold = image [mask ].min ()
267+
268+ # Get the indices of all neighbors for every pixel
269+ neighbors_indices = hp .get_all_neighbours (nside , np .arange (npix ))
270+
271+ # neighbors_indices has shape (8, npix) - 8 neighbors for each pixel
272+ # We need to transpose it to get (npix, 8) for easier processing
273+ neighbors_indices = neighbors_indices .T # Now shape is (npix, 8)
274+
275+ # Pad the image to handle -1 indices
276+ image_padded = np .zeros (npix + 1 )
277+ image_padded [1 :] = image [:]
278+ # Set the value for index -1 to a very low number to ensure it is never
279+ # considered the maximum. This corresponds to a padding value for invalid neighbors.
280+ image_padded [0 ] = - np .inf
281+
282+ # Shift neighbor indices by +1 so -1 becomes 0
283+ neighbors_indices_padded = neighbors_indices + 1
284+
285+ # Get the values of all neighbors from the padded image.
286+ neighbor_values = image_padded [neighbors_indices_padded ]
287+
288+ # Find the maximum value among all neighbors for each pixel
289+ max_neighbor_values = np .max (neighbor_values , axis = 1 )
290+
291+ # A pixel is a peak if its value is strictly greater than the maximum of its neighbors
292+ # AND it meets the threshold AND it's in the mask
293+ peaks_mask = (image > max_neighbor_values ) & (image >= threshold ) & mask
294+
295+ peak_indices = np .where (peaks_mask )[0 ]
296+ peak_heights = image [peak_indices ]
297+
298+ if ordered :
299+ inds = np .argsort (peak_heights )[::- 1 ]
300+ return peak_indices [inds ], peak_heights [inds ]
301+
302+ return peak_indices , peak_heights
303+
304+
228305# Class to compute multiscale peaks and wavelet l1 norm for an image
229306class HOS_starlet_l1norm_peaks :
230307 NBins = 0 # int: Number of bins to use
@@ -683,6 +760,86 @@ def get_wtl1_sphere(
683760 return np .array (bins_coll ), np .array (l1norm_coll )
684761
685762
763+ def get_wtpeaks_sphere (
764+ Map , nscales , noise_std = None , Mask = None , nbins = 31 , Min = - 2 , Max = 6 , verbose = False
765+ ):
766+ """
767+ Calculate the histogram of peak counts at all scales for a spherical HEALPix map.
768+ The overall logic and functionality is similar to the get_wtpeaks function.
769+ The wavelet transform is performed using CMRStarlet().
770+
771+ Parameters
772+ ----------
773+ Map : array_like
774+ HEALPix map to analyze.
775+ nscales : int
776+ Number of wavelet scales to use.
777+ noise_std : float, optional
778+ Standard deviation of the noise. If provided, the wavelet coefficients
779+ are divided by this value to obtain an SNR map.
780+ Mask : array_like, optional
781+ Caculate the histogram of peak counts only in the mask area.
782+ The default is None, the whole image is used at every scale.
783+ nbins : int, optional
784+ Number of bins for the histogram. The default is 31.
785+ Min : int, optional
786+ Minimum value for the histogram bins. The default is -2.
787+ Max : int, optional
788+ Maximum value for the histogram bins. The default is 6.
789+ verbose : bool, optional
790+ If True, print the minimum and maximum values at each scale. The default is False.
791+
792+ Returns
793+ -------
794+ tuple of lists
795+ (Peaks_Count, TabBinsCenter) where:
796+ - Peaks_Count[i] is a numpy array containing the histogram of peak counts for scale i.
797+ - TabBinsCenter is a numpy array with the center of each bin.
798+ """
799+
800+ # Determine Nside from the input map
801+ Nside = hp .npix2nside (Map .shape [0 ])
802+
803+ # Initialize and perform CMRStarlet transform
804+ C = CMRStarlet ()
805+ C .init_starlet (Nside , nscale = nscales )
806+ C .transform (Map )
807+
808+ # Set up histogram bins
809+ TabBins = np .linspace (Min , Max , nbins )
810+ TabBinsCenter = 0.5 * (TabBins [:- 1 ] + TabBins [1 :])
811+
812+ Peaks_Count = []
813+
814+ # Loop through each scale
815+ for i in range (nscales ):
816+ # Get normalized wavelet coefficients for the i-th scale
817+ if C .TabNorm [i ] == 0 :
818+ ScaleCoeffs = C .coef [i ].copy ()
819+ else :
820+ ScaleCoeffs = C .coef [i ] / C .TabNorm [i ]
821+
822+ # If noise_std is provided, convert to SNR
823+ if noise_std is not None :
824+ ScaleCoeffs = ScaleCoeffs / noise_std
825+
826+ if verbose :
827+ print (
828+ f"Scale { i + 1 } : Min = { ScaleCoeffs .min ():.4f} , Max = { ScaleCoeffs .max ():.4f} "
829+ )
830+
831+ # Get peaks for the current scale
832+ peak_indices , peak_heights = get_peaks_sphere (
833+ ScaleCoeffs , Nside , threshold = None , ordered = True , mask = Mask
834+ )
835+
836+ # Calculate histogram of peak heights
837+ counts , bin_edges = np .histogram (peak_heights , bins = TabBins )
838+ Peaks_Count .append (counts )
839+
840+ return Peaks_Count , TabBinsCenter
841+
842+
686843############# TESTS routine ############
687844
688845
0 commit comments