LSST Applications g0f08755f38+82efc23009,g12f32b3c4e+e7bdf1200e,g1653933729+a8ce1bb630,g1a0ca8cf93+50eff2b06f,g28da252d5a+52db39f6a5,g2bbee38e9b+37c5a29d61,g2bc492864f+37c5a29d61,g2cdde0e794+c05ff076ad,g3156d2b45e+41e33cbcdc,g347aa1857d+37c5a29d61,g35bb328faa+a8ce1bb630,g3a166c0a6a+37c5a29d61,g3e281a1b8c+fb992f5633,g414038480c+7f03dfc1b0,g41af890bb2+11b950c980,g5fbc88fb19+17cd334064,g6b1c1869cb+12dd639c9a,g781aacb6e4+a8ce1bb630,g80478fca09+72e9651da0,g82479be7b0+04c31367b4,g858d7b2824+82efc23009,g9125e01d80+a8ce1bb630,g9726552aa6+8047e3811d,ga5288a1d22+e532dc0a0b,gae0086650b+a8ce1bb630,gb58c049af0+d64f4d3760,gc28159a63d+37c5a29d61,gcf0d15dbbd+2acd6d4d48,gd7358e8bfb+778a810b6e,gda3e153d99+82efc23009,gda6a2b7d83+2acd6d4d48,gdaeeff99f8+1711a396fd,ge2409df99d+6b12de1076,ge79ae78c31+37c5a29d61,gf0baf85859+d0a5978c5a,gf3967379c6+4954f8c433,gfb92a5be7c+82efc23009,gfec2e1e490+2aaed99252,w.2024.46
LSST Data Management Base Package
Loading...
Searching...
No Matches
vignette.py
Go to the documentation of this file.
1# This file is part of ip_isr.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
21
22__all__ = ('VignetteConfig', 'VignetteTask', 'setValidPolygonCcdIntersect',
23 'maskVignettedRegion')
24
25import logging
26import numpy as np
27
28import lsst.geom as geom
29import lsst.afw.cameraGeom as cameraGeom
30import lsst.afw.geom as afwGeom
31
32from lsst.pex.config import Config, Field
33from lsst.pipe.base import Task
34
35
37 """Settings to define vignetting pattern.
38 """
39 xCenter = Field(
40 dtype=float,
41 doc="Center of vignetting pattern, in focal plane x coordinates.",
42 default=0.0,
43 )
44 yCenter = Field(
45 dtype=float,
46 doc="Center of vignetting pattern, in focal plane y coordinates.",
47 default=0.0,
48 )
49 radius = Field(
50 dtype=float,
51 doc="Radius of vignetting pattern, in focal plane coordinates.",
52 default=100.0,
53 check=lambda x: x >= 0
54 )
55 numPolygonPoints = Field(
56 dtype=int,
57 doc="Number of points used to define the vignette polygon.",
58 default=100,
59 )
60
61
62class VignetteTask(Task):
63 """Define a simple circular vignette pattern and optionally update mask
64 plane.
65 """
66 ConfigClass = VignetteConfig
67 _DefaultName = "isrVignette"
68
69 def run(self, exposure=None, doUpdateMask=True, maskPlane="NO_DATA", vignetteValue=None, log=None):
70 """Generate circular vignette pattern.
71
72 Parameters
73 ----------
74 exposure : `lsst.afw.image.Exposure`, optional
75 Exposure to construct, apply, and optionally mask vignette for.
76 doUpdateMask : `bool`, optional
77 If true, the mask will be updated to mask the vignetted region.
78 maskPlane : `str`, optional
79 Mask plane to assign vignetted pixels to.
80 vignetteValue : `float` or `None`, optional
81 Value to assign to the image array pixels within the ``polygon``
82 region. If `None`, image pixel values are not replaced.
83 log : `logging.Logger`, optional
84 Log object to write to.
85
86 Returns
87 -------
88 polygon : `lsst.afw.geom.Polygon`
89 Polygon defining the boundary of the vignetted region.
90 """
91 theta = np.linspace(0, 2*np.pi, num=self.config.numPolygonPoints, endpoint=False)
92 x = self.config.radius*np.cos(theta) + self.config.xCenter
93 y = self.config.radius*np.sin(theta) + self.config.yCenter
94 points = np.array([x, y]).transpose()
95 fpPolygon = afwGeom.Polygon([geom.Point2D(x1, y1) for x1, y1 in reversed(points)])
96 if exposure is None:
97 return fpPolygon
98
99 # Exposure was provided, so attach the validPolygon associated with the
100 # vignetted region.
101 setValidPolygonCcdIntersect(exposure, fpPolygon, log=log)
102
103 if doUpdateMask:
104 polygon = exposure.getInfo().getValidPolygon()
105 maskVignettedRegion(exposure, polygon, maskPlane="NO_DATA", vignetteValue=vignetteValue, log=log)
106 return fpPolygon
107
108
109def setValidPolygonCcdIntersect(ccdExposure, fpPolygon, log=None):
110 """Set valid polygon on ccdExposure associated with focal plane polygon.
111
112 The ccd exposure's valid polygon is the intersection of fpPolygon,
113 a valid polygon in focal plane coordinates, and the ccd corners,
114 in ccd pixel coordinates.
115
116 Parameters
117 ----------
118 ccdExposure : `lsst.afw.image.Exposure`
119 Exposure to process.
120 fpPolygon : `lsst.afw.geom.Polygon`
121 Polygon in focal plane coordinates.
122 log : `logging.Logger`, optional
123 Log object to write to.
124 """
125 # Get ccd corners in focal plane coordinates
126 ccd = ccdExposure.getDetector()
127 fpCorners = ccd.getCorners(cameraGeom.FOCAL_PLANE)
128 ccdPolygon = afwGeom.Polygon(fpCorners)
129 # Get intersection of ccd corners with fpPolygon
130 try:
131 intersect = ccdPolygon.intersectionSingle(fpPolygon)
133 intersect = None
134 if intersect is not None:
135 # Transform back to pixel positions and build new polygon
136 ccdPoints = ccd.transform(intersect, cameraGeom.FOCAL_PLANE, cameraGeom.PIXELS)
137 validPolygon = afwGeom.Polygon(ccdPoints)
138 ccdExposure.getInfo().setValidPolygon(validPolygon)
139 else:
140 if log is not None:
141 log.info("Ccd exposure does not overlap with focal plane polygon. Not setting validPolygon.")
142
143
144def maskVignettedRegion(exposure, polygon, maskPlane="NO_DATA", vignetteValue=None, log=None):
145 """Add mask bit to image pixels according to vignetted polygon region.
146
147 NOTE: this function could be used to mask and replace pixels associated
148 with any polygon in the exposure pixel coordinates.
149
150 Parameters
151 ----------
152 exposure : `lsst.afw.image.Exposure`
153 Image whose mask plane is to be updated.
154 polygon : `lsst.afw.geom.Polygon`
155 Polygon region defining the vignetted region in the pixel coordinates
156 of ``exposure``.
157 maskPlane : `str`, optional
158 Mask plane to assign vignetted pixels to.
159 vignetteValue : `float` or `None`, optional
160 Value to assign to the image array pixels within the ``polygon``
161 region. If `None`, image pixel values are not replaced.
162 log : `logging.Logger`, optional
163 Log object to write to.
164
165 Raises
166 ------
167 RuntimeError
168 Raised if no valid polygon exists.
169 """
170 log = log if log else logging.getLogger(__name__)
171 if not polygon:
172 log.info("No polygon provided. Masking nothing.")
173 return
174
175 fullyIlluminated = True
176 if not all(polygon.contains(exposure.getBBox().getCorners())):
177 fullyIlluminated = False
178 log.info("Exposure is fully illuminated? %s", fullyIlluminated)
179
180 if not fullyIlluminated:
181 # Scan pixels.
182 mask = exposure.getMask()
183 xx, yy = np.meshgrid(np.arange(0, mask.getWidth(), dtype=float),
184 np.arange(0, mask.getHeight(), dtype=float))
185
186 vignMask = ~(polygon.contains(xx, yy))
187
188 bitMask = mask.getPlaneBitMask(maskPlane)
189 maskArray = mask.getArray()
190 maskArray[vignMask] |= bitMask
191 log.info("Exposure contains {} vignetted pixels which are now masked with mask plane {}.".
192 format(np.count_nonzero(vignMask), maskPlane))
193
194 if vignetteValue is not None:
195 imageArray = exposure.getImage().getArray()
196 imageArray[vignMask] = vignetteValue
197 log.info("Vignetted pixels in image array have been replaced with {}.".format(vignetteValue))
Cartesian polygons.
Definition Polygon.h:59
An exception that indicates the single-polygon assumption has been violated.
Definition Polygon.h:53
run(self, exposure=None, doUpdateMask=True, maskPlane="NO_DATA", vignetteValue=None, log=None)
Definition vignette.py:69
maskVignettedRegion(exposure, polygon, maskPlane="NO_DATA", vignetteValue=None, log=None)
Definition vignette.py:144
setValidPolygonCcdIntersect(ccdExposure, fpPolygon, log=None)
Definition vignette.py:109