Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,12 @@ def test_analysis_recompute_after_roi_change():
with datalab_test_app_context(console=False) as win:
panel = win.imagepanel

# Create a 2D Gaussian image in a 200x200 image
# Create a 2D Gaussian image in a 200x200 image (pixel coordinates, as
# the ROIs and expected centroids below)
size = 200
param = Gauss2DParam.create(height=size, width=size, sigma=20)
param = Gauss2DParam.create(
height=size, width=size, sigma=20, physical_coords=False
)
img = create_image_from_param(param)
panel.add_object(img)

Expand Down Expand Up @@ -202,10 +205,12 @@ def test_analysis_recompute_after_recompute_1_to_1():
size = 200
# In Gauss2DParam, x0 and y0 are the center coordinates with Xmin=-10.0,
# Ymin=-10.0, Xmax=10.0, Ymax=10.0 by default.
# The centroid position should be at (49.75, 99.5).
# The centroid position should be at (49.75, 99.5) in pixel coordinates.
# After a 45° rotation, it should move closer to (64.32, 134.68).
# Or after a 90° rotation, it should move to (99.5, 149.2).
param = Gauss2DParam.create(height=size, width=size, x0=-5.0)
param = Gauss2DParam.create(
height=size, width=size, x0=-5.0, physical_coords=False
)
img = create_image_from_param(param)
panel.add_object(img)

Expand Down
43 changes: 43 additions & 0 deletions datalab/tests/features/common/creation_metadata_unit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@
PEAK_PARAMETERIZATION,
GaussParam,
SineParam,
SinusoidalGrating2DParam,
create_image,
create_signal,
)
from sigima.tools.signal.pulse import GaussianModel, LegacyPeakParameterizationError

from datalab.gui.creation import create_image_from_param
from datalab.gui.newobject import (
CREATION_PARAMETERS_FORMAT_VERSION,
CREATION_PARAMETERS_OPTION,
Expand All @@ -26,6 +29,7 @@
extract_creation_parameters,
insert_creation_parameters,
)
from datalab.history.core import decode_kwargs


