Ejemplo n.º 1
0
    def bypass_instcal(self, datasetType, pythonType, butlerLocation, dataId):
        # Workaround until I can access the butler
        instcalMap = self.map_instcal(dataId)
        dqmaskMap = self.map_dqmask(dataId)
        wtmapMap = self.map_wtmap(dataId)
        instcalType = getattr(afwImage,
                              instcalMap.getPythonType().split(".")[-1])
        dqmaskType = getattr(afwImage,
                             dqmaskMap.getPythonType().split(".")[-1])
        wtmapType = getattr(afwImage, wtmapMap.getPythonType().split(".")[-1])
        instcal = instcalType(instcalMap.getLocationsWithRoot()[0])
        dqmask = dqmaskType(dqmaskMap.getLocationsWithRoot()[0])
        wtmap = wtmapType(wtmapMap.getLocationsWithRoot()[0])

        mask = self.translate_dqmask(dqmask)
        variance = self.translate_wtmap(wtmap)

        mi = afwImage.MaskedImageF(afwImage.ImageF(instcal.getImage()), mask,
                                   variance)
        md = readMetadata(instcalMap.getLocationsWithRoot()[0])
        fix_header(md, translator_class=DecamTranslator)
        wcs = makeSkyWcs(md, strip=True)
        exp = afwImage.ExposureF(mi, wcs)

        exp.setPhotoCalib(
            afwImage.makePhotoCalibFromCalibZeroPoint(
                10**(0.4 * md.getScalar("MAGZERO")), 0))
        visitInfo = self.makeRawVisitInfo(md=md)
        exp.getInfo().setVisitInfo(visitInfo)

        for kw in ('LTV1', 'LTV2'):
            md.remove(kw)

        exp.setMetadata(md)
        return exp
Ejemplo n.º 2
0
    def readMetadata(self):
        """Read all header metadata directly into a PropertyList.

        Returns
        -------
        metadata : `~lsst.daf.base.PropertyList`
            Header metadata.
        """
        md = readMetadata(self.fileDescriptor.location.path)
        fix_header(md)
        return md
Ejemplo n.º 3
0
 def std_dark(self, item, dataId):
     exp = afwImage.makeExposure(afwImage.makeMaskedImage(item))
     rawPath = self.map_raw(dataId).getLocations()[0]
     headerPath = re.sub(r'[\[](\d+)[\]]$', "[0]", rawPath)
     md0 = readMetadata(headerPath)
     fix_header(md0, translator_class=DecamTranslator)
     visitInfo = self.makeRawVisitInfo(md0)
     exp.getInfo().setVisitInfo(visitInfo)
     return self._standardizeExposure(self.calibrations["dark"],
                                      exp,
                                      dataId,
                                      filter=False)
Ejemplo n.º 4
0
    def bypass_raw_visitInfo(self, datasetType, pythonType, location, dataId):
        fileName = location.getLocationsWithRoot()[0]
        mat = re.search(r"\[(\d+)\]$", fileName)
        if mat:
            hdu = int(mat.group(1))
            md = readMetadata(fileName, hdu=hdu)
        else:
            md = readMetadata(fileName)  # or hdu = INT_MIN; -(1 << 31)

        makeVisitInfo = self.MakeRawVisitInfoClass(log=self.log)
        fix_header(md, translator_class=self.translatorClass)
        return makeVisitInfo(md)
Ejemplo n.º 5
0
    def test_readMetadata(self):
        dataId = {'detector': 25}
        # detector 25 is in HDU 1
        expected = lsst.afw.fits.readMetadata(self.filename, 1)
        self.assertEqual(expected['CCDNUM'], 25)  # sanity check
        self.check_readMetadata(dataId, expected)

        dataId = {'detector': 1}
        # detector 1 is in HDU 2
        expected = lsst.afw.fits.readMetadata(self.filename, 2)
        astro_metadata_translator.fix_header(expected)
        self.assertEqual(expected['CCDNUM'], 1)  # sanity check
        self.check_readMetadata(dataId, expected)
