Digital Cover Photography(DCP)の解析

このノートの目的

このノートでは、スマートフォンや通常のカメラで撮影した林冠写真から、 「空がどれくらい見えるか」を定量化する流れを説明します。

解析には R パッケージ coveR を使用します。

coveRChianucci et al. (2022) で紹介されている、 デジタルカバーフォトグラフィー解析のためのパッケージです。

Warning

Chianucci et al. (2022) の論文本文と、現在の coveR の関数名・仕様には差があります。 そのため、論文のコードをそのまま実行すると動かない場合があります。

Noteインストール方法について

coveR は CRAN 未登録のため、通常は GitLab からインストールします。

CRAN 版を使いたい場合は、関連パッケージの coveR2 も選択肢です。

解析の全体像(先に流れを把握)

  1. 画像を読み込む
  2. 空と林冠を二値化する
  3. 空隙(ギャップ)をラベリングする
  4. ギャップサイズで分類する
  5. 開空度(Gap Fraction)などの指標を計算する

パッケージのインストールと読み込み

# renvを使用している場合
renv::install("git::https://github.com/cmartin/EXIFr.git")
renv::install("gitlab::fchianucci/coveR")

# renvを使用していない場合
# install.packages("devtools")
devtools::install_gitlab("fchianucci/coveR")
library(coveR)

画像の読み込み

image_path <- "data/dcp_example_21rinpan2.jpg"
terra::plotRGB(terra::rast(image_path))
Warning: [rast] unknown extent

RGB チャンネルの確認

空と林冠の分離にどのチャンネルが有効かを確認します。

terra::plot(
  terra::rast(list(
    terra::rast(image_path, lyrs = 1),
    terra::rast(image_path, lyrs = 2),
    terra::rast(image_path, lyrs = 3)
  )),
  main = c("red", "green", "blue"),
  col = gray.colors(10, start = 0, end = 1),
  axes = FALSE
)
Warning: [rast] unknown extent
Warning: [rast] unknown extent
Warning: [rast] unknown extent

青チャンネルでコントラストが高い場合が多いため、coveR でも青を活用します。

terra::plot(
  terra::rast(image_path, lyrs = 3),
  col = gray.colors(5, start = 0, end = 1),
  main = "blue channel"
)
Warning: [rast] unknown extent

coveR() による基本解析

res <- coveR(image_path, thdmethod = "Otsu", display = TRUE)

print(res)
# A tibble: 1 × 24
  path_id id       date                  day month  year    FC    CC    CP    Le
  <chr>   <chr>    <dttm>              <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 data    dcp_exa… 2024-05-14 10:26:17    14     5  2024 0.844 0.971 0.131  3.72
# ℹ 14 more variables: L <dbl>, CI <dbl>, k <dbl>, imgchannel <dbl>,
#   gap_method <chr>, gap_thd <dbl>, img_method <chr>, img_thd <dbl>,
#   blurriness <dbl>, BSI <dbl>, FileSize <dbl>, ImageSize <chr>, Camera <chr>,
#   Model <chr>

ギャップのラベリングとサイズ分類

img <- terra::rast(image_path, lyrs = 3)
Warning: [rast] unknown extent
myimg.rst <- terra::classify(img, rbind(c(-Inf, 105, 0), c(105, Inf, 1)))

vals <- matrix(
  terra::values(myimg.rst, format = "matrix"),
  nrow = nrow(myimg.rst),
  byrow = TRUE
)

y <- mgc::ConnCompLabel(vals)
yr <- terra::rast(nrows = nrow(myimg.rst), ncols = ncol(myimg.rst), vals = y)

ext <- terra::ext(myimg.rst)
terra::set.ext(yr, ext)
terra::set.names(yr, base::names(myimg.rst))

terra::plot(
  yr,
  col = c("black", rainbow(1000, start = 0, end = 1)),
  main = "labelled gaps",
  axes = FALSE,
  legend = FALSE,
  asp = 1
)

