Skip to content

Commit

Permalink
Nabil review
Browse files Browse the repository at this point in the history
  • Loading branch information
nabobalis committed Sep 10, 2024
1 parent 8b4696f commit 5884fc6
Show file tree
Hide file tree
Showing 13 changed files with 126 additions and 122 deletions.
3 changes: 3 additions & 0 deletions _typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ default.extend-ignore-identifiers-re = [
"iy",
"BA",
]

[default.extend-words]
eis = "eis"
6 changes: 4 additions & 2 deletions changelog/207.breaking.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ As solar imaging data continues to grow in complexity, the existing coalignment
**New Features:**

- **Coalignment Interface** (`sunkit_image.coalignment.interface`):

- ``coalign`` function: A high-level function for image coalignment with a specified method. Default method: :func:`~sunkit_image.coalignment.match_template.match_template_coalign`.
- ``AffineParams`` NamedTuple: Stores and passes affine transformation parameters.

- **Template Matching Coalignment** (`sunkit_image.coalignment.match_template`):

- ``match_template_coalign`` function: A coalignment method that uses template matching.

- **Decorator Utility** (`sunkit_image.coalignment.decorators`):

- ``register_coalignment_method`` decorator: Enables easy registration of coalignment methods.
- Global Registry: Maintains a dictionary of registered coalignment methods.

Expand All @@ -27,5 +30,4 @@ As solar imaging data continues to grow in complexity, the existing coalignment

**Examples**

- Please find the examples related to the :ref:`adding of coalignment method <sunkit-image-how-to-guide-add-a-new-coalignment-method>` and using a
coalignment method here :ref:`sphx_glr_generated_gallery_aligning_aia_with_eis_maps.py`
- Please find the examples related to the :ref:`adding of coalignment method <sunkit-image-how-to-guide-add-a-new-coalignment-method>` and using a coalignment method here :ref:`sphx_glr_generated_gallery_aligning_aia_with_eis_maps.py`
6 changes: 3 additions & 3 deletions docs/how_to_guide/adding_a_coalignment_method.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ The :func:`~sunkit_image.coalignment.coalign` function does not change any probl
Checking if the Method is Registered
====================================

To check if your method is registered, you can check if it is present in the registered methods dictionary ``registered_methods`` using the following code:
To check if your method is registered, you can check if it is present in the registered methods dictionary ``REGISTERED_METHODS`` using the following code:

.. code-block:: python
from sunkit_image.coalignment.decorators import registered_methods
print(registered_methods)
from sunkit_image.coalignment.decorators import REGISTERED_METHODS
print(REGISTERED_METHODS)
65 changes: 37 additions & 28 deletions examples/aligning_aia_with_eis_maps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,73 +3,82 @@
Coaligning EIS to AIA
=====================
This example shows how to EISA data to AIA using cross-correlation which is implemented as the "match_template" method.
This example shows how to EIS data to AIA using cross-correlation which is implemented as the "match_template" method.
"""
# sphinx_gallery_thumbnail_number = 2 # NOQA: ERA001

import matplotlib.pyplot as plt

import astropy.units as u
from astropy.io import fits
from astropy.visualization import ImageNormalize, AsinhStretch
from sunpy.net import Fido ,attrs as a
from astropy.visualization import AsinhStretch, ImageNormalize

import sunpy.map
from sunpy.net import Fido
from sunpy.net import attrs as a

from sunkit_image.coalignment import coalign
from sunkit_image.data.test import get_test_filepath

###################################################################################
# Firstly, let us acquire the EIS and AIA data we need for this example.
# Firstly, let us acquire the IS and AIA data we need for this example.
#
# For this example, we will use the IS data from the sunpy data repository.
# This is a preprocessed IS raster data.


eis_map = sunpy.map.Map("https://github.com/sunpy/data/raw/main/sunkit-image/eis_20140108_095727.fe_12_195_119.2c-0.int.fits")

with fits.open ("https://github.com/sunpy/data/raw/main/sunkit-image/eis_20140108_095727.fe_12_195_119.2c-0.int.fits") as hdul:
eis_map = sunpy.map.Map(hdul[0].data, hdul[0].header)
eis_map.plot()
fig = plt.figure()

ax = fig.add_subplot(111, projection=eis_map)
eis_map.plot(axes=ax, aspect=eis_map.meta['cdelt2'] / eis_map.meta['cdelt1'],
cmap='Blues_r', norm=ImageNormalize(stretch=AsinhStretch()))

###################################################################################
# Lets find the AIA image that we want to use as a reference.
# We would be using the image near the date_average of this raster.
# We want to be using an image near the "date_average" of the IS raster.

# The following way is necessary because this eis doesn't have direct date_start, date_avg and date_end attributes.
query = Fido.search(a.Time(start=eis_map.meta["date_beg"], near=eis_map.meta["date_avg"], end=eis_map.meta["date_end"]), a.Instrument('aia'), a.Wavelength(193*u.angstrom))
aia_file = Fido.fetch(query)
aia_map = sunpy.map.Map(aia_file)

####################################################################################
# Before coaligning the images, we first downsample the AIA image to the same plate
# scale as the EIS image. This is not done automatically.
# Before coaligning the images, we first downsample the AIA image to the same plate
# scale as the IS image. This is not done automatically.

nx = (aia_map.scale.axis1 * aia_map.dimensions.x) / eis_map.scale.axis1
ny = (aia_map.scale.axis2 * aia_map.dimensions.y) / eis_map.scale.axis2

aia_downsampled = aia_map.resample(u.Quantity([nx, ny]))

####################################################################################
# Now we can coalign EIS to AIA using a cross-correlation.
# For this we would be using the "match_template" method.
# For details of the implementation refer to the
# Now we can coalign IS to AIA using cross-correlation. For this we would be using the
# "match_template" method. For details of the implementation refer to the
# documentation of `~sunkit_image.coalignment.match_template.match_template_coalign`.

coaligned_eis_map = coalign(aia_downsampled, eis_map)

####################################################################################
# To check now effective this has been, we will plot the EIS data and
# overlap the bright regions from AIA before and after coalignment.
# Plot the EIS data and overlap the bright regions from AIA before and after coalignment.
# To check now effective this has been, we will plot the IS data and
# overlap the bright regions from AIA before and after the coalignment.

levels = [200, 400, 500, 700, 800] * aia_map.unit
levels = [800] * aia_map.unit

fig = plt.figure(figsize=(15, 7.5))

# Plot before coalignment
# Before coalignment
ax = fig.add_subplot(121, projection=eis_map)
eis_map.plot(axes=ax, title='Before', aspect=eis_map.meta['cdelt2'] / eis_map.meta['cdelt1'],
eis_map.plot(axes=ax, title='Before coalignment',
aspect=coaligned_eis_map.meta['cdelt2'] / coaligned_eis_map.meta['cdelt1'],
cmap='Blues_r', norm=ImageNormalize(stretch=AsinhStretch()))
aia_map.draw_contours(levels, axes=ax, alpha=0.3)
aia_map.draw_contours(levels, axes=ax)

# Plot after coalignment
# After coalignment
ax = fig.add_subplot(122, projection=coaligned_eis_map)
coaligned_eis_map.plot(axes=ax, title='After', aspect=coaligned_eis_map.meta['cdelt2'] / coaligned_eis_map.meta['cdelt1'],
cmap='Blues_r', norm=ImageNormalize(stretch=AsinhStretch()))
aia_map.draw_contours(levels, axes=ax, alpha=0.3)
coaligned_eis_map.plot(axes=ax, title='After coalignment',
aspect=coaligned_eis_map.meta['cdelt2'] / coaligned_eis_map.meta['cdelt1'],
cmap='Blues_r', norm=ImageNormalize(stretch=AsinhStretch()))
aia_map.draw_contours(levels, axes=ax)

fig.tight_layout()
plt.show()

plt.show()
2 changes: 1 addition & 1 deletion sunkit_image/coalignment/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from sunkit_image.coalignment.interface import coalign
from sunkit_image.coalignment.match_template import match_template_coalign as _
from sunkit_image.coalignment.match_template import match_template_coalign as _ # NOQA: F401

__all__ = ["coalign"]
6 changes: 3 additions & 3 deletions sunkit_image/coalignment/decorators.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
__all__ = ["register_coalignment_method", "registered_methods"]
__all__ = ["register_coalignment_method", "REGISTERED_METHODS"]

# Global Dictionary to store the registered methods and their names
registered_methods = {}
REGISTERED_METHODS = {}


def register_coalignment_method(name):
Expand All @@ -15,7 +15,7 @@ def register_coalignment_method(name):
"""

def decorator(func):
registered_methods[name] = func
REGISTERED_METHODS[name] = func
return func

return decorator
60 changes: 26 additions & 34 deletions sunkit_image/coalignment/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from sunpy.sun.models import differential_rotation
from sunpy.util.exceptions import SunpyUserWarning

from sunkit_image.coalignment.decorators import registered_methods
from sunkit_image.coalignment.decorators import REGISTERED_METHODS

__all__ = ["AffineParams"]

Expand All @@ -34,7 +34,7 @@ class AffineParams(NamedTuple):

def _update_fits_wcs_metadata(reference_map, target_map, affine_params):
"""
Update the metadata of a sunpy Map object based on affine transformation
Update the metadata of a sunpy.map.Map` based on affine transformation
parameters.
Parameters
Expand All @@ -43,46 +43,39 @@ def _update_fits_wcs_metadata(reference_map, target_map, affine_params):
The reference map object to which the target map is to be coaligned.
target_map : `sunpy.map.Map`
The original map object whose metadata is to be updated.
affine_params : object
An object containing the affine transformation parameters. This object must
have attributes for translation (dx, dy), scale, and rotation.
affine_params : `NamedTuple`
A `NamedTuple` containing the affine transformation parameters.
If you want to use a custom object, it must have attributes for "translation" (dx, dy), "scale", and "rotation_matrix".
Returns
-------
`sunpy.map.Map`
A new sunpy map object with updated metadata reflecting the affine transformation.
"""
# Extacting the affine parameters
pc_matrix = target_map.rotation_matrix
translation = affine_params.translation
scale = affine_params.scale
rotation_matrix = affine_params.rotation_matrix
# Updating the PC matrix
new_pc_matrix = pc_matrix @ rotation_matrix
# Updating the PC_ij matrix
new_pc_matrix = target_map.rotation_matrix @ affine_params.rotation_matrix
# Calculate the new reference pixel.
old_reference_pixel = np.array([target_map.reference_pixel.x.value, target_map.reference_pixel.y.value])
new_reference_pixel = scale*rotation_matrix @ old_reference_pixel + translation
old_reference_pixel = np.asarray([target_map.reference_pixel.x.value, target_map.reference_pixel.y.value])
new_reference_pixel = affine_params.scale * affine_params.rotation_matrix @ old_reference_pixel + affine_params.translation
reference_coord = reference_map.wcs.pixel_to_world(new_reference_pixel[0],new_reference_pixel[1])
Txshift = reference_coord.Tx - target_map.reference_coordinate.Tx
Tyshift = reference_coord.Ty - target_map.reference_coordinate.Ty

# Create a new map with the updated metadata
fixed_map = target_map.shift_reference_coord(Txshift, Tyshift)

fixed_map.meta["PC1_1"] = new_pc_matrix[0, 0]
fixed_map.meta["PC1_2"] = new_pc_matrix[0, 1]
fixed_map.meta["PC2_1"] = new_pc_matrix[1, 0]
fixed_map.meta["PC2_2"] = new_pc_matrix[1, 1]

fixed_map.meta['cdelt1'] = (target_map.scale[0] / scale[0]).value
fixed_map.meta['cdelt2'] = (target_map.scale[1] / scale[1]).value

fixed_map.meta['cdelt1'] = (target_map.scale[0] / affine_params.scale[0]).value
fixed_map.meta['cdelt2'] = (target_map.scale[1] / affine_params.scale[1]).value
return fixed_map