Ejemplo n.º 6
0
 def _listdir(self, path, prefix):
     for file in os.listdir(path):
         fileName = os.path.join(path, file)
         md = readMetadata(fileName)
         fix_header(md, translator_class=self._translatorClass)
         if "EXPNUM" not in md:
             return
         expnum = md["EXPNUM"]
         if expnum not in self.expnumMapper:
             self.expnumMapper[expnum] = {
                 self.instcalPrefix: None,
                 self.wtmapPrefix: None,
                 self.dqmaskPrefix: None
             }
         self.expnumMapper[expnum][prefix] = fileName
Ejemplo n.º 7
0
    def test_translator_fix_header(self):
        """Check that translator classes can fix headers."""

        # Read in a known header
        header = read_test_file("fitsheader-decam-0160496.yaml",
                                dir=os.path.join(TESTDIR, "data"))
        self.assertEqual(header["DTSITE"], "ct")
        fixed = fix_header(header, translator_class=NotDecamTranslator)
        self.assertTrue(fixed)
        self.assertEqual(header["DTSITE"], "hi")

        header["DTSITE"] = "reset"
        with self.assertLogs("astro_metadata_translator", level="FATAL"):
            fixed = fix_header(header, translator_class=AlsoNotDecamTranslator)
        self.assertFalse(fixed)
        self.assertEqual(header["DTSITE"], "reset")
Ejemplo n.º 8
0
    def _standardizeCpMasterCal(self,
                                datasetType,
                                item,
                                dataId,
                                setFilter=False):
        """Standardize a MasterCal image obtained from NOAO archive into Exposure

        These MasterCal images are MEF files with one HDU for each detector.
        Some WCS header, eg CTYPE1, exists only in the zeroth extensionr,
        so info in the zeroth header need to be copied over to metadata.

        Parameters
        ----------
        datasetType : `str`
            Dataset type ("bias", "flat", or "illumcor").
        item : `lsst.afw.image.DecoratedImage`
            The image read by the butler.
        dataId : data ID
            Data identifier.
        setFilter : `bool`
            Whether to set the filter in the Exposure.

        Returns
        -------
        result : `lsst.afw.image.Exposure`
            The standardized Exposure.
        """
        mi = afwImage.makeMaskedImage(item.getImage())
        md = item.getMetadata()
        masterCalMap = getattr(self, "map_" + datasetType)
        masterCalPath = masterCalMap(dataId).getLocationsWithRoot()[0]
        headerPath = re.sub(r'[\[](\d+)[\]]$', "[0]", masterCalPath)
        md0 = readMetadata(headerPath)
        fix_header(md0, translator_class=DecamTranslator)
        for kw in ('CTYPE1', 'CTYPE2', 'CRVAL1', 'CRVAL2', 'CUNIT1', 'CUNIT2',
                   'CD1_1', 'CD1_2', 'CD2_1', 'CD2_2'):
            if kw in md0.paramNames() and kw not in md.paramNames():
                md.add(kw, md0.getScalar(kw))
        wcs = makeSkyWcs(md, strip=True)
        exp = afwImage.makeExposure(mi, wcs)
        exp.setMetadata(md)
        return self._standardizeExposure(self.calibrations[datasetType],
                                         exp,
                                         dataId,
                                         filter=setFilter)
Ejemplo n.º 9
0
    def getInfo(self, filename):
        """Get information about the image from the filename and its contents

        Here, we open the image and parse the header, but one could also look at the filename itself
        and derive information from that, or set values from the configuration.

        Parameters
        ----------
        filename : `str`
            Name of file to inspect

        Returns
        -------
        phuInfo : `dict`
            File properties
        infoList : `list`
            List of file properties for each extension
        """
        md = readMetadata(filename, self.config.hdu)
        fix_header(md, translator_class=self.translator_class)
        phuInfo = self.getInfoFromMetadata(md)
        if len(self.config.extnames) == 0:
            # No extensions to worry about
            return phuInfo, [phuInfo]
        # Look in the provided extensions
        extnames = set(self.config.extnames)
        extnum = 0
        infoList = []
        while len(extnames) > 0:
            extnum += 1
            try:
                md = readMetadata(filename, extnum)
                fix_header(md, translator_class=self.translator_class)
            except Exception as e:
                self.log.warning("Error reading %s extensions %s: %s",
                                 filename, extnames, e)
                break
            ext = self.getExtensionName(md)
            if ext in extnames:
                hduInfo = self.getInfoFromMetadata(md, info=phuInfo.copy())
                # We need the HDU number when registering MEF files.
                hduInfo["hdu"] = extnum
                infoList.append(hduInfo)
                extnames.discard(ext)
        return phuInfo, infoList