def _legacy_gaussian_json(
Expand Down Expand Up @@ -117,3 +121,42 @@ def test_creation_parameter_formats_never_fall_back_implicitly() -> None:
obj.set_metadata_option(LEGACY_CREATION_PARAMETERS_OPTION, _legacy_gaussian_json())
with pytest.raises(ValueError, match="Conflicting creation parameter formats"):
extract_creation_parameters(obj)


def test_domain_image_creation_parameters_keep_their_coordinates() -> None:
"""Images created before the "Physical coordinates" option stay in pixels.

Recreating an image from creation parameters stored by DataLab 1.3 (without
the option) must give back the original pixel coordinates, while parameters
stored by this version are restored as they were.
"""
param = SinusoidalGrating2DParam.create(height=16, width=32)
image = create_image_from_param(param)
assert (image.x0, image.dx) == (param.xmin, (param.xmax - param.xmin) / 31)
restored = extract_creation_parameters(image)
assert isinstance(restored, SinusoidalGrating2DParam)
assert restored.physical_coords is True

payload = json.loads(dataset_to_json(param))
del payload["physical_coords"]
legacy = create_image("Legacy grating", np.zeros((16, 32)))
legacy.set_metadata_option(
CREATION_PARAMETERS_OPTION,
{
"format_version": CREATION_PARAMETERS_FORMAT_VERSION,
"dataset_json": json.dumps(payload),
},
)
legacy_param = extract_creation_parameters(legacy)
assert isinstance(legacy_param, SinusoidalGrating2DParam)
assert legacy_param.physical_coords is False
recreated = create_image_from_param(legacy_param)
assert (recreated.x0, recreated.y0, recreated.dx, recreated.dy) == (
0.0,
0.0,
1.0,
1.0,
)

history_kwargs = decode_kwargs({"param": {"__dataset_json__": json.dumps(payload)}})
assert history_kwargs["param"].physical_coords is False
2 changes: 2 additions & 0 deletions datalab/tests/features/image/geometry_transform_unit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@

def __create_test_image() -> sio.ImageObj:
"""Create a test image with geometry results for testing."""
# Geometry results and ROI below are defined in pixel coordinates
param = sio.Gauss2DParam.create(
height=600,
width=600,
x0=2.5,
y0=7.5,
title="Test image (with geometry results)",
dtype=sio.ImageDatatypes.UINT16,
physical_coords=False,
)
obj = sio.create_image_from_param(param)
for geometry in test_data.generate_geometry_results():
Expand Down
16 changes: 16 additions & 0 deletions doc/features/image/menu_create.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,19 @@ Create a new image from various models
:width: 30px
- 2D sinc
- :math:`z = A \cdot \mathrm{sinc}\left(\dfrac{\sqrt{(x-x_0)^2+(y-y_0)^2}}{\sigma}\right) + C`

The 2D Gaussian, 2D ramp, sinusoidal grating, ring pattern, Siemens star and
2D sinc models are computed over a domain
:math:`[x_{min}, x_{max}] \times [y_{min}, y_{max}]`. When the
**Physical coordinates** option is enabled (default), this domain defines the
image coordinates: the axes show :math:`x` and :math:`y` as used in the
equation, so that centers, widths and spatial frequencies can be read directly
on the image (e.g. a sinusoidal grating of frequency :math:`f_x = 0.1` shows
one period every 10 units along the X axis). When the option is disabled, the
image is displayed in pixel coordinates.

.. note::

Images created with DataLab 1.3 or earlier were displayed in pixel
coordinates: they keep these coordinates when their creation parameters
are edited or recomputed.
5 changes: 5 additions & 0 deletions doc/features/signal/menu_create.rst
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,8 @@ Create a new signal from various models:

For Gaussian, Lorentzian and Voigt models, :math:`A` is the signed peak height
above the baseline :math:`y_0`, expressed in the signal's Y unit.

The blackbody signal is the spectral radiance per unit wavelength of a blackbody
at temperature :math:`T`: wavelengths :math:`\lambda` are expressed in meters and
the spectral radiance in W·sr⁻¹·m⁻³. These axis titles and units are set by
default, and may be changed in the creation dialog.
54 changes: 43 additions & 11 deletions doc/features/signal/menu_processing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -374,44 +374,64 @@ Fitting
^^^^^^^

Fit a model to each selected signal.
This can be done automatically or through an interactive curve fitting dialog.
This can be done automatically or through an interactive curve fitting dialog,
which shows the formula of the model below the fit parameters.

The fitted signal stores the model parameters in its metadata
(``metadata["fit_params"]``), under the names given in the last column of the
table below:

.. list-table::
:header-rows: 1
:widths: 20, 80
:widths: 15, 55, 30

* - Model
- Equation
- Stored parameters
* - Linear
- :math:`y = c_{0} + c_{1} \cdot x`
- :math:`y = a \cdot x + b`
- ``a``, ``b``
* - Polynomial
- :math:`y = c_{0} + c_{1} \cdot x + c_{2} \cdot x^2 + ... + c_{n} \cdot x^n`
- :math:`y = a \cdot x^n + b \cdot x^{n-1} + c \cdot x^{n-2} + \ldots`
- ``a``, ``b``, ``c``, ...
* - Gaussian
- :math:`y = y_{0} + A \exp\left(-\dfrac{1}{2} \left(\dfrac{x - x_{0}}{\sigma}\right)^2\right)`
- :math:`A`: ``amplitude``, :math:`\sigma`: ``sigma``, :math:`x_0`: ``x0``, :math:`y_0`: ``y0``
* - Lorentzian
- :math:`y = y_{0} + \dfrac{A}{1+\left(\dfrac{x-x_{0}}{\sigma}\right)^2}`
- :math:`A`: ``amplitude``, :math:`\sigma`: ``sigma``, :math:`x_0`: ``x0``, :math:`y_0`: ``y0``
* - Voigt
- :math:`y = y_{0} + A \cdot \dfrac{\Re\left(w(z)\right)}{\Re\left(w(j/\sqrt{2})\right)}` with :math:`w(z) = \exp(-z^2) \operatorname{erfc}(-jz)` and :math:`z = \dfrac{x - x_{0} + j \sigma}{\sqrt{2} \sigma}`
- :math:`A`: ``amplitude``, :math:`\sigma`: ``sigma``, :math:`x_0`: ``x0``, :math:`y_0`: ``y0``
* - Multi-Gaussian
- :math:`y = y_{0} + \sum_{i=1}^{N} A_{i} \exp\left(-\dfrac{1}{2} \left(\dfrac{x - x_{0,i}}{\sigma_{i}}\right)^2\right)`
- :math:`A_i`: ``amplitude_i``, :math:`\sigma_i`: ``sigma_i``, :math:`x_{0,i}`: ``x0_i``, :math:`y_0`: ``y0``
* - Multi-Lorentzian
- :math:`y = y_{0} + \sum_{i=1}^{N} \dfrac{A_{i}}{1 + \left(\dfrac{x - x_{0,i}}{\sigma_{i}}\right)^2}`
- :math:`A_i`: ``amplitude_i``, :math:`\sigma_i`: ``sigma_i``, :math:`x_{0,i}`: ``x0_i``, :math:`y_0`: ``y0``
* - Planck
- :math:`y = y_{0} + A \cdot \left(\dfrac{x}{x_{0}}\right)^{-5} \cdot \left(\exp\left(\dfrac{5 x_{0}}{\sigma \cdot x}\right)-1\right)^{-1}` where :math:`x_{0}` and :math:`\sigma` are scale and width factors (see the note below)
- :math:`y = y_{0} + A \cdot \left(\dfrac{x}{x_{0}}\right)^{-5} \cdot \left(\exp\left(\dfrac{5 x_{0}}{\sigma \cdot x}\right)-1\right)^{-1}` where :math:`x_{0}` and :math:`\sigma` are scale and temperature factors (see the note below)
- :math:`A`: ``amp``, :math:`x_0`: ``x0``, :math:`\sigma`: ``sigma``, :math:`y_0`: ``y0``
* - Two half Gaussians
- | :math:`y = y_{L} + A_{L} \cdot \exp\left(-\dfrac{1}{2}\left(\dfrac{x - x_{0}}{\sigma_{L}}\right)^2\right)` if :math:`x < x_{0}`
| :math:`y = y_{R} + A_{R} \cdot \exp\left(-\dfrac{1}{2}\left(\dfrac{x - x_{0}}{\sigma_{R}}\right)^2\right)` otherwise
- | :math:`y = y_{0,L} + A_{L} \cdot \exp\left(-\dfrac{1}{2}\left(\dfrac{x - x_{0}}{\sigma_{L}}\right)^2\right)` if :math:`x < x_{0}`
| :math:`y = y_{0,R} + A_{R} \cdot \exp\left(-\dfrac{1}{2}\left(\dfrac{x - x_{0}}{\sigma_{R}}\right)^2\right)` otherwise
- :math:`A_L`: ``amp_left``, :math:`A_R`: ``amp_right``, :math:`\sigma_L`: ``sigma_left``, :math:`\sigma_R`: ``sigma_right``, :math:`x_0`: ``x0``, :math:`y_{0,L}`: ``y0_left``, :math:`y_{0,R}`: ``y0_right``
* - Two back-to-back exponentials
- | :math:`y = y_{0} + A_{L} \cdot \exp\left(B_{L} \cdot x\right)` if :math:`x < x_{c}`
| :math:`y = y_{0} + A_{R} \cdot \exp\left(B_{R} \cdot x\right)` otherwise
- | :math:`y = y_{0} + a_{L} \cdot \exp\left(b_{L} \cdot x\right)` if :math:`x < x_{c}`
| :math:`y = y_{0} + a_{R} \cdot \exp\left(b_{R} \cdot x\right)` otherwise
- :math:`x_c`: ``x_center``, :math:`a_L`: ``a_left``, :math:`b_L`: ``b_left``, :math:`a_R`: ``a_right``, :math:`b_R`: ``b_right``, :math:`y_0`: ``y0``
* - Exponential
- :math:`y = y_{0} + A \exp\left(B \cdot x\right)`
- :math:`y = y_{0} + a \exp\left(b \cdot x\right)`
- ``a``, ``b``, :math:`y_0`: ``y0``
* - Sinusoidal
- :math:`y = y_{0} + A \sin\left(2 \pi f \cdot x + \phi\right)`
- :math:`A`: ``amplitude``, :math:`f`: ``frequency``, :math:`\phi`: ``phase`` (in radians), :math:`y_0`: ``offset``
* - Cumulative Distribution Function (CDF)
- :math:`y = y_{0} + A \erf\left(\dfrac{x - x_{0}}{\sqrt{2} \sigma}\right)`
- :math:`y = y_{0} + A \erf\left(\dfrac{x - \mu}{\sqrt{2} \sigma}\right)`
- :math:`A`: ``amplitude``, :math:`\mu`: ``mu``, :math:`\sigma`: ``sigma``, :math:`y_0`: ``baseline``
* - Sigmoid
- :math:`y = y_{0} + \dfrac{A}{1 + \exp\left(-k \left(x - x_{0}\right)\right)}`
- :math:`A`: ``amplitude``, ``k``, :math:`x_0`: ``x0``, :math:`y_0`: ``offset``

For Gaussian, Lorentzian and Voigt models, :math:`A` is the signed peak height
above the baseline :math:`y_0`, expressed in the signal's Y unit.
Expand All @@ -426,6 +446,18 @@ above the baseline :math:`y_0`, expressed in the signal's Y unit.
particular the emission peak is located at approximately
:math:`1.007 \, x_{0}/\sigma`, not at :math:`x_{0}`.

The physical quantities are derived from these combinations. With
wavelengths in meters, the model matches Planck's law (spectral radiance
per unit wavelength) for:

* a temperature :math:`T = \dfrac{h c \, \sigma}{5 k_B \, x_{0}} \approx 2.878 \times 10^{-3} \, \dfrac{\sigma}{x_{0}}` K,
* an emissivity (or any other scale factor of the measurement)
:math:`\varepsilon = \dfrac{A \, x_{0}^5}{2 h c^2}`, when the signal is a
spectral radiance in W·sr⁻¹·m⁻³.

For example, fitting the blackbody signal created at 293 K gives back
:math:`T = 293` K and :math:`\varepsilon = 1`.

Multi-peak fit parameters
~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
7 changes: 7 additions & 0 deletions doc/locale/fr/LC_MESSAGES/features/image/menu_create.po
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,10 @@ msgstr "Sinc 2D"

msgid ":math:`z = A \\cdot \\mathrm{sinc}\\left(\\dfrac{\\sqrt{(x-x_0)^2+(y-y_0)^2}}{\\sigma}\\right) + C`"
msgstr ":math:`z = A \\cdot \\mathrm{sinc}\\left(\\dfrac{\\sqrt{(x-x_0)^2+(y-y_0)^2}}{\\sigma}\\right) + C`"

#, python-brace-format
msgid "The 2D Gaussian, 2D ramp, sinusoidal grating, ring pattern, Siemens star and 2D sinc models are computed over a domain :math:`[x_{min}, x_{max}] \\times [y_{min}, y_{max}]`. When the **Physical coordinates** option is enabled (default), this domain defines the image coordinates: the axes show :math:`x` and :math:`y` as used in the equation, so that centers, widths and spatial frequencies can be read directly on the image (e.g. a sinusoidal grating of frequency :math:`f_x = 0.1` shows one period every 10 units along the X axis). When the option is disabled, the image is displayed in pixel coordinates."
msgstr "Les modèles gaussienne 2D, rampe 2D, réseau sinusoïdal, anneaux, mire de Siemens et sinus cardinal 2D sont calculés sur un domaine :math:`[x_{min}, x_{max}] \\times [y_{min}, y_{max}]`. Lorsque l'option **Coordonnées physiques** est activée (par défaut), ce domaine définit les coordonnées de l'image : les axes représentent :math:`x` et :math:`y` tels qu'utilisés dans l'équation, de sorte que les centres, largeurs et fréquences spatiales se lisent directement sur l'image (par exemple, un réseau sinusoïdal de fréquence :math:`f_x = 0.1` présente une période toutes les 10 unités le long de l'axe X). Lorsque l'option est désactivée, l'image est affichée en coordonnées pixel."

