18from __future__
import division, print_function
24import _SourceXtractorPy
as cpp
26if sys.version_info.major < 3:
27 from StringIO
import StringIO
29 from io
import StringIO
34 super(FitsFile, self).
__init__(str(filename))
48 d[a.key()] = headers[a.key()]
57 A MeasurementImage is the processing unit
for SourceXtractor++. Measurements
and model fitting can be done
58 over one,
or many, of them. It models the image, plus its associated weight file, PSF, etc.
62 fits_file : str
or FitsFile object
63 The path to a FITS image,
or an instance of FitsFile
65 The path to a PSF. It can be either a FITS image,
or a PSFEx model.
66 weight_file : str
or FitsFile
67 The path to a FITS image
with the pixel weights,
or an instance of FitsFile
69 Image gain. If
None, `gain_keyword` will be used instead.
71 Keyword
for the header containing the gain.
73 Saturation value. If
None, `saturation_keyword` will be used instead.
74 saturation_keyword : str
75 Keyword
for the header containing the saturation value.
77 Flux scaling. Each pixel value will be multiplied by this. If
None, `flux_scale_keyword` will be used
79 flux_scale_keyword : str
80 Keyword
for the header containing the flux scaling.
82 The type of the weight image. It must be one of:
85 The image itself
is used to compute internally a constant variance (default)
87 The image itself
is used to compute internally a variance map
89 The weight image must contain a weight-map
in units of absolute standard deviations
92 The weight image must contain a weight-map
in units of relative variance.
94 The weight image must contain a weight-map
in units of relative weights. The data are converted
96 weight_absolute : bool
97 If
False, the weight map will be scaled according to an absolute variance map built
from the image itself.
98 weight_scaling : float
99 Apply an scaling to the weight map.
100 weight_threshold : float
101 Pixels
with weights beyond this value are treated just like pixels discarded by the masking process.
102 constant_background : float
103 If set a constant background of that value
is assumed
for the image instead of using automatic detection
105 For multi-extension FITS file specifies the HDU number
for the image. Default 0 (primary HDU)
107 For multi-extension FITS file specifies the HDU number
for the psf. Defaults to the same value
as image_hdu
109 For multi-extension FITS file specifies the HDU number
for the weight. Defaults to the same value
as image_hdu
114 setattr(self, attr_name, value)
116 expected_type = type(getattr(self, attr_name))
117 raise TypeError(
'Expecting {} for {}, got {}'.format(expected_type.__name__, attr_name,
118 type(value).__name__))
120 def __init__(self, fits_file, psf_file=None, weight_file=None, gain=None,
121 gain_keyword='GAIN', saturation=None, saturation_keyword='SATURATE',
122 flux_scale=None, flux_scale_keyword='FLXSCALE',
123 weight_type='none', weight_absolute=False, weight_scaling=1.,
124 weight_threshold=None, constant_background=None,
125 image_hdu=0, psf_hdu=None, weight_hdu=None
130 if isinstance(fits_file, FitsFile):
132 file_path = fits_file.filename
135 file_path = fits_file
137 if isinstance(weight_file, FitsFile):
138 weight_file = weight_file.filename
140 super(MeasurementImage, self).
__init__(os.path.abspath(file_path),
141 os.path.abspath(psf_file)
if psf_file
else '',
142 os.path.abspath(weight_file)
if weight_file
else '')
144 if image_hdu < 0
or (weight_hdu
is not None and weight_hdu < 0)
or (
145 psf_hdu
is not None and psf_hdu < 0):
146 raise ValueError(
'HDU indices start at 0')
151 'WEIGHT_FILENAME': self.weight_file
154 self.
meta.update(hdu_list.get_headers(image_hdu))
158 elif gain_keyword
in self.
meta:
163 if saturation
is not None:
165 elif saturation_keyword
in self.
meta:
170 if flux_scale
is not None:
172 elif flux_scale_keyword
in self.
meta:
180 if weight_threshold
is None:
186 if constant_background
is not None:
188 self.
_set_checked(
'constant_background_value', constant_background)
200 if weight_hdu
is None:
210 Human readable representation for the object
212 return 'Image {}: {} / {}, PSF: {} / {}, Weight: {} / {}'.format(
219 def __init__(self, fits_file, psf_file=None, weight_file=None, gain=None,
220 gain_keyword='GAIN', saturation=None, saturation_keyword='SATURATE',
221 flux_scale=None, flux_scale_keyword='FLXSCALE',
222 weight_type='none', weight_absolute=False, weight_scaling=1.,
223 weight_threshold=None, constant_background=None,
224 image_hdu=0, psf_hdu=None, weight_hdu=None,
225 image_layer=0, weight_layer=0):
226 super(DataCubeSlice, self).
__init__(fits_file, psf_file, weight_file, gain,
227 gain_keyword, saturation, saturation_keyword,
228 flux_scale, flux_scale_keyword,
229 weight_type, weight_absolute, weight_scaling,
230 weight_threshold, constant_background,
231 image_hdu, psf_hdu, weight_hdu)
242 Human readable representation for the object
244 return 'DataCubeSlice {}: {} / {} / {}, PSF: {} / {}, Weight: {} / {} / {}'.format(
252 Models the grouping of images. Measurement can *not* be made directly on instances of this type.
253 The configuration must be
"frozen" before creating a MeasurementGroup
262 Constructor. It is not recommended to be used directly. Use instead load_fits_image
or load_fits_images.
267 if len(kwargs) != 1
or (
'images' not in kwargs
and 'subgroups' not in kwargs):
268 raise ValueError(
'ImageGroup only takes as parameter one of "images" or "subgroups"')
269 key = list(kwargs.keys())[0]
271 if isinstance(kwargs[key], list):
275 if key ==
'subgroups':
289 How may subgroups or images are there
in this group
298 Allows to iterate on the contained subgroups or images
315 Splits the group in various subgroups, applying a filter on the contained images. If the group has
316 already been split, applies the split to each subgroup.
320 grouping_method : callable
321 A callable that receives
as a parameter the list of contained images,
and returns
322 a list of tuples,
with the grouping key value,
and the list of grouped images belonging to the given key.
332 If some images have
not been grouped by the callable.
337 sub_group.split(grouping_method)
339 subgrouped_images = grouping_method(self.
__images)
340 if sum(len(p[1])
for p
in subgrouped_images) != len(self.
__images):
342 raise ValueError(
'Some images were not grouped')
344 for k, im_list
in subgrouped_images:
352 Add new images to the group.
356 images : list of, or a single, MeasurementImage
361 If the group has been split, no new images can be added.
364 raise ValueError(
'ImageGroup is already subgrouped')
365 if isinstance(images, MeasurementImage):
372 Add a subgroup to a group.
377 The new of the new group
382 raise Exception(
'ImageGroup is not subgrouped yet')
384 raise Exception(
'Subgroup {} alread exists'.format(name))
393 True if the group
is a leaf group
404 The name of the subgroup.
414 If the group has not been split.
416 If the group has
not been found.
419 raise ValueError(
'ImageGroup is not subgrouped yet')
421 return next(x
for x
in self.
__subgroups if x[0] == name)[1]
422 except StopIteration:
423 raise KeyError(
'Group {} not found'.format(name))
425 def print(self, prefix='', show_images=False, file=sys.stderr):
427 Print a human-readable representation of the group.
432 Print each line with this prefix. Used internally
for indentation.
434 Show the images belonging to a leaf group.
436 Where to
print the representation. Defaults to sys.stderr
439 print(
'{}Image List ({})'.format(prefix, len(self.
__images)), file=file)
442 print(
'{} {}'.format(prefix, im), file=file)
444 print(
'{}Image sub-groups: {}'.format(prefix,
448 print(
'{} {}:'.format(prefix, name), file=file)
449 group.print(prefix +
' ', show_images, file)
456 A human-readable representation of the group
459 self.print(show_images=True, file=string)
460 return string.getvalue()
465 Callable that can be used to split an ImageGroup by a keyword value (i.e. FILTER).
470 FITS header keyword (i.e. FILTER)
487 images : list of MeasurementImage
488 List of images to group
492 list of tuples of str and list of MeasurementImage
494 (R, [frame_r_01.fits, frame_r_02.fits]),
495 (G, [frame_g_01.fits, frame_g_02.fits])
500 if self.
__key not in im.meta:
501 raise KeyError(
'The image {}[{}] does not contain the key {}'.format(
502 im.meta[
'IMAGE_FILENAME'], im.image_hdu, self.
__key
504 if im.meta[self.
__key]
not in result:
505 result[im.meta[self.
__key]] = []
506 result[im.meta[self.
__key]].append(im)
507 return [(k, result[k])
for k
in result]
512 Callable that can be used to split an ImageGroup by a keyword value (i.e. FILTER), applying a regular
513 expression and using the first matching group
as key.
520 Regular expression. The first matching group will be used
as grouping key.
538 images : list of MeasurementImage
539 List of images to group
543 list of tuples of str and list of MeasurementImage
547 if self.
__key not in im.meta:
548 raise KeyError(
'The image {}[{}] does not contain the key {}'.format(
549 im.meta[
'IMAGE_FILENAME'], im.image_hdu, self.
__key
552 if group
not in result:
554 result[group].append(im)
555 return [(k, result[k])
for k
in result]
560 Once an instance of this class is created from an
ImageGroup, its configuration is
"frozen". i.e.
561 no new images can be added,
or no new grouping applied.
565 image_group : ImageGroup
568 def __init__(self, image_group, is_subgroup=False):
574 if image_group.is_leaf():
575 self.
__images = [im
for im
in image_group]
592 The subgroup with the given name
or image
with the given index depending on whether this
is a leaf group.
597 Subgroup name
or image index
601 MeasurementGroup
or MeasurementImage
606 If we can
't find what we want
611 return next(x
for x
in self.
__subgroups if x[0] == index)[1]
612 except StopIteration:
613 raise KeyError(
'Group {} not found'.format(index))
618 raise KeyError(
'Image #{} not found'.format(index))
625 Number of subgroups, or images contained within the group
637 True if the group
is a leaf group
641 def print(self, prefix='', show_images=False, file=sys.stderr):
643 Print a human-readable representation of the group.
648 Print each line with this prefix. Used internally
for indentation.
650 Show the images belonging to a leaf group.
652 Where to
print the representation. Defaults to sys.stderr
655 print(
'{}Image List ({})'.format(prefix, len(self.
__images)), file=file)
658 print(
'{} {}'.format(prefix, im), file=file)
660 print(
'{}Measurement sub-groups: {}'.format(prefix,
','.join(
663 print(
'{} {}:'.format(prefix, name), file=file)
664 group.print(prefix +
' ', show_images, file=file)
671 A human-readable representation of the group
674 self.print(show_images=True, file=string)
675 return string.getvalue()