Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add set_bounding_box to models #1905

Merged
merged 3 commits into from
Oct 3, 2024
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
171 changes: 116 additions & 55 deletions photutils/psf/functional_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@
theta=theta.default, **kwargs):
super().__init__(flux=flux, x_0=x_0, y_0=y_0, x_fwhm=x_fwhm,
y_fwhm=y_fwhm, theta=theta, **kwargs)
self.set_bounding_box()

@property
def amplitude(self):
Expand All @@ -181,9 +182,9 @@
"""
return self.y_fwhm * GAUSSIAN_FWHM_TO_SIGMA

def bounding_box(self, factor=5.5):
def set_bounding_box(self, factor=5.5):
"""
Return a bounding box defining the limits of the model.
Set a bounding box defining the limits of the model.

The limits are adjusted for rotation.

Expand All @@ -193,20 +194,25 @@
The multiple of the x and y standard deviations (sigma) used
to define the limits.

Returns
-------
bounding_box : `astropy.modeling.bounding_box.ModelBoundingBox`
A bounding box defining the limits of the model.

Examples
--------
>>> from photutils.psf import GaussianPSF
>>> model = GaussianPSF(x_0=0, y_0=0, x_fwhm=2, y_fwhm=3)
>>> model.bounding_box # doctest: +FLOAT_CMP
ModelBoundingBox(
intervals={
x: Interval(lower=-4.6712699, upper=4.6712699)
y: Interval(lower=-7.0069049, upper=7.0069048)
x: Interval(lower=-4.671269901584105, upper=4.671269901584105)
y: Interval(lower=-7.006904852376157, upper=7.006904852376157)
}
model=GaussianPSF(inputs=('x', 'y'))
order='C'
)
>>> model.set_bounding_box(factor=7)
>>> model.bounding_box # doctest: +FLOAT_CMP
ModelBoundingBox(
intervals={
x: Interval(lower=-5.945252602016134, upper=5.945252602016134)
y: Interval(lower=-8.9178789030242, upper=8.9178789030242)
}
model=GaussianPSF(inputs=('x', 'y'))
order='C'
Expand All @@ -215,9 +221,8 @@
a = factor * self.x_sigma
b = factor * self.y_sigma
dx, dy = ellipse_extent(a, b, self.theta)

return ((self.y_0 - dy, self.y_0 + dy),
(self.x_0 - dx, self.x_0 + dx))
bbox = ((self.y_0 - dy, self.y_0 + dy), (self.x_0 - dx, self.x_0 + dx))
self.bounding_box = bbox

def evaluate(self, x, y, flux, x_0, y_0, x_fwhm, y_fwhm, theta):
"""
Expand Down Expand Up @@ -489,6 +494,7 @@
def __init__(self, *, flux=flux.default, x_0=x_0.default, y_0=y_0.default,
fwhm=fwhm.default, **kwargs):
super().__init__(flux=flux, x_0=x_0, y_0=y_0, fwhm=fwhm, **kwargs)
self.set_bounding_box()

@property
def amplitude(self):
Expand All @@ -504,9 +510,9 @@
"""
return self.fwhm * GAUSSIAN_FWHM_TO_SIGMA

def bounding_box(self, factor=5.5):
def set_bounding_box(self, factor=5.5):
"""
Return a bounding box defining the limits of the model.
Set a bounding box defining the limits of the model.

Parameters
----------
Expand All @@ -526,16 +532,27 @@
>>> model.bounding_box # doctest: +FLOAT_CMP
ModelBoundingBox(
intervals={
x: Interval(lower=-4.6712699, upper=4.6712699)
y: Interval(lower=-4.6712699, upper=4.6712699)
x: Interval(lower=-4.671269901584105, upper=4.671269901584105)
y: Interval(lower=-4.671269901584105, upper=4.671269901584105)
}
model=CircularGaussianPSF(inputs=('x', 'y'))
order='C'
)
>>> model.set_bounding_box(factor=7)
>>> model.bounding_box # doctest: +FLOAT_CMP
ModelBoundingBox(
intervals={
x: Interval(lower=-5.945252602016134, upper=5.945252602016134)
y: Interval(lower=-5.945252602016134, upper=5.945252602016134)
}
model=CircularGaussianPSF(inputs=('x', 'y'))
order='C'
)
"""
delta = factor * self.sigma
return ((self.y_0 - delta, self.y_0 + delta),
bbox = ((self.y_0 - delta, self.y_0 + delta),
(self.x_0 - delta, self.x_0 + delta))
self.bounding_box = bbox

def evaluate(self, x, y, flux, x_0, y_0, fwhm):
"""
Expand Down Expand Up @@ -763,6 +780,7 @@

super().__init__(flux=flux, x_0=x_0, y_0=y_0, x_fwhm=x_fwhm,
y_fwhm=y_fwhm, theta=theta, **kwargs)
self.set_bounding_box()

@property
def amplitude(self):
Expand All @@ -785,9 +803,9 @@
"""
return self.y_fwhm * GAUSSIAN_FWHM_TO_SIGMA

def bounding_box(self, factor=5.5):
def set_bounding_box(self, factor=5.5):
"""
Return a bounding box defining the limits of the model.
Set a bounding box defining the limits of the model.

The limits are adjusted for rotation.

Expand All @@ -809,8 +827,18 @@
>>> model.bounding_box # doctest: +FLOAT_CMP
ModelBoundingBox(
intervals={
x: Interval(lower=-4.6712699, upper=4.6712699)
y: Interval(lower=-7.0069049, upper=7.0069048)
x: Interval(lower=-4.671269901584105, upper=4.671269901584105)
y: Interval(lower=-7.006904852376157, upper=7.006904852376157)
}
model=GaussianPRF(inputs=('x', 'y'))
order='C'
)
>>> model.set_bounding_box(factor=7)
>>> model.bounding_box # doctest: +FLOAT_CMP
ModelBoundingBox(
intervals={
x: Interval(lower=-5.945252602016134, upper=5.945252602016134)
y: Interval(lower=-8.9178789030242, upper=8.9178789030242)
}
model=GaussianPRF(inputs=('x', 'y'))
order='C'
Expand All @@ -819,9 +847,8 @@
a = factor * self.x_fwhm * GAUSSIAN_FWHM_TO_SIGMA
b = factor * self.y_fwhm * GAUSSIAN_FWHM_TO_SIGMA
dx, dy = ellipse_extent(a, b, self.theta)

return ((self.y_0 - dy, self.y_0 + dy),
(self.x_0 - dx, self.x_0 + dx))
bbox = ((self.y_0 - dy, self.y_0 + dy), (self.x_0 - dx, self.x_0 + dx))
self.bounding_box = bbox

def evaluate(self, x, y, flux, x_0, y_0, x_fwhm, y_fwhm, theta):
"""
Expand Down Expand Up @@ -1010,6 +1037,7 @@
fwhm=fwhm.default, **kwargs):

super().__init__(flux=flux, x_0=x_0, y_0=y_0, fwhm=fwhm, **kwargs)
self.set_bounding_box()

@property
def amplitude(self):
Expand All @@ -1025,9 +1053,9 @@
"""
return self.fwhm * GAUSSIAN_FWHM_TO_SIGMA

def bounding_box(self, factor=5.5):
def set_bounding_box(self, factor=5.5):
"""
Return a bounding box defining the limits of the model.
Set a bounding box defining the limits of the model.

Parameters
----------
Expand All @@ -1047,16 +1075,27 @@
>>> model.bounding_box # doctest: +FLOAT_CMP
ModelBoundingBox(
intervals={
x: Interval(lower=-4.6712699, upper=4.6712699)
y: Interval(lower=-4.6712699, upper=4.6712699)
x: Interval(lower=-4.671269901584105, upper=4.671269901584105)
y: Interval(lower=-4.671269901584105, upper=4.671269901584105)
}
model=CircularGaussianPRF(inputs=('x', 'y'))
order='C'
)
>>> model.set_bounding_box(factor=7)
>>> model.bounding_box # doctest: +FLOAT_CMP
ModelBoundingBox(
intervals={
x: Interval(lower=-5.945252602016134, upper=5.945252602016134)
y: Interval(lower=-5.945252602016134, upper=5.945252602016134)
}
model=CircularGaussianPRF(inputs=('x', 'y'))
order='C'
)
"""
delta = factor * self.fwhm * GAUSSIAN_FWHM_TO_SIGMA
return ((self.y_0 - delta, self.y_0 + delta),
bbox = ((self.y_0 - delta, self.y_0 + delta),
(self.x_0 - delta, self.x_0 + delta))
self.bounding_box = bbox

def evaluate(self, x, y, flux, x_0, y_0, fwhm):
"""
Expand Down Expand Up @@ -1225,6 +1264,7 @@
sigma=sigma.default, **kwargs):

super().__init__(sigma=sigma, x_0=x_0, y_0=y_0, flux=flux, **kwargs)
self.set_bounding_box()

@property
def amplitude(self):
Expand All @@ -1240,9 +1280,9 @@
"""
return self.sigma / GAUSSIAN_FWHM_TO_SIGMA

def bounding_box(self, factor=5.5):
def set_bounding_box(self, factor=5.5):
"""
Return a bounding box defining the limits of the model.
Set a bounding box defining the limits of the model.

Parameters
----------
Expand All @@ -1258,35 +1298,31 @@
Examples
--------
>>> from photutils.psf import CircularGaussianPRF
>>> model = CircularGaussianPRF(x_0=0, y_0=0, sigma=2)
>>> model.bounding_box
>>> model = CircularGaussianPRF(x_0=0, y_0=0, fwhm=2)
>>> model.bounding_box # doctest: +FLOAT_CMP
ModelBoundingBox(
intervals={
x: Interval(lower=-11.0, upper=11.0)
y: Interval(lower=-11.0, upper=11.0)
x: Interval(lower=-4.671269901584105, upper=4.671269901584105)
y: Interval(lower=-4.671269901584105, upper=4.671269901584105)
}
model=CircularGaussianPRF(inputs=('x', 'y'))
order='C'
)

