LSST Applications 24.1.5,g02d81e74bb+fa3a7a026e,g180d380827+a53a32eff8,g2079a07aa2+86d27d4dc4,g2305ad1205+c0501b3732,g295015adf3+7d3e92f0ec,g2bbee38e9b+0e5473021a,g337abbeb29+0e5473021a,g33d1c0ed96+0e5473021a,g3a166c0a6a+0e5473021a,g3ddfee87b4+5dd1654d75,g48712c4677+3bf1020dcb,g487adcacf7+065c13d9cf,g50ff169b8f+96c6868917,g52b1c1532d+585e252eca,g591dd9f2cf+d7ac436cfb,g5a732f18d5+53520f316c,g64a986408d+fa3a7a026e,g858d7b2824+fa3a7a026e,g8a8a8dda67+585e252eca,g99cad8db69+a5a909b84f,g9ddcbc5298+9a081db1e4,ga1e77700b3+15fc3df1f7,ga8c6da7877+4cf350ccb2,gb0e22166c9+60f28cb32d,gba4ed39666+c2a2e4ac27,gbb8dafda3b+f991a0b59f,gc120e1dc64+9ccbfdb8be,gc28159a63d+0e5473021a,gcf0d15dbbd+5dd1654d75,gd96a1ce819+42fd0ee607,gdaeeff99f8+f9a426f77a,ge6526c86ff+0d71447b4b,ge79ae78c31+0e5473021a,gee10cc3b42+585e252eca,gff1a9f87cc+fa3a7a026e
LSST Data Management Base Package
Loading...
Searching...
No Matches
Public Member Functions | Public Attributes | Protected Member Functions | List of all members
lsst.ip.diffim.dipoleFitTask.DipoleModel Class Reference

Public Member Functions

 __init__ (self)
 
 makeBackgroundModel (self, in_x, pars=None)
 
 fitFootprintBackground (self, source, posImage, order=1)
 
 makeStarModel (self, bbox, psf, xcen, ycen, flux)
 
 makeModel (self, x, flux, xcenPos, ycenPos, xcenNeg, ycenNeg, fluxNeg=None, b=None, x1=None, y1=None, xy=None, x2=None, y2=None, bNeg=None, x1Neg=None, y1Neg=None, xyNeg=None, x2Neg=None, y2Neg=None, **kwargs)
 

Public Attributes

 debug
 
 log
 

Protected Member Functions

 _generateXYGrid (self, bbox)
 
 _getHeavyFootprintSubimage (self, fp, badfill=np.nan, grow=0)
 

Detailed Description

Lightweight class containing methods for generating a dipole model for fitting
to sources in diffims, used by DipoleFitAlgorithm.

See also:
`DMTN-007: Dipole characterization for image differencing  <https://dmtn-007.lsst.io>`_.

Definition at line 192 of file dipoleFitTask.py.

Constructor & Destructor Documentation

◆ __init__()

lsst.ip.diffim.dipoleFitTask.DipoleModel.__init__ ( self)

Definition at line 200 of file dipoleFitTask.py.

200 def __init__(self):
201 import lsstDebug
202 self.debug = lsstDebug.Info(__name__).debug
203 self.log = logging.getLogger(__name__)
204

Member Function Documentation

◆ _generateXYGrid()

lsst.ip.diffim.dipoleFitTask.DipoleModel._generateXYGrid ( self,
bbox )
protected
Generate a meshgrid covering the x,y coordinates bounded by bbox

Parameters
----------
bbox : `lsst.geom.Box2I`
    input Bounding Box defining the coordinate limits

Returns
-------
in_x : `numpy.array`
    (2, w, h)-dimensional numpy array containing the grid indexing over x- and
    y- coordinates

Definition at line 248 of file dipoleFitTask.py.

248 def _generateXYGrid(self, bbox):
249 """Generate a meshgrid covering the x,y coordinates bounded by bbox
250
251 Parameters
252 ----------
253 bbox : `lsst.geom.Box2I`
254 input Bounding Box defining the coordinate limits
255
256 Returns
257 -------
258 in_x : `numpy.array`
259 (2, w, h)-dimensional numpy array containing the grid indexing over x- and
260 y- coordinates
261 """
262
263 x, y = np.mgrid[bbox.getBeginY():bbox.getEndY(), bbox.getBeginX():bbox.getEndX()]
264 in_x = np.array([y, x]).astype(np.float64)
265 in_x[0, :] -= np.mean(in_x[0, :])
266 in_x[1, :] -= np.mean(in_x[1, :])
267 return in_x
268

