def stretch_hist_equalize(self, ch_nb): """Stretch the current image's colors by performing histogram equalization on channel *ch_nb*. """ LOG.info("Perform a histogram equalized contrast stretch.") if(self.channels[ch_nb].size == np.ma.count_masked(self.channels[ch_nb])): LOG.warning("Nothing to stretch !") return arr = self.channels[ch_nb] nwidth = 2048.0 carr = arr.compressed() imhist, bins = np.histogram(carr, nwidth, normed=True) cdf = imhist.cumsum() - imhist[0] cdf = cdf / cdf[-1] res = np.ma.empty_like(arr) res.mask = np.ma.getmaskarray(arr) res[~res.mask] = np.interp(carr, bins[:-1], cdf) self.channels[ch_nb] = res
def crude_stretch(self, ch_nb, min_stretch = None, max_stretch = None): """Perform simple linear stretching (without any cutoff) on the channel *ch_nb* of the current image and normalize to the [0,1] range.""" if(min_stretch is None): min_stretch = self.channels[ch_nb].min() if(max_stretch is None): max_stretch = self.channels[ch_nb].max() if((not self.channels[ch_nb].mask.all()) and max_stretch - min_stretch > 0): stretched = ((self.channels[ch_nb].data - min_stretch) * 1.0 / (max_stretch - min_stretch)) self.channels[ch_nb] = np.ma.array(stretched, mask=self.channels[ch_nb].mask) else: LOG.warning("Nothing to stretch !")
def stretch_logarithmic(self, ch_nb, factor=100.): """Move data into range [1:factor] and do a normalized logarithmic enhancement. """ LOG.debug("Perform a logarithmic contrast stretch.") if ((self.channels[ch_nb].size == np.ma.count_masked(self.channels[ch_nb])) or (self.channels[ch_nb].min() == self.channels[ch_nb].max())): LOG.warning("Nothing to stretch !") return crange=(0., 1.0) arr = self.channels[ch_nb] b = float(crange[1] - crange[0])/np.log(factor) c = float(crange[0]) slope = (factor-1.)/float(arr.max() - arr.min()) arr = 1. + (arr - arr.min())*slope arr = c + b*np.log(arr) self.channels[ch_nb] = arr
def stretch_linear(self, ch_nb, cutoffs=(0.005, 0.005)): """Stretch linearly the contrast of the current image on channel *ch_nb*, using *cutoffs* for left and right trimming. """ LOG.debug("Perform a linear contrast stretch.") if((self.channels[ch_nb].size == np.ma.count_masked(self.channels[ch_nb])) or self.channels[ch_nb].min() == self.channels[ch_nb].max()): LOG.warning("Nothing to stretch !") return nwidth = 2048.0 arr = self.channels[ch_nb] carr = arr.compressed() hist, bins = np.histogram(carr, nwidth) ndim = carr.size left = 0 hist_sum = 0.0 i = 0 while i < nwidth and hist_sum < cutoffs[0]*ndim: hist_sum = hist_sum + hist[i] i = i + 1 left = bins[i-1] right = 0 hist_sum = 0.0 i = nwidth - 1 while i >= 0 and hist_sum < cutoffs[1]*ndim: hist_sum = hist_sum + hist[i] i = i - 1 right = bins[i+1] delta_x = (right - left) LOG.debug("Interval: left=%f,right=%f width=%f" %(left,right,delta_x)) if delta_x > 0.0: self.channels[ch_nb] = np.ma.array((arr - left) / delta_x, mask = arr.mask) else: self.channels[ch_nb] = np.ma.zeros(arr.shape) LOG.warning("Unable to make a contrast stretch!")
def save(self, filename, compression=6, tags=None, gdal_options=None, fformat=None, blocksize=256, **kwargs): """Save the image to the given *filename*. If the extension is "tif", the image is saved to geotiff_ format, in which case the *compression* level can be given ([0, 9], 0 meaning off). See also :meth:`image.Image.save`, :meth:`image.Image.double_save`, and :meth:`image.Image.secure_save`. The *tags* argument is a dict of tags to include in the image (as metadata), and the *gdal_options* holds options for the gdal saving driver. A *blocksize* other than 0 will result in a tiled image (if possible), with tiles of size equal to *blocksize*. If the specified format *fformat* is not know to MPOP (and PIL), we will try to import module *fformat* and call the method `fformat.save`. .. _geotiff: http://trac.osgeo.org/geotiff/ """ file_tuple = os.path.splitext(filename) fformat = fformat or file_tuple[1][1:] if fformat.lower() in ('tif', 'tiff'): return self.geotiff_save(filename, compression, tags, gdal_options, blocksize, **kwargs) try: # Let image.pil_save it ? super(GeoImage, self).save(filename, compression, fformat=fformat) except mpop.imageo.image.UnknownImageFormat: # No ... last resort, try to import an external module. LOG.info("Importing image saver module '%s'" % fformat) try: saver = __import__(fformat, globals(), locals(), ['save']) except ImportError: raise mpop.imageo.image.UnknownImageFormat( "Unknown image format '%s'" % fformat) saver.save(self, filename, **kwargs)
def add_overlay(self, color=(0, 0, 0), width=0.5, resolution=None): """Add coastline and political borders to image, using *color* (tuple of integers between 0 and 255). Warning: Loses the masks ! *resolution* is chosen automatically if None (default), otherwise it should be one of: +-----+-------------------------+---------+ | 'f' | Full resolution | 0.04 km | | 'h' | High resolution | 0.2 km | | 'i' | Intermediate resolution | 1.0 km | | 'l' | Low resolution | 5.0 km | | 'c' | Crude resolution | 25 km | +-----+-------------------------+---------+ """ img = self.pil_image() import ConfigParser conf = ConfigParser.ConfigParser() conf.read(os.path.join(CONFIG_PATH, "mpop.cfg")) coast_dir = conf.get('shapes', 'dir') LOG.debug("Getting area for overlay: " + str(self.area)) if self.area is None: raise ValueError("Area of image is None, can't add overlay.") from mpop.projector import get_area_def if isinstance(self.area, str): self.area = get_area_def(self.area) LOG.info("Add coastlines and political borders to image.") LOG.debug("Area = " + str(self.area)) if resolution is None: x_resolution = ((self.area.area_extent[2] - self.area.area_extent[0]) / self.area.x_size) y_resolution = ((self.area.area_extent[3] - self.area.area_extent[1]) / self.area.y_size) res = min(x_resolution, y_resolution) if res > 25000: resolution = "c" elif res > 5000: resolution = "l" elif res > 1000: resolution = "i" elif res > 200: resolution = "h" else: resolution = "f" LOG.debug("Automagically choose resolution " + resolution) from pycoast import ContourWriterAGG cw_ = ContourWriterAGG(coast_dir) cw_.add_coastlines(img, self.area, outline=color, resolution=resolution, width=width) cw_.add_borders(img, self.area, outline=color, resolution=resolution, width=width) arr = np.array(img) if len(self.channels) == 1: self.channels[0] = np.ma.array(arr[:, :] / 255.0) else: for idx in range(len(self.channels)): self.channels[idx] = np.ma.array(arr[:, :, idx] / 255.0)
def geotiff_save(self, filename, compression=6, tags=None, gdal_options=None, blocksize=0, geotransform=None, spatialref=None, floating_point=False): """Save the image to the given *filename* in geotiff_ format, with the *compression* level in [0, 9]. 0 means not compressed. The *tags* argument is a dict of tags to include in the image (as metadata). By default it uses the 'area' instance to generate geotransform and spatialref information, this can be overwritten by the arguments *geotransform* and *spatialref*. *floating_point* allows the saving of 'L' mode images in floating point format if set to True. .. _geotiff: http://trac.osgeo.org/geotiff/ """ from osgeo import gdal, osr raster = gdal.GetDriverByName("GTiff") if floating_point: if self.mode != "L": raise ValueError("Image must be in 'L' mode for floating point" " geotif saving") channels = [self.channels[0].astype(np.float64)] fill_value = self.fill_value or 0 gformat = gdal.GDT_Float64 else: channels, fill_value = self._finalize() gformat = gdal.GDT_Byte LOG.debug("Saving to GeoTiff.") if tags is not None: self.tags.update(tags) if gdal_options is not None: self.gdal_options.update(gdal_options) g_opts = ["=".join(i) for i in self.gdal_options.items()] if compression != 0: g_opts.append("COMPRESS=DEFLATE") g_opts.append("ZLEVEL=" + str(compression)) if blocksize != 0: g_opts.append("TILED=YES") g_opts.append("BLOCKXSIZE=" + str(blocksize)) g_opts.append("BLOCKYSIZE=" + str(blocksize)) if(self.mode == "L"): ensure_dir(filename) if fill_value is not None: dst_ds = raster.Create(filename, self.width, self.height, 1, gformat, g_opts) else: g_opts.append("ALPHA=YES") dst_ds = raster.Create(filename, self.width, self.height, 2, gformat, g_opts) self._gdal_write_channels(dst_ds, channels, 255, fill_value) elif(self.mode == "LA"): ensure_dir(filename) g_opts.append("ALPHA=YES") dst_ds = raster.Create(filename, self.width, self.height, 2, gformat, g_opts) self._gdal_write_channels(dst_ds, channels[:-1], channels[1], fill_value) elif(self.mode == "RGB"): ensure_dir(filename) if fill_value is not None: dst_ds = raster.Create(filename, self.width, self.height, 3, gformat, g_opts) else: g_opts.append("ALPHA=YES") dst_ds = raster.Create(filename, self.width, self.height, 4, gformat, g_opts) self._gdal_write_channels(dst_ds, channels, 255, fill_value) elif(self.mode == "RGBA"): ensure_dir(filename) g_opts.append("ALPHA=YES") dst_ds = raster.Create(filename, self.width, self.height, 4, gformat, g_opts) self._gdal_write_channels(dst_ds, channels[:-1], channels[3], fill_value) else: raise NotImplementedError("Saving to GeoTIFF using image mode" " %s is not implemented."%self.mode) # Create raster GeoTransform based on upper left corner and pixel # resolution ... if not overwritten by argument geotranform. if geotransform: dst_ds.SetGeoTransform(geotransform) if spatialref: if not isinstance(spatialref, str): spatialref = spatialref.ExportToWkt() dst_ds.SetProjection(spatialref) else: try: from pyresample import utils from mpop.projector import get_area_def area = get_area_def(self.area) except (utils.AreaNotFound, AttributeError): area = self.area try: adfgeotransform = [area.area_extent[0], area.pixel_size_x, 0, area.area_extent[3], 0, -area.pixel_size_y] dst_ds.SetGeoTransform(adfgeotransform) srs = osr.SpatialReference() srs.ImportFromProj4(area.proj4_string) srs.SetProjCS(area.proj_id) try: srs.SetWellKnownGeogCS(area.proj_dict['ellps']) except KeyError: pass try: # Check for epsg code. srs.SetAuthority('PROJCS', 'EPSG', int(area.proj_dict['init']. split('epsg:')[1])) except (KeyError, IndexError): pass srs = srs.ExportToWkt() dst_ds.SetProjection(srs) except AttributeError: LOG.exception("Could not load geographic data, invalid area") self.tags.update({'TIFFTAG_DATETIME': self.time_slot.strftime("%Y:%m:%d %H:%M:%S")}) dst_ds.SetMetadata(self.tags, '') # Close the dataset dst_ds = None
def add_overlay(self, color=(0, 0, 0)): """Add coastline and political borders to image, using *color*. """ import acpgimage import _acpgpilext import pps_array2image self.convert("RGB") import ConfigParser conf = ConfigParser.ConfigParser() conf.read(os.path.join(CONFIG_PATH, "geo_image.cfg")) coast_dir = CONFIG_PATH coast_file = os.path.join(coast_dir, conf.get("coasts", "coast_file")) arr = np.zeros(self.channels[0].shape, np.uint8) LOG.debug("Adding overlay: " + str(self.area_id)) if not isinstance(self.area_id, str): area_id = self.area_id.area_id else: area_id = self.area_id LOG.info("Add coastlines and political borders to image. " "Area = %s" % (area_id)) rimg = acpgimage.image(area_id) rimg.info["nodata"] = 255 rimg.data = arr area_overlayfile = "%s/coastlines_%s.asc" % (coast_dir, area_id) LOG.info("Read overlay. Try find something prepared on the area...") try: overlay = _acpgpilext.read_overlay(area_overlayfile) LOG.info("Got overlay for area: %s." % area_overlayfile) except IOError: LOG.info("Didn't find an area specific overlay." " Have to read world-map...") overlay = _acpgpilext.read_overlay(coast_file) LOG.info("Add overlay.") overlay_image = pps_array2image.add_overlay(rimg, overlay, pil.fromarray(arr), color=1) val = np.ma.asarray(overlay_image) self.channels[0] = np.ma.where(val == 1, color[0], self.channels[0]) self.channels[0].mask = np.where(val == 1, False, np.ma.getmaskarray(self.channels[0])) self.channels[1] = np.ma.where(val == 1, color[1], self.channels[1]) self.channels[1].mask = np.where(val == 1, False, np.ma.getmaskarray(self.channels[1])) self.channels[2] = np.ma.where(val == 1, color[2], self.channels[2]) self.channels[2].mask = np.where(val == 1, False, np.ma.getmaskarray(self.channels[2]))
def geotiff_save( self, filename, compression=6, tags=None, gdal_options=None, blocksize=0, geotransform=None, spatialref=None ): """Save the image to the given *filename* in geotiff_ format, with the *compression* level in [0, 9]. 0 means not compressed. The *tags* argument is a dict of tags to include in the image (as metadata). By default it uses the 'area' instance to generate geotransform and spatialref information, this can be overwritte by the arguments *geotransform* and *spatialref*. .. _geotiff: http://trac.osgeo.org/geotiff/ """ from osgeo import gdal, osr raster = gdal.GetDriverByName("GTiff") channels, fill_value = self._finalize() LOG.debug("Saving to GeoTiff.") if tags is not None: self.tags.update(tags) if gdal_options is not None: self.gdal_options.update(gdal_options) g_opts = ["=".join(i) for i in self.gdal_options.items()] if compression != 0: g_opts.append("COMPRESS=DEFLATE") g_opts.append("ZLEVEL=" + str(compression)) if blocksize != 0: g_opts.append("TILED=YES") g_opts.append("BLOCKXSIZE=" + str(blocksize)) g_opts.append("BLOCKYSIZE=" + str(blocksize)) if self.mode == "L": ensure_dir(filename) if fill_value is not None: dst_ds = raster.Create(filename, self.width, self.height, 1, gdal.GDT_Byte, g_opts) else: dst_ds = raster.Create(filename, self.width, self.height, 2, gdal.GDT_Byte, g_opts) self._gdal_write_channels(dst_ds, channels, 255, fill_value) elif self.mode == "RGB": ensure_dir(filename) if fill_value is not None: dst_ds = raster.Create(filename, self.width, self.height, 3, gdal.GDT_Byte, g_opts) else: dst_ds = raster.Create(filename, self.width, self.height, 4, gdal.GDT_Byte, g_opts) self._gdal_write_channels(dst_ds, channels, 255, fill_value) elif self.mode == "RGBA": ensure_dir(filename) dst_ds = raster.Create(filename, self.width, self.height, 4, gdal.GDT_Byte, g_opts) self._gdal_write_channels(dst_ds, channels, channels[3], fill_value) else: raise NotImplementedError("Saving to GeoTIFF using image mode" " %s is not implemented." % self.mode) # Create raster GeoTransform based on upper left corner and pixel # resolution ... if not overwritten by argument geotranform. if geotransform: dst_ds.SetGeoTransform(geotransform) if spatialref: if not isinstance(spatialref, str): spatialref = spatialref.ExportToWkt() dst_ds.SetProjection(spatialref) else: try: from pyresample import utils from mpop.projector import get_area_def area = get_area_def(self.area_id) except (utils.AreaNotFound, AttributeError): area = self.area_id try: adfgeotransform = [ area.area_extent[0], area.pixel_size_x, 0, area.area_extent[3], 0, -area.pixel_size_y, ] dst_ds.SetGeoTransform(adfgeotransform) srs = osr.SpatialReference() srs.SetProjCS(area.proj_id) srs.ImportFromProj4(area.proj4_string) srs = srs.ExportToWkt() dst_ds.SetProjection(srs) except AttributeError: LOG.exception("Could not load geographic data, invalid area") self.tags.update({"TIFFTAG_DATETIME": self.time_slot.strftime("%Y:%m:%d %H:%M:%S")}) dst_ds.SetMetadata(self.tags, "") # Close the dataset dst_ds = None