Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into sweep_scan
Browse files Browse the repository at this point in the history
  • Loading branch information
rettigl committed Feb 27, 2024
2 parents d455642 + 2699335 commit b2eb8ac
Show file tree
Hide file tree
Showing 7 changed files with 545 additions and 72 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ python = ">=3.8, <3.12"
h5py = ">=3.6.0"
imutils = ">=0.5.4"
ipympl = ">=0.9.1"
ipywidgets = ">=7.7.1"
ipywidgets = ">=8.1.1"
matplotlib = ">=3.5.1"
numpy = ">=1.21.6"
opencv-python = ">=4.8.1.78"
Expand Down
319 changes: 296 additions & 23 deletions specsanalyzer/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@
from typing import Union

import imutils
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 @@ -46,7 +50,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 @@ -96,13 +101,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 @@ -233,29 +235,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 @@ -266,7 +539,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

0 comments on commit b2eb8ac

Please sign in to comment.