◆ _getHeavyFootprintSubimage()

lsst.ip.diffim.dipoleFitTask.DipoleModel._getHeavyFootprintSubimage ( self,
fp,
badfill = np.nan,
grow = 0 )
protected
Extract the image from a ``~lsst.afw.detection.HeavyFootprint``
as an `lsst.afw.image.ImageF`.

Parameters
----------
fp : `lsst.afw.detection.HeavyFootprint`
    HeavyFootprint to use to generate the subimage
badfill : `float`, optional
    Value to fill in pixels in extracted image that are outside the footprint
grow : `int`
    Optionally grow the footprint by this amount before extraction

Returns
-------
subim2 : `lsst.afw.image.ImageF`
    An `~lsst.afw.image.ImageF` containing the subimage.

Definition at line 269 of file dipoleFitTask.py.

269 def _getHeavyFootprintSubimage(self, fp, badfill=np.nan, grow=0):
270 """Extract the image from a ``~lsst.afw.detection.HeavyFootprint``
271 as an `lsst.afw.image.ImageF`.
272
273 Parameters
274 ----------
275 fp : `lsst.afw.detection.HeavyFootprint`
276 HeavyFootprint to use to generate the subimage
277 badfill : `float`, optional
278 Value to fill in pixels in extracted image that are outside the footprint
279 grow : `int`
280 Optionally grow the footprint by this amount before extraction
281
282 Returns
283 -------
284 subim2 : `lsst.afw.image.ImageF`
285 An `~lsst.afw.image.ImageF` containing the subimage.
286 """
287 bbox = fp.getBBox()
288 if grow > 0:
289 bbox.grow(grow)
290
291 subim2 = afwImage.ImageF(bbox, badfill)
292 fp.getSpans().unflatten(subim2.array, fp.getImageArray(), bbox.getCorners()[0])
293 return subim2
294

◆ fitFootprintBackground()

lsst.ip.diffim.dipoleFitTask.DipoleModel.fitFootprintBackground ( self,
source,
posImage,
order = 1 )
Fit a linear (polynomial) model of given order (max 2) to the background of a footprint.

Only fit the pixels OUTSIDE of the footprint, but within its bounding box.

Parameters
----------
source : `lsst.afw.table.SourceRecord`
    SourceRecord, the footprint of which is to be fit
posImage : `lsst.afw.image.Exposure`
    The exposure from which to extract the footprint subimage
order : `int`
    Polynomial order of background gradient to fit.

Returns
-------
pars : `tuple` of `float`
    `tuple` of length (1 if order==0; 3 if order==1; 6 if order == 2),
    containing the resulting fit parameters

Definition at line 295 of file dipoleFitTask.py.

295 def fitFootprintBackground(self, source, posImage, order=1):
296 """Fit a linear (polynomial) model of given order (max 2) to the background of a footprint.
297
298 Only fit the pixels OUTSIDE of the footprint, but within its bounding box.
299
300 Parameters
301 ----------
302 source : `lsst.afw.table.SourceRecord`
303 SourceRecord, the footprint of which is to be fit
304 posImage : `lsst.afw.image.Exposure`
305 The exposure from which to extract the footprint subimage
306 order : `int`
307 Polynomial order of background gradient to fit.
308
309 Returns
310 -------
311 pars : `tuple` of `float`
312 `tuple` of length (1 if order==0; 3 if order==1; 6 if order == 2),
313 containing the resulting fit parameters
314 """
315
316 # TODO look into whether to use afwMath background methods -- see
317 # http://lsst-web.ncsa.illinois.edu/doxygen/x_masterDoxyDoc/_background_example.html
318 fp = source.getFootprint()
319 bbox = fp.getBBox()
320 bbox.grow(3)
321 posImg = afwImage.ImageF(posImage.image, bbox, afwImage.PARENT)
322
323 # This code constructs the footprint image so that we can identify the pixels that are
324 # outside the footprint (but within the bounding box). These are the pixels used for
325 # fitting the background.
326 posHfp = afwDet.HeavyFootprintF(fp, posImage.getMaskedImage())
327 posFpImg = self._getHeavyFootprintSubimage(posHfp, grow=3)
328
329 isBg = np.isnan(posFpImg.array).ravel()
330
331 data = posImg.array.ravel()
332 data = data[isBg]
333 B = data
334
335 x, y = np.mgrid[bbox.getBeginY():bbox.getEndY(), bbox.getBeginX():bbox.getEndX()]
336 x = x.astype(np.float64).ravel()
337 x -= np.mean(x)
338 x = x[isBg]
339 y = y.astype(np.float64).ravel()
340 y -= np.mean(y)
341 y = y[isBg]
342 b = np.ones_like(x, dtype=np.float64)
343
344 M = np.vstack([b]).T # order = 0
345 if order == 1:
346 M = np.vstack([b, x, y]).T
347 elif order == 2:
348 M = np.vstack([b, x, y, x**2., y**2., x*y]).T
349
350 pars = np.linalg.lstsq(M, B, rcond=-1)[0]
351 return pars
352