ギャップサイズに基づいた分類

tbr <- data.frame(table(terra::values(yr)))
tbf <- tbr
tbf$id <- names(yr)
tbf$NR <- sum(tbf$Freq)
tbf$gL <- "Small_gap"

tbf$gL[as.character(tbf$Var1) == "0"] <- "Canopy"
tbf$gL[
  as.character(tbf$Var1) != "0" & tbf$Freq >= tbf$NR * 1.3 / 100
] <- "Large_gap"

head(tbf)
  Var1     Freq                      id       NR        gL
1    0 10171956 dcp_example_21rinpan2_3 12192768    Canopy
2    1        3 dcp_example_21rinpan2_3 12192768 Small_gap
3    2        1 dcp_example_21rinpan2_3 12192768 Small_gap
4    3       12 dcp_example_21rinpan2_3 12192768 Small_gap
5    4        5 dcp_example_21rinpan2_3 12192768 Small_gap
6    5        3 dcp_example_21rinpan2_3 12192768 Small_gap

分類に基づいた林冠情報の取得

out.cnp <- coveR(image_path, k = 0.5, display = FALSE)
head(out.cnp)
# A tibble: 1 × 24
  path_id id       date                  day month  year    FC    CC    CP    Le
  <chr>   <chr>    <dttm>              <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 data    dcp_exa… 2024-05-14 10:26:17    14     5  2024 0.844 0.971 0.131  3.72
# ℹ 14 more variables: L <dbl>, CI <dbl>, k <dbl>, imgchannel <dbl>,
#   gap_method <chr>, gap_thd <dbl>, img_method <chr>, img_thd <dbl>,
#   blurriness <dbl>, BSI <dbl>, FileSize <dbl>, ImageSize <chr>, Camera <chr>,
#   Model <chr>
tbf.new <- as.data.frame(tbf)
rec <- c("Large_gap" = 0.5, "Small_gap" = 1, "Canopy" = 0)
tbf.new$col <- unname(rec[tbf.new$gL])
tbf.new <- tbf.new[, c("Var1", "col")]
tbf.new$Var1 <- as.integer(as.character(tbf.new$Var1))

canopy.rst <- terra::classify(yr, as.matrix(tbf.new))
terra::set.crs(canopy.rst, terra::crs(img))
terra::set.names(canopy.rst, base::names(yr))
terra::set.ext(canopy.rst, ext)

terra::plot(
  canopy.rst,
  col = gray.colors(3, start = 0, end = 1),
  main = "classified image"
)

開空度(Gap Fraction)の計算

coveR() の出力に含まれる FC(Canopy Fraction)から、 開空度 GF(Gap Fraction)を次式で計算できます。

\[ GF = 1 - FC = 1 - \frac{G}{NR} \]

  • \(G\): ギャップ(空)ピクセル数
  • \(NR\): 全ピクセル数
df <- as.data.frame(out.cnp)
df$GF <- 1 - df$FC
head(df)
  path_id                        id                date day month year
1    data dcp_example_21rinpan2.jpg 2024-05-14 10:26:17  14     5 2024
         FC        CC        CP       Le      L        CI   k imgchannel
1 0.8442385 0.9710594 0.1306006 3.718858 3.9534 0.9406735 0.5          3
  gap_method gap_thd img_method img_thd blurriness   BSI FileSize ImageSize
1 macfarlane   0.013       Otsu     114     0.0472 0.421  7047754 4032_3024
  Camera     Model        GF
1  Apple iPhone 11 0.1557615

References

Chianucci, Francesco, Carlotta Ferrara, and Nicola Puletti. 2022. “coveR: An r Package for Processing Digital Cover Photography Images to Retrieve Forest Canopy Attributes.” Trees 36 (6): 1933–42. https://doi.org/10.1007/s00468-022-02338-5.