From 8659b9a11f941dfe44c7698834f8f98127a314d2 Mon Sep 17 00:00:00 2001 From: Pierre Raybaut <1311787+PierreRaybaut@users.noreply.github.com> Date: Mon, 26 Oct 2026 07:22:47 +0100 Subject: [PATCH] fix(contracts): record the image ROI convention for ROI parameters The selection of image pixels by regions of interest (rule sigima.image.roi.pixel_centers) was recorded in the execution context only when an input image carried regions of interest. The operations taking their regions of interest as parameters (extract_roi, extract_rois, erase and offset_correction), usually called on images without attached ROIs, depend on it as well: their description recorded no context, and a replay of such a call recorded before Sigima 1.4, which selected other pixels, was accepted and reported a different result instead of being refused. image_roi_convention() now also takes the contract: an operation with an image input and a ROI2DParam parameter (single or list) depends on the rule whatever its inputs. describe_call() and prepare_execution() pass the contract, so that the rule is recorded and checked for these operations. The rule and the contract versions are unchanged: the rule is recorded in more cases. Signal ROI parameters are not concerned. Closes #99 Assisted-by: Claude Opus 5.5 --- doc/contributing/guidelines.rst | 3 +- .../fr/LC_MESSAGES/contributing/guidelines.po | 4 +- .../LC_MESSAGES/release_notes/release_1.04.po | 4 +- .../fr/LC_MESSAGES/user_guide/contracts.po | 4 +- doc/release_notes/release_1.04.md | 2 +- doc/user_guide/contracts.rst | 3 +- sigima/proc/contracts/execution.py | 59 ++++++++++----- sigima/tests/common/execution_unit_test.py | 72 +++++++++++++++++++ 8 files changed, 125 insertions(+), 26 deletions(-) diff --git a/doc/contributing/guidelines.rst b/doc/contributing/guidelines.rst index 49dcc74..5fef674 100644 --- a/doc/contributing/guidelines.rst +++ b/doc/contributing/guidelines.rst @@ -127,7 +127,8 @@ Follow these rules when you add or change a computation function: - Rules that apply to every operation are recorded in the execution context rather than in each contract: the selection of image pixels by regions of interest (:data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`, recorded when an - input image carries regions of interest) and the X-alignment policies (see + input image carries regions of interest or when the operation takes image + regions of interest as parameters) and the X-alignment policies (see below). Increase the version of such a rule, not the contract versions, when it changes: a replay refuses the calls recorded with another version. diff --git a/doc/locale/fr/LC_MESSAGES/contributing/guidelines.po b/doc/locale/fr/LC_MESSAGES/contributing/guidelines.po index 75f6394..9aaa95d 100644 --- a/doc/locale/fr/LC_MESSAGES/contributing/guidelines.po +++ b/doc/locale/fr/LC_MESSAGES/contributing/guidelines.po @@ -112,8 +112,8 @@ msgstr "Liste de contrôle pour toute modification d'une fonction de calcul exis msgid "Historical exception: the contracts were published with Sigima 1.3.0 and kept version 1 in Sigima 1.4.0 although several results changed, because they had just been published and were not used yet. This one-off decision does not apply to later changes." msgstr "Exception historique : les contrats, publiés avec Sigima 1.3.0, ont conservé la version 1 dans Sigima 1.4.0 bien que plusieurs résultats aient changé, car ils venaient d'être publiés et n'étaient pas encore exploités. Cette décision ponctuelle ne s'applique pas aux modifications ultérieures." -msgid "Rules that apply to every operation are recorded in the execution context rather than in each contract: the selection of image pixels by regions of interest (:data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`, recorded when an input image carries regions of interest) and the X-alignment policies (see below). Increase the version of such a rule, not the contract versions, when it changes: a replay refuses the calls recorded with another version." -msgstr "Les règles qui s'appliquent à toutes les opérations sont enregistrées dans le contexte d'exécution plutôt que dans chaque contrat : la sélection des pixels d'une image par les régions d'intérêt (:data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`, enregistrée lorsqu'une image d'entrée porte des régions d'intérêt) et les politiques d'alignement en X (voir ci-dessous). Augmentez la version d'une telle règle, et non les versions des contrats, lorsqu'elle change : un rejeu refuse les appels enregistrés avec une autre version." +msgid "Rules that apply to every operation are recorded in the execution context rather than in each contract: the selection of image pixels by regions of interest (:data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`, recorded when an input image carries regions of interest or when the operation takes image regions of interest as parameters) and the X-alignment policies (see below). Increase the version of such a rule, not the contract versions, when it changes: a replay refuses the calls recorded with another version." +msgstr "Les règles qui s'appliquent à toutes les opérations sont enregistrées dans le contexte d'exécution plutôt que dans chaque contrat : la sélection des pixels d'une image par les régions d'intérêt (:data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`, enregistrée lorsqu'une image d'entrée porte des régions d'intérêt ou lorsque l'opération prend des régions d'intérêt d'image en paramètres) et les politiques d'alignement en X (voir ci-dessous). Augmentez la version d'une telle règle, et non les versions des contrats, lorsqu'elle change : un rejeu refuse les appels enregistrés avec une autre version." msgid "The structure of the contract (input roles, parameter arguments, outputs) is inferred from the annotations. Annotate every argument and the return value with names imported at runtime (not only under ``TYPE_CHECKING``). When roles cannot be inferred (more than two input objects), declare them with ``roles=(...)``." msgstr "La structure du contrat (rôles des entrées, arguments de paramètres, sorties) est déduite des annotations. Annotez chaque argument et la valeur de retour avec des noms importés à l'exécution (pas seulement sous ``TYPE_CHECKING``). Lorsque les rôles ne peuvent pas être déduits (plus de deux objets en entrée), déclarez-les avec ``roles=(...)``." diff --git a/doc/locale/fr/LC_MESSAGES/release_notes/release_1.04.po b/doc/locale/fr/LC_MESSAGES/release_notes/release_1.04.po index 872510a..6b39503 100644 --- a/doc/locale/fr/LC_MESSAGES/release_notes/release_1.04.po +++ b/doc/locale/fr/LC_MESSAGES/release_notes/release_1.04.po @@ -67,8 +67,8 @@ msgstr "**ROI rectangulaires en indices de pixels** : le rectangle physique d'un msgid "**ROIs saved with earlier versions**: The ROIs of objects saved with earlier versions are not converted: they select pixels according to the rules above, so that processing and analysis in these ROIs may give different results when they are recomputed." msgstr "**ROI enregistrées avec des versions antérieures** : les ROI des objets enregistrés avec des versions antérieures ne sont pas converties : elles sélectionnent les pixels selon les règles ci-dessus, de sorte que les traitements et analyses dans ces ROI peuvent donner des résultats différents lorsqu'ils sont recalculés." -msgid "**Replay of processing in image ROIs**: The selection of image pixels by regions of interest is now recorded with each call on images carrying regions of interest (rule `sigima.image.roi.pixel_centers`, `sigima.proc.contracts.IMAGE_ROI_CONVENTION`, in the execution context described by `describe_call` and `prepare_execution`). Applications that replay recorded processing (e.g. DataLab capsules) refuse to replay such calls recorded with earlier versions, which selected other pixels, instead of reporting a different result. Calls on images without regions of interest are not concerned." -msgstr "**Rejeu des traitements dans les ROI d'images** : la sélection des pixels d'une image par les régions d'intérêt est désormais enregistrée avec chaque appel sur des images portant des régions d'intérêt (règle `sigima.image.roi.pixel_centers`, `sigima.proc.contracts.IMAGE_ROI_CONVENTION`, dans le contexte d'exécution décrit par `describe_call` et `prepare_execution`). Les applications qui rejouent des traitements enregistrés (par exemple les capsules DataLab) refusent de rejouer de tels appels enregistrés avec des versions antérieures, qui sélectionnaient d'autres pixels, au lieu de signaler un résultat différent. Les appels sur des images sans région d'intérêt ne sont pas concernés." +msgid "**Replay of processing in image ROIs**: The selection of image pixels by regions of interest is now recorded with each call on images carrying regions of interest, and with each call of the operations taking image regions of interest as parameters (`extract_roi`, `extract_rois`, `erase` and `offset_correction`), whatever their input (rule `sigima.image.roi.pixel_centers`, `sigima.proc.contracts.IMAGE_ROI_CONVENTION`, in the execution context described by `describe_call` and `prepare_execution`). Applications that replay recorded processing (e.g. DataLab capsules) refuse to replay such calls recorded with earlier versions, which selected other pixels, instead of reporting a different result. Other calls on images without regions of interest are not concerned." +msgstr "**Rejeu des traitements dans les ROI d'images** : la sélection des pixels d'une image par les régions d'intérêt est désormais enregistrée avec chaque appel sur des images portant des régions d'intérêt, et avec chaque appel des opérations qui prennent des régions d'intérêt d'image en paramètres (`extract_roi`, `extract_rois`, `erase` et `offset_correction`), quelle que soit leur entrée (règle `sigima.image.roi.pixel_centers`, `sigima.proc.contracts.IMAGE_ROI_CONVENTION`, dans le contexte d'exécution décrit par `describe_call` et `prepare_execution`). Les applications qui rejouent des traitements enregistrés (par exemple les capsules DataLab) refusent de rejouer de tels appels enregistrés avec des versions antérieures, qui sélectionnaient d'autres pixels, au lieu de signaler un résultat différent. Les autres appels sur des images sans région d'intérêt ne sont pas concernés." msgid "**Image center**: `ImageObj.xc` and `ImageObj.yc` are now the center of the pixel grid (halfway between the centers of the first and last pixels), for uniform and non-uniform coordinates: they were half a pixel off for uniform coordinates. `width` and `height` are unchanged." msgstr "**Centre de l'image** : `ImageObj.xc` et `ImageObj.yc` sont désormais le centre de la grille de pixels (à mi-chemin entre les centres du premier et du dernier pixel), pour des coordonnées uniformes ou non : ils étaient décalés d'un demi-pixel pour des coordonnées uniformes. `width` et `height` sont inchangés." diff --git a/doc/locale/fr/LC_MESSAGES/user_guide/contracts.po b/doc/locale/fr/LC_MESSAGES/user_guide/contracts.po index bf48f4e..82de99e 100644 --- a/doc/locale/fr/LC_MESSAGES/user_guide/contracts.po +++ b/doc/locale/fr/LC_MESSAGES/user_guide/contracts.po @@ -51,8 +51,8 @@ msgstr "Règles du contexte d'exécution" msgid "Some rules apply to every operation, or to a family of operations, and are recorded with each call rather than in each contract, with their own identifier and version:" msgstr "Certaines règles s'appliquent à toutes les opérations, ou à une famille d'opérations, et sont enregistrées avec chaque appel plutôt que dans chaque contrat, avec leurs propres identifiant et version :" -msgid "the selection of image pixels by regions of interest (``sigima.image.roi.pixel_centers``, :data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`), recorded when an input image carries regions of interest;" -msgstr "la sélection des pixels d'une image par les régions d'intérêt (``sigima.image.roi.pixel_centers``, :data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`), enregistrée lorsqu'une image d'entrée porte des régions d'intérêt ;" +msgid "the selection of image pixels by regions of interest (``sigima.image.roi.pixel_centers``, :data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`), recorded when an input image carries regions of interest or when the operation takes image regions of interest as parameters (ROI extraction, erasing, offset correction);" +msgstr "la sélection des pixels d'une image par les régions d'intérêt (``sigima.image.roi.pixel_centers``, :data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`), enregistrée lorsqu'une image d'entrée porte des régions d'intérêt ou lorsque l'opération prend des régions d'intérêt d'image en paramètres (extraction de ROI, effacement, correction d'offset) ;" msgid "the alignment of signals on a common X grid, for operations combining several signals (``sigima.signal.x_alignment.*`` policies);" msgstr "l'alignement des signaux sur une grille X commune, pour les opérations combinant plusieurs signaux (politiques ``sigima.signal.x_alignment.*``) ;" diff --git a/doc/release_notes/release_1.04.md b/doc/release_notes/release_1.04.md index 0c5443d..3fd2b5d 100644 --- a/doc/release_notes/release_1.04.md +++ b/doc/release_notes/release_1.04.md @@ -23,7 +23,7 @@ The [user guide](../user_guide/overview.rst) shows how readers and connectors at * **Grids of ROIs**: The cells of ROI grids are now made of whole pixels: each cell holds the pixels whose centers lie in it, from its start included to its end excluded, and its ROI goes from the outer edges of these pixels. Adjacent cells thus never share a pixel, and the cells of a grid with 100 % sizes and steps tile the image exactly; cell positions are rounded to whole pixels. The grid of an image with non-uniform coordinates now spans the actual extent of its axes: its last rows and columns were left out. * **Rectangular ROIs in pixel indices**: The physical rectangle of a ROI defined in pixel indices now goes from the outer edges of its pixels, half a pixel before the center of the first pixel and half a pixel after the center of the last one. Converting a ROI between pixel indices and physical coordinates keeps the same pixels. * **ROIs saved with earlier versions**: The ROIs of objects saved with earlier versions are not converted: they select pixels according to the rules above, so that processing and analysis in these ROIs may give different results when they are recomputed. -* **Replay of processing in image ROIs**: The selection of image pixels by regions of interest is now recorded with each call on images carrying regions of interest (rule `sigima.image.roi.pixel_centers`, `sigima.proc.contracts.IMAGE_ROI_CONVENTION`, in the execution context described by `describe_call` and `prepare_execution`). Applications that replay recorded processing (e.g. DataLab capsules) refuse to replay such calls recorded with earlier versions, which selected other pixels, instead of reporting a different result. Calls on images without regions of interest are not concerned. +* **Replay of processing in image ROIs**: The selection of image pixels by regions of interest is now recorded with each call on images carrying regions of interest, and with each call of the operations taking image regions of interest as parameters (`extract_roi`, `extract_rois`, `erase` and `offset_correction`), whatever their input (rule `sigima.image.roi.pixel_centers`, `sigima.proc.contracts.IMAGE_ROI_CONVENTION`, in the execution context described by `describe_call` and `prepare_execution`). Applications that replay recorded processing (e.g. DataLab capsules) refuse to replay such calls recorded with earlier versions, which selected other pixels, instead of reporting a different result. Other calls on images without regions of interest are not concerned. * **Image center**: `ImageObj.xc` and `ImageObj.yc` are now the center of the pixel grid (halfway between the centers of the first and last pixels), for uniform and non-uniform coordinates: they were half a pixel off for uniform coordinates. `width` and `height` are unchanged. * **Ellipse coordinates**: `EllipseCoordinates` (`sigima.objects.shape`) now holds the 5 values `(x, y, a, b, theta)` of ellipse geometry results, which can thus be transformed. * **Operation contract versions**: The operation contracts keep version 1 in this release, although the result of some operations changed since 1.3.0 (for example `resampling`, `extract_roi` and the product of signals): the contracts had just been published with Sigima 1.3.0 and were not used yet. This one-off exception does not apply to later changes: from now on, the version of a contract is increased whenever a change of the computation changes its result, whatever the releases of the applications using Sigima, and calls recorded with an earlier version are refused instead of being replayed (see [Operation contracts and versions](../user_guide/contracts.rst)). The image profiles, changed afterwards (see below), are thus at version 2. diff --git a/doc/user_guide/contracts.rst b/doc/user_guide/contracts.rst index d76e94c..4c5ee4d 100644 --- a/doc/user_guide/contracts.rst +++ b/doc/user_guide/contracts.rst @@ -60,7 +60,8 @@ and version: - the selection of image pixels by regions of interest (``sigima.image.roi.pixel_centers``, :data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`), recorded when an input - image carries regions of interest; + image carries regions of interest or when the operation takes image regions of + interest as parameters (ROI extraction, erasing, offset correction); - the alignment of signals on a common X grid, for operations combining several signals (``sigima.signal.x_alignment.*`` policies); - the global options read by a computation and the metadata of its inputs it diff --git a/sigima/proc/contracts/execution.py b/sigima/proc/contracts/execution.py index 637cf91..d2f1013 100644 --- a/sigima/proc/contracts/execution.py +++ b/sigima/proc/contracts/execution.py @@ -24,14 +24,15 @@ The execution context also records the conventions that the result depends on whatever the operation. Rule ``sigima.image.roi.pixel_centers`` (version 1, :data:`IMAGE_ROI_CONVENTION`), recorded under ``roi_convention`` when an input -image carries regions of interest: they select the pixels whose centers lie in +image carries regions of interest or when the operation takes image regions of +interest as parameters (:class:`~sigima.objects.ROI2DParam`: ROI extraction, +erasing, offset correction): they select the pixels whose centers lie in their shape, bounds included; the part of the image holding a region of interest, the position of the results computed in it and the move of regions of interest by flips, rotations, transposition and translation follow the same pixels. Earlier Sigima versions (before 1.4) rounded bounds to the nearest pixel -indices: a replay refuses a call on images carrying regions of interest -recorded without this rule (``unsupported_context``) instead of giving another -result. +indices: a replay refuses such a call recorded without this rule +(``unsupported_context``) instead of giving another result. Errors carry a stable ``code`` (:class:`ExecutionError`); scientific refusals of a policy keep their own exception @@ -51,7 +52,7 @@ from typing import Any, Callable from sigima.config import options -from sigima.objects import ImageObj, SignalObj +from sigima.objects import ImageObj, ROI2DParam, SignalObj from sigima.objects.scalar.geometry import GeometryResult from sigima.objects.scalar.table import TableResult from sigima.proc.contracts.codec import PARAMETER_CODEC_VERSION, encode_value @@ -138,30 +139,52 @@ def default_roles(count: int) -> list[str]: return ["sources"] * count -def image_roi_convention(inputs: Sequence[Any]) -> dict[str, Any] | None: +def _takes_image_roi_parameters(contract: OperationContract) -> bool: + """Return True if the operation takes image regions of interest as parameters. + + Such an operation (e.g. ROI extraction, erasing, offset correction) selects + image pixels by regions of interest whatever its inputs. + """ + return any(role.kind == "image" for role in contract.inputs) and any( + issubclass(spec.paramclass, ROI2DParam) for spec in contract.parameters + ) + + +def image_roi_convention( + inputs: Sequence[Any], contract: OperationContract | None = None +) -> dict[str, Any] | None: """Return the image ROI convention a call depends on, as recorded. Args: inputs: Input objects of the call. + contract: Contract of the operation, if known: an operation taking image + regions of interest as parameters (:class:`~sigima.objects.ROI2DParam`, + single or list) depends on the convention whatever its inputs. Returns: A copy of :data:`IMAGE_ROI_CONVENTION` if an input image carries regions - of interest, None otherwise. + of interest or if the operation takes image regions of interest as + parameters, None otherwise. """ + if contract is not None and _takes_image_roi_parameters(contract): + return dict(IMAGE_ROI_CONVENTION) for obj in inputs: if isinstance(obj, ImageObj) and obj.roi is not None and not obj.roi.is_empty(): return dict(IMAGE_ROI_CONVENTION) return None -def _check_image_roi_convention(context: dict[str, Any], inputs: list[Any]) -> None: - """Check that a replay on images with regions of interest recorded the rule. +def _check_image_roi_convention( + context: dict[str, Any], inputs: list[Any], contract: OperationContract +) -> None: + """Check that a replay depending on image regions of interest recorded the rule. Raises: ExecutionError: ``unsupported_context`` if an input image carries regions - of interest and the recorded rule is missing or another one. + of interest or the operation takes image regions of interest as + parameters, and the recorded rule is missing or another one. """ - expected = image_roi_convention(inputs) + expected = image_roi_convention(inputs, contract) if expected is None or context.get("roi_convention") == expected: return recorded = context.get("roi_convention") @@ -299,7 +322,7 @@ def describe_call( } description["pattern"] = contract.pattern description["options"] = _current_options(contract) - convention = image_roi_convention(inputs) + convention = image_roi_convention(inputs, contract) if convention is not None: description["context"]["roi_convention"] = convention if _inputs_match(contract, inputs): @@ -351,7 +374,8 @@ class PreparedExecution: context: Execution context to record: ``x_alignment`` (record of the policy applied, or None), when the computation reads global options, ``options`` (their values) and, when an input image carries regions of - interest, ``roi_convention`` (:data:`IMAGE_ROI_CONVENTION`). + interest or the operation takes image regions of interest as + parameters, ``roi_convention`` (:data:`IMAGE_ROI_CONVENTION`). options: Global option values applied while the function runs. """ @@ -436,8 +460,9 @@ def prepare_execution( ExecutionError: ``input_mismatch`` if the inputs do not fit the roles, ``invalid_parameters`` if the parameters cannot be decoded or do not fit, ``unsupported_context`` if a recorded global option is missing, or if an - input image carries regions of interest and the recorded context does - not name the image ROI convention (:data:`IMAGE_ROI_CONVENTION`). + input image carries regions of interest (or the operation takes image + regions of interest as parameters) and the recorded context does not + name the image ROI convention (:data:`IMAGE_ROI_CONVENTION`). XAlignmentError: If a recorded policy is unknown or the inputs cannot be aligned (scientific refusal, also in ordinary processing). """ @@ -457,7 +482,7 @@ def prepare_execution( arguments = _parameter_arguments(contract, param) replay = context is not None if replay: - _check_image_roi_convention(context, list(inputs)) + _check_image_roi_convention(context, list(inputs), contract) option_values: dict[str, Any] = {} if contract.reads_options: if replay: @@ -484,7 +509,7 @@ def prepare_execution( exec_context: dict[str, Any] = {"x_alignment": record} if option_values: exec_context["options"] = dict(option_values) - convention = image_roi_convention(inputs) + convention = image_roi_convention(inputs, contract) if convention is not None: exec_context["roi_convention"] = convention return PreparedExecution( diff --git a/sigima/tests/common/execution_unit_test.py b/sigima/tests/common/execution_unit_test.py index 5665f54..c3a0c1f 100644 --- a/sigima/tests/common/execution_unit_test.py +++ b/sigima/tests/common/execution_unit_test.py @@ -193,6 +193,78 @@ def test_image_roi_convention_is_recorded_and_checked() -> None: assert contracts.image_roi_convention([signal]) is None +def _roi_params(count: int) -> list[sigima.objects.ROI2DParam]: + """Rectangles whose bounds do not lie on pixel centers.""" + return [ + sigima.objects.ROI2DParam.create( + geometry="rectangle", x0=1.5 + 3 * i, y0=2.5, dx=2.0, dy=3.0 + ) + for i in range(count) + ] + + +@pytest.mark.parametrize( + "func, param", + [ + (sigima.proc.image.extract_roi, _roi_params(1)[0]), + (sigima.proc.image.extract_rois, _roi_params(2)), + (sigima.proc.image.erase, _roi_params(1)[0]), + (sigima.proc.image.erase, _roi_params(2)), + (sigima.proc.image.offset_correction, _roi_params(1)[0]), + ], +) +def test_image_roi_parameters_depend_on_the_convention(func, param) -> None: + """Operations taking image ROIs as parameters record and require the pixel + convention, also on images without regions of interest (regression: a replay + recorded before Sigima 1.4, which selected other pixels, was accepted).""" + rule = dict(contracts.IMAGE_ROI_CONVENTION) + contract = _contract(func) + image = sigima.objects.create_image("i", np.arange(64.0).reshape(8, 8)) + assert image.roi is None + assert contracts.image_roi_convention([image], contract) == rule + description = _json(describe_call(func, [image], param)) + assert description["context"] == {"roi_convention": rule} + assert not description["limits"] + ordinary = prepare_execution(contract, [image], param=param) + assert ordinary.context == {"x_alignment": None, "roi_convention": rule} + reference = ordinary.run() + values = description["parameters"] + replay = prepare_execution( + contract, [image], values=values, context=ordinary.context + ) + assert np.array_equal(replay.run().data, reference.data) + for recorded in ( + {}, + {"x_alignment": None}, + {"roi_convention": None}, + {"roi_convention": {"rule": "sigima.image.roi.pixel_centers", "version": 2}}, + ): + with pytest.raises(ExecutionError) as info: + prepare_execution(contract, [image], values=values, context=recorded) + assert info.value.code == "unsupported_context" + + +def test_operations_taking_image_roi_parameters() -> None: + """The operations depending on the convention through their parameters are + detected from their contracts; signal ROI parameters are not concerned.""" + registry = contracts.build_contract_registry() + found = { + contract.operation_id + for contract in registry.contracts + if contracts.image_roi_convention([], contract) is not None + } + assert found == { + "sigima.image.erase", + "sigima.image.extract_roi", + "sigima.image.extract_rois", + "sigima.image.offset_correction", + } + signal = _signal([0, 1, 2, 3], [-2.0, 0.0, 1.0, 4.0]) + roi = sigima.objects.ROI1DParam.create(xmin=0.5, xmax=2.5) + description = describe_call(sigima.proc.signal.extract_roi, [signal], roi) + assert not description["context"] + + def test_ordinary_and_replay_give_the_same_result() -> None: """Ordinary processing and replay share the same preparation.""" src = _signal([0, 1, 2, 3], [-2.0, 0.0, 1.0, 4.0])