I have a raster image (20231031_110019.png, although it could be any format, jpg, png, bmp) and would like to individually calculate the areas of discrete regions (I will call them 'islands' although they are actually areas of fungal growth on a petri dish) in this raster.
I am trying to use the spatstat package in R to do this. I found a post stating the following code can do this:
library(spatstat)
## create example data
dd <- dilation(redwood, 0.5, polygonal=FALSE)
## find connected components
P <- connected(dd)
## convert to a tessellation
B <- tess(image=P)
## compute area of each tile
answer <- tile.areas(B)
An owin object (redwood in the above instance) is needed to execute the dilation command and I cannot find a way to convert a raster into an owin object without the maptools package, which is no longer available on CRAN. I also have been unable to install an archived version maptools using devtools.
What would a workaround be for this?
Is there another way to convert a raster into a useable object?
Is there an equally straightforward method for calculating the areas of the fungal 'islands' in my raster image?
Many thanks!
Emily
With "terra" you can first identify the patches, and then count the number of cells in each patch. You could then multiply that number with the actual size of each pixel to get areas. "patches" are defined as islands of values in a sea of missing values; so in this case 255 needs to be set as the missing values flag.
library(terra)
x <- rast("https://i.sstatic.net/XjIVG.png")
NAflag(x) <- 255
p <- patches(x)
f <- freq(p)
f
# layer value count
#1 1 1 36015
#2 1 2 17132
#3 1 3 15571
#4 1 4 15345
#5 1 5 38977
#6 1 6 48821
#7 1 7 49660
#8 1 8 41246
Some illustration
bnds <- as.polygons(p)
area <- subst(p, f$value, f$count)
plot(trim(p, 100), main="ID")
plot(trim(area, 100), main="area")
lines(bnds, col="dark gray")
text(bnds, cex=.75)
To go from pixel counts to area you need to know the size of your image in the real world. Let's say it has a height of 20 and a width of 15 cm. In that case you could do
ext(x) <- c(0, 15, 0, 20)
cell_size <- prod(res(x))
f$area <- f$count * cell_size
head(f)
# layer value count area
#1 1 1 36015 0.900375
#2 1 2 17132 0.428300
#3 1 3 15571 0.389275
#4 1 4 15345 0.383625
#5 1 5 38977 0.974425
#6 1 6 48821 1.220525
With area expressed in cm2.
At that point you could also go the polygons-route suggested by Eduardo like this:
library(terra)
x <- rast("https://i.sstatic.net/XjIVG.png")
NAflag(x) <- 255
ext(x) <- c(0, 15, 0, 20)
crs(x) <- "local"
a <- as.polygons(x) |> disagg()
a$id <- 1:nrow(a)
a$area <- expanse(a, transform=FALSE)
plot(a, "area")
text(a)
head(data.frame(a))
# XjIVG id area
#1 0 1 0.900375
#2 0 2 0.428300
#3 0 3 0.389275
#4 0 4 0.383625
#5 0 5 0.974425
#6 0 6 1.220525
PS: Note that if this were geographic data you would want to set the coordinate reference system with crs<-
and use cellSize
to get the actual size of the cells in the real world and then use zonal
to compute the area of each patch.