Make a distinction between inner and outer NA in a raster in R

In R, how could I distinguish between inner and outer NA

in a bitmap with some shape having NA

both around and inside?

In the example below, how could I, for example, select only NA

outside the R logo (i.e., how can I make everything that goes into the logo circle as white)?

library(raster)
r <- raster(system.file("external/rlogo.grd", package="raster"))
r[r>240] = NA
par(mfrow=c(1,2))
plot(r, main='r')
plot(is.na(r), main="is.na(r)")

      

enter image description here

+3


source to share


2 answers


You really don't have many options. This type of analysis usually requires more sophisticated methods. Here, however, this is a simple workaround with a function clump

:

#your inital code
library(raster)
r <- raster(system.file("external/rlogo.grd", package="raster"))
rna <- rc <- r
rna[r>240] = NA
par(mfrow=c(2,2))

#reclass values <=240 to NA (needed for clump function. 
#Here, NAs are used to seperate clumps)
rc[r<=240] <- NA
rc <- clump(rc)

#what you get after applying the clump function
#are homogenous areas that are separated by NAs. 
#Let reclassify all areas with an ID > 8. 
#In this case, these are the areas inside the ring.
rc_reclass <- rc
rc_reclass[rc_reclass>8]  <- 100

#let do some plotting
plot(r, main='r')
plot(is.na(rna), main="is.na(r)")
plot(rc, main="clumps")
plot(rc_reclass, main="clumps reclass")

      



enter image description here

+2


source


I agree with @maRtin, it's a little tricky. Not only do you have no NoData value allocated, but the image is a little messy.

However, I think I found a way that is slightly better than the clump

one that uses a spatial domain to separate areas:

First, I get the focal values ​​of the pixel neighborhoods:

#make copy
r2 <- r

# focal values
fv <- getValuesFocal(r2,ngb = c(3,3))

      

Then I first exclude all pixels that have a neighborhood value greater than 242.8. It was purely trial and error, but it works well.

ix <- rowMeans(fv,na.rm = T) > 242.8
r2[ix] <- NA

      

In fact, you may already consider it acceptable. The only problem is that there is a small border around the range that should be NA.



enter image description here

So somehow I need to get rid of the rest of the NA pixels. This is what I am trying to do with an iterative exception. On each iteration, I see if there are pixels that still have NA values ​​and the maximum value is below a certain threshold. Again, there are a lot of games around and I think you could achieve a better result than this, but I think this is the way to go.

while (TRUE){

  fv <- getValuesFocal(r2,ngb = c(3,3))
  ix <- apply(fv,1,function(x) max(x,na.rm=T)) > 243 & rowSums(is.na(fv)) > 0

  if (any(ix)){

    r2[ix] <- NA


  } else {
    break
  }
}

      

After a few iterations, I get this:

enter image description here

Obviously there are already some pixels that shouldn't be, maybe it can be done with a little more tinkering.

Another interesting thought would be looking at all three channels. If you load the image using brick

, you can get the RGB channels. I've tried several things like max, mean, mode, sd, etc. but to no avail.

+2


source







All Articles