Fractional Cover Modeling with Planet Tanager and Multivariate Random Forests
Author
Mickey Campbell & Phil Dennison
Published
August 27, 2026
Introduction
The purpose of this document is to demonstrate the application of multivariate random forests (MRFs) for predicting fractional cover using Planet Tanager hyperspectral imagery. Changes in fractional cover correspond to vegetation phenology, as well as drought, wildfire, disease, and other ecosystem disturbances. Fractional cover is also useful for mapping crop tillage practices. Our previous work has found that MRFs have robust capabilities for accurately mapping fractional cover, and we’re proposing to use Tanager scenes in combination with field-measured fractional cover to validate MRF fractional cover mapping.
MRFs are random forests that are trained to predict multiple response variables simultaneously (Ishwaran et al. 2008). Despite their limited use to date in the field of remote sensing, MRFs come with all of the same benefits of their much more popular univariate counterparts (e.g., handling of large, correlated predictor sets, ability to leverage non-linear predictor-response relationships, etc.) (Breiman 2001). In the context of fractional cover mapping, they have the added benefit of an innate sum-to-one constraint for predictions. In building trees that minimize loss across multiple response variables, MRFs leverage the complementary nature of predictor-response relationships (i.e., if one fraction increases, necessarily the other(s) must decrease). In other words, the covariance structure of the response variables helps the forest learn meaningful patterns among the predictor variables, enhancing predictive ability.
Synthetic Mixtures
We will train and test our MRF model using 10,000 synthetic spectral mixtures that are feature random mixtures of green vegetation (GV), non-photosynthetic vegetation (NPV), and soil endmembers, derived from field and lab spectra (Dennison et al. 2023). Each mixture has a known fraction of GV, NPV, and soil, along with spectral reflectance at 159 bands (10nm band spacing) between 440 and 2400nm (440 - 1320nm; 1490 - 1770nm; 2000 - 2400nm). The spectra were darkened to approximate a real-world sample of reflectance, and noise was added to simulate satellite sensor signal-to-noise ratios.
Here is a snapshot of these data:
Show code
library(data.table)library(knitr)library(Ternary)library(randomForestSRC)library(terra)library(rhdf5)# suppress terra progress barsterraOptions(progress =0)# define directory structuremain_dir <-"S:/ursa/campbell/Other/planet_tanager"# read in spectral mixtures datadf <-file.path(main_dir, "synthetic_mixtures.csv") |>fread() |>as.data.frame()# print sample of the datakable(head(df), digits =3)
Table 1: First few rows of the synthetic mixture reflectance data used to train and test a MRF fractional cover prediction model.
gv
npv
soil
nm_440
nm_450
nm_460
nm_470
nm_480
nm_490
nm_500
nm_510
nm_520
nm_530
nm_540
nm_550
nm_560
nm_570
nm_580
nm_590
nm_600
nm_610
nm_620
nm_630
nm_640
nm_650
nm_660
nm_670
nm_680
nm_690
nm_700
nm_710
nm_720
nm_730
nm_740
nm_750
nm_760
nm_770
nm_780
nm_790
nm_800
nm_810
nm_820
nm_830
nm_840
nm_850
nm_860
nm_870
nm_880
nm_890
nm_900
nm_910
nm_920
nm_930
nm_940
nm_950
nm_960
nm_970
nm_980
nm_990
nm_1000
nm_1010
nm_1020
nm_1030
nm_1040
nm_1050
nm_1060
nm_1070
nm_1080
nm_1090
nm_1100
nm_1110
nm_1120
nm_1130
nm_1140
nm_1150
nm_1160
nm_1170
nm_1180
nm_1190
nm_1200
nm_1210
nm_1220
nm_1230
nm_1240
nm_1250
nm_1260
nm_1270
nm_1280
nm_1290
nm_1300
nm_1310
nm_1320
nm_1490
nm_1500
nm_1510
nm_1520
nm_1530
nm_1540
nm_1550
nm_1560
nm_1570
nm_1580
nm_1590
nm_1600
nm_1610
nm_1620
nm_1630
nm_1640
nm_1650
nm_1660
nm_1670
nm_1680
nm_1690
nm_1700
nm_1710
nm_1720
nm_1730
nm_1740
nm_1750
nm_1760
nm_1770
nm_2000
nm_2010
nm_2020
nm_2030
nm_2040
nm_2050
nm_2060
nm_2070
nm_2080
nm_2090
nm_2100
nm_2110
nm_2120
nm_2130
nm_2140
nm_2150
nm_2160
nm_2170
nm_2180
nm_2190
nm_2200
nm_2210
nm_2220
nm_2230
nm_2240
nm_2250
nm_2260
nm_2270
nm_2280
nm_2290
nm_2300
nm_2310
nm_2320
nm_2330
nm_2340
nm_2350
nm_2360
nm_2370
nm_2380
nm_2390
nm_2400
0.366
0.533
0.102
0.035
0.037
0.038
0.040
0.041
0.043
0.045
0.048
0.053
0.058
0.062
0.065
0.067
0.067
0.067
0.068
0.070
0.071
0.073
0.075
0.077
0.078
0.081
0.082
0.085
0.089
0.102
0.122
0.150
0.184
0.212
0.235
0.248
0.254
0.259
0.263
0.265
0.268
0.270
0.272
0.272
0.274
0.276
0.277
0.280
0.280
0.281
0.281
0.281
0.281
0.277
0.271
0.265
0.264
0.266
0.268
0.272
0.277
0.281
0.284
0.291
0.295
0.296
0.301
0.300
0.300
0.304
0.301
0.298
0.292
0.282
0.273
0.263
0.262
0.264
0.265
0.264
0.269
0.270
0.276
0.277
0.279
0.281
0.284
0.283
0.280
0.281
0.279
0.271
0.150
0.154
0.160
0.165
0.170
0.174
0.179
0.184
0.188
0.191
0.196
0.199
0.204
0.208
0.208
0.212
0.215
0.214
0.215
0.215
0.213
0.213
0.211
0.209
0.207
0.205
0.201
0.199
0.198
0.114
0.117
0.119
0.120
0.119
0.119
0.118
0.118
0.117
0.119
0.120
0.121
0.123
0.124
0.125
0.127
0.129
0.130
0.132
0.133
0.134
0.135
0.135
0.134
0.130
0.127
0.123
0.120
0.119
0.119
0.115
0.112
0.111
0.111
0.108
0.106
0.107
0.105
0.103
0.102
0.101
0.065
0.327
0.608
0.077
0.079
0.080
0.082
0.083
0.086
0.090
0.093
0.098
0.103
0.108
0.114
0.120
0.126
0.132
0.138
0.143
0.147
0.151
0.154
0.159
0.161
0.165
0.169
0.173
0.177
0.180
0.186
0.191
0.198
0.205
0.212
0.217
0.221
0.225
0.227
0.229
0.233
0.234
0.235
0.238
0.240
0.241
0.244
0.246
0.246
0.249
0.250
0.250
0.252
0.254
0.255
0.257
0.257
0.259
0.261
0.263
0.267
0.267
0.269
0.272
0.274
0.275
0.278
0.279
0.280
0.282
0.284
0.286
0.287
0.286
0.286
0.286
0.288
0.289
0.290
0.293
0.294
0.295
0.300
0.299
0.299
0.304
0.303
0.305
0.306
0.307
0.307
0.308
0.288
0.292
0.294
0.296
0.299
0.301
0.303
0.304
0.303
0.304
0.307
0.308
0.310
0.313
0.316
0.316
0.313
0.317
0.315
0.318
0.318
0.315
0.318
0.315
0.315
0.311
0.313
0.315
0.314
0.281
0.281
0.283
0.282
0.278
0.274
0.275
0.273
0.273
0.272
0.270
0.271
0.274
0.273
0.273
0.274
0.270
0.268
0.264
0.260
0.257
0.257
0.262
0.266
0.264
0.261
0.259
0.257
0.257
0.253
0.252
0.250
0.246
0.244
0.241
0.240
0.237
0.236
0.236
0.235
0.233
0.311
0.110
0.579
0.058
0.058
0.059
0.060
0.061
0.063
0.067
0.071
0.077
0.084
0.090
0.096
0.102
0.106
0.110
0.114
0.119
0.122
0.125
0.128
0.131
0.135
0.138
0.141
0.144
0.149
0.156
0.167
0.181
0.200
0.220
0.237
0.245
0.250
0.254
0.257
0.258
0.261
0.263
0.265
0.266
0.267
0.269
0.271
0.272
0.272
0.273
0.273
0.275
0.275
0.273
0.271
0.271
0.273
0.274
0.275
0.277
0.281
0.286
0.287
0.289
0.293
0.294
0.297
0.297
0.300
0.300
0.299
0.300
0.300
0.293
0.290
0.288
0.288
0.289
0.287
0.289
0.289
0.292
0.294
0.297
0.298
0.298
0.299
0.301
0.299
0.299
0.298
0.294
0.238
0.239
0.242
0.244
0.246
0.251
0.255
0.253
0.256
0.259
0.260
0.262
0.265
0.268
0.269
0.271
0.272
0.273
0.273
0.272
0.271
0.272
0.270
0.270
0.269
0.267
0.266
0.266
0.264
0.221
0.225
0.227
0.227
0.228
0.230
0.230
0.230
0.231
0.231
0.231
0.232
0.234
0.234
0.232
0.233
0.232
0.231
0.228
0.228
0.225
0.226
0.229
0.232
0.232
0.232
0.226
0.228
0.226
0.227
0.225
0.222
0.218
0.217
0.215
0.214
0.214
0.210
0.208
0.208
0.205
0.104
0.790
0.106
0.055
0.057
0.059
0.061
0.063
0.066
0.069
0.072
0.074
0.078
0.081
0.083
0.086
0.088
0.091
0.093
0.095
0.098
0.099
0.103
0.105
0.109
0.111
0.114
0.117
0.121
0.126
0.132
0.139
0.147
0.157
0.167
0.176
0.180
0.185
0.188
0.192
0.196
0.200
0.203
0.205
0.207
0.211
0.214
0.217
0.221
0.222
0.225
0.228
0.230
0.230
0.232
0.232
0.235
0.237
0.241
0.242
0.244
0.246
0.248
0.252
0.255
0.258
0.262
0.262
0.265
0.265
0.268
0.267
0.270
0.268
0.269
0.268
0.270
0.273
0.274
0.274
0.276
0.280
0.280
0.284
0.288
0.290
0.291
0.295
0.298
0.298
0.300
0.299
0.254
0.259
0.262
0.264
0.268
0.273
0.275
0.278
0.280
0.283
0.287
0.289
0.294
0.295
0.300
0.301
0.303
0.301
0.299
0.302
0.300
0.299
0.296
0.296
0.295
0.297
0.298
0.297
0.296
0.260
0.260
0.256
0.250
0.244
0.240
0.234
0.229
0.228
0.225
0.224
0.225
0.226
0.228
0.230
0.232
0.234
0.237
0.236
0.240
0.241
0.243
0.243
0.241
0.234
0.226
0.221
0.213
0.210
0.210
0.208
0.201
0.201
0.199
0.197
0.199
0.202
0.202
0.200
0.202
0.203
0.157
0.318
0.525
0.094
0.096
0.099
0.102
0.105
0.109
0.114
0.121
0.129
0.138
0.146
0.154
0.160
0.166
0.171
0.175
0.180
0.184
0.187
0.192
0.198
0.201
0.205
0.209
0.214
0.220
0.228
0.240
0.253
0.267
0.282
0.293
0.298
0.305
0.311
0.312
0.318
0.321
0.323
0.324
0.328
0.328
0.331
0.332
0.336
0.336
0.338
0.341
0.344
0.346
0.347
0.348
0.348
0.350
0.351
0.353
0.355
0.358
0.359
0.366
0.367
0.372
0.371
0.375
0.379
0.381
0.381
0.380
0.384
0.380
0.380
0.378
0.376
0.381
0.385
0.380
0.383
0.390
0.392
0.398
0.399
0.400
0.401
0.403
0.404
0.405
0.407
0.408
0.404
0.362
0.366
0.371
0.372
0.375
0.379
0.380
0.383
0.388
0.388
0.389
0.391
0.395
0.393
0.394
0.398
0.396
0.395
0.397
0.396
0.398
0.396
0.393
0.392
0.389
0.391
0.390
0.393
0.390
0.336
0.341
0.344
0.346
0.342
0.345
0.345
0.345
0.345
0.345
0.345
0.346
0.348
0.348
0.349
0.348
0.346
0.347
0.345
0.340
0.335
0.335
0.335
0.334
0.334
0.331
0.326
0.323
0.319
0.316
0.313
0.308
0.305
0.304
0.299
0.294
0.298
0.300
0.300
0.298
0.296
0.317
0.335
0.348
0.063
0.065
0.065
0.066
0.068
0.070
0.073
0.078
0.084
0.091
0.097
0.102
0.107
0.111
0.113
0.116
0.119
0.122
0.124
0.127
0.129
0.133
0.135
0.137
0.141
0.147
0.158
0.176
0.199
0.226
0.248
0.264
0.274
0.282
0.285
0.291
0.294
0.296
0.300
0.303
0.305
0.308
0.310
0.315
0.315
0.317
0.321
0.322
0.324
0.325
0.327
0.324
0.323
0.323
0.323
0.326
0.328
0.334
0.338
0.345
0.345
0.350
0.353
0.356
0.357
0.357
0.357
0.352
0.350
0.348
0.342
0.333
0.332
0.331
0.330
0.330
0.330
0.336
0.337
0.342
0.344
0.349
0.350
0.350
0.355
0.354
0.353
0.345
0.345
0.224
0.230
0.234
0.239
0.244
0.248
0.252
0.256
0.257
0.259
0.263
0.268
0.273
0.274
0.278
0.280
0.282
0.281
0.281
0.281
0.280
0.278
0.276
0.271
0.270
0.272
0.271
0.267
0.266
0.199
0.199
0.204
0.205
0.202
0.203
0.201
0.201
0.200
0.199
0.201
0.202
0.204
0.206
0.207
0.207
0.207
0.209
0.208
0.208
0.205
0.207
0.210
0.212
0.209
0.204
0.201
0.198
0.195
0.194
0.191
0.188
0.185
0.185
0.183
0.182
0.181
0.181
0.179
0.179
0.177
Below is a ternary plot featuring a sample of 100 mixtures. Three randomly selected mixtures are highlighted, as their spectra will be plotted out next.
Show code
# create empty ternary plotpar(mar =rep(0,4))TernaryPlot(alab ="GV",blab ="NPV",clab ="Soil",lab.col =c("green", "red", "blue"))# get sample for plottingset.seed(57)df_tern <- df[sample(nrow(df), 100),]# get rgb colorscols <-rgb(red = df_tern$npv,green = df_tern$gv,blue = df_tern$soil)# grab a few random mixturesrand_mixs <-sample(nrow(df_tern), 3)# get fraction columnsfrcols <-c("gv", "npv", "soil")# add sample of pointsTernaryPoints( df_tern[,frcols],pch =16,col =adjustcolor(cols, alpha.f =0.75),cex =1)# add random mixture pointsTernaryPoints( df_tern[rand_mixs, frcols], pch =21, bg = cols[rand_mixs], cex =3)TernaryText( df_tern[rand_mixs, frcols], labels =c(1,2,3),col ="white",font =2)
Figure 1: Ternary plot with a sample of synthetic mixtures from our training data. Points are colored according to their fractional cover in RGB space, where NPV fraction is displayed through the Red channel, GV Green, and Soil blue. The full reflectance spectra of points 1, 2, and 3 are shown in the next figure.
Below is a plot containing the reflectance spectra of the three points shown in the previous ternary plot.
Show code
# get reflectance columnsref_cols <-grep("^nm", colnames(df), value = T)# get wavelengthswvls <- ref_cols |>sub("nm\\_", "", x = _) |>as.numeric()wvls_all <-seq(min(wvls), max(wvls), 10)# create reflectance matrixref_mat <-matrix(NA,nrow =nrow(df_tern), ncol =length(wvls_all))colnames(ref_mat) <- wvls_allfor (i inseq_along(wvls_all)){ wvl <- wvls_all[i]if (paste0("nm_", wvl) %in%colnames(df_tern)){ ref_mat[,i] <- df_tern[,paste0("nm_", wvl)] }}# create empty plotpar(mar =c(5,5,1,10), las =1)plot(x =range(wvls_all),y =range(ref_mat[rand_mixs,], na.rm = T),type ="n",xlab ="Wavelength (nm)",ylab ="Reflectance")# add random mixtureslines(x = wvls_all,y = ref_mat[rand_mixs[1],],lwd =3,col = cols[rand_mixs[1]])lines(x = wvls_all,y = ref_mat[rand_mixs[2],],lwd =3,col = cols[rand_mixs[2]])lines(x = wvls_all,y = ref_mat[rand_mixs[3],],lwd =3,col = cols[rand_mixs[3]])# get fractions of sample mixturesfracs_1 <-paste0("Mixture 1\nGV: ", formatC(df_tern$gv[rand_mixs[1]], digits =2, format ="f"), "\nNPV: ",formatC(df_tern$npv[rand_mixs[1]], digits =2, format ="f"), "\nSoil: ",formatC(df_tern$soil[rand_mixs[1]], digits =2, format ="f"))fracs_2 <-paste0("Mixture 2\n(GV: ", formatC(df_tern$gv[rand_mixs[2]], digits =2, format ="f"), "\nNPV: ",formatC(df_tern$npv[rand_mixs[2]], digits =2, format ="f"), "\nSoil: ",formatC(df_tern$soil[rand_mixs[2]], digits =2, format ="f"))fracs_3 <-paste0("Mixture 3\n(GV: ", formatC(df_tern$gv[rand_mixs[3]], digits =2, format ="f"), "\nNPV: ",formatC(df_tern$npv[rand_mixs[3]], digits =2, format ="f"), "\nSoil: ",formatC(df_tern$soil[rand_mixs[3]], digits =2, format ="f"))# add legendpar(xpd =NA)legend(x =par("usr")[2] +0.05*diff(par("usr")[1:2]),y =mean(par("usr")[3:4]),legend =c(fracs_1, fracs_2, fracs_3),lwd =5,col = cols[rand_mixs],bty ="n",xjust =0,yjust =0.5,y.intersp =2)par(xpd = F)
Figure 2: Spectral reflectance of the three example synthetic mixtures shown in Figure 1. Lines are colored according to their fractional cover in RGB space, where NPV fraction is displayed through the Red channel, GV Green, and Soil blue.
Train Random Forest Model
Next, we will train a MRF model using the randomForestSRC package(Ishwaran et al. 2008). To do this, we will first randomly split our 10,000 mixtures into training (80%) and test (20%) datasets. A model will be trained with the training data and used to predict fractional cover with the test data. The scatterplots below show error for the test data. GV cover is distinct and is modeled with the highest accuracy, while NPV and soil are more difficult to separate. Past work has demonstrated that imaging spectrometer data, like Tanager, improves accuracy of NPV-soil separation due to being able to resolve lignocellulose absorption.
Show code
# split into training and testtrain_frac <-0.8samp <-sample(nrow(df), nrow(df) * train_frac)df_train <- df[samp,]df_test <- df[-samp,]# train multivariate random forestmrf <-rfsrc(Multivar(gv, npv, soil) ~ .,data = df_train)# apply to test datapred <-predict(mrf, df_test)# compile test predictions and observationsdf_pred_obs <-data.frame(gv_obs = df_test$gv,npv_obs = df_test$npv,soil_obs = df_test$soil,gv_pred = pred$regrOutput$gv$predicted,npv_pred = pred$regrOutput$npv$predicted,soil_pred = pred$regrOutput$soil$predicted)# create plotting functionplot_fun <-function(frac){plot(x =c(0,1), y =c(0,1), type ="n", xaxt ="n", yaxt ="n", xlab =NA,ylab =NA ) x <- df_pred_obs[,paste0(frac, "_obs")] y <- df_pred_obs[,paste0(frac, "_pred")]if (frac =="gv"){ col_pt <-adjustcolor("green", 0.75) col_ln <-"darkgreen" } elseif (frac =="npv"){ col_pt <-adjustcolor("red", 0.75) col_ln <-"darkred" } else { col_pt <-adjustcolor("blue", 0.75) col_ln <-"darkblue" }grid()box()abline(0,1)points(x, y, pch =16, col = col_pt) mod <-lm(y ~ x)abline(mod, lwd =3, col = col_ln) r2 <-1-sum((x-y)^2)/sum((x-mean(x))^2) r2 <-formatC(r2, digits =2, format ="f") r2 <-bquote(R^2==.(r2)) rmse <-sqrt(mean((x-y)^2)) rmse <-formatC(rmse, digits =2, format ="f") rmse <-bquote(RMSE==.(rmse))legend("topleft",legend =c(r2, rmse),text.col = col_ln,text.font =2,bty ="n",x.intersp =0 )}# plot predictions and observationspar(mfrow =c(1,3), mar =rep(0,4), oma =c(5,5,2,1), las =1)plot_fun("gv")mtext("GV", side =3, font =2, col ="darkgreen", line =0.5)axis(1)axis(2)plot_fun("npv")mtext("NPV", side =3, font =2, col ="darkred", line =0.5)axis(1)plot_fun("soil")mtext("Soil", side =3, font =2, col ="darkblue", line =0.5)axis(1)mtext("Observed Fraction", 1, 3, outer = T, font =2)mtext("Predicted Fraction", 2, 3, outer = T, font =2, las =0)
Figure 3: MRF-predicted versus observed fractional cover for GV, NPV, and Soil.
Explore Tanager Scene
With a MRF model now trained, we can now move towards image-based prediction. We acquired a Planet Tanager scene that was captured in Central Utah in 2025 during the Monroe Canyon Fire. We downloaded the orthorectified surface reflectance product (ortho_sr_hdf5).
Below, we will load the scene’s imagery and metadata, and map it out using a natural color composite. The smoke plume of this fire – one of Utah’s largest in recent history – can be easily seen in the image.
Show code
# get hdf fileh5_file <-file.path(main_dir, "20250724_190927_83_4001_ortho_sr_hdf5.h5")# open as SpatRasterDataseth5_sds <-sds(h5_file)# find surface reflectance bandssr_id <-grep("surface_reflectance$",names(h5_sds),ignore.case =TRUE)# create SpatRaster of surface reflectancesr <- h5_sds[sr_id]# read hdf attributessr_h5_path <-"/HDFEOS/GRIDS/HYP/Data Fields/surface_reflectance"attrs <-h5readAttributes(h5_file, sr_h5_path)# get useful attributeswavelength_nm <-as.numeric(attrs$wavelengths)fwhm_nm <-as.numeric(attrs$fwhm)good_band <-as.logical(attrs$good_wavelengths)# combineband_metadata <-data.frame(layer =seq_len(nlyr(sr)),band =sprintf("B%03d", 1:nlyr(sr)),wavelength_nm = wavelength_nm,fwhm_nm = fwhm_nm,good_wavelength = good_band)# add band names to imagerynames(sr) <- band_metadata$band# get rgb imager_band <-which.min((abs(650- band_metadata$wavelength_nm)))g_band <-which.min((abs(550- band_metadata$wavelength_nm)))b_band <-which.min((abs(450- band_metadata$wavelength_nm)))sr_rgb <- sr[[c(r_band, g_band, b_band)]]sr_rgb <-ifel(sr_rgb <0, 0, sr_rgb)sr_rgb <-ifel(sr_rgb >0.25, 0.25, sr_rgb)# plot itplotRGB(sr_rgb, scale =0.25)
Figure 4: Natural color composite of Planet Tanager scene captured in Central Utah during the Monroe Canyon Fire.
Interpolation
Given that the wavelengths of Tanager data differ from our 10nm-resolution synthetic mixture data, we will first interpolate each image pixel’s spectrum to match the band centers of the synthetic mixture data.
We will now use the MRF model to predict fractional cover with the interpolated Tanager scene. Below you will see the resulting three-band predicted map, where predicted NPV fraction is being displayed through the red channel, GV through green, and Soil through blue. Some very interesting patterns emerge, including:
A very useful capture of the Monroe Canyon Fire burn scar, predicted as high soil fraction
The ecological gradient from soil/NPV-dominated rangeland at lower elevations to the GV-dominated forests at higher elevations
Older fire scars showing up as NPV-dominated late-summer senesced grass/shrub cover
Riparian and agricultural areas featuring very high GV fraction
Issues with clouds being predicted as high soil fraction (see next section) and cloud shadows being predicted as high GV fraction
Figure 5: MRF-predicted fractional cover displayed as a three-channel (RGB) image, where NPV fraction is displayed through the red channel, GV through green, and Soil through blue.
Masking
We will use the ortho_sr_hdf5beta_cloud_mask layer to mask out the clouds (and some larger sections of smoke plume).
Show code
# find beta cloud maskcloud_id <-grep("beta_cloud_mask$",names(h5_sds),ignore.case =TRUE)# extract as a SpatRastercloud_mask <- h5_sds[cloud_id]names(cloud_mask) <-"beta_cloud_mask"# set cloudy areas to NApred_img <-ifel(cloud_mask ==1, NA, pred_img)# plot predictionsplotRGB(pred_img, scale =1)
Figure 6: MRF-predicted fractional cover displayed as a three-channel (RGB) image, as in Figure 5, but with clouds masked.
Conclusions
MRF provides a potential machine learning method for global mapping of fractional cover. We have shown that this method can be applied to Planet Tanager imagery in a manner that should be highly transferable across ecosystems while innately enforcing a sum-to-one constraint. Tanager data and field measurement of fractional cover can be used to validate MRF-estimated fractions, demonstrate scalability, and prove applicability across key ecosystems.
Dennison, Philip E, Brian T Lamb, Michael J Campbell, et al. 2023. “Modeling Global Indices for Estimating Non-Photosynthetic Vegetation Cover.”Remote Sensing of Environment 295: 113715. https://doi.org/10.1016/j.rse.2023.113715.
Ishwaran, Hemant, Udaya B. Kogalur, Eugene H. Blackstone, and Michael S. Lauer. 2008. “Random Survival Forests.”The Annals of Applied Statistics 2 (3): 841–60. https://doi.org/10.1214/08-AOAS169.