◆ makeBackgroundModel()

lsst.ip.diffim.dipoleFitTask.DipoleModel.makeBackgroundModel ( self,
in_x,
pars = None )
Generate gradient model (2-d array) with up to 2nd-order polynomial

Parameters
----------
in_x : `numpy.array`
    (2, w, h)-dimensional `numpy.array`, containing the
    input x,y meshgrid providing the coordinates upon which to
    compute the gradient. This will typically be generated via
    `_generateXYGrid()`. `w` and `h` correspond to the width and
    height of the desired grid.
pars : `list` of `float`, optional
    Up to 6 floats for up
    to 6 2nd-order 2-d polynomial gradient parameters, in the
    following order: (intercept, x, y, xy, x**2, y**2). If `pars`
    is emtpy or `None`, do nothing and return `None` (for speed).

Returns
-------
result : `None` or `numpy.array`
    return None, or 2-d numpy.array of width/height matching
    input bbox, containing computed gradient values.

Definition at line 205 of file dipoleFitTask.py.

205 def makeBackgroundModel(self, in_x, pars=None):
206 """Generate gradient model (2-d array) with up to 2nd-order polynomial
207
208 Parameters
209 ----------
210 in_x : `numpy.array`
211 (2, w, h)-dimensional `numpy.array`, containing the
212 input x,y meshgrid providing the coordinates upon which to
213 compute the gradient. This will typically be generated via
214 `_generateXYGrid()`. `w` and `h` correspond to the width and
215 height of the desired grid.
216 pars : `list` of `float`, optional
217 Up to 6 floats for up
218 to 6 2nd-order 2-d polynomial gradient parameters, in the
219 following order: (intercept, x, y, xy, x**2, y**2). If `pars`
220 is emtpy or `None`, do nothing and return `None` (for speed).
221
222 Returns
223 -------
224 result : `None` or `numpy.array`
225 return None, or 2-d numpy.array of width/height matching
226 input bbox, containing computed gradient values.
227 """
228
229 # Don't fit for other gradient parameters if the intercept is not included.
230 if (pars is None) or (len(pars) <= 0) or (pars[0] is None):
231 return
232
233 y, x = in_x[0, :], in_x[1, :]
234 gradient = np.full_like(x, pars[0], dtype='float64')
235 if len(pars) > 1 and pars[1] is not None:
236 gradient += pars[1] * x
237 if len(pars) > 2 and pars[2] is not None:
238 gradient += pars[2] * y
239 if len(pars) > 3 and pars[3] is not None:
240 gradient += pars[3] * (x * y)
241 if len(pars) > 4 and pars[4] is not None:
242 gradient += pars[4] * (x * x)
243 if len(pars) > 5 and pars[5] is not None:
244 gradient += pars[5] * (y * y)
245
246 return gradient
247

◆ makeModel()

lsst.ip.diffim.dipoleFitTask.DipoleModel.makeModel ( self,
x,
flux,
xcenPos,
ycenPos,
xcenNeg,
ycenNeg,
fluxNeg = None,
b = None,
x1 = None,
y1 = None,
xy = None,
x2 = None,
y2 = None,
bNeg = None,
x1Neg = None,
y1Neg = None,
xyNeg = None,
x2Neg = None,
y2Neg = None,
** kwargs )
Generate dipole model with given parameters.