msgid "Images created with DataLab 1.3 or earlier were displayed in pixel coordinates: they keep these coordinates when their creation parameters are edited or recomputed."
msgstr "Les images créées avec DataLab 1.3 ou une version antérieure étaient affichées en coordonnées pixel : elles conservent ces coordonnées lorsque leurs paramètres de création sont modifiés ou recalculés."
13 changes: 13 additions & 0 deletions doc/locale/fr/LC_MESSAGES/features/signal/menu_create.po
Original file line number Diff line number Diff line change
Expand Up @@ -311,3 +311,16 @@ msgstr ""
"Pour les modèles gaussien, lorentzien et de Voigt, :math:`A` est la hauteur "
"signée du pic au-dessus de la ligne de base :math:`y_0`, exprimée dans "
"l'unité Y du signal."

msgid ""
"The blackbody signal is the spectral radiance per unit wavelength of a "
"blackbody at temperature :math:`T`: wavelengths :math:`\\lambda` are "
"expressed in meters and the spectral radiance in W·sr⁻¹·m⁻³. These axis "
"titles and units are set by default, and may be changed in the creation "
"dialog."
msgstr ""
"Le signal de corps noir est la luminance énergétique spectrale (par unité de "
"longueur d'onde) d'un corps noir à la température :math:`T` : les longueurs "
"d'onde :math:`\\lambda` sont exprimées en mètres et la luminance énergétique "
"spectrale en W·sr⁻¹·m⁻³. Ces titres et unités d'axes sont définis par défaut "
"et peuvent être modifiés dans la boîte de dialogue de création."
Loading
Loading