Ejemplo n.º 10
0
    def std_raw(self, item, dataId):
        """Fixup raw image problems.

        1. Apply header patches. This should be done by the CameraMapper base
        class, but it isn't yet (DM-23959).

        2. Fix an early ADC bug.

        Since this is almost certainly an FPGA bug, I'll base the
        decision on the FPGA version number. As of 2016-12-01 the
        keyword is misnamed, so we can fix the format if the keyword
        does not exist.

        See _shiftAmpPixels() for the implementation.

        Parameters
        ----------
        item : image-like object
            Can be any of lsst.afw.image.Exposure,
            lsst.afw.image.DecoratedImage, lsst.afw.image.Image
            or lsst.afw.image.MaskedImage
            the image-like object whose header information needs to be patched.
        dataId : `dict`
            Dataset identifier

        Returns
        -------
        item : image-like object
            the input object with patched header information.
        """
        exp = super(PfsMapper, self).std_raw(item, dataId)

        md = exp.getMetadata()
        fix_header(md, translator_class=PfsTranslator)
        try:
            dataVersion = int(md.get('W_VERSIONS_FPGA'), 16)
        except Exception:
            dataVersion = None

        if dataVersion is not None and dataVersion <= 0x0070:
            self._shiftAmpPixels(exp)

        return exp
Ejemplo n.º 11
0
    def std_raw_md(self, item, dataId):
        """Fixup raw header metadata problems.

        This should be done by the CameraMapper base class, but it isn't yet (DM-23959).

        Parameters
        ----------
        item : `lsst.daf.base.PropertyList`
            The raw metadata to be fixed

        dataId : `dict`
            Dataset identifier

        Returns
        -------
        item : `lsst.daf.base.PropertyList`
            The modified raw metadata.
        """
        fix_header(item, translator_class=PfsTranslator)
        return item
Ejemplo n.º 12
0
def readRawFitsHeader(fileName, translator_class=None):
    """Read a FITS header from a raw file and fix it up as required.

    Parameters
    ----------
    fileName : `str`
        Name of the FITS file. Can include a HDU specifier (although 0
        is ignored).
    translator_class : `~astro_metadata_translator.MetadataTranslator`,
                       optional
        Any translator class to use for fixing up the header.

    Returns
    -------
    md : `PropertyList`
        Metadata from file. We also merge the contents with the
        next HDU if an ``INHERIT`` key is not specified. If an explicit
        HDU is encoded with the file name and it is greater than 0 then
        no merging will occur.
    """
    mat = re.search(r"\[(\d+)\]$", fileName)
    hdu = None
    if mat:
        # Treat 0 as a special case
        # For some instruments the primary header is empty
        requested = int(mat.group(1))
        if requested > 0:
            hdu = requested

    if hdu is not None:
        md = lsst.afw.fits.readMetadata(fileName, hdu=hdu)
    else:
        # For raw some of these files need the second header to be
        # read as well. Not all instruments want the double read
        # but for now it's easiest to always merge.
        phdu = lsst.afw.fits.readMetadata(fileName, 0)
        md = lsst.afw.fits.readMetadata(fileName)
        md = merge_headers([phdu, md], mode="overwrite")

    fix_header(md, translator_class=translator_class)
    return md
Ejemplo n.º 13
0
    def readMetadata(self):
        """Read all header metadata directly into a PropertyList.

        Specialist version since some of our data does not
        set INHERIT=T so we have to merge the headers manually.

        Returns
        -------
        metadata : `~lsst.daf.base.PropertyList`
            Header metadata.
        """
        file = self.fileDescriptor.location.path
        phdu = lsst.afw.fits.readMetadata(file, 0)
        if "INHERIT" in phdu:
            # Trust the inheritance flag
            return super().readMetadata()

        # Merge ourselves
        md = merge_headers([phdu, lsst.afw.fits.readMetadata(file)],
                           mode="overwrite")
        fix_header(md)
        return md
