24 from deprecated.sphinx
import deprecated
28 from .rgb
import replaceSaturatedPixels, getZScale
32 """Return a naive total intensity from the red, blue, and green intensities
36 imageR : `lsst.afw.image.MaskedImage`, `lsst.afw.image.Image`, or `numpy.ndarray`, (Nx, Ny)
37 intensity of image that'll be mapped to red; or intensity if imageG and imageB are None
38 imageG : `lsst.afw.image.MaskedImage`, `lsst.afw.image.Image`, or `numpy.ndarray`, (Nx, Ny)
39 intensity of image that'll be mapped to green; or None
40 imageB : `lsst.afw.image.MaskedImage`, `lsst.afw.image.Image`, or `numpy.ndarray`, (Nx, Ny)
41 intensity of image that'll be mapped to blue; or None
45 image : type of ``imageR``, ``imageG``, and `imageB``
47 if imageG
is None or imageB
is None:
48 assert imageG
is None and imageB
is None, \
49 "Please specify either a single image or red, green, and blue images"
52 imageRGB = [imageR, imageG, imageB]
54 for i, c
in enumerate(imageRGB):
55 if hasattr(c,
"getImage"):
56 c = imageRGB[i] = c.getImage()
57 if hasattr(c,
"getArray"):
58 imageRGB[i] = c.getArray()
60 intensity = (imageRGB[0] + imageRGB[1] + imageRGB[2])/float(3)
64 Image = afwImage.ImageU
if intensity.dtype ==
'uint16' else afwImage.ImageF
66 if hasattr(imageR,
"getImage"):
68 elif hasattr(imageR,
"getArray"):
69 intensity = Image(intensity)
75 """Base class to map red, blue, green intensities into uint8 values
79 minimum : `float` or sequence of `float`
80 Intensity that should be mapped to black. If an array, has three
83 The image to be used to calculate the mapping.
84 If provided, also the default for makeRgbImage()
88 self.
_uint8Max = float(np.iinfo(np.uint8).max)
94 assert len(minimum) == 3,
"Please provide 1 or 3 values for minimum"
100 xSize=None, ySize=None, rescaleFactor=None):
101 """Convert 3 arrays, imageR, imageG, and imageB into a numpy RGB image
103 imageR : `lsst.afw.image.Image` or `numpy.ndarray`, (Nx, Ny)
104 Image to map to red (if `None`, use the image passed to the ctor)
105 imageG : `lsst.afw.image.Image` or `numpy.ndarray`, (Nx, Ny), optional
106 Image to map to green (if `None`, use imageR)
107 imageB : `lsst.afw.image.Image` or `numpy.ndarray`, (Nx, Ny), optional
108 Image to map to blue (if `None`, use imageR)
109 xSize : `int`, optional
110 Desired width of RGB image. If ``ySize`` is `None`, preserve aspect ratio
111 ySize : `int`, optional
112 Desired height of RGB image
113 rescaleFactor : `float`, optional
114 Make size of output image ``rescaleFactor*size`` of the input image
119 "You must provide an image (or pass one to the constructor)")
127 imageRGB = [imageR, imageG, imageB]
128 for i, c
in enumerate(imageRGB):
129 if hasattr(c,
"getImage"):
130 c = imageRGB[i] = c.getImage()
131 if hasattr(c,
"getArray"):
132 imageRGB[i] = c.getArray()
134 if xSize
is not None or ySize
is not None:
135 assert rescaleFactor
is None,
"You may not specify a size and rescaleFactor"
136 h, w = imageRGB[0].shape
138 ySize = int(xSize*h/float(w) + 0.5)
140 xSize = int(ySize*w/float(h) + 0.5)
142 size = (ySize, xSize)
143 elif rescaleFactor
is not None:
144 size = float(rescaleFactor)
151 except ImportError
as e:
153 f
"Unable to rescale as scipy.misc is unavailable: {e}")
155 for i, im
in enumerate(imageRGB):
156 imageRGB[i] = scipy.misc.imresize(
157 im, size, interp=
'bilinear', mode=
'F')
162 """Return the total intensity from the red, blue, and green intensities
166 This is a naive computation, and may be overridden by subclasses
171 """Map an intensity into the range of a uint8, [0, 255] (but not converted to uint8)
173 with np.errstate(invalid=
'ignore', divide=
'ignore'):
174 return np.where(intensity <= 0, 0,
177 def _convertImagesToUint8(self, imageR, imageG, imageB):
178 """Use the mapping to convert images imageR, imageG, and imageB to a triplet of uint8 images
180 imageR = imageR - self.
minimum[0]
181 imageG = imageG - self.
minimum[1]
182 imageB = imageB - self.
minimum[2]
186 imageRGB = [imageR, imageG, imageB]
187 with np.errstate(invalid=
"ignore"):
195 r0, g0, b0 = imageRGB
198 with np.errstate(invalid=
'ignore', divide=
'ignore'):
199 for i, c
in enumerate(imageRGB):
200 c = np.where(r0 > g0,
202 np.where(r0 >= pixmax, c*pixmax/r0, c),
203 np.where(b0 >= pixmax, c*pixmax/b0, c)),
205 np.where(g0 >= pixmax, c*pixmax/g0, c),
206 np.where(b0 >= pixmax, c*pixmax/b0, c))).astype(np.uint8)
207 c[c > pixmax] = pixmax
215 """A linear map of red, blue, green intensities into uint8 values
219 minimum : `float` or sequence of `float`
220 Intensity that should be mapped to black. If an array, has three
221 elements for R, G, B.
223 Intensity that should be mapped to white
225 Image to estimate minimum/maximum if not explicitly set
228 def __init__(self, minimum=None, maximum=None, image=None):
229 if minimum
is None or maximum
is None:
230 assert image
is not None,
"You must provide an image if you don't set both minimum and maximum"
234 minimum = stats.getValue(afwMath.MIN)
236 maximum = stats.getValue(afwMath.MAX)
238 Mapping.__init__(self, minimum, image)
244 assert maximum - minimum != 0,
"minimum and maximum values must not be equal"
245 self.
_range = float(maximum - minimum)
248 """Return an array which, when multiplied by an image, returns that
249 image mapped to the range of a uint8, [0, 255] (but not converted to uint8)
251 The intensity is assumed to have had ``minimum`` subtracted (as that
252 can be done per-band)
254 with np.errstate(invalid=
'ignore', divide=
'ignore'):
255 return np.where(intensity <= 0, 0,
256 np.where(intensity >= self.
_range,
261 """A mapping for a linear stretch chosen by the zscale algorithm
262 (preserving colours independent of brightness)
264 x = (I - minimum)/range
269 Image whose parameters are desired
271 The number of samples to use to estimate the zscale parameters
275 def __init__(self, image, nSamples=1000, contrast=0.25):
276 if not hasattr(image,
"getArray"):
277 image = afwImage.ImageF(image)
278 z1, z2 =
getZScale(image, nSamples, contrast)
280 LinearMapping.__init__(self, z1, z2, image)
284 """A mapping for an asinh stretch (preserving colours independent of brightness)
286 x = asinh(Q (I - minimum)/range)/Q
290 This reduces to a linear stretch if Q == 0
292 See http://adsabs.harvard.edu/abs/2004PASP..116..133L
296 Mapping.__init__(self, minimum)
313 self.
_soften = Q/float(dataRange)
316 """Return an array which, when multiplied by an image, returns that image mapped to the range of a
317 uint8, [0, 255] (but not converted to uint8)
319 The intensity is assumed to have had minimum subtracted (as that can be done per-band)
321 with np.errstate(invalid=
'ignore', divide=
'ignore'):
322 return np.where(intensity <= 0, 0, np.arcsinh(intensity*self.
_soften)*self.
_slope/intensity)
326 """A mapping for an asinh stretch, estimating the linear stretch by zscale
328 x = asinh(Q (I - z1)/(z2 - z1))/Q
333 The image to analyse, or a list of 3 images to be converted to an intensity image
335 The asinh softening parameter
336 pedestal : `float` or sequence of `float`, optional
337 The value, or array of 3 values, to subtract from the images
339 N.b. pedestal, if not None, is removed from the images when calculating the zscale
340 stretch, and added back into Mapping.minimum[]
349 assert len(image)
in (1, 3,),
"Please provide 1 or 3 images"
353 if pedestal
is not None:
355 assert len(pedestal)
in (
356 1, 3,),
"Please provide 1 or 3 pedestals"
358 pedestal = 3*[pedestal]
361 for i, im
in enumerate(image):
362 if pedestal[i] != 0.0:
363 if hasattr(im,
"getImage"):
365 if hasattr(im,
"getArray"):
368 image[i] = im - pedestal[i]
370 pedestal = len(image)*[0.0]
376 dataRange = zscale.maximum - zscale.minimum[0]
377 minimum = zscale.minimum
379 for i, level
in enumerate(pedestal):
382 AsinhMapping.__init__(self, minimum, dataRange, Q)
386 def makeRGB(imageR, imageG=None, imageB=None, minimum=0, dataRange=5, Q=8, fileName=None,
387 saturatedBorderWidth=0, saturatedPixelValue=None,
388 xSize=None, ySize=None, rescaleFactor=None):
389 """Make a set of three images into an RGB image using an asinh stretch and
390 optionally write it to disk
397 minimum : `float` or sequence of `float`
401 The output file. The suffix defines the format, and must be supported by matplotlib
403 If saturatedBorderWidth is non-zero, replace saturated pixels with
404 ``saturatedPixelValue``. Note that replacing saturated pixels requires
405 that the input images be `lsst.afw.image.MaskedImage`.
416 if saturatedBorderWidth:
417 if saturatedPixelValue
is None:
419 "saturatedPixelValue must be set if saturatedBorderWidth is set")
421 saturatedBorderWidth, saturatedPixelValue)
424 rgb = asinhMap.makeRgbImage(imageR, imageG, imageB,
425 xSize=xSize, ySize=ySize, rescaleFactor=rescaleFactor)
434 """Display an rgb image using matplotlib
439 The RGB image in question
441 If `True`, call `matplotlib.pyplot.show()`
443 import matplotlib.pyplot
as plt
444 plt.imshow(rgb, interpolation=
'nearest', origin=
"lower")
451 """Write an RGB image to disk
456 The output file. The suffix defines the format, and must be supported by matplotlib
458 Most versions of matplotlib support png and pdf (although the eps/pdf/svg writers may be buggy,
459 possibly due an interaction with useTeX=True in the matplotlib settings).
461 If your matplotlib bundles pil/pillow you should also be able to write jpeg and tiff files.
463 The image, as made by e.g. makeRGB
465 import matplotlib.image
466 matplotlib.image.imsave(fileName, rgbImage)
473 @deprecated(reason=
"Use `AsinhMapping` instead. To be removed after 20.0.0.",
474 category=FutureWarning)
476 """Deprecated object used to support legacy API
486 """Deprecated object used to support legacy API
489 def __init__(self, imageR, imageG, imageB, mapping):
490 asinh =
AsinhMapping(mapping.minimum, mapping.dataRange, mapping.Q)
491 self.
rgb = asinh.makeRgbImage(imageR, imageG, imageB)
498 reason=
"Use `Mapping.makeRgbImage` instead. To be removed after 20.0.0.",
499 category=FutureWarning)
501 """Deprecated legacy API
503 return _RgbImageF(imageR, imageG, imageB, mapping)