This is the function whose sum-of-squared difference from data
is minimized by `lmfit`.

x : TODO: DM-17458
    Input independent variable. Used here as the grid on
    which to compute the background gradient model.
flux : `float`
    Desired flux of the positive lobe of the dipole
xcenPos, ycenPos : `float`
    Desired x,y-centroid of the positive lobe of the dipole
xcenNeg, ycenNeg : `float`
    Desired x,y-centroid of the negative lobe of the dipole
fluxNeg : `float`, optional
    Desired flux of the negative lobe of the dipole, set to 'flux' if None
b, x1, y1, xy, x2, y2 : `float`
    Gradient parameters for positive lobe.
bNeg, x1Neg, y1Neg, xyNeg, x2Neg, y2Neg : `float`, optional
    Gradient parameters for negative lobe.
    They are set to the corresponding positive values if None.

**kwargs : `dict` [`str`]
    Keyword arguments passed through ``lmfit`` and
    used by this function. These must include:

    - ``psf`` Psf model used to generate the 'star'
    - ``rel_weight`` Used to signify least-squares weighting of posImage/negImage
        relative to diffim. If ``rel_weight == 0`` then posImage/negImage are ignored.
    - ``bbox`` Bounding box containing region to be modelled

Returns
-------
zout : `numpy.array`
    Has width and height matching the input bbox, and
    contains the dipole model with given centroids and flux(es). If
    ``rel_weight`` = 0, this is a 2-d array with dimensions matching
    those of bbox; otherwise a stack of three such arrays,
    representing the dipole (diffim), positive, and negative images
    respectively.

Definition at line 396 of file dipoleFitTask.py.

399 **kwargs):
400 """Generate dipole model with given parameters.
401
402 This is the function whose sum-of-squared difference from data
403 is minimized by `lmfit`.
404
405 x : TODO: DM-17458
406 Input independent variable. Used here as the grid on
407 which to compute the background gradient model.
408 flux : `float`
409 Desired flux of the positive lobe of the dipole
410 xcenPos, ycenPos : `float`
411 Desired x,y-centroid of the positive lobe of the dipole
412 xcenNeg, ycenNeg : `float`
413 Desired x,y-centroid of the negative lobe of the dipole
414 fluxNeg : `float`, optional
415 Desired flux of the negative lobe of the dipole, set to 'flux' if None
416 b, x1, y1, xy, x2, y2 : `float`
417 Gradient parameters for positive lobe.
418 bNeg, x1Neg, y1Neg, xyNeg, x2Neg, y2Neg : `float`, optional
419 Gradient parameters for negative lobe.
420 They are set to the corresponding positive values if None.
421
422 **kwargs : `dict` [`str`]
423 Keyword arguments passed through ``lmfit`` and
424 used by this function. These must include:
425
426 - ``psf`` Psf model used to generate the 'star'
427 - ``rel_weight`` Used to signify least-squares weighting of posImage/negImage
428 relative to diffim. If ``rel_weight == 0`` then posImage/negImage are ignored.
429 - ``bbox`` Bounding box containing region to be modelled
430
431 Returns
432 -------
433 zout : `numpy.array`
434 Has width and height matching the input bbox, and
435 contains the dipole model with given centroids and flux(es). If
436 ``rel_weight`` = 0, this is a 2-d array with dimensions matching
437 those of bbox; otherwise a stack of three such arrays,
438 representing the dipole (diffim), positive, and negative images
439 respectively.
440 """
441
442 psf = kwargs.get('psf')
443 rel_weight = kwargs.get('rel_weight') # if > 0, we're including pre-sub. images
444 fp = kwargs.get('footprint')
445 bbox = fp.getBBox()
446
447 if fluxNeg is None:
448 fluxNeg = flux
449
450 self.log.debug('flux: %.2f fluxNeg: %.2f x+: %.2f x-: %.2f y+: %.2f y-: %.2f ',
451 flux, fluxNeg, xcenPos, xcenNeg, ycenPos, ycenNeg)
452 if x1 is not None:
453 self.log.debug(' b: %.2f x1: %.2f y1: %.2f', b, x1, y1)
454 if xy is not None:
455 self.log.debug(' xy: %.2f x2: %.2f y2: %.2f', xy, x2, y2)
456
457 posIm = self.makeStarModel(bbox, psf, xcenPos, ycenPos, flux)
458 negIm = self.makeStarModel(bbox, psf, xcenNeg, ycenNeg, fluxNeg)
459
460 in_x = x
461 if in_x is None: # use the footprint to generate the input grid
462 y, x = np.mgrid[bbox.getBeginY():bbox.getEndY(), bbox.getBeginX():bbox.getEndX()]
463 in_x = np.array([x, y]) * 1.
464 in_x[0, :] -= in_x[0, :].mean() # center it!
465 in_x[1, :] -= in_x[1, :].mean()
466
467 if b is not None:
468 gradient = self.makeBackgroundModel(in_x, (b, x1, y1, xy, x2, y2))
469
470 # If bNeg is None, then don't fit the negative background separately
471 if bNeg is not None:
472 gradientNeg = self.makeBackgroundModel(in_x, (bNeg, x1Neg, y1Neg, xyNeg, x2Neg, y2Neg))
473 else:
474 gradientNeg = gradient
475
476 posIm.array[:, :] += gradient
477 negIm.array[:, :] += gradientNeg
478
479 # Generate the diffIm model
480 diffIm = afwImage.ImageF(bbox)
481 diffIm += posIm
482 diffIm -= negIm
483
484 zout = diffIm.array
485 if rel_weight > 0.:
486 zout = np.append([zout], [posIm.array, negIm.array], axis=0)
487
488 return zout
489
490

