CHM_smooth = CHM %>% # any values below 0, set to 0 terra::clamp(lower = 0, values = TRUE) %>% # fill in NA values with their neighbors' min value terra::focal(w=3, fun="min", na.policy="only", na.rm=TRUE, expand = FALSE) %>% ifel(!is.na(crop(DTM_smooth, .)) & is.na(.), 0, .) %>% # apply a median filter only to cells where there are big changes in Z value ifel(terra::terrain(., "TRI") > 5, terra::focal(., w = 3, fun = "median", na.policy = "omit", na.rm = TRUE, expand = FALSE), ., filename = paste0(root_dir, "/output/_4_CHM_raster/CHM_smooth.tif"), overwrite = TRUE)
I only had a look at the output product, but this currently seems to replace NA values by 0 values. It might be easier to debug/understand without the tidyverse pipe/concatenation (cf. also my comment below).