Ejemplo n.º 14
0
    def readMetadata(self):
        """Read all header metadata directly into a PropertyList.
        Specialist version since some of our data does not
        set INHERIT=T so we have to merge the headers manually.
        Returns
        -------
        metadata : `~lsst.daf.base.PropertyList`
            Header metadata.
        """
        file = self.fileDescriptor.location.path
        phdu = lsst.afw.fits.readMetadata(file, 0)
        index, md = self._determineHDU(self.dataId['detector'])
        if "INHERIT" in phdu:
            # Trust the inheritance flag
            return super().readMetadata()

        # Merge ourselves
        md = merge_headers([phdu, md],
                           mode="overwrite")
        #fix_header(md)
        astro_metadata_translator.fix_header(md,translator_class=VircamTranslator)
        #print('md:',md)
        return md
Ejemplo n.º 15
0
    def test_basic_fix_header(self):
        """Test that a header can be fixed if we specify a local path.
        """

        header = read_test_file("fitsheader-decam-0160496.yaml",
                                dir=os.path.join(TESTDIR, "data"))
        self.assertEqual(header["DETECTOR"], "S3-111_107419-8-3")

        # First fix header but using no search path (should work as no-op)
        fixed = fix_header(header)
        self.assertFalse(fixed)

        # Now using the test corrections directory
        fixed = fix_header(header,
                           search_path=os.path.join(TESTDIR, "data",
                                                    "corrections"))
        self.assertTrue(fixed)
        self.assertEqual(header["DETECTOR"], "NEW-ID")

        # Test that fix_header of unknown header is allowed
        header = {"SOMETHING": "UNKNOWN"}
        fixed = fix_header(header)
        self.assertFalse(fixed)
Ejemplo n.º 16
0
    def extractMetadata(self, filename: str) -> RawFileData:
        datasets = []
        fitsData = lsst.afw.fits.Fits(filename, "r")

        # NOTE: The primary header (HDU=0) does not contain detector data.
        for i in range(1, fitsData.countHdus()):
            fitsData.setHdu(i)
            header = fitsData.readMetadata()
            if not header["EXTNAME"].startswith("ccd"):
                continue
            fix_header(header)
            datasets.append(self._calculate_dataset_info(header, filename))

        # The data model currently assumes that whilst multiple datasets
        # can be associated with a single file, they must all share the
        # same formatter.
        instrument = MegaPrime()
        FormatterClass = instrument.getRawFormatter(datasets[0].dataId)

        self.log.info(f"Found images for {len(datasets)} detectors in {filename}")
        return RawFileData(datasets=datasets, filename=filename,
                           FormatterClass=FormatterClass,
                           instrumentClass=type(instrument))
Ejemplo n.º 17
0
    def test_hsc_fix_header(self):
        """Check that one of the known HSC corrections is being applied
        properly."""
        header = {
            "EXP-ID": "HSCA00120800",
            "INSTRUME": "HSC",
            "DATA-TYP": "FLAT"
        }

        fixed = fix_header(header, translator_class=HscTranslator)
        self.assertTrue(fixed)
        self.assertEqual(header["DATA-TYP"], "OBJECT")

        # And that this header won't be corrected
        header = {
            "EXP-ID": "HSCA00120800X",
            "INSTRUME": "HSC",
            "DATA-TYP": "FLAT"
        }

        fixed = fix_header(header, translator_class=HscTranslator)
        self.assertFalse(fixed)
        self.assertEqual(header["DATA-TYP"], "FLAT")
