Ejemplo n.º 1
0
    def run(self, exposure, defects=None, keepCRs=None):
        """!Repair an Exposure's defects and cosmic rays

        \param[in, out] exposure  lsst.afw.image.Exposure to process.  Exposure must have a valid Psf.
                                  Modified in place.
        \param[in]      defects   an lsst.meas.algorithms.DefectListT object.  If None, do no
                                  defect correction.
        \param[in]      keepCRs   don't interpolate over the CR pixels (defer to RepairConfig if None)

        \throws AssertionError with the following strings:

        <DL>
          <DT> No exposure provided
          <DD> The object provided as exposure evaluates to False
          <DT> No PSF provided
          <DD> The Exposure has no associated Psf
        </DL>
        """
        assert exposure, "No exposure provided"
        psf = exposure.getPsf()
        assert psf, "No PSF provided"

        frame = getDebugFrame(self._display, "repair.before")
        if frame:
            getDisplay(frame).mtv(exposure)

        if defects is not None and self.config.doInterpolate:
            self.interp.run(exposure, defects=defects)

        if self.config.doCosmicRay:
            self.cosmicRay(exposure, keepCRs=keepCRs)

        frame = getDebugFrame(self._display, "repair.after")
        if frame:
            getDisplay(frame).mtv(exposure)
Ejemplo n.º 2
0
    def run(self, exposure, defects=None, keepCRs=None):
        """!Repair an Exposure's defects and cosmic rays

        @param[in, out] exposure  lsst.afw.image.Exposure to process.  Exposure must have a valid Psf.
                                  Modified in place.
        @param[in]      defects   an lsst.meas.algorithms.DefectListT object.  If None, do no
                                  defect correction.
        @param[in]      keepCRs   don't interpolate over the CR pixels (defer to RepairConfig if None)

        @throws AssertionError with the following strings:

        <DL>
          <DT> No exposure provided
          <DD> The object provided as exposure evaluates to False
          <DT> No PSF provided
          <DD> The Exposure has no associated Psf
        </DL>
        """
        assert exposure, "No exposure provided"
        psf = exposure.getPsf()
        assert psf, "No PSF provided"

        frame = getDebugFrame(self._display, "repair.before")
        if frame:
            afwDisplay.Display(frame).mtv(exposure)

        if defects is not None and self.config.doInterpolate:
            self.interp.run(exposure, defects=defects)

        if self.config.doCosmicRay:
            self.cosmicRay(exposure, keepCRs=keepCRs)

        frame = getDebugFrame(self._display, "repair.after")
        if frame:
            afwDisplay.Display(frame).mtv(exposure)
Ejemplo n.º 3
0
    def runDataRef(self, sensorRef):
        """Do instrument signature removal on an exposure

        Correct for saturation, bias, overscan, dark, flat..., perform CCD assembly,
        optionally combine snaps, and interpolate over defects and saturated pixels.

        If config.doSnapCombine true then combine the two ISR-corrected snaps to produce the final exposure.
        If config.doSnapCombine false then uses ISR-corrected snap 0 as the final exposure.
        In either case, the final exposure is persisted as "postISRCCD" if config.doWriteSpans is True,
        and the two snaps are persisted as "snapExp" if config.doWriteSnaps is True.

        @param sensorRef daf.persistence.butlerSubset.ButlerDataRef of the data to be processed
        @return a pipeBase.Struct with fields:
        - exposure: the exposure after application of ISR
        """
        self.log.info("Performing ISR on sensor %s", sensorRef.dataId)
        camera = sensorRef.get("camera")
        snapDict = dict()
        for snapRef in sensorRef.subItems(level="snap"):
            snapId = snapRef.dataId['snap']
            if snapId not in (0, 1):
                raise RuntimeError("Unrecognized snapId=%s" % (snapId, ))

            self.log.info("Performing ISR on snap %s", snapRef.dataId)
            ccdExposure = snapRef.get('raw')
            isrData = self.readIsrData(snapRef, ccdExposure)
            ccdExposure = self.run(ccdExposure,
                                   camera=camera,
                                   **isrData.getDict()).exposure
            snapDict[snapId] = ccdExposure

            if self.config.doWriteSnaps:
                sensorRef.put(ccdExposure, "snapExp", snap=snapId)

            frame = getDebugFrame(self._display, "snapExp%d" % (snapId, ))
            if frame:
                getDisplay(frame).mtv(ccdExposure)

        if self.config.doSnapCombine:
            loadSnapDict(snapDict, snapIdList=(0, 1), sensorRef=sensorRef)
            postIsrExposure = self.snapCombine.run(snapDict[0],
                                                   snapDict[1]).exposure
        else:
            self.log.warn("doSnapCombine false; using snap 0 as the result")
            loadSnapDict(snapDict, snapIdList=(0, ), sensorRef=sensorRef)
            postIsrExposure = snapDict[0]

        if self.config.doWrite:
            sensorRef.put(postIsrExposure, "postISRCCD")

        frame = getDebugFrame(self._display, "postISRCCD")
        if frame:
            getDisplay(frame).mtv(postIsrExposure)

        return pipeBase.Struct(exposure=postIsrExposure, )
Ejemplo n.º 4
0
    def runDataRef(self, sensorRef):
        """Do instrument signature removal on an exposure

        Correct for saturation, bias, overscan, dark, flat..., perform CCD assembly,
        optionally combine snaps, and interpolate over defects and saturated pixels.

        If config.doSnapCombine true then combine the two ISR-corrected snaps to produce the final exposure.
        If config.doSnapCombine false then uses ISR-corrected snap 0 as the final exposure.
        In either case, the final exposure is persisted as "postISRCCD" if config.doWriteSpans is True,
        and the two snaps are persisted as "snapExp" if config.doWriteSnaps is True.

        @param sensorRef daf.persistence.butlerSubset.ButlerDataRef of the data to be processed
        @return a pipeBase.Struct with fields:
        - exposure: the exposure after application of ISR
        """
        self.log.info("Performing ISR on sensor %s", sensorRef.dataId)
        camera = sensorRef.get("camera")
        snapDict = dict()
        for snapRef in sensorRef.subItems(level="snap"):
            snapId = snapRef.dataId['snap']
            if snapId not in (0, 1):
                raise RuntimeError("Unrecognized snapId=%s" % (snapId,))

            self.log.info("Performing ISR on snap %s", snapRef.dataId)
            ccdExposure = snapRef.get('raw')
            isrData = self.readIsrData(snapRef, ccdExposure)
            ccdExposure = self.run(ccdExposure, camera=camera, **isrData.getDict()).exposure
            snapDict[snapId] = ccdExposure

            if self.config.doWriteSnaps:
                sensorRef.put(ccdExposure, "snapExp", snap=snapId)

            frame = getDebugFrame(self._display, "snapExp%d" % (snapId,))
            if frame:
                getDisplay(frame).mtv(ccdExposure)

        if self.config.doSnapCombine:
            loadSnapDict(snapDict, snapIdList=(0, 1), sensorRef=sensorRef)
            postIsrExposure = self.snapCombine.run(snapDict[0], snapDict[1]).exposure
        else:
            self.log.warn("doSnapCombine false; using snap 0 as the result")
            loadSnapDict(snapDict, snapIdList=(0,), sensorRef=sensorRef)
            postIsrExposure = snapDict[0]

        if self.config.doWrite:
            sensorRef.put(postIsrExposure, "postISRCCD")

        frame = getDebugFrame(self._display, "postISRCCD")
        if frame:
            getDisplay(frame).mtv(postIsrExposure)

        return pipeBase.Struct(
            exposure=postIsrExposure,
        )
Ejemplo n.º 5
0
    def run(self, exposure, background=None, stats=True, statsKeys=None):
        """Fit and subtract the background of an exposure.

        Parameters
        ----------
        exposure : `lsst.afw.image.Exposure`
            Exposure whose background is to be subtracted.
        background : `lsst.afw.math.BackgroundList`
            Initial background model already subtracted. May be None if no background
            has been subtracted.
        stats : `bool`
            If True then measure the mean and variance of the full background model and
            record the results in the exposure's metadata.
        statsKeys : `tuple`
            Key names used to store the mean and variance of the background in the
            exposure's metadata (another tuple); if None then use ("BGMEAN", "BGVAR");
            ignored if stats is false.

        Returns
        -------
        background : `lsst.afw.math.BackgroundLst`
            Full background model (initial model with changes), contained in an
            `lsst.pipe.base.Struct`.
        """
        if background is None:
            background = afwMath.BackgroundList()

        maskedImage = exposure.getMaskedImage()
        fitBg = self.fitBackground(maskedImage)
        maskedImage -= fitBg.getImageF(self.config.algorithm, self.config.undersampleStyle)

        actrl = fitBg.getBackgroundControl().getApproximateControl()
        background.append((fitBg, getattr(afwMath.Interpolate, self.config.algorithm),
                           fitBg.getAsUsedUndersampleStyle(), actrl.getStyle(),
                           actrl.getOrderX(), actrl.getOrderY(), actrl.getWeighting()))

        if stats:
            self._addStats(exposure, background, statsKeys=statsKeys)

        subFrame = getDebugFrame(self._display, "subtracted")
        if subFrame:
            subDisp = afwDisplay.getDisplay(frame=subFrame)
            subDisp.mtv(exposure, title="subtracted")

        bgFrame = getDebugFrame(self._display, "background")
        if bgFrame:
            bgDisp = afwDisplay.getDisplay(frame=bgFrame)
            bgImage = background.getImage()
            bgDisp.mtv(bgImage, title="background")

        return pipeBase.Struct(
            background=background,
        )
Ejemplo n.º 6
0
    def postprocessExposure(self, outExposure, inExposure):
        """Set exposure non-image attributes, including wcs and metadata and display exposure (if requested)
        
        Call after assembling the pixels
        
        @param[in,out]  outExposure assembled exposure:
                                    - removes unwanted keywords
                                    - sets calib, filter, and detector
        @param[in]      inExposure  input exposure
        """
        self.setWcs(outExposure = outExposure, inExposure = inExposure)

        exposureMetadata = inExposure.getMetadata()
        for key in self.allKeysToRemove:
            if exposureMetadata.exists(key):
                exposureMetadata.remove(key)
        outExposure.setMetadata(exposureMetadata)

        if self.config.setGain:
            self.setGain(outExposure = outExposure)

        inCalib = inExposure.getCalib()
        outCalib = outExposure.getCalib()
        outCalib.setExptime(inCalib.getExptime())
        outCalib.setMidTime(inCalib.getMidTime())

        outExposure.setFilter(inExposure.getFilter())

        frame = getDebugFrame(self._display, "assembledExposure")
        if frame:
            getDisplay(frame).mtv(outExposure)
