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
6 changes: 6 additions & 0 deletions doc/locale/fr/LC_MESSAGES/api/index.po
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ msgstr ":mod:`sigima.objects`"
msgid "Object model for signals and images (:class:`sigima.objects.SignalObj` and :class:`sigima.objects.ImageObj`), scalar results (:class:`sigima.objects.GeometryResult` and :class:`sigima.objects.TableResult`), and related functions"
msgstr "Modèle d'objet pour les signaux et les images (:class:`sigima.objects.SignalObj` et :class:`sigima.objects.ImageObj`), résultats scalaires (:class:`sigima.objects.GeometryResult` et :class:`sigima.objects.TableResult`), et fonctions associées"

msgid ":mod:`sigima.objects.annotations`"
msgstr ":mod:`sigima.objects.annotations`"

msgid "Renderer-independent graphical annotation model, serialization, and transformations (see :doc:`annotations`)"
msgstr "Modèle d'annotation graphique indépendant du moteur de rendu, sérialisation et transformations (voir :doc:`annotations`)"

msgid ":mod:`sigima.proc`"
msgstr ":mod:`sigima.proc`"

Expand Down
34 changes: 34 additions & 0 deletions doc/locale/fr/LC_MESSAGES/release_notes/release_1.03.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) 2026, DataLab Platform Developers
# This file is distributed under the same license as the Sigima package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2026.
#
#, fuzzy
msgid ""
msgstr ""
"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"

msgid "Version 1.3"
msgstr "Version 1.3"

msgid "Sigima Version 1.3.0"
msgstr "Sigima Version 1.3.0"

msgid "✨ New features since version 1.2.0"
msgstr "✨ Nouvelles fonctionnalités depuis la version 1.2.0"

msgid "**Brightness and contrast adjustment**: A new ROI-aware image processing performs numerically stable clipped linear remapping from persistent minimum and maximum bounds, preserves the source dtype and non-finite values, and exposes portable histogram context for native and browser interfaces. Tiny floating-point ranges and finite IEEE-754 extremes are supported without coupling headless computation to histogram rendering. The low-level helpers support floating-point arrays and integers up to 32 bits; they explicitly reject `int64` and `uint64` instead of risking precision loss at integer endpoints."
msgstr "**Ajustement de la luminosité et du contraste** : Un nouveau traitement d'image conscient de la ROI effectue un remappage linéaire tronqué numériquement stable à partir de limites minimales et maximales persistantes, préserve le type de données source et les valeurs non finies, et expose un contexte d'histogramme portable pour les interfaces natives et navigateur. Les plages de flottants minuscules et les extrêmes finis IEEE-754 sont pris en charge sans coupler le calcul sans tête au rendu de l'histogramme. Les helpers bas niveau prennent en charge les tableaux flottants et les entiers jusqu'à 32 bits ; ils rejettent explicitement `int64` et `uint64` au lieu de risquer une perte de précision aux extrémités des entiers."

msgid "**Portable graphical annotations**: Signals and images may now carry versioned, renderer-independent points, shapes, text, cursors and axis ranges. Annotations survive Sigima file round trips and supported image transformations, and are displayed consistently by the PlotPy and Matplotlib visualization backends. Existing PlotPy annotations remain readable and can be migrated explicitly while unknown application data is preserved. This implements [Issue #53](https://github.com/DataLab-Platform/Sigima/issues/53)."
msgstr "**Annotations graphiques portables** : Les signaux et les images peuvent désormais transporter des points, des formes, du texte, des curseurs et des plages d'axes versionnés et indépendants du moteur de rendu. Les annotations survivent aux allers-retours des fichiers Sigima et aux transformations d'images prises en charge, et sont affichées de manière cohérente par les backends de visualisation PlotPy et Matplotlib. Les annotations PlotPy existantes restent lisibles et peuvent être migrées explicitement tandis que les données d'application inconnues sont préservées. Cela implémente [Issue #53](https://github.com/DataLab-Platform/Sigima/issues/53)."