Ejemplo n.º 18
0
    def extractMetadata(self, filename: str) -> RawFileData:
        """Extract and process metadata from a single raw file.

        Parameters
        ----------
        filename : `str`
            Path to the file.

        Returns
        -------
        data : `RawFileData`
            A structure containing the metadata extracted from the file,
            as well as the original filename.  All fields will be populated,
            but the `RawFileData.dataId` attribute will be a minimal
            (unexpanded) `DataCoordinate` instance.

        Notes
        -----
        Assumes that there is a single dataset associated with the given
        file.  Instruments using a single file to store multiple datasets
        must implement their own version of this method.
        """
        # Manually merge the primary and "first data" headers here because we
        # do not know in general if an input file has set INHERIT=T.
        phdu = readMetadata(filename, 0)
        header = merge_headers([phdu, readMetadata(filename)], mode="overwrite")
        fix_header(header)
        datasets = [self._calculate_dataset_info(header, filename)]

        # The data model currently assumes that whilst multiple datasets
        # can be associated with a single file, they must all share the
        # same formatter.
        FormatterClass = self.instrument.getRawFormatter(datasets[0].dataId)

        return RawFileData(datasets=datasets, filename=filename,
                           FormatterClass=FormatterClass)
Ejemplo n.º 19
0
 def test_fix_header(self):
     from astro_metadata_translator import fix_header
     from astro_metadata_translator.tests import read_test_file
     # Test that header fix up is working
     # Not all headers are used in metadata translation
     test_data = (
         ("latiss-AT_O_20210212_000006.yaml",
          dict(RASTART=260.024385071917)),
         ("latiss-AT_O_20210210_000011.yaml",
          dict(RASTART=355.41750341182313)),
     )
     for filename, expected in test_data:
         with self.subTest(f"Testing {filename}"):
             header = read_test_file(filename, dir=self.datadir)
             modified = fix_header(header)
             self.assertTrue(modified)
             for k, v in expected.items():
                 self.assertEqual(header[k], v,
                                  f"Testing {k} in {filename}")
Ejemplo n.º 20
0
 def readMetadata(self):
     index, metadata = self._determineHDU(self.dataId['detector'])
     astro_metadata_translator.fix_header(metadata)
     return metadata
Ejemplo n.º 21
0
    def getInfo(self, filename):
        """Get information about the image from the filename and its contents

        The information we want isn't in the headers, but in the filname.

        Parameters
        ----------
        filename : `str`
            Name of file to inspect

        Returns
        -------
        info : `dict`
            File properties from the PHU.
        infoList : `list` of `dict`
            File properties from the extensions.
        """
        self.log.debug('interpreting filename <%s>' % filename)

        path, name = os.path.split(filename)

        matches = re.search(
            r"^PF(%s)(%s)-?(\d{6})(\d)(\d)\.fits$" %
            (self.sites, self.categories), name)
        if not matches:
            raise RuntimeError("Unable to interpret filename: %s" % filename)
        site, category, visit, spectrograph, armNum = matches.groups()

        armNum = int(armNum)
        spectrograph = int(spectrograph)
        visit = int(visit, base=10)

        # spectrograph 2 was called 9 at JHU, at least in the early days, so if we find
        # a spectrograph 9 we re-assign its number to 2
        if spectrograph == 9:
            spectrograph = 2
            self.log.info("Visit %06d has spectrograph == 9" % visit)

        if spectrograph < 1 or spectrograph > self.nSpectrograph + 1:
            raise RuntimeError('spectrograph (=%d) out of bounds 1..%d' %
                               (spectrograph, self.nSpectrograph))

        try:
            arm = self.arms[armNum - 1]  # 1-indexed
        except IndexError as exc:
            raise IndexError('armNum=%d out of bounds 1..%d' %
                             (armNum, max(self.arms.keys()))) from exc

        ccd = PfsMapper.computeDetectorId(spectrograph, arm)
        self.log.debug(
            'site = <%s>, category = <%s>, visit = <%s>, spectrograph = <%d>, armNum = <%d>, '
            'arm = <%s>, ccd = <%d>' %
            (site, category, visit, spectrograph, armNum, arm, ccd))

        info = dict(site=site,
                    category=category,
                    visit=visit,
                    filter=arm,
                    arm=arm,
                    spectrograph=spectrograph,
                    ccd=ccd)

        if os.path.exists(filename):
            header = afwImage.readMetadata(filename)

            # Patch the header
            original = header.toOrderedDict()
            fixed = header.toOrderedDict()
            fix_header(fixed,
                       translator_class=PfsTranslator,
                       filename=filename)
            for kk, vv in fixed.items():
                if kk in original and vv == original[kk]:
                    continue
                header.set(kk, vv)

            info = self.getInfoFromMetadata(header, info=info)
        return info, [info]