Ejemplo n.º 7
0
    def postprocessExposure(self, outExposure, inExposure):
        """Set exposure non-image attributes, including wcs and metadata and display exposure (if requested)
        
        Call after assembling the pixels
        
        @param[in,out]  outExposure assembled exposure:
                                    - removes unwanted keywords
                                    - sets calib, filter, and detector
        @param[in]      inExposure  input exposure
        """
        self.setWcs(outExposure = outExposure, inExposure = inExposure)

        exposureMetadata = inExposure.getMetadata()
        for key in self.allKeysToRemove:
            if exposureMetadata.exists(key):
                exposureMetadata.remove(key)
        outExposure.setMetadata(exposureMetadata)

        if self.config.setGain:
            self.setGain(outExposure = outExposure)

        inCalib = inExposure.getCalib()
        outCalib = outExposure.getCalib()
        outCalib.setExptime(inCalib.getExptime())
        outCalib.setMidTime(inCalib.getMidTime())

        outExposure.setFilter(inExposure.getFilter())

        frame = getDebugFrame(self._display, "assembledExposure")
        if frame:
            getDisplay(frame).mtv(outExposure)
Ejemplo n.º 8
0
    def postprocessExposure(self, outExposure, inExposure):
        """Set exposure non-image attributes, including wcs and metadata and display exposure (if requested)

        Call after assembling the pixels

        @param[in,out]  outExposure assembled exposure:
                                    - removes unwanted keywords
                                    - sets photoCalib, filter, and detector
        @param[in]      inExposure  input exposure
        """
        self.setWcs(outExposure=outExposure, inExposure=inExposure)

        exposureMetadata = inExposure.getMetadata()
        for key in self.allKeysToRemove:
            if exposureMetadata.exists(key):
                exposureMetadata.remove(key)
        outExposure.setMetadata(exposureMetadata)

        # note: don't copy PhotoCalib, because it is assumed to be unknown in raw data
        outExposure.setFilter(inExposure.getFilter())
        outExposure.getInfo().setVisitInfo(inExposure.getInfo().getVisitInfo())

        frame = getDebugFrame(self._display, "assembledExposure")
        if frame:
            afwDisplay.Display(frame=frame).mtv(outExposure, title="postprocessExposure")
Ejemplo n.º 9
0
    def __init__(self, butler=None, refObjLoader=None, schema=None, **kwargs):
        """!Construct a CharacterizeImageTask

        @param[in] butler  A butler object is passed to the refObjLoader constructor in case
            it is needed to load catalogs.  May be None if a catalog-based star selector is
            not used, if the reference object loader constructor does not require a butler,
            or if a reference object loader is passed directly via the refObjLoader argument.
        @param[in] refObjLoader  An instance of LoadReferenceObjectsTasks that supplies an
            external reference catalog to a catalog-based star selector.  May be None if a
            catalog star selector is not used or the loader can be constructed from the
            butler argument.
        @param[in,out] schema  initial schema (an lsst.afw.table.SourceTable), or None
        @param[in,out] kwargs  other keyword arguments for lsst.pipe.base.CmdLineTask
        """
        pipeBase.CmdLineTask.__init__(self, **kwargs)
        if schema is None:
            schema = SourceTable.makeMinimalSchema()
        self.schema = schema
        self.makeSubtask("background")
        self.makeSubtask("installSimplePsf")
        self.makeSubtask("repair")
        self.makeSubtask("measurePsf", schema=self.schema)
        if self.config.doMeasurePsf and self.measurePsf.usesMatches:
            if not refObjLoader:
                self.makeSubtask('refObjLoader', butler=butler)
                refObjLoader = self.refObjLoader
            self.makeSubtask("astrometry", refObjLoader=refObjLoader)
        self.makeSubtask("detectAndMeasure", schema=self.schema)
        self._initialFrame = getDebugFrame(self._display, "frame") or 1
        self._frame = self._initialFrame
        self.schema.checkUnits(parse_strict=self.config.checkUnitsParseStrict)
Ejemplo n.º 10
0
    def postprocessExposure(self, outExposure, inExposure):
        """Set exposure non-image attributes, including wcs and metadata and display exposure (if requested)

        Call after assembling the pixels

        @param[in,out]  outExposure assembled exposure:
                                    - removes unwanted keywords
                                    - sets wcs, filter, and detector
        @param[in]      inExposure  input exposure
        """
        if inExposure.hasWcs():
            outExposure.setWcs(inExposure.getWcs())

        exposureMetadata = inExposure.getMetadata()
        for key in self.allKeysToRemove:
            if exposureMetadata.exists(key):
                exposureMetadata.remove(key)
        outExposure.setMetadata(exposureMetadata)

        # note: don't copy PhotoCalib, because it is assumed to be unknown in raw data
        outExposure.setFilterLabel(inExposure.getFilterLabel())
        outExposure.getInfo().setVisitInfo(inExposure.getInfo().getVisitInfo())

        frame = getDebugFrame(self._display, "assembledExposure")
        if frame:
            afwDisplay.Display(frame=frame).mtv(outExposure,
                                                title="postprocessExposure")
Ejemplo n.º 11
0
    def postprocessExposure(self, outExposure, inExposure):
        """Set exposure non-image attributes, including wcs and metadata and display exposure (if requested)

        Call after assembling the pixels

        @param[in,out]  outExposure assembled exposure:
                                    - removes unwanted keywords
                                    - sets calib, filter, and detector
        @param[in]      inExposure  input exposure
        """
        self.setWcs(outExposure=outExposure, inExposure=inExposure)

        exposureMetadata = inExposure.getMetadata()
        for key in self.allKeysToRemove:
            if exposureMetadata.exists(key):
                exposureMetadata.remove(key)
        outExposure.setMetadata(exposureMetadata)

        # note: Calib is not copied, presumably because it is assumed unknown in raw data
        outExposure.setFilter(inExposure.getFilter())
        outExposure.getInfo().setVisitInfo(inExposure.getInfo().getVisitInfo())

        frame = getDebugFrame(self._display, "assembledExposure")
        if frame:
            getDisplay(frame).mtv(outExposure)
Ejemplo n.º 12
0
    def debugRatios(self, stepname, ratios, i, j, coeff=0.0, valid=False):
        """Utility function to examine the final CT ratio set.

        Parameters
        ----------
        stepname : `str`
            State of processing to view.
        ratios : `dict` of `dict` of `np.ndarray`
            Array of measured CT ratios, indexed by source/victim
            amplifier.
        i : `str`
            Index of the source amplifier.
        j : `str`
            Index of the target amplifier.
        coeff : `float`, optional
            Coefficient calculated to plot along with the simple mean.
        valid : `bool`, optional
            Validity to be added to the plot title.
        """
        frame = getDebugFrame(self._display, stepname)
        if frame:
            if i == j or ratios is None or len(ratios) < 1:
                pass

            ratioList = ratios[i][j]
            if ratioList is None or len(ratioList) < 1:
                pass

            mean = np.mean(ratioList)
            std = np.std(ratioList)
            import matplotlib.pyplot as plt
            figure = plt.figure(1)
            figure.clear()
            plt.hist(x=ratioList, bins=len(ratioList),
                     cumulative=True, color='b', density=True, histtype='step')
            plt.xlabel("Measured pixel ratio")
            plt.ylabel(f"CDF: n={len(ratioList)}")
            plt.xlim(np.percentile(ratioList, [1.0, 99]))
            plt.axvline(x=mean, color="k")
            plt.axvline(x=coeff, color='g')
            plt.axvline(x=(std / np.sqrt(len(ratioList))), color='r')
            plt.axvline(x=-(std / np.sqrt(len(ratioList))), color='r')
            plt.title(f"(Source {i} -> Target {j}) mean: {mean:.2g} coeff: {coeff:.2g} valid: {valid}")
            figure.show()

            prompt = "Press Enter to continue: "
            while True:
                ans = input(prompt).lower()
                if ans in ("", "c",):
                    break
                elif ans in ("pdb", "p",):
                    import pdb
                    pdb.set_trace()
            plt.close()
Ejemplo n.º 13
0
    def display(self, itemName, exposure, sourceCat=None):
        """Display exposure and sources on next frame, if display of itemName has been requested
        @param[in] itemName  name of item in debugInfo
        @param[in] exposure  exposure to display
        @param[in] sourceCat  source catalog to display
        """
        val = getDebugFrame(self._display, itemName)
        if not val:
            return

        displayAstrometry(exposure=exposure, sourceCat=sourceCat, frame=self._frame, pause=False)
        self._frame += 1
Ejemplo n.º 14
0
    def run(self, exposure, background=None, stats=True, statsKeys=None):
        """!Fit and subtract the background of an exposure

        @param[in,out] exposure  exposure whose background is to be subtracted
        @param[in,out] background  initial background model already subtracted from exposure
            (an lsst.afw.math.BackgroundList). May be None if no background has been subtracted.
        @param[in] stats  if True then measure the mean and variance of the full background model
                        and record the results in the exposure's metadata
        @param[in] statsKeys  key names used to store the mean and variance of the background
            in the exposure's metadata (a pair of strings); if None then use ("BGMEAN", "BGVAR");
            ignored if stats is false

        @return an lsst.pipe.base.Struct containing:
        - background  full background model (initial model with changes), an lsst.afw.math.BackgroundList
        """
        if background is None:
            background = afwMath.BackgroundList()

        maskedImage = exposure.getMaskedImage()
        fitBg = self.fitBackground(maskedImage)
        maskedImage -= fitBg.getImageF()
        background.append(fitBg)

        if stats:
            self._addStats(exposure, background, statsKeys=statsKeys)

        subFrame = getDebugFrame(self._display, "subtracted")
        if subFrame:
            subDisp = afwDisplay.getDisplay(frame=subFrame)
            subDisp.mtv(exposure, title="subtracted")

        bgFrame = getDebugFrame(self._display, "background")
        if bgFrame:
            bgDisp = afwDisplay.getDisplay(frame=bgFrame)
            bgImage = background.getImage()
            bgDisp.mtv(bgImage, title="background")

        return pipeBase.Struct(
            background = background,
        )
Ejemplo n.º 15
0
    def run(self, exposure, background=None, stats=True, statsKeys=None):
        """!Fit and subtract the background of an exposure

        @param[in,out] exposure  exposure whose background is to be subtracted
        @param[in,out] background  initial background model already subtracted from exposure
            (an lsst.afw.math.BackgroundList). May be None if no background has been subtracted.
        @param[in] stats  if True then measure the mean and variance of the full background model
                        and record the results in the exposure's metadata
        @param[in] statsKeys  key names used to store the mean and variance of the background
            in the exposure's metadata (a pair of strings); if None then use ("BGMEAN", "BGVAR");
            ignored if stats is false

        @return an lsst.pipe.base.Struct containing:
        - background  full background model (initial model with changes), an lsst.afw.math.BackgroundList
        """
        if background is None:
            background = afwMath.BackgroundList()

        maskedImage = exposure.getMaskedImage()
        fitBg = self.fitBackground(maskedImage)
        maskedImage -= fitBg.getImageF()
        background.append(fitBg)

        if stats:
            self._addStats(exposure, background, statsKeys=statsKeys)

        subFrame = getDebugFrame(self._display, "subtracted")
        if subFrame:
            subDisp = afwDisplay.getDisplay(frame=subFrame)
            subDisp.mtv(exposure, title="subtracted")

        bgFrame = getDebugFrame(self._display, "background")
        if bgFrame:
            bgDisp = afwDisplay.getDisplay(frame=bgFrame)
            bgImage = background.getImage()
            bgDisp.mtv(bgImage, title="background")

        return pipeBase.Struct(
            background=background,
        )
Ejemplo n.º 16
0
    def display(self, itemName, exposure, sourceCat=None):
        """Display exposure and sources on next frame, if display of itemName has been requested

        @param[in] itemName  name of item in debugInfo
        @param[in] exposure  exposure to display
        @param[in] sourceCat  source catalog to display
        """
        val = getDebugFrame(self._display, itemName)
        if not val:
            return

        displayAstrometry(exposure=exposure, sourceCat=sourceCat, frame=self._frame, pause=False)
        self._frame += 1
Ejemplo n.º 17
0
    def debugRatios(self, stepname, ratios, i, j):
        """Utility function to examine the final CT ratio set.

        Parameters
        ----------
        stepname : `str`
            State of processing to view.
        ratios : `List` of `List` of `np.ndarray`
            Array of measured CT ratios, indexed by source/victim
            amplifier.
        i : `int`
            Index of the source amplifier.
        j : `int`
            Index of the target amplifier.
        """
        frame = getDebugFrame(self._display, stepname)
        if frame:
            if i == j or ratios is None or len(ratios) < 1:
                pass

            RR = ratios[i][j]
            if RR is None or len(RR) < 1:
                pass

            value = np.mean(RR)

            import matplotlib.pyplot as plot
            figure = plot.figure(1)
            figure.clear()
            plot.hist(x=RR, bins='auto', color='b', rwidth=0.9)
            plot.xlabel("Measured pixel ratio")
            plot.axvline(x=value, color="k")
            plot.title("(Source %d -> Victim %d) clipped mean ratio: %f" %
                       (i, j, value))
            figure.show()

            prompt = "Press Enter to continue: "
            while True:
                ans = input(prompt).lower()
                if ans in (
                        "",
                        "c",
                ):
                    break
            plot.close()
Ejemplo n.º 18
0
    def __init__(self, butler=None, refObjLoader=None, schema=None, **kwargs):
        """!Construct a CharacterizeImageTask

        @param[in] butler  A butler object is passed to the refObjLoader constructor in case
            it is needed to load catalogs.  May be None if a catalog-based star selector is
            not used, if the reference object loader constructor does not require a butler,
            or if a reference object loader is passed directly via the refObjLoader argument.
        # TODO DM-34769: remove rebObjLoader kwarg here.
        @param[in] refObjLoader  An instance of LoadReferenceObjectsTasks that supplies an
            external reference catalog to a catalog-based star selector.  May be None if a
            catalog star selector is not used or the loader can be constructed from the
            butler argument.
        @param[in,out] schema  initial schema (an lsst.afw.table.SourceTable), or None
        @param[in,out] kwargs  other keyword arguments for lsst.pipe.base.CmdLineTask
        """
        super().__init__(**kwargs)

        if schema is None:
            schema = SourceTable.makeMinimalSchema()
        self.schema = schema
        self.makeSubtask("background")
        self.makeSubtask("installSimplePsf")
        self.makeSubtask("repair")
        self.makeSubtask("measurePsf", schema=self.schema)
        # TODO DM-34769: remove this `if` block
        if self.config.doMeasurePsf and self.measurePsf.usesMatches:
            if not refObjLoader:
                self.makeSubtask('refObjLoader', butler=butler)
                refObjLoader = self.refObjLoader
            self.makeSubtask("ref_match", refObjLoader=refObjLoader)
        self.algMetadata = dafBase.PropertyList()
        self.makeSubtask('detection', schema=self.schema)
        if self.config.doDeblend:
            self.makeSubtask("deblend", schema=self.schema)
        self.makeSubtask('measurement',
                         schema=self.schema,
                         algMetadata=self.algMetadata)
        if self.config.doApCorr:
            self.makeSubtask('measureApCorr', schema=self.schema)
            self.makeSubtask('applyApCorr', schema=self.schema)
        self.makeSubtask('catalogCalculation', schema=self.schema)
        self._initialFrame = getDebugFrame(self._display, "frame") or 1
        self._frame = self._initialFrame
        self.schema.checkUnits(parse_strict=self.config.checkUnitsParseStrict)
        self.outputSchema = afwTable.SourceCatalog(self.schema)
    def __init__(self, schema=None, **kwargs):
        """!Construct a CharacterizeImageTask

        @param[in,out] schema  initial schema (an lsst.afw.table.SourceTable), or None
        @param[in,out] kwargs  other keyword arguments for lsst.pipe.base.CmdLineTask
        """
        pipeBase.CmdLineTask.__init__(self, **kwargs)
        if schema is None:
            schema = SourceTable.makeMinimalSchema()
        self.schema = schema
        self.makeSubtask("installSimplePsf")
        self.makeSubtask("repair")
        self.makeSubtask("measurePsf", schema=self.schema)
        if self.config.doMeasurePsf and self.measurePsf.usesMatches:
            self.makeSubtask("astrometry")
        self.makeSubtask("detectAndMeasure", schema=self.schema)
        self._initialFrame = getDebugFrame(self._display, "frame") or 1
        self._frame = self._initialFrame
Ejemplo n.º 20
0
    def __init__(self, schema=None, **kwargs):
        """!Construct a CharacterizeImageTask

        @param[in,out] schema  initial schema (an lsst.afw.table.SourceTable), or None
        @param[in,out] kwargs  other keyword arguments for lsst.pipe.base.CmdLineTask
        """
        pipeBase.CmdLineTask.__init__(self, **kwargs)
        if schema is None:
            schema = SourceTable.makeMinimalSchema()
        self.schema = schema
        self.makeSubtask("installSimplePsf")
        self.makeSubtask("repair")
        self.makeSubtask("measurePsf", schema=self.schema)
        if self.config.doMeasurePsf and self.measurePsf.usesMatches:
            self.makeSubtask("astrometry")
        self.makeSubtask("detectAndMeasure", schema=self.schema)
        self._initialFrame = getDebugFrame(self._display, "frame") or 1
        self._frame = self._initialFrame
Ejemplo n.º 21
0
    def debugView(self, stepname, ampImage, defects, detector):
        # def _plotDefects(self, exp, visit, defects, imageType):  # pragma: no cover
        """Plot the defects found by the task.

        Parameters
        ----------
        exp : `lsst.afw.image.exposure.Exposure`
            The exposure in which the defects were found.
        visit : `int`
            The visit number.
        defects : `lsst.ip.isr.Defect`
            The defects to plot.
        imageType : `str`
            The type of image, either 'dark' or 'flat'.
        """
        frame = getDebugFrame(self._display, stepname)
        if frame:
            disp = afwDisplay.Display(frame=frame)
            disp.scale('asinh', 'zscale')
            disp.setMaskTransparency(80)
            disp.setMaskPlaneColor("BAD", afwDisplay.RED)

            maskedIm = ampImage.clone()
            defects.maskPixels(maskedIm, "BAD")

            mpDict = maskedIm.mask.getMaskPlaneDict()
            for plane in mpDict.keys():
                if plane in ['BAD']:
                    continue
                disp.setMaskPlaneColor(plane, afwDisplay.IGNORE)

            disp.setImageColormap('gray')
            disp.mtv(maskedIm)
            cameraGeom.utils.overlayCcdBoxes(detector,
                                             isTrimmed=True,
                                             display=disp)
            prompt = "Press Enter to continue [c]... "
            while True:
                ans = input(prompt).lower()
                if ans in (
                        '',
                        'c',
                ):
                    break
Ejemplo n.º 22
0
    def debugFit(self, stepname, xVector, yVector, yModel):
        """Debug method for linearity fitting.
        Parameters
        ----------
        stepname : `str`
            A label to use to check if we care to debug at a given
            line of code.
        xVector : `numpy.array`, (N,)
            The values to use as the independent variable in the
            linearity fit.
        yVector : `numpy.array`, (N,)
            The values to use as the dependent variable in the
            linearity fit.
        yModel : `numpy.array`, (N,)
            The values to use as the linearized result.
        """
        frame = getDebugFrame(self._display, stepname)
        if frame:
            import matplotlib.pyplot as plt
            figure, axes = plt.subplots(1)

            axes.scatter(xVector, yVector, c='blue', marker='+')
            modelX = np.arange(np.min(xVector) - 1, np.max(xVector) + 1, 0.1)
            axes.plot(modelX, exponentialModel(modelX, *yModel), 'r-')
            plt.xlabel("Instrumental PSF magnitude")
            plt.ylabel("Source size trace")
            plt.title(f"BFK slope: {yModel[0]:.3f} + {yModel[1]:.3f} m")
            figure.show()
            prompt = "Press Enter or c to continue [chp]..."

            while True:
                ans = input(prompt).lower()
                if ans in (
                        "",
                        " ",
                        "c",
                ):
                    break
                elif ans in ("p", ):
                    import pdb
                    pdb.set_trace()
                elif ans in ("h", ):
                    print("[h]elp [c]ontinue [p]db e[x]itDebug")
            plt.close()
Ejemplo n.º 23
0
    def debugPixels(self, stepname, pixelsIn, pixelsOut, i, j):
        """Utility function to examine the CT ratio pixel values.

        Parameters
        ----------
        stepname : `str`
            State of processing to view.
        pixelsIn : `np.ndarray`
            Pixel values from the potential crosstalk "source".
        pixelsOut : `np.ndarray`
            Pixel values from the potential crosstalk "victim".
        i : `int`
            Index of the source amplifier.
        j : `int`
            Index of the target amplifier.
        """
        frame = getDebugFrame(self._display, stepname)
        if frame:
            if i == j or len(pixelsIn) == 0 or len(pixelsOut) < 1:
                pass
            import matplotlib.pyplot as plot
            figure = plot.figure(1)
            figure.clear()

            axes = figure.add_axes((0.1, 0.1, 0.8, 0.8))
            axes.plot(pixelsIn, pixelsOut / pixelsIn, 'k+')
            plot.xlabel("Source amplifier pixel value")
            plot.ylabel("Measured pixel ratio")
            plot.title("(Source %d -> Victim %d) median ratio: %f" %
                       (i, j, np.median(pixelsOut / pixelsIn)))
            figure.show()

            prompt = "Press Enter to continue: "
            while True:
                ans = input(prompt).lower()
                if ans in (
                        "",
                        "c",
                ):
                    break
            plot.close()
Ejemplo n.º 24
0
    def debugView(self, stepname, exposure):
        """Utility function to examine the image being processed.

        Parameters
        ----------
        stepname : `str`
            State of processing to view.
        exposure : `lsst.afw.image.Exposure`
            Exposure to view.
        """
        frame = getDebugFrame(self._display, stepname)
        if frame:
            display = getDisplay(frame)
            display.scale('asinh', 'zscale')
            display.mtv(exposure)

            prompt = "Press Enter to continue: "
            while True:
                ans = input(prompt).lower()
                if ans in ("", "c",):
                    break
Ejemplo n.º 25
0
    def __init__(self, butler=None, schema=None, **kwargs):
        """!Construct a CharacterizeSpotsTask
        @param[in] butler  A butler object is passed to the refObjLoader constructor in case
            it is needed to load catalogs.  May be None if a catalog-based star selector is
            not used, if the reference object loader constructor does not require a butler,
            or if a reference object loader is passed directly via the refObjLoader argument.
        @param[in,out] schema  initial schema (an lsst.afw.table.SourceTable), or None
        @param[in,out] kwargs  other keyword arguments for lsst.pipe.base.CmdLineTask
        """
        super().__init__(**kwargs)

        if schema is None:
            schema = SourceTable.makeMinimalSchema()
        self.schema = schema
        self.makeSubtask("installSimplePsf")
        self.makeSubtask("repair")
        self.algMetadata = dafBase.PropertyList()
        self.makeSubtask('measurement', schema=self.schema, algMetadata=self.algMetadata)
        self.makeSubtask('catalogCalculation', schema=self.schema)
        self._initialFrame = getDebugFrame(self._display, "frame") or 1
        self._frame = self._initialFrame
        self.schema.checkUnits(parse_strict=self.config.checkUnitsParseStrict)
        self.outputSchema = afwTable.SourceCatalog(self.schema)
Ejemplo n.º 26
0
    def debugPixels(self, stepname, pixelsIn, pixelsOut, sourceName, targetName):
        """Utility function to examine the CT ratio pixel values.

        Parameters
        ----------
        stepname : `str`
            State of processing to view.
        pixelsIn : `np.ndarray`
            Pixel values from the potential crosstalk source.
        pixelsOut : `np.ndarray`
            Pixel values from the potential crosstalk target.
        sourceName : `str`
            Source amplifier name
        targetName : `str`
            Target amplifier name
        """
        frame = getDebugFrame(self._display, stepname)
        if frame:
            import matplotlib.pyplot as plt
            figure = plt.figure(1)
            figure.clear()

            axes = figure.add_axes((0.1, 0.1, 0.8, 0.8))
            axes.plot(pixelsIn, pixelsOut / pixelsIn, 'k+')
            plt.xlabel("Source amplifier pixel value")
            plt.ylabel("Measured pixel ratio")
            plt.title(f"(Source {sourceName} -> Target {targetName}) median ratio: "
                      f"{(np.median(pixelsOut / pixelsIn))}")
            figure.show()

            prompt = "Press Enter to continue: "
            while True:
                ans = input(prompt).lower()
                if ans in ("", "c",):
                    break
            plt.close()
Ejemplo n.º 27
0
    def debugFit(self, stepname, xVector, yVector, yModel, mask, ampName):
        """Debug method for linearity fitting.

        Parameters
        ----------
        stepname : `str`
            A label to use to check if we care to debug at a given
            line of code.
        xVector : `numpy.array`
            The values to use as the independent variable in the
            linearity fit.
        yVector : `numpy.array`
            The values to use as the dependent variable in the
            linearity fit.
        yModel : `numpy.array`
            The values to use as the linearized result.
        mask : `numpy.array` [ `bool` ], optional
            A mask to indicate which entries of ``xVector`` and
            ``yVector`` to keep.
        ampName : `str`
            Amplifier name to lookup linearity correction values.

        """
        frame = getDebugFrame(self._display, stepname)
        if frame:
            import matplotlib.pyplot as plt
            fig, axs = plt.subplots(2)

            if mask is None:
                mask = np.ones_like(xVector, dtype=bool)

            fig.suptitle(f"{stepname} {ampName} {self.config.linearityType}")
            if stepname == 'linearFit':
                axs[0].set_xlabel("Input Abscissa (time or mondiode)")
                axs[0].set_ylabel("Input Ordinate (flux)")
                axs[1].set_xlabel("Linear Ordinate (linear flux)")
                axs[1].set_ylabel("Flux Difference: (input - linear)")
            elif stepname in ('polyFit', 'splineFit'):
                axs[0].set_xlabel("Linear Abscissa (linear flux)")
                axs[0].set_ylabel("Input Ordinate (flux)")
                axs[1].set_xlabel("Linear Ordinate (linear flux)")
                axs[1].set_ylabel("Flux Difference: (input - full model fit)")
            elif stepname == 'solution':
                axs[0].set_xlabel("Input Abscissa (time or mondiode)")
                axs[0].set_ylabel("Linear Ordinate (linear flux)")
                axs[1].set_xlabel("Model flux (linear flux)")
                axs[1].set_ylabel("Flux Difference: (linear - model)")

            axs[0].set_yscale('log')
            axs[0].set_xscale('log')
            axs[0].scatter(xVector, yVector)
            axs[0].scatter(xVector[~mask], yVector[~mask], c='red', marker='x')
            axs[1].set_xscale('log')

            axs[1].scatter(yModel, yVector[mask] - yModel)
            fig.show()

            prompt = "Press Enter or c to continue [chpx]..."
            while True:
                ans = input(prompt).lower()
                if ans in (
                        "",
                        " ",
                        "c",
                ):
                    break
                elif ans in ("p", ):
                    import pdb
                    pdb.set_trace()
                elif ans in ("h", ):
                    print("[h]elp [c]ontinue [p]db")
                elif ans in ('x', ):
                    exit()
            plt.close()
Ejemplo n.º 28
0
    def debugHistogram(self, stepname, ampImage, nSigmaUsed, exp):
        """
        Make a histogram of the distribution of pixel values for each amp.

        The main image data histogram is plotted in blue. Edge pixels,
        if masked, are in red. Note that masked edge pixels do not contribute
        to the underflow and overflow numbers.

        Note that this currently only supports the 16-amp LSST detectors.

        Parameters
        ----------
        dataRef : `lsst.daf.persistence.ButlerDataRef`
            dataRef for the detector.
        exp : `lsst.afw.image.exposure.Exposure`
            The exposure in which the defects were found.
        visit : `int`
            The visit number.
        nSigmaUsed : `float`
            The number of sigma used for detection
        """
        frame = getDebugFrame(self._display, stepname)
        if frame:
            import matplotlib.pyplot as plt

            detector = exp.getDetector()
            nX = np.floor(np.sqrt(len(detector)))
            nY = len(detector) // nX
            fig, ax = plt.subplots(nrows=nY,
                                   ncols=nX,
                                   sharex='col',
                                   sharey='row',
                                   figsize=(13, 10))

            expTime = exp.getInfo().getVisitInfo().getExposureTime()

            for (amp, a) in zip(reversed(detector), ax.flatten()):
                mi = exp.maskedImage[amp.getBBox()]

                # normalize by expTime as we plot in ADU/s and don't always work with master calibs
                mi.image.array /= expTime
                stats = afwMath.makeStatistics(
                    mi, afwMath.MEANCLIP | afwMath.STDEVCLIP)
                mean, sigma = stats.getValue(afwMath.MEANCLIP), stats.getValue(
                    afwMath.STDEVCLIP)
                # Get array of pixels
                EDGEBIT = exp.maskedImage.mask.getPlaneBitMask("EDGE")
                imgData = mi.image.array[(mi.mask.array
                                          & EDGEBIT) == 0].flatten()
                edgeData = mi.image.array[(mi.mask.array
                                           & EDGEBIT) != 0].flatten()

                thrUpper = mean + nSigmaUsed * sigma
                thrLower = mean - nSigmaUsed * sigma

                nRight = len(imgData[imgData > thrUpper])
                nLeft = len(imgData[imgData < thrLower])

                nsig = nSigmaUsed + 1.2  # add something small so the edge of the plot is out from level used
                leftEdge = mean - nsig * nSigmaUsed * sigma
                rightEdge = mean + nsig * nSigmaUsed * sigma
                nbins = np.linspace(leftEdge, rightEdge, 1000)
                ey, bin_borders, patches = a.hist(edgeData,
                                                  histtype='step',
                                                  bins=nbins,
                                                  lw=1,
                                                  edgecolor='red')
                y, bin_borders, patches = a.hist(imgData,
                                                 histtype='step',
                                                 bins=nbins,
                                                 lw=3,
                                                 edgecolor='blue')

                # Report number of entries in over-and -underflow bins, i.e. off the edges of the histogram
                nOverflow = len(imgData[imgData > rightEdge])
                nUnderflow = len(imgData[imgData < leftEdge])

                # Put v-lines and textboxes in
                a.axvline(thrUpper, c='k')
                a.axvline(thrLower, c='k')
                msg = f"{amp.getName()}\nmean:{mean: .2f}\n$\\sigma$:{sigma: .2f}"
                a.text(0.65, 0.6, msg, transform=a.transAxes, fontsize=11)
                msg = f"nLeft:{nLeft}\nnRight:{nRight}\nnOverflow:{nOverflow}\nnUnderflow:{nUnderflow}"
                a.text(0.03, 0.6, msg, transform=a.transAxes, fontsize=11.5)

                # set axis limits and scales
                a.set_ylim([1., 1.7 * np.max(y)])
                lPlot, rPlot = a.get_xlim()
                a.set_xlim(np.array([lPlot, rPlot]))
                a.set_yscale('log')
                a.set_xlabel("ADU/s")
        return
