Esempio n. 1
0
    def testGetImageData(self):

        eimg = self._getEimage()

        image = ButlerWrapper.getImageData(eimg)
        self.assertTrue(isinstance(image, np.ndarray))
        self.assertEqual(image.shape, (4000, 4072))
Esempio n. 2
0
    def setUpClass(cls):

        dataDirPath = os.path.join(getModulePath(), "tests")
        cls.dataDir = tempfile.TemporaryDirectory(dir=dataDirPath)
        cls._ingestImages()

        cls.butlerWrapper = ButlerWrapper(cls.dataDir.name)
Esempio n. 3
0
    def testGetImageData(self):

        exposure = self._getRawExp()

        image = ButlerWrapper.getImageData(exposure)
        self.assertTrue(isinstance(image, np.ndarray))
        self.assertEqual(image.shape, (4020, 4176))
Esempio n. 4
0
def plotHist(exposure, name="", numOfBin=1000, log=False, saveFilePath=None):
    """
    
    Plot the histogram.
    
    Arguments:
        exposure {[exposure]} -- Data butler of exposure image.
    
    Keyword Arguments:
        name {string} -- Image title name (default: {""}).
        numOfBin {int} -- Number of bins (default: {1000}).
        log {bool} -- The histogram axis will be set to a log scale if log=True 
                      (default: {False}).
        saveFilePath {[str]} -- Save image to file path. (default: {None})
    """

    # Get the image data
    img = ButlerWrapper.getImageData(exposure)

    # Plot the histogram
    plt.figure()
    plt.hist(img.flatten(), bins=int(numOfBin), log=log)
    plt.title(name)

    if (saveFilePath is not None):
        plt.savefig(saveFilePath, bbox_inches="tight")
        plt.close()
    else:
        plt.show()
Esempio n. 5
0
def plotHist(exposure, name="", numOfBin=1000, log=False, saveFilePath=None):
    """Plot the histogram.

    Parameters
    ----------
    exposure : Exposure
        Data butler of exposure image.
    name : str, optional
        Image title name. (the default is "".)
    numOfBin : int, optional
        Number of bins. (the default is 1000.)
    log : bool, optional
        The histogram axis will be set to a log scale if log=True. (the default
        is False.)
    saveFilePath : str, optional
        Save image to file path. (the default is None.)
    """

    # Get the image data
    img = ButlerWrapper.getImageData(exposure)

    # Plot the histogram
    plt.figure()
    plt.hist(img.flatten(), bins=int(numOfBin), log=log)
    plt.title(name)

    if saveFilePath is None:
        plt.show()
    else:
        plt.savefig(saveFilePath, bbox_inches="tight")
        plt.close()
Esempio n. 6
0
def plotHist(exposure, name="", numOfBin=1000, log=False, saveFilePath=None):
    """Plot the histogram.

    Parameters
    ----------
    exposure : Exposure
        Data butler of exposure image.
    name : str, optional
        Image title name. (the default is "".)
    numOfBin : int, optional
        Number of bins. (the default is 1000.)
    log : bool, optional
        The histogram axis will be set to a log scale if log=True. (the default
        is False.)
    saveFilePath : str, optional
        Save image to file path. (the default is None.)
    """

    # Get the image data
    img = ButlerWrapper.getImageData(exposure)

    # Plot the histogram
    plt.figure()
    plt.hist(img.flatten(), bins=int(numOfBin), log=log)
    plt.title(name)

    if (saveFilePath is not None):
        plt.savefig(saveFilePath, bbox_inches="tight")
        plt.close()
    else:
        plt.show()
Esempio n. 7
0
class TestButlerWrapper(unittest.TestCase):
    """Test the butler wrapper class."""
    def setUp(self):

        self.inputs = os.path.join(getModulePath(), "tests", "testData",
                                   "repackagedPhoSimData")
        self.butlerWrapper = ButlerWrapper(self.inputs)

    def testGetRawExp(self):

        exposure = self._getRawExp()
        self.assertEqual(exposure.getDimensions()[0], 4176)
        self.assertEqual(exposure.getDimensions()[1], 4020)

    def _getRawExp(self):

        visit, raft, sensor = self._getDefaultSurveyMetaData()
        exposure = self.butlerWrapper.getRawExp(visit, raft, sensor)

        return exposure

    def _getDefaultSurveyMetaData(self):

        visit = 20
        raft = "R00"
        sensor = "S22"

        return visit, raft, sensor

    def testSetInputsAndOutputs(self):

        self.butlerWrapper.setInputsAndOutputs(inputs=self.inputs)

        exposure = self._getRawExp()
        self.assertEqual(exposure.getDimensions()[0], 4176)

    def testGetImageData(self):

        exposure = self._getRawExp()

        image = ButlerWrapper.getImageData(exposure)
        self.assertTrue(isinstance(image, np.ndarray))
        self.assertEqual(image.shape, (4020, 4176))
Esempio n. 8
0
    def setPostIsrCcdInputs(self, inputs):
        """Set inputs of post instrument signature removal (ISR) CCD images.

        Parameters
        ----------
        inputs : RepositoryArgs, dict, or str
            Can be a single item or a list. Provides arguments to load an
            existing repository (or repositories). String is assumed to be a
            URI and is used as the cfgRoot (URI to the location of the cfg
            file). (Local file system URI does not have to start with
            'file://' and in this way can be a relative path). The
            'RepositoryArgs' class can be used to provide more parameters with
            which to initialize a repository (such as 'mapper', 'mapperArgs',
            'tags', etc. See the 'RepositoryArgs' documentation for more
            details). A dict may be used as shorthand for a 'RepositoryArgs'
            class instance. The dict keys must match parameters to the
            'RepositoryArgs.__init__' function.
        """

        self.butlerWrapper = ButlerWrapper(inputs)
Esempio n. 9
0
def poltExposureImage(exposure, name="", scale="log", cmap="gray", vmin=None,
                      vmax=None, saveFilePath=None):
    """Plot the exposure image.

    Parameters
    ----------
    exposure : Exposure
        Data butler of exposure image.
    name : str, optional
        Image title name. (the default is "".)
    scale : str, optional
        Scale of image map (log or linear). (the default is "log".)
    cmap : str, optional
        Color map definition. (the default is "gray".)
    vmin : float, optional
        Mininum value to show. This normalizes the luminance data. (the default
        is None.)
    vmax : float, optional
        Maximum value to show. This normalizes the luminance data. (the default
        is None.)
    saveFilePath : str, optional
        Save image to file path. (the default is None.)
    """

    # Get the image data
    img = ButlerWrapper.getImageData(exposure)

    # Change the scale if needed
    if scale not in ("linear", "log"):
        print("No %s scale to choose. Only 'linear' and 'log' scales are allowed." % scale)
        return

    # Decide the norm in imshow for the ploting
    if (scale == "linear"):
        plotNorm = None
    elif (scale == "log"):
        if (img.min()) < 0:
            plotNorm = SymLogNorm(linthresh=0.03)
        else:
            plotNorm = LogNorm()

    # Plot the image
    plt.figure()
    plt.imshow(img, cmap=cmap, origin="lower", norm=plotNorm, vmin=vmin,
               vmax=vmax)
    plt.colorbar()
    plt.title(name)

    if (saveFilePath is not None):
        plt.savefig(saveFilePath, bbox_inches="tight")
        plt.close()
    else:
        plt.show()