msgid "**Interactive Plotly visualization**: Signals and images can now be inspected in a browser with zoom, pan and hover through the optional Plotly backend. Sigima also exposes dependency-free Plotly JSON builders for applications and notebooks, including portable annotation, ROI and geometry-result overlays."
msgstr "**Visualisation interactive Plotly** : Les signaux et les images peuvent désormais être inspectés dans un navigateur avec zoom, panoramique et survol grâce au backend Plotly optionnel. Sigima expose également des constructeurs JSON Plotly sans dépendance pour les applications et les notebooks, y compris les superpositions d'annotations portables, de ROI et de résultats géométriques."

msgid "**Parameter validation**: Numeric creation and processing parameters now expose precise unconditional bounds, while mode-dependent and source-dependent constraints are checked before computation. Signed coordinate and grid conventions, as well as point-like or descending signal domains, remain supported."
msgstr "**Validation des paramètres** : Les paramètres de création et de traitement numériques exposent désormais des limites inconditionnelles précises, tandis que les contraintes dépendantes du mode et de la source sont vérifiées avant le calcul. Les conventions de coordonnées et de grille signées, ainsi que les domaines de signaux ponctuels ou décroissants, restent prises en charge."
6 changes: 6 additions & 0 deletions doc/locale/fr/LC_MESSAGES/user_guide/features.po
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,12 @@ msgstr "Seuillage minimal"
msgid "Exposure and Intensity Correction"
msgstr "Correction d'exposition et d'intensité"

msgid ":func:`adjust_brightness_contrast <sigima.proc.image.adjust_brightness_contrast>`"
msgstr ""

msgid "Histogram-driven brightness and contrast adjustment"
msgstr "Réglage de la luminosité et du contraste piloté par histogramme"

msgid ":func:`histogram <sigima.proc.image.histogram>`"
msgstr ""

Expand Down
4 changes: 3 additions & 1 deletion doc/release_notes/release_1.03.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@

### ✨ New features since version 1.2.0 ###