Ejemplo n.º 29
0
    def run(self, ccdExposure, bias=None, dark=None,  flat=None, defects=None, fringes=None, bfKernel=None):
        """!Perform instrument signature removal on an exposure

        Steps include:
        - Detect saturation, apply overscan correction, bias, dark and flat
        - Perform CCD assembly
        - Interpolate over defects, saturated pixels and all NaNs

        \param[in] ccdExposure  -- lsst.afw.image.exposure of detector data
        \param[in] bias -- exposure of bias frame
        \param[in] dark -- exposure of dark frame
        \param[in] flat -- exposure of flatfield
        \param[in] defects -- list of detects
        \param[in] fringes -- a pipeBase.Struct with field fringes containing
                              exposure of fringe frame or list of fringe exposure
        \param[in] bfKernel -- kernel for brighter-fatter correction

        \return a pipeBase.Struct with field:
         - exposure
        """

        #Validate Input
        if self.config.doBias and bias is None:
            raise RuntimeError("Must supply a bias exposure if config.doBias True")
        if self.config.doDark and dark is None:
            raise RuntimeError("Must supply a dark exposure if config.doDark True")
        if self.config.doFlat and flat is None:
            raise RuntimeError("Must supply a flat exposure if config.doFlat True")
        if self.config.doBrighterFatter and bfKernel is None:
            raise RuntimeError("Must supply a kernel if config.doBrighterFatter True")
        if fringes is None:
            fringes = pipeBase.Struct(fringes=None)
        if self.config.doFringe and not isinstance(fringes, pipeBase.Struct):
            raise RuntimeError("Must supply fringe exposure as a pipeBase.Struct")

        defects = [] if defects is None else defects

        ccd = ccdExposure.getDetector()
        ccdExposure = self.convertIntToFloat(ccdExposure)

        if not ccd:
            assert not self.config.doAssembleCcd, "You need a Detector to run assembleCcd"
            ccd = [FakeAmp(ccdExposure, self.config)]

        for amp in ccd:
            #if ccdExposure is one amp, check for coverage to prevent performing ops multiple times
            if ccdExposure.getBBox().contains(amp.getBBox()):
                self.saturationDetection(ccdExposure, amp)
                self.overscanCorrection(ccdExposure, amp)

        if self.config.doAssembleCcd:
            ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)

        if self.config.doBias:
            self.biasCorrection(ccdExposure, bias)

        if self.config.doBrighterFatter:
            self.brighterFatterCorrection(ccdExposure, bfKernel,
                                          self.config.brighterFatterMaxIter,
                                          self.config.brighterFatterThreshold,
                                          self.config.brighterFatterApplyGain,
                                          )

        if self.config.doDark:
            self.darkCorrection(ccdExposure, dark)

        for amp in ccd:
            #if ccdExposure is one amp, check for coverage to prevent performing ops multiple times
            if ccdExposure.getBBox().contains(amp.getBBox()):
                ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
                self.updateVariance(ampExposure, amp)

        if self.config.doFringe and not self.config.fringeAfterFlat:
            self.fringe.run(ccdExposure, **fringes.getDict())

        if self.config.doFlat:
            self.flatCorrection(ccdExposure, flat)

        self.maskAndInterpDefect(ccdExposure, defects)

        self.saturationInterpolation(ccdExposure)

        self.maskAndInterpNan(ccdExposure)

        if self.config.doFringe and self.config.fringeAfterFlat:
            self.fringe.run(ccdExposure, **fringes.getDict())

        ccdExposure.getCalib().setFluxMag0(self.config.fluxMag0T1 * ccdExposure.getCalib().getExptime())

        frame = getDebugFrame(self._display, "postISRCCD")
        if frame:
            getDisplay(frame).mtv(ccdExposure)

        return pipeBase.Struct(
            exposure = ccdExposure,
        )
Ejemplo n.º 30
0
    def run(self, exposure, exposureIdInfo=None, background=None,
            icSourceCat=None):
        """!Calibrate an exposure (science image or coadd)

        @param[in,out] exposure  exposure to calibrate (an
            lsst.afw.image.ExposureF or similar);
            in:
            - MaskedImage
            - Psf
            out:
            - MaskedImage has background subtracted
            - Wcs is replaced
            - PhotoCalib is replaced
        @param[in] exposureIdInfo  ID info for exposure (an
            lsst.obs.base.ExposureIdInfo) If not provided, returned
            SourceCatalog IDs will not be globally unique.
        @param[in,out] background  background model already subtracted from
            exposure (an lsst.afw.math.BackgroundList). May be None if no
            background has been subtracted, though that is unusual for
            calibration. A refined background model is output.
        @param[in] icSourceCat  A SourceCatalog from CharacterizeImageTask
            from which we can copy some fields.

        @return pipe_base Struct containing these fields:
        - exposure  calibrate science exposure with refined WCS and PhotoCalib
        - background  model of background subtracted from exposure (an
          lsst.afw.math.BackgroundList)
        - sourceCat  catalog of measured sources
        - astromMatches  list of source/refObj matches from the astrometry
          solver
        """
        # detect, deblend and measure sources
        if exposureIdInfo is None:
            exposureIdInfo = ExposureIdInfo()

        if background is None:
            background = BackgroundList()
        sourceIdFactory = IdFactory.makeSource(exposureIdInfo.expId,
                                               exposureIdInfo.unusedBits)
        table = SourceTable.make(self.schema, sourceIdFactory)
        table.setMetadata(self.algMetadata)

        detRes = self.detection.run(table=table, exposure=exposure,
                                    doSmooth=True)
        sourceCat = detRes.sources
        if detRes.fpSets.background:
            for bg in detRes.fpSets.background:
                background.append(bg)
        if self.config.doSkySources:
            skySourceFootprints = self.skySources.run(mask=exposure.mask, seed=exposureIdInfo.expId)
            if skySourceFootprints:
                for foot in skySourceFootprints:
                    s = sourceCat.addNew()
                    s.setFootprint(foot)
                    s.set(self.skySourceKey, True)
        if self.config.doDeblend:
            self.deblend.run(exposure=exposure, sources=sourceCat)
        self.measurement.run(
            measCat=sourceCat,
            exposure=exposure,
            exposureId=exposureIdInfo.expId
        )
        if self.config.doApCorr:
            self.applyApCorr.run(
                catalog=sourceCat,
                apCorrMap=exposure.getInfo().getApCorrMap()
            )
        self.catalogCalculation.run(sourceCat)

        self.setPrimaryFlags.run(sourceCat, includeDeblend=self.config.doDeblend)

        if icSourceCat is not None and \
           len(self.config.icSourceFieldsToCopy) > 0:
            self.copyIcSourceFields(icSourceCat=icSourceCat,
                                    sourceCat=sourceCat)

        # TODO DM-11568: this contiguous check-and-copy could go away if we
        # reserve enough space during SourceDetection and/or SourceDeblend.
        # NOTE: sourceSelectors require contiguous catalogs, so ensure
        # contiguity now, so views are preserved from here on.
        if not sourceCat.isContiguous():
            sourceCat = sourceCat.copy(deep=True)

        # perform astrometry calibration:
        # fit an improved WCS and update the exposure's WCS in place
        astromMatches = None
        matchMeta = None
        if self.config.doAstrometry:
            try:
                astromRes = self.astrometry.run(
                    exposure=exposure,
                    sourceCat=sourceCat,
                )
                astromMatches = astromRes.matches
                matchMeta = astromRes.matchMeta
            except Exception as e:
                if self.config.requireAstrometry:
                    raise
                self.log.warn("Unable to perform astrometric calibration "
                              "(%s): attempting to proceed" % e)

        # compute photometric calibration
        if self.config.doPhotoCal:
            try:
                photoRes = self.photoCal.run(exposure, sourceCat=sourceCat, expId=exposureIdInfo.expId)
                exposure.setPhotoCalib(photoRes.photoCalib)
                # TODO: reword this to phrase it in terms of the calibration factor?
                self.log.info("Photometric zero-point: %f" %
                              photoRes.photoCalib.instFluxToMagnitude(1.0))
                self.setMetadata(exposure=exposure, photoRes=photoRes)
            except Exception as e:
                if self.config.requirePhotoCal:
                    raise
                self.log.warn("Unable to perform photometric calibration "
                              "(%s): attempting to proceed" % e)
                self.setMetadata(exposure=exposure, photoRes=None)

        if self.config.doInsertFakes:
            self.insertFakes.run(exposure, background=background)

            table = SourceTable.make(self.schema, sourceIdFactory)
            table.setMetadata(self.algMetadata)

            detRes = self.detection.run(table=table, exposure=exposure,
                                        doSmooth=True)
            sourceCat = detRes.sources
            if detRes.fpSets.background:
                for bg in detRes.fpSets.background:
                    background.append(bg)
            if self.config.doDeblend:
                self.deblend.run(exposure=exposure, sources=sourceCat)
            self.measurement.run(
                measCat=sourceCat,
                exposure=exposure,
                exposureId=exposureIdInfo.expId
            )
            if self.config.doApCorr:
                self.applyApCorr.run(
                    catalog=sourceCat,
                    apCorrMap=exposure.getInfo().getApCorrMap()
                )
            self.catalogCalculation.run(sourceCat)

            if icSourceCat is not None and len(self.config.icSourceFieldsToCopy) > 0:
                self.copyIcSourceFields(icSourceCat=icSourceCat,
                                        sourceCat=sourceCat)

        frame = getDebugFrame(self._display, "calibrate")
        if frame:
            displayAstrometry(
                sourceCat=sourceCat,
                exposure=exposure,
                matches=astromMatches,
                frame=frame,
                pause=False,
            )

        return pipeBase.Struct(
            exposure=exposure,
            background=background,
            sourceCat=sourceCat,
            astromMatches=astromMatches,
            matchMeta=matchMeta,
            # These are duplicate entries with different names for use with
            # gen3 middleware
            outputExposure=exposure,
            outputCat=sourceCat,
            outputBackground=background,
        )
Ejemplo n.º 31
0
    def calibrate(self, exposure, exposureIdInfo=None, background=None, icSourceCat=None):
        """!Calibrate an exposure (science image or coadd)

        @param[in,out] exposure  exposure to calibrate (an lsst.afw.image.ExposureF or similar);
            in:
            - MaskedImage
            - Psf
            out:
            - MaskedImage has background subtracted
            - Wcs is replaced
            - Calib zero-point is set
        @param[in] exposureIdInfo  ID info for exposure (an lsst.daf.butlerUtils.ExposureIdInfo)
            If not provided, returned SourceCatalog IDs will not be globally unique.
        @param[in,out] background  background model already subtracted from exposure
            (an lsst.afw.math.BackgroundList). May be None if no background has been subtracted,
            though that is unusual for calibration.
            A refined background model is output.
        @param[in] icSourceCat  A SourceCatalog from CharacterizeImageTask from which we can copy
            some fields.

        @return pipe_base Struct containing these fields:
        - exposure  calibrate science exposure with refined WCS and Calib
        - background  model of background subtracted from exposure (an lsst.afw.math.BackgroundList)
        - sourceCat  catalog of measured sources
        - astromMatches  list of source/refObj matches from the astrometry solver
        """
        # detect, deblend and measure sources
        if exposureIdInfo is None:
            exposureIdInfo = ExposureIdInfo()

        procRes = self.detectAndMeasure.run(
            exposure = exposure,
            exposureIdInfo = exposureIdInfo,
            background = background,
        )
        background = procRes.background
        sourceCat = procRes.sourceCat

        if icSourceCat is not None and len(self.config.icSourceFieldsToCopy) > 0:
            self.copyIcSourceFields(icSourceCat=icSourceCat, sourceCat=sourceCat)

        # perform astrometry calibration:
        # fit an improved WCS and update the exposure's WCS in place
        astromMatches = None
        if self.config.doAstrometry:
            try:
                astromRes = self.astrometry.run(
                    exposure = exposure,
                    sourceCat = sourceCat,
                )
                astromMatches = astromRes.matches
            except Exception as e:
                if self.config.requireAstrometry:
                    raise
                self.log.warn("Unable to perform astrometric calibration (%s): attempting to proceed" % e)

        # compute photometric calibration
        if self.config.doPhotoCal:
            try:
                if astromMatches is None:
                    astromRes = self.astrometry.loadAndMatch(exposure=exposure, sourceCat=sourceCat)
                photoRes = self.photoCal.run(exposure, astromMatches)
                exposure.getCalib().setFluxMag0(photoRes.calib.getFluxMag0())
                self.log.info("Photometric zero-point: %f" % photoRes.calib.getMagnitude(1.0))
                self.setMetadata(exposure=exposure, photoRes=photoRes)
            except Exception as e:
                if self.config.requirePhotoCal:
                    raise
                self.log.warn("Unable to perform photometric calibration (%s): attempting to proceed" % e)
                self.setMetadata(exposure=exposure, photoRes=None)

        frame = getDebugFrame(self._display, "calibrate")
        if frame:
            displayAstrometry(
                sourceCat = sourceCat,
                exposure = exposure,
                matches = astromMatches,
                frame = frame,
                pause = False,
            )

        return pipeBase.Struct(
            exposure = exposure,
            background = background,
            sourceCat = sourceCat,
            astromMatches = astromMatches,
        )