Esempio n. 10
0
def poltExposureImage(exposure, name="", scale="log", cmap="gray", vmin=None, vmax=None, saveFilePath=None):
    """
    
    Plot the exposure image.
    
    Arguments:
        exposure {[exposure]} -- Data butler of exposure image.
    
    Keyword Arguments:
        name {[str]} -- Image title name. (default: {""})
        scale {[str]} -- Scale of image map (log or linear). (default: {"log"})
        cmap {[str]} -- Color map definition. (default: {"gray"})
        vmin {[float]} -- Mininum value to show. This normalizes the luminance data. (default: {None})
        vmax {[float]} -- Maximum value to show. This normalizes the luminance data. (default: {None})
        saveFilePath {[str]} -- Save image to file path. (default: {None})
    """
    # Get the image data
    img = ButlerWrapper.getImageData(exposure)

    # Change the scale if needed
    if scale not in ("linear", "log"):
        print("No %s scale to choose. Only 'linear' and 'log' scales are allowed." % scale)
        return

    # Decide the norm in imshow for the ploting
    if (scale == "linear"):
        plotNorm = None
    elif (scale == "log"):
        if (img.min()) < 0:
            plotNorm = SymLogNorm(linthresh=0.03)
        else:
            plotNorm = LogNorm()
    
    # Plot the image
    plt.figure()
    plt.imshow(img, cmap=cmap, origin="lower", norm=plotNorm, vmin=vmin, vmax=vmax)
    plt.colorbar()
    plt.title(name)

    if (saveFilePath is not None):
        plt.savefig(saveFilePath, bbox_inches="tight")
        plt.close()
    else:
        plt.show()
Esempio n. 11
0
    def setPostIsrCcdInputs(self, inputs):
        """Set inputs of post instrument signature removal (ISR) CCD images.

        Parameters
        ----------
        inputs : RepositoryArgs, dict, or str
            Can be a single item or a list. Provides arguments to load an
            existing repository (or repositories). String is assumed to be a
            URI and is used as the cfgRoot (URI to the location of the cfg
            file). (Local file system URI does not have to start with
            'file://' and in this way can be a relative path). The
            'RepositoryArgs' class can be used to provide more parameters with
            which to initialize a repository (such as 'mapper', 'mapperArgs',
            'tags', etc. See the 'RepositoryArgs' documentation for more
            details). A dict may be used as shorthand for a 'RepositoryArgs'
            class instance. The dict keys must match parameters to the
            'RepositoryArgs.__init__' function.
        """

        self.butlerWrapper = ButlerWrapper(inputs)
