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
32measurement_images = {}
36 super(FitsFile, self).
__init__(str(filename))
50 d[a.key()] = headers[a.key()]
58 A MeasurementImage is the processing unit
for SourceXtractor++. Measurements
and model fitting can be done
59 over one,
or many, of them. It models the image, plus its associated weight file, PSF, etc.
63 fits_file : str
or FitsFile object
64 The path to a FITS image,
or an instance of FitsFile
66 The path to a PSF. It can be either a FITS image,
or a PSFEx model.
67 weight_file : str
or FitsFile
68 The path to a FITS image
with the pixel weights,
or an instance of FitsFile
70 Image gain. If
None, `gain_keyword` will be used instead.
72 Keyword
for the header containing the gain.
74 Saturation value. If
None, `saturation_keyword` will be used instead.
75 saturation_keyword : str
76 Keyword
for the header containing the saturation value.
78 Flux scaling. Each pixel value will be multiplied by this. If
None, `flux_scale_keyword` will be used
80 flux_scale_keyword : str
81 Keyword
for the header containing the flux scaling.
83 The type of the weight image. It must be one of:
86 The image itself
is used to compute internally a constant variance (default)
88 The image itself
is used to compute internally a variance map
90 The weight image must contain a weight-map
in units of absolute standard deviations
93 The weight image must contain a weight-map
in units of relative variance.
95 The weight image must contain a weight-map
in units of relative weights. The data are converted
97 weight_absolute : bool
98 If
False, the weight map will be scaled according to an absolute variance map built
from the image itself.
99 weight_scaling : float
100 Apply an scaling to the weight map.
101 weight_threshold : float
102 Pixels
with weights beyond this value are treated just like pixels discarded by the masking process.
103 constant_background : float
104 If set a constant background of that value
is assumed
for the image instead of using automatic detection
106 For multi-extension FITS file specifies the HDU number
for the image. Default 0 (primary HDU)
108 For multi-extension FITS file specifies the HDU number
for the psf. Defaults to the same value
as image_hdu
110 For multi-extension FITS file specifies the HDU number
for the weight. Defaults to the same value
as image_hdu
113 def __init__(self, fits_file, psf_file=None, weight_file=None, gain=None,
114 gain_keyword='GAIN', saturation=None, saturation_keyword='SATURATE',
115 flux_scale=None, flux_scale_keyword='FLXSCALE',
116 weight_type='none', weight_absolute=False, weight_scaling=1.,
117 weight_threshold=None, constant_background=None,
118 image_hdu=0, psf_hdu=None, weight_hdu=None
123 if isinstance(fits_file, FitsFile):
125 file_path = fits_file.filename
128 file_path = fits_file
130 if isinstance(weight_file, FitsFile):
131 weight_file = weight_file.filename
133 super(MeasurementImage, self).
__init__(os.path.abspath(file_path),
134 os.path.abspath(psf_file)
if psf_file
else '',
135 os.path.abspath(weight_file)
if weight_file
else '')
137 if image_hdu < 0
or (weight_hdu
is not None and weight_hdu < 0)
or (psf_hdu
is not None and psf_hdu < 0):
138 raise ValueError(
'HDU indices start at 0')
141 'IMAGE_FILENAME': self.file,
142 'PSF_FILENAME': self.psf_file,
143 'WEIGHT_FILENAME': self.weight_file
146 self.
meta.update(hdu_list.get_headers(image_hdu))
150 elif gain_keyword
in self.
meta:
151 self.
gain = float(self.
meta[gain_keyword])
155 if saturation
is not None:
157 elif saturation_keyword
in self.
meta:
162 if flux_scale
is not None:
164 elif flux_scale_keyword
in self.
meta:
172 if weight_threshold
is None:
178 if constant_background
is not None:
192 if weight_hdu
is None:
197 global measurement_images
198 measurement_images[self.id] = self
205 Human readable representation for the object
207 return 'Image {}: {} / {}, PSF: {} / {}, Weight: {} / {}'.format(
214 Print a human-readable representation of the configured measurement images.
219 Where to print the representation. Defaults to sys.stderr
221 print('Measurement images:', file=file)
222 for i
in measurement_images:
223 im = measurement_images[i]
224 print(
'Image {}'.format(i), file=file)
225 print(
' File: {}'.format(im.file), file=file)
226 print(
' PSF: {}'.format(im.psf_file), file=file)
227 print(
' Weight: {}'.format(im.weight_file), file=file)
232 Models the grouping of images. Measurement can *not* be made directly on instances of this type.
233 The configuration must be
"frozen" before creating a MeasurementGroup
242 Constructor. It is not recommended to be used directly. Use instead load_fits_image
or load_fits_images.
247 if len(kwargs) != 1
or (
'images' not in kwargs
and 'subgroups' not in kwargs):
248 raise ValueError(
'ImageGroup only takes as parameter one of "images" or "subgroups"')
249 key = list(kwargs.keys())[0]
251 if isinstance(kwargs[key], list):
255 if key ==
'subgroups':
269 How may subgroups or images are there
in this group
278 Allows to iterate on the contained subgroups or images
295 Splits the group in various subgroups, applying a filter on the contained images. If the group has
296 already been split, applies the split to each subgroup.
300 grouping_method : callable
301 A callable that receives
as a parameter the list of contained images,
and returns
302 a list of tuples,
with the grouping key value,
and the list of grouped images belonging to the given key.
312 If some images have
not been grouped by the callable.
317 sub_group.split(grouping_method)
319 subgrouped_images = grouping_method(self.
__images)
320 if sum(len(p[1])
for p
in subgrouped_images) != len(self.
__images):
322 raise ValueError(
'Some images were not grouped')
324 for k, im_list
in subgrouped_images:
332 Add new images to the group.
336 images : list of, or a single, MeasurementImage
341 If the group has been split, no new images can be added.
344 raise ValueError(
'ImageGroup is already subgrouped')
345 if isinstance(images, MeasurementImage):
352 Add a subgroup to a group.
357 The new of the new group
362 raise Exception(
'ImageGroup is not subgrouped yet')
364 raise Exception(
'Subgroup {} alread exists'.format(name))
373 True if the group
is a leaf group
384 The name of the subgroup.
394 If the group has not been split.
396 If the group has
not been found.
399 raise ValueError(
'ImageGroup is not subgrouped yet')
401 return next(x
for x
in self.
__subgroups if x[0] == name)[1]
402 except StopIteration:
403 raise KeyError(
'Group {} not found'.format(name))
405 def print(self, prefix='', show_images=False, file=sys.stderr):
407 Print a human-readable representation of the group.
412 Print each line with this prefix. Used internally
for indentation.
414 Show the images belonging to a leaf group.
416 Where to
print the representation. Defaults to sys.stderr
419 print(
'{}Image List ({})'.format(prefix, len(self.
__images)), file=file)
422 print(
'{} {}'.format(prefix, im), file=file)
424 print(
'{}Image sub-groups: {}'.format(prefix,
','.
join(str(x)
for x, _
in self.
__subgroups)), file=file)
426 print(
'{} {}:'.format(prefix, name), file=file)
427 group.print(prefix +
' ', show_images, file)
434 A human-readable representation of the group
437 self.print(show_images=True, file=string)
438 return string.getvalue()
441 """Creates an image group with the images of a (possibly multi-HDU) single FITS file.
443 If image is multi-hdu, psf
and weight can either be multi hdu
or lists of individual files.
445 In any case, they are matched
in order
and HDUs
not containing images (two dimensional arrays) are ignored.
447 :param image: The filename of the FITS file containing the image(s)
448 :param psf: psf file
or list of psf files
449 :param weight: FITS file
for the weight image
or a list of such files
451 :
return: A ImageGroup representing the images
455 image_hdu_idx = image_hdu_list.hdu_list
458 if isinstance(psf, list):
459 if len(psf) != len(image_hdu_idx):
460 raise ValueError(
"The number of psf files must match the number of images!")
462 psf_hdu_idx = [0] * len(psf_list)
464 psf_list = [psf] * len(image_hdu_idx)
465 psf_hdu_idx = range(len(image_hdu_idx))
468 if isinstance(weight, list):
469 if len(weight) != len(image_hdu_idx):
470 raise ValueError(
"The number of weight files must match the number of images!")
472 weight_hdu_idx = [0] * len(weight_list)
474 weight_list = [
None] * len(image_hdu_idx)
475 weight_hdu_idx = [0] * len(weight_list)
478 weight_hdu_idx = weight_hdu_list.hdu_list
479 weight_list = [weight_hdu_list] * len(image_hdu_idx)
482 for hdu, psf_file, psf_hdu, weight_file, weight_hdu
in zip(
483 image_hdu_idx, psf_list, psf_hdu_idx, weight_list, weight_hdu_idx):
485 image_hdu=hdu, psf_hdu=psf_hdu, weight_hdu=weight_hdu, **kwargs))
490 """Creates an image group for the given images.
495 A list of relative paths to the images FITS files. Can also be single string in which case,
496 this function acts like load_fits_image
498 A list of relative paths to the PSF FITS files (optional). It must match the length of image_list
or be
None.
499 weights : list of str
500 A list of relative paths to the weight files (optional). It must match the length of image_list
or be
None.
505 A ImageGroup representing the images
510 In case of mismatched list of files
513 if isinstance(images, list):
515 raise ValueError(
"An empty list passed to load_fits_images")
517 psfs = psfs
or [
None] * len(images)
518 weights = weights
or [
None] * len(images)
520 if not isinstance(psfs, list)
or len(psfs) != len(images):
521 raise ValueError(
"The number of image files and psf files must match!")
523 if not isinstance(weights, list)
or len(weights) != len(images):
524 raise ValueError(
"The number of image files and weight files must match!")
527 for f, p, w
in zip(images, psfs, weights):
540 Callable that can be used to split an ImageGroup by a keyword value (i.e. FILTER).
545 FITS header keyword (i.e. FILTER)
562 images : list of MeasurementImage
563 List of images to group
567 list of tuples of str and list of MeasurementImage
569 (R, [frame_r_01.fits, frame_r_02.fits]),
570 (G, [frame_g_01.fits, frame_g_02.fits])
575 if self.
__key not in im.meta:
576 raise KeyError(
'The image {}[{}] does not contain the key {}'.format(
577 im.meta[
'IMAGE_FILENAME'], im.image_hdu, self.
__key
579 if im.meta[self.
__key]
not in result:
580 result[im.meta[self.
__key]] = []
581 result[im.meta[self.
__key]].append(im)
582 return [(k, result[k])
for k
in result]
587 Callable that can be used to split an ImageGroup by a keyword value (i.e. FILTER), applying a regular
588 expression and using the first matching group
as key.
595 Regular expression. The first matching group will be used
as grouping key.
613 images : list of MeasurementImage
614 List of images to group
618 list of tuples of str and list of MeasurementImage
622 if self.
__key not in im.meta:
623 raise KeyError(
'The image {}[{}] does not contain the key {}'.format(
624 im.meta[
'IMAGE_FILENAME'], im.image_hdu, self.
__key
627 if group
not in result:
629 result[group].append(im)
630 return [(k, result[k])
for k
in result]
635 Once an instance of this class is created from an
ImageGroup, its configuration is
"frozen". i.e.
636 no new images can be added,
or no new grouping applied.
640 image_group : ImageGroup
645 def __init__(self, image_group, is_subgroup=False):
651 if image_group.is_leaf():
652 self.
__images = [im
for im
in image_group]
656 MeasurementGroup._all_groups.append(self)
671 The subgroup with the given name
or image
with the given index depending on whether this
is a leaf group.
676 Subgroup name
or image index
680 MeasurementGroup
or MeasurementImage
685 If we can
't find what we want
690 return next(x
for x
in self.
__subgroups if x[0] == index)[1]
691 except StopIteration:
692 raise KeyError(
'Group {} not found'.format(index))
697 raise KeyError(
'Image #{} not found'.format(index))
704 Number of subgroups, or images contained within the group
716 True if the group
is a leaf group
720 def print(self, prefix='', show_images=False, file=sys.stderr):
722 Print a human-readable representation of the group.
727 Print each line with this prefix. Used internally
for indentation.
729 Show the images belonging to a leaf group.
731 Where to
print the representation. Defaults to sys.stderr
734 print(
'{}Image List ({})'.format(prefix, len(self.
__images)), file=file)
737 print(
'{} {}'.format(prefix, im), file=file)
739 print(
'{}Measurement sub-groups: {}'.format(prefix,
','.
join(
742 print(
'{} {}:'.format(prefix, name), file=file)
743 group.print(prefix +
' ', show_images, file=file)
750 A human-readable representation of the group
753 self.print(show_images=True, file=string)
754 return string.getvalue()
ELEMENTS_API auto join(Args &&... args) -> decltype(joinPath(std::forward< Args >(args)...))