Ejemplo n.º 32
0
    def calibrate(self,
                  exposure,
                  exposureIdInfo=None,
                  background=None,
                  icSourceCat=None):
        """!Calibrate an exposure (science image or coadd)

        @param[in,out] exposure  exposure to calibrate (an
            lsst.afw.image.ExposureF or similar);
            in:
            - MaskedImage
            - Psf
            out:
            - MaskedImage has background subtracted
            - Wcs is replaced
            - Calib zero-point is set
        @param[in] exposureIdInfo  ID info for exposure (an
            lsst.obs.base.ExposureIdInfo) If not provided, returned
            SourceCatalog IDs will not be globally unique.
        @param[in,out] background  background model already subtracted from
            exposure (an lsst.afw.math.BackgroundList). May be None if no
            background has been subtracted, though that is unusual for
            calibration. A refined background model is output.
        @param[in] icSourceCat  A SourceCatalog from CharacterizeImageTask
            from which we can copy some fields.

        @return pipe_base Struct containing these fields:
        - exposure  calibrate science exposure with refined WCS and Calib
        - background  model of background subtracted from exposure (an
          lsst.afw.math.BackgroundList)
        - sourceCat  catalog of measured sources
        - astromMatches  list of source/refObj matches from the astrometry
          solver
        """
        # detect, deblend and measure sources
        if exposureIdInfo is None:
            exposureIdInfo = ExposureIdInfo()

        if background is None:
            background = BackgroundList()
        sourceIdFactory = IdFactory.makeSource(exposureIdInfo.expId,
                                               exposureIdInfo.unusedBits)
        table = SourceTable.make(self.schema, sourceIdFactory)
        table.setMetadata(self.algMetadata)

        if self.config.doInsertFakes:
            self.insertFakes.run(exposure, background=background)

        detRes = self.detection.run(table=table,
                                    exposure=exposure,
                                    doSmooth=True)
        sourceCat = detRes.sources
        if detRes.fpSets.background:
            background.append(detRes.fpSets.background)
        if self.config.doDeblend:
            self.deblend.run(exposure=exposure, sources=sourceCat)
        self.measurement.run(measCat=sourceCat,
                             exposure=exposure,
                             exposureId=exposureIdInfo.expId)
        if self.config.doApCorr:
            self.applyApCorr.run(catalog=sourceCat,
                                 apCorrMap=exposure.getInfo().getApCorrMap())
        self.catalogCalculation.run(sourceCat)

        if icSourceCat is not None and \
           len(self.config.icSourceFieldsToCopy) > 0:
            self.copyIcSourceFields(icSourceCat=icSourceCat,
                                    sourceCat=sourceCat)

        # perform astrometry calibration:
        # fit an improved WCS and update the exposure's WCS in place
        astromMatches = None
        if self.config.doAstrometry:
            try:
                astromRes = self.astrometry.run(
                    exposure=exposure,
                    sourceCat=sourceCat,
                )
                astromMatches = astromRes.matches
            except Exception as e:
                if self.config.requireAstrometry:
                    raise
                self.log.warn("Unable to perform astrometric calibration "
                              "(%s): attempting to proceed" % e)

        # compute photometric calibration
        if self.config.doPhotoCal:
            try:
                photoRes = self.photoCal.run(exposure, sourceCat=sourceCat)
                exposure.getCalib().setFluxMag0(photoRes.calib.getFluxMag0())
                self.log.info("Photometric zero-point: %f" %
                              photoRes.calib.getMagnitude(1.0))
                self.setMetadata(exposure=exposure, photoRes=photoRes)
            except Exception as e:
                if self.config.requirePhotoCal:
                    raise
                self.log.warn("Unable to perform photometric calibration "
                              "(%s): attempting to proceed" % e)
                self.setMetadata(exposure=exposure, photoRes=None)

        frame = getDebugFrame(self._display, "calibrate")
        if frame:
            displayAstrometry(
                sourceCat=sourceCat,
                exposure=exposure,
                matches=astromMatches,
                frame=frame,
                pause=False,
            )

        return pipeBase.Struct(
            exposure=exposure,
            background=background,
            sourceCat=sourceCat,
            astromMatches=astromMatches,
        )
Ejemplo n.º 33
0
    def run(self, ccdExposure, bias=None, linearizer=None, dark=None, flat=None, defects=None,
            fringes=None, bfKernel=None):
        """!Perform instrument signature removal on an exposure

        Steps include:
        - Detect saturation, apply overscan correction, bias, dark and flat
        - Perform CCD assembly
        - Interpolate over defects, saturated pixels and all NaNs

        \param[in] ccdExposure  -- lsst.afw.image.exposure of detector data
        \param[in] bias -- exposure of bias frame
        \param[in] linearizer -- linearizing functor; a subclass of lsst.ip.isrFunctions.LinearizeBase
        \param[in] dark -- exposure of dark frame
        \param[in] flat -- exposure of flatfield
        \param[in] defects -- list of detects
        \param[in] fringes -- a pipeBase.Struct with field fringes containing
                              exposure of fringe frame or list of fringe exposure
        \param[in] bfKernel -- kernel for brighter-fatter correction

        \return a pipeBase.Struct with field:
         - exposure
        """

        # parseAndRun expects to be able to call run() with a dataRef; see DM-6640
        if isinstance(ccdExposure, ButlerDataRef):
            return self.runDataRef(ccdExposure)

        ccd = ccdExposure.getDetector()

        # Validate Input
        if self.config.doBias and bias is None:
            raise RuntimeError("Must supply a bias exposure if config.doBias True")
        if self.doLinearize(ccd) and linearizer is None:
            raise RuntimeError("Must supply a linearizer if config.doBias True")
        if self.config.doDark and dark is None:
            raise RuntimeError("Must supply a dark exposure if config.doDark True")
        if self.config.doFlat and flat is None:
            raise RuntimeError("Must supply a flat exposure if config.doFlat True")
        if self.config.doBrighterFatter and bfKernel is None:
            raise RuntimeError("Must supply a kernel if config.doBrighterFatter True")
        if fringes is None:
            fringes = pipeBase.Struct(fringes=None)
        if self.config.doFringe and not isinstance(fringes, pipeBase.Struct):
            raise RuntimeError("Must supply fringe exposure as a pipeBase.Struct")
        if self.config.doDefect and defects is None:
            raise RuntimeError("Must supply defects if config.doDefect True")

        ccdExposure = self.convertIntToFloat(ccdExposure)

        if not ccd:
            assert not self.config.doAssembleCcd, "You need a Detector to run assembleCcd"
            ccd = [FakeAmp(ccdExposure, self.config)]

        for amp in ccd:
            # if ccdExposure is one amp, check for coverage to prevent performing ops multiple times
            if ccdExposure.getBBox().contains(amp.getBBox()):
                self.saturationDetection(ccdExposure, amp)
                self.suspectDetection(ccdExposure, amp)
                self.overscanCorrection(ccdExposure, amp)

        if self.config.doAssembleCcd:
            ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
            if self.config.expectWcs and not ccdExposure.getWcs():
                self.log.warn("No WCS found in input exposure")

        if self.config.doBias:
            self.biasCorrection(ccdExposure, bias)

        if self.doLinearize(ccd):
            linearizer(image=ccdExposure.getMaskedImage().getImage(), detector=ccd, log=self.log)

        for amp in ccd:
            # if ccdExposure is one amp, check for coverage to prevent performing ops multiple times
            if ccdExposure.getBBox().contains(amp.getBBox()):
                ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
                self.updateVariance(ampExposure, amp)

        if self.config.doBrighterFatter:
            self.brighterFatterCorrection(ccdExposure, bfKernel,
                                          self.config.brighterFatterMaxIter,
                                          self.config.brighterFatterThreshold,
                                          self.config.brighterFatterApplyGain,
                                          )

        if self.config.doDark:
            self.darkCorrection(ccdExposure, dark)

        if self.config.doFringe and not self.config.fringeAfterFlat:
            self.fringe.run(ccdExposure, **fringes.getDict())

        if self.config.doFlat:
            self.flatCorrection(ccdExposure, flat)

        if self.config.doDefect:
            self.maskAndInterpDefect(ccdExposure, defects)

        if self.config.doSaturationInterpolation:
            self.saturationInterpolation(ccdExposure)

        self.maskAndInterpNan(ccdExposure)

        if self.config.doFringe and self.config.fringeAfterFlat:
            self.fringe.run(ccdExposure, **fringes.getDict())

        exposureTime = ccdExposure.getInfo().getVisitInfo().getExposureTime()
        ccdExposure.getCalib().setFluxMag0(self.config.fluxMag0T1*exposureTime)

        frame = getDebugFrame(self._display, "postISRCCD")
        if frame:
            getDisplay(frame).mtv(ccdExposure)

        return pipeBase.Struct(
            exposure=ccdExposure,
        )
