How to develop a multi-level source image

Purpose

In the SNAP product data model, bands obtain their pixel data from source images which are in instances of type com.bc.ceres.glevel.MultiLevelImage. A MultiLevelImage is a actually a java.awt.image.RenderedImage with the additional capability to serve sub-sampled versions of itself for given resolution levels ranging from zero to maximum value. Level zero is the MultiLevelImage itself. MultiLevelImages are designed to somehow provide (e.g. compute or read) pixel data at requested resolution levels and for requested image tiles at that resolution level. A number of sub-classes exists as well as a number of helper classes.

Example

The following example explains how a band "magnitude" is computed from two input bands "real" and "imag" using the following helper classes:

  • com.bc.ceres.glevel.support.DefaultMultiLevelImage - a default implementation of a multi-level images that requires a multi-level source.
  • com.bc.ceres.glevel.support.AbstractMultiLevelSource - an abstract, partly implementation of a multi-level source which requires implementors to provide a RenderedImage for a given resolution level.
  • org.esa.beam.jai.RasterDataNodeOpImage - an abstract, partly implementation of a JAI OpImage for raster data nodes which requires implementors to prvide the actual pixel computation for a an image tile.

 

Multi-level Source Image
static Product createTestProduct(int width, int height, int tileSize, int numResolutionsMax) {
    Product product = new Product("test", "test", width, height);
    product.setPreferredTileSize(tileSize, tileSize);
    product.setNumResolutionsMax(numResolutionsMax);

    Band realBand = product.addBand("real", "X * X - Y * Y", ProductData.TYPE_FLOAT64);
    Band imagBand = product.addBand("imag", "2 * X * Y", ProductData.TYPE_FLOAT64);
    Band magnitudeBand = product.addBand("magnitude", ProductData.TYPE_FLOAT64);

    MultiLevelModel multiLevelModel = ImageManager.getMultiLevelModel(realBand);
    MultiLevelSource multiLevelSource = new AbstractMultiLevelSource(multiLevelModel) {

        @Override
        public void reset() {
            super.reset();
            magnitudeBand.fireProductNodeDataChanged();
        }

        @Override
        public RenderedImage createImage(int level) {
            return new MagnitudeOpImage(magnitudeBand, ResolutionLevel.create(getModel(), level),
                                        realBand, imagBand);
        }
    };

    magnitudeBand.setSourceImage(new DefaultMultiLevelImage(multiLevelSource));
    return product;
}

static class MagnitudeOpImage extends RasterDataNodeOpImage {

    private final Band realBand;
    private final Band imagBand;

    public MagnitudeOpImage(Band outputBand, ResolutionLevel level, Band realBand, Band imagBand) {
        super(outputBand, level);
        this.realBand = realBand;
        this.imagBand = imagBand;
    }

    @Override
    protected void computeProductData(ProductData outputData, Rectangle region) throws IOException {
        ProductData realData = getRawProductData(realBand, region);
        ProductData imagData = getRawProductData(imagBand, region);
        int numElems = region.width * region.height;
        for (int i = 0; i < numElems; i++) {
            double real = realData.getElemDoubleAt(i);
            double imag = imagData.getElemDoubleAt(i);
            double result = Math.sqrt(real * real + imag * imag);
            outputData.setElemDoubleAt(i, result);
        }
    }
}

Multi-level Source Image
 

 

References:

SNAP-46 - Getting issue details... STATUS