-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DM-18739: Generate defects from standardized format #66
Changes from 7 commits
f414a3e
e84db23
4a54549
67ad559
b67afa5
73fce32
1ac866e
1b8c6eb
faf2ef6
9978bed
fccdd54
4022a6a
e0c72cc
dd63b84
1339d21
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
# -*- python -*- | ||
from lsst.sconsUtils import scripts | ||
scripts.BasicSConstruct("obs_test") | ||
scripts.BasicSConstruct("obs_test", defaultTargets=scripts.DEFAULT_TARGETS + ("data",)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Config override for lsst.pipe.tasks.IngestCalibsTask | ||
config.parse.translation = {'calibDate': 'CALIBDATE', | ||
'filter': 'FILTER', | ||
'ccd': 'DETECTOR', | ||
} | ||
config.register.columns = {'calibDate': 'text', | ||
'validStart': 'text', | ||
'validEnd': 'text', | ||
'filter': 'text', | ||
'ccd': 'int', | ||
} | ||
config.register.visit = ['calibDate'] | ||
config.register.validityUntilSuperseded = ['defects',] | ||
config.register.unique = ['calibDate'] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,86 +21,20 @@ | |
# see <http://www.lsstcorp.org/LegalNotices/>. | ||
# | ||
import argparse | ||
import time | ||
|
||
import numpy | ||
from astropy.io import fits | ||
from dateutil import parser as date_parser | ||
r-owen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
import lsst.afw.geom as afwGeom | ||
import lsst.afw.image as afwImage | ||
from lsst.ip.isr import getDefectListFromMask | ||
from lsst.meas.algorithms import Defects | ||
|
||
DefectsPath = "defects_c0.fits" | ||
DefectsPath = "defects_c0" | ||
"""Output path for defects file.""" | ||
detectorName = "0" | ||
"""Detector name.""" | ||
detectorSerial = "0000011" | ||
"""Detector serial code""" | ||
|
||
|
||
def getBBoxList(path, detectorName): | ||
"""Read a defects file and return the defects as a list of bounding | ||
boxes. | ||
|
||
Parameters | ||
--------- | ||
path : `str` | ||
Path to input defects file; a fits file. | ||
detectorName : `str` | ||
Name of detector. | ||
""" | ||
with fits.open(path) as hduList: | ||
for hdu in hduList[1:]: | ||
if hdu.header["name"] != detectorName: | ||
print("skipping header with name=%r" % (hdu.header["name"],)) | ||
continue | ||
|
||
bboxList = [] | ||
for data in hdu.data: | ||
bbox = afwGeom.Box2I( | ||
afwGeom.Point2I(int(data['x0']), int(data['y0'])), | ||
afwGeom.Extent2I(int(data['width']), int(data['height'])), | ||
) | ||
bboxList.append(bbox) | ||
return bboxList | ||
raise RuntimeError("Data not found for detector %r" % (detectorName,)) | ||
|
||
|
||
def writeDefectsFile(bboxList, path, detectorSerial, detectorName): | ||
"""Write a defects FITS file. | ||
|
||
Parameters | ||
---------- | ||
bboxList : `list` of `lsst.geom.Box2I` | ||
List of bounding boxes defining defect locations. | ||
path : `str` | ||
Path of output defects file; should end with ".fits". | ||
detectorSerial : `str` | ||
Serial code of detector. | ||
detectorName : `str` | ||
Name of detector. | ||
""" | ||
head = fits.Header() | ||
head.update('SERIAL', detectorSerial, 'Serial of the detector') | ||
head.update('NAME', detectorName, 'Name of detector for this defect map') | ||
head.update('CDATE', time.asctime(time.gmtime()), 'UTC of creation') | ||
|
||
x0 = numpy.array([d.getMinX() for d in bboxList]) | ||
y0 = numpy.array([d.getMinY() for d in bboxList]) | ||
width = numpy.array([d.getWidth() for d in bboxList]) | ||
height = numpy.array([d.getHeight() for d in bboxList]) | ||
|
||
col1 = fits.Column(name='x0', format='I', array=numpy.array(x0)) | ||
col2 = fits.Column(name='y0', format='I', array=numpy.array(y0)) | ||
col3 = fits.Column(name='height', format='I', array=numpy.array(height)) | ||
col4 = fits.Column(name='width', format='I', array=numpy.array(width)) | ||
cols = fits.ColDefs([col1, col2, col3, col4]) | ||
tbhdu = fits.new_table(cols, header=head) | ||
hdu = fits.PrimaryHDU() | ||
thdulist = fits.HDUList([hdu, tbhdu]) | ||
thdulist.writeto(DefectsPath) | ||
|
||
|
||
if __name__ == "__main__": | ||
parser = argparse.ArgumentParser( | ||
description="""Construct a defects file from the mask plane of a test camera bias frame. | ||
|
@@ -112,13 +46,20 @@ def writeDefectsFile(bboxList, path, detectorSerial, detectorName): | |
args = parser.parse_args() | ||
|
||
biasMI = afwImage.MaskedImageF(args.bias) | ||
defectList = getDefectListFromMask(biasMI, "BAD") | ||
bboxList = [defect.getBBox() for defect in defectList] | ||
writeDefectsFile(bboxList, DefectsPath, detectorSerial, detectorName) | ||
defectList = Defects.fromMask(biasMI, "BAD") | ||
SimonKrughoff marked this conversation as resolved.
Show resolved
Hide resolved
|
||
valid_start = date_parser.parse('19700101T000000') | ||
md = defectList.getMetadata() | ||
md['INSTRUME'] = 'test' | ||
md['DETECTOR'] = detectorName | ||
md['CALIBDATE'] = valid_start.isoformat() | ||
md['FILTER'] = None | ||
md['CALIB_ID'] = (f'detector={detectorName} calibDate={valid_start.isoformat()} ' | ||
'ccd={detectorName} ccdnum={detectorName} filter=None') | ||
SimonKrughoff marked this conversation as resolved.
Show resolved
Hide resolved
|
||
defectList.writeText(DefectsPath) | ||
print("wrote defects file %r" % (DefectsPath,)) | ||
|
||
test2BBoxList = getBBoxList(DefectsPath, detectorName) | ||
assert len(bboxList) == len(test2BBoxList) | ||
for boxA, boxB in zip(bboxList, test2BBoxList): | ||
assert boxA == boxB | ||
test2defectList = Defects.readText(DefectsPath+".ecsv") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it work to include the ".ecsv" suffix on DefectsPath itself -- would the correct file get written? If so, I think it would make the code a bit simpler and easier to understand. Also the description string would give the correct path for the output file (right now it is missing the ".ecsv" suffix). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you want to get the return value from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That sounds reasonable, though it means that the full name of the file cannot be included in the help string for the command line parser is produced. I suppose one could always print a message when saving the data. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe I have addressed this. |
||
assert len(defectList) == len(test2defectList) | ||
SimonKrughoff marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for dA, dB in zip(defectList, test2defectList): | ||
SimonKrughoff marked this conversation as resolved.
Show resolved
Hide resolved
|
||
assert dA.getBBox() == dB.getBBox() | ||
print("verified that defects file %r round trips correctly" % (DefectsPath,)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean
IngestDefectsTask
?