Esempio n. 12
0
class WepController(object):

    CORNER_WFS_LIST = [
        "R00_SW0",
        "R00_SW1",
        "R04_SW0",
        "R04_SW1",
        "R40_SW0",
        "R40_SW1",
        "R44_SW0",
        "R44_SW1",
    ]

    def __init__(self, dataCollector, isrWrapper, sourSelc, sourProc, wfEsti):
        """Initialize the wavefront estimation pipeline (WEP) controller class.

        Parameters
        ----------
        dataCollector : CamDataCollector
            Camera data collector.
        isrWrapper : CamIsrWrapper
            Instrument signature removal (ISR) wrapper.
        sourSelc : SourceSelector
            Source selector.
        sourProc : SourceProcessor
            Source processor.
        wfEsti : WfEstimator
            Wavefront estimator.
        """

        # Data collector to use DM ingest command line task
        self.dataCollector = dataCollector

        # ISR wrapper to use DM ISR command line task
        self.isrWrapper = isrWrapper

        # Source selector to query the bright star catalog
        self.sourSelc = sourSelc

        # Source processor to get the donut image or do the deblending
        self.sourProc = sourProc

        # Wavefront estimator to get zk
        self.wfEsti = wfEsti

        # Butler wrapper to use DM data butler
        self.butlerWrapper = None

    def getDataCollector(self):
        """Get the attribute of data collector.

        Returns
        -------
        CamDataCollector
            Data collector.
        """

        return self.dataCollector

    def getIsrWrapper(self):
        """Get the attribute of ISR wraper.

        ISR: Instrument signature removal.

        Returns
        -------
        CamIsrWrapper
            ISR wraper.
        """

        return self.isrWrapper

    def getSourSelc(self):
        """Get the attribute of source selector.

        Returns
        -------
        SourceSelector
            Source selector.
        """

        return self.sourSelc

    def getSourProc(self):
        """Get the attribute of source processor.

        Returns
        -------
        SourceProcessor
            Source processor.
        """

        return self.sourProc

    def getWfEsti(self):
        """Get the attribute of wavefront estimator.

        Returns
        -------
        WfEstimator
            Wavefront estimator.
        """

        return self.wfEsti

    def getButlerWrapper(self):
        """Get the attribute of butler wrapper.

        Returns
        -------
        ButlerWrapper
            Butler wrapper.
        """

        return self.butlerWrapper

    def setPostIsrCcdInputs(self, inputs):
        """Set inputs of post instrument signature removal (ISR) CCD images.

        Parameters
        ----------
        inputs : RepositoryArgs, dict, or str
            Can be a single item or a list. Provides arguments to load an
            existing repository (or repositories). String is assumed to be a
            URI and is used as the cfgRoot (URI to the location of the cfg
            file). (Local file system URI does not have to start with
            'file://' and in this way can be a relative path). The
            'RepositoryArgs' class can be used to provide more parameters with
            which to initialize a repository (such as 'mapper', 'mapperArgs',
            'tags', etc. See the 'RepositoryArgs' documentation for more
            details). A dict may be used as shorthand for a 'RepositoryArgs'
            class instance. The dict keys must match parameters to the
            'RepositoryArgs.__init__' function.
        """

        self.butlerWrapper = ButlerWrapper(inputs)

    def getPostIsrImgMapByPistonDefocal(self, sensorNameList, obsIdList):
        """Get the post ISR image map that the defocal images are by the
        pistion motion.

        Parameters
        ----------
        sensorNameList : list
            List of sensor name.
        obsIdList : list
            Observation Id list in [intraObsId, extraObsId].

        Returns
        -------
        dict
            Post-ISR image map. The dictionary key is the sensor name. The
            dictionary item is the defocal image on the camera coordinate.
            (type: DefocalImage).
        """

        return self._getImgMapByPistonDefocal(sensorNameList, obsIdList, ImageType.Amp)

    def _getImgMapByPistonDefocal(self, sensorNameList, obsIdList, imageType):
        """Get the image map that the defocal images are by the pistion motion.

        Parameters
        ----------
        sensorNameList : list
            List of sensor name.
        obsIdList : list
            Observation Id list in [intraObsId, extraObsId].
        imageType : ImageType
            Image type.

        Returns
        -------
        dict
            Image map. The dictionary key is the sensor name. The dictionary
            item is the defocal image on the camera coordinate. (type:
            DefocalImage).

        Raises
        ------
        ValueError
            The image type is not supported.
        """

        # Get the waveront image map
        wfsImgMap = dict()
        for sensorName in sensorNameList:

            # Get the sensor name information
            raft, sensor = self._getSensorInfo(sensorName)

            # The intra/ extra defocal images are decided by obsId
            imgList = []
            for visit in obsIdList:

                # Get the exposure image in ndarray
                if imageType == ImageType.Amp:
                    exp = self.butlerWrapper.getPostIsrCcd(int(visit), raft, sensor)
                elif imageType == ImageType.Eimg:
                    exp = self.butlerWrapper.getEimage(int(visit), raft, sensor)
                else:
                    raise ValueError("The %s is not supported." % imageType)

                img = self.butlerWrapper.getImageData(exp)

                # Transform the image in DM coordinate to camera coordinate.
                camImg = self._transImgDmCoorToCamCoor(img)

                # Collect the image
                imgList.append(camImg)

            wfsImgMap[sensorName] = DefocalImage(
                intraImg=imgList[0], extraImg=imgList[1]
            )

        return wfsImgMap

    def _getSensorInfo(self, sensorName):
        """Get the sensor information.

        Parameters
        ----------
        sensorName : str
            Sensor name (e.g. "R22_S11" or "R44_SW0"). Note: SW0 and SW1 are
            used as the wavefront sensor in lsst.obs.lsst.LsstCamMapper.

        Returns
        -------
        str
            Raft.
        str
            Sensor.

        Raises
        ------
        ValueError
            Can not match the raft and sensor from sensorName.
        """

        m = re.match(r"R(\d\d)_S([W\d]\d)", sensorName)
        if m is not None:
            raftId, sensorId = m.groups()
            raft = "R" + raftId
            sensor = "S" + sensorId
            return raft, sensor
        else:
            raise ValueError(f"Can not match the raft and sensor from {sensorName}")

    def _transImgDmCoorToCamCoor(self, dmImg):
        """Transfrom the image in DM coordinate to camera coordinate.

        The geometry transformation is defined in LSE-349.

        Parameters
        ----------
        dmImg : numpy.ndarray
            Image in DM coordinate.

        Returns
        -------
        numpy.ndarray
            Image is camera coordinate.
        """

        # For the PhoSim mapper, it is the transpose (x, y) to (y, x).
        camImg = dmImg.T

        return camImg

    def getEimgMapByPistonDefocal(self, sensorNameList, obsIdList):
        """Get the eimage map that the defocal images are by the pistion
        motion.

        Parameters
        ----------
        sensorNameList : list
            List of sensor name.
        obsIdList : list
            Observation Id list in [intraObsId, extraObsId].

        Returns
        -------
        dict
            Eimage map. The dictionary key is the sensor name. The dictionary
            item is the defocal image on the camera coordinate. (type:
            DefocalImage).
        """

        return self._getImgMapByPistonDefocal(sensorNameList, obsIdList, ImageType.Eimg)

    def getPostIsrImgMapOnCornerWfs(self, sensorNameList, obsId):
        """Get the post ISR image map of corner wavefront sensors.

        Parameters
        ----------
        sensorNameList : list
            List of sensor name.
        obsId : int
            Observation Id.

        Returns
        -------
        dict
            Post-ISR image map. The dictionary key is the sensor name. The
            dictionary item is the defocal image on the camera coordinate.
            (type: DefocalImage).
        """
        pass

    def getDonutMap(self, neighborStarMap, wfsImgMap, filterType, doDeblending=False):
        """Get the donut map on each wavefront sensor (WFS).

        Parameters
        ----------
        neighborStarMap : dict
            Information of neighboring stars and candidate stars with the name
            of sensor as a dictionary.
        wfsImgMap : dict
            Post-ISR image map. The dictionary key is the sensor name. The
            dictionary item is the defocal image on the camera coordinate.
            (type: DefocalImage).
        filterType : FilterType
            Filter type.
        doDeblending : bool, optional
            Do the deblending or not. If False, only consider the single donut
            based on the bright star catalog.(the default is False.)

        Returns
        -------
        dict
            Donut image map. The dictionary key is the sensor name. The
            dictionary item is the list of donut image (type:
            list[DonutImage]).
        """

        donutMap = dict()
        for sensorName, nbrStar in neighborStarMap.items():

            # Configure the source processor
            self.sourProc.config(sensorName=sensorName)

            # Get the defocal images: [intra, extra]
            defocalImgList = [
                wfsImgMap[sensorName].getIntraImg(),
                wfsImgMap[sensorName].getExtraImg(),
            ]

            # Get the bright star id list on specific sensor
            brightStarIdList = list(nbrStar.getId())
            for starIdIdx in range(len(brightStarIdList)):

                # Get the single star map
                for jj in range(len(defocalImgList)):

                    ccdImg = defocalImgList[jj]

                    # Get the segment of image
                    if ccdImg is not None:
                        (
                            singleSciNeiImg,
                            allStarPosX,
                            allStarPosY,
                            magRatio,
                            offsetX,
                            offsetY,
                        ) = self.sourProc.getSingleTargetImage(
                            ccdImg, nbrStar, starIdIdx, filterType
                        )

                        # Only consider the single donut if no deblending
                        if (not doDeblending) and (len(magRatio) != 1):
                            continue

                        # Get the single donut/ deblended image
                        if (len(magRatio) == 1) or (not doDeblending):
                            imgDeblend = singleSciNeiImg

                            if len(magRatio) == 1:
                                realcx, realcy = searchDonutPos(imgDeblend)
                            else:
                                realcx = allStarPosX[-1]
                                realcy = allStarPosY[-1]

                        # Do the deblending or not
                        elif len(magRatio) == 2 and doDeblending:
                            imgDeblend, realcx, realcy = self.sourProc.doDeblending(
                                singleSciNeiImg, allStarPosX, allStarPosY, magRatio
                            )
                            # Update the magnitude ratio
                            magRatio = [1]

                        else:
                            continue

                        # Extract the image
                        if len(magRatio) == 1:
                            sizeInPix = self.wfEsti.getSizeInPix()
                            x0 = np.floor(realcx - sizeInPix / 2).astype("int")
                            y0 = np.floor(realcy - sizeInPix / 2).astype("int")
                            imgDeblend = imgDeblend[
                                y0 : y0 + sizeInPix, x0 : x0 + sizeInPix
                            ]

                        # Rotate the image if the sensor is the corner
                        # wavefront sensor
                        if sensorName in self.CORNER_WFS_LIST:

                            # Get the Euler angle
                            eulerZangle = round(
                                self.sourProc.getEulerZinDeg(sensorName)
                            )

                            # Change the sign if the angle < 0
                            while eulerZangle < 0:
                                eulerZangle += 360

                            # Do the rotation of matrix
                            numOfRot90 = eulerZangle // 90
                            imgDeblend = np.flipud(
                                np.rot90(np.flipud(imgDeblend), numOfRot90)
                            )

                        # Put the deblended image into the donut map
                        if sensorName not in donutMap.keys():
                            donutMap[sensorName] = []

                        # Check the donut exists in the list or not
                        starId = brightStarIdList[starIdIdx]
                        donutIndex = self._searchDonutListId(
                            donutMap[sensorName], starId
                        )

                        # Create the donut object and put into the list if it
                        # is needed
                        if donutIndex < 0:

                            # Calculate the field X, Y
                            pixelX = realcx + offsetX
                            pixelY = realcy + offsetY
                            fieldX, fieldY = self.sourProc.camXYtoFieldXY(
                                pixelX, pixelY
                            )

                            # Instantiate the DonutImage class
                            donutImg = DonutImage(
                                starId, pixelX, pixelY, fieldX, fieldY
                            )
                            donutMap[sensorName].append(donutImg)

                            # Search for the donut index again
                            donutIndex = self._searchDonutListId(
                                donutMap[sensorName], starId
                            )

                        # Get the donut image list
                        donutList = donutMap[sensorName]

                        # Take the absolute value for images, which might
                        # contain the negative value after the ISR correction.
                        # This happens for the amplifier images.
                        imgDeblend = np.abs(imgDeblend)

                        # Set the intra focal image
                        if jj == 0:
                            donutList[donutIndex].setImg(intraImg=imgDeblend)
                        # Set the extra focal image
                        elif jj == 1:
                            donutList[donutIndex].setImg(extraImg=imgDeblend)

        return donutMap

    def _searchDonutListId(self, donutList, starId):
        """Search the bright star ID in the donut list.

        Parameters
        ----------
        donutList : list
            List of DonutImage object.
        starId : int
            Star Id.

        Returns
        -------
        int
             Index of donut image object with the specific starId.
        """

        index = -1
        for ii in range(len(donutList)):
            if donutList[ii].getStarId() == int(starId):
                index = ii
                break

        return index

    def calcWfErr(self, donutMap):
        """Calculate the wavefront error in annular Zernike polynomials
        (z4-z22).

        Parameters
        ----------
        donutMap : dict
            Donut image map. The dictionary key is the sensor name. The
            dictionary item is the donut image (type: DonutImage).

        Returns
        -------
        dict
            Donut image map with calculated wavefront error.
        """

        for sensorName, donutList in donutMap.items():

            for ii in range(len(donutList)):

                # Get the intra- and extra-focal donut images

                # Check the sensor is the corner WFS or not.
                # Only consider "intra" WFS.
                # For LsstSimMapper, Intra: C0 -> A; Extra: C1 -> B
                # For LsstCamMapper, Intra: C0 -> SW1; Extra: C1 ->SW0

                # Look for the intra-focal image
                if sensorName.endswith("SW1"):
                    intraDonut = donutList[ii]

                    # Get the extra-focal sensor name
                    extraFocalSensorName = sensorName.replace("SW1", "SW0")

                    # Get the donut list of extra-focal sensor
                    extraDonutList = donutMap[extraFocalSensorName]
                    if ii < len(extraDonutList):
                        extraDonut = extraDonutList[ii]
                    else:
                        continue
                # Pass the extra-focal image
                elif sensorName.endswith("SW0"):
                    continue
                # Scientific sensor
                else:
                    intraDonut = extraDonut = donutList[ii]

                # Calculate the wavefront error

                # Get the field X, Y position
                intraFieldXY = intraDonut.getFieldPos()
                extraFieldXY = extraDonut.getFieldPos()

                # Get the defocal images
                intraImg = intraDonut.getIntraImg()
                extraImg = extraDonut.getExtraImg()

                # Calculate the wavefront error
                zer4UpNm = self._calcSglWfErr(
                    intraImg, extraImg, intraFieldXY, extraFieldXY
                )

                # Put the value to the donut image
                intraDonut.setWfErr(zer4UpNm)
                extraDonut.setWfErr(zer4UpNm)

        # Intentionally to expose this return value to show the input,
        # donutMap, has been modified.
        return donutMap

    def _calcSglWfErr(self, intraImg, extraImg, intraFieldXY, extraFieldXY):
        """Calculate the wavefront error in annular Zernike polynomials
        (z4-z22) for single donut.

        Parameters
        ----------
        intraImg : numpy.ndarray
            Intra-focal donut image.
        extraImg : numpy.ndarray
            Extra-focal donut image.
        intraFieldXY : tuple
            Field x, y in degree of intra-focal donut image.
        extraFieldXY : tuple
            Field x, y in degree of extra-focal donut image.

        Returns
        -------
        numpy.ndarray
            Coefficients of Zernike polynomials (z4 - z22) in nm.
        """

        # Set the images
        self.wfEsti.setImg(intraFieldXY, DefocalType.Intra, image=intraImg)
        self.wfEsti.setImg(extraFieldXY, DefocalType.Extra, image=extraImg)

        # Reset the wavefront estimator
        self.wfEsti.reset()

        # Calculate the wavefront error
        zer4UpNm = self.wfEsti.calWfsErr()

        return zer4UpNm

    def calcAvgWfErrOnSglCcd(self, donutList):
        """Calculate the average of wavefront error on single CCD.

        CCD: Charge-coupled device.

        Parameters
        ----------
        donutList : list
            List of donut object (type: DonutImage).

        Returns
        -------
        numpy.ndarray
            Average of wavefront error in nm.
        """

        # Calculate the weighting of donut image
        wgtRatio = self._calcWeiRatio(donutList)

        # Calculate the mean wavefront error
        avgErr = 0
        for ii in range(len(donutList)):

            donut = donutList[ii]
            zer4UpNmArr = donut.getWfErr()

            # Assign the zer4UpNm
            if len(zer4UpNmArr) == 0:
                zer4UpNm = 0
            else:
                zer4UpNm = zer4UpNmArr

            avgErr = avgErr + wgtRatio[ii] * zer4UpNm

        return avgErr

    def _calcWeiRatio(self, donutList):
        """Calculate the weighting ratio of donut in the list.

        Parameters
        ----------
        donutList : list
            List of donut object (type: DonutImage).

        Returns
        -------
        numpy.ndarray
            Array of weighting ratio of donuts to do the average of wavefront
            error.
        """

        # Weighting of donut image. Use the simple average at this moment.
        # Need to consider the S/N and other factors in the future.

        # Check the available zk and give the ratio
        wgtRatio = []
        for donut in donutList:
            if len(donut.getWfErr()) == 0:
                wgtRatio.append(0)
            else:
                wgtRatio.append(1)

        # Do the normalization
        wgtRatioArr = np.array(wgtRatio)
        normalizedwgtRatioArr = wgtRatioArr / np.sum(wgtRatioArr)

        return normalizedwgtRatioArr

    def genMasterDonut(self, donutMap, zcCol=np.zeros(22)):
        """Generate the master donut map.

        Parameters
        ----------
        donutMap : dict
            Donut image map. The dictionary key is the sensor name. The
            dictionary item is the donut image (type: DonutImage).
        zcCol : numpy.ndarray, optional
            Coefficients of wavefront (z1-z22) in nm. (the default is
            np.zeros(22).)

        Returns
        -------
        dict
            Master donut image map. The dictionary key is the sensor name. The
            dictionary item is the master donut image (type: DonutImage).
        """

        masterDonutMap = dict()
        for sensorName, donutList in donutMap.items():

            # Get the master donut on single CCD
            masterDonut = self._genMasterImgOnSglCcd(donutList, zcCol=zcCol)

            # Put the master donut to donut map
            masterDonutMap[sensorName] = [masterDonut]

        return masterDonutMap

    def _genMasterImgOnSglCcd(self, donutList, zcCol):
        """Generate the master donut image on single CCD.

        CCD: Charge-coupled device.

        Parameters
        ----------
        sensorName : str
            Canonical sensor name (e.g. "R:2,2 S:1,1").
        donutList : list
            List of donut object (type: DonutImage).
        zcCol : numpy.ndarray
            Coefficients of wavefront (z1-z22) in nm.

        Returns
        -------
        DonutImage
            Master donut.
        """

        intraProjImgList = []
        extraProjImgList = []
        for donut in donutList:

            # Get the field x, y
            fieldXY = donut.getFieldPos()

            # Set the image
            intraImg = donut.getIntraImg()
            if intraImg is not None:

                # Get the projected image
                projImg = self._getProjImg(fieldXY, DefocalType.Intra, intraImg, zcCol)

                # Collect the projected donut
                intraProjImgList.append(projImg)

            extraImg = donut.getExtraImg()
            if extraImg is not None:

                # Get the projected image
                projImg = self._getProjImg(fieldXY, DefocalType.Extra, extraImg, zcCol)

                # Collect the projected donut
                extraProjImgList.append(projImg)

        # Generate the master donut
        stackIntraImg = self._stackImg(intraProjImgList)
        stackExtraImg = self._stackImg(extraProjImgList)

        # Put the master donut to donut map
        pixelX, pixelY = searchDonutPos(stackIntraImg)
        masterDonut = DonutImage(
            0, pixelX, pixelY, 0, 0, intraImg=stackIntraImg, extraImg=stackExtraImg
        )

        return masterDonut

    def _getProjImg(self, fieldXY, defocalType, defocalImg, zcCol):
        """Get the projected image on the pupil.

        Parameters
        ----------
        fieldXY : tuple
            Position of donut on the focal plane in the degree for intra- and
            extra-focal images (field X, field Y).
        defocalType : enum 'DefocalType'
            Defocal type of image.
        defocalImg : numpy.ndarray
            Donut defocal image.
        zcCol : numpy.ndarray
            Coefficients of wavefront (z1-z22).

        Returns
        -------
        numpy.ndarray
            Projected image.
        """

        # Set the image
        self.wfEsti.setImg(fieldXY, defocalType, image=defocalImg)

        # Get the image in the type of CompensationImageDecorator
        if defocalType == DefocalType.Intra:
            img = self.wfEsti.getIntraImg()
        elif defocalType == DefocalType.Extra:
            img = self.wfEsti.getExtraImg()

        # Get the distortion correction (offaxis)
        algo = self.wfEsti.getAlgo()
        offAxisCorrOrder = algo.getOffAxisPolyOrder()

        inst = self.wfEsti.getInst()
        img.setOffAxisCorr(inst, offAxisCorrOrder)

        # Do the image cocenter
        img.imageCoCenter(inst)

        # Do the compensation/ projection
        img.compensate(inst, algo, zcCol, self.wfEsti.getOptModel())

        # Return the projected image
        return img.getImg()

    def _stackImg(self, imgList):
        """Stack the images.

        Parameters
        ----------
        imgList : list
            List of image.

        Returns
        -------
        numpy.ndarray
            Stacked image.
        """

        if len(imgList) == 0:
            stackImg = None
        else:
            # Get the minimun image dimension
            dimXlist = []
            dimYlist = []
            for img in imgList:
                dy, dx = img.shape
                dimXlist.append(dx)
                dimYlist.append(dy)

            dimX = np.min(dimXlist)
            dimY = np.min(dimYlist)

            deltaX = dimX // 2
            deltaY = dimY // 2

            # Stack the image by summation directly
            stackImg = np.zeros([dimY, dimX])
            for img in imgList:
                dy, dx = img.shape
                cy = int(dy / 2)
                cx = int(dx / 2)
                stackImg += img[cy - deltaY : cy + deltaY, cx - deltaX : cx + deltaX]

        return stackImg
