# Class ImageProcessor

Object
ImageProcessor

All Implemented Interfaces:
  `Cloneable`

public class ImageProcessor extends Object implements Cloneable
A predefined set of operations on images. After instantiation, `ImageProcessor` can be configured for the following aspects:

- Preferences about the tiling of an image in relationship with a given image size.
- Interpolation method to use during resampling operations.
- Fill values to use for pixels that cannot be computed.
- Colorization algorithm to apply for colorizing a computed image.
- Positional accuracy hints for enabling the use of faster algorithm when a lower accuracy is acceptable.
- Whether operations can be executed in parallel. By default operations on unknown `RenderedImage` implementations are executed sequentially in the caller thread, for safety reasons. Some operations can be parallelized, but it should be enabled only if the `RenderedImage` is known to be thread-safe and has concurrent (or fast) implementation of `RenderedImage​.getTile(int, int)`. Apache SIS implementations of `RenderedImage` can be parallelized, but it may not be the case of images from other libraries.
- Whether the operations should fail if an exception is thrown while processing a tile. By default errors during calculation are propagated as an `ImagingOpException`, in which case no result is available. But errors can also be notified as a `LogRecord` instead, in which case partial results may be available.

For each image operations, above properties are combined with parameters given to the operation method. Each method in this `ImageProcessor` class documents the properties used in addition of method parameters.

## API design

Properties (setter methods) are used for values that can be applied unchanged on many different images. For example, the interpolation method can be specified once and used unchanged for many `resample(…)` operations. On the other hand, method arguments are used for values that are usually specific to the image to process. For example, the `MathTransform` argument given to the `resample(…)` operation depends tightly on the source image and destination bounds (also given in arguments); those information usually need to be recomputed for each image.

## Deferred calculations

Methods in this class may compute the result at some later time after the method returned, instead of computing the result immediately on method call. Consequently, unless otherwise specified, `RenderedImage` arguments should be stable, i.e. pixel values should not be modified after method return.

## Area of interest

Some operations accept an optional area of interest argument specified as a `Shape` instance in pixel coordinates. If a shape is given, it should not be modified after `ImageProcessor` method call because the given object may be retained directly (i.e. the `Shape` is not always cloned; it depends on its class). In addition, the `Shape` implementation shall be thread-safe (assuming its state stay unmodified) unless the execution mode is set to `ImageProcessor​.Mode​.PARALLEL`.

## Error handling

If an exception occurs during the computation of a tile, then the `ImageProcessor` behavior is controlled by the `errorHandler` property:

- If `ErrorHandler​.THROW`, the exception is wrapped in an `ImagingOpException` and thrown.
- If `ErrorHandler​.LOG`, the exception is logged and a partial result is returned.
- If any other value, the exception is wrapped in a `LogRecord` and sent to that filter. The filter can store the log record, for example for showing later in a graphical user interface (GUI). If the filter returns `true`, the log record is also logged, otherwise it is silently discarded. In both cases a partial result is returned.

## Thread-safety

`ImageProcessor` is safe for concurrent use in multi-threading environment. Note however that `ImageProcessor` instances are mutable; consider cloning if setter methods are invoked on a shared instance.

Since:
  1.1
See Also:
  - `GridCoverageProcessor`

- ## Nested Class Summary

  Nested Classes
  Modifier and Type
  Class
  Description
  `static enum`
  `ImageProcessor​.Mode`
  Execution modes specifying whether operations can be executed in parallel.

