def _update_bounding_polygon(self): polygons = [im.polygon for im in self._images] if len(polygons) == 0: self._polygon = SphericalPolygon([]) self._radec = [] else: self._polygon = SphericalPolygon.multi_union(polygons) self._radec = list(self._polygon.to_radec())
def update_bounding_polygon(self): """ Recompute bounding polygons of the member images. """ polygons = [im.polygon for im in self._images] if len(polygons) == 0: self._polygon = SphericalPolygon([]) else: self._polygon = SphericalPolygon.multi_union(polygons)
def set_members(self, mlist, polygon): if mlist is None: member_list = [] elif isinstance(mlist, list): member_list = mlist if [1 for m in mlist if not isinstance(m, SkyLineMember)]: raise ValueError("The \'mlist\' argument must be either "\ "a single \'SkyLineMember\' object or a " \ "Python list of \'SkyLineMember\' objects.") elif isinstance(mlist, SkyLineMember): member_list = [mlist] else: raise ValueError("The \'mlist\' argument must be either "\ "a single \'SkyLineMember\' object or a " \ "Python list of \'SkyLineMember\' objects.") self._members = [] # Not using set to preserve order n = len(member_list) if n == 0: if polygon is None: self.polygon = SphericalPolygon([]) else: self.polygon = deepcopy(polygon) self._id = '' self._is_mf_mosaic = False elif n == 1: assert isinstance(member_list[0], SkyLineMember) if polygon is None: self.polygon = deepcopy(member_list[0].polygon) else: self.polygon = deepcopy(polygon) self._id = member_list[0].id self._members.append(member_list[0]) self._is_mf_mosaic = False else: assert isinstance(member_list[0], SkyLineMember) if polygon is None: mpol = deepcopy(member_list[0].polygon) else: mpol = deepcopy(polygon) self._members.append(member_list[0]) for m in member_list[1:]: # Report corrupted members list instead of skipping assert isinstance(m, SkyLineMember) if m not in self._members: self._members.append(m) if polygon is None: mpol = mpol.union(m.polygon) self.polygon = mpol self._update_mosaic_flag_id()
def __init__(self, image, wcs_fwd, wcs_inv, pix_area=1.0, convf=1.0, mask=None, id=None, skystat=None, stepsize=None, meta=None): """ Initializes the SkyImage object. Parameters ---------- image : numpy.ndarray A 2D array of image data. wcs_fwd : function "forward" pixel-to-world transformation function. wcs_inv : function "inverse" world-to-pixel transformation function. pix_area : float, optional Average pixel's sky area. convf : float, optional Conversion factor that when multiplied to `image` data converts the data to "uniform" (across multiple images) surface brightness units. .. note:: The functionality to support this conversion is not yet implemented and at this moment `convf` is ignored. mask : numpy.ndarray A 2D array that indicates what pixels in the input `image` should be used for sky computations (``1``) and which pixels should **not** be used for sky computations (``0``). id : anything The value of this parameter is simple stored within the `SkyImage` object. While it can be of any type, it is prefereble that `id` be of a type with nice string representation. skystat : callable, None, optional A callable object that takes a either a 2D image (2D `numpy.ndarray`) or a list of pixel values (a Nx1 array) and returns a tuple of two values: some statistics (e.g., mean, median, etc.) and number of pixels/values from the input image used in computing that statistics. When `skystat` is not set, `SkyImage` will use :py:class:`~jwst_pipeline.skymatch.skystatistics.SkyStats` object to perform sky statistics on image data. stepsize : int, None, optional Spacing between vertices of the image's bounding polygon. Default value of `None` creates bounding polygons with four vertices corresponding to the corners of the image. meta : dict, None, optional A dictionary of various items to be stored within the `SkyImage` object. """ self.image = image self.convf = convf self.meta = meta self._id = id self._pix_area = pix_area # WCS self.wcs_fwd = wcs_fwd self.wcs_inv = wcs_inv # initial sky value: self._sky = 0.0 # check that mask has the same shape as image: if mask is None: self.mask = None else: if image is None: raise ValueError("'mask' must be None when 'image' is None") self.mask = np.asanyarray(mask, dtype=np.bool) if self.mask.shape != image.shape: raise ValueError("'mask' must have the same shape as 'image'.") # create spherical polygon bounding the image if image is None or wcs_fwd is None or wcs_inv is None: self._radec = [(np.array([]), np.array([]))] self._polygon = SphericalPolygon([]) self._poly_area = 0.0 else: self.calc_bounding_polygon(stepsize) # set sky statistics function (NOTE: it must return statistics and # the number of pixels used after clipping) if skystat is None: self.set_builtin_skystat() else: self.skystat = skystat
def calc_sky(self, overlap=None, delta=True): """ Compute sky background value. Parameters ---------- overlap : SkyImage, SkyGroup, SphericalPolygon, list of tuples, \ None, optional Another `SkyImage`, `SkyGroup`, :py:class:`stsci.sphere.polygons.SphericalPolygon`, or a list of tuples of (RA, DEC) of vertices of a spherical polygon. This parameter is used to indicate that sky statistics should computed only in the region of intersection of *this* image with the polygon indicated by `overlap`. When `overlap` is `None`, sky statistics will be computed over the entire image. delta : bool, optional Should this function return absolute sky value or the difference between the computed value and the value of the sky stored in the `sky` property. Returns ------- skyval : float, None Computed sky value (absolute or relative to the `sky` attribute). If there are no valid data to perform this computations (e.g., because this image does not overlap with the image indicated by `overlap`), `skyval` will be set to `None`. npix : int Number of pixels used to compute sky statistics. polyarea : float Area (in srad) of the polygon that bounds data used to compute sky statistics. """ if overlap is None: if self.mask is None: data = self.image else: data = self.image[self.mask] polyarea = self.poly_area else: fill_mask = np.zeros(self.image.shape, dtype=bool) if isinstance(overlap, SkyImage): intersection = self.intersection(overlap) polyarea = np.fabs(intersection.area()) radec = list(intersection.to_radec()) elif isinstance(overlap, SkyGroup): radec = [] polyarea = 0.0 for im in overlap: intersection = self.intersection(im) polyarea1 = np.fabs(intersection.area()) if polyarea1 == 0.0: continue polyarea += polyarea1 radec += list(intersection.to_radec()) elif isinstance(overlap, SphericalPolygon): radec = [] polyarea = 0.0 for p in overlap._polygons: intersection = self.intersection(SphericalPolygon([p])) polyarea1 = np.fabs(intersection.area()) if polyarea1 == 0.0: continue polyarea += polyarea1 radec += list(intersection.to_radec()) else: # assume a list of (ra, dec) tuples: radec = [] polyarea = 0.0 for r, d in overlap: poly = SphericalPolygon.from_radec(r, d) polyarea1 = np.fabs(poly.area()) if polyarea1 == 0.0 or len(r) < 4: continue polyarea += polyarea1 radec.append(self.intersection(poly).to_radec()) if polyarea == 0.0: return (None, 0, 0.0) for ra, dec in radec: if len(ra) < 4: continue # set pixels in 'fill_mask' that are inside a polygon to True: x, y = self.wcs_inv(ra, dec, 0) poly_vert = list(zip(*[x, y])) polygon = region.Polygon(True, poly_vert) fill_mask = polygon.scan(fill_mask) if self.mask is not None: fill_mask &= self.mask data = self.image[fill_mask] if data.size < 1: return (None, 0, 0.0) # Calculate sky try: skyval, npix = self._skystat(data) except ValueError: return (None, 0, 0.0) if delta: skyval -= self._sky return skyval, npix, polyarea