Beispiel #1
0
def mean_overscans(imagefile, nexp=7, plots=None, verbose=False):
    if nexp is None:
        nexp = 7
    result = [
    ]  # decay constant, amplitude, flux of image, residue of first overscan, sensor number, segment, run number
    for segment in range(1, 16 + 1):
        header = getheader(imagefile)
        info = [
            header['LSST_NUM'], 'Segment {}'.format(segment),
            header['RUNNUM'].split()[0]
        ]
        image_untrimmed = ImageF(imagefile, segment)
        amp = makeAmplifierGeometry(imagefile)

        flat_mean = np.mean(
            imutils.trim(image_untrimmed, imaging=amp.imaging).array[:, -1])
        # mean value of the last column of the image

        image = imutils.trim(image_untrimmed, imaging=amp.serial_overscan)
        overscans_mean = [
            np.mean([image.array[i, j] for i in np.arange(len(image.array))])
            for j in range(len(image.array[0]))
        ]
        bias = np.mean(overscans_mean[5:-2])
        over_mean_subfit = overscans_mean[:nexp]
        params, cov = curve_fit(exp_fit,
                                np.arange(nexp),
                                over_mean_subfit,
                                p0=(10, 10, 20000),
                                bounds=([.1, 0, 0], [20, 300, 50000]))
        residue = params[1] / (flat_mean - bias)

        result.append([params[0], params[1], flat_mean, residue, *info])

        if verbose:
            print('Segment {seg}:\n  Decay    : {p[0]:<10.03g} pixels\n  Amplitude: {p[1]:<10.03g} '\
                    'ADU\n  Offset   : {p[2]:<10.03g} ADU'.format(seg=segment, p=params))
        if plots is None:
            continue
        fig = plt.figure(figsize=(10, 10))
        plt.plot(over_mean_subfit, ls='none', marker='.')
        xfit = np.linspace(0, nexp - 1, 50)
        plt.plot(xfit, [exp_fit(x, *params) for x in xfit])
        plt.title(
            'Superflat Mean Serial Overscan in {0} {1} Run {2}'.format(*info))
        plt.figtext(0.5,0.5,('Decay constant: {p[0]:.03g} pixels \nAmplitude: {p[1]:.03g}'\
                    ' ADU\nImage flux: {0:.00f} ADU\nResidue in first overscan pixel: {1:.03%}').format(flat_mean,residue,p=params))
        plt.ylabel('ADU')
        plt.xlabel('Pixel')
        plt.legend(['Data', 'Fit'])
        fig.patch.set_facecolor('white')
        plt.savefig(('{0}/{1}_{2}_run{3}.png'.format(plots,
                                                     *info)).replace(" ", ""))
        plt.close(fig)

    return result
Beispiel #2
0
def pixel_counts(ccd_file, input_mask=None):
    """
    Based on the sensor geometry and an optional input mask, compute
    the total number of pixels in the imaging regions of the
    amplifiers and the total number of masked pixels within the
    imaging regions.  If no input mask is given, the standard rolloff
    mask for the vendor device is used.

    @return <total number of imaging region pixels>, <number of masked pixels>
    """
    if input_mask is not None:
        mask_file = input_mask
    else:
        mask_file = tempfile.mkstemp(suffix='.fits', dir='.')[-1]
        rolloff_mask(ccd_file, mask_file)
    ccd = MaskedCCD(mask_file)
    num_masked = 0
    num_total = 0
    imaging = ccd.amp_geom.imaging
    for amp in ccd:
        imarr = imutils.trim(ccd[amp].getImage(), imaging).getArray()
        num_masked += len(np.where(imarr != 0)[0])
        num_total += imarr.shape[0]*imarr.shape[1]
    if input_mask is None:
        try:
            os.remove(mask_file)
        except OSError:
            pass
    return num_total, num_masked
Beispiel #3
0
    def unbiased_and_trimmed_image(self,
                                   amp,
                                   overscan=None,
                                   imaging=None,
                                   **kwargs):
        """
        Return an offset-corrected image where the offset values generated using
        either of the bias(), bias_row(), bias_func() or bias_spline() methods from
        image_utils.py. The default bias method is set to bias_row(). Keyword arguments
        can be passed depending on which bias method is used.

        Keyword Arguments:
        fit_order: The order of the polynomial. This only needs to be specified when
            using the 'func' method. The default is: 1.
        k: The degree of the spline fit. This only needs to be specified when using
            the 'spline' method. The default is: 3.
        s: The amount of smoothing to be applied to the fit. This only needs to be
            specified when using the 'spline' method. The default is: 18000.
        t: The number of knots. If None, finds the number of knots to use for a given
            smoothing factor, s. This only needs to be specified when using the 'spline'
            method. The default is: None.
        """
        unbiased_image = self.bias_subtracted_image(amp, overscan, **kwargs)
        if imaging is None:
            imaging = self.amp_geom.imaging
        mi = imutils.trim(unbiased_image, imaging)
        if self._applyMasks:
            self.applyInterpolateFromMask(mi)
        return mi
Beispiel #4
0
 def unbiased_and_trimmed_image(self,
                                amp,
                                overscan=None,
                                imaging=None,
                                fit_order=1):
     unbiased_image = self.bias_subtracted_image(amp, overscan, fit_order)
     if imaging is None:
         imaging = self.amp_geom.imaging
     mi = imutils.trim(unbiased_image, imaging)
     if self._applyMasks:
         self.applyInterpolateFromMask(mi)
     return mi
Beispiel #5
0
 def test_generate_mask(self):
     "Test the generated mask for expected masked and unmasked pixels."
     sensorTest.generate_mask(self.template_file,
                              self.mask_file,
                              mask_plane='TRAPS',
                              pixels=self.pixels,
                              columns=self.columns,
                              temp_mask_image='my_temp_mask_file.fits')
     ccd = sensorTest.MaskedCCD(self.mask_file)
     for amp in self.pixels:
         image = imutils.trim(ccd[amp].getImage(), ccd.amp_geom.imaging)
         imarr = image.getArray()
         for ix, iy in self.pixels[amp]:
             self.assertNotEqual(0, imarr[iy][ix])
             for xoffset, yoffset in ((-1, -1), (-1, 0), (-1, 1), (0, -1),
                                      (0, 1), (1, -1), (1, 0), (1, 1)):
                 self.assertEqual(0, imarr[iy+yoffset][ix+xoffset])
     for amp in self.columns:
         image = imutils.trim(ccd[amp].getImage(), ccd.amp_geom.imaging)
         imarr = image.getArray()
         for ix in self.columns[amp]:
             self.assertNotEqual(0, imarr[0][ix])
             self.assertEqual(imarr[0][ix]*imarr.shape[0], sum(imarr[:, ix]))
Beispiel #6
0
 def __init__(self, ccd, amp, bg_reg=(10, 10)):
     self.ccd = ccd
     self.amp = amp
     self.ccdtemp = ccd.md.get('CCDTEMP')
     self.fe55_yield = Fe55Yield(self.ccdtemp)
     raw_image = ccd[amp]
     try:
         self.imarr = raw_image.getArray()
     except AttributeError:
         self.imarr = raw_image.getImage().getArray()
     self.image = imutils.trim(raw_image, imaging=ccd.amp_geom.imaging)
     self.image -= self._bg_image(*bg_reg)
     flags = afwMath.MEANCLIP | afwMath.STDEVCLIP
     stats = afwMath.makeStatistics(self.image, flags, self.ccd.stat_ctrl)
     self.mean = stats.getValue(afwMath.MEANCLIP)
     self.stdev = stats.getValue(afwMath.STDEVCLIP)
     self.footprint_signal = self._footprint_signal_spans
Beispiel #7
0
def unbias_amp(img,
               serial_oscan,
               bias_type=None,
               superbias_im=None,
               region=None,
               bias_type_col=None,
               parallel_oscan=None):
    """Unbias the data from a particular amp

    Paramters
    ---------
    img : `ImageF`
        The image
    serial_oscan : `Box2I`
        Serial overscan bounding box
    bias_type : `str` or `None`
        Method of unbiasing to applly
    superbias_im : `ImageF`
        Optional superbias frame to subtract off
    region : `Box2I`
        Return to return data for

    Returns
    -------
    iamge : `ImageF`
        The unbiased image
    """
    if bias_type is not None:
        image = imutil.unbias_and_trim(img,
                                       serial_oscan,
                                       bias_method=bias_type,
                                       bias_frame=superbias_im,
                                       imaging=region,
                                       bias_method_col=bias_type_col,
                                       overscan_col=parallel_oscan)
    else:
        image = img
        if superbias_im is not None:
            image -= superbias_im
        if region is not None:
            image = imutil.trim(image, region)

    return image
Beispiel #8
0
def flat_gain(image1,
              image2,
              count=1000,
              dx=100,
              dy=100,
              binsize=1,
              seed=None):
    """
    Calculate the gain and noise of a CCD camera system by
    examining two flat field images. The calculation is the standard
    mean/variance thing.
    """
    # If seed is None, the seed is generated from /dev/urandom.
    random.seed(seed)

    # Unbias using the mean bias of the two images.
    bmean = (imutils.bias(image1) + imutils.bias(image2)) / 2.
    image1 -= bmean
    image2 -= bmean

    # Trim prescan and overscan.
    image1 = imutils.trim(image1)
    image2 = imutils.trim(image2)

    # Rebin into binsize x binsize pixels.
    im1 = imutils.rebin(image1, binsize)
    im2 = imutils.rebin(image2, binsize)

    if dx > im1.getWidth():
        dx = im1.getWidth()
    if dy > im1.getHeight():
        dy = im1.getHeight()

    # Sample detector at size=count locations.
    try:
        xarr = random.randint(im1.getWidth() - dx - 1, size=count)
    except ValueError:
        # Rebinned image width smaller than requested dx, so just
        # generate subregions using the full x extent.
        xarr = np.zeros(count, dtype=np.int)
    try:
        yarr = random.randint(im1.getHeight() - dy - 1, size=count)
    except ValueError:
        # Rebinned image height smaller than requested dy, so just
        # generate subregions using the full y extent.
        yarr = np.zeros(count, dtype=np.int)

    gains = []
    ntrial = 0
    exception_count = 0
    for x, y in zip(xarr, yarr):
        bbox = lsstGeom.Box2I(lsstGeom.Point2I(int(x), int(y)),
                              lsstGeom.Extent2I(dx, dy))
        imarr1 = im1.Factory(im1, bbox).getArray()
        imarr2 = im2.Factory(im2, bbox).getArray()
        # Calculate flat ratio and correct subarray of image 2.
        fratio = np.mean(imarr1 / imarr2)
        imarr2 *= fratio
        # Calculate the mean value of the flat field images.
        fmean = (np.mean(imarr1) + np.mean(imarr2)) / 2.
        # Calculate the variance of the flat difference image.
        fvar = np.var(imarr1 - imarr2) / 2.
        gains.append(fvar / fmean)
    gain = 1. / np.median(gains)  # gain in Ne/DN
    return gain, im1, im2
Beispiel #9
0
 def _bg_image(self, nx, ny):
     bg_ctrl = afwMath.BackgroundControl(nx, ny, self.ccd.stat_ctrl)
     bg = afwMath.makeBackground(self.ccd[self.amp], bg_ctrl)
     image_region = self.ccd.amp_geom.imaging
     return imutils.trim(bg.getImageF(), imaging=image_region)
Beispiel #10
0
    def run(self, sensor_id, dark_files, mask_files, gains, bias_frame=None):
        imutils.check_temperatures(dark_files,
                                   self.config.temp_set_point_tol,
                                   setpoint=self.config.temp_set_point,
                                   warn_only=True)
        median_images = {}
        md = imutils.Metadata(dark_files[0], 1)
        for amp in imutils.allAmps(dark_files[0]):
            median_images[amp] = imutils.fits_median(dark_files,
                                                     imutils.dm_hdu(amp))
        medfile = os.path.join(self.config.output_dir,
                               '%s_median_dark_current.fits' % sensor_id)
        imutils.writeFits(median_images, medfile, dark_files[0])

        ccd = MaskedCCD(medfile, mask_files=mask_files, bias_frame=bias_frame)

        dark95s = {}
        exptime = md.get('EXPTIME')
        if self.config.verbose:
            self.log.info("Amp        95 percentile    median")
        dark_curr_pixels = []
        dark_curr_pixels_per_amp = {}
        for amp in ccd:
            imaging_region = ccd.amp_geom.imaging
            overscan = ccd.amp_geom.serial_overscan
            image = imutils.unbias_and_trim(ccd[amp].getImage(), overscan,
                                            imaging_region)
            mask = imutils.trim(ccd[amp].getMask(), imaging_region)
            imarr = image.getArray()
            mskarr = mask.getArray()
            pixels = imarr.reshape(1, imarr.shape[0] * imarr.shape[1])[0]
            masked = mskarr.reshape(1, mskarr.shape[0] * mskarr.shape[1])[0]
            unmasked = [
                pixels[i] for i in range(len(pixels)) if masked[i] == 0
            ]
            unmasked.sort()
            unmasked = np.array(unmasked) * gains[amp] / exptime
            dark_curr_pixels_per_amp[amp] = unmasked
            dark_curr_pixels.extend(unmasked)
            try:
                dark95s[amp] = unmasked[int(len(unmasked) * 0.95)]
                median = unmasked[len(unmasked) / 2]
            except IndexError as eobj:
                print str(eobj)
                dark95s[amp] = -1.
                median = -1.
            if self.config.verbose:
                self.log.info("%2i         %.2e         %.2e" %
                              (amp, dark95s[amp], median))
        #
        # Compute 95th percentile dark current for CCD as a whole.
        #
        dark_curr_pixels = sorted(dark_curr_pixels)
        darkcurr95 = dark_curr_pixels[int(len(dark_curr_pixels) * 0.95)]
        dark95mean = np.mean(dark95s.values())
        if self.config.verbose:
            #self.log.info("CCD: mean 95 percentile value = %s" % dark95mean)
            self.log.info("CCD-wide 95 percentile value = %s" % darkcurr95)
        #
        # Update header of dark current median image file with dark
        # files used and dark95 values, and write dark95 values to the
        # eotest results file.
        #
        results_file = self.config.eotest_results_file
        if results_file is None:
            results_file = os.path.join(self.config.output_dir,
                                        '%s_eotest_results.fits' % sensor_id)
        results = EOTestResults(results_file, namps=len(ccd))
        output = fits.open(medfile)
        for i, dark in enumerate(dark_files):
            output[0].header['DARK%02i' % i] = os.path.basename(dark)
        # Write overall dark current 95th percentile
        results.output['AMPLIFIER_RESULTS'].header['DARK95'] = darkcurr95
        for amp in ccd:
            output[0].header['DARK95%s' %
                             imutils.channelIds[amp]] = dark95s[amp]
            results.add_seg_result(amp, 'DARK_CURRENT_95', dark95s[amp])
        fitsWriteto(output, medfile, clobber=True, checksum=True)
        results.write(clobber=True)
        return dark_curr_pixels_per_amp, dark95s
def normed_mean_response_vscol(sflat_file):
    """
    For an input .fits file, calculates the normalized sigma clipped
    mean flux vs. Col# for a group of Rows returns two arrays for
    the top and bottom section of the CCD
    """
    amc = sensorTest.MaskedCCD(sflat_file)
    amps = imutils.allAmps(sflat_file)
    ncol = amc.amp_geom.nx
    sensor_type = amc.amp_geom.vendor.lower()
    imaging = amc.amp_geom.imaging
    # use 200 rows close to the amplifier
    row_lo = 10
    row_hi = 210

    # top row
    averow_top = np.zeros(ncol*8)
    for i_amp in range(1, 8+1):
        # Segments 10-17
        anamp = imutils.trim(amc[i_amp], imaging=imaging)
        anamp_im = anamp.getImage()
        anamp_arr = anamp_im.getArray()

        # use a robust mean
        anamp_meanbyrow, _, _ \
            = stats.sigma_clipped_stats(anamp_arr[row_lo:row_hi, :], axis=0)

        # normalize
        nmean_byrow = anamp_meanbyrow/np.median(anamp_meanbyrow)

        lopix = 0 + (i_amp-1)*ncol
        hipix = ncol + (i_amp-1)*ncol
        averow_top[lopix:hipix] = np.flip(nmean_byrow)

    # bot row
    averow_bot = np.zeros((ncol*8))
    for j_amp in range(16, 8, -1):
        if j_amp not in amps:
            continue
        # Segments 00-07
        # i_amp goes from 1 to 8, in order of increasing Yccs
        i_amp = 17 - j_amp
        anamp = imutils.trim(amc[j_amp], imaging=imaging)
        anamp_im = anamp.getImage()
        anamp_arr = anamp_im.getArray()

        # use a robust mean
        anamp_meanbyrow, _, _ \
            = stats.sigma_clipped_stats(anamp_arr[row_lo:row_hi, :], axis=0)

        # normalize
        nmean_byrow = anamp_meanbyrow/np.median(anamp_meanbyrow)

        lopix = 0 + (i_amp-1)*ncol
        hipix = ncol + (i_amp-1)*ncol
        if sensor_type == 'e2v':
            averow_bot[lopix:hipix] = nmean_byrow
        elif sensor_type == 'itl':
            averow_bot[lopix:hipix] = np.flip(nmean_byrow)

    # analyze the gaps between amplifiers for Divisidero Tearing, and
    # find the max(abs) deviation in the +-2 columns at the boundaries
    max_divisidero_tearing = []    # 14 entries per CCD
    for k in range(1, 7+1):
        collo = ncol*k - 2  # 2nd to last column in Amplifier
        max_divisidero = np.max(np.abs(averow_top[collo:collo+4] - 1.0))    # +-2 columns
        max_divisidero_tearing.append(max_divisidero)

    for k in range(1, 7+1):
        if k + 8 not in amps:
            continue
        collo = ncol*k - 2  # 2nd to last column in Amplifier
        max_divisidero = np.max(np.abs(averow_bot[collo:collo+4] - 1.0))    # +-2 columns
        max_divisidero_tearing.append(max_divisidero)

    return averow_top, averow_bot, max_divisidero_tearing