- ## Constructor Summary

  Constructors
  Constructor
  Description
  `ImageProcessor()`
  Creates a new processor with default configuration.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `RenderedImage`
  `addUserProperties(RenderedImage source, Map<String,Object> properties)`
  Returns an image augmented with user-defined property values.
  `RenderedImage`
  `aggregateBands(RenderedImage... sources)`
  Aggregates in a single image all the bands of all specified images, in order.
  `RenderedImage`
  `aggregateBands(RenderedImage[] sources, int[][] bandsPerSource)`
  Aggregates in a single image the specified bands of a sequence of source images, in order.
  `ImageProcessor`
  `clone()`
  Returns an image processor with the same configuration as this processor.
  `RenderedImage`
  `convert(RenderedImage source, NumberRange<?>[] sourceRanges, MathTransform1D[] converters, DataType targetType)`
  Returns an image with sample values converted by the given functions.
  `boolean`
  `equals(Object object)`
  Returns `true` if the given object is an image processor of the same class with the same configuration.
  `static DoubleUnaryOperator`
  `filterNodataValues(Number... values)`
  Builds an operator which can be used for filtering "no data" sample values.
  `Colorizer`
  `getColorizer()`
  Returns the colorization algorithm to apply on computed images, or `null` for default.
  `ErrorHandler`
  `getErrorHandler()`
  Returns whether exceptions occurring during computation are propagated or logged.
  `ImageProcessor​.Mode`
  `getExecutionMode()`
  Returns whether operations can be executed in parallel.
  `Number[]`
  `getFillValues()`
  Returns the values to use for pixels that cannot be computed.
  `ImageLayout`
  `getImageLayout()`
  Returns the preferences about the tiling of an image in relationship with a given image size.
  `Interpolation`
  `getInterpolation()`
  Returns the interpolation method to use during resample operations.
  `Quantity<?>[]`
  `getPositionalAccuracyHints()`
  Returns hints about the desired positional accuracy, in "real world" units or in pixel units.
  `int`
  `hashCode()`
  Returns a hash code value for this image processor based on its current configuration.
  `List<NavigableMap<Double,Shape>>`
  `isolines(RenderedImage data, double[][] levels, MathTransform gridToCRS)`
  Generates isolines at the specified levels computed from data provided by the given image.
  `RenderedImage`
  `mask(RenderedImage source, Shape mask, boolean maskInside)`
  Applies a mask defined by a geometric shape.
  `RenderedImage`
  `overlay(RenderedImage[] sources, Rectangle bounds)`
  Creates a new image overlay or returns one of the given sources if equivalent.
  `RenderedImage`
  `prefetch(RenderedImage source, Rectangle areaOfInterest)`
  Computes immediately all tiles in the given region of interest, then returns an image with those tiles ready.
  `RenderedImage`
  `reformat(RenderedImage source, SampleModel sampleModel)`
  Reformats the given image with a different sample model.
  `RenderedImage`
  `resample(RenderedImage source, Rectangle bounds, MathTransform toSource)`
  Creates a new image which will resample the given image.
  `RenderedImage`
  `selectBands(RenderedImage source, int... bands)`
  Selects a subset of bands in the given image.
  `void`
  `setColorizer(Colorizer colorizer)`
  Sets the colorization algorithm to apply on computed images.
  `void`
  `setErrorHandler(ErrorHandler handler)`
  Sets whether exceptions occurring during computation are propagated or logged.
  `void`
  `setExecutionMode(ImageProcessor​.Mode mode)`
  Sets whether operations can be executed in parallel.
  `void`
  `setFillValues(Number... values)`
  Sets the values to use for pixels that cannot be computed.
  `void`
  `setImageLayout(ImageLayout layout)`
  Sets the preferences (size, tile size, sample model, etc.) of destination images.
  `void`
  `setInterpolation(Interpolation method)`
  Sets the interpolation method to use during resample operations.
  `void`
  `setPositionalAccuracyHints(Quantity<?>... hints)`
  Sets hints about desired positional accuracy, in "real world" units or in pixel units.
  `RenderedImage`
  `statistics(RenderedImage source, Shape areaOfInterest, DoubleUnaryOperator... sampleFilters)`
  Returns an image with statistics (minimum, maximum, mean, standard deviation) on each bands.
  `RenderedImage`
  `stretchColorRamp(RenderedImage source, Map<String,?> modifiers)`
  Returns an image with the same sample values as the given image, but with its color ramp stretched between specified or inferred bounds.
  `Statistics[]`
  `valueOfStatistics(RenderedImage source, Shape areaOfInterest, DoubleUnaryOperator... sampleFilters)`
  Returns statistics (minimum, maximum, mean, standard deviation) on each bands of the given image.
  `RenderedImage`
  `visualize(RenderedImage source)`
  Returns an image where all sample values are indices of colors in an `IndexColorModel`.
  `RenderedImage`
  `visualize(RenderedImage source, Rectangle bounds, MathTransform toSource)`
  Returns an image as the resampling of the given image followed by a conversion to integer sample values.

  ### Methods inherited from class Object

  `finalize, getClass, notify, notifyAll, toString, wait, wait, wait`

- ## Constructor Details

  - ### ImageProcessor

    public ImageProcessor()
    Creates a new processor with default configuration. The execution mode is initialized to `ImageProcessor​.Mode​.DEFAULT` and the error handler to `ErrorHandler​.THROW`.

