diff --git a/prose/blocks/detection.py b/prose/blocks/detection.py index 6e3b2255..00cd6fd4 100644 --- a/prose/blocks/detection.py +++ b/prose/blocks/detection.py @@ -19,7 +19,7 @@ def __init__( sort: bool = True, min_separation: float = None, min_area: float = 0, - minor_length: float = 0, + eccentricity_bound: float = 0, name: str = None, ): """Base class for sources detection. @@ -38,8 +38,8 @@ def __init__( minimum separation in pixels from one source to the other. Between two sources, greater ADU is kept, by default None min_area : float, optional minimum area in pixels of the sources to detect, by default 0 - minor_length : float, optional - minimum length of semi-major axis of sources to detect, by default 0 + eccentricity_bound : int, optional + maximum eccentricity of sources to detect, by default 1 name : str, optional name of the block, by default None """ @@ -49,7 +49,7 @@ def __init__( self.min_separation = min_separation self.threshold = threshold self.min_area = min_area - self.minor_length = minor_length + self.eccentricity_bound = eccentricity_bound def clean(self, sources): peaks = np.array([s.peak for s in sources]) @@ -68,7 +68,7 @@ def clean(self, sources): for i, s in enumerate(final_sources): if final_sources[i].keep: distances = np.linalg.norm( - s.coords - final_sources.coords, axis=1 + s.coords - [final_source.coords for final_source in final_sources], axis=1 ) distances[i] == np.nan idxs = np.flatnonzero(distances < self.min_separation) @@ -114,7 +114,7 @@ def regions(self, image: Image, threshold=None): regions = [ r for r in regions - if r.area >= self.min_area and r.axis_major_length >= self.minor_length + if r.area >= self.min_area and r.eccentricity <= self.eccentricity_bound ] return regions @@ -133,7 +133,7 @@ def __init__( min_separation=None, name=None, min_area=0, - minor_length=0, + eccentricity_bound=1, ): """Detect all sources. @@ -153,8 +153,8 @@ def __init__( minimum separation in pixels from one source to the other. Between two sources, greater ADU is kept, by default None min_area : str, optional minimum area in pixels of the sources to detect, by default 0 - minor_length : str, optional - minimum length of semi-major axis of sources to detect, by default 0 + eccentricity_bound : int, optional + maximum eccentricity of sources to detect, by default 1 name : str, optional name of the block, by default None """ @@ -165,7 +165,7 @@ def __init__( min_separation=min_separation, name=name, min_area=min_area, - minor_length=minor_length, + eccentricity_bound=eccentricity_bound, ) def run(self, image): @@ -175,7 +175,7 @@ def run(self, image): class PointSourceDetection(_SourceDetection): - def __init__(self, unit_euler=False, min_area=3, minor_length=2, **kwargs): + def __init__(self, unit_euler=False, min_area=3, eccentricity_bound=2, **kwargs): """Detect point sources (as :py:class:`~prose.core.source.PointSource`). |read| :code:`Image.data` @@ -188,8 +188,8 @@ def __init__(self, unit_euler=False, min_area=3, minor_length=2, **kwargs): whether to consider sources with euler number == 1, by default False min_area : str, optional minimum area in pixels of the sources to detect, by default 0 - minor_length : str, optional - minimum length of semi-major axis of sources to detect, by default 0 + eccentricity_bound : int, optional + maximum eccentricity of sources to detect, by default 1 threshold : float, optional detection threshold for sources, by default 4 n : int, optional @@ -202,10 +202,10 @@ def __init__(self, unit_euler=False, min_area=3, minor_length=2, **kwargs): name of the block, by default None """ - super().__init__(min_area=min_area, minor_length=minor_length, **kwargs) + super().__init__(min_area=min_area, eccentricity_bound=eccentricity_bound, **kwargs) self.unit_euler = unit_euler self.min_area = min_area - self.minor_length = minor_length + self.eccentricity_bound = eccentricity_bound def run(self, image): regions = self.regions(image) @@ -220,13 +220,13 @@ def run(self, image): class TraceDetection(_SourceDetection): - def __init__(self, minor_length=5, **kwargs): + def __init__(self, eccentricity_bound=5, **kwargs): """Detect trace sources (as :py:class:`~prose.core.source.TraceSource`) Parameters ---------- - minor_length : str, optional - minimum length of semi-major axis of sources to detect, by default 0 + eccentricity_bound : int, optional + maximum eccentricity of sources to detect, by default 1 min_area : str, optional minimum area in pixels of the sources to detect, by default 0 threshold : float, optional @@ -240,7 +240,7 @@ def __init__(self, minor_length=5, **kwargs): name : str, optional name of the block, by default None """ - super().__init__(minor_length=minor_length, **kwargs) + super().__init__(eccentricity_bound=eccentricity_bound, **kwargs) def run(self, image): regions = self.regions(image) @@ -251,7 +251,7 @@ def run(self, image): # backward compatibility class SegmentedPeaks(PointSourceDetection): def __init__( - self, unit_euler=False, min_area=3, minor_length=2, n_stars=None, **kwargs + self, unit_euler=False, min_area=3, eccentricity_bound=2, n_stars=None, **kwargs ): """Detect point sources (backward compatibility) @@ -259,11 +259,11 @@ def __init__( """ super().__init__( - n=n_stars, min_area=min_area, minor_length=minor_length, **kwargs + n=n_stars, min_area=min_area, eccentricity_bound=eccentricity_bound, **kwargs ) self.unit_euler = unit_euler self.min_area = min_area - self.minor_length = minor_length + self.eccentricity_bound = eccentricity_bound # TODO: document @@ -336,7 +336,7 @@ def __init__( def detect(self, image): _, median, std = sigma_clipped_stats(image.data, sigma=self.sigma_clip) finder = DAOStarFinder(fwhm=self.fwhm, threshold=self.lower_snr * std) - sources = finder(image.data - median) + sources = finder(image.data - median, mask=image.mask) coordinates = np.transpose( np.array([sources["xcentroid"].data, sources["ycentroid"].data]) diff --git a/prose/blocks/geometry.py b/prose/blocks/geometry.py index 5ca44956..d87e9a4b 100644 --- a/prose/blocks/geometry.py +++ b/prose/blocks/geometry.py @@ -258,7 +258,7 @@ def __init__(self, reference_image: Image, n=10, discard=True, **kwargs): self._parallel_friendly = True def run(self, image): - if len(image.sources.coords) >= 5: + if len(image.sources.coords) >= 3: # changed from 5 to 3, as some SPIRIT images have very few sources if len(image.sources.coords) <= 2: shift = self.ref_coords[0] - image.sources.coords[0] else: diff --git a/prose/blocks/photometry.py b/prose/blocks/photometry.py index eb1079ba..0418cb66 100644 --- a/prose/blocks/photometry.py +++ b/prose/blocks/photometry.py @@ -40,7 +40,7 @@ def run(self, image: Image): apertures = [image.sources.apertures(r) for r in radii] aperture_fluxes = np.array( - [aperture_photometry(image.data, a)["aperture_sum"].data for a in apertures] + [aperture_photometry(image.data, a, mask=image.mask)["aperture_sum"].data for a in apertures] ).T image.aperture = {"fluxes": aperture_fluxes, "radii": radii} diff --git a/prose/blocks/utils.py b/prose/blocks/utils.py index 137ee2ed..966423cb 100644 --- a/prose/blocks/utils.py +++ b/prose/blocks/utils.py @@ -622,6 +622,8 @@ def __init__(self, n=5, name=None): def run(self, image: Image): sigma = image.fwhm + self.metadata = image.metadata + self.header = image.header if len(self._images) < self.n: self._images.append(image) self._sigmas.append(sigma) @@ -633,3 +635,5 @@ def run(self, image: Image): def terminate(self): self.stack = Image(easy_median([im.data for im in self._images])) + self.stack.metadata = self.metadata + self.stack.header = self.header diff --git a/prose/core/image.py b/prose/core/image.py index 4d0e07a8..e9191fcd 100644 --- a/prose/core/image.py +++ b/prose/core/image.py @@ -57,6 +57,9 @@ class Image: header: Optional[Header] = None """FITS header associated with the image (optional)""" + + mask: Optional[np.ndarray] = None + """Mask (commonly used for bad pixels) that is written in the CleanBadPixels block""" _wcs = None diff --git a/prose/fluxes.py b/prose/fluxes.py index 71827614..6bc2e373 100644 --- a/prose/fluxes.py +++ b/prose/fluxes.py @@ -113,7 +113,7 @@ def auto_diff_1d(fluxes, i=None): def best_weights(j): _w = w.copy() - _w[idxs[j::]] = 0.0 + _w[idxs[j:]] = 0.0 _w[i] = 0.0 return _w @@ -296,7 +296,7 @@ def diff(self, comps: np.ndarray = None): differential :code:`Fluxes` """ if comps is not None: - weights = np.zeros(self.fluxes[0:2]) + weights = np.zeros((self.fluxes.shape[0], self.fluxes.shape[1])) weights[:, comps] = 1 else: weights = None