27 from .rgb
import replaceSaturatedPixels, getZScale
31 """Return a naive total intensity from the red, blue, and green intensities
35 imageR : `lsst.afw.image.MaskedImage`, `lsst.afw.image.Image`, or `numpy.ndarray`, (Nx, Ny)
36 intensity of image that'll be mapped to red; or intensity if imageG and imageB are None
37 imageG : `lsst.afw.image.MaskedImage`, `lsst.afw.image.Image`, or `numpy.ndarray`, (Nx, Ny)
38 intensity of image that'll be mapped to green; or None
39 imageB : `lsst.afw.image.MaskedImage`, `lsst.afw.image.Image`, or `numpy.ndarray`, (Nx, Ny)
40 intensity of image that'll be mapped to blue; or None
44 image : type of ``imageR``, ``imageG``, and `imageB``
46 if imageG
is None or imageB
is None:
47 assert imageG
is None and imageB
is None, \
48 "Please specify either a single image or red, green, and blue images"
51 imageRGB = [imageR, imageG, imageB]
53 for i, c
in enumerate(imageRGB):
54 if hasattr(c,
"getImage"):
55 c = imageRGB[i] = c.getImage()
56 if hasattr(c,
"getArray"):
57 imageRGB[i] = c.getArray()
59 intensity = (imageRGB[0] + imageRGB[1] + imageRGB[2])/float(3)
63 Image = afwImage.ImageU
if intensity.dtype ==
'uint16' else afwImage.ImageF
65 if hasattr(imageR,
"getImage"):
67 elif hasattr(imageR,
"getArray"):
68 intensity = Image(intensity)
74 """Base class to map red, blue, green intensities into uint8 values
78 minimum : `float` or sequence of `float`
79 Intensity that should be mapped to black. If an array, has three
82 The image to be used to calculate the mapping.
83 If provided, also the default for makeRgbImage()
87 self.
_uint8Max_uint8Max = float(np.iinfo(np.uint8).max)
93 assert len(minimum) == 3,
"Please provide 1 or 3 values for minimum"
99 xSize=None, ySize=None, rescaleFactor=None):
100 """Convert 3 arrays, imageR, imageG, and imageB into a numpy RGB image
102 imageR : `lsst.afw.image.Image` or `numpy.ndarray`, (Nx, Ny)
103 Image to map to red (if `None`, use the image passed to the ctor)
104 imageG : `lsst.afw.image.Image` or `numpy.ndarray`, (Nx, Ny), optional
105 Image to map to green (if `None`, use imageR)
106 imageB : `lsst.afw.image.Image` or `numpy.ndarray`, (Nx, Ny), optional
107 Image to map to blue (if `None`, use imageR)
108 xSize : `int`, optional
109 Desired width of RGB image. If ``ySize`` is `None`, preserve aspect ratio
110 ySize : `int`, optional
111 Desired height of RGB image
112 rescaleFactor : `float`, optional
113 Make size of output image ``rescaleFactor*size`` of the input image
116 if self.
_image_image
is None:
118 "You must provide an image (or pass one to the constructor)")
119 imageR = self.
_image_image
126 imageRGB = [imageR, imageG, imageB]
127 for i, c
in enumerate(imageRGB):
128 if hasattr(c,
"getImage"):
129 c = imageRGB[i] = c.getImage()
130 if hasattr(c,
"getArray"):
131 imageRGB[i] = c.getArray()
133 if xSize
is not None or ySize
is not None:
134 assert rescaleFactor
is None,
"You may not specify a size and rescaleFactor"
135 h, w = imageRGB[0].shape
137 ySize = int(xSize*h/float(w) + 0.5)
139 xSize = int(ySize*w/float(h) + 0.5)
141 size = (ySize, xSize)
142 elif rescaleFactor
is not None:
143 size = float(rescaleFactor)
150 except ImportError
as e:
152 f
"Unable to rescale as scipy.misc is unavailable: {e}")
154 for i, im
in enumerate(imageRGB):
155 imageRGB[i] = scipy.misc.imresize(
156 im, size, interp=
'bilinear', mode=
'F')
161 """Return the total intensity from the red, blue, and green intensities
165 This is a naive computation, and may be overridden by subclasses
170 """Map an intensity into the range of a uint8, [0, 255] (but not converted to uint8)
172 with np.errstate(invalid=
'ignore', divide=
'ignore'):
173 return np.where(intensity <= 0, 0,
176 def _convertImagesToUint8(self, imageR, imageG, imageB):
177 """Use the mapping to convert images imageR, imageG, and imageB to a triplet of uint8 images
179 imageR = imageR - self.
minimumminimum[0]
180 imageG = imageG - self.
minimumminimum[1]
181 imageB = imageB - self.
minimumminimum[2]
185 imageRGB = [imageR, imageG, imageB]
186 with np.errstate(invalid=
"ignore"):
194 r0, g0, b0 = imageRGB
197 with np.errstate(invalid=
'ignore', divide=
'ignore'):
198 for i, c
in enumerate(imageRGB):
199 c = np.where(r0 > g0,
201 np.where(r0 >= pixmax, c*pixmax/r0, c),
202 np.where(b0 >= pixmax, c*pixmax/b0, c)),
204 np.where(g0 >= pixmax, c*pixmax/g0, c),
205 np.where(b0 >= pixmax, c*pixmax/b0, c))).astype(np.uint8)
206 c[c > pixmax] = pixmax
214 """A linear map of red, blue, green intensities into uint8 values
218 minimum : `float` or sequence of `float`
219 Intensity that should be mapped to black. If an array, has three
220 elements for R, G, B.
222 Intensity that should be mapped to white
224 Image to estimate minimum/maximum if not explicitly set
227 def __init__(self, minimum=None, maximum=None, image=None):
228 if minimum
is None or maximum
is None:
229 assert image
is not None,
"You must provide an image if you don't set both minimum and maximum"
233 minimum = stats.getValue(afwMath.MIN)
235 maximum = stats.getValue(afwMath.MAX)
237 Mapping.__init__(self, minimum, image)
243 assert maximum - minimum != 0,
"minimum and maximum values must not be equal"
244 self.
_range_range = float(maximum - minimum)
247 """Return an array which, when multiplied by an image, returns that
248 image mapped to the range of a uint8, [0, 255] (but not converted to uint8)
250 The intensity is assumed to have had ``minimum`` subtracted (as that
251 can be done per-band)
253 with np.errstate(invalid=
'ignore', divide=
'ignore'):
254 return np.where(intensity <= 0, 0,
255 np.where(intensity >= self.
_range_range,
260 """A mapping for a linear stretch chosen by the zscale algorithm
261 (preserving colours independent of brightness)
263 x = (I - minimum)/range
268 Image whose parameters are desired
270 The number of samples to use to estimate the zscale parameters
274 def __init__(self, image, nSamples=1000, contrast=0.25):
275 if not hasattr(image,
"getArray"):
276 image = afwImage.ImageF(image)
277 z1, z2 =
getZScale(image, nSamples, contrast)
279 LinearMapping.__init__(self, z1, z2, image)
283 """A mapping for an asinh stretch (preserving colours independent of brightness)
285 x = asinh(Q (I - minimum)/range)/Q
289 This reduces to a linear stretch if Q == 0
291 See http://adsabs.harvard.edu/abs/2004PASP..116..133L
295 Mapping.__init__(self, minimum)
312 self.
_soften_soften = Q/float(dataRange)
315 """Return an array which, when multiplied by an image, returns that image mapped to the range of a
316 uint8, [0, 255] (but not converted to uint8)
318 The intensity is assumed to have had minimum subtracted (as that can be done per-band)
320 with np.errstate(invalid=
'ignore', divide=
'ignore'):
321 return np.where(intensity <= 0, 0, np.arcsinh(intensity*self.
_soften_soften)*self.
_slope_slope/intensity)
325 """A mapping for an asinh stretch, estimating the linear stretch by zscale
327 x = asinh(Q (I - z1)/(z2 - z1))/Q
332 The image to analyse, or a list of 3 images to be converted to an intensity image
334 The asinh softening parameter
335 pedestal : `float` or sequence of `float`, optional
336 The value, or array of 3 values, to subtract from the images
338 N.b. pedestal, if not None, is removed from the images when calculating the zscale
339 stretch, and added back into Mapping.minimum[]
348 assert len(image)
in (1, 3,),
"Please provide 1 or 3 images"
352 if pedestal
is not None:
354 assert len(pedestal)
in (
355 1, 3,),
"Please provide 1 or 3 pedestals"
357 pedestal = 3*[pedestal]
360 for i, im
in enumerate(image):
361 if pedestal[i] != 0.0:
362 if hasattr(im,
"getImage"):
364 if hasattr(im,
"getArray"):
367 image[i] = im - pedestal[i]
369 pedestal = len(image)*[0.0]
375 dataRange = zscale.maximum - zscale.minimum[0]
376 minimum = zscale.minimum
378 for i, level
in enumerate(pedestal):
381 AsinhMapping.__init__(self, minimum, dataRange, Q)
385 def makeRGB(imageR, imageG=None, imageB=None, minimum=0, dataRange=5, Q=8, fileName=None,
386 saturatedBorderWidth=0, saturatedPixelValue=None,
387 xSize=None, ySize=None, rescaleFactor=None):
388 """Make a set of three images into an RGB image using an asinh stretch and
389 optionally write it to disk
396 minimum : `float` or sequence of `float`
400 The output file. The suffix defines the format, and must be supported by matplotlib
402 If saturatedBorderWidth is non-zero, replace saturated pixels with
403 ``saturatedPixelValue``. Note that replacing saturated pixels requires
404 that the input images be `lsst.afw.image.MaskedImage`.
415 if saturatedBorderWidth:
416 if saturatedPixelValue
is None:
418 "saturatedPixelValue must be set if saturatedBorderWidth is set")
420 saturatedBorderWidth, saturatedPixelValue)
423 rgb = asinhMap.makeRgbImage(imageR, imageG, imageB,
424 xSize=xSize, ySize=ySize, rescaleFactor=rescaleFactor)
433 """Display an rgb image using matplotlib
438 The RGB image in question
440 If `True`, call `matplotlib.pyplot.show()`
442 import matplotlib.pyplot
as plt
443 plt.imshow(rgb, interpolation=
'nearest', origin=
"lower")
450 """Write an RGB image to disk
455 The output file. The suffix defines the format, and must be supported by matplotlib
457 Most versions of matplotlib support png and pdf (although the eps/pdf/svg writers may be buggy,
458 possibly due an interaction with useTeX=True in the matplotlib settings).
460 If your matplotlib bundles pil/pillow you should also be able to write jpeg and tiff files.
462 The image, as made by e.g. makeRGB
464 import matplotlib.image
465 matplotlib.image.imsave(fileName, rgbImage)
def __init__(self, minimum, dataRange, Q=8)
def mapIntensityToUint8(self, intensity)
def __init__(self, image, Q=8, pedestal=None)
def mapIntensityToUint8(self, intensity)
def __init__(self, minimum=None, maximum=None, image=None)
def makeRgbImage(self, imageR=None, imageG=None, imageB=None, xSize=None, ySize=None, rescaleFactor=None)
def intensity(self, imageR, imageG, imageB)
def _convertImagesToUint8(self, imageR, imageG, imageB)
def __init__(self, minimum=None, image=None)
def mapIntensityToUint8(self, intensity)
def __init__(self, image, nSamples=1000, contrast=0.25)
daf::base::PropertyList * list
def computeIntensity(imageR, imageG=None, imageB=None)
def writeRGB(fileName, rgbImage)
def displayRGB(rgb, show=True)
def makeRGB(imageR, imageG=None, imageB=None, minimum=0, dataRange=5, Q=8, fileName=None, saturatedBorderWidth=0, saturatedPixelValue=None, xSize=None, ySize=None, rescaleFactor=None)
void replaceSaturatedPixels(ImageT &rim, ImageT &gim, ImageT &bim, int borderWidth=2, float saturatedPixelValue=65535)
std::pair< double, double > getZScale(image::Image< T > const &image, int const nSamples=1000, double const contrast=0.25)
Calculate an IRAF/ds9-style zscaling.
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > * makeMaskedImage(typename std::shared_ptr< Image< ImagePixelT >> image, typename std::shared_ptr< Mask< MaskPixelT >> mask=Mask< MaskPixelT >(), typename std::shared_ptr< Image< VariancePixelT >> variance=Image< VariancePixelT >())
A function to return a MaskedImage of the correct type (cf.
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())
Handle a watered-down front-end to the constructor (no variance)
Angle abs(Angle const &a)