Skip to main content

I have an 8-bit mixed raster, where there are numeric values from 0 to 100, and then certain values over 200 are classified, 200 = water, 205 = snow, etc.

Unfortunately there is a bit of noise in the data where there are single pixels coded as water (200).

 

I would like to remove these individual pixels, replacing them with either a mean or majority filter (of 0-100 values only), without affecting any other pixel including 200 value pixels that are contiguous (so a straight convolution of the raster is out).

The closest thing I can find is the Erdas Imagine Clump (to identify contiguous pixel groups) and either Sieve or Eliminate to replace groups smaller than a given threshold. https://hexagongeospatial.fluidtopics.net/reader/fH0o7KrMKUViXGUeoilQuA/cAzM7j6Akx80Fj5zsuhRFQ

Any ideas of an efficient way to do this in FME? I have approximate 275GB of data to process.

HI @jdh,

I'm throwing out an idea that one of my fellow Safers suggested.  This might be doable with some RasterConvolvers and RasterExpressionEvaluators  (Note: he didn't test this, and there may be some things to iron out):

1. Generate a "replacement" raster that we'll use to fill in the noise pixels. We can do this with a RasterConvolver, using the mean/majority operation, where the weights are

1 1 1
1 0 1
1 1 1

That is, we want to get the mean/majority of the surrounding cells only.

2. Generate a "mask" raster that we'll use to determine where to apply the replacement raster. This can be done with a couple steps: 

a. Put the original raster through a RasterExpressionEvaluator to map water (200) to 1, and everything else to 0.

b. Use a RasterConvolver with the Sum operation and no Divisor, where the weights are

0 1 0
1 10 1
0 1 0

This will produce a raster where the cell value tells us something about the cell and its surroundings:

 0, 4]   -> the center cell was not water, but there were surrounding water cells

 

10       -> the center cell was water, there were no surrounding water cells

 

h11, 14] -> the center cell was water, there were some surrounding water cells

This mask shows us which values to replace: we only want to replace values in the original raster where the mask = 10.

3. Use a couple RasterExpressionEvaluators to combine the original and replacement rasters, with the mask. That is, when the mask = 10 take the value from the replacement raster. Otherwise, take the original value.

I hope this helps!


HI @jdh,

I'm throwing out an idea that one of my fellow Safers suggested.  This might be doable with some RasterConvolvers and RasterExpressionEvaluators  (Note: he didn't test this, and there may be some things to iron out):

1. Generate a "replacement" raster that we'll use to fill in the noise pixels. We can do this with a RasterConvolver, using the mean/majority operation, where the weights are

1 1 1
1 0 1
1 1 1

That is, we want to get the mean/majority of the surrounding cells only.

2. Generate a "mask" raster that we'll use to determine where to apply the replacement raster. This can be done with a couple steps: 

a. Put the original raster through a RasterExpressionEvaluator to map water (200) to 1, and everything else to 0.

b. Use a RasterConvolver with the Sum operation and no Divisor, where the weights are

0 1 0
1 10 1
0 1 0

This will produce a raster where the cell value tells us something about the cell and its surroundings:

 0, 4]   -> the center cell was not water, but there were surrounding water cells

 

10       -> the center cell was water, there were no surrounding water cells

 

h11, 14] -> the center cell was water, there were some surrounding water cells

This mask shows us which values to replace: we only want to replace values in the original raster where the mask = 10.

3. Use a couple RasterExpressionEvaluators to combine the original and replacement rasters, with the mask. That is, when the mask = 10 take the value from the replacement raster. Otherwise, take the original value.

I hope this helps!

Step 2B is an excellent suggestion, and the piece I was missing. The colleague who suggested it wouldn't happen to be Dmitri, would it? 


Step 2B is an excellent suggestion, and the piece I was missing. The colleague who suggested it wouldn't happen to be Dmitri, would it?

Oh I'm glad this helped! Good guess, but it wasn't Dmitri.


HI @jdh,

I'm throwing out an idea that one of my fellow Safers suggested.  This might be doable with some RasterConvolvers and RasterExpressionEvaluators  (Note: he didn't test this, and there may be some things to iron out):

1. Generate a "replacement" raster that we'll use to fill in the noise pixels. We can do this with a RasterConvolver, using the mean/majority operation, where the weights are

1 1 1
1 0 1
1 1 1

That is, we want to get the mean/majority of the surrounding cells only.

2. Generate a "mask" raster that we'll use to determine where to apply the replacement raster. This can be done with a couple steps: 

a. Put the original raster through a RasterExpressionEvaluator to map water (200) to 1, and everything else to 0.

b. Use a RasterConvolver with the Sum operation and no Divisor, where the weights are

0 1 0
1 10 1
0 1 0

This will produce a raster where the cell value tells us something about the cell and its surroundings:

 0, 4]   -> the center cell was not water, but there were surrounding water cells

 

10       -> the center cell was water, there were no surrounding water cells

 

h11, 14] -> the center cell was water, there were some surrounding water cells

This mask shows us which values to replace: we only want to replace values in the original raster where the mask = 10.

3. Use a couple RasterExpressionEvaluators to combine the original and replacement rasters, with the mask. That is, when the mask = 10 take the value from the replacement raster. Otherwise, take the original value.

I hope this helps!

Thank you @nampreetatsafe​ for this very creative approach. My impression is that those suggested convolution filters would only address cases of individual single pixels (which was indeed the requestor's use case). If one wants to fill up holes made up of up to 5 pixels for instance, then I assume running the GDAL's Sieve tool (eventually using a SystemCaller) would still be the best solution.


Reply