Coverage for tests/test_cameras.py: 43%
71 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-16 15:24 -0700
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-16 15:24 -0700
1# This file is part of lsst-images.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# Use of this source code is governed by a 3-clause BSD-style
10# license that can be found in the LICENSE file.
12from __future__ import annotations
14import os
15from typing import Any
17import pytest
19from lsst.images import YX
20from lsst.images.cameras import AmplifierRawGeometry, Detector, ReadoutCorner
21from lsst.images.tests import DP2_VISIT_DETECTOR_DATA_ID, RoundtripFits, compare_detector_to_legacy
23EXTERNAL_DATA_DIR = os.environ.get("TESTDATA_IMAGES_DIR", None)
26@pytest.fixture(scope="session")
27def legacy_camera_data() -> dict[str, Any]:
28 """Return a dict with legacy_camera and legacy_detector loaded from the
29 test data directory.
30 """
31 if EXTERNAL_DATA_DIR is None: 31 ↛ 33line 31 didn't jump to line 33 because the condition on line 31 was always true
32 pytest.skip("TESTDATA_IMAGES_DIR is not in the environment.")
33 try:
34 from lsst.afw.cameraGeom import Camera as LegacyCamera
35 from lsst.afw.image import ExposureFitsReader
36 except ImportError:
37 pytest.skip("lsst.afw could not be imported.")
38 legacy_camera = LegacyCamera.readFits(os.path.join(EXTERNAL_DATA_DIR, "dp2", "legacy", "camera.fits"))
39 legacy_detector = ExposureFitsReader(
40 os.path.join(EXTERNAL_DATA_DIR, "dp2", "legacy", "visit_image.fits")
41 ).readDetector()
42 return {"legacy_camera": legacy_camera, "legacy_detector": legacy_detector}
45def test_visit_image_detector_legacy_conversions(legacy_camera_data: dict[str, Any]) -> None:
46 """Test converting a visit-image detector from legacy and back with
47 serialization in between.
48 """
49 legacy_detector = legacy_camera_data["legacy_detector"]
50 detector = Detector.from_legacy(
51 legacy_detector,
52 instrument=DP2_VISIT_DETECTOR_DATA_ID["instrument"],
53 visit=DP2_VISIT_DETECTOR_DATA_ID["visit"],
54 )
55 compare_detector_to_legacy(detector, legacy_detector, is_raw_assembled=True)
56 with RoundtripFits(detector) as roundtrip:
57 pass
58 compare_detector_to_legacy(roundtrip.result, legacy_detector, is_raw_assembled=True)
59 compare_detector_to_legacy(detector, roundtrip.result.to_legacy(), is_raw_assembled=True)
62def test_camera_detector_legacy_conversions(legacy_camera_data: dict[str, Any]) -> None:
63 """Test converting one detector of each type from a legacy Camera and
64 back.
65 """
66 legacy_camera = legacy_camera_data["legacy_camera"]
67 detector_types_seen = set()
68 for legacy_detector_1 in legacy_camera:
69 if legacy_detector_1.getType() in detector_types_seen:
70 continue
71 detector_types_seen.add(legacy_detector_1.getType())
72 detector = Detector.from_legacy(
73 legacy_detector_1,
74 instrument=DP2_VISIT_DETECTOR_DATA_ID["instrument"],
75 )
76 compare_detector_to_legacy(detector, legacy_detector_1, is_raw_assembled=False)
77 with RoundtripFits(detector) as roundtrip:
78 pass
79 compare_detector_to_legacy(roundtrip.result, legacy_detector_1, is_raw_assembled=False)
80 legacy_detector_2 = roundtrip.result.to_legacy()
81 compare_detector_to_legacy(detector, legacy_detector_2, is_raw_assembled=False)
84def test_expanded_detector_roundtrip(legacy_camera_data: dict[str, Any]) -> None:
85 """Test round-tripping a detector that holds both assembled and
86 unassembled raw amplifier geometry.
87 """
88 legacy_camera = legacy_camera_data["legacy_camera"]
89 legacy_detector = legacy_camera_data["legacy_detector"]
90 legacy_camera_detector = legacy_camera[DP2_VISIT_DETECTOR_DATA_ID["detector"]]
91 detector = Detector.from_legacy(
92 legacy_camera_detector,
93 instrument=DP2_VISIT_DETECTOR_DATA_ID["instrument"],
94 visit=DP2_VISIT_DETECTOR_DATA_ID["visit"],
95 is_raw_assembled=False,
96 )
97 for amplifier, legacy_assembled_amplifier in zip(
98 detector.amplifiers, legacy_detector.getAmplifiers(), strict=True
99 ):
100 assert amplifier.unassembled_raw_geometry is not None
101 assert amplifier.assembled_raw_geometry is None
102 amplifier.assembled_raw_geometry = AmplifierRawGeometry.from_legacy_amplifier(
103 legacy_assembled_amplifier
104 )
105 with RoundtripFits(detector) as roundtrip:
106 pass
107 compare_detector_to_legacy(roundtrip.result, legacy_camera_detector, is_raw_assembled=False)
108 compare_detector_to_legacy(roundtrip.result, legacy_detector, is_raw_assembled=True)
111def test_as_flips() -> None:
112 """Tes that ReadoutCorner.as_flips returns the correct YX flip flags for
113 each corner.
114 """
115 assert ReadoutCorner.LL.as_flips() == YX(y=False, x=False)
116 assert ReadoutCorner.LR.as_flips() == YX(y=False, x=True)
117 assert ReadoutCorner.UL.as_flips() == YX(y=True, x=False)
118 assert ReadoutCorner.UR.as_flips() == YX(y=True, x=True)
121def test_flips_roundtrip() -> None:
122 """Test that ReadoutCorner.from_flips is the inverse of as_flips for all
123 corners.
124 """
125 for corner in ReadoutCorner:
126 flips = corner.as_flips()
127 assert ReadoutCorner.from_flips(y=flips.y, x=flips.x) is corner
130def test_apply_flips() -> None:
131 """Test that ReadoutCorner.apply_flips with identity returns the same
132 corner, and a few specific flips are correct.
133 """
134 for corner in ReadoutCorner:
135 assert corner.apply_flips(y=False, x=False) is corner
136 assert ReadoutCorner.LL.apply_flips(y=True, x=True) is ReadoutCorner.UR
137 assert ReadoutCorner.LR.apply_flips(y=False, x=True) is ReadoutCorner.LL
138 assert ReadoutCorner.UL.apply_flips(y=True, x=False) is ReadoutCorner.LL
139 assert ReadoutCorner.UR.apply_flips(y=True, x=True) is ReadoutCorner.LL