Snappy: Mask (Clouds and others)

I opened a S2-File with product = ProductIO.readProduct(src_path) and would like to use one of the Masks (e.g. cirrus_clouds_10m) to overwrite the values in the RGB-Bands. How can I access them?

I tried it with product.getMaskImage('cirrus_clouds_10m') because I thought it could be the right method but I receive an error:

Traceback (most recent call last):
Debug Probe, prompt 1, line 1
RuntimeError: no matching Java method overloads found

Does anyone has an idea how to access correctly these masks?

Thanks in advance.

You should call product.getMaskGroup().get('cirrus_clouds_10m')
This will give you a Mask. On this you can call getSourceImage for example.

The method you have called is intended to create a new mask by an expression. It needs also a second parameter, the associatedRaster. That’s why you’ve got ‘no matching Java method overloads found’.


Good morning everyone

I finally had some time to continue to work on this project again. In my script I implemented now a code to read out the mask-data:

src_data= ProductIO.readProduct(src_path)

mask_data = src_data.getMaskGroup().get('cirrus_clouds_60m').getProduct()
mask_width = mask_data.getSceneRasterWidth()
mask_height = mask_data.getSceneRasterHeight()

Now I would like to go through every pixel. If the value of the mask is equal to e.g. “not a cloud pixel”, then it should copy the pixel-value of the src_data to the new output product.

for w_i in range(mask_width):
    for h_i in range(mask_width):
         print <pixelvalue at position (w_i,h_i)>
         if <pixelvalue at position (w_i,h_i)> != 0:
             new_data = src_data.<pixelvalue at position (w_i,h_i)>

How can that be done? In the Manual and in the example code I can find a function product.readPixels(0, 0, w, h, rad13_data) for it. But I could not find any description about it in Product.

Does anyone know how to read such a single pixel value out of a product?

Thank you very much.

PS @marpet: Where does the “b” in the description come from? Should that be “p”?

from snappy import ProductIO
import numpy as np
import matplotlib.pyplot as plt

p = ProductIO.readProduct(‘snappy/testdata/MER_FRS_L1B_SUBSET.dim’)
rad13 = p.getBand(‘radiance_13’)
w = b.getRasterWidth()
h = b.getRasterHeight()

rad13_data = np.zeros(w * h, np.float32)
b.readPixels(0, 0, w, h, rad13_data)
rad13_data.shape = h, w
imgplot = plt.imshow(rad13_data)

Does nobody access S2-data via snappy? :cry:

1 Like

The b should actually be rad13
I have update the example code.

Instead of mask_data.getSceneRasterWidth() you should use mask_data.getRasterWidth(), otherwise you get the width and height of the whole scene which might be different as the one of the band respectively mask.
How reading and writing can be done is shown in one of the examples you can find in the snappy folder.

I think this one is of interest for you.

I am not sure whether this thread was fully answered. Reading the mask seems to be more complicated than I thought.
So the Mask was extracted with product.getMaskGroup().get(‘cirrus_clouds_10m’), but now how do you read through this mask in the ‘for’ loop?

When I do getValidMaskImage() on my bands, I get a mask, but not the one I get from the line above. It’s a plain mask with only 0 values. This is a print of the ValidMaskImage:
PlanarImage[minX=0 minY=0 width=10980 height=10980 tileGridXOffset=0 tileGridYOffset=0 tileWidth=512 tileHeight=512 sampleModel=ComponentSampleModelJAI: dataType=0 numBands=1 width=512 height=512 bandOffsets=[ 0 ] colorModel=ColorModel: #pixelBits = 8 numComponents = 1 color space = java.awt.color.ICC_ColorSpace@1846ad0f transparency = 1 has alpha = false isAlphaPre = false]

So essentially my question is: how do you associate/assign the cloud mask as a validMask to the bands (therefore replacing the one printed above) when you read through them in the for loop like in the example:
b4.readValidMask(0, y, width, 1, v4)
b8.readValidMask(0, y, width, 1, v8)

It will run as such, but the validMask that it reads is not the cloud mask that I extracted, and the output ndvi image is created as if no mask was applied.
I tried to addMask to b4 and b8, but that does not work. And why would I need to add it as I extracted it from that same product.

Thanks and have a good day!

I have tried two identical masks in the following way:

  1. validPixelMask = product.getMaskGroup().get(‘opaque_clouds_10m’)

2.Color = jpy.get_type(‘java.awt.Color’)
BandMathsType = jpy.get_type(‘org.esa.snap.core.datamodel.Mask$BandMathsType’)
validPixelMask = BandMathsType.create(“cloud_mask”, None, width, height, ‘opaque_clouds_10m’, Color.GREEN, 0.0)

The first one cannot be read using validPixelMask.readValidMask(0, y, width, 1, v4), while the second one can, Although they are both org.esa.snap.core.datamodel.Mask.

Maybe this will help you understand my problem.


1 Like

When you call getValidMaskImage() on the result of product.getMaskGroup().get('opaque_clouds_10m') then you retrieve the valid mask for the cirrus mask. It is possible that masks of valid and invalid areas. That’s what the valid mask will provide.

If you already have a mask you can use getSampleInt(x, y) to retrieve the value of the mask at xy-position. The value will be 0 or 255.
You can also get the image of the mask. For this use mask.getSourceImage(). From this image you can get the data by sourceImage.getData().getSample(x, y, 0).

You can also change the valid mask of band by the the valid pixel expression and the no-data value.
For example
b4.setValidPixelExpression(‘opaque_clouds_10m == 0’)
If you now do b4.getValidMaskImage() this image should reflect the settings.
Should also work with b4.readValidMask(0, y, width, 1, v4).

I hope this makes it clearer and answers at least some of your questions.

Top notch :+1:
It’s working exactly as intended. The setValidPixelExpression was what I was missing to “enable” that valid mask over the bands.

Thanks once again.