def read_file(file,
              hdrnum,
              print_trace,
              outstream=sys.stdout,
              errstream=sys.stderr,
              output_mode="verbose",
              write_heading=False):
    """Read the specified file and process it.

    Parameters
    ----------
    file : `str`
        The file from which the header is to be read.
    hdrnum : `int`
        The HDU number to read. The primary header is always read and
        merged with the header from this HDU.
    print_trace : `bool`
        If there is an error reading the file and this parameter is `True`,
        a full traceback of the exception will be reported. If `False` prints
        a one line summary of the error condition.
    outstream : `io.StringIO`, optional
        Output stream to use for standard messages. Defaults to `sys.stdout`.
    errstream : `io.StringIO`, optional
        Stream to send messages that would normally be sent to standard
        error. Defaults to `sys.stderr`.
    output_mode : `str`, optional
        Output mode to use. Must be one of "verbose", "none", "table",
        "yaml", or "fixed".  "yaml" and "fixed" can be modified with a
        "native" suffix to indicate that the output should be a representation
        of the native object type representing the header (which can be
        PropertyList or an Astropy header).  Without this modify headers
        will be dumped as simple `dict` form.
        "auto" is not allowed by this point.
    write_heading: `bool`, optional
        If `True` and in table mode, write a table heading out before writing
        the content.

    Returns
    -------
    success : `bool`
        `True` if the file was handled successfully, `False` if the file
        could not be processed.
    """
    if output_mode not in OUTPUT_MODES:
        raise ValueError(f"Output mode of '{output_mode}' is not understood.")
    if output_mode == "auto":
        raise ValueError("Output mode can not be 'auto' here.")

    # This gets in the way in tabular mode
    if output_mode != "table":
        print(f"Analyzing {file}...", file=errstream)

    try:
        if file.endswith(".yaml"):
            md = read_test_file(file, )
            if hdrnum != 0:
                # YAML can't have HDUs
                hdrnum = 0
        else:
            md = read_metadata(file, 0)
        if md is None:
            print(f"Unable to open file {file}", file=errstream)
            return False
        if hdrnum != 0:
            mdn = read_metadata(file, int(hdrnum))
            # Astropy does not allow append mode since it does not
            # convert lists to multiple cards. Overwrite for now
            if mdn is not None:
                md = merge_headers([md, mdn], mode="overwrite")
            else:
                print(f"HDU {hdrnum} was not found. Ignoring request.",
                      file=errstream)

        if output_mode.endswith("native"):
            # Strip native and don't change type of md
            output_mode = output_mode[:-len("native")]
        else:
            # Rewrite md as simple dict for output
            md = {k: v for k, v in md.items()}

        if output_mode in ("yaml", "fixed"):

            if output_mode == "fixed":
                fix_header(md, filename=file)

            # The header should be written out in the insertion order
            print(yaml.dump(md, sort_keys=False), file=outstream)
            return True
        obs_info = ObservationInfo(md, pedantic=True, filename=file)
        if output_mode == "table":
            columns = [
                "{:{fmt}}".format(getattr(obs_info, c["attr"]),
                                  fmt=c["format"]) for c in TABLE_COLUMNS
            ]

            if write_heading:
                # Construct headings of the same width as the items
                # we have calculated.  Doing this means we don't have to
                # work out for ourselves how many characters will be used
                # for non-strings (especially Quantity)
                headings = []
                separators = []
                for thiscol, defn in zip(columns, TABLE_COLUMNS):
                    width = len(thiscol)
                    headings.append("{:{w}.{w}}".format(defn["label"],
                                                        w=width))
                    separators.append("-" * width)
                print(" ".join(headings), file=outstream)
                print(" ".join(separators), file=outstream)

            row = " ".join(columns)
            print(row, file=outstream)
        elif output_mode == "verbose":
            print(f"{obs_info}", file=outstream)
        elif output_mode == "none":
            pass
        else:
            raise RuntimeError(
                f"Output mode of '{output_mode}' not recognized but should be known."
            )
    except Exception as e:
        if print_trace:
            traceback.print_exc(file=outstream)
        else:
            print(repr(e), file=outstream)
        return False
    return True
