@@ -216,7 +216,8 @@ def __call__(self, image=None, trace_object=None, width=None,
216216
217217 # extract
218218 ext1d = np .sum (self .image .data * wimg , axis = crossdisp_axis )
219- return _to_spectrum1d_pixels (ext1d * self .image .unit )
219+ return Spectrum1D (ext1d * self .image .unit ,
220+ spectral_axis = self .image .spectral_axis )
220221
221222
222223@dataclass
@@ -280,50 +281,86 @@ class HorneExtract(SpecreduceOperation):
280281 def spectrum (self ):
281282 return self .__call__ ()
282283
283- def _parse_image (self , variance = None , mask = None , unit = None ):
284+ def _parse_image (self , image ,
285+ variance = None , mask = None , unit = None , disp_axis = 1 ):
284286 """
285- Convert all accepted image types to a consistently formatted Spectrum1D.
286- Takes some extra arguments exactly as they come from self.__call__() to
287- handle cases where users specify them as arguments instead of as
288- attributes of their image object.
287+ Convert all accepted image types to a consistently formatted
288+ Spectrum1D object.
289+
290+ HorneExtract needs its own version of this method because it is
291+ more stringent in its requirements for input images. The extra
292+ arguments are needed to handle cases where these parameters were
293+ specified as arguments and those where they came as attributes
294+ of the image object.
295+
296+ Parameters
297+ ----------
298+ image : `~astropy.nddata.NDData`-like or array-like, required
299+ The image to be parsed. If None, defaults to class' own
300+ image attribute.
301+ variance : `~numpy.ndarray`, optional
302+ (Only used if ``image`` is not an NDData object.)
303+ The associated variances for each pixel in the image. Must
304+ have the same dimensions as ``image``. If all zeros, the variance
305+ will be ignored and treated as all ones. If any zeros, those
306+ elements will be excluded via masking. If any negative values,
307+ an error will be raised.
308+ mask : `~numpy.ndarray`, optional
309+ (Only used if ``image`` is not an NDData object.)
310+ Whether to mask each pixel in the image. Must have the same
311+ dimensions as ``image``. If blank, all non-NaN pixels are
312+ unmasked.
313+ unit : `~astropy.units.Unit` or str, optional
314+ (Only used if ``image`` is not an NDData object.)
315+ The associated unit for the data in ``image``. If blank,
316+ fluxes are interpreted as unitless.
317+ disp_axis : int, optional
318+ The index of the image's dispersion axis. Should not be
319+ changed until operations can handle variable image
320+ orientations. [default: 1]
289321 """
290322
291- if isinstance (self . image , np .ndarray ):
292- img = self . image
293- elif isinstance (self . image , u .quantity .Quantity ):
294- img = self . image .value
323+ if isinstance (image , np .ndarray ):
324+ img = image
325+ elif isinstance (image , u .quantity .Quantity ):
326+ img = image .value
295327 else : # NDData, including CCDData and Spectrum1D
296- img = self . image .data
328+ img = image .data
297329
298330 # mask is set as None when not specified upon creating a Spectrum1D
299331 # object, so we must check whether it is absent *and* whether it's
300332 # present but set as None
301- if getattr (self .image , 'mask' , None ) is not None :
302- mask = self .image .mask
333+ if getattr (image , 'mask' , None ) is not None :
334+ mask = image .mask
335+ elif mask is not None :
336+ pass
303337 else :
304338 mask = np .ma .masked_invalid (img ).mask
305339
340+ if img .shape != mask .shape :
341+ raise ValueError ('image and mask shapes must match.' )
342+
306343 # Process uncertainties, converting to variances when able and throwing
307344 # an error when uncertainties are missing or less easily converted
308- if (hasattr (self . image , 'uncertainty' )
309- and self . image .uncertainty is not None ):
310- if self . image .uncertainty .uncertainty_type == 'var' :
311- variance = self . image .uncertainty .array
312- elif self . image .uncertainty .uncertainty_type == 'std' :
345+ if (hasattr (image , 'uncertainty' )
346+ and image .uncertainty is not None ):
347+ if image .uncertainty .uncertainty_type == 'var' :
348+ variance = image .uncertainty .array
349+ elif image .uncertainty .uncertainty_type == 'std' :
313350 warnings .warn ("image NDData object's uncertainty "
314351 "interpreted as standard deviation. if "
315352 "incorrect, use VarianceUncertainty when "
316353 "assigning image object's uncertainty." )
317- variance = self . image .uncertainty .array ** 2
318- elif self . image .uncertainty .uncertainty_type == 'ivar' :
319- variance = 1 / self . image .uncertainty .array
354+ variance = image .uncertainty .array ** 2
355+ elif image .uncertainty .uncertainty_type == 'ivar' :
356+ variance = 1 / image .uncertainty .array
320357 else :
321- # other options are InverseVariance and UnknownVariance
358+ # other options are InverseUncertainty and UnknownUncertainty
322359 raise ValueError ("image NDData object has unexpected "
323360 "uncertainty type. instead, try "
324361 "VarianceUncertainty or StdDevUncertainty." )
325- elif (hasattr (self . image , 'uncertainty' )
326- and self . image .uncertainty is None ):
362+ elif (hasattr (image , 'uncertainty' )
363+ and image .uncertainty is None ):
327364 # ignore variance arg to focus on updating NDData object
328365 raise ValueError ('image NDData object lacks uncertainty' )
329366 else :
@@ -332,7 +369,7 @@ def _parse_image(self, variance=None, mask=None, unit=None):
332369 "variance must be specified. consider "
333370 "wrapping it into one object by instead "
334371 "passing an NDData image." )
335- elif self . image .shape != variance .shape :
372+ elif image .shape != variance .shape :
336373 raise ValueError ("image and variance shapes must match" )
337374
338375 if np .any (variance < 0 ):
@@ -349,16 +386,14 @@ def _parse_image(self, variance=None, mask=None, unit=None):
349386
350387 variance = VarianceUncertainty (variance )
351388
352- unit = getattr (self . image , 'unit' ,
353- u .Unit (self . unit ) if self . unit is not None else u .Unit ())
389+ unit = getattr (image , 'unit' ,
390+ u .Unit (unit ) if unit is not None else u .Unit ())
354391
355- spectral_axis = getattr (self .image , 'spectral_axis' ,
356- (np .arange (img .shape [self .disp_axis ])
357- if hasattr (self , 'disp_axis' )
358- else np .arange (img .shape [1 ])) * u .pix )
392+ spectral_axis = getattr (image , 'spectral_axis' ,
393+ np .arange (img .shape [disp_axis ]) * u .pix )
359394
360- self . image = Spectrum1D (img * unit , spectral_axis = spectral_axis ,
361- uncertainty = variance , mask = mask )
395+ return Spectrum1D (img * unit , spectral_axis = spectral_axis ,
396+ uncertainty = variance , mask = mask )
362397
363398 def __call__ (self , image = None , trace_object = None ,
364399 disp_axis = None , crossdisp_axis = None ,
@@ -423,7 +458,7 @@ def __call__(self, image=None, trace_object=None,
423458 unit = unit if unit is not None else self .unit
424459
425460 # parse image and replace optional arguments with updated values
426- self ._parse_image (variance , mask , unit )
461+ self .image = self . _parse_image (image , variance , mask , unit , disp_axis )
427462 variance = self .image .uncertainty .array
428463 unit = self .image .unit
429464
0 commit comments