Skip to content

Commit

Permalink
Merge branch 'image_safe_getters' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
thusser committed Dec 23, 2023
2 parents eb9cdc3 + 105b85f commit 25372d7
Show file tree
Hide file tree
Showing 19 changed files with 99 additions and 54 deletions.
55 changes: 47 additions & 8 deletions pyobs/images/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from numpy.typing import NDArray

from pyobs.utils.fits import FilenameFormatter
import pyobs.utils.exceptions as exc

MetaClass = TypeVar("MetaClass")

Expand Down Expand Up @@ -244,7 +245,7 @@ def to_bytes(self) -> bytes:

def write_catalog(self, f: Any, *args: Any, **kwargs: Any) -> None:
"""Write catalog to file object."""
if self.catalog is None:
if self._catalog is None:
return

hdu = table_to_hdu(self._catalog)
Expand Down Expand Up @@ -364,55 +365,93 @@ def get_meta_safe(self, meta_class: Type[MetaClass], default: Optional[MetaClass
return default

@property
def data(self) -> Optional[NDArray[Any]]:
def data(self) -> NDArray[Any]:
if self._data is None:
raise exc.ImageError("No data found in image.")
return self._data

@property
def safe_data(self) -> Optional[NDArray[Any]]:
return self._data

@data.setter
def data(self, val: Optional[NDArray[Any]]):
self._data = val

@property
def header(self) -> Optional[fits.Header]:
def header(self) -> fits.Header:
if self._header is None:
raise exc.ImageError("No header found in image.")
return self._header

@property
def safe_header(self) -> Optional[fits.Header]:
return self._header

@header.setter
def header(self, val: Optional[fits.Header]):
self._header = val

@property
def mask(self) -> Optional[NDArray[Any]]:
def mask(self) -> NDArray[Any]:
if self._mask is None:
raise exc.ImageError("No mask found in image.")
return self._mask

@property
def safe_mask(self) -> Optional[NDArray[Any]]:
return self._mask

@mask.setter
def mask(self, val: Optional[NDArray[Any]]):
self._mask = val

@property
def uncertainty(self) -> Optional[NDArray[Any]]:
def uncertainty(self) -> NDArray[Any]:
if self._uncertainty is None:
raise exc.ImageError("No uncertainties found in image.")
return self._uncertainty

@property
def safe_uncertainty(self) -> Optional[NDArray[Any]]:
return self._uncertainty

@uncertainty.setter
def uncertainty(self, val: Optional[NDArray[Any]]):
self._uncertainty = val

@property
def catalog(self) -> Optional[Table]:
def catalog(self) -> Table:
if self._catalog is None:
raise exc.ImageError("No catalog found in image.")
return self._catalog

@property
def safe_catalog(self) -> Optional[Table]:
return self._catalog

@catalog.setter
def catalog(self, val: Optional[Table]):
self._catalog = val

@property
def raw(self) -> Optional[NDArray[Any]]:
def raw(self) -> NDArray[Any]:
if self._raw is None:
raise exc.ImageError("No raw data found in image.")
return self._raw

@property
def safe_raw(self) -> Optional[NDArray[Any]]:
return self._raw

@raw.setter
def raw(self, val: Optional[NDArray[Any]]):
self._raw = val

@property
def meta(self) -> Optional[Dict[Any, Any]]:
def meta(self) -> Dict[Any, Any]:
if self._meta is None:
self._meta: Dict[Any, Any] = {}
return self._meta

@meta.setter
Expand Down
2 changes: 1 addition & 1 deletion pyobs/images/processors/_daobackgroundremover.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def _estimate_background(self, image: Image) -> npt.NDArray[float]:
filter_size=self._filter_size,
sigma_clip=self._sigma_clip,
bkg_estimator=self._bkg_estimator,
mask=image.mask,
mask=image.safe_mask,
)

return bkg.background
Expand Down
2 changes: 0 additions & 2 deletions pyobs/images/processors/astrometry/_dotnet_request_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ def _build_request_data(self) -> None:

def __call__(self, image: Image) -> _DotNetRequest:
# set catalog and header
if image.catalog is None:
raise exc.ImageError("No catalog found in image.")
self._catalog = image.catalog[["x", "y", "flux", "peak"]].to_pandas()
self._header = image.header

Expand Down
2 changes: 1 addition & 1 deletion pyobs/images/processors/detection/daophot.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ async def __call__(self, image: Image) -> Image:
Image with attached catalog.
"""

if image.data is None:
if image.safe_data is None:
log.warning("No data found in image.")
return image

Expand Down
18 changes: 12 additions & 6 deletions pyobs/images/processors/detection/pysep.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,22 @@ class SepSourceDetection(SourceDetection):
__module__ = "pyobs.images.processors.detection"

_CATALOG_KEYS = [
"x", "y",
"x",
"y",
"peak",
"flux",
"fwhm",
"a", "b", "theta",
"a",
"b",
"theta",
"ellipticity",
"tnpix",
"kronrad",
"fluxrad25", "fluxrad50", "fluxrad75",
"xwin", "ywin",
"fluxrad25",
"fluxrad50",
"fluxrad75",
"xwin",
"ywin",
]

def __init__(
Expand Down Expand Up @@ -77,7 +83,7 @@ async def __call__(self, image: Image) -> Image:
Image with attached catalog.
"""

if image.data is None:
if image.safe_data is None:
log.warning("No data found in image.")
return image

Expand All @@ -104,7 +110,7 @@ async def __call__(self, image: Image) -> Image:

@staticmethod
def _get_mask_or_default(image: Image) -> np.ndarray:
if image.mask is not None:
if image.safe_mask is not None:
return image.mask

return np.zeros(image.data.shape, dtype=bool)
Expand Down
2 changes: 1 addition & 1 deletion pyobs/images/processors/exptime/star.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ async def _calc_exp_time(self, image: Image) -> float:
self._image = copy(image)
last_exp_time = image.header["EXPTIME"]

if self._image.catalog is None:
if self._image.safe_catalog is None:
log.info("No catalog found in image.")
return last_exp_time

Expand Down
2 changes: 1 addition & 1 deletion pyobs/images/processors/misc/smooth.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ async def __call__(self, image: Image) -> Image:
"""

output_image = image.copy()
if output_image.data is None:
if output_image.safe_data is None:
log.warning("No data found in image.")
return image

Expand Down
4 changes: 2 additions & 2 deletions pyobs/images/processors/misc/softbin.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ async def __call__(self, image: Image) -> Image:
"""

output_image = image.copy()
if output_image.data is None:
if output_image.safe_data is None:
log.warning("No data found in image.")
return image

output_image.data = self._reshape_image(output_image.data)
if output_image.data is None:
if output_image.safe_data is None:
log.warning("No data found in image after reshaping.")
return image

Expand Down
2 changes: 1 addition & 1 deletion pyobs/images/processors/offsets/brighteststar.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ async def __call__(self, image: Image) -> Image:
"""

# get catalog and sort by flux
cat = image.catalog
cat = image.safe_catalog
if cat is None or len(cat) < 1:
log.warning("No catalog found in image.")
return image
Expand Down
8 changes: 1 addition & 7 deletions pyobs/images/processors/offsets/nstar.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,6 @@ async def _boxes_from_ref(self, image: Image, star_box_size: int) -> List:
# run pipeline on 1st image
img = await self.run_pipeline(image)

# check data and catalog
if img.data is None:
raise ValueError("No data found in image.")
if img.catalog is None:
raise ValueError("No catalog found in image.")

# do photometry and get catalog
sources = self._fits2numpy(img.catalog)

Expand Down Expand Up @@ -273,7 +267,7 @@ def _calculate_offsets(self, image: Image) -> Tuple[Optional[float], Optional[fl
"""

# data?
if image.data is None:
if image.safe_data is None:
return None, None

# calculate offset for each star
Expand Down
4 changes: 0 additions & 4 deletions pyobs/images/processors/offsets/projected.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,6 @@ def _process(self, image: Image) -> Tuple[npt.NDArray[float], npt.NDArray[float]
# get image data and header
data, hdr = image.data, image.header

# no data?
if data is None:
raise ValueError("Image contains no data.")

# trimsec
if "TRIMSEC" in hdr:
m = re.match(r"\[([0-9]+):([0-9]+),([0-9]+):([0-9]+)\]", hdr["TRIMSEC"])
Expand Down
5 changes: 3 additions & 2 deletions pyobs/images/processors/photometry/photutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ async def __call__(self, image: Image) -> Image:
return image

# fetch catalog
if image.catalog is None:
if image.safe_catalog is None:
log.warning("No catalog in image.")
return image
sources = image.catalog.copy()
Expand Down Expand Up @@ -96,7 +96,8 @@ async def __call__(self, image: Image) -> Image:

# do photometry
phot = await loop.run_in_executor(
None, partial(aperture_photometry, image.data, aperture, mask=image.mask, error=image.uncertainty)
None,
partial(aperture_photometry, image.data, aperture, mask=image.safe_mask, error=image.safe_uncertainty),
)

# calc flux
Expand Down
17 changes: 10 additions & 7 deletions pyobs/images/processors/photometry/pysep.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,15 @@ def _photometry(image: Image) -> Image:
from pyobs.images.processors.detection import SepSourceDetection

# check data
if image.data is None:
if image.safe_data is None:
log.warning("No data found in image.")
return image
if image.catalog is None:
if image.safe_catalog is None:
log.warning("No catalog found in image.")
return image

# no mask?
mask = image.mask if image.mask is not None else np.ones(image.data.shape, dtype=bool)

# remove background
data, bkg = SepSourceDetection.remove_background(image.data, mask)
data, bkg = SepSourceDetection.remove_background(image.data, image.safe_mask)

# fetch catalog
sources = image.catalog.copy()
Expand All @@ -93,7 +90,13 @@ def _photometry(image: Image) -> Image:
for diameter in [1, 2, 3, 4, 5, 6, 7, 8]:
if image.pixel_scale is not None:
flux, fluxerr, flag = sep.sum_circle(
data, x, y, diameter / 2.0 / image.pixel_scale, mask=image.mask, err=image.uncertainty, gain=gain
data,
x,
y,
diameter / 2.0 / image.pixel_scale,
mask=image.safe_mask,
err=image.safe_uncertainty,
gain=gain,
)
sources["fluxaper{0}".format(diameter)] = flux
sources["fluxerr{0}".format(diameter)] = fluxerr
Expand Down
2 changes: 1 addition & 1 deletion pyobs/modules/camera/basecamera.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ async def __expose(self, exposure_time: float, image_type: ImageType, broadcast:
self._exposure = ExposureInfo(start=datetime.datetime.utcnow(), exposure_time=exposure_time)
try:
image = await self._expose(exposure_time, open_shutter, abort_event=self.expose_abort)
if image is None or image.data is None:
if image is None or image.safe_data is None:
raise exc.GrabImageError("Could not take image.")

except exc.PyObsError:
Expand Down
2 changes: 1 addition & 1 deletion pyobs/modules/image/seeing.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ async def process_new_image_event(self, event: Event, sender: str) -> bool:
return False

# get catalog
cat = image.catalog
cat = image.safe_catalog
if cat is None:
# no catalog found in file
return False
Expand Down
2 changes: 1 addition & 1 deletion pyobs/utils/focusseries/photometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def analyse_image(self, image: Image, focus_value: float) -> None:
self._source_detection(image)

# filter
sources = image.catalog
sources = image.safe_catalog
if sources is None:
return
sources = sources[sources["ellipticity"] < 0.1]
Expand Down
2 changes: 1 addition & 1 deletion pyobs/utils/focusseries/projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def analyse_image(self, image: Image, focus_value: float) -> None:
"""

# clean data
if image.data is None:
if image.safe_data is None:
return
data = self._clean(image.data)

Expand Down
2 changes: 1 addition & 1 deletion tests/images/processors/test_removebackground.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,4 @@ def test_call_const_background(mocker):
image = Image(data=np.ones((20, 20)))
output_image = remover(image)

np.testing.assert_array_equal(output_image.data, np.zeros((20, 20)))
np.testing.assert_array_equal(output_image.data, np.zeros((20, 20)))
20 changes: 14 additions & 6 deletions tests/images/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,28 @@ def assert_array_equal_or_none(check_value, value):


def assert_equal_image_params(image, data=None, mask=None, uncertainty=None, catalog=None, raw=None, meta={}):
assert_array_equal_or_none(data, image.data)
assert_array_equal_or_none(mask, image.mask)
assert_array_equal_or_none(uncertainty, image.uncertainty)
assert_array_equal_or_none(data, image.safe_data)
assert_array_equal_or_none(mask, image.safe_mask)
assert_array_equal_or_none(uncertainty, image.safe_uncertainty)

if catalog is None:
assert image.catalog is None
assert image.safe_catalog is None
else:
assert_array_equal_or_none(catalog.as_array(), image.catalog.as_array())
assert_array_equal_or_none(raw, image.raw)
assert_array_equal_or_none(raw, image.safe_raw)
assert image.meta == meta


def assert_equal_image(image, other):
assert_equal_image_params(image, other.data, other.mask, other.uncertainty, other.catalog, other.raw, other.meta)
assert_equal_image_params(
image,
other.safe_data,
other.safe_mask,
other.safe_uncertainty,
other.safe_catalog,
other.safe_raw,
other.meta,
)


@pytest.fixture()
Expand Down

0 comments on commit 25372d7

Please sign in to comment.