This range can be set directly (see: `Model.bounding_box
<astropy.modeling.Model.bounding_box>`) or by using a different
factor, like:

>>> model.bounding_box = model.bounding_box(factor=2)
>>> model.bounding_box
>>> model.set_bounding_box(factor=7)
>>> model.bounding_box # doctest: +FLOAT_CMP
ModelBoundingBox(
intervals={
x: Interval(lower=-4.0, upper=4.0)
y: Interval(lower=-4.0, upper=4.0)
x: Interval(lower=-5.945252602016134, upper=5.945252602016134)
y: Interval(lower=-5.945252602016134, upper=5.945252602016134)
}
model=CircularGaussianPRF(inputs=('x', 'y'))
order='C'
)
"""
delta = factor * self.fwhm * GAUSSIAN_FWHM_TO_SIGMA
return ((self.y_0 - delta, self.y_0 + delta),
bbox = ((self.y_0 - delta, self.y_0 + delta),
(self.x_0 - delta, self.x_0 + delta))
self.bounding_box = bbox

def evaluate(self, x, y, flux, x_0, y_0, sigma):
"""
Expand Down Expand Up @@ -1438,6 +1474,7 @@
sigma=sigma.default, **kwargs):

super().__init__(sigma=sigma, x_0=x_0, y_0=y_0, flux=flux, **kwargs)
self.set_bounding_box()

Check warning on line 1477 in photutils/psf/functional_models.py

View check run for this annotation

Codecov / codecov/patch

