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
10 changes: 6 additions & 4 deletions config/quality_control.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ resources:

metrics:

mask_obscuration:
pixel_mask:
enabled: true
params:
aperture: gaussian
sigma: 2.5
aperture:
type: circular
radius: 2.6
unit: sigma

goodness_of_fit:
enabled: true
Expand All @@ -25,7 +27,7 @@ metrics:

rejection:

mask_obscuration:
pixel_mask:
enabled: true
policy:
threshold:
Expand Down
9 changes: 5 additions & 4 deletions src/wf_psf/quality_control/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,18 +390,19 @@ def validate_rejection_policy_metrics(config: QualityControlConfig) -> None:
If an enabled rejection policy references an unknown or disabled
quality metric.
"""
for metric_name, metric_rejection_policy in config.rejection.items():
if not metric_rejection_policy.enabled:
for metric_name, rejection_policy in config.rejection.items():
if not rejection_policy.enabled:
continue

if metric_name not in config.metrics:
raise ValueError(
f"Rejection policy configured for unknown metric '{metric_name}' "
f"Rejection policy configured for unknown metric '{metric_name}'."
)

if not config.metrics[metric_name].enabled:
raise ValueError(
f"Rejection policy cannot be enabled because metric '{metric_name}' is disabled."
f"Rejection policy cannot be enabled because metric "
f"'{metric_name}' is disabled."
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
from .base import QualityMetric


class MaskObscurationMetric(QualityMetric):
"""Assess masked pixel obscuration for each dataset sample."""
class PixelMaskMetric(QualityMetric):
"""Evaluate pixel-mask metrics for each dataset sample."""

name = "mask_obscuration"
name = "pixel_mask"

def compute(self, dataset):
"""Compute the mask obscuration metric for each dataset sample."""
"""Compute pixel-mask metrics for each dataset sample."""
pass
4 changes: 2 additions & 2 deletions src/wf_psf/quality_control/metrics/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
Jennifer Pollack <jennifer.pollack@cea.fr>
"""

from .mask_obscuration import MaskObscurationMetric
from .pixel_masks import PixelMaskMetric
from .goodness_of_fit import GoodnessOfFitMetric
from .base import QualityMetric
from wf_psf.utils.registry import Registry
Expand Down Expand Up @@ -39,6 +39,6 @@ def register_metric(self, metric_cls: type[QualityMetric]) -> None:
def build_metrics_registry():
"""Create the registry of built-in quality metrics."""
registry = MetricsRegistry()
registry.register_metric(MaskObscurationMetric)
registry.register_metric(PixelMaskMetric)
registry.register_metric(GoodnessOfFitMetric)
return registry
22 changes: 11 additions & 11 deletions src/wf_psf/tests/test_quality_control/config_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,18 @@ def test_quality_control_config_loading():
== "inference_oversampled.yaml"
)

assert "mask_obscuration" in config.metrics
assert isinstance(config.metrics["mask_obscuration"], QualityMetricConfig)
assert config.metrics["mask_obscuration"].enabled is True
assert config.metrics["mask_obscuration"].required_resources == []
assert "pixel_mask" in config.metrics
assert isinstance(config.metrics["pixel_mask"], QualityMetricConfig)
assert config.metrics["pixel_mask"].enabled is True
assert config.metrics["pixel_mask"].required_resources == []

assert isinstance(config.metrics["goodness_of_fit"], QualityMetricConfig)
assert config.metrics["goodness_of_fit"].required_resources == (
[ResourceIdentifier.from_string("psf_models.standard")]
)