Ejemplo n.º 34
0
    def fitBackground(self, maskedImage, nx=0, ny=0, algorithm=None):
        """!Estimate the background of a masked image

        @param[in] maskedImage  masked image whose background is to be computed
        @param[in] nx  number of x bands; if 0 compute from width and config.binSize
        @param[in] ny  number of y bands; if 0 compute from height and config.binSize
        @param[in] algorithm  name of interpolation algorithm; if None use self.config.algorithm

        @return fit background as an lsst.afw.math.Background

        @throw RuntimeError if lsst.afw.math.makeBackground returns None,
            which is apparently one way it indicates failure
        """
        if not nx:
            nx = maskedImage.getWidth()//self.config.binSize + 1
        if not ny:
            ny = maskedImage.getHeight()//self.config.binSize + 1

        unsubFrame = getDebugFrame(self._display, "unsubtracted")
        if unsubFrame:
            unsubDisp = afwDisplay.getDisplay(frame=unsubFrame)
            unsubDisp.mtv(maskedImage, title="unsubtracted")
            xPosts = numpy.rint(numpy.linspace(0, maskedImage.getWidth() + 1, num=nx, endpoint=True))
            yPosts = numpy.rint(numpy.linspace(0, maskedImage.getHeight() + 1, num=ny, endpoint=True))
            with unsubDisp.Buffering():
                for (xMin, xMax), (yMin, yMax) in itertools.product(zip(xPosts[:-1], xPosts[1:]),
                                                                    zip(yPosts[:-1], yPosts[1:])):
                    unsubDisp.line([(xMin, yMin), (xMin, yMax), (xMax, yMax), (xMax, yMin), (xMin, yMin)])


        sctrl = afwMath.StatisticsControl()
        sctrl.setAndMask(reduce(lambda x, y: x | maskedImage.getMask().getPlaneBitMask(y),
                                self.config.ignoredPixelMask, 0x0))
        sctrl.setNanSafe(self.config.isNanSafe)

        self.log.logdebug("Ignoring mask planes: %s" % ", ".join(self.config.ignoredPixelMask))

        if algorithm is None:
            algorithm = self.config.algorithm

        bctrl = afwMath.BackgroundControl(algorithm, nx, ny,
                                          self.config.undersampleStyle, sctrl,
                                          self.config.statisticsProperty)

        # TODO: The following check should really be done within lsst.afw.math.
        #       With the current code structure, it would need to be accounted for in the doGetImage()
        #       function in BackgroundMI.cc (which currently only checks against the interpolation settings,
        #       which is not appropriate when useApprox=True)
        #       and/or the makeApproximate() function in afw/Approximate.cc.
        #       See ticket DM-2920: "Clean up code in afw for Approximate background
        #       estimation" (which includes a note to remove the following and the
        #       similar checks in pipe_tasks/matchBackgrounds.py once implemented)
        #
        # Check that config setting of approxOrder/binSize make sense
        # (i.e. ngrid (= shortDimension/binSize) > approxOrderX) and perform
        # appropriate undersampleStlye behavior.
        if self.config.useApprox:
            if self.config.approxOrderY not in (self.config.approxOrderX, -1):
                raise ValueError("Error: approxOrderY not in (approxOrderX, -1)")
            order = self.config.approxOrderX
            minNumberGridPoints = self.config.approxOrderX + 1
            if min(nx,ny) <= self.config.approxOrderX:
                self.log.warn("Too few points in grid to constrain fit: min(nx, ny) < approxOrder) "
                            "[min(%d, %d) < %d]" % (nx, ny, self.config.approxOrderX))
                if self.config.undersampleStyle == "THROW_EXCEPTION":
                    raise ValueError("Too few points in grid (%d, %d) for order (%d) and binsize (%d)" % (
                            nx, ny, self.config.approxOrderX, self.config.binSize))
                elif self.config.undersampleStyle == "REDUCE_INTERP_ORDER":
                    if order < 1:
                        raise ValueError("Cannot reduce approxOrder below 0.  " +
                                         "Try using undersampleStyle = \"INCREASE_NXNYSAMPLE\" instead?")
                    order = min(nx, ny) - 1
                    self.log.warn("Reducing approxOrder to %d" % order)
                elif self.config.undersampleStyle == "INCREASE_NXNYSAMPLE":
                    newBinSize = min(maskedImage.getWidth(),maskedImage.getHeight())//(minNumberGridPoints-1)
                    if newBinSize < 1:
                        raise ValueError("Binsize must be greater than 0")
                    newNx = maskedImage.getWidth()//newBinSize + 1
                    newNy = maskedImage.getHeight()//newBinSize + 1
                    bctrl.setNxSample(newNx)
                    bctrl.setNySample(newNy)
                    self.log.warn("Decreasing binSize from %d to %d for a grid of (%d, %d)" %
                                (self.config.binSize, newBinSize, newNx, newNy))

            actrl = afwMath.ApproximateControl(afwMath.ApproximateControl.CHEBYSHEV, order, order,
                                               self.config.weighting)
            bctrl.setApproximateControl(actrl)

        bg = afwMath.makeBackground(maskedImage, bctrl)
        if bg is None:
            raise RuntimeError("lsst.afw.math.makeBackground failed to fit a background model")
        return bg
Ejemplo n.º 35
0
    def fitBackground(self, maskedImage, nx=0, ny=0, algorithm=None):
        """!Estimate the background of a masked image

        @param[in] maskedImage  masked image whose background is to be computed
        @param[in] nx  number of x bands; if 0 compute from width and config.binSizeX
        @param[in] ny  number of y bands; if 0 compute from height and config.binSizeY
        @param[in] algorithm  name of interpolation algorithm; if None use self.config.algorithm

        @return fit background as an lsst.afw.math.Background

        @throw RuntimeError if lsst.afw.math.makeBackground returns None,
            which is apparently one way it indicates failure
        """

        binSizeX = self.config.binSize if self.config.binSizeX == 0 else self.config.binSizeX
        binSizeY = self.config.binSize if self.config.binSizeY == 0 else self.config.binSizeY

        if not nx:
            nx = maskedImage.getWidth() // binSizeX + 1
        if not ny:
            ny = maskedImage.getHeight() // binSizeY + 1

        unsubFrame = getDebugFrame(self._display, "unsubtracted")
        if unsubFrame:
            unsubDisp = afwDisplay.getDisplay(frame=unsubFrame)
            unsubDisp.mtv(maskedImage, title="unsubtracted")
            xPosts = numpy.rint(
                numpy.linspace(0,
                               maskedImage.getWidth() + 1,
                               num=nx,
                               endpoint=True))
            yPosts = numpy.rint(
                numpy.linspace(0,
                               maskedImage.getHeight() + 1,
                               num=ny,
                               endpoint=True))
            with unsubDisp.Buffering():
                for (xMin, xMax), (yMin, yMax) in itertools.product(
                        zip(xPosts[:-1], xPosts[1:]),
                        zip(yPosts[:-1], yPosts[1:])):
                    unsubDisp.line([(xMin, yMin), (xMin, yMax), (xMax, yMax),
                                    (xMax, yMin), (xMin, yMin)])

        sctrl = afwMath.StatisticsControl()
        badMask = maskedImage.mask.getPlaneBitMask(
            self.config.ignoredPixelMask)

        sctrl.setAndMask(badMask)
        sctrl.setNanSafe(self.config.isNanSafe)

        self.log.debug("Ignoring mask planes: %s" %
                       ", ".join(self.config.ignoredPixelMask))
        if (maskedImage.mask.getArray() & badMask).all():
            raise pipeBase.TaskError(
                "All pixels masked. Cannot estimate background")

        if algorithm is None:
            algorithm = self.config.algorithm

        # TODO: DM-22814. This call to a deprecated BackgroundControl constructor
        # is necessary to support the algorithm parameter; it # should be replaced with
        #
        #     afwMath.BackgroundControl(nx, ny, sctrl, self.config.statisticsProperty)
        #
        # when algorithm has been deprecated and removed.
        with suppress_deprecations():
            bctrl = afwMath.BackgroundControl(algorithm, nx, ny,
                                              self.config.undersampleStyle,
                                              sctrl,
                                              self.config.statisticsProperty)

        # TODO: The following check should really be done within lsst.afw.math.
        #       With the current code structure, it would need to be accounted for in the doGetImage()
        #       function in BackgroundMI.cc (which currently only checks against the interpolation settings,
        #       which is not appropriate when useApprox=True)
        #       and/or the makeApproximate() function in afw/Approximate.cc.
        #       See ticket DM-2920: "Clean up code in afw for Approximate background
        #       estimation" (which includes a note to remove the following and the
        #       similar checks in pipe_tasks/matchBackgrounds.py once implemented)
        #
        # Check that config setting of approxOrder/binSize make sense
        # (i.e. ngrid (= shortDimension/binSize) > approxOrderX) and perform
        # appropriate undersampleStlye behavior.
        if self.config.useApprox:
            if self.config.approxOrderY not in (self.config.approxOrderX, -1):
                raise ValueError(
                    "Error: approxOrderY not in (approxOrderX, -1)")
            order = self.config.approxOrderX
            minNumberGridPoints = order + 1
            if min(nx, ny) <= order:
                self.log.warn(
                    "Too few points in grid to constrain fit: min(nx, ny) < approxOrder) "
                    "[min(%d, %d) < %d]" % (nx, ny, order))
                if self.config.undersampleStyle == "THROW_EXCEPTION":
                    raise ValueError(
                        "Too few points in grid (%d, %d) for order (%d) and binSize (%d, %d)"
                        % (nx, ny, order, binSizeX, binSizeY))
                elif self.config.undersampleStyle == "REDUCE_INTERP_ORDER":
                    if order < 1:
                        raise ValueError(
                            "Cannot reduce approxOrder below 0.  "
                            "Try using undersampleStyle = \"INCREASE_NXNYSAMPLE\" instead?"
                        )
                    order = min(nx, ny) - 1
                    self.log.warn("Reducing approxOrder to %d" % order)
                elif self.config.undersampleStyle == "INCREASE_NXNYSAMPLE":
                    # Reduce bin size to the largest acceptable square bins
                    newBinSize = min(
                        maskedImage.getWidth(),
                        maskedImage.getHeight()) // (minNumberGridPoints - 1)
                    if newBinSize < 1:
                        raise ValueError("Binsize must be greater than 0")
                    newNx = maskedImage.getWidth() // newBinSize + 1
                    newNy = maskedImage.getHeight() // newBinSize + 1
                    bctrl.setNxSample(newNx)
                    bctrl.setNySample(newNy)
                    self.log.warn(
                        "Decreasing binSize from (%d, %d) to %d for a grid of (%d, %d)"
                        % (binSizeX, binSizeY, newBinSize, newNx, newNy))

            actrl = afwMath.ApproximateControl(
                afwMath.ApproximateControl.CHEBYSHEV, order, order,
                self.config.weighting)
            bctrl.setApproximateControl(actrl)

        bg = afwMath.makeBackground(maskedImage, bctrl)
        if bg is None:
            raise RuntimeError(
                "lsst.afw.math.makeBackground failed to fit a background model"
            )
        return bg