◆ makeStarModel()

lsst.ip.diffim.dipoleFitTask.DipoleModel.makeStarModel ( self,
bbox,
psf,
xcen,
ycen,
flux )
Generate a 2D image model of a single PDF centered at the given coordinates.

Parameters
----------
bbox : `lsst.geom.Box`
    Bounding box marking pixel coordinates for generated model
psf : TODO: DM-17458
    Psf model used to generate the 'star'
xcen : `float`
    Desired x-centroid of the 'star'
ycen : `float`
    Desired y-centroid of the 'star'
flux : `float`
    Desired flux of the 'star'

Returns
-------
p_Im : `lsst.afw.image.Image`
    2-d stellar image of width/height matching input ``bbox``,
    containing PSF with given centroid and flux

Definition at line 353 of file dipoleFitTask.py.

353 def makeStarModel(self, bbox, psf, xcen, ycen, flux):
354 """Generate a 2D image model of a single PDF centered at the given coordinates.
355
356 Parameters
357 ----------
358 bbox : `lsst.geom.Box`
359 Bounding box marking pixel coordinates for generated model
360 psf : TODO: DM-17458
361 Psf model used to generate the 'star'
362 xcen : `float`
363 Desired x-centroid of the 'star'
364 ycen : `float`
365 Desired y-centroid of the 'star'
366 flux : `float`
367 Desired flux of the 'star'
368
369 Returns
370 -------
371 p_Im : `lsst.afw.image.Image`
372 2-d stellar image of width/height matching input ``bbox``,
373 containing PSF with given centroid and flux
374 """
375
376 # Generate the psf image, normalize to flux
377 psf_img = psf.computeImage(geom.Point2D(xcen, ycen)).convertF()
378 psf_img_sum = np.nansum(psf_img.array)
379 psf_img *= (flux/psf_img_sum)
380
381 # Clip the PSF image bounding box to fall within the footprint bounding box
382 psf_box = psf_img.getBBox()
383 psf_box.clip(bbox)
384 psf_img = afwImage.ImageF(psf_img, psf_box, afwImage.PARENT)
385
386 # Then actually crop the psf image.
387 # Usually not necessary, but if the dipole is near the edge of the image...
388 # Would be nice if we could compare original pos_box with clipped pos_box and
389 # see if it actually was clipped.
390 p_Im = afwImage.ImageF(bbox)
391 tmpSubim = afwImage.ImageF(p_Im, psf_box, afwImage.PARENT)
392 tmpSubim += psf_img
393
394 return p_Im
395

Member Data Documentation

◆ debug

lsst.ip.diffim.dipoleFitTask.DipoleModel.debug

Definition at line 202 of file dipoleFitTask.py.

◆ log

lsst.ip.diffim.dipoleFitTask.DipoleModel.log

Definition at line 203 of file dipoleFitTask.py.


The documentation for this class was generated from the following file: