784 inputs = butlerQC.get(inputRefs)
785 exposures = inputs.pop(
"exposures")
787 id_generator = self.config.id_generator.apply(butlerQC.quantum.dataId)
790 dataIds=[ref.datasetRef.dataId
for ref
in inputRefs.astrometry_ref_cat],
791 refCats=inputs.pop(
"astrometry_ref_cat"),
792 name=self.config.connections.astrometry_ref_cat,
793 config=self.config.astrometry_ref_loader, log=self.log)
794 self.astrometry.setRefObjLoader(astrometry_loader)
797 dataIds=[ref.datasetRef.dataId
for ref
in inputRefs.photometry_ref_cat],
798 refCats=inputs.pop(
"photometry_ref_cat"),
799 name=self.config.connections.photometry_ref_cat,
800 config=self.config.photometry_ref_loader, log=self.log)
801 self.photometry.match.setRefObjLoader(photometry_loader)
803 if self.config.doMaskDiffractionSpikes:
806 self.diffractionSpikeMask.setRefObjLoader(photometry_loader)
808 if self.config.do_illumination_correction:
809 background_flat = inputs.pop(
"background_flat")
810 illumination_correction = inputs.pop(
"illumination_correction")
812 background_flat =
None
813 illumination_correction =
None
816 if self.config.useButlerCamera:
817 if "camera_model" in inputs:
818 camera_model = inputs.pop(
"camera_model")
820 self.log.warning(
"useButlerCamera=True, but camera is not available for filter %s. The "
821 "astrometry fit will use the WCS already attached to the exposure.",
822 exposures[0].filter.bandLabel)
825 assert not inputs,
"runQuantum got more inputs than expected"
829 result = pipeBase.Struct(
831 stars_footprints=
None,
832 psf_stars_footprints=
None,
833 background_to_photometric_ratio=
None,
839 id_generator=id_generator,
840 background_flat=background_flat,
841 illumination_correction=illumination_correction,
842 camera_model=camera_model,
844 except pipeBase.AlgorithmError
as e:
845 error = pipeBase.AnnotatedPartialOutputsError.annotate(
849 result.psf_stars_footprints,
850 result.stars_footprints,
853 butlerQC.put(result, outputRefs)
856 butlerQC.put(result, outputRefs)
865 background_flat=None,
866 illumination_correction=None,
869 """Find stars and perform psf measurement, then do a deeper detection
870 and measurement and calibrate astrometry and photometry from that.
874 exposures : `lsst.afw.image.Exposure` or \
875 `list` [`lsst.afw.image.Exposure`]
876 Post-ISR exposure(s), with an initial WCS, VisitInfo, and Filter.
877 Modified in-place during processing if only one is passed.
878 If two exposures are passed, treat them as snaps and combine
879 before doing further processing.
880 id_generator : `lsst.meas.base.IdGenerator`, optional
881 Object that generates source IDs and provides random seeds.
882 result : `lsst.pipe.base.Struct`, optional
883 Result struct that is modified to allow saving of partial outputs
884 for some failure conditions. If the task completes successfully,
885 this is also returned.
886 background_flat : `lsst.afw.image.Exposure`, optional
887 Background flat-field image.
888 illumination_correction : `lsst.afw.image.Exposure`, optional
889 Illumination correction image.
890 camera_model : `lsst.afw.cameraGeom.Camera`, optional
891 Camera to be used if constructing updated WCS.
895 result : `lsst.pipe.base.Struct`
896 Results as a struct with attributes:
899 Calibrated exposure, with pixels in nJy units.
900 (`lsst.afw.image.Exposure`)
902 Stars that were used to calibrate the exposure, with
903 calibrated fluxes and magnitudes.
904 (`astropy.table.Table`)
906 Footprints of stars that were used to calibrate the exposure.
907 (`lsst.afw.table.SourceCatalog`)
909 Stars that were used to determine the image PSF.
910 (`astropy.table.Table`)
911 ``psf_stars_footprints``
912 Footprints of stars that were used to determine the image PSF.
913 (`lsst.afw.table.SourceCatalog`)
915 Background that was fit to the exposure when detecting
916 ``stars``. (`lsst.afw.math.BackgroundList`)
917 ``applied_photo_calib``
918 Photometric calibration that was fit to the star catalog and
919 applied to the exposure. (`lsst.afw.image.PhotoCalib`)
920 This is `None` if ``config.do_calibrate_pixels`` is `False`.
921 ``astrometry_matches``
922 Reference catalog stars matches used in the astrometric fit.
923 (`list` [`lsst.afw.table.ReferenceMatch`] or
924 `lsst.afw.table.BaseCatalog`).
925 ``photometry_matches``
926 Reference catalog stars matches used in the photometric fit.
927 (`list` [`lsst.afw.table.ReferenceMatch`] or
928 `lsst.afw.table.BaseCatalog`).
930 Copy of the mask plane of `exposure`.
931 (`lsst.afw.image.Mask`)
934 result = pipeBase.Struct()
935 if id_generator
is None:
938 result.exposure = self.snap_combine.run(exposures).exposure
939 self.log.info(
"Initial PhotoCalib: %s", result.exposure.getPhotoCalib())
941 result.exposure.metadata[
"LSST CALIB ILLUMCORR APPLIED"] =
False
944 if self.config.do_illumination_correction:
945 if not result.exposure.metadata.get(
"LSST ISR FLAT APPLIED",
False):
946 raise pipeBase.InvalidQuantumError(
947 "Cannot use do_illumination_correction with an image that has not had a flat applied",
951 if camera_model.get(result.exposure.detector.getId()):
952 self.log.info(
"Updating WCS with the provided camera model.")
956 "useButlerCamera=True, but detector %s is not available in the provided camera. The "
957 "astrometry fit will use the WCS already attached to the exposure.",
958 result.exposure.detector.getId())
960 result.background =
None
961 summary_stat_catalog =
None
967 have_fit_astrometry =
False
968 have_fit_photometry =
False
973 illumination_correction,
976 result.psf_stars_footprints, result.background, _, adaptive_det_res_struct = self.
_compute_psf(
979 background_to_photometric_ratio=result.background_to_photometric_ratio,
986 if result.psf_stars_footprints[
"slot_Centroid_flag"].all():
987 psf_shape = result.exposure.psf.computeShape(result.exposure.psf.getAveragePosition())
989 n_sources=len(result.psf_stars_footprints),
990 psf_shape_ixx=psf_shape.getIxx(),
991 psf_shape_iyy=psf_shape.getIyy(),
992 psf_shape_ixy=psf_shape.getIxy(),
993 psf_size=psf_shape.getDeterminantRadius(),
996 if result.background
is None:
1000 result.psf_stars = result.psf_stars_footprints.asAstropy()
1004 result.exposure.wcs,
1005 sourceList=result.psf_stars_footprints,
1006 include_covariance=self.config.do_include_astrometric_errors,
1009 result.exposure, result.psf_stars_footprints
1011 num_astrometry_matches = len(astrometry_matches)
1012 self.metadata[
"astrometry_matches_count"] = num_astrometry_matches
1013 if "astrometry_matches" in self.config.optional_outputs:
1016 result.psf_stars = result.psf_stars_footprints.asAstropy()
1020 if self.config.doMaskDiffractionSpikes:
1021 self.diffractionSpikeMask.run(result.exposure)
1023 if self.config.do_adaptive_threshold_detection:
1026 background_to_photometric_ratio=result.background_to_photometric_ratio,
1034 background_to_photometric_ratio=result.background_to_photometric_ratio,
1035 adaptive_det_res_struct=adaptive_det_res_struct,
1036 num_astrometry_matches=num_astrometry_matches,
1038 psf = result.exposure.getPsf()
1039 psfSigma = psf.computeShape(result.exposure.getBBox().getCenter()).getDeterminantRadius()
1040 self.
_match_psf_stars(result.psf_stars_footprints, result.stars_footprints,
1045 result.exposure.wcs,
1046 sourceList=result.stars_footprints,
1047 include_covariance=self.config.do_include_astrometric_errors,
1050 summary_stat_catalog = result.stars_footprints
1051 result.stars = result.stars_footprints.asAstropy()
1052 self.metadata[
"star_count"] = np.sum(~result.stars[
"sky_source"])
1057 self.astrometry.check(result.exposure, result.stars_footprints, len(astrometry_matches))
1058 result.stars = result.stars_footprints.asAstropy()
1059 have_fit_astrometry =
True
1061 result.stars_footprints, photometry_matches, \
1062 photometry_meta, photo_calib = self.
_fit_photometry(result.exposure, result.stars_footprints)
1063 have_fit_photometry =
True
1064 self.metadata[
"photometry_matches_count"] = len(photometry_matches)
1067 result.stars = result.stars_footprints.asAstropy()
1071 summary_stat_catalog = result.stars_footprints
1072 if "photometry_matches" in self.config.optional_outputs:
1075 if "mask" in self.config.optional_outputs:
1076 result.mask = result.exposure.mask.clone()
1077 except pipeBase.AlgorithmError:
1078 if not have_fit_psf:
1079 result.exposure.setPsf(
None)
1080 if not have_fit_astrometry:
1081 result.exposure.setWcs(
None)
1082 if not have_fit_photometry:
1083 result.exposure.setPhotoCalib(
None)
1090 self.
_summarize(result.exposure, summary_stat_catalog, result.background)
1093 self.
_summarize(result.exposure, summary_stat_catalog, result.background)
1095 if self.config.do_calibrate_pixels:
1099 background_to_photometric_ratio=result.background_to_photometric_ratio,
1101 result.applied_photo_calib = photo_calib
1103 result.applied_photo_calib =
None
1107 if self.config.run_sattle:
1111 populate_sattle_visit_cache(result.exposure.getInfo().getVisitInfo(),
1112 historical=self.config.sattle_historical)
1113 self.log.info(
'Successfully triggered load of sattle visit cache')
1114 except requests.exceptions.HTTPError:
1115 self.log.exception(
"Sattle visit cache update failed; continuing with image processing")
1164 def _compute_psf(self, exposure, id_generator, background_to_photometric_ratio=None):
1165 """Find bright sources detected on an exposure and fit a PSF model to
1166 them, repairing likely cosmic rays before detection.
1168 Repair, detect, measure, and compute PSF twice, to ensure the PSF
1169 model does not include contributions from cosmic rays.
1173 exposure : `lsst.afw.image.Exposure`
1174 Exposure to detect and measure bright stars on.
1175 id_generator : `lsst.meas.base.IdGenerator`
1176 Object that generates source IDs and provides random seeds.
1177 background_to_photometric_ratio : `lsst.afw.image.Image`, optional
1178 Image to convert photometric-flattened image to
1179 background-flattened image.
1183 sources : `lsst.afw.table.SourceCatalog`
1184 Catalog of detected bright sources.
1185 background : `lsst.afw.math.BackgroundList`
1186 Background that was fit to the exposure during detection.
1187 cell_set : `lsst.afw.math.SpatialCellSet`
1188 PSF candidates returned by the psf determiner.
1190 def log_psf(msg, addToMetadata=False):
1191 """Log the parameters of the psf and background, with a prepended
1192 message. There is also the option to add the PSF sigma to the task
1198 Message to prepend the log info with.
1199 addToMetadata : `bool`, optional
1200 Whether to add the final psf sigma value to the task
1201 metadata (the default is False).
1203 position = exposure.psf.getAveragePosition()
1204 sigma = exposure.psf.computeShape(position).getDeterminantRadius()
1205 dimensions = exposure.psf.computeImage(position).getDimensions()
1206 if background
is not None:
1207 median_background = np.median(background.getImage().array)
1209 median_background = 0.0
1210 self.log.info(
"%s sigma=%0.4f, dimensions=%s; median background=%0.2f",
1211 msg, sigma, dimensions, median_background)
1213 self.metadata[
"final_psf_sigma"] = sigma
1215 self.log.info(
"First pass detection with Guassian PSF FWHM=%s pixels",
1216 self.config.install_simple_psf.fwhm)
1217 self.install_simple_psf.run(exposure=exposure)
1219 background = self.psf_subtract_background.run(
1221 backgroundToPhotometricRatio=background_to_photometric_ratio,
1223 log_psf(
"Initial PSF:")
1224 self.psf_repair.run(exposure=exposure, keepCRs=
True)
1226 table = afwTable.SourceTable.make(self.
psf_schema, id_generator.make_table_id_factory())
1227 if not self.config.do_adaptive_threshold_detection:
1228 adaptive_det_res_struct =
None
1231 detections = self.psf_detection.run(
1234 background=background,
1235 backgroundToPhotometricRatio=background_to_photometric_ratio,
1238 initialThreshold = self.config.psf_detection.thresholdValue
1239 initialThresholdMultiplier = self.config.psf_detection.includeThresholdMultiplier
1240 adaptive_det_res_struct = self.psf_adaptive_threshold_detection.run(
1242 initialThreshold=initialThreshold,
1243 initialThresholdMultiplier=initialThresholdMultiplier,
1245 detections = adaptive_det_res_struct.detections
1247 self.metadata[
"initial_psf_positive_footprint_count"] = detections.numPos
1248 self.metadata[
"initial_psf_negative_footprint_count"] = detections.numNeg
1249 self.metadata[
"initial_psf_positive_peak_count"] = detections.numPosPeaks
1250 self.metadata[
"initial_psf_negative_peak_count"] = detections.numNegPeaks
1251 self.psf_source_measurement.run(detections.sources, exposure)
1252 psf_result = self.psf_measure_psf.run(exposure=exposure, sources=detections.sources)
1259 if not self.config.do_adaptive_threshold_detection:
1263 self.install_simple_psf.run(exposure=exposure)
1265 log_psf(
"Rerunning with simple PSF:")
1273 self.psf_repair.run(exposure=exposure, keepCRs=
True)
1276 detections = self.psf_detection.run(
1279 background=background,
1280 backgroundToPhotometricRatio=background_to_photometric_ratio,
1282 self.psf_source_measurement.run(detections.sources, exposure)
1283 psf_result = self.psf_measure_psf.run(exposure=exposure, sources=detections.sources)
1285 self.metadata[
"simple_psf_positive_footprint_count"] = detections.numPos
1286 self.metadata[
"simple_psf_negative_footprint_count"] = detections.numNeg
1287 self.metadata[
"simple_psf_positive_peak_count"] = detections.numPosPeaks
1288 self.metadata[
"simple_psf_negative_peak_count"] = detections.numNegPeaks
1289 log_psf(
"Final PSF:", addToMetadata=
True)
1292 self.psf_repair.run(exposure=exposure)
1294 self.psf_source_measurement.run(detections.sources, exposure)
1298 return detections.sources, background, psf_result.cellSet, adaptive_det_res_struct
1330 def _find_stars(self, exposure, background, id_generator, background_to_photometric_ratio=None,
1331 adaptive_det_res_struct=None, num_astrometry_matches=None):
1332 """Detect stars on an exposure that has a PSF model, and measure their
1333 PSF, circular aperture, compensated gaussian fluxes.
1337 exposure : `lsst.afw.image.Exposure`
1338 Exposure to detect and measure stars on.
1339 background : `lsst.afw.math.BackgroundList`
1340 Background that was fit to the exposure during detection;
1341 modified in-place during subsequent detection.
1342 id_generator : `lsst.meas.base.IdGenerator`
1343 Object that generates source IDs and provides random seeds.
1344 background_to_photometric_ratio : `lsst.afw.image.Image`, optional
1345 Image to convert photometric-flattened image to
1346 background-flattened image.
1350 stars : `SourceCatalog`
1351 Sources that are very likely to be stars, with a limited set of
1352 measurements performed on them.
1355 id_generator.make_table_id_factory())
1357 maxAdaptiveDetIter = 8
1359 if adaptive_det_res_struct
is not None:
1360 for adaptiveDetIter
in range(maxAdaptiveDetIter):
1362 adaptiveDetectionConfig.reEstimateBackground =
False
1363 adaptiveDetectionConfig.includeThresholdMultiplier = 2.0
1365 adaptive_det_res_struct.thresholdValue*adaptive_det_res_struct.includeThresholdMultiplier
1367 adaptiveDetectionConfig.thresholdValue = max(
1368 self.config.star_detection.thresholdValue,
1369 threshFactor*psfThreshold/adaptiveDetectionConfig.includeThresholdMultiplier
1371 self.log.info(
"Using adaptive threshold detection (nIter = %d) with "
1372 "thresholdValue = %.2f and multiplier = %.1f",
1373 adaptiveDetIter, adaptiveDetectionConfig.thresholdValue,
1374 adaptiveDetectionConfig.includeThresholdMultiplier)
1376 config=adaptiveDetectionConfig
1378 detections = adaptiveDetectionTask.run(
1381 background=background,
1382 backgroundToPhotometricRatio=background_to_photometric_ratio,
1384 nFootprint = len(detections.sources)
1387 for src
in detections.sources:
1388 nPeakSrc = len(src.getFootprint().getPeaks())
1392 minIsolated = min(400, max(3, 0.005*nPeak, 0.6*num_astrometry_matches))
1394 self.log.info(
"Adaptive threshold detection _find_stars nIter: %d; "
1395 "nPeak/nFootprint = %.2f (max is 800); nIsolated = %d (min is %.1f).",
1396 adaptiveDetIter, nPeak/nFootprint, nIsolated, minIsolated)
1397 if nPeak/nFootprint > 800
or nIsolated < minIsolated:
1398 threshFactor = max(0.01, 1.5*threshFactor)
1399 self.log.warning(
"nPeak/nFootprint = %.2f (max is 800); nIsolated = %d "
1400 "(min is %.1f).", nPeak/nFootprint, nIsolated, minIsolated)
1404 threshFactor *= 0.75
1405 self.log.warning(
"No footprints detected on image. Decreasing threshold "
1406 "factor to %.2f. and rerunning.", threshFactor)
1410 detections = self.star_detection.run(
1413 background=background,
1414 backgroundToPhotometricRatio=background_to_photometric_ratio,
1416 sources = detections.sources
1417 self.star_sky_sources.run(exposure.mask, id_generator.catalog_id, sources)
1419 n_sky_sources = np.sum(sources[
"sky_source"])
1420 if (self.config.do_downsample_footprints
1421 and (len(sources) - n_sky_sources) > self.config.downsample_max_footprints):
1422 if exposure.info.id
is None:
1423 self.log.warning(
"Exposure does not have a proper id; using 0 seed for downsample.")
1426 seed = exposure.info.id & 0xFFFFFFFF
1428 gen = np.random.RandomState(seed)
1431 indices = np.arange(len(sources))[~sources[
"sky_source"]]
1432 indices = gen.choice(
1434 size=self.config.downsample_max_footprints,
1437 skyIndices, = np.where(sources[
"sky_source"])
1438 indices = np.concatenate((indices, skyIndices))
1440 self.log.info(
"Downsampling from %d to %d non-sky-source footprints.", len(sources), len(indices))
1442 sel = np.zeros(len(sources), dtype=bool)
1444 sources = sources[sel]
1448 self.star_deblend.run(exposure=exposure, sources=sources)
1451 if not sources.isContiguous():
1452 sources = sources.copy(deep=
True)
1455 self.star_measurement.run(sources, exposure)
1456 self.metadata[
"post_deblend_source_count"] = np.sum(~sources[
"sky_source"])
1457 self.metadata[
"saturated_source_count"] = np.sum(sources[
"base_PixelFlags_flag_saturated"])
1458 self.metadata[
"bad_source_count"] = np.sum(sources[
"base_PixelFlags_flag_bad"])
1463 self.star_normalized_calibration_flux.run(exposure=exposure, catalog=sources)
1464 self.star_apply_aperture_correction.run(sources, exposure.apCorrMap)
1465 self.star_catalog_calculation.run(sources)
1466 self.star_set_primary_flags.run(sources)
1468 result = self.star_selector.run(sources)
1470 if not result.sourceCat.isContiguous():
1471 return result.sourceCat.copy(deep=
True)
1473 return result.sourceCat
1769 """Remeasure the exposure's background with iterative adaptive
1770 threshold detection.
1774 result : `lsst.pipe.base.Struct`
1775 The current state of the result Strut from the run method of
1776 CalibrateImageTask. Will be modified in place.
1777 background_to_photometric_ratio : `lsst.afw.image.Image`, optional
1778 Image to convert photometric-flattened image to
1779 background-flattened image.
1783 result : `lsst.pipe.base.Struct`
1784 The modified result Struct with the new background subtracted.
1789 backgroundOrig = result.background.clone()
1790 median_background_orig = np.median(backgroundOrig.getImage().array)
1791 self.log.info(
"Original median_background = %.2f", median_background_orig)
1792 result.exposure.image.array += result.background.getImage().array
1795 origMask = result.exposure.mask.clone()
1796 bad_mask_planes = [
"BAD",
"EDGE",
"NO_DATA"]
1797 detected_mask_planes = [
"DETECTED",
"DETECTED_NEGATIVE"]
1799 detected_mask_planes,
1801 minDetFracForFinalBg = 0.02
1802 maxDetFracForFinalBg = 0.93
1806 maxAdaptiveDetIter = 10
1807 for dilateIter
in range(maxAdaptiveDetIter):
1808 dilatedMask = origMask.clone()
1809 for maskName
in detected_mask_planes:
1811 detectedMaskBit = dilatedMask.getPlaneBitMask(maskName)
1812 detectedMaskSpanSet = SpanSet.fromMask(dilatedMask, detectedMaskBit)
1813 detectedMaskSpanSet = detectedMaskSpanSet.dilated(nPixToDilate)
1814 detectedMaskSpanSet = detectedMaskSpanSet.clippedTo(dilatedMask.getBBox())
1816 detectedMask = dilatedMask.getMaskPlane(maskName)
1817 dilatedMask.clearMaskPlane(detectedMask)
1819 detectedMaskSpanSet.setMask(dilatedMask, detectedMaskBit)
1822 detected_mask_planes,
1824 if detected_fraction_dilated < maxDetFracForFinalBg
or nPixToDilate == 1:
1829 result.exposure.mask = dilatedMask
1830 self.log.warning(
"detected_fraction_orig = %.3f detected_fraction_dilated = %.3f",
1831 detected_fraction_orig, detected_fraction_dilated)
1832 n_above_max_per_amp = -99
1833 highest_detected_fraction_per_amp = float(
"nan")
1836 n_above_max_per_amp, highest_detected_fraction_per_amp, no_zero_det_amps = \
1838 detected_mask_planes, bad_mask_planes)
1839 self.log.warning(
"Dilated mask: n_above_max_per_amp = %d, "
1840 "highest_detected_fraction_per_amp = %.3f",
1841 n_above_max_per_amp, highest_detected_fraction_per_amp)
1843 bgIgnoreMasksToAdd = [
"SAT",
"SUSPECT",
"SPIKE"]
1844 detected_fraction = 1.0
1845 nFootprintTemp = 1e12
1847 for maskName
in bgIgnoreMasksToAdd:
1848 if (maskName
in result.exposure.mask.getMaskPlaneDict().keys()
1849 and maskName
not in starBackgroundDetectionConfig.background.ignoredPixelMask):
1850 starBackgroundDetectionConfig.background.ignoredPixelMask += [maskName]
1851 starBackgroundDetectionConfig.doTempLocalBackground =
False
1852 starBackgroundDetectionConfig.nSigmaToGrow = 70.0
1853 starBackgroundDetectionConfig.reEstimateBackground =
False
1854 starBackgroundDetectionConfig.includeThresholdMultiplier = 1.0
1855 starBackgroundDetectionConfig.thresholdValue = max(2.0, 0.2*median_background_orig)
1856 starBackgroundDetectionConfig.thresholdType =
"pixel_stdev"
1858 n_above_max_per_amp = -99
1859 highest_detected_fraction_per_amp = float(
"nan")
1860 doCheckPerAmpDetFraction =
True
1862 minFootprints = self.config.star_background_min_footprints
1864 for nIter
in range(maxIter):
1865 currentThresh = starBackgroundDetectionConfig.thresholdValue
1866 nZeroEncountered = 0
1867 if nFootprintTemp == 0:
1868 zeroFactor = min(0.98, 0.9 + 0.01*nZeroEncountered)
1869 starBackgroundDetectionConfig.thresholdValue = zeroFactor*currentThresh
1870 self.log.warning(
"No footprints detected. Decreasing threshold to %.2f and rerunning.",
1871 starBackgroundDetectionConfig.thresholdValue)
1873 config=starBackgroundDetectionConfig)
1875 tempDetections = starBackgroundDetectionTask.run(
1876 table=table, exposure=result.exposure, clearMask=
True)
1877 nFootprintTemp = len(tempDetections.sources)
1878 minFootprints = max(self.config.star_background_min_footprints,
1879 int(self.config.star_background_peak_fraction*tempDetections.numPosPeaks))
1880 minFootprints = min(200, minFootprints)
1881 nZeroEncountered += 1
1882 if nFootprintTemp >= minFootprints:
1884 detected_mask_planes,
1886 self.log.info(
"nIter = %d, thresh = %.2f: Fraction of pixels marked as DETECTED or "
1887 "DETECTED_NEGATIVE in star_background_detection = %.3f "
1888 "(max is %.3f; min is %.3f) nFootprint = %d (current min is %d)",
1889 nIter, starBackgroundDetectionConfig.thresholdValue,
1890 detected_fraction, maxDetFracForFinalBg, minDetFracForFinalBg,
1891 nFootprintTemp, minFootprints)
1896 if nFootprintTemp > 0
and nFootprintTemp < minFootprints:
1899 if detected_fraction > maxDetFracForFinalBg
or nFootprintTemp <= minFootprints:
1900 starBackgroundDetectionConfig.thresholdValue = 1.07*currentThresh
1901 if nFootprintTemp < minFootprints
and detected_fraction > 0.9*maxDetFracForFinalBg:
1902 if nFootprintTemp == 1:
1903 starBackgroundDetectionConfig.thresholdValue = 1.4*currentThresh
1905 starBackgroundDetectionConfig.thresholdValue = 1.2*currentThresh
1907 if n_above_max_per_amp > 1:
1908 starBackgroundDetectionConfig.thresholdValue = 1.1*currentThresh
1909 if detected_fraction < minDetFracForFinalBg:
1910 starBackgroundDetectionConfig.thresholdValue = 0.8*currentThresh
1912 config=starBackgroundDetectionConfig)
1914 tempDetections = starBackgroundDetectionTask.run(
1915 table=table, exposure=result.exposure, clearMask=
True)
1916 result.exposure.mask |= dilatedMask
1917 nFootprintTemp = len(tempDetections.sources)
1918 minFootprints = max(self.config.star_background_min_footprints,
1919 int(self.config.star_background_peak_fraction*tempDetections.numPosPeaks))
1920 minFootprints = min(200, minFootprints)
1923 self.log.info(
"nIter = %d, thresh = %.2f: Fraction of pixels marked as DETECTED or "
1924 "DETECTED_NEGATIVE in star_background_detection = %.3f "
1925 "(max is %.3f; min is %.3f) nFooprint = %d (current min is %d)",
1926 nIter, starBackgroundDetectionConfig.thresholdValue,
1927 detected_fraction, maxDetFracForFinalBg, minDetFracForFinalBg,
1928 nFootprintTemp, minFootprints)
1930 n_amp = len(result.exposure.detector.getAmplifiers())
1931 if doCheckPerAmpDetFraction:
1932 n_above_max_per_amp, highest_detected_fraction_per_amp, no_zero_det_amps = \
1934 detected_mask_planes, bad_mask_planes)
1936 if not no_zero_det_amps:
1937 starBackgroundDetectionConfig.thresholdValue = 0.95*currentThresh
1939 if (detected_fraction < maxDetFracForFinalBg
and detected_fraction > minDetFracForFinalBg
1940 and n_above_max_per_amp < int(0.75*n_amp)
1941 and no_zero_det_amps
1942 and nFootprintTemp >= minFootprints):
1943 if (n_above_max_per_amp < max(1, int(0.15*n_amp))
1944 or detected_fraction < 0.85*maxDetFracForFinalBg):
1947 self.log.warning(
"Making small tweak....")
1948 starBackgroundDetectionConfig.thresholdValue = 1.05*currentThresh
1949 self.log.warning(
"n_above_max_per_amp = %d (abs max is %d)", n_above_max_per_amp, int(0.75*n_amp))
1953 self.log.info(
"Fraction of pixels marked as DETECTED or DETECTED_NEGATIVE is now %.5f "
1954 "(highest per amp section = %.5f)",
1955 detected_fraction, highest_detected_fraction_per_amp)
1957 if detected_fraction > maxDetFracForFinalBg:
1958 result.exposure.mask = dilatedMask
1959 self.log.warning(
"Final fraction of pixels marked as DETECTED or DETECTED_NEGATIVE "
1960 "was too large in star_background_detection = %.3f (max = %.3f). "
1961 "Reverting to dilated mask from PSF detection...",
1962 detected_fraction, maxDetFracForFinalBg)
1963 star_background = self.star_background.run(
1964 exposure=result.exposure,
1965 backgroundToPhotometricRatio=background_to_photometric_ratio,
1967 result.background.append(star_background[0])
1969 median_background = np.median(result.background.getImage().array)
1970 self.log.info(
"New initial median_background = %.2f", median_background)
1978 if detected_fraction < 0.5:
1979 dilatedMask = result.exposure.mask.clone()
1980 for maskName
in detected_mask_planes:
1982 detectedMaskBit = dilatedMask.getPlaneBitMask(maskName)
1983 detectedMaskSpanSet = SpanSet.fromMask(dilatedMask, detectedMaskBit)
1984 detectedMaskSpanSet = detectedMaskSpanSet.dilated(nPixToDilate)
1985 detectedMaskSpanSet = detectedMaskSpanSet.clippedTo(dilatedMask.getBBox())
1987 detectedMask = dilatedMask.getMaskPlane(maskName)
1988 dilatedMask.clearMaskPlane(detectedMask)
1990 detectedMaskSpanSet.setMask(dilatedMask, detectedMaskBit)
1993 detected_mask_planes,
1995 result.exposure.mask = dilatedMask
1996 self.log.debug(
"detected_fraction_orig = %.3f detected_fraction_dilated = %.3f",
1997 detected_fraction_orig, detected_fraction_dilated)
2000 for maskName
in bgIgnoreMasksToAdd:
2001 if (maskName
in result.exposure.mask.getMaskPlaneDict().keys()
2002 and maskName
not in pedestalBackgroundConfig.ignoredPixelMask):
2003 pedestalBackgroundConfig.ignoredPixelMask += [maskName]
2004 pedestalBackgroundConfig.statisticsProperty =
"MEDIAN"
2005 pedestalBackgroundConfig.approxOrderX = 0
2006 pedestalBackgroundConfig.binSize = 64
2008 pedestalBackgroundList = pedestalBackgroundTask.run(
2009 exposure=result.exposure,
2010 background=result.background,
2011 backgroundToPhotometricRatio=background_to_photometric_ratio,
2015 pedestalBackground.append(pedestalBackgroundList[1])
2016 pedestalBackgroundLevel = pedestalBackground.getImage().array[0, 0]
2017 self.log.info(
"Subtracted pedestalBackgroundLevel = %.4f", pedestalBackgroundLevel)
2020 mask = result.exposure.mask
2021 for mp
in detected_mask_planes:
2022 if mp
not in mask.getMaskPlaneDict():
2023 mask.addMaskPlane(mp)
2024 mask &= ~mask.getPlaneBitMask(detected_mask_planes)