* **Brightness and contrast adjustment**: A new ROI-aware image processing performs numerically stable clipped linear remapping from persistent minimum and maximum bounds, preserves the source dtype and non-finite values, and exposes portable histogram context for native and browser interfaces. Tiny floating-point ranges and finite IEEE-754 extremes are supported without coupling headless computation to histogram rendering. The low-level helpers support floating-point arrays and integers up to 32 bits; they explicitly reject `int64` and `uint64` instead of risking precision loss at integer endpoints.
* **Portable graphical annotations**: Signals and images may now carry versioned, renderer-independent points, shapes, text, cursors and axis ranges. Annotations survive Sigima file round trips and supported image transformations, and are displayed consistently by the PlotPy and Matplotlib visualization backends. Existing PlotPy annotations remain readable and can be migrated explicitly while unknown application data is preserved. This implements [Issue #53](https://github.com/DataLab-Platform/Sigima/issues/53).
* **Interactive Plotly visualization**: Signals and images can now be inspected in a browser with zoom, pan and hover through the optional Plotly backend. Sigima also exposes dependency-free Plotly JSON builders for applications and notebooks, including portable annotation, ROI and geometry-result overlays.
* **Interactive Plotly visualization**: Signals and images can now be inspected in a browser with zoom, pan and hover through the optional Plotly backend. Sigima also exposes dependency-free Plotly JSON builders for applications and notebooks, including portable annotation, ROI and geometry-result overlays.
* **Parameter validation**: Numeric creation and processing parameters now expose precise unconditional bounds, while mode-dependent and source-dependent constraints are checked before computation. Signed coordinate and grid conventions, as well as point-like or descending signal domains, remain supported.
2 changes: 2 additions & 0 deletions doc/user_guide/features.rst
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,8 @@ Exposure and Intensity Correction

* - Function
- Description
* - :func:`adjust_brightness_contrast <sigima.proc.image.adjust_brightness_contrast>`
- Histogram-driven brightness and contrast adjustment
* - :func:`histogram <sigima.proc.image.histogram>`
- Compute image histogram
* - :func:`equalize_hist <sigima.proc.image.equalize_hist>`
Expand Down
36 changes: 23 additions & 13 deletions sigima/locale/fr/LC_MESSAGES/sigima.po
Original file line number Diff line number Diff line change
Expand Up @@ -317,18 +317,18 @@ msgstr "Image sans titre"
msgid "Title"
msgstr "Titre"

msgid "Image height: number of rows"
msgstr "Hauteur de l'image : nombre de lignes"

msgid "Height"
msgstr "Hauteur"

msgid "Image width: number of columns"
msgstr "Largeur de l'image : nombre de colonnes"
msgid "Image height: number of rows"
msgstr "Hauteur de l'image : nombre de lignes"

msgid "Width"
msgstr "Largeur"

msgid "Image width: number of columns"
msgstr "Largeur de l'image : nombre de colonnes"

msgid "Type"
msgstr "Type"

Expand Down Expand Up @@ -374,18 +374,18 @@ msgstr "Décalage X"
msgid "Y offset"
msgstr "Décalage Y"

msgid "Value for dark squares"
msgstr "Valeur des carrés foncés"

msgid "Minimum value"
msgstr "Minimum"

msgid "Value for light squares"
msgstr "Valeur des carrés clairs"
msgid "Value for dark squares"
msgstr "Valeur des carrés foncés"

msgid "Maximum value"
msgstr "Maximum"

msgid "Value for light squares"
msgstr "Valeur des carrés clairs"

msgid "Amplitude and offset"
msgstr "Amplitude et décalage"

Expand Down Expand Up @@ -843,6 +843,9 @@ msgstr "- Infini"
msgid "Replace special values (image)"
msgstr "Remplacer les valeurs spéciales (image)"

msgid "rows"
msgstr "lignes"

msgid "columns"
msgstr "colonnes"

Expand Down Expand Up @@ -1073,6 +1076,15 @@ msgstr "Interprète les seuils bas et haut en tant que quantiles des niveaux des
msgid "Value to fill past edges of input if mode is constant."
msgstr "Valeur de remplissage si le mode est constant."

msgid "Brightness and contrast"
msgstr "Luminosité et contraste"

msgid "Minimum"
msgstr "Minimum"

msgid "Adjust the intensity window using the histogram."
msgstr "Ajuster la fenêtre d'intensité à l'aide de l'histogramme."

msgid "Gamma"
msgstr "Gamma"

Expand Down Expand Up @@ -1436,9 +1448,6 @@ msgstr "Manuel"
msgid "Mean"
msgstr "Moyenne"

msgid "Minimum"
msgstr "Minimum"

msgid "Threshold method"
msgstr "Méthode de seuillage"

Expand Down Expand Up @@ -1712,3 +1721,4 @@ msgstr "Barycentre"

msgid "Plot dialog"
msgstr "Fenêtre de tracé"

7 changes: 7 additions & 0 deletions sigima/objects/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
annotation_to_dict,
is_graphical_annotation_dict,
)
from sigima.validation import validate_dataset

if sys.version_info >= (3, 11):
# Use Self from typing module in Python 3.11+
Expand Down Expand Up @@ -123,6 +124,12 @@ class BaseRandomParam(BaseProcParam):
class UniformDistributionParam(BaseRandomParam):
"""Uniform-distribution signal/image parameters."""

def validate_parameters(self, *context: object) -> None:
"""Validate uniform-distribution bounds."""
validate_dataset(super(), *context)
if self.vmin > self.vmax:
raise ValueError("vmin must be less than or equal to vmax")

