Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specsscan crop #2

Merged
merged 21 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ license = "MIT"
python = ">=3.8, <3.12"
h5py = ">=3.6.0"
ipympl = ">=0.9.1"
ipywidgets = ">=7.7.1"
ipywidgets = ">=8.1.1"
matplotlib = ">=3.5.1"
numpy = ">=1.21.6"
pynxtools = ">=0.0.9"
Expand Down
319 changes: 296 additions & 23 deletions specsanalyzer/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@
from typing import Tuple
from typing import Union

import ipywidgets as ipw
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import xarray as xr
from IPython.display import display

from specsanalyzer import io
from specsanalyzer.config import parse_config
Expand Down Expand Up @@ -45,7 +49,8 @@ def __init__(
**kwds,
)
self._attributes = MetaHandler(meta=metadata)

self._data_array = None
self.print_msg = True
try:
self._config["calib2d_dict"] = io.parse_calib2d_to_dict(
self._config["calib2d_file"],
Expand Down Expand Up @@ -95,13 +100,10 @@ def convert_image(
) -> xr.DataArray:
"""Converts an imagin in physical unit data, angle vs energy


Args:
raw_img (np.ndarray): Raw image data, numpy 2d matrix
lens_mode (str):
analzser lens mode, check calib2d for a list
of modes Camelback naming convention e.g. "WideAngleMode"

lens_mode (str): analzser lens mode, check calib2d for a list
of modes Camelcase naming convention e.g. "WideAngleMode"
kinetic_energy (float): set analyser kinetic energy
pass_energy (float): set analyser pass energy
work_function (float): set analyser work function
Expand Down Expand Up @@ -228,29 +230,300 @@ def convert_image(
dims=["Position", "Ekin"],
)

# TODO discuss how to handle cropping. Can he store one set of cropping
# parameters in the config, or should we store one set per pass energy/
# lens mode/ kinetic energy in the dict?

# Handle cropping based on parameters stored in correction dictionary
crop = kwds.pop("crop", self._config.get("crop", False))
if crop:
try:
ek_min = kwds.pop("ek_min", self._config["ek_min"])
ek_max = kwds.pop("ek_max", self._config["ek_max"])
ang_min = kwds.pop("ang_min", self._config["ang_min"])
ang_max = kwds.pop("ang_max", self._config["ang_max"])
data_array = crop_xarray(
data_array,
ang_min,
ang_max,
ek_min,
ek_max,
)
range_dict: dict = self._correction_matrix_dict[lens_mode][kinetic_energy][
pass_energy
][work_function]["crop_params"]
ang_min = range_dict["ang_min"]
ang_max = range_dict["ang_max"]
ek_min = range_dict["ek_min"]
ek_max = range_dict["ek_max"]
if self.print_msg:
print("Using saved crop parameters...")
data_array = crop_xarray(data_array, ang_min, ang_max, ek_min, ek_max)
except KeyError:
pass
try:
ang_range_min = (
kwds["ang_range_min"]
if "ang_range_min" in kwds
else self._config["ang_range_min"]
)
ang_range_max = (
kwds["ang_range_max"]
if "ang_range_max" in kwds
else self._config["ang_range_max"]
)
ek_range_min = (
kwds["ek_range_min"]
if "ek_range_min" in kwds
else self._config["ek_range_min"]
)
ek_range_max = (
kwds["ek_range_max"]
if "ek_range_max" in kwds
else self._config["ek_range_max"]
)
ang_min = (
ang_range_min
* (
data_array.coords[data_array.dims[0]][-1]
- data_array.coords[data_array.dims[0]][0]
)
+ data_array.coords[data_array.dims[0]][0]
)
ang_max = (
ang_range_max
* (
data_array.coords[data_array.dims[0]][-1]
- data_array.coords[data_array.dims[0]][0]
)
+ data_array.coords[data_array.dims[0]][0]
)
ek_min = (
ek_range_min
* (
data_array.coords[data_array.dims[1]][-1]
- data_array.coords[data_array.dims[1]][0]
)
+ data_array.coords[data_array.dims[1]][0]
)
ek_max = (
ek_range_max
* (
data_array.coords[data_array.dims[1]][-1]
- data_array.coords[data_array.dims[1]][0]
)
+ data_array.coords[data_array.dims[1]][0]
)
if self.print_msg:
print("Cropping parameters not found, using cropping ranges from config...")
data_array = crop_xarray(data_array, ang_min, ang_max, ek_min, ek_max)
except KeyError:
if self.print_msg:
print(
"Warning: Cropping parameters not found, "
"use method crop_tool() after loading.",
)

return data_array

def crop_tool(
self,
raw_img: np.ndarray,
lens_mode: str,
kinetic_energy: float,
pass_energy: float,
work_function: float,
apply: bool = False,
**kwds,
):
"""Crop tool for selecting cropping parameters
Args:
raw_img (np.ndarray): Raw image data, numpy 2d matrix
lens_mode (str): analzser lens mode, check calib2d for a list
of modes Camelcase naming convention e.g. "WideAngleMode"
kinetic_energy (float): set analyser kinetic energy
pass_energy (float): set analyser pass energy
work_function (float): set analyser work function
apply (bool, optional): Option to directly apply the pre-selected cropping parameters.
Defaults to False.
**kwds: Keyword parameters for the crop tool:

-ek_range_min
-ek_range_max
-ang_range_min
-ang_range_max
"""

data_array = self.convert_image(
raw_img=raw_img,
lens_mode=lens_mode,
kinetic_energy=kinetic_energy,
pass_energy=pass_energy,
work_function=work_function,
crop=False,
)

matplotlib.use("module://ipympl.backend_nbagg")
fig = plt.figure()
ax = fig.add_subplot(111)
try:
mesh_obj = data_array.plot(ax=ax)
except AttributeError:
print("Load the scan first!")
raise

lineh1 = ax.axhline(y=data_array.Angle[0])
lineh2 = ax.axhline(y=data_array.Angle[-1])
linev1 = ax.axvline(x=data_array.Ekin[0])
linev2 = ax.axvline(x=data_array.Ekin[-1])

try:
range_dict = self._correction_matrix_dict[lens_mode][kinetic_energy][pass_energy][
work_function
]["crop_params"]

ek_min = range_dict["ek_min"]
ek_max = range_dict["ek_max"]
ang_min = range_dict["ang_min"]
ang_max = range_dict["ang_max"]
except KeyError:
try:
ang_range_min = (
kwds["ang_range_min"]
if "ang_range_min" in kwds
else self._config["ang_range_min"]
)
ang_range_max = (
kwds["ang_range_max"]
if "ang_range_max" in kwds
else self._config["ang_range_max"]
)
ek_range_min = (
kwds["ek_range_min"] if "ek_range_min" in kwds else self._config["ek_range_min"]
)
ek_range_max = (
kwds["ek_range_max"] if "ek_range_max" in kwds else self._config["ek_range_max"]
)
ang_min = (
ang_range_min
* (
data_array.coords[data_array.dims[0]][-1]
- data_array.coords[data_array.dims[0]][0]
)
+ data_array.coords[data_array.dims[0]][0]
)
ang_max = (
ang_range_max
* (
data_array.coords[data_array.dims[0]][-1]
- data_array.coords[data_array.dims[0]][0]
)
+ data_array.coords[data_array.dims[0]][0]
)
ek_min = (
ek_range_min
* (
data_array.coords[data_array.dims[1]][-1]
- data_array.coords[data_array.dims[1]][0]
)
+ data_array.coords[data_array.dims[1]][0]
)
ek_max = (
ek_range_max
* (
data_array.coords[data_array.dims[1]][-1]
- data_array.coords[data_array.dims[1]][0]
)
+ data_array.coords[data_array.dims[1]][0]
)
except KeyError:
ek_min = data_array.coords[data_array.dims[1]][0]
ek_max = data_array.coords[data_array.dims[1]][-1]
ang_min = data_array.coords[data_array.dims[0]][0]
ang_max = data_array.coords[data_array.dims[0]][-1]

vline_range = [ek_min, ek_max]
hline_range = [ang_min, ang_max]

vline_slider = ipw.FloatRangeSlider(
description="Ekin",
value=vline_range,
min=data_array.Ekin[0],
max=data_array.Ekin[-1],
step=0.1,
)
hline_slider = ipw.FloatRangeSlider(
description="Angle",
value=hline_range,
min=data_array.Angle[0],
max=data_array.Angle[-1],
step=0.1,
)
clim_slider = ipw.FloatRangeSlider(
description="colorbar limits",
value=[data_array.data.min(), data_array.data.max()],
min=data_array.data.min(),
max=data_array.data.max(),
)

def update(hline, vline, v_vals):
lineh1.set_ydata(hline[0])
lineh2.set_ydata(hline[1])
linev1.set_xdata(vline[0])
linev2.set_xdata(vline[1])
mesh_obj.set_clim(vmin=v_vals[0], vmax=v_vals[1])
fig.canvas.draw_idle()

ipw.interact(
update,
hline=hline_slider,
vline=vline_slider,
v_vals=clim_slider,
)

def cropit(val): # pylint: disable=unused-argument
ang_min = min(hline_slider.value)
ang_max = max(hline_slider.value)
ek_min = min(vline_slider.value)
ek_max = max(vline_slider.value)
self._data_array = crop_xarray(data_array, ang_min, ang_max, ek_min, ek_max)
self._correction_matrix_dict[lens_mode][kinetic_energy][pass_energy][work_function] = {
"crop_params": {
"ek_min": ek_min,
"ek_max": ek_max,
"ang_min": ang_min,
"ang_max": ang_max,
},
}
self._config["ek_range_min"] = (
(ek_min - data_array.coords[data_array.dims[1]][0])
/ (
data_array.coords[data_array.dims[1]][-1]
- data_array.coords[data_array.dims[1]][0]
)
).item()
self._config["ek_range_max"] = (
(ek_max - data_array.coords[data_array.dims[1]][0])
/ (
data_array.coords[data_array.dims[1]][-1]
- data_array.coords[data_array.dims[1]][0]
)
).item()
self._config["ang_range_min"] = (
(ang_min - data_array.coords[data_array.dims[0]][0])
/ (
data_array.coords[data_array.dims[0]][-1]
- data_array.coords[data_array.dims[0]][0]
)
).item()
self._config["ang_range_max"] = (
(ang_max - data_array.coords[data_array.dims[0]][0])
/ (
data_array.coords[data_array.dims[0]][-1]
- data_array.coords[data_array.dims[0]][0]
)
).item()

ax.cla()
self._data_array.plot(ax=ax, add_colorbar=False)
fig.canvas.draw_idle()

vline_slider.close()
hline_slider.close()
clim_slider.close()
apply_button.close()

apply_button = ipw.Button(description="Crop")
display(apply_button)
apply_button.on_click(cropit)
plt.show()
if apply:
cropit("")


def mergedicts(
dict1: dict,
Expand All @@ -261,7 +534,7 @@ def mergedicts(

Args:
dict1 (dict): dictionary 1
dict2 (dict): dictiontary 2
dict2 (dict): dictionary 2

Yields:
dict: merged dictionary generator
Expand Down
1 change: 1 addition & 0 deletions specsscan/config/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ units:
azimuth: "degree"
polar: "degree"
voltage: "V"
temperature: "K"
Loading
Loading