def _warn_user_of_separation(reference_map,target_map):
def _warn_user_of_separation(reference_map, target_map):
"""
Issues a warning if the separation between the reference and target maps is
large.
Issues a warning if the separation between the ``reference_map`` and
``target_map`` is large.
Parameters
----------
Expand Down Expand Up @@ -124,15 +117,16 @@ def _warn_user_of_separation(reference_map,target_map):

def coalign(reference_map, target_map, method='match_template'):
"""
Performs image coalignment using a specified method (defaults to
`~sunkit_image.coalignment.match_template.match_template_coalign`). This
function updates the metadata of the target map to align it with the
reference map.
Performs image coalignment using the specified method.
This function updates the metadata of the target map to align it with the reference map.
.. note::
* This function is intended to correct maps with known incorrect metadata. It is not designed to address issues like differential rotation or changes in observer location, which are encoded in the coordinate metadata.
* The function modifies the metadata of the map, not the underlying array data. For adjustments that involve coordinate transformations, consider using `~sunpy.map.GenericMap.reproject_to` instead.
* This function is intended to correct maps with known incorrect metadata.
It is not designed to address issues like differential rotation or changes in observer location, which are encoded in the coordinate metadata.
* The function modifies the metadata of the map, not the underlying array data.
For adjustments that involve coordinate transformations, consider using `~sunpy.map.GenericMap.reproject_to` instead.
Parameters
----------
Expand All @@ -153,14 +147,12 @@ def coalign(reference_map, target_map, method='match_template'):
ValueError
If the specified method is not registered.
"""
if method not in registered_methods:
msg = (f"Method {method} is not a registered method: {list(registered_methods.keys())}."
if method not in REGISTERED_METHODS:
msg = (f"Method {method} is not a registered method: {list(REGISTERED_METHODS.keys())}."

Check warning on line 151 in sunkit_image/coalignment/interface.py

View check run for this annotation

Codecov / codecov/patch

sunkit_image/coalignment/interface.py#L151

Added line #L151 was not covered by tests
"The method needs to be registered, with the correct import.")
raise ValueError(msg)

Check warning on line 153 in sunkit_image/coalignment/interface.py

View check run for this annotation

Codecov / codecov/patch

sunkit_image/coalignment/interface.py#L153

Added line #L153 was not covered by tests
target_array = target_map.data
reference_array = reference_map.data

_warn_user_of_separation(reference_map, target_map)

AffineParams = registered_methods[method](reference_array, target_array)
return _update_fits_wcs_metadata(reference_map, target_map, AffineParams)
affine_params = REGISTERED_METHODS[method](reference_array, target_array)
return _update_fits_wcs_metadata(reference_map, target_map, affine_params)
18 changes: 9 additions & 9 deletions sunkit_image/coalignment/match_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,28 +94,28 @@ def match_template_coalign(reference_array, target_array):
Parameters
----------
input_array : numpy.ndarray
input_array : `numpy.ndarray`
The input 2D array to be coaligned.
template_array : numpy.ndarray
template_array : `numpy.ndarray`
The template 2D array to align to.
Returns
-------
AffineParams
A named tuple containing the following affine transformation parameters:
`sunkit_image.coalignment.interface.AffineParams`
A `NamedTuple` containing the following affine transformation parameters:
- scale : list
- scale : `list`
A list of tuples representing the scale transformation as an identity matrix.
- rotation : float
- rotation : `float`
The rotation angle in radians, which is fixed at 0.0 in this function.
- translation : tuple
- translation : `tuple`
A tuple containing the x and y translation values.
"""
corr = match_template(np.float64(reference_array), np.float64(target_array))
# Find the best match location
y_shift, x_shift = _find_best_match_location(corr)
# Particularly for this method, there is no change in the rotation or scaling, hence the hardcoded values of scale to 1.0 & rotation to identity matrix
# Particularly for this method, there is no change in the rotation or scaling,
# hence the hardcoded values of scale to 1.0 & rotation to identity matrix
scale = np.array([1.0, 1.0])
rotation_matrix = np.eye(2)
return AffineParams(scale=scale, rotation_matrix=rotation_matrix, translation=(x_shift , y_shift ))
Loading

0 comments on commit 5884fc6

Please sign in to comment.