def apply_integer_range(self, vmin, vmax):
"""Do something in case of integer min-max range."""
self.vmin, self.vmax = float(vmin), float(vmax)
Expand Down
10 changes: 9 additions & 1 deletion sigima/objects/image/creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from sigima.objects import base
from sigima.objects.image.object import ImageObj
from sigima.tools.image import scale_data_to_min_max
from sigima.validation import validate_dataset


def create_image(
Expand Down Expand Up @@ -404,7 +405,7 @@ class Gauss2DParam(

a = gds.FloatItem("A", default=None, check=False)
xmin = gds.FloatItem("x<sub>min</sub>", default=-10.0).set_pos(col=1)
sigma = gds.FloatItem("σ", default=1.0)
sigma = gds.FloatItem("σ", default=1.0, min=0.0, nonzero=True)
xmax = gds.FloatItem("x<sub>max</sub>", default=10.0).set_pos(col=1)
mu = gds.FloatItem("μ", default=0.0)
ymin = gds.FloatItem("y<sub>min</sub>", default=-10.0).set_pos(col=1)
Expand Down Expand Up @@ -719,6 +720,12 @@ class SiemensStar2DParam(
ymax = gds.FloatItem("y<sub>max</sub>", default=100.0).set_pos(col=1)
_g2_end = gds.EndGroup("")

def validate_parameters(self, *context: object) -> None:
"""Validate radial limits."""
validate_dataset(super(), *context)
if self.inner_radius > self.outer_radius:
raise ValueError("inner_radius must be less than or equal to outer_radius")

def generate_title(self) -> str:
"""Generate a title based on current parameters."""
return f"Siemens(n={self.n_spokes})"
Expand Down Expand Up @@ -864,6 +871,7 @@ def create_image_from_param(param: NewImageParam) -> ImageObj:
param.width = 1024
if param.dtype is None:
param.dtype = ImageDatatypes.UINT16
validate_dataset(param)
# Generate data first, as some `generate_title()` methods may depend on it:
shape = (param.height, param.width)
data = param.generate_2d_data(shape)
Expand Down
2 changes: 2 additions & 0 deletions sigima/objects/image/roi.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import sigima.tools.image
from sigima.config import _
from sigima.objects import base
from sigima.validation import validate_dataset

if TYPE_CHECKING:
from sigima.objects.image.object import ImageObj
Expand Down Expand Up @@ -175,6 +176,7 @@ def to_single_roi(
Returns:
Single ROI
"""
validate_dataset(self, obj)
if self.geometry == "rectangle":
return RectangularROI.from_param(obj, self)
if self.geometry == "circle":
Expand Down
22 changes: 20 additions & 2 deletions sigima/objects/signal/creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
PulseFitModel,
VoigtModel,
)
from sigima.validation import validate_dataset


def create_signal(
Expand Down Expand Up @@ -277,6 +278,10 @@ def generate_title(self) -> str:
# or when the NewSignalParam class is used alone).
sep = gds.SeparatorItem()

def validate_parameters(self, *context: object) -> None:
"""Run validators provided by cooperative base classes."""
validate_dataset(super(), *context)

def generate_x_data(self) -> np.ndarray:
"""Generate x data based on current parameters."""
return np.linspace(self.xmin, self.xmax, self.size)
Expand Down Expand Up @@ -479,7 +484,7 @@ class BaseGaussLorentzVoigtParam(NewSignalParam):
).set_prop("display", hide=True)
amplitude = gds.FloatItem("A", default=1.0)
y0 = gds.FloatItem("y<sub>0</sub>", default=0.0).set_pos(col=1)
sigma = gds.FloatItem("σ", default=1.0)
sigma = gds.FloatItem("σ", default=1.0, min=0.0, nonzero=True)
mu = gds.FloatItem("μ", default=0.0).set_pos(col=1)

@classmethod
Expand Down Expand Up @@ -1064,6 +1069,12 @@ class PulseParam(NewSignalParam, title=_("Pulse")):
offset = gds.FloatItem(_("Offset"), default=10.0)
stop = gds.FloatItem(_("End"), default=5.0).set_pos(col=1)

def validate_parameters(self, *context: object) -> None:
"""Validate pulse chronology."""
super().validate_parameters(*context)
if self.start > self.stop:
raise ValueError("start must be less than or equal to stop")

def generate_title(self) -> str:
"""Generate a title based on current parameters."""
return (
Expand Down Expand Up @@ -1131,7 +1142,7 @@ class BasePulseParam(NewSignalParam):
offset = gds.FloatItem(_("Initial value"), default=0.0)
amplitude = gds.FloatItem(_("Amplitude"), default=5.0).set_pos(col=1)
noise_amplitude = gds.FloatItem(_("Noise amplitude"), default=0.2, min=0.0)
x_rise_start = gds.FloatItem(_("Rise start time"), default=3.0, min=0.0)
x_rise_start = gds.FloatItem(_("Rise start time"), default=3.0)
total_rise_time = gds.FloatItem(_("Total rise time"), default=2.0, min=0.0).set_pos(
col=1
)
Expand Down Expand Up @@ -1260,6 +1271,12 @@ def square_duration(self) -> float:
"""Calculate the square duration from FWHM and total rise/fall times."""
return self.fwhm - 0.5 * self.total_rise_time - 0.5 * self.total_fall_time

def validate_parameters(self, *context: object) -> None:
"""Validate square-pulse timing."""
super().validate_parameters(*context)
if self.square_duration < 0.0:
raise ValueError("square pulse plateau duration must be non-negative")

def get_plateau_range(self) -> tuple[float, float]:
"""Get the theoretical plateau range (start, end) for the square signal.

Expand Down Expand Up @@ -1547,6 +1564,7 @@ def create_signal_from_param(param: NewSignalParam) -> SignalObj:
Raises:
NotImplementedError: if the signal type is not supported
"""
validate_dataset(param)
# Generate data first, as some `generate_title()` methods may depend on it:
x, y = param.generate_1d_data()
# Check if user has customized the title or left it as default/empty
Expand Down
2 changes: 2 additions & 0 deletions sigima/objects/signal/roi.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

from sigima.config import _
from sigima.objects import base
from sigima.validation import validate_dataset

if TYPE_CHECKING:
from sigima.objects.signal.object import SignalObj
Expand All @@ -52,6 +53,7 @@ def to_single_roi(self, obj: SignalObj) -> SegmentROI:
Returns:
Single ROI
"""
validate_dataset(self, obj)
assert isinstance(self.xmin, float) and isinstance(self.xmax, float)
return SegmentROI([self.xmin, self.xmax], False, title=self.title)

Expand Down
2 changes: 2 additions & 0 deletions sigima/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@
"BlobDOHParam",
"BlobLOGParam",
"BlobOpenCVParam",
"BrightnessContrastParam",
"ButterworthParam",
"CannyParam",
"ClipParam",
Expand Down Expand Up @@ -411,6 +412,7 @@
BlobDOHParam,
BlobLOGParam,
BlobOpenCVParam,
BrightnessContrastParam,
ButterworthParam,
CannyParam,
ContourShapeParam,
Expand Down
10 changes: 10 additions & 0 deletions sigima/proc/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,16 @@ class NormalizeParam(gds.DataSet, title=_("Normalize")):
class HistogramParam(gds.DataSet, title=_("Histogram")):
"""Histogram parameters"""

def validate_parameters(self, *context: object) -> None:
"""Validate optional histogram limits."""
del context
if (
self.lower is not None
and self.upper is not None
and self.lower > self.upper
):
raise ValueError("lower must be less than or equal to upper")

def get_suffix(self, data: np.ndarray) -> str:
"""Return suffix for the histogram computation

Expand Down
Loading
Loading