- ## Method Details

  - ### getImageLayout

    public ImageLayout getImageLayout()
    Returns the preferences about the tiling of an image in relationship with a given image size. The `ImageLayout` determines characteristics (size, tile size, sample model, etc.) of destination images.
    Returns:
      preferences about the tiling of an image in relationship with a given image size.
    Since:
      1.5

  - ### setImageLayout

    public void setImageLayout(ImageLayout layout)
    Sets the preferences (size, tile size, sample model, etc.) of destination images.
    Parameters:
      `layout` - new preferences about the tiling of an image in relationship with a given image size.
    Since:
      1.5

  - ### getInterpolation

    public Interpolation getInterpolation()
    Returns the interpolation method to use during resample operations.
    Returns:
      interpolation method to use during resample operations.
    See Also:
      - `resample(RenderedImage, Rectangle, MathTransform)`

  - ### setInterpolation

    public void setInterpolation(Interpolation method)
    Sets the interpolation method to use during resample operations.
    Parameters:
      `method` - interpolation method to use during resample operations.
    See Also:
      - `resample(RenderedImage, Rectangle, MathTransform)`

  - ### getFillValues

    public Number[] getFillValues()
    Returns the values to use for pixels that cannot be computed. This method returns a copy of the array set by the last call to `setFillValues(Number...)`.
    Returns:
      fill values to use for pixels that cannot be computed, or `null` for the defaults.

  - ### setFillValues

    public void setFillValues(Number... values)
    Sets the values to use for pixels that cannot be computed. The given array may be `null` or may contain `null` elements for default values. Those defaults are zero for images storing sample values as integers, or `Float​.NaN` or `Double​.NaN` for images storing sample values as floating point numbers. If the given array contains less elements than the number of bands in an image, missing elements will be assumed null. If the given array contains more elements than the number of bands, extraneous elements will be ignored.
    Parameters:
      `values` - fill values to use for pixels that cannot be computed, or `null` for the defaults.

  - ### getColorizer

    public Colorizer getColorizer()
    Returns the colorization algorithm to apply on computed images, or `null` for default. This method returns the value set by the last call to `setColorizer(Colorizer)`.
    Returns:
      colorization algorithm to apply on computed image, or `null` for default.
    Since:
      1.4

  - ### setColorizer

    public void setColorizer(Colorizer colorizer)
    Sets the colorization algorithm to apply on computed images. The colorizer is invoked when the rendered image produced by an `ImageProcessor` operation needs a `ColorModel` which is not straightforward.

    #### Examples

    The color model of a `resample(…)` operation is straightforward: it is the same `ColorModel` than the source image. Consequently the colorizer is not invoked for that operation.

    But by contrast, the color model of an `aggregateBands(…)` operation cannot be determined in such straightforward way. If three or four bands are aggregated, should they be interpreted as an (A)RGB image? The `Colorizer` allows to specify the desired behavior.

    Parameters:
      `colorizer` - colorization algorithm to apply on computed image, or `null` for default.
    Since:
      1.4

  - ### getPositionalAccuracyHints

    public Quantity<?>[] getPositionalAccuracyHints()
    Returns hints about the desired positional accuracy, in "real world" units or in pixel units. This is an empty array by default, which means that `ImageProcessor` aims for the best accuracy it can produce. If the returned array is non-empty and contains accuracies large enough, `ImageProcessor` may use some slightly faster algorithms at the expense of accuracy.
    Returns:
      desired accuracy in no particular order, or an empty array if none.

  - ### setPositionalAccuracyHints

    public void setPositionalAccuracyHints(Quantity<?>... hints)
    Sets hints about desired positional accuracy, in "real world" units or in pixel units. More than one hint can be specified for allowing the use of different units. For example, the given array can contain an accuracy in metres and an accuracy in seconds, for specifying desired accuracies in both spatial dimensions and in the temporal dimension. Accuracy can also be specified in both real world units such as metres and in pixel units, which are converted to real world units depending on image resolution. If more than one value is applicable to a dimension (after unit conversion if needed), the smallest value is taken.

    Those values are only hints, the `ImageProcessor` is free to ignore them. In any cases there is no guarantee that computed images will met those accuracies. The given values are honored on a best effort basis only.

    In current implementation, `ImageProcessor` recognizes only accuracies in `Units​.PIXEL`. A value such as 0.125 pixel may cause `ImageProcessor` to use some a slightly faster algorithm at the expense of accuracy during resampling operations.

    Parameters:
      `hints` - desired accuracy in no particular order, or a `null` array if none. Null elements in the array are ignored.

  - ### getExecutionMode

    public ImageProcessor.Mode getExecutionMode()
    Returns whether operations can be executed in parallel. If `ImageProcessor​.Mode​.SEQUENTIAL`, operations are executed sequentially in the caller thread. If `ImageProcessor​.Mode​.PARALLEL`, some operations may be parallelized using an arbitrary number of threads.
    Returns:
      whether the operations can be executed in parallel.

  - ### setExecutionMode

    public void setExecutionMode(ImageProcessor.Mode mode)
    Sets whether operations can be executed in parallel. This value can be set to `ImageProcessor​.Mode​.PARALLEL` if the `RenderedImage` instances are thread-safe and provide a concurrent (or very fast) implementation of `RenderedImage​.getTile(int, int)`. If `ImageProcessor​.Mode​.SEQUENTIAL`, only the caller thread is used. Sequential operations may be useful for processing `RenderedImage` implementations that may not be thread-safe.

    It is safe to set this flag to `ImageProcessor​.Mode​.PARALLEL` with `BufferedImage` or with Apache SIS implementations of `RenderedImage`.

    Parameters:
      `mode` - whether the operations can be executed in parallel.

  - ### getErrorHandler

    public ErrorHandler getErrorHandler()
    Returns whether exceptions occurring during computation are propagated or logged. If `ErrorHandler​.THROW` (the default), exceptions are wrapped in `ImagingOpException` and thrown. If `ErrorHandler​.LOG`, exceptions are wrapped in a `LogRecord`, filtered then eventually logged.
    Returns:
      whether exceptions occurring during computation are propagated or logged.

  - ### setErrorHandler

    public void setErrorHandler(ErrorHandler handler)
    Sets whether exceptions occurring during computation are propagated or logged. The default behavior is to wrap exceptions in `ImagingOpException` and throw them. If this property is set to `ErrorHandler​.LOG`, then exceptions will be wrapped in `LogRecord` instead, in which case a partial result may be available. Only one log record is created for all tiles that failed for the same operation on the same image.

    #### Limitations

    In current `ImageProcessor` implementation, the error handler is not honored by all operations. Some operations may continue to throw an exception on failure (the behavior of default error handler) even if a different handler has been specified. Each operation specifies in its Javadoc whether the operation uses error handler or not.
    Parameters:
      `handler` - handler to notify when an operation failed on one or more tiles, or `ErrorHandler​.THROW` for propagating the exception.

  - ### filterNodataValues

    public static DoubleUnaryOperator filterNodataValues(Number... values)
    Builds an operator which can be used for filtering "no data" sample values. Calls to the operator `applyAsDouble(x)` will return `Double​.NaN` if the x value is equal to one of the given no-data `values`, and will return x unchanged otherwise.

    #### Usage

    This operator can be used as a `sampleFilters` argument in calls to `statistics(…)` or `valueOfStatistics(…)` methods. It is redundant with transfer function work, but can be useful for images not managed by a `GridCoverage`.
    Parameters:
      `values` - the "no data" values, or `null` if none. Null and NaN elements are ignored.
    Returns:
      an operator for filtering the given "no data" values, or `null` if there is no non-NaN value to filter.
    Since:
      1.2
    See Also:
      - `statistics(RenderedImage, Shape, DoubleUnaryOperator[])`
      - `SampleDimension​.getTransferFunction()`

  - ### valueOfStatistics

    public Statistics[] valueOfStatistics(RenderedImage source, Shape areaOfInterest, DoubleUnaryOperator... sampleFilters)
    Returns statistics (minimum, maximum, mean, standard deviation) on each bands of the given image. Invoking this method is equivalent to invoking the `statistics(…)` method and extracting immediately the statistics property value, except that this method guarantees that all statistics are non-null and supports custom error handlers.

    If `areaOfInterest` is null and `sampleFilters` is `null` or empty, then the default behavior is as below:

    - If the "org.apache.sis.Statistics" property value exists in the given image, then that value is returned with the null array elements (if any) replaced by computed values. Note that the returned statistics are not necessarily for the whole image. They are whatever statistics the property provider considered as representative.
    - Otherwise statistics are computed for the whole image.

    #### Sample converters

    An arbitrary `DoubleUnaryOperator` can be applied on sample values before to add them to statistics. The main purpose is to replace "no-data values" by `Double​.NaN` values for instructing `Statistics​.accept(double)` to ignore them. The `filterNodataValues(Number...)` convenience method can be used for building an operator filtering "no data" sample values.

    #### Properties used

    This operation uses the following properties in addition to method parameters:
    - Execution mode (parallel or sequential).
    - Error handler (custom action executed if an exception is thrown).

    #### Result relationship with source

    This method fetches (from property values) or computes statistics immediately. Changes in the `source` image after this method call do not change the results.
    Parameters:
      `source` - the image for which to compute statistics.
      `areaOfInterest` - pixel coordinates of the area of interest, or `null` for the default.
      `sampleFilters` - converters to apply on sample values before to add them to statistics, or `null` or an empty array if none. The array may have any length and may contain null elements. For all `i < numBands`, non-null `sampleFilters[i]` are applied to band i.
    Returns:
      the statistics of sample values in each band. Guaranteed non-null and without null element.
    Throws:
      `ImagingOpException` - if an error occurred during calculation and the error handler is `ErrorHandler​.THROW`.
    See Also:
      - `statistics(RenderedImage, Shape, DoubleUnaryOperator...)`
      - `filterNodataValues(Number...)`
      - `PlanarImage​.STATISTICS_KEY`

  - ### statistics

    public RenderedImage statistics(RenderedImage source, Shape areaOfInterest, DoubleUnaryOperator... sampleFilters)
    Returns an image with statistics (minimum, maximum, mean, standard deviation) on each bands. The property value will be computed when first requested (it is not computed immediately by this method).

    If `areaOfInterest` is null and `sampleFilters` is `null` or empty, then the default is as below:

    - If the "org.apache.sis.Statistics" property value exists in the given image, then that image is returned as-is. Note that the existing property value is not necessarily statistics for the whole image. They are whatever statistics the property provider considers as representative.
    - Otherwise an image augmented with a "org.apache.sis.Statistics" property value is returned.

    #### Sample converters

    An arbitrary `DoubleUnaryOperator` can be applied on sample values before to add them to statistics. The main purpose is to replace "no-data values" by `Double​.NaN` values for instructing `Statistics​.accept(double)` to ignore them. The `filterNodataValues(Number...)` convenience method can be used for building an operator filtering "no data" sample values.

    ##### API design note

    The `areaOfInterest` and `sampleFilters` arguments are complementary. Both of them filter the data accepted for statistics. In ISO 19123 terminology, the `areaOfInterest` argument filters the coverage domain while the `sampleFilters` argument filters the coverage range. Another connection with OGC/ISO standards is that `DoubleUnaryOperator` in this context does the same work as transfer function. It can be useful for images not managed by a `GridCoverage`.

    #### Properties used

    This operation uses the following properties in addition to method parameters:
    - Execution mode (parallel or sequential).
    - Error handler (whether to fail if an exception is thrown).
    Parameters:
      `source` - the image for which to provide statistics.
      `areaOfInterest` - pixel coordinates of the area of interest, or `null` for the default.
      `sampleFilters` - converters to apply on sample values before to add them to statistics, or `null` or an empty array if none. The array may have any length and may contain null elements. For all `i < numBands`, non-null `sampleFilters[i]` are applied to band i.
    Returns:
      an image with an "org.apache.sis.Statistics" property. May be `image` if the given argument already has a statistics property.
    See Also:
      - `valueOfStatistics(RenderedImage, Shape, DoubleUnaryOperator...)`
      - `filterNodataValues(Number...)`
      - `PlanarImage​.STATISTICS_KEY`

  - ### stretchColorRamp

    public RenderedImage stretchColorRamp(RenderedImage source, Map<String,?> modifiers)
    Returns an image with the same sample values as the given image, but with its color ramp stretched between specified or inferred bounds. For example, in a gray scale image, pixels with the minimum value will be black and pixels with the maximum value will be white. This operation is a kind of tone mapping, a technique used in image processing to map one set of colors to another. The mapping applied by this method is conceptually a simple linear transform (a scale and an offset) applied on sample values before they are mapped to their colors.

    The minimum and maximum value can be either specified explicitly, or determined from `statistics` on the image. In the latter case a range of value is determined first from the minimum and maximum values found in the image, optionally narrowed to an interval of some standard deviations around the mean value.

    Narrowing with standard deviations is useful for data having a Gaussian distribution, as often seen in nature. In such distribution, 99.9% of the data are between the mean ± 3×standard deviation, but some values may still appear much further. The minimum and maximum values alone are not a robust way to compute a range of values for the color ramp because a single value very far from other values is sufficient for making the colors difficult to distinguish for 99.9% of the data.

    The range of values for the color ramp can be narrowed with following modifiers (a `Map` is used for allowing addition of more modifiers in future Apache SIS versions). All unrecognized modifiers are silently ignored. If no modifier is specified, then the color ramp will be stretched from minimum to maximum values found in specified image.

    Value range modifiers
    Key | Purpose | Values
    --- | --- | ---
    `"minimum"` | Minimum value (omitted if computed from statistics). | `Number`
    `"maximum"` | Maximum value (omitted if computed from statistics). | `Number`
    `"multStdDev"` | Multiple of the standard deviation. | `Number` (typical values: 1.5, 2 or 3)
    `"statistics"` | Statistics or image from which to get statistics. | `Statistics` or `RenderedImage`
    `"areaOfInterest"` | Pixel coordinates of the region for which to compute statistics. | `Shape`
    `"nodataValues"` | Values to ignore in statistics. | `Number` or `Number[]`
    `"sampleDimensions"` | Meaning of pixel values. | `SampleDimension` or `SampleDimension[]`

    Note: if no value is associated to the `"sampleDimensions"` key, then the default value will be the "org.apache.sis.SampleDimensions" image property value if defined. That value can be an array, in which case the sample dimension of the visible band is taken.

    #### Properties used

    This operation uses the following properties in addition to method parameters:
    - (none)

    #### Limitation

    Current implementation can stretch only gray scale images (a future version may extend support to images using index color models). If this method cannot stretch the color ramp, for example because the given image is an RGB image, then the image is returned unchanged.
    Parameters:
      `source` - the image to recolor.
      `modifiers` - modifiers for narrowing the range of values, or `null` if none.
    Returns:
      the image with color ramp stretched between the specified or calculated bounds, or `image` unchanged if the operation cannot be applied on the given image.
    Throws:
      `IllegalArgumentException` - if the value associated to one of about keys is not of expected type.

  - ### addUserProperties

    public RenderedImage addUserProperties(RenderedImage source, Map<String,Object> properties)
    Returns an image augmented with user-defined property values. The specified properties overwrite any property that may be defined by the source image. When an image property value is requested, the steps are:
    1. If the `properties` map has an entry for the property name, returns the associated value. It may be `null`.
    2. Otherwise if the property is defined by the source image, returns its value. It may be `null`.
    3. Otherwise returns `Image​.UndefinedProperty`.
    The given `properties` map is retained by reference in the returned image. The `Map` is not copied in order to allow the use of custom implementations doing deferred calculations. If the caller intends to modify the map content after this method call, (s)he should use a `ConcurrentMap`.

    The returned image is "live": changes in `source` image properties or in `properties` map entries are immediately reflected in the returned image.

    Null are valid image property values. An entry associated with the `null` value in the `properties` map is not the same as an absence of entry.

    Parameters:
      `source` - the source image to augment with user-specified property values.
      `properties` - properties overwriting or completing `source` properties.
    Returns:
      an image augmented with the specified properties.
    Since:
      1.4
    See Also:
      - `RenderedImage​.getPropertyNames()`
      - `RenderedImage​.getProperty(String)`

  - ### selectBands

    public RenderedImage selectBands(RenderedImage source, int... bands)
    Selects a subset of bands in the given image. This method can also be used for changing band order or repeating the same band from the source image. If the specified `bands` are the same as the source image bands in the same order, then `source` is returned directly.

    This method returns an image sharing the same data buffer as the source image; pixel values are not copied. Consequently, changes in the source image are reflected immediately in the returned image.

    If the given image is an instance of `WritableRenderedImage`, then the returned image will also be a `WritableRenderedImage`. In such case values written in the returned image will be written directly in the source image.

    #### Properties used

    This operation uses the following properties in addition to method parameters:
    - (none)
    Parameters:
      `source` - the image in which to select bands.
      `bands` - indices of bands to retain.
    Returns:
      image width selected bands.
    Throws:
      `IllegalArgumentException` - if a band index is invalid.

  - ### aggregateBands

    public RenderedImage aggregateBands(RenderedImage... sources)
    Aggregates in a single image all the bands of all specified images, in order. All sources images should map pixel coordinates to the same geospatial locations. A pixel at coordinates (x, y) in the aggregated image will contain values from the pixels at the same coordinates in all source images. The result image will be bounded by the intersection of all source images.

    This convenience method delegates to `aggregateBands(RenderedImage[], int[][])`. See that method for more information on restrictions, writable images, memory saving and properties used.

    Parameters:
      `sources` - images whose bands shall be aggregated, in order. At least one image must be provided.
    Returns:
      the aggregated image, or `sources[0]` returned directly if only one image was supplied.
    Throws:
      `IllegalArgumentException` - if there is an incompatibility between some source images.
    Since:
      1.4
    See Also:
      - `aggregateBands(RenderedImage[], int[][])`

  - ### aggregateBands

    public RenderedImage aggregateBands(RenderedImage[] sources, int[][] bandsPerSource)
    Aggregates in a single image the specified bands of a sequence of source images, in order. This method performs the same work as `aggregateBands(RenderedImage...)`, but with the possibility to specify the bands to retain in each source image. The `bandsPerSource` argument specifies the bands to select in each source image. That array can be `null` for selecting all bands in all source images, or may contain `null` elements for selecting all bands of the corresponding image. An empty array element (i.e. zero band to select) discards the corresponding source image. In the latter case, the discarded element in the `sources` array may be `null`.

    #### Restrictions

    All images shall use the same data type, and all source images shall intersect each other with a non-empty intersection area. However it is not required that all images have the same bounds or the same tiling scheme.

    #### Writable image

    If all source images are `WritableRenderedImage` instances, then the returned image will also be a `WritableRenderedImage`. In such case values written in the returned image will be copied back to the source images.

    #### Memory saving

    The returned image may opportunistically share the underlying data arrays of some source images. Bands are really copied only when sharing is not possible. The actual strategy may be a mix of both arrays sharing and bands copies.

    #### Repeated bands

    For any value of i, the array at `bandsPerSource[i]` shall not contain duplicated values. This restriction is for capturing common errors, in order to reduce the risk of accidental band repetition. However the same band can be repeated indirectly if the same image is repeated at different values of i. But even when a source band is referenced many times, all occurrences still share pixel data copied at most once.

    #### Properties used

    This operation uses the following properties in addition to method parameters:
    - Colorizer.
    - Execution mode (parallel or sequential).
    Parameters:
      `sources` - images whose bands shall be aggregated, in order. At least one image must be provided.
      `bandsPerSource` - bands to use for each source image, in order. May contain `null` elements.
    Returns:
      the aggregated image, or one of the sources if it can be used directly.
    Throws:
      `IllegalArgumentException` - if there is an incompatibility between some source images or if some band indices are duplicated or outside their range of validity.
    Since:
      1.4

  - ### overlay

    public RenderedImage overlay(RenderedImage[] sources, Rectangle bounds)
    Creates a new image overlay or returns one of the given sources if equivalent. All source images shall have the same pixel coordinate system, but they may have different bounding boxes, tile sizes and tile indices. Images are drawn in reverse order: the last source image is drawn first, and the first source image is drawn last on top of all other images. All images are considered fully opaque, including the alpha channel which is handled as an ordinary band.

    The returned image may have less sources than the ones given in argument if this method determines that some sources will never be drawn (i.e., are fully hidden behind the first images). If only one source appears to be effectively used, this method returns that image directly.

    #### Optimization

    The returned image may share some tiles from any source images (without copy) if the tile can be used directly with no change.

    #### Preconditions

    All source images shall have the same number of bands (but not necessarily the same sample model). All source images should have equivalent color model, otherwise color consistency is not guaranteed. At least one image shall intersect the given bounds.

    #### Properties used

    This operation uses the following properties in addition to method parameters:
    - Image layout for the desired sample model and color model.
    - Image bounds adjustment flag for deciding whether to use a modified image size if `bounds` is not divisible by a tile size.
    - Colorizer for customizing the rendered image color model.
    Parameters:
      `sources` - the images to overlay. Null array elements are ignored.
      `bounds` - range of pixel coordinates, or `null` for the union of all source images.
    Returns:
      the image overlay, or one of the given sources if only one is suitable.
    Throws:
      `IllegalArgumentException` - if there is an incompatibility between some source images or if no image intersect the given bounds.
    Since:
      1.5
    See Also:
      - `ImageCombiner`

  - ### reformat

    public RenderedImage reformat(RenderedImage source, SampleModel sampleModel)
    Reformats the given image with a different sample model. This operation copies the pixel values in a new image. Despite the copies being done on a tile-by-tile basis when each tile is first requested, this is still a relatively costly operation compared to the usual Apache SIS approach of creating views as much as possible. Therefore, this method should be used only when necessary.

    #### Preconditions

    The new sample model shall have the same number of bands as in the given image.

    #### Properties used

    This operation uses the following properties in addition to method parameters:
    - Image layout for the default sample model.
    - Image bounds adjustment flag for deciding whether to use a modified image size if the source image size is not divisible by a tile size.
    Parameters:
      `source` - the images to reformat.
      `sampleModel` - the desired sample model. Can be null only if a default sample model is specified by `ImageLayout​.sampleModel`.
    Returns:
      the reformatted image.
    Since:
      1.5

  - ### mask

    public RenderedImage mask(RenderedImage source, Shape mask, boolean maskInside)
    Applies a mask defined by a geometric shape. If `maskInside` is `true`, then all pixels inside the given shape are set to the fill values. If `maskInside` is `false`, then the mask is reversed: the pixels set to fill values are the ones outside the shape.

    #### Properties used

    This operation uses the following properties in addition to method parameters:
    - Fill values values to assign to pixels inside/outside the shape.
    Parameters:
      `source` - the image on which to apply a mask.
      `mask` - geometric area (in pixel coordinates) of the mask.
      `maskInside` - `true` for masking pixels inside the shape, or `false` for masking outside.
    Returns:
      an image with mask applied.
    Since:
      1.2
    See Also:
      - `GridCoverageProcessor​.mask(GridCoverage, RegionOfInterest, boolean)`

  - ### convert

    public RenderedImage convert(RenderedImage source, NumberRange<?>[] sourceRanges, MathTransform1D[] converters, DataType targetType)
    Returns an image with sample values converted by the given functions. The results can be stored as `byte`, `short`, `int`, `float` or `double` values, not necessarily the same type as the source values. If the result values are stored as integers, then they are rounded to nearest integers and clamped in the valid range of the target integer type.

    If the source image is a `WritableRenderedImage` and the given converters are invertible, then the returned image will also be a `WritableRenderedImage` instance. In such case values written in the returned image will be reflected in the source image, with rounding and clamping if the source values are stored as integers.

    The number of bands in the returned image is the length of the `converters` array, which must be greater than 0 and not greater than the number of bands in the source image. If the `converters` array length is less than the number of source bands, all source bands at index ≥ `converters​.length` will be ignored.

    The `sourceRanges` array is only a hint for this method. The array may be `null` or contain `null` elements, and may be of any length. Missing elements are considered null and extraneous elements are ignored. Those ranges do not need to encompass all possible values; it is sufficient to provide only typical or "most interesting" ranges.

    #### Properties used

    This operation uses the following properties in addition to method parameters:
    - Colorizer for customizing the rendered image color model.

    #### Result relationship with source

    Changes in the source image are reflected in the returned image if the source image notifies tile observers.
    Parameters:
      `source` - the image for which to convert sample values.
      `sourceRanges` - approximate ranges of values for each band in source image, or `null` if unknown.
      `converters` - the transfer functions to apply on each band of the source image.
      `targetType` - the type of data in the image resulting from conversions.
    Returns:
      the image which computes converted values from the given source.
    Since:
      1.4
    See Also:
      - `GridCoverageProcessor​.convert(GridCoverage, MathTransform1D[], Function)`

  - ### resample

    public RenderedImage resample(RenderedImage source, Rectangle bounds, MathTransform toSource)
    Creates a new image which will resample the given image. The resampling operation is defined by a potentially non-linear transform from the new image to the specified source image. That transform should map pixel centers. If that transform produces coordinates that are outside source envelope bounds, then the corresponding pixels in the new image are set to fill values. Otherwise sample values are interpolated using the method given by `getInterpolation()`.

    If the given source is an instance of `ResampledImage`, then this method will use the source of the given source. The intent is to avoid resampling a resampled image; instead this method works on the original data.

    #### Properties used

    This operation uses the following properties in addition to method parameters:
    - Interpolation method (nearest neighbor, bilinear, etc).
    - Fill values for pixels outside source image.
    - Image bounds adjustment flag for deciding whether to use a modified image size if `bounds` size is not divisible by a tile size.
    - Positional accuracy hints for enabling faster resampling at the cost of lower precision.

    #### Result relationship with source

    Changes in the source image are reflected in the returned images if the source image notifies tile observers.
    Parameters:
      `source` - the image to be resampled.
      `bounds` - domain of pixel coordinates of resampled image to create. Fields are updated in-place by this method if the `ImageLayout​.isImageBoundsAdjustmentAllowed` flag is true.
      `toSource` - conversion of pixel center coordinates from resampled image to `source` image.
    Returns:
      resampled image (may be `source`).
    See Also:
      - `GridCoverageProcessor​.resample(GridCoverage, GridGeometry)`

  - ### prefetch

    public RenderedImage prefetch(RenderedImage source, Rectangle areaOfInterest)
    Computes immediately all tiles in the given region of interest, then returns an image with those tiles ready. Computations will use many threads if execution mode is parallel.

    #### Properties used

    This operation uses the following properties in addition to method parameters:
    - Execution mode (parallel or sequential).
    - Error handler (whether to fail if an exception is thrown).
    Parameters:
      `source` - the image to compute immediately (may be `null`).
      `areaOfInterest` - pixel coordinates of the region to prefetch, or `null` for the whole image.
    Returns:
      image with all tiles intersecting the AOI computed, or `null` if the given image was null.
    Throws:
      `ImagingOpException` - if an exception occurred during `RenderedImage​.getTile(int, int)` call. This exception wraps the original exception as its cause.

  - ### visualize

    public RenderedImage visualize(RenderedImage source)
    Returns an image where all sample values are indices of colors in an `IndexColorModel`. If the given image stores sample values as unsigned bytes or short integers, then those values are used as-is (they are not copied or converted). Otherwise this operation will convert sample values to unsigned bytes in order to enable the use of `IndexColorModel`.

    The resulting image is suitable for visualization purposes, but should not be used for computation purposes. There is no guarantee about the number of bands in returned image or about which formula is used for converting floating point values to integer values.

    #### How to specify colors

    The image colors can be controlled by the `Colorizer` set on this image processor. It is possible to specify explicitly the `ColorModel` to use, but this approach is unsafe because it depends on the pixel values after their conversion to the visualization image, which is implementation dependent. A safer approach is to define colors relative to pixel values before their conversions. It can be done in two ways, depending on whether the "org.apache.sis.SampleDimensions" image property is defined or not. Those two ways are described in next sections and can be combined in a chain of fallbacks. For example the following colorizer will choose colors based on sample dimensions if available, or fallback on predefined ranges of pixel values otherwise:

        Function<Category,Color[]>  flexible   = ...;
        Map<NumberRange<?>,Color[]> predefined = ...;
        processor.setColorizer(Colorizer.forCategories(flexible)     // Preferred way.
                       .orElse(Colorizer.forRanges(predefined)));    // Fallback.

    ##### Specifying colors for ranges of pixel values

    When no `SampleDimension` information is available, the recommended way to specify colors is like below. In this example, min and max are minimum and maximum values (inclusive in this example, but they could be exclusive as well) in the source image. Those extrema can be floating point values. This example specifies only one range of values, but arbitrary numbers of non-overlapping ranges are allowed.

        NumberRange<?> range = NumberRange.create(min, true, max, true);
        Color[] colors = {Color.BLUE, Color.MAGENTA, Color.RED};
        processor.setColorizer(Colorizer.forRanges(Map.of(range, colors)));
        RenderedImage visualization = processor.visualize(source, null);

    The map given to the colorizer specifies the colors to use for different ranges of values in the source image. The ranges of values in the returned image may not be the same; this method is free to rescale them. The `Color` arrays may have any length; colors will be interpolated as needed for fitting the ranges of values in the destination image.

    ##### Specifying colors for sample dimension categories

    If `SampleDimension` information is available, a more flexible way to specify colors is to associate colors to category names instead of predetermined ranges of pixel values. The ranges will be inferred indirectly, from the categories themselves encapsulated in sample dimensions. The colors are determined by a function receiving `Category` inputs.

        Map<String,Color[]> colors = Map.of(
            "Temperature", new Color[] {Color.BLUE, Color.MAGENTA, Color.RED},
            "Wind speed",  new Color[] {Color.GREEN, Color.CYAN, Color.BLUE});

        processor.setColorizer(Colorizer.forCategories((category) ->
            colors.get(category.getName().toString(Locale.ENGLISH))));

        RenderedImage visualization = processor.visualize(source, ranges);

    This separation makes easier to apply colors based on criterion other than numerical values. For example, colors could be determined from category name such as "Temperature", or units of measurement. The `Color` arrays may have any length; colors will be interpolated as needed for fitting the ranges of values in the destination image.

    #### Properties used

    This operation uses the following properties in addition to method parameters:
    - Colorizer for customizing the rendered image color model.
    Parameters:
      `source` - the image to recolor for visualization purposes.
    Returns:
      recolored image for visualization purposes only.
    Since:
      1.4
    See Also:
      - `Colorizer​.forRanges(Map)`
      - `Colorizer​.forCategories(Function)`
      - `PlanarImage​.SAMPLE_DIMENSIONS_KEY`

  - ### visualize

    public RenderedImage visualize(RenderedImage source, Rectangle bounds, MathTransform toSource)
    Returns an image as the resampling of the given image followed by a conversion to integer sample values. This is a combination of the following methods, as a single image operation for avoiding creation of an intermediate image step:
    1. `resample(source, bounds, toSource)`
    2. `visualize(resampled)`
    Combining above steps may be advantageous when the `resample(…)` result is not needed for anything else than visualization. If the same resampling may be needed for computational purposes, then it may be more advantageous to keep above method calls separated instead of using this `visualize(…)` method.

    The resulting image is suitable for visualization purposes, but should not be used for computation purposes. There is no guarantee about the number of bands in returned image or about which formula is used for converting floating point values to integer values.

    #### Properties used

    This operation uses the following properties in addition to method parameters:
    - Interpolation method (nearest neighbor, bilinear, etc).
    - Fill values for pixels outside source image.
    - Image bounds adjustment flag for deciding whether to use a modified image size if `bounds` size is not divisible by a tile size.
    - Positional accuracy hints for enabling faster resampling at the cost of lower precision.
    - Colorizer for customizing the rendered image color model.
    Parameters:
      `source` - the image to be resampled and recolored.
      `bounds` - domain of pixel coordinates of resampled image to create. Fields are updated in-place by this method if the `ImageLayout​.isImageBoundsAdjustmentAllowed` flag is true.
      `toSource` - conversion of pixel center coordinates from resampled image to `source` image.
    Returns:
      resampled and recolored image for visualization purposes only.
    Since:
      1.4
    See Also:
      - `resample(RenderedImage, Rectangle, MathTransform)`

  - ### isolines

    public List<NavigableMap<Double,Shape>> isolines(RenderedImage data, double[][] levels, MathTransform gridToCRS)
    Generates isolines at the specified levels computed from data provided by the given image. Isolines will be computed for every bands in the given image. For each band, the result is given as a `Map` where keys are the specified `levels` and values are the isolines at the associated level. If there are no isolines for a given level, there will be no corresponding entry in the map.

    #### Properties used

    This operation uses the following properties in addition to method parameters:
    - Execution mode (parallel or sequential).
    Parameters:
      `data` - image providing source values.
      `levels` - values for which to compute isolines. An array should be provided for each band. If there is more bands than `levels​.length`, the last array is reused for all remaining bands.
      `gridToCRS` - transform from pixel coordinates to geometry coordinates, or `null` if none. Integer source coordinates are located at pixel centers.
    Returns:
      the isolines for specified levels in each band. The `List` size is the number of bands. For each band, the `Map` size is equal or less than `levels[band].length`. Map keys are the specified levels, excluding those for which there are no isolines. Map values are the isolines as a Java2D `Shape`.
    Throws:
      `ImagingOpException` - if an error occurred during calculation.

  - ### equals

    public boolean equals(Object object)
    Returns `true` if the given object is an image processor of the same class with the same configuration.
    Overrides:
      `equals` in class `Object`
    Parameters:
      `object` - the other object to compare with this processor.
    Returns:
      whether the other object is an image processor of the same class with the same configuration.

  - ### hashCode

    public int hashCode()
    Returns a hash code value for this image processor based on its current configuration.
    Overrides:
      `hashCode` in class `Object`
    Returns:
      a hash code value for this processor.

  - ### clone

    public ImageProcessor clone()
    Returns an image processor with the same configuration as this processor.
    Overrides:
      `clone` in class `Object`
    Returns:
      a clone of this image processor.