photutils/psf/functional_models.py#L1477

Added line #L1477 was not covered by tests


class MoffatPSF(Fittable2DModel):
Expand Down Expand Up @@ -1555,6 +1592,7 @@

super().__init__(flux=flux, x_0=x_0, y_0=y_0, alpha=alpha, beta=beta,
**kwargs)
self.set_bounding_box()

@property
def fwhm(self):
Expand All @@ -1563,9 +1601,9 @@
"""
return 2.0 * self.alpha * np.sqrt(2 ** (1.0 / self.beta) - 1)

def bounding_box(self, factor=10.0):
def set_bounding_box(self, factor=10.0):
"""
Return a bounding box defining the limits of the model.
Set a bounding box defining the limits of the model.

Parameters
----------
Expand All @@ -1584,16 +1622,27 @@
>>> model.bounding_box # doctest: +FLOAT_CMP
ModelBoundingBox(
intervals={
x: Interval(lower=-20.3929811, upper=20.3929811)
y: Interval(lower=-20.3929811, upper=20.3929811)
x: Interval(lower=-20.39298114135835, upper=20.39298114135835)
y: Interval(lower=-20.39298114135835, upper=20.39298114135835)
}
model=MoffatPSF(inputs=('x', 'y'))
order='C'
)
>>> model.set_bounding_box(factor=7)
>>> model.bounding_box # doctest: +FLOAT_CMP
ModelBoundingBox(
intervals={
x: Interval(lower=-14.27508679895084, upper=14.27508679895084)
y: Interval(lower=-14.27508679895084, upper=14.27508679895084)
}
model=MoffatPSF(inputs=('x', 'y'))
order='C'
)
"""
delta = factor * self.fwhm
return ((self.y_0 - delta, self.y_0 + delta),
bbox = ((self.y_0 - delta, self.y_0 + delta),
(self.x_0 - delta, self.x_0 + delta))
self.bounding_box = bbox

def evaluate(self, x, y, flux, x_0, y_0, alpha, beta):
"""
Expand Down Expand Up @@ -1775,6 +1824,7 @@
radius=radius.default, **kwargs):

super().__init__(flux=flux, x_0=x_0, y_0=y_0, radius=radius, **kwargs)
self.set_bounding_box()

@property
def fwhm(self):
Expand All @@ -1783,9 +1833,9 @@
"""
return 2.0 * 1.616339948310703 * self.radius / self._rz / np.pi

def bounding_box(self, factor=10.0):
def set_bounding_box(self, factor=10.0):
"""
Return a bounding box defining the limits of the model.
Set a bounding box defining the limits of the model.

Parameters
----------
Expand All @@ -1804,16 +1854,27 @@
>>> model.bounding_box # doctest: +FLOAT_CMP
ModelBoundingBox(
intervals={
x: Interval(lower=-25.30997880, upper=25.30997880)
y: Interval(lower=-25.30997880, upper=25.30997880)
x: Interval(lower=-25.30997880648709, upper=25.30997880648709)
y: Interval(lower=-25.30997880648709, upper=25.30997880648709)
}
model=AiryDiskPSF(inputs=('x', 'y'))
order='C'
)
>>> model.set_bounding_box(factor=7)
>>> model.bounding_box # doctest: +FLOAT_CMP
ModelBoundingBox(
intervals={
x: Interval(lower=-17.71698516454096, upper=17.71698516454096)
y: Interval(lower=-17.71698516454096, upper=17.71698516454096)
}
model=AiryDiskPSF(inputs=('x', 'y'))
order='C'
)
"""
delta = factor * self.fwhm
return ((self.y_0 - delta, self.y_0 + delta),
bbox = ((self.y_0 - delta, self.y_0 + delta),
(self.x_0 - delta, self.x_0 + delta))
self.bounding_box = bbox

def evaluate(self, x, y, flux, x_0, y_0, radius):
"""
Expand Down
Loading