Ejemplo n.º 23
0
    def bypass_raw(self, datasetType, pythonType, location, dataId):
        """Magic method that is called automatically if it exists.
        """
        from lsst.ip.isr import AssembleCcdTask

        config = AssembleCcdTask.ConfigClass()
        config.doTrim = False

        assembleTask = AssembleCcdTask(config=config)
        logger = lsst.log.Log.getLogger("ZtfMapper")

        # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

        assert len(location.getLocations()
                   ) == 1  # KTL says that this is always true, but check
        relativeFileName = location.getLocations()[0]
        if location.storage.exists(relativeFileName):
            fileName = location.storage.locationWithRoot(relativeFileName)
            data = afwImage.ImageF(fileName, hdu=1)
            data = pythonType(afwImage.MaskedImageF(data))
        else:
            raise IOError(f"Unable to find {relativeFileName}")

        # Get the Detector
        detector = self.camera[self._extractDetectorName(dataId)]
        #
        # Read all the data
        #
        ampDict = {}
        for i, amp in enumerate(detector, 1):
            ampExp = pythonType(amp.getRawBBox())
            ampExp.setDetector(detector)
            ampDict[amp.getName()] = ampExp

            hdu = amp.get('hdu')
            if i == 1:
                assert i == hdu  # don't read twice
                data = data.image
            else:
                data = afwImage.ImageF(fileName, hdu=hdu)

            ampExp[amp.getRawDataBBox()].image = data

            bias = afwImage.ImageF(fileName, hdu=hdu + 4)
            ampExp[amp.getRawHorizontalOverscanBBox()].image = bias

        exposure = assembleTask.assembleCcd(ampDict)

        md = afwImage.readMetadata(fileName, hdu=0)
        fix_header(md, translator_class=self.translatorClass)
        exposure.setMetadata(md)

        visitInfo = ZtfMakeRawVisitInfo(logger)(md)
        exposure.getInfo().setVisitInfo(visitInfo)

        boresight = visitInfo.getBoresightRaDec()
        if boresight.isFinite():
            exposure.setWcs(
                getWcsFromDetector(exposure.getDetector(),
                                   boresight,
                                   180 * geom.degrees,
                                   flipX=True))
        else:
            logger.warn(
                f"Unable to set WCS for {dataId} from header as RA/Dec/Angle are unavailable"
            )

        return exposure
Ejemplo n.º 24
0
 def readMetadata(self):
     # Headers are duplicated so no need to merge with primary
     index, metadata = self._determineHDU(self.dataId["detector"])
     fix_header(metadata)
     return metadata