Esempio n. 13
0
class WepController(object):

    CORNER_WFS_LIST = ["R:0,0 S:2,2,A", "R:0,0 S:2,2,B", "R:0,4 S:2,0,A",
                       "R:0,4 S:2,0,B", "R:4,0 S:0,2,A", "R:4,0 S:0,2,B",
                       "R:4,4 S:0,0,A", "R:4,4 S:0,0,B"]

    def __init__(self, dataCollector, isrWrapper, sourSelc, sourProc, wfEsti):
        """Initialize the wavefront estimation pipeline (WEP) controller class.

        Parameters
        ----------
        dataCollector : CamDataCollector
            Camera data collector.
        isrWrapper : CamIsrWrapper
            Instrument signature removal (ISR) wrapper.
        sourSelc : SourceSelector
            Source selector.
        sourProc : SourceProcessor
            Source processor.
        wfEsti : WfEstimator
            Wavefront estimator.
        """

        self.dataCollector = dataCollector
        self.isrWrapper = isrWrapper
        self.sourSelc = sourSelc
        self.sourProc = sourProc
        self.wfEsti = wfEsti

        self.butlerWrapper = None

    def getDataCollector(self):
        """Get the attribute of data collector.

        Returns
        -------
        CamDataCollector
            Data collector.
        """

        return self.dataCollector

    def getIsrWrapper(self):
        """Get the attribute of ISR wraper.

        ISR: Instrument signature removal.

        Returns
        -------
        CamIsrWrapper
            ISR wraper.
        """

        return self.isrWrapper

    def getSourSelc(self):
        """Get the attribute of source selector.

        Returns
        -------
        SourceSelector
            Source selector.
        """

        return self.sourSelc

    def getSourProc(self):
        """Get the attribute of source processor.

        Returns
        -------
        SourceProcessor
            Source processor.
        """

        return self.sourProc

    def getWfEsti(self):
        """Get the attribute of wavefront estimator.

        Returns
        -------
        WfEstimator
            Wavefront estimator.
        """

        return self.wfEsti

    def getButlerWrapper(self):
        """Get the attribute of butler wrapper.

        Returns
        -------
        ButlerWrapper
            Butler wrapper.
        """

        return self.butlerWrapper

    def setPostIsrCcdInputs(self, inputs):
        """Set inputs of post instrument signature removal (ISR) CCD images.

        Parameters
        ----------
        inputs : RepositoryArgs, dict, or str
            Can be a single item or a list. Provides arguments to load an
            existing repository (or repositories). String is assumed to be a
            URI and is used as the cfgRoot (URI to the location of the cfg
            file). (Local file system URI does not have to start with
            'file://' and in this way can be a relative path). The
            'RepositoryArgs' class can be used to provide more parameters with
            which to initialize a repository (such as 'mapper', 'mapperArgs',
            'tags', etc. See the 'RepositoryArgs' documentation for more
            details). A dict may be used as shorthand for a 'RepositoryArgs'
            class instance. The dict keys must match parameters to the
            'RepositoryArgs.__init__' function.
        """

        self.butlerWrapper = ButlerWrapper(inputs)

    def getPostIsrImgMapByPistonDefocal(self, sensorNameList, obsIdList):
        """Get the post ISR image map that the defocal images are by the
        pistion motion.

        Parameters
        ----------
        sensorNameList : list
            List of sensor name.
        obsIdList : list
            Observation Id list in [intraObsId, extraObsId].

        Returns
        -------
        dict
            Post-ISR image map. The dictionary key is the sensor name. The
            dictionary item is the defocal image on the camera coordinate.
            (type: DefocalImage).
        """

        # Get the waveront image map
        wfsImgMap = dict()
        for sensorName in sensorNameList:

            # Get the sensor name information
            raft, sensor = self._getSensorInfo(sensorName)[0:2]

            # The intra/ extra defocal images are decided by obsId
            imgList = []
            for visit in obsIdList:

                # Get the exposure image in ndarray
                exp = self.butlerWrapper.getPostIsrCcd(int(visit), raft, sensor)
                img = self.butlerWrapper.getImageData(exp)

                # Transform the image in DM coordinate to camera coordinate.
                camImg = self._transImgDmCoorToCamCoor(img)

                # Collect the image
                imgList.append(camImg)

            wfsImgMap[sensorName] = DefocalImage(intraImg=imgList[0],
                                                 extraImg=imgList[1])

        return wfsImgMap

    def _transImgDmCoorToCamCoor(self, dmImg):
        """Transfrom the image in DM coordinate to camera coordinate.

        Parameters
        ----------
        dmImg : numpy.ndarray
            Image in DM coordinate.

        Returns
        -------
        numpy.ndarray
            Image is camera coordinate.
        """

        # The relationship between DM and camera coordinate should be 90 degree
        # difference.
        # For the PhoSim mapper, it is the transpose (x, y) to (y, x). This
        # should be fixed.

        camImg = dmImg.T

        return camImg

    def getPostIsrImgMapOnCornerWfs(self, sensorNameList, obsId):
        """Get the post ISR image map of corner wavefront sensors.

        Parameters
        ----------
        sensorNameList : list
            List of sensor name.
        obsId : int
            Observation Id.

        Returns
        -------
        dict
            Post-ISR image map. The dictionary key is the sensor name. The
            dictionary item is the defocal image on the camera coordinate.
            (type: DefocalImage).
        """
        pass

    def _getSensorInfo(self, sensorName):
        """Get the sensor information.

        Parameters
        ----------
        sensorName : str
            Sensor name (e.g. "R:2,2 S:1,1" or "R:0,0 S:2,2,A")

        Returns
        -------
        str
            Raft.
        str
            Sensor.
        str
            Channel.
        """

        raft = sensor = channel = None

        # Use the regular expression to analyze the input name
        m = re.match(r"R:(\d,\d) S:(\d,\d)(?:,([A,B]))?$", sensorName)
        if (m is not None):
            raft, sensor, channel = m.groups()[0:3]

        # This is for the phosim mapper use.
        # For example, raft is "R22" and sensor is "S11".
        raftAbbName = "R" + raft[0] + raft[-1]
        sensorAbbName = "S" + sensor[0] + sensor[-1]

        return raftAbbName, sensorAbbName, channel

    def getDonutMap(self, neighborStarMap, wfsImgMap, filterType,
                    doDeblending=False):
        """Get the donut map on each wavefront sensor (WFS).

        Parameters
        ----------
        neighborStarMap : dict
            Information of neighboring stars and candidate stars with the name
            of sensor as a dictionary.
        wfsImgMap : dict
            Post-ISR image map. The dictionary key is the sensor name. The
            dictionary item is the defocal image on the camera coordinate.
            (type: DefocalImage).
        filterType : FilterType
            Filter type.
        doDeblending : bool, optional
            Do the deblending or not. If False, only consider the single donut
            based on the bright star catalog.(the default is False.)

        Returns
        -------
        dict
            Donut image map. The dictionary key is the sensor name. The
            dictionary item is the list of donut image (type:
            list[DonutImage]).
        """

        donutMap = dict()
        for sensorName, nbrStar in neighborStarMap.items():

            # Get the abbraviated sensor name
            abbrevName = abbrevDectectorName(sensorName)

            # Configure the source processor
            self.sourProc.config(sensorName=abbrevName)

            # Get the defocal images: [intra, extra]
            defocalImgList = [wfsImgMap[sensorName].getIntraImg(),
                              wfsImgMap[sensorName].getExtraImg()]

            # Get the bright star id list on specific sensor
            brightStarIdList = list(nbrStar.getId())
            for starIdIdx in range(len(brightStarIdList)):

                # Get the single star map
                for jj in range(len(defocalImgList)):

                    ccdImg = defocalImgList[jj]

                    # Get the segment of image
                    if (ccdImg is not None):
                        singleSciNeiImg, allStarPosX, allStarPosY, magRatio, \
                            offsetX, offsetY = \
                            self.sourProc.getSingleTargetImage(
                                ccdImg, nbrStar, starIdIdx, filterType)

                        # Only consider the single donut if no deblending
                        if (not doDeblending) and (len(magRatio) != 1):
                            continue

                        # Get the single donut/ deblended image
                        if (len(magRatio) == 1) or (not doDeblending):
                            imgDeblend = singleSciNeiImg

                            if (len(magRatio) == 1):
                                realcx, realcy = searchDonutPos(imgDeblend)
                            else:
                                realcx = allStarPosX[-1]
                                realcy = allStarPosY[-1]

                        # Do the deblending or not
                        elif (len(magRatio) == 2 and doDeblending):
                            imgDeblend, realcx, realcy = \
                                self.sourProc.doDeblending(
                                    singleSciNeiImg, allStarPosX, allStarPosY,
                                    magRatio)
                            # Update the magnitude ratio
                            magRatio = [1]

                        else:
                            continue

                        # Extract the image
                        if (len(magRatio) == 1):
                            sizeInPix = self.wfEsti.getSizeInPix()
                            x0 = np.floor(realcx - sizeInPix / 2).astype("int")
                            y0 = np.floor(realcy - sizeInPix / 2).astype("int")
                            imgDeblend = imgDeblend[y0:y0 + sizeInPix,
                                                    x0:x0 + sizeInPix]

                        # Rotate the image if the sensor is the corner
                        # wavefront sensor
                        if sensorName in self.CORNER_WFS_LIST:

                            # Get the Euler angle
                            eulerZangle = round(self.sourProc.getEulerZinDeg(
                                abbrevName))

                            # Change the sign if the angle < 0
                            while (eulerZangle < 0):
                                eulerZangle += 360

                            # Do the rotation of matrix
                            numOfRot90 = eulerZangle // 90
                            imgDeblend = np.flipud(
                                np.rot90(np.flipud(imgDeblend), numOfRot90))

                        # Put the deblended image into the donut map
                        if sensorName not in donutMap.keys():
                            donutMap[sensorName] = []

                        # Check the donut exists in the list or not
                        starId = brightStarIdList[starIdIdx]
                        donutIndex = self._searchDonutListId(
                            donutMap[sensorName], starId)

                        # Create the donut object and put into the list if it
                        # is needed
                        if (donutIndex < 0):

                            # Calculate the field X, Y
                            pixelX = realcx + offsetX
                            pixelY = realcy + offsetY
                            fieldX, fieldY = self.sourProc.camXYtoFieldXY(
                                pixelX, pixelY)

                            # Instantiate the DonutImage class
                            donutImg = DonutImage(starId, pixelX, pixelY,
                                                  fieldX, fieldY)
                            donutMap[sensorName].append(donutImg)

                            # Search for the donut index again
                            donutIndex = self._searchDonutListId(
                                donutMap[sensorName], starId)

                        # Get the donut image list
                        donutList = donutMap[sensorName]

                        # Take the absolute value for images, which might
                        # contain the negative value after the ISR correction.
                        # This happens for the amplifier images.
                        imgDeblend = np.abs(imgDeblend)

                        # Set the intra focal image
                        if (jj == 0):
                            donutList[donutIndex].setImg(intraImg=imgDeblend)
                        # Set the extra focal image
                        elif (jj == 1):
                            donutList[donutIndex].setImg(extraImg=imgDeblend)

        return donutMap

    def _searchDonutListId(self, donutList, starId):
        """Search the bright star ID in the donut list.

        Parameters
        ----------
        donutList : list
            List of DonutImage object.
        starId : int
            Star Id.

        Returns
        -------
        int
             Index of donut image object with the specific starId.
        """

        index = -1
        for ii in range(len(donutList)):
            if (donutList[ii].getStarId() == int(starId)):
                index = ii
                break

        return index

    def calcWfErr(self, donutMap):
        """Calculate the wavefront error in annular Zernike polynomials
        (z4-z22).

        Parameters
        ----------
        donutMap : dict
            Donut image map. The dictionary key is the sensor name. The
            dictionary item is the donut image (type: DonutImage).

        Returns
        -------
        dict
            Donut image map with calculated wavefront error.
        """

        for sensorName, donutList in donutMap.items():

            for ii in range(len(donutList)):

                # Get the intra- and extra-focal donut images

                # Check the sensor is the corner WFS or not. Only consider "A"
                # Intra: C0 -> A; Extra: C1 -> B

                # Look for the intra-focal image
                if sensorName.endswith("A"):
                    intraDonut = donutList[ii]

                    # Get the extra-focal sensor name
                    extraFocalSensorName = sensorName.replace("A", "B")

                    # Get the donut list of extra-focal sensor
                    extraDonutList = donutMap[extraFocalSensorName]
                    if (ii < len(extraDonutList)):
                        extraDonut = extraDonutList[ii]
                    else:
                        continue
                # Pass the extra-focal image
                elif sensorName.endswith("B"):
                    continue
                # Scientific sensor
                else:
                    intraDonut = extraDonut = donutList[ii]

                # Calculate the wavefront error

                # Get the field X, Y position
                intraFieldXY = intraDonut.getFieldPos()
                extraFieldXY = extraDonut.getFieldPos()

                # Get the defocal images
                intraImg = intraDonut.getIntraImg()
                extraImg = extraDonut.getExtraImg()

                # Calculate the wavefront error
                zer4UpNm = self._calcSglWfErr(intraImg, extraImg, intraFieldXY,
                                              extraFieldXY)

                # Put the value to the donut image
                intraDonut.setWfErr(zer4UpNm)
                extraDonut.setWfErr(zer4UpNm)

        # Intentionally to expose this return value to show the input,
        # donutMap, has been modified.
        return donutMap

    def _calcSglWfErr(self, intraImg, extraImg, intraFieldXY, extraFieldXY):
        """Calculate the wavefront error in annular Zernike polynomials
        (z4-z22) for single donut.

        Parameters
        ----------
        intraImg : numpy.ndarray
            Intra-focal donut image.
        extraImg : numpy.ndarray
            Extra-focal donut image.
        intraFieldXY : tuple
            Field x, y in degree of intra-focal donut image.
        extraFieldXY : tuple
            Field x, y in degree of extra-focal donut image.

        Returns
        -------
        numpy.ndarray
            Coefficients of Zernike polynomials (z4 - z22) in nm.
        """

        # Set the images
        self.wfEsti.setImg(intraFieldXY, DefocalType.Intra, image=intraImg)
        self.wfEsti.setImg(extraFieldXY, DefocalType.Extra, image=extraImg)

        # Reset the wavefront estimator
        self.wfEsti.reset()

        # Calculate the wavefront error
        zer4UpNm = self.wfEsti.calWfsErr()

        return zer4UpNm

    def calcAvgWfErrOnSglCcd(self, donutList):
        """Calculate the average of wavefront error on single CCD.

        CCD: Charge-coupled device.

        Parameters
        ----------
        donutList : list
            List of donut object (type: DonutImage).

        Returns
        -------
        numpy.ndarray
            Average of wavefront error in nm.
        """

        # Calculate the weighting of donut image
        wgtRatio = self._calcWeiRatio(donutList)

        # Calculate the mean wavefront error
        avgErr = 0
        for ii in range(len(donutList)):

            donut = donutList[ii]
            zer4UpNmArr = donut.getWfErr()

            # Assign the zer4UpNm
            if (len(zer4UpNmArr) == 0):
                zer4UpNm = 0
            else:
                zer4UpNm = zer4UpNmArr

            avgErr = avgErr + wgtRatio[ii] * zer4UpNm

        return avgErr

    def _calcWeiRatio(self, donutList):
        """Calculate the weighting ratio of donut in the list.

        Parameters
        ----------
        donutList : list
            List of donut object (type: DonutImage).

        Returns
        -------
        numpy.ndarray
            Array of weighting ratio of donuts to do the average of wavefront
            error.
        """

        # Weighting of donut image. Use the simple average at this moment.
        # Need to consider the S/N and other factors in the future.

        # Check the available zk and give the ratio
        wgtRatio = []
        for donut in donutList:
            if (len(donut.getWfErr()) == 0):
                wgtRatio.append(0)
            else:
                wgtRatio.append(1)

        # Do the normalization
        wgtRatioArr = np.array(wgtRatio)
        normalizedwgtRatioArr = wgtRatioArr / np.sum(wgtRatioArr)

        return normalizedwgtRatioArr

    def genMasterDonut(self, donutMap, zcCol=np.zeros(22)):
        """Generate the master donut map.

        Parameters
        ----------
        donutMap : dict
            Donut image map. The dictionary key is the sensor name. The
            dictionary item is the donut image (type: DonutImage).
        zcCol : numpy.ndarray, optional
            Coefficients of wavefront (z1-z22) in nm. (the default is
            np.zeros(22).)

        Returns
        -------
        dict
            Master donut image map. The dictionary key is the sensor name. The
            dictionary item is the master donut image (type: DonutImage).
        """

        masterDonutMap = dict()
        for sensorName, donutList in donutMap.items():

            # Get the master donut on single CCD
            masterDonut = self._genMasterImgOnSglCcd(donutList, zcCol=zcCol)

            # Put the master donut to donut map
            masterDonutMap[sensorName] = [masterDonut]

        return masterDonutMap

    def _genMasterImgOnSglCcd(self, donutList, zcCol):
        """Generate the master donut image on single CCD.

        CCD: Charge-coupled device.

        Parameters
        ----------
        sensorName : str
            Canonical sensor name (e.g. "R:2,2 S:1,1").
        donutList : list
            List of donut object (type: DonutImage).
        zcCol : numpy.ndarray
            Coefficients of wavefront (z1-z22) in nm.

        Returns
        -------
        DonutImage
            Master donut.
        """

        intraProjImgList = []
        extraProjImgList = []
        for donut in donutList:

            # Get the field x, y
            fieldXY = donut.getFieldPos()

            # Set the image
            intraImg = donut.getIntraImg()
            if (intraImg is not None):

                # Get the projected image
                projImg = self._getProjImg(fieldXY, DefocalType.Intra,
                                           intraImg, zcCol)

                # Collect the projected donut
                intraProjImgList.append(projImg)

            extraImg = donut.getExtraImg()
            if (extraImg is not None):

                # Get the projected image
                projImg = self._getProjImg(fieldXY, DefocalType.Extra,
                                           extraImg, zcCol)

                # Collect the projected donut
                extraProjImgList.append(projImg)

        # Generate the master donut
        stackIntraImg = self._stackImg(intraProjImgList)
        stackExtraImg = self._stackImg(extraProjImgList)

        # Put the master donut to donut map
        pixelX, pixelY = searchDonutPos(stackIntraImg)
        masterDonut = DonutImage(0, pixelX, pixelY, 0, 0,
                                 intraImg=stackIntraImg, extraImg=stackExtraImg)

        return masterDonut

    def _getProjImg(self, fieldXY, defocalType, defocalImg, zcCol):
        """Get the projected image on the pupil.

        Parameters
        ----------
        fieldXY : tuple
            Position of donut on the focal plane in the degree for intra- and
            extra-focal images (field X, field Y).
        defocalType : enum 'DefocalType'
            Defocal type of image.
        defocalImg : numpy.ndarray
            Donut defocal image.
        zcCol : numpy.ndarray
            Coefficients of wavefront (z1-z22).

        Returns
        -------
        numpy.ndarray
            Projected image.
        """

        # Set the image
        self.wfEsti.setImg(fieldXY, defocalType, image=defocalImg)

        # Get the image in the type of CompensationImageDecorator
        if (defocalType == DefocalType.Intra):
            img = self.wfEsti.getIntraImg()
        elif (defocalType == DefocalType.Extra):
            img = self.wfEsti.getExtraImg()

        # Get the distortion correction (offaxis)
        algo = self.wfEsti.getAlgo()
        offAxisCorrOrder = algo.getOffAxisPolyOrder()

        inst = self.wfEsti.getInst()
        img.setOffAxisCorr(inst, offAxisCorrOrder)

        # Do the image cocenter
        img.imageCoCenter(inst)

        # Do the compensation/ projection
        img.compensate(inst, algo, zcCol, self.wfEsti.getOptModel())

        # Return the projected image
        return img.getImg()

    def _stackImg(self, imgList):
        """Stack the images.

        Parameters
        ----------
        imgList : list
            List of image.

        Returns
        -------
        numpy.ndarray
            Stacked image.
        """

        if (len(imgList) == 0):
            stackImg = None
        else:
            # Get the minimun image dimension
            dimXlist = []
            dimYlist = []
            for img in imgList:
                dy, dx = img.shape
                dimXlist.append(dx)
                dimYlist.append(dy)

            dimX = np.min(dimXlist)
            dimY = np.min(dimYlist)

            deltaX = dimX//2
            deltaY = dimY//2

            # Stack the image by summation directly
            stackImg = np.zeros([dimY, dimX])
            for img in imgList:
                dy, dx = img.shape
                cy = int(dy / 2)
                cx = int(dx / 2)
                stackImg += img[cy - deltaY:cy + deltaY,
                                cx - deltaX:cx + deltaX]

        return stackImg
