Analizando objetos

Diretório das imagens

# mudar de acordo com a pasta em seu computador
setwd("E:/Desktop/tiagoolivoto/static/tutorials/pliman_lca/imgs")

A função analyze_objects() pode ser usada para contar objetos em uma imagem. Vamos começar com um exemplo simples com a imagem object_300dpi.png disponível na página GitHub. Para facilitar a importação de imagens desta pasta, uma função auxiliar image_pliman() é usada.

# gerar tabelas html
print_tbl <- function(table,  digits = 3, ...){
  knitr::kable(table, booktabs = TRUE, digits = digits, ...)
}
library(pliman)
## |==========================================================|
## | Tools for Plant Image Analysis (pliman 1.0.0)            |
## | Author: Tiago Olivoto                                    |
## | Type 'vignette('pliman_start')' for a short tutorial     |
## | Visit 'http://bit.ly/pkg_pliman' for a complete tutorial |
## |==========================================================|
library(tidyverse)
## -- Attaching packages --------------------------------------- tidyverse 1.3.1 --
## v ggplot2 3.3.5     v purrr   0.3.4
## v tibble  3.1.6     v dplyr   1.0.7
## v tidyr   1.1.4     v stringr 1.4.0
## v readr   2.1.1     v forcats 0.5.1
## Warning: package 'tidyr' was built under R version 4.1.1
## Warning: package 'readr' was built under R version 4.1.2
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x forcats::%>%()  masks stringr::%>%(), dplyr::%>%(), purrr::%>%(), tidyr::%>%(), tibble::%>%(), pliman::%>%()
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
library(patchwork)

img <- image_pliman("objects_300dpi.jpg", plot = TRUE)

A imagem acima foi produzida com o Microsoft PowerPoint. Tem uma resolução conhecida de 300 dpi(pontos por polegada) e mostra quatro objetos

  • Quadrado maior: 10 x 10 cm (100 cm$^2$)
  • Quadrado menor: 5 x 5 cm(25 cm$^2$)
  • Retângulo: 4 x 2 cm(8 cm$^2$)
  • Círculo: 3 cm de diâmetro(~7,07 cm$^2$)

Para contar os objetos na imagem usamos analyze_objects() e informamos a imagem (o único argumento obrigatório). Primeiro, usamos image_binary() para ver o índice mais adequado para segmentar os objetos do fundo. Por padrão, R, G, B e seus valores normalizados.

image_binary(img)

Analizar objetos

img_res <- analyze_objects(img, marker = "id")

Ajustando as medidas do objeto

Os resultados foram armazenados em img_res. Como não há escala declarada no exemplo acima, não temos ideia sobre a área real dos objetos em cm$^2$, apenas em pixels. Neste caso, usamos get_measures() para ajustar as medidas de pixels para unidades métricas.

Existem duas formas principais de ajustar as medidas do objeto (de pixels a cm, por exemplo). O primeiro é declarar a área, perímetro ou raio conhecido de um determinado objeto. A medida para os outros objetos será então calculada por uma regra de três simples. A segunda é declarando uma resolução de imagem conhecida em dpi (pontos por polegada). Neste caso, perímetro, área e raio serão ajustados pelo dpi informado.

Declarando um valor conhecido

Como conhecemos a área do quadrado maior (objeto 1), vamos ajustar a área dos outros objetos na imagem usando isso.

get_measures(img_res,
             id = 1,
             area ~ 100)
## -----------------------------------------
## measures corrected with:
## object id: 1
## area     : 100
## -----------------------------------------
## Total    : 139.985 
## Average  : 34.996 
## -----------------------------------------
##   id        x        y    area area_ch perimeter radius_mean radius_min
## 1  1  668.506  798.002 100.000  99.915    39.932       5.733      4.992
## 2  2 1737.513  453.246  25.041  24.978    19.924       2.865      2.495
## 3  3 1737.575 1296.331   7.023   7.015     8.504       1.491      1.483
## 4  4 1737.972  939.503   7.922   7.880    11.886       1.666      0.986
##   radius_max radius_sd radius_ratio diam_mean diam_min diam_max major_axis
## 1      7.062    74.203        1.415    11.466    9.983   14.124     11.552
## 2      3.524    36.957        1.413     5.730    4.989    7.049      5.781
## 3      1.502     0.398        1.012     2.983    2.967    3.003      2.991
## 4      2.222    50.007        2.253     3.332    1.973    4.444      4.611
##   minor_axis eccentricity  theta solidity circularity
## 1     11.542        0.041  0.014    1.001       0.788
## 2      5.776        0.041  1.539    1.003       0.793
## 3      2.990        0.026 -1.490    1.001       1.220
## 4      2.291        0.868  0.000    1.005       0.705

O mesmo pode ser usado para ajustar as medidas com base no perímetro ou raio. Vamos ajustar o perímetro dos objetos pelo perímetro do objeto 2(20 cm).

Declarando a resolução da imagem

Se a resolução da imagem for conhecida, todas as medidas serão ajustadas de acordo com esta resolução. Vamos ver um exemplo numérico com pixels_to_cm(). Esta função converte o número de pixels (px) em cm, considerando a resolução da imagem em dpi, da seguinte forma: \(cm = px \times(2,54 / dpi)\). Como sabemos o número de pixels do quadrado maior, seu perímetro em cm é dado por

# número de pixels para o perímetro do quadrado maior

ls_px <- img_res$results$perimeter[1]
pixels_to_cm(px = ls_px, dpi = 300)
## [1] 39.878

O perímetro do objeto 1 ajustado pela resolução da imagem é muito próximo do verdadeiro (40 cm). Abaixo, os valores de todas as medidas são ajustados declarando o argumento dpi em get_measures().

img_res_cor <- get_measures(img_res, dpi = 300)
print_tbl(t(img_res_cor))
1 2 3 4
id 1.000 2.000 3.000 4.000
x 668.506 1737.513 1737.575 1737.972
y 798.002 453.246 1296.331 939.503
area 99.729 24.973 7.004 7.900
area_ch 99.644 24.910 6.996 7.858
perimeter 39.878 19.897 8.492 11.870
radius_mean 5.725 2.861 1.489 1.664
radius_min 4.985 2.491 1.481 0.985
radius_max 7.052 3.520 1.500 2.219
radius_sd 74.203 36.957 0.398 50.007
radius_ratio 1.415 1.413 1.012 2.253
diam_mean 11.450 5.722 2.979 3.327
diam_min 9.970 4.983 2.963 1.970
diam_max 14.105 7.039 2.999 4.438
major_axis 11.536 5.773 2.987 4.604
minor_axis 11.526 5.768 2.986 2.288
eccentricity 0.041 0.041 0.026 0.868
theta 0.014 1.539 -1.490 0.000
solidity 1.001 1.003 1.001 1.005
circularity 0.788 0.793 1.220 0.705