Ejemplo n.º 25
0
def assemble_raw(dataId, componentInfo, cls):
    """Called by the butler to construct the composite type "raw".

    Note that we still need to define "_raw" and copy various fields over.

    Parameters
    ----------
    dataId : `lsst.daf.persistence.dataId.DataId`
        The data ID.
    componentInfo : `dict`
        dict containing the components, as defined by the composite definition
        in the mapper policy.
    cls : 'object'
        Unused.

    Returns
    -------
    exposure : `lsst.afw.image.Exposure`
        The assembled exposure.
    """
    from lsst.ip.isr import AssembleCcdTask

    config = AssembleCcdTask.ConfigClass()
    config.doTrim = False

    assembleTask = AssembleCcdTask(config=config)

    ampExps = componentInfo['raw_amp'].obj
    if len(ampExps) == 0:
        raise RuntimeError("Unable to read raw_amps for %s" % dataId)

    ccd = ampExps[0].getDetector()      # the same (full, CCD-level) Detector is attached to all ampExps
    #
    # Check that the geometry in the metadata matches cameraGeom
    #
    logger = lsst.log.Log.getLogger("LsstCamMapper")
    warned = False
    for i, (amp, ampExp) in enumerate(zip(ccd, ampExps)):
        ampMd = ampExp.getMetadata().toDict()

        if amp.getRawBBox() != ampExp.getBBox():  # Oh dear. cameraGeom is wrong -- probably overscan
            if amp.getRawDataBBox().getDimensions() != amp.getBBox().getDimensions():
                raise RuntimeError("Active area is the wrong size: %s v. %s" %
                                   (amp.getRawDataBBox().getDimensions(), amp.getBBox().getDimensions()))
            if not warned:
                logger.warn("amp.getRawBBox() != data.getBBox(); patching. (%s v. %s)",
                            amp.getRawBBox(), ampExp.getBBox())
                warned = True

            w, h = ampExp.getBBox().getDimensions()
            ow, oh = amp.getRawBBox().getDimensions()  # "old" (cameraGeom) dimensions
            #
            # We could trust the BIASSEC keyword, or we can just assume that
            # they've changed the number of overscan pixels (serial and/or
            # parallel).  As Jim Chiang points out, the latter is safer
            #
            bbox = amp.getRawHorizontalOverscanBBox()
            hOverscanBBox = geom.BoxI(bbox.getBegin(),
                                      geom.ExtentI(w - bbox.getBeginX(), bbox.getHeight()))
            bbox = amp.getRawVerticalOverscanBBox()
            vOverscanBBox = geom.BoxI(bbox.getBegin(),
                                      geom.ExtentI(bbox.getWidth(), h - bbox.getBeginY()))

            amp.setRawBBox(ampExp.getBBox())
            amp.setRawHorizontalOverscanBBox(hOverscanBBox)
            amp.setRawVerticalOverscanBBox(vOverscanBBox)
            #
            # This gets all the geometry right for the amplifier, but the size
            # of the untrimmed image will be wrong and we'll put the amp
            # sections in the wrong places, i.e.
            #   amp.getRawXYOffset()
            # will be wrong.  So we need to recalculate the offsets.
            #
            xRawExtent, yRawExtent = amp.getRawBBox().getDimensions()

            x0, y0 = amp.getRawXYOffset()
            ix, iy = x0//ow, y0/oh
            x0, y0 = ix*xRawExtent, iy*yRawExtent
            amp.setRawXYOffset(geom.ExtentI(ix*xRawExtent, iy*yRawExtent))
        #
        # Check the "IRAF" keywords, but don't abort if they're wrong
        #
        # Only warn about the first amp, use debug for the others
        #
        detsec = bboxFromIraf(ampMd["DETSEC"]) if "DETSEC" in ampMd else None
        datasec = bboxFromIraf(ampMd["DATASEC"]) if "DATASEC" in ampMd else None
        biassec = bboxFromIraf(ampMd["BIASSEC"]) if "BIASSEC" in ampMd else None

        logCmd = logger.warn if i == 0 else logger.debug
        if detsec and amp.getBBox() != detsec:
            logCmd("DETSEC doesn't match for %s (%s != %s)", dataId, amp.getBBox(), detsec)
        if datasec and amp.getRawDataBBox() != datasec:
            logCmd("DATASEC doesn't match for %s (%s != %s)", dataId, amp.getRawDataBBox(), detsec)
        if biassec and amp.getRawHorizontalOverscanBBox() != biassec:
            logCmd("BIASSEC doesn't match for %s (%s != %s)",
                   dataId, amp.getRawHorizontalOverscanBBox(), detsec)

    ampDict = {}
    for amp, ampExp in zip(ccd, ampExps):
        ampDict[amp.getName()] = ampExp

    exposure = assembleTask.assembleCcd(ampDict)

    md = componentInfo['raw_hdu'].obj
    fix_header(md)  # No mapper so cannot specify the translator class
    exposure.setMetadata(md)
    visitInfo = LsstCamMakeRawVisitInfo(logger)(md)
    exposure.getInfo().setVisitInfo(visitInfo)

    boresight = visitInfo.getBoresightRaDec()
    rotangle = visitInfo.getBoresightRotAngle()

    if boresight.isFinite():
        exposure.setWcs(getWcsFromDetector(exposure.getDetector(), boresight,
                                           90*geom.degrees - rotangle))
    else:
        # Should only warn for science observations but VisitInfo does not know
        logger.warn("Unable to set WCS for %s from header as RA/Dec/Angle are unavailable" %
                    (dataId,))

    return exposure