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)")
source to share
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")
source to share
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.
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:
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.
source to share