lsst.afw
g3a5ebb7d8a+28b83bf6a5
Toggle main menu visibility
Loading...
Searching...
No Matches
python
lsst
afw
image
_image
_slicing.py
Go to the documentation of this file.
1
# This file is part of afw.
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
# This program is free software: you can redistribute it and/or modify
10
# it under the terms of the GNU General Public License as published by
11
# the Free Software Foundation, either version 3 of the License, or
12
# (at your option) any later version.
13
#
14
# This program is distributed in the hope that it will be useful,
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
# GNU General Public License for more details.
18
#
19
# You should have received a copy of the GNU General Public License
20
# along with this program. If not, see <https://www.gnu.org/licenses/>.
21
22
from
lsst.geom
import
Point2I, Box2I, Extent2I
23
from
._imageLib
import
LOCAL, PARENT, ImageOrigin
24
25
__all__ = [
"supportSlicing"
]
26
27
28
def
splitSliceArgs
(sliceArgs):
29
"""Separate the actual slice from origin arguments to __getitem__ or
30
__setitem__, using PARENT for the origin if it is not provided.
31
32
Parameter
33
---------
34
sliceArgs : `tuple`, `Box2I`, or `Point2I`
35
The first argument passed to an image-like object's
36
``__getitem__`` or ``__setitem__``.
37
38
Returns
39
-------
40
sliceArgs : `tuple`, `Box2I`, or `Point2I`
41
The original sliceArgs passed in, with any ImageOrigin argument
42
stripped off.
43
origin : `ImageOrigin`
44
Enum value that sets whether or not to consider xy0 in positions.
45
46
See interpretSliceArgs for more information.
47
48
Intended primarily for internal use by `supportSlicing()`.
49
"""
50
defaultOrigin = PARENT
51
try
:
52
if
isinstance(sliceArgs[-1], ImageOrigin):
53
# Args are already a tuple that includes the origin.
54
if
len(sliceArgs) == 2:
55
return
sliceArgs[0], sliceArgs[-1]
56
else
:
57
return
sliceArgs[:-1], sliceArgs[-1]
58
else
:
59
# Args are a tuple that does not include the origin; add it to make origin explicit.
60
return
sliceArgs, defaultOrigin
61
except
TypeError:
# Arg is a scalar; return it along with the default origin.
62
return
sliceArgs, defaultOrigin
63
64
65
def
handleNegativeIndex
(index, size, origin, default):
66
"""Handle negative indices passed to image accessors.
67
68
When negative indices are used in LOCAL coordinates, we interpret them as
69
relative to the upper bounds of the array, as in regular negative indexing
70
in Python.
71
72
Using negative indices in PARENT coordinates is not allowed unless passed
73
via a `Point2I` or `Box2I`; the potential for confusion between actual
74
negative indices (when ``xy0 < 0``) and offsets relative to the upper
75
bounds of the array is too great.
76
77
Parameters
78
----------
79
index : `int` or `None`
80
1-d pixel index to interpret, as given by a caller to an image-like
81
object's ``__getitem__`` or ``__setitem__``.
82
size : `int`
83
Size of the image in the dimension corresponding to ``index``.
84
origin : `ImageOrigin`
85
Enum value that sets whether or not to consider xy0 in positions.
86
default : `int`
87
Index to return if `index` is None.
88
89
Returns
90
-------
91
index : `int`
92
If ``origin==PARENT``, either the given ``index`` or ``default``.
93
If ``origin==LOCAL``, an equivalent index guaranteed to be nonnegative.
94
95
Intended primarily for internal use by `supportSlicing()`.
96
"""
97
if
index
is
None
:
98
assert
default
is
not
None
99
return
default
100
if
index < 0:
101
if
origin == LOCAL:
102
index = size + index
103
else
:
104
raise
IndexError(
"Negative indices are not permitted with the PARENT origin. "
105
"Use LOCAL to use negative to index relative to the end, "
106
"and Point2I or Box2I indexing to access negative pixels "
107
"in PARENT coordinates."
)
108
return
index
109
110
111
def
translateSliceArgs
(sliceArgs, bboxGetter):
112
"""Transform a tuple of slice objects into a Box2I, correctly handling negative indices.
113
114
see `interpretSliceArgs` for a description of parameters
115
116
Returns
117
-------
118
box : `Box2I` or `None`
119
A box to use to create a subimage, or None if the slice refers to a
120
scalar.
121
index: `tuple` or `None`
122
An ``(x, y)`` tuple of integers, or None if the slice refers to a
123
box.
124
origin : `ImageOrigin`
125
Enum indicating whether to account for xy0.
126
"""
127
slices, origin =
splitSliceArgs
(sliceArgs)
128
if
isinstance(slices, Point2I):
129
return
None
, slices, origin
130
elif
isinstance(slices, Box2I):
131
return
slices,
None
, origin
132
133
x, y, origin =
interpretSliceArgs
(sliceArgs, bboxGetter)
134
135
if
isinstance(x, slice):
136
assert
isinstance(y, slice)
137
if
x.step
is
not
None
or
y.step
is
not
None
:
138
raise
ValueError(
"Slices with steps are not supported in image indexing."
)
139
begin =
Point2I
(x.start, y.start)
140
end =
Point2I
(x.stop, y.stop)
141
return
Box2I
(begin, end - begin),
None
, origin
142
143
assert
not
isinstance(y, slice)
144
return
None
,
Point2I
(x, y), origin
145
146
147
def
interpretSliceArgs
(sliceArgs, bboxGetter):
148
"""Transform arguments to __getitem__ or __setitem__ to a standard form.
149
150
Parameters
151
----------
152
sliceArgs : `tuple`, `Box2I`, or `Point2I`
153
Slice arguments passed directly to `__getitem__` or `__setitem__`.
154
bboxGetter : callable
155
Callable that accepts an ImageOrigin enum value and returns the
156
appropriate image bounding box. Usually the bound getBBox method
157
of an Image, Mask, or MaskedImage object.
158
159
Returns
160
-------
161
x : int or slice
162
Index or slice in the x dimension
163
y : int or slice
164
Index or slice in the y dimension
165
origin : `ImageOrigin`
166
Either `PARENT` (coordinates respect XY0) or LOCAL
167
(coordinates do not respect XY0)
168
"""
169
slices, origin =
splitSliceArgs
(sliceArgs)
170
if
isinstance(slices, Point2I):
171
return
slices.getX(), slices.getY(), origin
172
elif
isinstance(slices, Box2I):
173
x0 = slices.getMinX()
174
y0 = slices.getMinY()
175
return
slice(x0, x0 + slices.getWidth()), slice(y0, y0 + slices.getHeight()), origin
176
elif
isinstance(slices, slice):
177
if
slices.start
is
not
None
or
slices.stop
is
not
None
or
slices.step
is
not
None
:
178
raise
TypeError(
"Single-dimension slices must not have bounds."
)
179
x = slices
180
y = slices
181
origin = LOCAL
# doesn't matter, as the slices cover the full image
182
else
:
183
x, y = slices
184
185
bbox = bboxGetter(origin)
186
if
isinstance(x, slice):
187
if
isinstance(y, slice):
188
xSlice = slice(
handleNegativeIndex
(x.start, bbox.getWidth(), origin, default=bbox.getBeginX()),
189
handleNegativeIndex
(x.stop, bbox.getWidth(), origin, default=bbox.getEndX()))
190
ySlice = slice(
handleNegativeIndex
(y.start, bbox.getHeight(), origin, default=bbox.getBeginY()),
191
handleNegativeIndex
(y.stop, bbox.getHeight(), origin, default=bbox.getEndY()))
192
return
xSlice, ySlice, origin
193
raise
TypeError(
"Mixed indices of the form (slice, int) are not supported for images."
)
194
195
if
isinstance(y, slice):
196
raise
TypeError(
"Mixed indices of the form (int, slice) are not supported for images."
)
197
x =
handleNegativeIndex
(x, bbox.getWidth(), origin, default=
None
)
198
y =
handleNegativeIndex
(y, bbox.getHeight(), origin, default=
None
)
199
return
x, y, origin
200
201
202
def
imageIndicesToNumpy
(sliceArgs, bboxGetter):
203
"""Convert slicing format to numpy
204
205
LSST `afw` image-like objects use an `[x,y]` coordinate
206
convention, accept `Point2I` and `Box2I`
207
objects for slicing, and slice relative to the
208
bounding box `XY0` location;
209
while python and numpy use the convention `[y,x]`
210
with no `XY0`, so this method converts the `afw`
211
indices or slices into numpy indices or slices
212
213
Parameters
214
----------
215
sliceArgs: `sequence`, `Point2I` or `Box2I`
216
An `(xIndex, yIndex)` pair, or a single `(xIndex,)` tuple,
217
where `xIndex` and `yIndex` can be a `slice` or `int`,
218
or list of `int` objects, and if only a single `xIndex` is
219
given, a `Point2I` or `Box2I`.
220
bboxGetter : callable
221
Callable that accepts an ImageOrigin enum value and returns the
222
appropriate image bounding box. Usually the bound getBBox method
223
of an Image, Mask, or MaskedImage object.
224
225
Returns
226
-------
227
y: `int` or `slice`
228
Index or `slice` in the y dimension
229
x: `int` or `slice`
230
Index or `slice` in the x dimension
231
bbox: `Box2I`
232
Bounding box of the image.
233
If `bbox` is `None` then the result is a point and
234
not a subset of an image.
235
"""
236
# Use a common slicing algorithm as single band images
237
x, y, origin =
interpretSliceArgs
(sliceArgs, bboxGetter)
238
x0 = bboxGetter().getMinX()
239
y0 = bboxGetter().getMinY()
240
241
if
origin == PARENT:
242
if
isinstance(x, slice):
243
assert
isinstance(y, slice)
244
bbox =
Box2I
(
Point2I
(x.start, y.start),
Extent2I
(x.stop-x.start, y.stop-y.start))
245
x = slice(x.start - x0, x.stop - x0)
246
y = slice(y.start - y0, y.stop - y0)
247
else
:
248
x = x - x0
249
y = y - y0
250
bbox =
None
251
return
y, x, bbox
252
elif
origin != LOCAL:
253
raise
ValueError(
"Unrecognized value for origin"
)
254
255
# Use a local bounding box
256
if
isinstance(x, slice):
257
assert
isinstance(y, slice)
258
bbox =
Box2I
(
Point2I
(x.start + x0, y.start + y0),
259
Extent2I
(x.stop-x.start, y.stop-y.start))
260
else
:
261
bbox =
None
262
return
y, x, bbox
263
264
265
def
supportSlicing
(cls):
266
"""Support image slicing
267
"""
268
269
def
Factory
(self, *args, **kwargs):
270
"""Return an object of this type
271
"""
272
return
cls(*args, **kwargs)
273
cls.Factory = Factory
274
275
def
clone
(self):
276
"""Return a deep copy of self"""
277
return
cls(self,
True
)
278
cls.clone = clone
279
280
def
__getitem__(self, imageSlice):
# noqa: N807
281
box, index, origin =
translateSliceArgs
(imageSlice, self.getBBox)
282
if
box
is
not
None
:
283
return
self.subset(box, origin=origin)
284
return
self._get(index, origin=origin)
285
cls.__getitem__ = __getitem__
286
287
def
__setitem__(self, imageSlice, rhs):
# noqa: N807
288
box, index, origin =
translateSliceArgs
(imageSlice, self.getBBox)
289
if
box
is
not
None
:
290
if
self.assign(rhs, box, origin)
is
NotImplemented:
291
lhs = self.subset(box, origin=origin)
292
lhs.set(rhs)
293
else
:
294
self._set(index, origin=origin, value=rhs)
295
cls.__setitem__ = __setitem__
lsst::geom::Box2I
lsst::geom::Extent< int, 2 >
lsst::geom::Point< int, 2 >
lsst::afw::image._image._slicing.interpretSliceArgs
interpretSliceArgs(sliceArgs, bboxGetter)
Definition
_slicing.py:147
lsst::afw::image._image._slicing.clone
clone
Definition
_slicing.py:278
lsst::afw::image._image._slicing.supportSlicing
supportSlicing(cls)
Definition
_slicing.py:265
lsst::afw::image._image._slicing.handleNegativeIndex
handleNegativeIndex(index, size, origin, default)
Definition
_slicing.py:65
lsst::afw::image._image._slicing.imageIndicesToNumpy
imageIndicesToNumpy(sliceArgs, bboxGetter)
Definition
_slicing.py:202
lsst::afw::image._image._slicing.Factory
Factory
Definition
_slicing.py:273
lsst::afw::image._image._slicing.splitSliceArgs
splitSliceArgs(sliceArgs)
Definition
_slicing.py:28
lsst::afw::image._image._slicing.translateSliceArgs
translateSliceArgs(sliceArgs, bboxGetter)
Definition
_slicing.py:111
lsst::geom
Generated on
for lsst.afw by
1.17.0