Ejemplo n.º 36
0
    def calibrate(self,
                  exposure,
                  exposureIdInfo,
                  background=None,
                  icSourceCat=None):
        """!Calibrate an exposure (science image or coadd)

        @param[in,out] exposure  exposure to calibrate (an lsst.afw.image.ExposureF or similar);
            in:
            - MaskedImage
            - Psf
            out:
            - MaskedImage has background subtracted
            - Wcs is replaced
            - Calib zero-point is set
        @param[in,out] background  background model already subtracted from exposure
            (an lsst.afw.math.BackgroundList). May be None if no background has been subtracted,
            though that is unusual for calibration.
            A refined background model is output.

        @return pipe_base Struct containing these fields:
        - exposure  calibrate science exposure with refined WCS and Calib
        - background  model of background subtracted from exposure (an lsst.afw.math.BackgroundList)
        - sourceCat  catalog of measured sources
        - astromMatches  list of source/refObj matches from the astrometry solver
        """
        # detect, deblend and measure sources
        procRes = self.detectAndMeasure.run(
            exposure=exposure,
            exposureIdInfo=exposureIdInfo,
            background=background,
        )
        background = procRes.background
        sourceCat = procRes.sourceCat

        if icSourceCat is not None and len(
                self.config.icSourceFieldsToCopy) > 0:
            self.copyIcSourceFields(icSourceCat=icSourceCat,
                                    sourceCat=sourceCat)

        # perform astrometry calibration:
        # fit an improved WCS and update the exposure's WCS in place
        astromMatches = None
        if self.config.doAstrometry:
            try:
                astromRes = self.astrometry.run(
                    exposure=exposure,
                    sourceCat=sourceCat,
                )
                astromMatches = astromRes.matches
            except Exception as e:
                if self.config.requireAstrometry:
                    raise
                self.log.warn(
                    "Unable to perform astrometric calibration (%s): attempting to proceed"
                    % e)

        # compute photometric calibration
        if self.config.doPhotoCal:
            try:
                if astromMatches is None:
                    astromRes = self.astrometry.loadAndMatch(
                        exposure=exposure, sourceCat=sourceCat)
                photoRes = self.photoCal.run(exposure, astromMatches)
                exposure.getCalib().setFluxMag0(photoRes.calib.getFluxMag0())
                self.log.info("Photometric zero-point: %f" %
                              photoRes.calib.getMagnitude(1.0))
                self.setMetadata(exposure=exposure, photoRes=photoRes)
            except Exception as e:
                if self.config.requirePhotoCal:
                    raise
                self.log.warn(
                    "Unable to perform photometric calibration (%s): attempting to proceed"
                    % e)
                self.setMetadata(exposure=exposure, photoRes=None)

        frame = getDebugFrame(self._display, "calibrate")
        if frame:
            displayAstrometry(
                sourceCat=sourceCat,
                exposure=exposure,
                astromMatches=astromMatches,
                frame=frame,
                pause=False,
            )

        return pipeBase.Struct(
            exposure=exposure,
            background=background,
            sourceCat=sourceCat,
            astromMatches=astromMatches,
        )
Ejemplo n.º 37
0
    def run(self, snap0, snap1, defects=None):
        """Combine two snaps

        @param[in] snap0: snapshot exposure 0
        @param[in] snap1: snapshot exposure 1
        @defects[in] defect list (for repair task)
        @return a pipe_base Struct with fields:
        - exposure: snap-combined exposure
        - sources: detected sources, or None if detection not performed
        """
        # initialize optional outputs
        sources = None

        if self.config.doRepair:
            self.log.info("snapCombine repair")
            psf = self.makeInitialPsf(snap0, fwhmPix=self.config.repairPsfFwhm)
            snap0.setPsf(psf)
            snap1.setPsf(psf)
            self.repair.run(snap0, defects=defects, keepCRs=False)
            self.repair.run(snap1, defects=defects, keepCRs=False)

            repair0frame = getDebugFrame(self._display, "repair0")
            if repair0frame:
                getDisplay(repair0frame).mtv(snap0)
            repair1frame = getDebugFrame(self._display, "repair1")
            if repair1frame:
                getDisplay(repair1frame).mtv(snap1)

        if self.config.doDiffIm:
            if self.config.doPsfMatch:
                self.log.info("snapCombine psfMatch")
                diffRet = self.diffim.run(snap0, snap1, "subtractExposures")
                diffExp = diffRet.subtractedImage

                # Measure centroid and width of kernel; dependent on ticket #1980
                # Useful diagnostic for the degree of astrometric shift between snaps.
                diffKern = diffRet.psfMatchingKernel
                width, height = diffKern.getDimensions()

            else:
                diffExp = afwImage.ExposureF(snap0, True)
                diffMi = diffExp.getMaskedImage()
                diffMi -= snap1.getMaskedImage()

            psf = self.makeInitialPsf(snap0)
            diffExp.setPsf(psf)
            table = afwTable.SourceTable.make(self.schema)
            table.setMetadata(self.algMetadata)
            detRet = self.detection.makeSourceCatalog(table, diffExp)
            sources = detRet.sources
            fpSets = detRet.fpSets
            if self.config.doMeasurement:
                self.measurement.measure(diffExp, sources)

            mask0 = snap0.getMaskedImage().getMask()
            mask1 = snap1.getMaskedImage().getMask()
            fpSets.positive.setMask(mask0, "DETECTED")
            fpSets.negative.setMask(mask1, "DETECTED")

            maskD = diffExp.getMaskedImage().getMask()
            fpSets.positive.setMask(maskD, "DETECTED")
            fpSets.negative.setMask(maskD, "DETECTED_NEGATIVE")

        combinedExp = self.addSnaps(snap0, snap1)

        return pipeBase.Struct(
            exposure=combinedExp,
            sources=sources,
        )
Ejemplo n.º 38
0
    def run(self, snap0, snap1, defects=None):
        """Combine two snaps

        @param[in] snap0: snapshot exposure 0
        @param[in] snap1: snapshot exposure 1
        @defects[in] defect list (for repair task)
        @return a pipe_base Struct with fields:
        - exposure: snap-combined exposure
        - sources: detected sources, or None if detection not performed
        """
        # initialize optional outputs
        sources = None

        if self.config.doRepair:
            self.log.info("snapCombine repair")
            psf = self.makeInitialPsf(snap0, fwhmPix=self.config.repairPsfFwhm)
            snap0.setPsf(psf)
            snap1.setPsf(psf)
            self.repair.run(snap0, defects=defects, keepCRs=False)
            self.repair.run(snap1, defects=defects, keepCRs=False)

            repair0frame = getDebugFrame(self._display, "repair0")
            if repair0frame:
                getDisplay(repair0frame).mtv(snap0)
            repair1frame = getDebugFrame(self._display, "repair1")
            if repair1frame:
                getDisplay(repair1frame).mtv(snap1)

        if self.config.doDiffIm:
            if self.config.doPsfMatch:
                self.log.info("snapCombine psfMatch")
                diffRet = self.diffim.run(snap0, snap1, "subtractExposures")
                diffExp = diffRet.subtractedImage

                # Measure centroid and width of kernel; dependent on ticket #1980
                # Useful diagnostic for the degree of astrometric shift between snaps.
                diffKern = diffRet.psfMatchingKernel
                width, height = diffKern.getDimensions()

            else:
                diffExp = afwImage.ExposureF(snap0, True)
                diffMi = diffExp.getMaskedImage()
                diffMi -= snap1.getMaskedImage()

            psf = self.makeInitialPsf(snap0)
            diffExp.setPsf(psf)
            table = afwTable.SourceTable.make(self.schema)
            table.setMetadata(self.algMetadata)
            detRet = self.detection.run(table, diffExp)
            sources = detRet.sources
            fpSets = detRet.fpSets
            if self.config.doMeasurement:
                self.measurement.measure(diffExp, sources)

            mask0 = snap0.getMaskedImage().getMask()
            mask1 = snap1.getMaskedImage().getMask()
            fpSets.positive.setMask(mask0, "DETECTED")
            fpSets.negative.setMask(mask1, "DETECTED")

            maskD = diffExp.getMaskedImage().getMask()
            fpSets.positive.setMask(maskD, "DETECTED")
            fpSets.negative.setMask(maskD, "DETECTED_NEGATIVE")

        combinedExp = self.addSnaps(snap0, snap1)

        return pipeBase.Struct(
            exposure=combinedExp,
            sources=sources,
        )
Ejemplo n.º 39
0
    def run(self,
            ccdExposure,
            bias=None,
            dark=None,
            flat=None,
            defects=None,
            fringes=None):
        """!Perform instrument signature removal on an exposure

        Steps include:
        - Detect saturation, apply overscan correction, bias, dark and flat
        - Perform CCD assembly
        - Interpolate over defects, saturated pixels and all NaNs

        \param[in] ccdExposure  -- lsst.afw.image.exposure of detector data
        \param[in] bias -- exposure of bias frame
        \param[in] dark -- exposure of dark frame
        \param[in] flat -- exposure of flatfield
        \param[in] defects -- list of detects
        \param[in] fringes -- a pipeBase.Struct with field fringes containing
                              exposure of fringe frame or list of fringe exposure

        \return a pipeBase.Struct with field:
         - exposure
        """

        #Validate Input
        if self.config.doBias and bias is None:
            raise RuntimeError(
                "Must supply a bias exposure if config.doBias True")
        if self.config.doDark and dark is None:
            raise RuntimeError(
                "Must supply a dark exposure if config.doDark True")
        if self.config.doFlat and flat is None:
            raise RuntimeError(
                "Must supply a flat exposure if config.doFlat True")
        if fringes is None:
            fringes = pipeBase.Struct(fringes=None)
        if self.config.doFringe and not isinstance(fringes, pipeBase.Struct):
            raise RuntimeError(
                "Must supply fringe exposure as a pipeBase.Struct")

        defects = [] if defects is None else defects

        ccd = ccdExposure.getDetector()
        ccdExposure = self.convertIntToFloat(ccdExposure)

        if not ccd:
            assert not self.config.doAssembleCcd, "You need a Detector to run assembleCcd"
            ccd = [FakeAmp(ccdExposure, self.config)]

        for amp in ccd:
            #if ccdExposure is one amp, check for coverage to prevent performing ops multiple times
            if ccdExposure.getBBox().contains(amp.getBBox()):
                self.saturationDetection(ccdExposure, amp)
                self.overscanCorrection(ccdExposure, amp)

        if self.config.doAssembleCcd:
            ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)

        if self.config.doBias:
            self.biasCorrection(ccdExposure, bias)

        if self.config.doDark:
            self.darkCorrection(ccdExposure, dark)

        for amp in ccd:
            #if ccdExposure is one amp, check for coverage to prevent performing ops multiple times
            if ccdExposure.getBBox().contains(amp.getBBox()):
                ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
                self.updateVariance(ampExposure, amp)

        if self.config.doFringe and not self.config.fringeAfterFlat:
            self.fringe.run(ccdExposure, **fringes.getDict())

        if self.config.doFlat:
            self.flatCorrection(ccdExposure, flat)

        self.maskAndInterpDefect(ccdExposure, defects)

        self.saturationInterpolation(ccdExposure)

        self.maskAndInterpNan(ccdExposure)

        if self.config.doFringe and self.config.fringeAfterFlat:
            self.fringe.run(ccdExposure, **fringes.getDict())

        if False:  # RHL XXX  This scalar/dict disagreement needs to get resolved between HSC and LSST (i.e. Paul and Lauren).  Do not check me in
            ccdExposure.getCalib().setFluxMag0(
                self.config.fluxMag0T1 * ccdExposure.getCalib().getExptime())

        frame = getDebugFrame(self._display, "postISRCCD")
        if frame:
            getDisplay(frame).mtv(ccdExposure)

        return pipeBase.Struct(exposure=ccdExposure, )