assert isinstance(config.rejection["mask_obscuration"], RejectionPolicyConfig)
assert config.rejection["mask_obscuration"].policy == {
assert isinstance(config.rejection["pixel_mask"], RejectionPolicyConfig)
assert config.rejection["pixel_mask"].policy == {
"threshold": {
"value": 3.0,
},
Expand Down Expand Up @@ -101,8 +101,8 @@ def test_required_resources_element_must_be_a_str():
def test_metrics_minimal():
config = load_config("valid/metric_minimal.yaml")

assert config.metrics["mask_obscuration"].enabled is True
assert config.metrics["mask_obscuration"].params == {}
assert config.metrics["pixel_mask"].enabled is True
assert config.metrics["pixel_mask"].params == {}
assert config.rejection == {}
assert config.reporting.save_metrics is False
assert config.reporting.log_statistics is False
Expand All @@ -111,7 +111,7 @@ def test_metrics_minimal():
def test_metric_enabled_must_be_boolean():
with pytest.raises(
TypeError,
match="Metric `enabled` flag for 'mask_obscuration' must be boolean",
match="Metric `enabled` flag for 'pixel_mask' must be boolean",
):
load_config("invalid/metric_invalid_enabled.yaml")

Expand Down Expand Up @@ -264,11 +264,11 @@ def test_validate_rejection_policy_metrics_all_valid(qc_config_factory):


def test_validate_rejection_policy_metrics_metric_not_found(qc_config_factory):
config = qc_config_factory(rejection_metric="mask_obscuration")
config = qc_config_factory(rejection_metric="pixel_mask")

with pytest.raises(
ValueError,
match="Rejection policy configured for unknown metric 'mask_obscuration'.",
match="Rejection policy configured for unknown metric 'pixel_mask'.",
):
validate_rejection_policy_metrics(config)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
metrics:

mask_obscuration: true
pixel_mask: true
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
metrics:
mask_obscuration:
pixel_mask:
enabled: "yes"
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
metrics:
mask_obscuration:
pixel_mask:
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ resources:

metrics:

mask_obscuration:
pixel_mask:
enabled: false
params:
aperture: gaussian
sigma: 2.5
aperture:
type: circular
radius: 2.6
unit: sigma


rejection:

mask_obscuration:
pixel_mask:
enabled: true
policy:
threshold:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
metrics:

mask_obscuration:
pixel_mask:
enabled: true

Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ resources:

metrics:

mask_obscuration:
pixel_mask:
enabled: true
params:
aperture: gaussian
sigma: 2.5
aperture:
type: circular
radius: 2.6
unit: sigma

goodness_of_fit:
enabled: true
Expand All @@ -30,7 +32,7 @@ metrics:

rejection:

mask_obscuration:
pixel_mask:
enabled: true
policy:
threshold:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ resources:

metrics:

mask_obscuration:
pixel_mask:
enabled: true
params:
aperture: gaussian
sigma: 2.5
aperture:
type: circular
radius: 2.6
unit: sigma

goodness_of_fit:
enabled: true
Expand All @@ -24,7 +26,7 @@ metrics:

rejection:

mask_obscuration:
pixel_mask:
enabled: true
policy:
threshold:
Expand Down
22 changes: 11 additions & 11 deletions src/wf_psf/tests/test_quality_control/pipeline_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from wf_psf.quality_control.pipeline import QualityControlPipeline
from wf_psf.quality_control.config import QualityControlConfig
from wf_psf.quality_control.metrics.mask_obscuration import MaskObscurationMetric
from wf_psf.quality_control.metrics.pixel_masks import PixelMaskMetric
from wf_psf.quality_control.metrics.goodness_of_fit import GoodnessOfFitMetric
from wf_psf.quality_control.rejection.threshold import ThresholdRejectionPolicy

Expand All @@ -26,7 +26,7 @@ def test_pipeline_constructor(pipeline_factory):
assert isinstance(pipeline.config, QualityControlConfig)

# Check metrics registry
assert pipeline.metrics_registry.get("mask_obscuration") is MaskObscurationMetric
assert pipeline.metrics_registry.get("pixel_mask") is PixelMaskMetric
assert pipeline.metrics_registry.get("goodness_of_fit") is GoodnessOfFitMetric

# Check rejection registry
Expand All @@ -39,7 +39,7 @@ def test_pipeline_instantiate_metrics_valid(pipeline_factory):
metrics = pipeline._instantiate_metrics()

assert len(metrics) == 2
assert isinstance(metrics["mask_obscuration"], MaskObscurationMetric)
assert isinstance(metrics["pixel_mask"], PixelMaskMetric)
assert isinstance(metrics["goodness_of_fit"], GoodnessOfFitMetric)


Expand All @@ -56,8 +56,8 @@ def test_pipeline_instantiate_rejection_policy_valid(pipeline_factory):
rejection_policies = pipeline._instantiate_rejection_policies()

assert len(rejection_policies) == 1
assert isinstance(rejection_policies["mask_obscuration"], ThresholdRejectionPolicy)
assert rejection_policies["mask_obscuration"].value == 3.0
assert isinstance(rejection_policies["pixel_mask"], ThresholdRejectionPolicy)
assert rejection_policies["pixel_mask"].value == 3.0

assert "goodness_of_fit" not in rejection_policies

Expand All @@ -69,7 +69,7 @@ def test_pipeline_run_single_rejection_policy(pipeline_factory):

with (
patch.object(
MaskObscurationMetric,
PixelMaskMetric,
"compute",
return_value=metric_result,
) as mock_mask_compute,
Expand Down Expand Up @@ -99,7 +99,7 @@ def test_pipeline_run_single_rejection_policy(pipeline_factory):
mock_apply.assert_called_once_with(metric_result)

assert np.array_equal(
result.metrics["mask_obscuration"],
result.metrics["pixel_mask"],
np.array([1.0, 2.0, 3.0]),
)

Expand All @@ -109,7 +109,7 @@ def test_pipeline_run_single_rejection_policy(pipeline_factory):
)

assert np.array_equal(
result.validity_masks["mask_obscuration"],
result.validity_masks["pixel_mask"],
np.array([True, False, True]),
)

Expand All @@ -132,7 +132,7 @@ def test_pipeline_run_multiple_rejection_policies(pipeline_factory):

with (
patch.object(
MaskObscurationMetric,
PixelMaskMetric,
"compute",
return_value=metric_result,
) as mock_mask_compute,
Expand Down Expand Up @@ -164,7 +164,7 @@ def test_pipeline_run_multiple_rejection_policies(pipeline_factory):
assert mock_apply.call_count == 2

assert np.array_equal(
result.metrics["mask_obscuration"],
result.metrics["pixel_mask"],
np.array([1.0, 2.0, 3.0]),
)

Expand All @@ -174,7 +174,7 @@ def test_pipeline_run_multiple_rejection_policies(pipeline_factory):
)

assert np.array_equal(
result.validity_masks["mask_obscuration"],
result.validity_masks["pixel_mask"],
np.array([True, True, False]),
)

Expand Down
2 changes: 1 addition & 1 deletion src/wf_psf/tests/test_quality_control/resources_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def second_preparer(config):
def test_get_required(qc_config_factory):
config = qc_config_factory(
metrics={
"mask_obscuration": QualityMetricConfig(
"pixel_mask": QualityMetricConfig(
enabled=True,
required_resources=[],
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
MetricsRegistry,
build_metrics_registry,
)
from wf_psf.quality_control.metrics.mask_obscuration import MaskObscurationMetric
from wf_psf.quality_control.metrics.pixel_masks import PixelMaskMetric
from wf_psf.quality_control.metrics.goodness_of_fit import GoodnessOfFitMetric


Expand All @@ -29,16 +29,16 @@ def compute(self, dataset) -> None:
def test_duplicate_metric_registration_raises():
registry = MetricsRegistry()

registry.register_metric(MaskObscurationMetric)
registry.register_metric(PixelMaskMetric)

with pytest.raises(ValueError):
registry.register_metric(MaskObscurationMetric)
registry.register_metric(PixelMaskMetric)


def test_build_metrics_registry():
registry = build_metrics_registry()

assert registry.get("mask_obscuration") is MaskObscurationMetric
assert registry.get("pixel_mask") is PixelMaskMetric

assert registry.get("goodness_of_fit") is GoodnessOfFitMetric

Expand Down
Loading