Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
bec3eaf
commit reference matlab code
skbarber Mar 5, 2026
6adc5d3
Support list inputs for image analysis
skbarber Mar 5, 2026
dc3bbc9
Merge branch 'master' into Widen-ImageAnalyzer-base-signatures
skbarber Mar 5, 2026
f1085e2
Merge branch 'Widen-ImageAnalyzer-base-signatures' into HTT-Magspec-i…
skbarber Mar 5, 2026
4e17753
add some example data and calibration data
skbarber Mar 6, 2026
69dbf45
move things
skbarber Mar 6, 2026
344d334
Refactor magspec calibration into pluggable classes
skbarber Mar 6, 2026
f2890c7
add vignette correction as a processing step
skbarber Mar 7, 2026
15ed8e5
Update __init__.py
skbarber Mar 7, 2026
a9b774b
Add a charge calibration to magspec analyzer
skbarber Mar 7, 2026
e5b34ed
add notebook for testing htt magspec
skbarber Mar 9, 2026
b1264e2
axis interpolation now preserves the integral of the signal
skbarber Mar 10, 2026
769da5d
consolidation of existing code into more manageable pieces
skbarber Mar 10, 2026
2e88c83
add more useful example data
skbarber Mar 10, 2026
bf4a268
working example with comparison to original data
skbarber Mar 10, 2026
51dab8d
Update HTT_magspec_analysis_tests.ipynb
skbarber Mar 11, 2026
f449ef4
add option to pass magnetic field directly through auxialiary_data kw…
skbarber Mar 11, 2026
5858ecf
Save calibrated images and spectra to disk
skbarber Mar 24, 2026
f7d3118
Merge branch 'master' into HTT-Magspec-image-stitching-algorithm
skbarber Mar 24, 2026
a8f9369
Use explicit BField_key for aux magnetic field
skbarber Mar 24, 2026
566e1d6
delete original matlab code, move calibrations to data
skbarber Mar 26, 2026
35c51d6
Update HTT_magspec_analysis_tests.ipynb
skbarber Mar 26, 2026
8be40a7
formating
skbarber Mar 26, 2026
52d5c25
formatting
skbarber Mar 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions ImageAnalysis/image_analysis/algorithms/axis_interpolation.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,19 @@ def interpolate_image_axis(
)
physical_max = calib_max

# Create uniform physical axis
# Compute original bin widths
dE = np.gradient(pixel_to_physical)

# Uniform axis
uniform_axis = np.linspace(physical_min, physical_max, num_points)

# Interpolate
if axis == 1: # Horizontal (typical for energy dispersion)
# Interpolate each row
if axis == 1:
output = np.zeros((image.shape[0], num_points), dtype=image.dtype)

for i in range(image.shape[0]):
output[i] = np.interp(uniform_axis, pixel_to_physical, image[i, :])
density = image[i, :] / dE
density_interp = np.interp(uniform_axis, pixel_to_physical, density)
output[i] = density_interp
logger.debug(
f"Interpolated {image.shape[0]} rows from {image.shape[1]} to {num_points} points"
)
Expand Down
62 changes: 44 additions & 18 deletions ImageAnalysis/image_analysis/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,28 @@ def __init__(self, **config):
pass

def analyze_image(
self, image: Union[Array1D, Array2D], auxiliary_data: Optional[dict] = None
self,
image: Union[Array1D, Array2D, list[Array1D], list[Array2D]],
auxiliary_data: Optional[dict] = None,
) -> ImageAnalyzerResult:
"""Calculate metrics from an image.
"""Calculate metrics from an image or list of images.

This function should be implemented by each device's ImageAnalyzer subclass,
to run on an image from that device (obviously).

Should take full-size (i.e. uncropped) image.

For multi-device analyzers (e.g. multi-camera diagnostics), a list of
arrays may be passed — one per device. The analyzer is responsible for
combining them as needed and returning a single result.

Parameters
----------
image : 2d array (e.g. MxN) or standard y vs x data (eg. Nx2)
auxiliary_data : dict
Additional data used by the image image_analyzer for this image, such as
image : Union[Array1D, Array2D, list[Array1D], list[Array2D]]
A single 2D array (e.g. MxN), a single 1D dataset (e.g. Nx2),
or a list of such arrays for multi-device analysis.
auxiliary_data : dict, optional
Additional data used by the image analyzer for this image, such as
image range.

Returns
Expand All @@ -84,16 +92,23 @@ def analyze_image(
raise NotImplementedError()

def analyze_image_file(
self, image_filepath: Path, auxiliary_data: Optional[dict] = None
self,
image_filepath: Union[Path, list[Path]],
auxiliary_data: Optional[dict] = None,
) -> ImageAnalyzerResult:
"""
Method to enable the use of a file path rather than Array2D.
Method to enable the use of a file path (or list of paths) rather than arrays.

For multi-device analyzers, a list of file paths can be passed — one
per device. The paths are loaded via :meth:`load_image` and the
resulting array(s) are passed to :meth:`analyze_image`.

Parameters
----------
image_filepath : Path
auxiliary_data : dict
Additional data used by the image image_analyzer for this image, such as
image_filepath : Union[Path, list[Path]]
A single file path or a list of file paths for multi-device analysis.
auxiliary_data : dict, optional
Additional data used by the image analyzer for this image, such as
image range.

Returns
Expand All @@ -106,23 +121,34 @@ def analyze_image_file(

return self.analyze_image(image, auxiliary_data)

def load_image(self, file_path: Path) -> Union[Array1D, Array2D]:
def load_image(
self, file_path: Union[Path, list[Path]]
) -> Union[Array1D, Array2D, list[Union[Array1D, Array2D]]]:
"""
Load an image from a path.
Load an image from a path, or multiple images from a list of paths.

By default, the read_imaq_png function is used.
For file types not directly supported by this method, e.g. .himg files from a
Haso device type, this method be implemented in that device's ImageAnalyzer
subclass.
When given a single path, loads and returns a single array using
:func:`read_imaq_image` (or subclass override).

When given a list of paths, recursively calls ``self.load_image`` on
each path and returns a list of arrays. This means subclasses that
override ``load_image`` for custom file formats (e.g. ``.himg``)
automatically get list support for free.

Parameters
----------
file_path : Path
file_path : Union[Path, list[Path]]
A single file path or a list of file paths for multi-device loading.

Returns
-------
image : Union[Array1D,Array2D]
Union[Array1D, Array2D, list[Union[Array1D, Array2D]]]
A single array for a single path, or a list of arrays for a list
of paths.
"""
if isinstance(file_path, list):
return [self.load_image(p) for p in file_path]

image = read_imaq_image(file_path)

return image
Expand Down
Loading
Loading