load_images
anomaly_match.data_io.load_images
¶
Functions for loading and processing images as a wrapper around fitsbolt.
apply_fits_flux_conversion(filepath, zeropoint_keyword='MAGZERO')
¶
Read a FITS file and convert pixel values to flux density in Jansky.
Uses the AB zeropoint from the FITS header and cutana's
convert_mosaic_to_flux to ensure the training path applies the
exact same conversion as the cutana prediction path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filepath
|
str
|
Path to the FITS file. |
required |
zeropoint_keyword
|
str
|
FITS header keyword for the AB zeropoint. |
'MAGZERO'
|
Returns:
| Type | Description |
|---|---|
ndarray
|
np.ndarray: Flux-converted 2-D image in Jansky (float32). |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the primary HDU contains no image data. |
Source code in anomaly_match/data_io/load_images.py
get_fitsbolt_config(cfg, size_override='default')
¶
Get the fitsbolt configuration from the provided configuration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cfg
|
DotMap
|
The configuration object. |
required |
size_override
|
list[int] | str | None
|
If "default", use cfg.normalisation.image_size. If None, disable resizing (full resolution). Otherwise use the provided value. |
'default'
|
Returns:
| Name | Type | Description |
|---|---|---|
DotMap |
DotMap
|
The cfg with a cfg.fitsbolt_cfg subdotmap. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If configuration does not include a normalisation config. |
Source code in anomaly_match/data_io/load_images.py
normalise_channel_combination(channel_combination)
¶
Rescale output rows (with non-negative weights) whose weights sum to more than 1.
fitsbolt combines channels and then clips the result to the output dtype
range (np.clip(...).astype — see Lasloruhberg/fitsbolt#40). A row of
non-negative weights summing to more than 1 therefore pushes bright pixels
past the range and silently drops that signal on uint8 output. Scaling
such a row to sum 1 turns it into a weighted average that can never exceed
the input range, so no value is lost.
Rows that already sum to <= 1 are left untouched (identity, single-band
broadcast, and any convex combination are unaffected). Rows containing a
negative weight are not rescaled — a scale can't bring them back into
range — but their presence is reported so the caller can warn.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
channel_combination
|
ndarray | None
|
|
required |
Returns:
| Type | Description |
|---|---|
ndarray | None
|
Tuple |
list[int]
|
(possibly rescaled) array — a new array only when a row changed, |
bool
|
otherwise the input unchanged — or |
tuple[ndarray | None, list[int], bool]
|
row indices that were rescaled; |
tuple[ndarray | None, list[int], bool]
|
weight is negative. |
Source code in anomaly_match/data_io/load_images.py
fitsbolt_applies_channel_combination(cfg)
¶
Whether fitsbolt combined the bands in the file-decode path.
Answers one narrow question: for an image fitsbolt has just decoded through
:func:load_and_process_wrapper or
:func:load_and_process_single_wrapper, did fitsbolt already apply the
matrix? It did whenever fits_extension is set, because
:func:get_fitsbolt_config hands it one then; otherwise the matrix is
applied afterwards, in :func:finish_channel_combination.
Only those two wrappers may consult this. fits_extension also holds
band names for Cutana catalogues, so this returns True for a Cutana or
Zarr config even though the container decoders null the matrix out of their
own fitsbolt config and combine it themselves — they call
:func:_apply_channel_combination directly (see
container_loaders.decode_zarr_image) and deliberately bypass
:func:finish_channel_combination. Routing them through it would take the
"fitsbolt already combined" branch and drop the user's matrix entirely.
Read the answer from here rather than from the fitsbolt config that came
back: fitsbolt fills in a channel_combination of its own during
load_and_process_images (recompute_config_channel_combination
mutates the config in place), so by the time an image is decoded that field
no longer says who is responsible for combining.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cfg
|
DotMap
|
AnomalyMatch configuration. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
Source code in anomaly_match/data_io/load_images.py
blank_output_rows(channel_combination)
¶
Find the output channels a channel_combination matrix blanks.
AnomalyMatch reads a row whose weights are all zero as "leave this output
channel empty" — the way a user drops a band they don't want the model to
see without changing n_output_channels or retraining at a different
channel count.
A row that merely sums to zero is not blank: [1, -1] is the difference
of two bands and produces a real output channel. Blanking it would replace
the user's data with zeros, and — because the substitution in
:func:fitsbolt_safe_channel_combination would also drop the negative
weight before fitsbolt sees it — would do so instead of raising the error
fitsbolt owes a FITS source with negative weights.
The tolerance is per weight rather than on the row sum, so a row of negligible weights still counts as blank and never reaches fitsbolt's own zero-sum check.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
channel_combination
|
ndarray | None
|
|
required |
Returns:
| Type | Description |
|---|---|
list[int]
|
Sorted indices of the blank rows; empty when nothing is blanked. |
Source code in anomaly_match/data_io/load_images.py
fitsbolt_safe_channel_combination(channel_combination)
¶
Rewrite blank rows so fitsbolt's config validator accepts the matrix.
fitsbolt rejects a zero-sum row outright (must not sum to zero) and
re-validates on every load_and_process_images call, so a blanked
channel cannot reach the FITS path as-is (#588). Its combine step is a
plain matmul that would handle the zero row correctly — only the validator
stands in the way. So each blank row is replaced by a copy of a surviving
row, and :func:zero_blank_output_channels zeroes those channels again
once fitsbolt is done.
Duplicating a row that is already in the output leaves the kept channels bit-identical: the substituted channel adds no pixel value the stack didn't already contain, so the cross-channel minimum and maximum that CONVERSION_ONLY and ZSCALE clip against are unchanged, and LOG and ASINH normalise each channel independently anyway.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
channel_combination
|
ndarray | None
|
|
required |
Returns:
| Type | Description |
|---|---|
ndarray | None
|
A validator-safe matrix of the same shape, the input unchanged when no |
ndarray | None
|
row is blank, or |
Raises:
| Type | Description |
|---|---|
ValueError
|
If every row is blank — there is no surviving row to copy, and the result would be an entirely empty image rather than a deliberately blanked channel. |
Source code in anomaly_match/data_io/load_images.py
fitsbolt_channel_combination(cfg)
¶
The channel_combination to hand fitsbolt's create_config for cfg.
fitsbolt applies the matrix to every input in 0.3.x (fitsbolt#39 no longer
skips non-FITS arrays), so for non-FITS sources it gets None and AnomalyMatch
applies the matrix post-hoc (:func:finish_channel_combination and the
Cutana/Zarr decoders), which avoids a double application. FITS inputs still go
through fitsbolt, which combines bands within normalisation — but its validator
rejects the all-zero rows AnomalyMatch uses to blank an output channel, so it gets
the substituted matrix and :func:finish_channel_combination re-blanks afterwards
(#588).
:func:get_fitsbolt_config and validate_config both call this, so a config
validates exactly when the matrix the run hands fitsbolt is acceptable.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cfg
|
DotMap
|
AnomalyMatch configuration. |
required |
Returns:
| Type | Description |
|---|---|
ndarray | None
|
The validator-safe matrix when fitsbolt applies it, otherwise |
Source code in anomaly_match/data_io/load_images.py
fitsbolt_decode_config(cfg)
¶
Per-decode copy of cfg.fitsbolt_cfg, sized for what fitsbolt must return.
When AnomalyMatch applies channel_combination itself (non-FITS sources —
see :func:fitsbolt_channel_combination), fitsbolt must hand back the
matrix's input channels so the post-hoc combine has something to map from.
Asking it for n_output_channels instead makes it try to reduce, say,
3 RGB channels to 1 with no matrix, which it refuses — so a 1- or 2-output
matrix on an RGB image folder or Zarr store never decoded at all.
A copy rather than an edit: cfg.fitsbolt_cfg.n_output_channels is saved
into checkpoints as the model's channel count, and the shared config is read
concurrently by preview and prediction threads.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cfg
|
DotMap
|
AnomalyMatch configuration with |
required |
Returns:
| Type | Description |
|---|---|
DotMap
|
A fitsbolt config safe to mutate for a single decode. |
Source code in anomaly_match/data_io/load_images.py
zero_blank_output_channels(image, blank_rows)
¶
Blank the output channels that :func:fitsbolt_safe_channel_combination filled in.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
image
|
ndarray
|
HWC image fitsbolt produced from the substituted matrix. |
required |
blank_rows
|
list[int]
|
Output channel indices to zero. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The image with blank_rows zeroed. Modifies a copy, never the input. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If image has fewer channels than the matrix has rows — the decode produced a shape the matrix cannot describe, so zeroing by index would silently blank the wrong channel. |
Source code in anomaly_match/data_io/load_images.py
finish_channel_combination(image, cfg)
¶
Complete channel combination for an image fitsbolt has just processed.
Two paths meet here, and which one applies depends on whether fitsbolt
already combined the bands (:func:fitsbolt_applies_channel_combination):
- fitsbolt combined the bands (FITS inputs) — it ran the substituted matrix
from :func:
fitsbolt_safe_channel_combination, so the only work left is re-blanking the channels the user zeroed. - fitsbolt did not (
channel_combination=Nonefor non-FITS inputs) — the full matrix is applied here as post-processing, the single application for those sources.
For file decodes only. The Zarr and Cutana decoders combine their own output
and must keep bypassing this — see
:func:fitsbolt_applies_channel_combination for why routing them through it
would drop the matrix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
image
|
ndarray
|
HWC image returned by the fitsbolt pipeline. |
required |
cfg
|
DotMap
|
AnomalyMatch configuration holding |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The image with the configured channel combination fully applied. |
Source code in anomaly_match/data_io/load_images.py
load_and_process_wrapper(filepaths, cfg, desc='Loading images', show_progress=True)
¶
Load and process multiple image files using fitsbolt.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filepaths
|
list[str]
|
List of image file paths to load. |
required |
cfg
|
DotMap
|
Configuration object with normalisation settings. |
required |
desc
|
str
|
Description for the loading progress bar. |
'Loading images'
|
show_progress
|
bool
|
Whether to show a progress bar. |
True
|
Returns:
| Type | Description |
|---|---|
list[tuple[str, ndarray]]
|
List of (filepath, processed image) tuples. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the number of loaded images does not match filepaths. |
Source code in anomaly_match/data_io/load_images.py
load_and_process_single_wrapper(filepath, cfg, desc='image load and process', show_progress=False, prediction=False, size_override='default')
¶
Load and process a single image file.
Creates a fitsbolt config if not part of current cfg.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filepath
|
str
|
The path to the image file. |
required |
cfg
|
DotMap
|
The configuration object of AnomalyMatch. |
required |
desc
|
str
|
Description for the loading process. Defaults to "image load and process". |
'image load and process'
|
show_progress
|
bool
|
Whether to show progress. Defaults to False. |
False
|
prediction
|
bool
|
Whether this is a prediction step. Defaults to False. |
False
|
size_override
|
list[int] | str | None
|
If "default", use cfg.normalisation.image_size. If None, disable resizing (full resolution). |
'default'
|
Returns:
| Type | Description |
|---|---|
ndarray
|
np.ndarray: The processed image in H,W,C format. |
Source code in anomaly_match/data_io/load_images.py
process_single_wrapper(image, cfg, desc='source', *, combine_post_hoc=False)
¶
Process a single image using fitsbolt.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
image
|
ndarray
|
Input image as numpy array |
required |
cfg
|
DotMap
|
Configuration object with fitsbolt_cfg set |
required |
desc
|
str
|
Description for logging |
'source'
|
combine_post_hoc
|
bool
|
Whether the caller applies |
False
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Processed image |
Raises:
| Type | Description |
|---|---|
ValueError
|
If fitsbolt_cfg is not properly set in cfg |
Source code in anomaly_match/data_io/load_images.py
detect_num_channels(root_dir, filenames)
¶
Detect the number of image channels from a sample image file.
For FITS files, returns None since channel count depends on fits_extension and channel_combination settings.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
root_dir
|
str
|
Directory containing the images. |
required |
filenames
|
list[str]
|
List of image filenames. |
required |
Returns:
| Type | Description |
|---|---|
int | None
|
Detected number of channels, or None if detection is not applicable. |