Esempio n. 14
0
    def setUp(self):

        self.inputs = os.path.join(getModulePath(), "tests", "testData",
                                   "repackagedPhoSimData")
        self.butlerWrapper = ButlerWrapper(self.inputs)
Esempio n. 15
0
def poltExposureImage(exposure,
                      name="",
                      scale="log",
                      cmap="gray",
                      vmin=None,
                      vmax=None,
                      saveFilePath=None):
    """Plot the exposure image.

    Parameters
    ----------
    exposure : Exposure
        Data butler of exposure image.
    name : str, optional
        Image title name. (the default is "".)
    scale : str, optional
        Scale of image map (log or linear). (the default is "log".)
    cmap : str, optional
        Color map definition. (the default is "gray".)
    vmin : float, optional
        Mininum value to show. This normalizes the luminance data. (the default
        is None.)
    vmax : float, optional
        Maximum value to show. This normalizes the luminance data. (the default
        is None.)
    saveFilePath : str, optional
        Save image to file path. (the default is None.)
    """

    # Get the image data
    img = ButlerWrapper.getImageData(exposure)

    # Change the scale if needed
    if scale not in ("linear", "log"):
        print(
            "No %s scale to choose. Only 'linear' and 'log' scales are allowed."
            % scale)
        return

    # Decide the norm in imshow for the ploting
    if scale == "linear":
        plotNorm = None
    elif scale == "log":
        if (img.min()) < 0:
            plotNorm = SymLogNorm(linthresh=0.03)
        else:
            plotNorm = LogNorm()

    # Plot the image
    plt.figure()
    plt.imshow(img,
               cmap=cmap,
               origin="lower",
               norm=plotNorm,
               vmin=vmin,
               vmax=vmax)
    plt.colorbar()
    plt.title(name)

    if saveFilePath is None:
        plt.show()
    else:
        plt.savefig(saveFilePath, bbox_inches="tight")
        plt.close()