22 __all__ = [
"MultibandBase"]
24 from abc
import ABC, abstractmethod
30 """Base class for multiband objects 32 The LSST stack has a number of image-like classes that have 33 data in multiple bands that are stored as separate objects. 34 Analyzing the data can be easier using a Multiband object that 35 wraps the underlying data as a single data cube that can be sliced and 36 updated as a single object. 38 `MultibandBase` is designed to contain the most important universal 39 methods for initializing, slicing, and extracting common parameters 40 (such as the bounding box or XY0 position) to all of the single band classes, 41 as long as derived classes either call the base class `__init__` 42 or set the `_filters`, `_singles`, and `_bbox`. 49 List of single band objects 51 By default `MultibandBase` uses `singles[0].getBBox()` to set 52 the bounding box of the multiband 54 def __init__(self, filters, singles, bbox=None):
55 self.
_filters = tuple([f
for f
in filters])
62 err =
"`singles` are required to have the same bounding box, received {0}" 63 raise ValueError(err.format(bboxes))
69 """Copy the current object 71 This must be overloaded in a subclass of `MultibandBase` 76 Whether or not to make a deep copy 80 result: `MultibandBase` 81 copy of the instance that inherits from `MultibandBase` 87 """List of filter names for the single band objects 93 """List of single band objects 103 """Minimum coordinate in the bounding box 111 X component of XY0 `Point2I.getX()` 113 return self.
getBBox().getMinX()
119 Y component of XY0 `Point2I.getY()` 121 return self.
getBBox().getMinY()
125 """Minimum (y,x) position 127 This is the position of `self.getBBox().getMin()`, 128 but available as a tuple for numpy array indexing. 130 return (self.
y0, self.
x0)
134 """Width of the images 136 return self.
getBBox().getWidth()
140 """Height of the images 142 return self.
getBBox().getHeight()
148 """Get a slice of the underlying array 150 If only a single filter is specified, 151 return the single band object sliced 154 if not isinstance(args, tuple):
162 if not isinstance(filterIndex, slice)
and len(filterIndex) == 1:
164 return self.
singles[filterIndex[0]][indices[1:]]
165 elif len(indices) == 2:
166 return self.
singles[filterIndex[0]][indices[1]]
168 return self.
singles[filterIndex[0]]
170 return self.
_slice(filters=filters, filterIndex=filterIndex, indices=indices[1:])
184 def _filterNamesToIndex(self, filterIndex):
185 """Convert a list of filter names to an index or a slice 189 filterIndex: iterable or `object` 190 Index to specify a filter or list of filters, 191 usually a string or enum. 192 For example `filterIndex` can be 193 `"R"` or `["R", "G", "B"]` or `[Filter.R, Filter.G, Filter.B]`,
194 if `Filter` is an enum.
199 Names of the filters in the slice
200 filterIndex: `slice` or `list` of `int`
201 Index of each filter in `filterNames` in
204 if isinstance(filterIndex, slice):
205 if filterIndex.start is not None:
206 start = self.filters.index(filterIndex.start)
209 if filterIndex.stop is not None:
210 stop = self.filters.index(filterIndex.stop)
213 filterIndices = slice(start, stop, filterIndex.step)
214 filterNames = self.filters[filterIndices]
216 if isinstance(filterIndex, str):
217 filterNames = [filterIndex]
218 filterIndices = [self.filters.index(filterIndex)]
221 # Check to see if the filterIndex is an iterable
222 filterNames = [f for f in filterIndex]
224 filterNames = [filterIndex]
225 filterIndices = [self.filters.index(f) for f in filterNames]
226 return tuple(filterNames), filterIndices
228 def setXY0(self, xy0):
229 """Shift the bounding box but keep the same Extent
234 New minimum bounds of the bounding box
236 self._bbox = Box2I(xy0, self._bbox.getDimensions())
237 for singleObj in self.singles:
238 singleObj.setXY0(xy0)
240 def shiftedTo(self, xy0):
241 """Shift the bounding box but keep the same Extent
243 This method is broken until DM-10781 is completed.
248 New minimum bounds of the bounding box
252 result: `MultibandBase`
253 A copy of the object, shifted to `xy0`.
255 raise NotImplementedError("shiftedBy not implemented until DM-10781")
256 result = self.clone(False)
257 result._bbox = Box2I(xy0, result._bbox.getDimensions())
258 for singleObj in result.singles:
259 singleObj.setXY0(xy0)
262 def shiftedBy(self, offset):
263 """Shift a bounding box by an offset, but keep the same Extent
265 This method is broken until DM-10781 is completed.
270 Amount to shift the bounding box in x and y.
274 result: `MultibandBase`
275 A copy of the object, shifted by `offset`
277 raise NotImplementedError("shiftedBy not implemented until DM-10781")
278 xy0 = self._bbox.getMin() + offset
279 return self.shiftedTo(xy0)
282 def _slice(self, filters, filterIndex, indices):
283 """Slice the current object and return the result
285 Different inherited classes will handling slicing differently,
286 so this method must be overloaded in inherited classes.
290 filters: `list` of `str`
291 List of filter names for the slice. This is a subset of the
292 filters in the parent multiband object
293 filterIndex: `list` of `int` or `slice`
294 Index along the filter dimension
295 indices: `tuple` of remaining indices
296 `MultibandBase.__getitem__` separates the first (filter)
297 index from the remaining indices, so `indices` is a tuple
298 of all of the indices that come after `filter` in the
299 `args` passed to `MultibandBase.__getitem__`.
304 Sliced version of the current object, which could be the
305 same class or a different class depending on the
311 result = "<{0}, filters={1}, bbox={2}>".format(
312 self.__class__.__name__, self.filters, self.getBBox().__repr__())
316 if hasattr(self, "array"):
317 return str(self.array)
318 return self.__repr__()
def _filterNamesToIndex(self, filterIndex)
bool all(CoordinateExpr< N > const &expr) noexcept
Return true if all elements are true.
def _slice(self, filters, filterIndex, indices)
def __getitem__(self, args)
def clone(self, deep=True)
def __init__(self, filters, singles, bbox=None)