Skip to content

Commit

Permalink
removed watermask operator and used the classifier directly (#2)
Browse files Browse the repository at this point in the history
* removed watermask operator and used the classifier directly

* inlined watermask also for OLCI and improved S2
  • Loading branch information
marpet authored Aug 13, 2019
1 parent 352e7b9 commit 8eda856
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 67 deletions.
4 changes: 4 additions & 0 deletions idepix-olci/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@
<artifactId>snap-raster</artifactId>
<version>${snap.version}</version>
</dependency>
<dependency>
<groupId>org.esa.snap</groupId>
<artifactId>snap-watermask</artifactId>
</dependency>

<dependency>
<groupId>org.esa.snap</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,18 @@
import org.esa.snap.idepix.core.util.IdepixIO;
import org.esa.snap.idepix.core.util.IdepixUtils;
import org.esa.snap.idepix.core.util.SchillerNeuralNetWrapper;
import org.esa.snap.watermask.operator.WatermaskClassifier;

import java.awt.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.Calendar;
import java.util.Map;

import static org.esa.snap.idepix.core.IdepixConstants.LAND_WATER_MASK_RESOLUTION;
import static org.esa.snap.idepix.core.IdepixConstants.OVERSAMPLING_FACTOR_X;
import static org.esa.snap.idepix.core.IdepixConstants.OVERSAMPLING_FACTOR_Y;

/**
* OLCI pixel classification operator.
* Processes both land and water, so we get rid of separate land/water merge operator.
Expand Down Expand Up @@ -80,9 +85,6 @@ public class IdepixOlciClassificationOp extends Operator {
@SourceProduct(alias = "rhotoa")
private Product rad2reflProduct;

@SourceProduct(alias = "waterMask", optional = true)
private Product waterMaskProduct;

@SourceProduct(alias = "o2Corr", optional = true)
private Product o2CorrProduct;

Expand All @@ -91,7 +93,6 @@ public class IdepixOlciClassificationOp extends Operator {


private Band[] olciReflBands;
private Band landWaterBand;

private Band surface13Band;
private Band trans13Band;
Expand All @@ -113,6 +114,7 @@ public class IdepixOlciClassificationOp extends Operator {
private GeometryFactory gf;
private Polygon arcticPolygon;
private Polygon antarcticaPolygon;
private WatermaskClassifier watermaskClassifier;


@Override
Expand All @@ -121,13 +123,18 @@ public void initialize() throws OperatorException {
nnInterpreter = IdepixOlciCloudNNInterpreter.create();
readSchillerNeuralNets();
createTargetProduct();
if (useSrtmLandWaterMask) {
try {
watermaskClassifier = new WatermaskClassifier(LAND_WATER_MASK_RESOLUTION,
OVERSAMPLING_FACTOR_X,
OVERSAMPLING_FACTOR_Y);
} catch (IOException e) {
throw new OperatorException("Could not initialise SRTM land-water mask", e);
}
}

initSeaIceClassifier();

if (waterMaskProduct != null && useSrtmLandWaterMask) {
landWaterBand = waterMaskProduct.getBand("land_water_fraction");
}

if (o2CorrProduct != null) {
surface13Band = o2CorrProduct.getBand("surface_13");
trans13Band = o2CorrProduct.getBand("trans_13");
Expand Down Expand Up @@ -188,10 +195,6 @@ private void createTargetProduct() throws OperatorException {

@Override
public void computeTileStack(Map<Band, Tile> targetTiles, Rectangle rectangle, ProgressMonitor pm) throws OperatorException {
Tile waterFractionTile = null;
if (landWaterBand != null) {
waterFractionTile = getSourceTile(landWaterBand, rectangle);
}

Tile surface13Tile = null;
Tile trans13Tile = null;
Expand All @@ -216,13 +219,15 @@ public void computeTileStack(Map<Band, Tile> targetTiles, Rectangle rectangle, P
nnTargetTile = targetTiles.get(targetProduct.getBand(IdepixConstants.NN_OUTPUT_BAND_NAME));
}
try {
GeoCoding geoCoding = sourceProduct.getSceneGeoCoding();
for (int y = rectangle.y; y < rectangle.y + rectangle.height; y++) {
checkForCancellation();
for (int x = rectangle.x; x < rectangle.x + rectangle.width; x++) {
int waterFraction = -1;
if (waterFractionTile != null) {
waterFraction = waterFractionTile.getSampleInt(x, y);
if (useSrtmLandWaterMask) {
waterFraction = watermaskClassifier.getWaterMaskFraction(geoCoding, x, y);
}

initCloudFlag(olciQualityFlagTile, targetTiles.get(cloudFlagTargetBand), olciReflectanceTiles, y, x);
final boolean isBright = olciQualityFlagTile.getSampleBit(x, y, IdepixOlciConstants.L1_F_BRIGHT);
cloudFlagTargetTile.setSample(x, y, IdepixConstants.IDEPIX_BRIGHT, isBright);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ public class IdepixOlciOp extends BasisOp {
private Product rad2reflProduct;
private Product ctpProduct;
private Product o2CorrProduct;
private Product waterMaskProduct;

private Map<String, Product> classificationInputProducts;
private Map<String, Object> classificationParameters;
Expand Down Expand Up @@ -235,13 +234,6 @@ private void preProcess() {
outputCtp);
}

if (useSrtmLandWaterMask) {
HashMap<String, Object> waterMaskParameters = new HashMap<>();
waterMaskParameters.put("resolution", IdepixConstants.LAND_WATER_MASK_RESOLUTION);
waterMaskParameters.put("subSamplingFactorX", IdepixConstants.OVERSAMPLING_FACTOR_X);
waterMaskParameters.put("subSamplingFactorY", IdepixConstants.OVERSAMPLING_FACTOR_Y);
waterMaskProduct = GPF.createProduct("LandWaterMask", waterMaskParameters, sourceProduct);
}
}

private void setClassificationParameters() {
Expand All @@ -261,7 +253,6 @@ private void setClassificationInputProducts() {
classificationInputProducts = new HashMap<>();
classificationInputProducts.put("l1b", sourceProduct);
classificationInputProducts.put("rhotoa", rad2reflProduct);
classificationInputProducts.put("waterMask", waterMaskProduct);
if (considerCloudsOverSnow) {
classificationInputProducts.put("o2Corr", o2CorrProduct);
}
Expand Down
4 changes: 4 additions & 0 deletions idepix-s2msi/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
</description>

<dependencies>
<dependency>
<groupId>org.esa.snap</groupId>
<artifactId>idepix-core</artifactId>
</dependency>
<dependency>
<groupId>org.esa.snap</groupId>
<artifactId>ceres-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package org.esa.snap.idepix.s2msi;

import com.bc.ceres.core.ProgressMonitor;
import org.esa.snap.idepix.s2msi.util.S2IdepixConstants;
import org.esa.snap.idepix.s2msi.util.S2IdepixUtils;
import org.esa.snap.core.datamodel.Band;
import org.esa.snap.core.datamodel.FlagCoding;
import org.esa.snap.core.datamodel.GeoCoding;
Expand All @@ -21,11 +19,19 @@
import org.esa.snap.core.gpf.annotations.TargetProduct;
import org.esa.snap.core.util.ProductUtils;
import org.esa.snap.dem.gpf.AddElevationOp;
import org.esa.snap.idepix.s2msi.util.S2IdepixConstants;
import org.esa.snap.idepix.s2msi.util.S2IdepixUtils;
import org.esa.snap.watermask.operator.WatermaskClassifier;

import java.awt.Color;
import java.awt.Rectangle;
import java.io.IOException;
import java.util.Map;

import static org.esa.snap.idepix.core.IdepixConstants.LAND_WATER_MASK_RESOLUTION;
import static org.esa.snap.idepix.core.IdepixConstants.OVERSAMPLING_FACTOR_X;
import static org.esa.snap.idepix.core.IdepixConstants.OVERSAMPLING_FACTOR_Y;

/**
* Sentinel-2 (MSI) pixel classification operator.
*
Expand All @@ -42,6 +48,7 @@ public class S2IdepixClassificationOp extends Operator {
public static final double DELTA_RHO_TOA_442_THRESHOLD = 0.03;
public static final double RHO_TOA_442_THRESHOLD = 0.03;


private static final float WATER_MASK_SOUTH_BOUND = -58.0f;
// private static final String VALID_PIXEL_EXPRESSION = "detector_footprint_B1 " +
// "and detector_footprint_B2 " +
Expand Down Expand Up @@ -145,8 +152,8 @@ public class S2IdepixClassificationOp extends Operator {
@SourceProduct(alias = "l1c", description = "The MSI L1C source product.")
Product sourceProduct;

@SourceProduct(alias = "waterMask", optional = true)
private Product waterMaskProduct;
// @SourceProduct(alias = "waterMask", optional = true)
// private Product waterMaskProduct;

@TargetProduct(description = "The target product.")
Product targetProduct;
Expand Down Expand Up @@ -180,6 +187,7 @@ public class S2IdepixClassificationOp extends Operator {
Band ndwiBand;

private Product elevationProduct;
private WatermaskClassifier watermaskClassifier;


// public static final String NN_NAME = "20x4x2_1012.9.net"; // Landsat 'all' NN
Expand All @@ -197,13 +205,20 @@ public void initialize() throws OperatorException {
Color.GREEN, 0.0);
validPixelMask.setOwner(getSourceProduct());

boolean isHigherResolutionInput = sourceProduct.getBand("B2") != null
&& sourceProduct.getBand("B2").getGeoCoding().getMapCRS().getName().toString().contains("UTM")
&& sourceProduct.getBand("B2").getImageToModelTransform().getScaleX() < LAND_WATER_MASK_RESOLUTION;
try {
watermaskClassifier = new WatermaskClassifier(LAND_WATER_MASK_RESOLUTION,
isHigherResolutionInput ? 1 : OVERSAMPLING_FACTOR_X,
isHigherResolutionInput ? 1 : OVERSAMPLING_FACTOR_Y);
} catch (IOException e) {
throw new OperatorException("Could not initialise SRTM land-water mask", e);
}

// readSchillerNeuralNets();
createTargetProduct();

if (waterMaskProduct != null) {
landWaterBand = waterMaskProduct.getBand("land_water_fraction");
}

if (sourceProduct.containsBand(S2IdepixConstants.ELEVATION_BAND_NAME)) {
elevationProduct = sourceProduct;
} else {
Expand All @@ -226,12 +241,7 @@ public void computeTileStack(Map<Band, Tile> targetTiles, Rectangle rectangle, P
s2ReflectanceTiles[i] = getSourceTile(s2MsiReflBands[i], rectangle);
}

Tile waterFractionTile = null;
if (waterMaskProduct != null) {
waterFractionTile = getSourceTile(landWaterBand, rectangle);
}

GeoPos geoPos = null;
final Band cloudFlagTargetBand = targetProduct.getBand(S2IdepixUtils.IDEPIX_CLASSIF_FLAGS);
final Tile cloudFlagTargetTile = targetTiles.get(cloudFlagTargetBand);

Expand All @@ -250,6 +260,7 @@ public void computeTileStack(Map<Band, Tile> targetTiles, Rectangle rectangle, P
final Tile elevationTile = getSourceTile(elevationBand, rectangle);
final Tile validPixelTile = getSourceTile(validPixelMask, rectangle);


try {
for (int y = rectangle.y; y < rectangle.y + rectangle.height; y++) {
checkForCancellation();
Expand All @@ -268,9 +279,9 @@ public void computeTileStack(Map<Band, Tile> targetTiles, Rectangle rectangle, P
// set up pixel properties for given instruments...
S2IdepixAlgorithm s2MsiAlgorithm = createS2MsiAlgorithm(s2ReflectanceTiles,
szaTile, vzaTile, saaTile, vaaTile,
waterFractionTile,
elevationTile,
validPixelTile,
watermaskClassifier,
s2MsiReflectance,
y,
x);
Expand Down Expand Up @@ -409,10 +420,9 @@ private void copyReflectancesForCloudShadow() {

private S2IdepixAlgorithm createS2MsiAlgorithm(Tile[] s2MsiReflectanceTiles,
Tile szaTile, Tile vzaTile, Tile saaTile, Tile vaaTile,
Tile waterFractionTile,
Tile elevationTile,
Tile validPixelTile,
float[] s2MsiReflectances,
WatermaskClassifier classifier, float[] s2MsiReflectances,
int y,
int x) {
S2IdepixAlgorithm s2MsiAlgorithm = new S2IdepixAlgorithm();
Expand All @@ -422,11 +432,9 @@ private S2IdepixAlgorithm createS2MsiAlgorithm(Tile[] s2MsiReflectanceTiles,
}
s2MsiAlgorithm.setRefl(s2MsiReflectances);

boolean isLand = false;
if (waterMaskProduct != null) {
final int waterFraction = waterFractionTile.getSampleInt(x, y);
isLand = isLandPixel(x, y, waterFraction, s2MsiAlgorithm);
}
final byte waterFraction = classifier.getWaterMaskFraction(sourceProduct.getSceneGeoCoding(), x, y);

boolean isLand = isLandPixel(x, y, waterFraction, s2MsiAlgorithm);
s2MsiAlgorithm.setIsLand(isLand);

final double sza = szaTile.getSampleDouble(x, y);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@
description = "Pixel identification and classification for Sentinel-2 MSI.")
public class S2IdepixOp extends Operator {

private static final int LAND_WATER_MASK_RESOLUTION = 50;
private static final int OVERSAMPLING_FACTOR_X = 3;
private static final int OVERSAMPLING_FACTOR_Y = 3;

@Parameter(defaultValue = "true",
label = " Write TOA reflectances to the target product",
Expand Down Expand Up @@ -132,7 +129,6 @@ public class S2IdepixOp extends Operator {
@TargetProduct(description = "The target product.")
private Product targetProduct;

private Product waterMaskProduct;
private Product postProcessingProduct;
private Product s2ClassifProduct;

Expand All @@ -149,11 +145,9 @@ public void initialize() throws OperatorException {
}

private void processSentinel2() {
processLandWaterMask();

Map<String, Product> inputProducts = new HashMap<>(4);
inputProducts.put("l1c", sourceProduct);
inputProducts.put("waterMask", waterMaskProduct);

final Map<String, Object> pixelClassificationParameters = createPixelClassificationParameters();

Expand Down Expand Up @@ -181,23 +175,6 @@ private void processSentinel2() {
setTargetProduct(targetProduct);
}

private void processLandWaterMask() {
boolean isHigherResolutionInput = sourceProduct.getBand("B2") != null
&& sourceProduct.getBand("B2").getGeoCoding().getMapCRS().getName().toString().contains("UTM")
&& sourceProduct.getBand("B2").getImageToModelTransform().getScaleX() < LAND_WATER_MASK_RESOLUTION;
HashMap<String, Object> waterMaskParameters = new HashMap<>();
waterMaskParameters.put("resolution", LAND_WATER_MASK_RESOLUTION);
if (isHigherResolutionInput) {
System.out.println("No subsampling of " + sourceProduct.getBand("B2").getImageToModelTransform().getScaleX() + " m product necessary to access " + LAND_WATER_MASK_RESOLUTION + " m water mask");
waterMaskParameters.put("subSamplingFactorX", 1);
waterMaskParameters.put("subSamplingFactorY", 1);
} else {
waterMaskParameters.put("subSamplingFactorX", OVERSAMPLING_FACTOR_X);
waterMaskParameters.put("subSamplingFactorY", OVERSAMPLING_FACTOR_Y);
}
waterMaskProduct = GPF.createProduct("LandWaterMask", waterMaskParameters, sourceProduct);
}

private void computePostProcessProduct() {
postProcessingProduct = s2ClassifProduct;
Product cloudBufferProduct = null;
Expand Down

0 comments on commit 8eda856

Please sign in to comment.