Source code for smart.processing
import numpy as np
import skimage as ski
from matplotlib import colors
from skimage.morphology import disk
import astropy.units as u
from sunpy.map import Map, all_coordinates_from_map, coordinate_is_on_solar_disk
__all__ = [
"map_threshold",
"calculate_cosine_correction",
"cosine_correct_data",
"smooth_los_threshold",
"smart_prep",
]
[docs]
def map_threshold(im_map):
"""
Set off disk pixels to black and clip the vmin and vmax of the map.
Parameters
----------
im_map : `~sunpy.map.Map`
Unprocessed magnetogram map.
Returns
-------
im_map : `~sunpy.map.Map`
Processed magnetogram map.
"""
im_map.data[~coordinate_is_on_solar_disk(all_coordinates_from_map(im_map))] = np.nan
im_map.cmap.set_bad("k")
im_map.plot_settings["norm"] = colors.Normalize(vmin=-200, vmax=200)
return im_map
[docs]
@u.quantity_input
def smooth_los_threshold(
im_map: Map,
thresh: u.Quantity[u.Gauss] = 100 * u.Gauss,
dilation_radius: u.Quantity[u.arcsec] = 5 * u.arcsec,
sigma: u.Quantity[u.arcsec] = 10 * u.arcsec,
min_size: u.Quantity[u.arcsec] = 2250 * u.arcsec,
):
"""
We apply Smoothing, a noise Threshold, and an LOS correction, respectively, to the data.
Parameters
----------
im_map : ``~sunpy.map.Map``
Processed SunPy magnetogram map.
thresh : `int`, optional
Threshold value to identify regions of interest (default is 100 Gauss).
dilation_radius : `int`, optional
Radius of the disk for binary dilation (default is 2 arcsecs).
sigma : `int`, optional
Standard deviation for Gaussian smoothing (default is 10 arcsecs).
min_size : `int`, optional
Minimum size of regions to keep in final mask (default is 2250 arcsecs**2).
Returns
-------
smooth_map : `~sunpy.map.Map`
Map after applying Gaussian smoothing.
filtered_labels : `numpy.ndarray`
2D array with each pixel labelled.
mask_sizes : `~numpy.ndarray`
Boolean array indicating the sizes of each labeled region.
"""
arcsec_to_pixel = ((im_map.scale[0] + im_map.scale[1]) / 2) ** (-1)
dilation_radius = (np.round(dilation_radius * arcsec_to_pixel)).to_value(u.pix)
sigma = (np.round(sigma * arcsec_to_pixel)).to_value(u.pix)
min_size = (np.round(min_size * arcsec_to_pixel)).to_value(u.pix)
cosmap_data = cosine_correct_data(im_map)
negmask = cosmap_data < -thresh
posmask = cosmap_data > thresh
mask = negmask | posmask
dilated_mask = ski.morphology.binary_dilation(mask, disk(dilation_radius))
smoothed_data = ski.filters.gaussian(np.nan_to_num(cosmap_data) * dilated_mask, sigma)
smooth_map = Map(smoothed_data, im_map.meta)
labels = ski.measure.label(dilated_mask)
label_sizes = np.bincount(labels.ravel())
mask_sizes = label_sizes > min_size
mask_sizes[0] = 0
filtered_labels = mask_sizes[labels]
return smooth_map, filtered_labels, mask_sizes
[docs]
def calculate_cosine_correction(im_map: Map):
"""
Find the cosine correction values for on-disk pixels.
Parameters
----------
im_map : `~sunpy.map.Map`
Processed SunPy magnetogram map.
Returns
-------
cos_correction : `~numpy.ndarray`
Array of cosine correction factors for each pixel. Values greater than a threshold (edge) are set to 1.
"""
coordinates = all_coordinates_from_map(im_map)
on_disk = coordinate_is_on_solar_disk(coordinates)
cos_correction = np.ones_like(im_map.data)
radial_angle = np.arccos(np.cos(coordinates.Tx[on_disk]) * np.cos(coordinates.Ty[on_disk]))
cos_cor_ratio = (radial_angle / im_map.rsun_obs).value
cos_cor_ratio = np.clip(cos_cor_ratio, -1, 1)
cos_correction[on_disk] = 1 / (np.cos(np.arcsin(cos_cor_ratio)))
return cos_correction
[docs]
def cosine_correct_data(im_map: Map, cosmap=None):
"""
Perform magnetic field cosine correction.
Parameters
----------
im_map : `~sunpy.map.Map`
Processed SunPy magnetogram map.
cosmap : `numpy.ndarray`, optional
An array of the cosine correction factors for each pixel.
If not provided, computed using calculate_cosine_correction.
Returns
-------
corrected_data : `~numpy.ndarray`
The magnetic field data after applying the cosine correction (units = Gauss).
"""
if cosmap is None:
cosmap = calculate_cosine_correction(im_map)
scale = (im_map.scale[0] + im_map.scale[1]) / 2
angle_limit = (scale / im_map.rsun_obs).value
cos_limit = 1 / np.cos(angle_limit)
cosmap = np.clip(cosmap, None, cos_limit)
corrected_data = im_map.data * cosmap * u.Gauss
return corrected_data
[docs]
def smart_prep(im_map):
"""
Prepare map for use in segmentation and characterization processes.
Parameters
----------
im_map : `~sunpy.map.Map`
Unprocessed SunPy magnetogram map.
Returns
-------
thresholded_map : `~sunpy.map.Map`
Processed SunPy magnetogram map.
cos_correctiom : `~numpy.ndarray`
Array of cosine correction factors for each pixel.
"""
thresholded_map = map_threshold(im_map)
smooth_map, *_ = smooth_los_threshold(thresholded_map)
cos_correction = calculate_cosine_correction(smooth_map)
return thresholded_map, cos_correction