Entendendo as medidas

object_contour(img) %>%  # obtém o contorno dos objetos
  poly_mass() %>%        # computa o centro de massa e raios mínimo e máximo
  plot_mass()            # plota as medidas

  • Quadrado maior:
  • O diâmetro mínimo (a = 9,97) pode ser considerado como o lado do quadrado

  • O diâmetro máximo, dado por \(a \sqrt{2}\) pode ser considerado a diagonal do quadrado ($9,97 \sqrt{2} = 14.099$

t(img_res_cor)
##                    1        2        3        4
## id             1.000    2.000    3.000    4.000
## x            668.506 1737.513 1737.575 1737.972
## y            798.002  453.246 1296.331  939.503
## area          99.729   24.973    7.004    7.900
## area_ch       99.644   24.910    6.996    7.858
## perimeter     39.878   19.897    8.492   11.870
## radius_mean    5.725    2.861    1.489    1.664
## radius_min     4.985    2.491    1.481    0.985
## radius_max     7.052    3.520    1.500    2.219
## radius_sd     74.203   36.957    0.398   50.007
## radius_ratio   1.415    1.413    1.012    2.253
## diam_mean     11.450    5.722    2.979    3.327
## diam_min       9.970    4.983    2.963    1.970
## diam_max      14.105    7.039    2.999    4.438
## major_axis    11.536    5.773    2.987    4.604
## minor_axis    11.526    5.768    2.986    2.288
## eccentricity   0.041    0.041    0.026    0.868
## theta          0.014    1.539   -1.490    0.000
## solidity       1.001    1.003    1.001    1.005
## circularity    0.788    0.793    1.220    0.705

Objetos médios

Aqui, contaremos os grãos na imagem soybean_touch.jpg. Esta imagem tem um fundo ciano e contém 30 grãos de soja que se tocam. A função analyze_objects() segmenta a imagem usando como padrão o índice azul normalizado, como segue \(NB =(B /(R + G + B))\), onde R, G e B são as faixas vermelha, verde e azul. Os objetos são contados e os objetos segmentados são coloridos com permutações aleatórias.

soy <- image_pliman("soybean_touch.jpg")

count <- analyze_objects(soy)

count$statistics %>% 
  print_tbl()
stat value
n 30.000
min_area 1366.000
mean_area 2057.367
max_area 2445.000
sd_area 230.557
sum_area 61721.000

Os usuários podem definir show_contour = FALSE para remover a linha de contorno e identificar os objetos (neste exemplo, os grãos) usando os argumentos marker = "id". A cor do fundo também pode ser alterada com col_background.

count2 <-
  analyze_objects(soy,
                  show_contour = FALSE,
                  marker = "id",
                  show_segmentation = FALSE,
                  col_background = "white") # padrão

# Obtenha as medidas do objeto

medidas <- get_measures(count)
medidas %>% 
  print_tbl()
id x y area area_ch perimeter radius_mean radius_min radius_max radius_sd radius_ratio diam_mean diam_min diam_max major_axis minor_axis eccentricity theta solidity circularity
1 245.833 509.841 2286 2321 158 26.543 22.763 28.941 1.425 1.271 53.086 45.526 57.883 56.422 51.849 0.394 -0.870 0.985 1.151
2 538.056 401.896 2299 2258 153 26.607 24.957 28.400 0.935 1.138 53.214 49.914 56.800 56.604 51.733 0.406 -0.838 1.018 1.234
3 237.592 339.825 2312 2282 152 26.699 23.965 29.044 1.233 1.212 53.398 47.930 58.088 57.510 51.247 0.454 -0.572 1.013 1.258
4 345.357 105.783 2445 2406 158 27.513 24.682 30.471 1.730 1.235 55.027 49.365 60.942 60.922 51.105 0.544 -0.991 1.016 1.231
5 406.931 77.549 2302 2264 153 26.649 23.965 29.636 1.640 1.237 53.298 47.931 59.272 58.860 49.819 0.533 1.144 1.017 1.236
6 277.445 260.559 2163 2120 149 25.766 24.291 27.887 0.765 1.148 51.532 48.581 55.774 53.963 51.097 0.322 -0.163 1.020 1.224
7 301.206 370.092 2217 2202 154 26.113 23.591 28.687 1.358 1.216 52.227 47.182 57.375 56.484 50.034 0.464 -1.493 1.007 1.175
8 192.828 379.645 2207 2176 149 26.105 23.715 28.858 1.488 1.217 52.210 47.431 57.717 57.339 49.030 0.518 0.956 1.014 1.249
9 434.710 553.707 2174 2132 148 25.890 23.750 28.506 1.437 1.200 51.781 47.499 57.012 56.797 48.755 0.513 0.946 1.020 1.247
10 594.744 47.311 2219 2182 153 26.160 23.352 29.659 1.856 1.270 52.320 46.704 59.319 58.590 48.249 0.567 -1.032 1.017 1.191
11 468.997 56.425 2315 2275 155 26.765 23.031 30.780 2.349 1.336 53.531 46.061 61.560 61.230 48.157 0.618 1.292 1.018 1.211
12 461.172 156.027 2175 2131 148 25.933 23.071 29.512 1.656 1.279 51.866 46.142 59.024 57.396 48.284 0.541 1.090 1.021 1.248
13 202.075 203.461 2188 2166 153 26.008 22.487 29.808 1.987 1.326 52.015 44.974 59.616 58.474 47.719 0.578 -1.131 1.010 1.175
14 403.486 169.015 2035 1994 143 25.019 22.398 27.010 1.168 1.206 50.038 44.796 54.020 54.002 48.012 0.458 -1.094 1.021 1.251
15 245.987 221.375 2117 2091 148 25.528 21.950 29.113 1.793 1.326 51.056 43.900 58.226 56.809 47.542 0.547 -1.282 1.012 1.215
16 250.400 436.934 1964 1928 142 24.552 22.971 26.258 0.798 1.143 49.104 45.943 52.515 51.553 48.573 0.335 -1.383 1.019 1.224
17 84.671 206.432 2183 2144 151 25.923 22.825 28.743 1.708 1.259 51.846 45.650 57.486 57.492 48.382 0.540 0.023 1.018 1.203
18 448.412 296.209 2068 2023 145 25.196 23.357 27.032 0.993 1.157 50.392 46.714 54.063 53.898 48.857 0.422 -1.555 1.022 1.236
19 296.178 186.505 2056 2012 144 25.132 22.639 27.213 1.211 1.202 50.265 45.279 54.426 54.444 48.094 0.469 1.549 1.022 1.246
20 321.973 321.691 1978 1982 151 24.635 21.223 27.370 1.595 1.290 49.271 42.445 54.739 52.546 48.202 0.398 1.518 0.998 1.090
21 550.202 200.506 1939 1902 141 24.400 22.578 26.353 0.849 1.167 48.799 45.156 52.706 51.869 47.628 0.396 0.733 1.019 1.226
22 106.294 432.089 1922 1886 140 24.304 22.838 26.264 0.891 1.150 48.608 45.675 52.528 51.942 47.122 0.421 0.759 1.019 1.232
23 242.940 388.543 1926 1942 146 24.391 22.412 27.420 0.958 1.223 48.781 44.825 54.840 50.517 48.757 0.262 -0.901 0.992 1.135
24 492.988 344.441 1891 1855 139 24.076 21.894 25.988 1.180 1.187 48.152 43.789 51.976 52.247 46.096 0.471 1.505 1.019 1.230
25 721.705 586.342 1915 1873 140 24.250 21.886 26.818 1.350 1.225 48.500 43.772 53.636 53.127 45.908 0.503 1.339 1.022 1.228
26 510.468 158.372 1787 1767 137 23.466 21.173 26.180 1.009 1.236 46.932 42.345 52.360 48.654 47.026 0.257 -1.172 1.011 1.196
27 92.838 569.395 1743 1708 134 23.124 21.249 25.332 1.044 1.192 46.248 42.499 50.663 50.016 44.394 0.461 0.812 1.020 1.220
28 281.037 474.071 1819 1923 154 23.484 17.887 27.203 2.497 1.521 46.968 35.773 54.405 49.847 47.188 0.322 0.105 0.946 0.964
29 273.273 547.459 1710 1726 143 22.780 17.617 25.910 1.792 1.471 45.561 35.234 51.820 49.140 44.737 0.414 -0.600 0.991 1.051
30 265.292 143.411 1366 1331 117 20.405 18.590 22.030 0.772 1.185 40.809 37.181 44.060 43.697 39.816 0.412 -0.461 1.026 1.254

No exemplo a seguir, selecionaremos objetos com uma área acima da média de todos os objetos usando lower_size = 2057,36. Além disso, usaremos o argumento show_original = FALSE para mostrar os resultados como uma máscara.

analyze_objects(soy, 
                marker = "id",
                show_original = FALSE,
                lower_size = 2057.36)

Os usuários também podem usar os argumentos topn_* para selecionar os n objetos com base nas menores ou maiores áreas. Vamos ver como selecionar os 5 grãos com a menor área, mostrando os grãos originais em um fundo azul. Também usaremos o argumento my_index para escolher um índice personalizado para segmentar a imagem. Apenas para comparação, iremos configurar explicitamente o índice azul normalizado chamando my_index = "B/(R + G + B)".

analyze_objects(soy,
                marker = "id",
                topn_lower = 5,
                col_background = "black",
                my_index = "B /(R + G + B)")

Objetos grandes

cob <- image_import("cob.jpg")

# segmentação normal
analyze_objects(cob, marker = "id")

# ajustar o parâmetro object_size
cob_res <- 
  analyze_objects(cob,
                  object_size = "large",
                  marker = "id")

Objetos pequenos

wheat <- image_import("wheat.jpg")
analyze_objects(wheat)

vicia <- image_import("vicia2.jpg")
analyze_objects(vicia,
                index = "R",
                marker = "point",
                marker_col = "red",
                show_contour = FALSE)

Coordenadas de objetos

Os usuários podem obter as coordenadas para todos os objetos desejados com object_coord(). Quando o argumento id é definido como NULL (padrão), um retângulo delimitador é desenhado incluindo todos os objetos. Use id = "all" para obter as coordenadas de todos os objetos na imagem ou use um vetor numérico para indicar os objetos para calcular as coordenadas. Note que o argumento watershed = FALSE é usado para

folhas <- image_import(image = "leaves.jpg", plot = TRUE)

# obter o id de cada objeto
object_id(folhas,  watershed = FALSE)

# Obtenha as coordenadas de um retângulo delimitador em torno de todos os objetos
object_coord(folhas, watershed = FALSE)

## $col_min
## [1] 47
## 
## $col_max
## [1] 877
## 
## $row_min
## [1] 35
## 
## $row_max
## [1] 1112
# Obtenha as coordenadas para todos os objetos
object_coord(folhas,
             id = "all",
             watershed = FALSE)

## $col_min
##  [1]  47  51  54  56  75  79  94 262 292 292 295 308 314 374 376 390 392 412 422
## [20] 463 482 499 526 529 537 540 544 597 603 610 642 661 684 703 709 710 710 714
## [39] 761 778 779
## 
## $col_max
##  [1]  52 372 294 411 308  88 261 686 296 569 308 510 483 378 380 394 529 598 426
## [20] 677 486 572 531 534 657 610 730 658 608 717 785 869 877 707 713 768 714 718
## [39] 765 796 783
## 
## $row_min
##  [1] 534 769 464 181 622 425 390  35 516 977 515 345 531 155 156 163 688 796 176
## [20] 450 556 622 360 359 325 699 916 783 782 555 720 152 802 720 403 318 722 723
## [39] 320 362 639
## 
## $row_max
##  [1]  538 1037  606  370  761  430  452  189  520 1112  520  422  662  159  160
## [16]  167  734  941  180  564  560  660  364  363  362  732 1019  896  786  658
## [31]  762  308  932  724  407  406  726  727  324  641  643
# Obtenha as coordenadas dos objetos 1 e 3
object_coord(folhas,
             id = c(2, 4),
             watershed = FALSE)

## $col_min
## [1] 51 56
## 
## $col_max
## [1] 372 411
## 
## $row_min
## [1] 769 181
## 
## $row_max
## [1] 1037  370

Isolando objetos

Para isolar objetos, a função object_isolate() é usada. No exemplo a seguir, irei isolar o objeto 32 e definir uma borda de 5 pixels ao redor do objeto.

id1 <- 
  object_isolate(folhas,
                 watershed = FALSE,
                 id = 32,
                 edge = 5)
plot(id1)

Processamento em lote

Na análise de imagens, frequentemente é necessário processar mais de uma imagem. Por exemplo, no melhoramento de plantas, o número de grãos por planta (por exemplo, trigo) é frequentemente usado na seleção indireta de plantas de alto rendimento. No pliman, o processamento em lote pode ser feito quando o usuário declara o argumento pattern.

Para acelerar o tempo de processamento, especialmente para um grande número de imagens, o argumento parallel = TRUE pode ser usado. Nesse caso, as imagens são processadas de forma assíncrona(em paralelo) em sessões R separadas rodando em segundo plano na mesma máquina. O número de seções é configurado para 50% dos núcleos disponíveis. Este número pode ser controlado explicitamente com o argumento trabalhadores.

system.time(
  list_res <- analyze_objects(pattern = "img_sb")
)
## Processing image img_sb_50_1 |===                                | 8% 00:00:00 

## Processing image img_sb_50_10 |=====                             | 15% 00:00:00 

## Processing image img_sb_50_11 |========                          | 23% 00:00:01 

## Processing image img_sb_50_12 |==========                        | 31% 00:00:02 

## Processing image img_sb_50_13 |=============                     | 38% 00:00:02 

## Processing image img_sb_50_2 |================                   | 46% 00:00:03 

## Processing image img_sb_50_3 |===================                | 54% 00:00:04 

## Processing image img_sb_50_4 |======================             | 62% 00:00:05 

## Processing image img_sb_50_5 |========================           | 69% 00:00:06 

## Processing image img_sb_50_6 |===========================        | 77% 00:00:07 

## Processing image img_sb_50_7 |==============================     | 85% 00:00:08 

## Processing image img_sb_50_8 |================================   | 92% 00:00:09 

## Processing image img_sb_50_9 |===================================| 100% 00:00:10 
## --------------------------------------------
##         Image Objects
##   img_sb_50_1     100
##  img_sb_50_10      29
##  img_sb_50_11      23
##  img_sb_50_12      15
##  img_sb_50_13       7
##   img_sb_50_2      90
##   img_sb_50_3      83
##   img_sb_50_4      75
##   img_sb_50_5      70
##   img_sb_50_6      60
##   img_sb_50_7      57
##   img_sb_50_8      48
##   img_sb_50_9      36
## --------------------------------------------
## Done!

##   usuário   sistema decorrido 
##     10.44      0.51     10.97
# procesamento paralelo
# três múltiplas seções
system.time(
  list_res <- 
    analyze_objects(pattern = "img_sb",
                    show_image = FALSE,
                    parallel = TRUE,
                    workers = 3)
)
## Image processing using multiple sessions (3). Please wait.
## --------------------------------------------
##         Image Objects
##   img_sb_50_1     100
##  img_sb_50_10      29
##  img_sb_50_11      23
##  img_sb_50_12      15
##  img_sb_50_13       7
##   img_sb_50_2      90
##   img_sb_50_3      83
##   img_sb_50_4      75
##   img_sb_50_5      70
##   img_sb_50_6      60
##   img_sb_50_7      57
##   img_sb_50_8      48
##   img_sb_50_9      36
## --------------------------------------------
## Done!
##   usuário   sistema decorrido 
##      0.03      0.03      3.01
list_res$count %>% 
  head() %>%  
  print_tbl()
Image Objects
1 img_sb_50_1 100
7 img_sb_50_10 29
13 img_sb_50_11 23
19 img_sb_50_12 15
25 img_sb_50_13 7
31 img_sb_50_2 90
list_res$results %>% 
  head() %>% 
  print_tbl()
img id x y area area_ch perimeter radius_mean radius_min radius_max radius_sd radius_ratio diam_mean diam_min diam_max major_axis minor_axis eccentricity theta solidity circularity
img_sb_50_1 1 518.854 260.189 185 171 44 7.197 5.645 8.276 0.644 1.466 14.393 11.289 16.553 16.777 14.083 0.543 0.874 1.082 1.201
img_sb_50_1 2 468.818 398.552 192 175 43 7.357 6.306 8.202 0.415 1.301 14.715 12.612 16.404 16.288 15.046 0.383 1.105 1.097 1.305
img_sb_50_1 3 332.133 442.092 173 160 42 6.969 6.169 7.854 0.370 1.273 13.938 12.338 15.707 15.059 14.694 0.219 -0.425 1.081 1.232
img_sb_50_1 4 242.217 269.533 184 168 42 7.230 6.163 8.288 0.540 1.345 14.460 12.326 16.577 16.748 13.979 0.551 1.074 1.095 1.311
img_sb_50_1 5 365.399 453.936 188 171 42 7.310 6.243 8.210 0.481 1.315 14.621 12.485 16.419 16.734 14.297 0.520 -0.659 1.099 1.339
img_sb_50_1 6 540.210 199.448 181 167 44 7.138 6.252 8.087 0.463 1.294 14.276 12.504 16.175 15.695 14.813 0.330 0.806 1.084 1.175

Forma de objetos

A função analyze_objects() calcula uma gama de medidas que podem ser utilizadas para estudar a forma dos objetos, como por exemplo, folhas. Como exemplo, usarei a imagem potato_leaves.png, que foi coletada de Gupta et al.(2020)

batata <- image_pliman("potato_leaves.jpg", plot = TRUE)

pot_meas <-
  analyze_objects(batata,
                  watershed = FALSE,
                  marker = "id",
                  show_chull = TRUE) # mostra o casco convex

pot_meas$results %>% 
  print_tbl()
id x y area area_ch perimeter radius_mean radius_min radius_max radius_sd radius_ratio diam_mean diam_min diam_max major_axis minor_axis eccentricity theta solidity circularity
1 854.542 224.043 51380 54536 852 131.565 92.111 198.025 26.061 2.150 263.131 184.222 396.050 305.737 242.212 0.610 1.394 0.942 0.889
2 197.844 217.851 58923 76706 1064 140.296 70.106 192.361 28.585 2.744 280.592 140.212 384.723 318.244 274.128 0.508 -0.099 0.768 0.654
3 536.210 240.238 35117 62792 1310 109.900 38.137 188.511 35.510 4.943 219.800 76.273 377.021 253.498 243.279 0.281 1.097 0.559 0.257

As três medidas principais (em unidades de pixel) são:

  1. area a área do objeto.
  2. area_ch a área do casco convexo.
  3. perímetro o perímetro do objeto.

Usando essas medidas, a circularidade e a solidez são calculadas conforme mostrado em (Gupta et al, 2020).

$$ circularidade = 4 \pi(area / perimeter ^ 2) $$

$$ solidez = area / area \_ch$$

A circularidade é influenciada por serrilhas e saliências. A solidez é sensível a folhas com lóbulos profundos ou com pecíolo distinto e pode ser usada para distinguir folhas sem tais estruturas. Ao contrário da circularidade, não é muito sensível a serrilhas e saliências menores, uma vez que o casco convexo permanece praticamente inalterado.

Contorno do objeto

Os usuários também podem obter o contorno do objeto e o casco convexo da seguinte forma:

cont <-
  object_contour(batata,
                 watershed = FALSE,
                 show_image = FALSE)

plot(batata)
plot_contour(cont, col = "red", lwd = 3)

Casco convexo

A função object_contour() retorna uma lista com os pontos de coordenadas para cada contorno do objeto que pode ser usado posteriormente para obter o casco convexo com conv_hull().

conv <- conv_hull(cont)
plot(batata)
plot_contour(conv, col = "black", lwd = 3)
plot_measures(pot_meas, measure = "solidity")

Área do casco convexo

Então, a área do casco convexo pode ser obtida com poly_area().

area <- poly_area(conv)
area
## $`1`
## [1] 54536
## 
## $`2`
## [1] 76706
## 
## $`3`
## [1] 62792.5

Folhas como ggplot2

# criar um quadro de dados para contorno e casco convexo
library(tidyverse)

df_cont <- bind_rows(cont, .id = "objeto")
df_conv <- bind_rows(conv, .id = "objeto")

ggplot(df_cont, aes(X1, X2, group = objeto)) +
  geom_polygon(aes(fill = objeto)) +
  geom_polygon(data = df_conv,
               aes(x, y, fill = objeto),
               alpha = 0.3) +
  theme_void() +
  theme(legend.position = "bottom")

Comprimento e largura

No exemplo a seguir, mostro como calcular o contorno do objeto, o centro de massa e os raios máximo e mínimo (em unidades de pixel). Nesse caso, o diâmetro mínimo e máximo (calculado com analyze_objects()) pode ser usado como uma medida para aproximar a largura e o comprimento do grão do feijão, respectivamente.

bean <- image_import("bean.jpg", plot = TRUE)

bean_meas <- 
  analyze_objects(bean,
                  index = "G",
                  fill_hull = TRUE,
                  watershed = FALSE,
                  show_contour = FALSE,
                  col_background = "black",
                  marker = "id")
bean_meas_cor <- get_measures(bean_meas, dpi = 300)
bean_meas_cor[, c("id", "diam_min", "diam_max")]
##    id diam_min diam_max
## 1   1    0.998    2.272
## 6   6    0.916    2.324
## 11 11    1.002    2.105
## 12 12    0.940    2.151
## 29 29    0.875    1.867
## 34 34    0.940    2.248
# contorno
cont <- 
  object_contour(bean,
                 index = "G",
                 watershed = FALSE,
                 show_image = FALSE)

plot_contour(cont, col = "white", lwd = 2)

# centro de massa
cm <- poly_mass(cont)
plot_mass(cm,
          col = "white",
          arrow = TRUE)

# plota a largura e comprimento
plot_measures(bean_meas_cor, measure = "diam_min", vjust = 120)
plot_measures(bean_meas_cor, measure = "diam_max", hjust = 180)

Comprimento de raiz

No exemplo a seguir, mostro como medir o comprimento da raiz de mudas de soja. As imagens foram coletadas de Silva et al. 2019^[Silva LJ da, Medeiros AD de, Oliveira AMS (2019) SeedCalc, a new automated R software tool for germination and seedling length data processing. J Seed Sci 41:250–257. https://doi.org/10.1590/2317-1545V42N2217267︎].

roots <- image_import("root.jpg", plot = TRUE)

r1_meas <- 
  analyze_objects(roots,
                  index = "B",
                  marker = "id",
                  invert = TRUE)

Observe que a segmentação “watershed” segmentou os objetos conectados em vários objetos pequenos. Podemos melhorar esta segmentação usando o argumento tolerance (algumas tentativas serão necessárias para encontrar um valor adequado).

r1_meas <- 
  analyze_objects(roots,
                  index = "B",
                  marker = "id",
                  invert = TRUE,
                  tolerance = 3.5)

Muito melhor, mas ainda não é o que procuramos. Observe que as raízes e os cotilédones foram selecionados. Podemos, no entanto, usar uma restrição na seleção de objetos que, neste caso, pode ser a excentricidade do objeto. Usando os argumentos lower_eccent os objetos podem ser selecionados em relação à sua excentricidade.

r1_meas <- 
  analyze_objects(roots,
                  index = "B",
                  marker = "id",
                  invert = TRUE,
                  tolerance = 3,
                  lower_eccent = 0.95)

r1_meas_cor <- get_measures(r1_meas, dpi = 150)
# calcula a metade do perímetro
r1_meas_cor$metade_perimetro <- r1_meas_cor$perimeter / 2

# plota as medidas
plot(roots)
plot_measures(r1_meas_cor,
              measure = "metade_perimetro",
              hjust = 30,
              col = "red")
plot_measures(r1_meas_cor,
              measure = "diam_max",
              hjust = -30)

Valores RGB para cada objeto

Para obter a intensidade RGB de cada objeto de imagem, usamos o argumento object_rgb = TRUE na função analyze_objects() . Neste exemplo,

img <- image_import("green.jpg", plot = TRUE)

# identifica o índice que melhor segmenta a imagem
image_binary(img, index = "all")

O índice NB (padrão) foi escolhido para segmentar os grãos do fundo. A média dos valores da banda verde será calculada declarando object_index = "G".

soy_green <-
  analyze_objects(img,
                  object_index = "G",
                  marker_col = "black",
                  col_background = "white",
                  show_contour = FALSE)
plot_measures(soy_green,
              measure = "G",
              col = "black")

Parece que grãos com valores médios de verde (G) inferiores a 0.5 podem ser consideradas sementes esverdeadas. Os usuários podem então trabalhar com esse recurso e adaptá-lo ao seu caso.

report <- summary_index(soy_green, index = "G", cut_point = 0.5)
ids <- report$ids

plot(img)
plot_measures(soy_green,
              id = ids,
              measure = "G",
              col = "black")
cont <- object_contour(img, show_image = FALSE)
plot_contour(cont, id = ids, col = "red")

Grande número de objetos

Quando existem muitos objetos, o argumento parallel = TRUE irá acelerar a extração dos valores RGB. No exemplo a seguir, uma imagem com 1343 grãos de Vicia cracca é analisada. Os índices "R" e "R/G" são computados. Os grãos com um valor médio de vermelho superior a 0,25 são destacados.

img2 <- image_import("vicia.jpg")
vicia <-
  analyze_objects(img2,
                  index = "B",
                  object_index = pliman_indexes_eq(),
                  show_image = FALSE,
                  parallel = TRUE)

head(vicia$object_index) %>%
  print_tbl()
id R G B R/(R+G+B) G/(R+G+B) B/(R+G+B) G/B R/B G/R sqrt((R^2+G^2+B^2)/3) sqrt((R2+G2+B*2)/3) (R-G)/(R+G) (2G-R-B)/(2G+R+B) (2*R-G-B)/(G-B) (G-R)/(G+R) (G-B)/(G+B) (R-B)/(R+B) R+G+B ((R+G+B)-3*B)/(R+G+B) (G-R)/(G+R-B) atan(2*(B-G-R)/30.5*(G-R)) atan(2*(R-G-R)/30.5*(G-B)) B/G R+G+B/3 0.299R + 0.587G + 0.114*B (25*(G-R)/(G+R-B)+1.25) (max(R,G,B) - min(R,G,B)) / max(R,G,B) (R-B)/R 2*(R-G-B)/(G-B) R2/(B*G3)
1 0.334 0.336 0.255 0.354 0.363 0.282 1.334 1.325 1.041 0.313 0.778 -0.016 0.066 -Inf 0.016 0.132 0.115 0.925 0.153 5.000000e-02 0 -0.002 0.782 0.755 0.326 2.509000e+00 0.912 0.168 -Inf 13.233
2 0.296 0.298 0.239 0.348 0.356 0.296 1.238 1.225 Inf 0.280 0.734 -0.017 0.050 -Inf 0.017 0.097 0.081 0.833 0.111 Inf 0 -0.001 0.836 0.673 0.290 Inf 1.000 -Inf -Inf 19.327
3 0.281 0.289 0.229 0.348 0.362 0.290 1.263 1.223 1.045 0.268 0.725 -0.020 0.062 -Inf 0.020 0.112 0.092 0.799 0.130 4.300000e-02 0 -0.001 0.804 0.646 0.280 2.317000e+00 0.898 0.154 -Inf 15.999
4 0.290 0.300 0.240 0.345 0.361 0.294 1.253 1.207 1.058 0.279 0.736 -0.026 0.061 -Inf 0.026 0.106 0.081 0.831 0.118 5.800000e-02 0 -0.001 0.816 0.671 0.290 2.700000e+00 0.892 0.126 -Inf 16.655
5 0.270 0.301 0.256 0.319 0.364 0.317 1.181 1.060 1.230 0.278 0.738 -0.074 0.068 -Inf 0.074 0.076 0.003 0.827 0.049 2.330000e-01 0 -0.001 0.871 0.657 0.287 7.065000e+00 0.977 -0.129 -Inf 10.694
6 0.154 0.202 0.246 0.255 0.335 0.410 0.825 0.640 1.531 0.206 0.630 -0.147 0.005 3.018 0.147 -0.097 -0.235 0.603 -0.229 6.872053e+12 0 0.001 1.220 0.439 0.193 1.718013e+14 0.992 -0.974 14.723 14.218
cont2 <-
  object_contour(img2,
                 index = "B",
                 show_image = FALSE)

ids2 <- which(vicia$object_index$R > 0.25)
plot(img2)
plot_contour(cont2, id = ids2, col = "red")

# cria gráfico de densidade dos valores RGB para as duas classes de grãos
rgbs <-
  vicia$object_rgb %>%
  mutate(type = ifelse(id %in% ids2, "Destacado", "Não destacado")) %>%
  select(-id) %>%
  pivot_longer(-type) %>% 
  subset(name == "G")

ggplot(rgbs, aes(x = value)) +
  geom_density(fill = "green", alpha = 0.5) +
  facet_wrap(~type)

Área foliar

Uma imagem

folhas <- image_import(image = "leaves.jpg", plot = TRUE)

af <-
  analyze_objects(folhas,
                  watershed = FALSE,
                  lower_eccent = 0.3,
                  show_contour = FALSE,
                  col_background = "black")
af_cor <- get_measures(af, dpi = 50.8)
plot_measures(af_cor, measure = "area")

Preenchendo ‘buracos’

Um aspecto importante a se considerar é quando há a presença de ‘buracos’ nas folhas. Isto pode ocorrer, por exemplo, pelo ataque de pragas. Neste caso, a área teria que ser considerada, pois ela estava lá!

holes <- image_import("holes.jpg", plot = TRUE)

af <-
  analyze_objects(holes,
                  watershed = FALSE,
                  col_background = "white",
                  marker = "area",
                  marker_col = "red",
                  marker_size = 3,
                  show_image = FALSE,
                  save_image = TRUE,
                  dir_processed = tempdir(),
                  contour_size = 5)

af2 <-
  analyze_objects(holes,
                  fill_hull = TRUE, # preenche 'buracos'
                  watershed = FALSE,
                  col_background = "white",
                  marker = "area",
                  marker_col = "red",
                  marker_size = 3,
                  show_image = FALSE,
                  save_image = TRUE,
                  prefix = "proc2_",
                  dir_processed = tempdir(),
                  contour_size = 5)

imgs <- image_import(pattern = "proc", path = tempdir())
image_combine(imgs)

Várias imagens da mesma amostra

Se os usuários precisarem analisar várias imagens da mesma amostra, as imagens da mesma amostra devem compartilhar o mesmo prefixo de nome de arquivo, que é definido como a parte do nome do arquivo que precede o primeiro hífen (-) ou underscore (_). Então, ao usar get_measures(), as medidas das imagens de folhas chamadas, por exemplo, F1-1.jpeg, F1_2.jpeg e F1-3.jpeg serão combinadas em uma única imagem (F1), mostrado no objeto merge. Isso é útil, por exemplo, para analisar folhas grandes que precisam ser divididas em várias imagens ou várias folhas pertencentes à mesma amostra que não podem ser digitalizadas em uma imagem única.

No exemplo a seguir, cinco imagens serão usadas como exemplos. Cada imagem possui folhas de diferentes espécies. As imagens foram divididas em imagens diferentes que compartilham o mesmo prefixo (por exemplo, L1_*, L2_* e assim por diante). Observe que para garantir que todas as imagens sejam processadas, todas as imagens devem compartilhar um padrão comum, neste caso (“L”). Os três pontos no canto inferior direito têm uma distância conhecida de 5 cm entre eles, que pode ser usada para extrair o dpi da imagem com dpi(). Apenas para fins didáticos, considerarei que todas as imagens têm resolução de 100 dpi.

# imagens inteiras
imgs <-
  image_import(pattern = "leaf",
               plot = TRUE,
               ncol = 2)

# imagens da mesma amostra
sample_imgs <-
  image_import(pattern = "L",
               plot = TRUE,
               ncol = 5)

Aqui, usarei o pattern =" L " para indicar que todas as imagens com este nome de padrão devem ser analisadas. O índice verde (" G ") é usado para segmentar as folhas e divisor de águas = FALSO é usado para omitir o algoritmo de segmentação de divisor de águas.

merged <-
  analyze_objects(pattern = "L",
                  index = "B",
                  watershed = FALSE)
## Processing image L1_1 |====                                      | 8% 00:00:00 

## Processing image L1_2 |=======                                   | 17% 00:00:00 

## Processing image L2_1 |==========                                | 25% 00:00:00 

## Processing image L2_2 |==============                            | 33% 00:00:01 

## Processing image L3_1 |==================                        | 42% 00:00:01 

## Processing image L3_2 |=====================                     | 50% 00:00:01 

## Processing image L3_3 |========================                  | 58% 00:00:01 

## Processing image L4_1 |============================              | 67% 00:00:02 

## Processing image L4_2 |================================          | 75% 00:00:02 

## Processing image L4_3 |===================================       | 83% 00:00:02 

## Processing image L5_1 |======================================    | 92% 00:00:03 

## Processing image L5_2 |==========================================| 100% 00:00:03 
## --------------------------------------------
##  Image Objects
##   L1_1       1
##   L1_2       1
##   L2_1       2
##   L2_2       3
##   L3_1       1
##   L3_2       1
##   L3_3       1
##   L4_1       2
##   L4_2       2
##   L4_3       3
##   L5_1       3
##   L5_2       3
## --------------------------------------------
## Done!

Usando a função get_measures() é possível converter as medidas de unidades de pixel em unidades métricas (cm$^ 2$).

merged_cor <- get_measures(merged, dpi = 100)

Observe que o merged_cor é uma lista com três objetos:

  • results: um data frame que contém as medidas de cada objeto individual (neste caso, folha) de cada imagem analisada.
merged_cor$results %>% 
  print_tbl()
img id x y area area_ch perimeter radius_mean radius_min radius_max radius_sd radius_ratio diam_mean diam_min diam_max major_axis minor_axis eccentricity theta solidity circularity
L1_1 1 427.723 516.719 102.170 292.253 115.189 6.217 0.561 11.913 114.118 21.226 12.435 1.122 23.826 19.863 15.144 0.647 0.158 0.350 0.097
L1_2 1 372.556 526.031 92.840 207.023 107.188 5.341 1.042 10.168 86.064 9.754 10.682 2.085 20.336 15.123 13.215 0.486 0.600 0.448 0.102
L2_1 1 251.404 282.798 46.319 46.789 24.003 3.936 2.527 5.920 35.873 2.342 7.872 5.054 11.840 10.479 5.708 0.839 1.502 0.990 1.010
L2_1 2 146.366 701.900 35.305 36.136 21.488 3.441 2.311 5.209 32.274 2.254 6.881 4.621 10.417 9.308 4.873 0.852 -1.533 0.977 0.961
L2_2 1 326.113 391.141 19.026 19.221 16.739 2.613 1.659 4.129 29.010 2.488 5.225 3.319 8.259 7.301 3.375 0.887 1.560 0.990 0.853
L2_2 2 113.850 755.321 38.153 38.670 20.904 3.534 2.518 5.097 26.159 2.024 7.069 5.036 10.195 8.955 5.462 0.792 1.556 0.987 1.097
L2_2 3 376.213 784.804 16.320 16.439 14.580 2.365 1.532 3.616 23.766 2.361 4.730 3.063 7.231 6.509 3.238 0.867 1.395 0.993 0.965
L3_1 1 253.962 480.290 64.082 117.849 79.324 4.199 0.408 9.476 80.727 23.207 8.397 0.817 18.952 15.225 8.681 0.822 -1.550 0.544 0.128
L3_2 1 200.077 436.484 30.205 57.570 54.813 2.830 0.065 6.576 56.098 100.588 5.661 0.131 13.152 9.857 7.103 0.693 1.564 0.525 0.126
L3_3 1 204.984 363.476 52.026 87.382 80.137 3.494 0.077 7.821 65.208 101.216 6.989 0.155 15.641 12.404 8.414 0.735 1.538 0.595 0.102
L4_1 1 270.258 326.544 54.355 54.773 24.638 4.177 3.390 5.717 21.940 1.686 8.354 6.780 11.434 9.918 7.035 0.705 1.484 0.992 1.125
L4_1 3 252.952 845.157 61.679 63.975 28.499 4.540 3.654 6.488 27.727 1.776 9.080 7.307 12.975 10.549 7.549 0.699 1.428 0.964 0.954
L4_2 1 291.942 235.565 61.503 62.870 28.067 4.544 3.148 6.592 32.407 2.094 9.087 6.297 13.183 10.970 7.278 0.748 -1.332 0.978 0.981
L4_2 2 260.787 799.754 73.268 75.388 30.658 4.938 3.787 7.013 30.654 1.852 9.876 7.574 14.026 11.609 8.152 0.712 1.527 0.972 0.980
L4_3 1 206.166 213.417 29.186 29.581 19.279 3.097 2.054 4.374 26.315 2.130 6.193 4.107 8.748 8.256 4.535 0.836 -1.536 0.987 0.987
L4_3 2 219.514 552.896 19.503 20.439 16.713 2.595 1.608 3.940 25.493 2.451 5.191 3.215 7.881 7.069 3.548 0.865 1.538 0.954 0.877
L4_3 3 229.163 937.157 34.149 35.426 23.520 3.470 2.211 5.138 31.462 2.324 6.940 4.421 10.277 8.809 5.090 0.816 1.471 0.964 0.776
L5_1 1 225.338 275.720 52.797 54.448 31.217 4.590 2.259 7.735 63.899 3.425 9.179 4.517 15.470 13.610 5.029 0.929 -1.479 0.970 0.681
L5_1 2 335.525 884.869 31.675 32.608 24.460 3.619 1.727 6.199 52.019 3.590 7.237 3.454 12.398 10.718 3.823 0.934 1.382 0.971 0.665
L5_1 3 120.360 887.853 30.192 32.323 27.102 3.827 1.649 6.774 59.974 4.109 7.654 3.297 13.549 11.055 3.579 0.946 -1.564 0.934 0.517
L5_2 1 339.022 300.133 45.622 46.825 30.023 4.331 2.188 7.465 63.358 3.412 8.662 4.376 14.930 13.160 4.466 0.941 -1.547 0.974 0.636
L5_2 2 205.390 802.624 42.866 43.898 29.464 4.267 2.060 7.335 62.330 3.561 8.534 4.120 14.670 12.766 4.355 0.940 1.508 0.976 0.621
L5_2 3 535.301 819.600 44.996 46.294 29.337 4.300 1.992 7.465 62.372 3.748 8.600 3.984 14.930 13.069 4.450 0.940 -1.450 0.972 0.657
  • summary: um data frame que contém o resumo dos resultados, contendo o número de objetos em cada imagem (n) a soma, média e desvio padrão da área de cada imagem, bem como o valor médio para todas as outras medidas (perímetro, raio, etc.)
merged_cor$summary %>% 
  print_tbl()
img n area_sum area_mean area_sd area_ch perimeter radius_mean radius_min radius_max radius_sd radius_ratio diam_mean diam_min diam_max major_axis minor_axis eccentricity theta solidity circularity
L1_1 1 102.170 102.170 0.000 292.253 115.189 6.217 0.561 11.913 114.118 21.226 12.435 1.122 23.826 19.863 15.144 0.647 0.158 0.350 0.097
L1_2 1 92.840 92.840 0.000 207.023 107.188 5.341 1.042 10.168 86.064 9.754 10.682 2.085 20.336 15.123 13.215 0.486 0.600 0.448 0.102
L2_1 2 81.624 40.812 7.788 41.462 22.746 3.688 2.419 5.564 34.073 2.298 7.377 4.838 11.128 9.893 5.290 0.845 -0.016 0.983 0.986
L2_2 3 73.500 24.500 11.901 24.777 17.407 2.837 1.903 4.281 26.312 2.291 5.675 3.806 8.562 7.588 4.025 0.849 1.504 0.990 0.972
L3_1 1 64.082 64.082 0.000 117.849 79.324 4.199 0.408 9.476 80.727 23.207 8.397 0.817 18.952 15.225 8.681 0.822 -1.550 0.544 0.128
L3_2 1 30.205 30.205 0.000 57.570 54.813 2.830 0.065 6.576 56.098 100.588 5.661 0.131 13.152 9.857 7.103 0.693 1.564 0.525 0.126
L3_3 1 52.026 52.026 0.000 87.382 80.137 3.494 0.077 7.821 65.208 101.216 6.989 0.155 15.641 12.404 8.414 0.735 1.538 0.595 0.102
L4_1 2 116.034 58.017 5.178 59.374 26.568 4.358 3.522 6.102 24.833 1.731 8.717 7.044 12.205 10.233 7.292 0.702 1.456 0.978 1.040
L4_2 2 134.771 67.385 8.319 69.129 29.362 4.741 3.468 6.802 31.530 1.973 9.482 6.936 13.605 11.289 7.715 0.730 0.097 0.975 0.980
L4_3 3 82.838 27.613 7.449 28.482 19.837 3.054 1.957 4.484 27.757 2.302 6.108 3.915 8.968 8.045 4.391 0.839 0.491 0.968 0.880
L5_1 3 114.664 38.221 12.645 39.793 27.593 4.012 1.878 6.903 58.631 3.708 8.023 3.756 13.806 11.795 4.144 0.937 -0.554 0.958 0.621
L5_2 3 133.484 44.495 1.445 45.672 29.608 4.299 2.080 7.422 62.687 3.573 8.599 4.160 14.843 12.998 4.424 0.940 -0.496 0.974 0.638
  • merge: um data frame que contém os resultados mesclados pelo prefixo da imagem. Observe que, neste caso, os resultados são apresentados por L1, L2, L3, L4 e L5.
merged_cor$merge %>% 
  print_tbl()
img n area_sum area_mean area_sd area_ch perimeter radius_mean radius_min radius_max radius_sd radius_ratio diam_mean diam_min diam_max major_axis minor_axis eccentricity theta solidity circularity
L1 2 195.010 97.505 0.000 249.638 111.189 5.779 0.802 11.040 100.091 15.490 11.558 1.604 22.081 17.493 14.179 0.567 0.379 0.399 0.099
L2 5 155.124 32.656 9.845 33.120 20.077 3.263 2.161 4.922 30.192 2.295 6.526 4.322 9.845 8.741 4.658 0.847 0.744 0.987 0.979
L3 3 146.313 48.771 0.000 87.600 71.425 3.508 0.184 7.958 67.344 75.004 7.016 0.367 15.915 12.495 8.066 0.750 0.517 0.555 0.119
L4 7 333.643 51.005 6.982 52.328 25.256 4.051 2.982 5.796 28.040 2.002 8.102 5.965 11.592 9.856 6.466 0.757 0.681 0.974 0.967
L5 6 248.149 41.358 7.045 42.733 28.600 4.156 1.979 7.162 60.659 3.641 8.311 3.958 14.325 12.396 4.284 0.938 -0.525 0.966 0.629

O area_sum de img L1 é a soma das duas folhas (uma em cada imagem)

sum(merged_cor$results$area[1:2])
## [1] 195.01
df_leaf <-
  merged_cor$results %>% 
  separate(img, into = c("img", "code"))

# leaf area of the different species
p1 <- 
  ggplot(df_leaf, aes(x = img, y = area)) +
  geom_boxplot() +
  geom_jitter(color = "red") +
  labs(x = "Imagem", y = expression(Área~(cm^2)))

p2 <- 
  ggplot(df_leaf, aes(x = img, y = area)) +
  stat_summary(fun = sum,
               geom = "bar",
               col = "black") +
  labs(x = "Imagem", y = expression(Área~total~(cm^2)))


# solidity of the different species
p3 <- 
  ggplot(df_leaf, aes(x = img, y = solidity)) +
  geom_boxplot() +
  geom_jitter(color = "red") +
  labs(x = "Imagem", y = "Solidez")

p1 + p2 + p3 +
  plot_layout(ncol = 3)

Previous
Next