├── NEWS.md ├── LICENSE ├── tests ├── testthat.R └── testthat │ └── test-integration.R ├── .Rbuildignore ├── NAMESPACE ├── man ├── df_model.Rd ├── install_deepforest.Rd └── get_data.Rd ├── docker-compose.yml ├── load_and_test.R ├── .gitignore ├── inst └── CITATION ├── LICENSE.md ├── DESCRIPTION ├── .github └── workflows │ └── test-package.yml ├── Dockerfile ├── paper ├── paper.md └── paper.bib ├── R └── deepforestr.R ├── CODE_OF_CONDUCT.md └── README.md /NEWS.md: -------------------------------------------------------------------------------- 1 | 2 | deepforestr 1.0.0 3 | ================= 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2021 2 | COPYRIGHT HOLDER: The Deepforestr Contributors and The University of Florida -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library("testthat") 2 | library("devtools") 3 | 4 | test_check("deepforestr") 5 | 6 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^CONTRIBUTING\.md$ 4 | ^CODE_OF_CONDUCT\.md$ 5 | ^LICENSE\.md$ 6 | ^\.zenodo\.json$ 7 | ^codecov\.yml$ 8 | ^\.github$ 9 | ^Dockerfile$ 10 | ^docker-compose.yml$ 11 | ^load_and_test.R$ 12 | ^LICENSE.md$ 13 | paper$ 14 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(df_model) 4 | export(get_data) 5 | export(install_deepforest) 6 | importFrom(reticulate,conda_remove) 7 | importFrom(reticulate,import) 8 | importFrom(reticulate,install_miniconda) 9 | importFrom(reticulate,py_install) 10 | importFrom(reticulate,r_to_py) 11 | -------------------------------------------------------------------------------- /man/df_model.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/deepforestr.R 3 | \name{df_model} 4 | \alias{df_model} 5 | \title{Deepforest Model object} 6 | \usage{ 7 | df_model() 8 | } 9 | \description{ 10 | Deepforest Model object 11 | } 12 | \examples{ 13 | \dontrun{ 14 | model = deepforestr::df_model() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | 5 | rdata: 6 | container_name: deepforestr_container 7 | image: deepforestr_image 8 | command: bash -c "python --version" 9 | environment: 10 | # If IN_DOCKER is set, 11 | # Otherwise use local 12 | "IN_DOCKER" : "true" 13 | "NOT_CRAN" : "true" 14 | build: . 15 | -------------------------------------------------------------------------------- /man/install_deepforest.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/deepforestr.R 3 | \name{install_deepforest} 4 | \alias{install_deepforest} 5 | \title{Install the DeepForest Python package} 6 | \usage{ 7 | install_deepforest() 8 | } 9 | \description{ 10 | Install the DeepForest Python package 11 | } 12 | \examples{ 13 | \dontrun{ 14 | deepforestr::install_deepforest()} 15 | 16 | } 17 | -------------------------------------------------------------------------------- /man/get_data.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/deepforestr.R 3 | \name{get_data} 4 | \alias{get_data} 5 | \title{Get example data} 6 | \usage{ 7 | get_data(path) 8 | } 9 | \arguments{ 10 | \item{path}{path to the example data file} 11 | } 12 | \description{ 13 | Get example data 14 | } 15 | \examples{ 16 | \dontrun{ 17 | deepforestr::get_data("OSBS_029.png") 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /load_and_test.R: -------------------------------------------------------------------------------- 1 | # Used for continous testing platform 2 | # Install analysis packages using pacman 3 | # Pacman will load the packages and install 4 | # the packaes not available 5 | 6 | if (Sys.getenv("IN_DOCKER") == "true") { 7 | # Install pacman if it isn't already installed 8 | if ("pacman" %in% rownames(installed.packages()) == FALSE) install.packages("pacman") 9 | suppressMessages( 10 | pacman::p_load(devtools, readr, rmarkdown, 11 | testthat, tidyverse, reticulate, semver) 12 | ) 13 | 14 | devtools::install_local(".", force = TRUE) 15 | # Test package 16 | test_dir("tests/testthat", reporter = c("check", "progress")) 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # History files 2 | .Rhistory 3 | .Rapp.history 4 | 5 | # Session Data files 6 | .RData 7 | 8 | # User-specific files 9 | .Ruserdata 10 | 11 | # Example code in package build process 12 | *-Ex.R 13 | 14 | # Output files from R CMD build 15 | /*.tar.gz 16 | 17 | # Output files from R CMD check 18 | /*.Rcheck/ 19 | 20 | # RStudio files 21 | .Rproj.user/ 22 | 23 | # produced vignettes 24 | vignettes/*.html 25 | vignettes/*.pdf 26 | 27 | # OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3 28 | .httr-oauth 29 | 30 | # knitr and R markdown default cache directories 31 | *_cache/ 32 | /cache/ 33 | 34 | # Temporary files created by R markdown 35 | *.utf8.md 36 | *.knit.md 37 | 38 | # R Environment Variables 39 | .Renviron 40 | -------------------------------------------------------------------------------- /inst/CITATION: -------------------------------------------------------------------------------- 1 | citHeader("To cite DeepForest in publications use:") 2 | 3 | citEntry( 4 | entry = "Article", 5 | title = "DeepForest: A Python package for RGB deep learning tree crown delineation", 6 | author = "Weinstein, Ben G. and Marconi, Sergio and Aubry-Kientz, Mélaine and Vincent, Gregoire and Senyondo, Henry and White, Ethan P.", 7 | journal = "Methods in Ecology and Evolution", 8 | year = 2020, 9 | volume = 11, 10 | number = 12, 11 | pages = 1743-1751, 12 | url = "https://besjournals.onlinelibrary.wiley.com/doi/abs/10.1111/2041-210X.13472", 13 | textVersion = paste("Weinstein, BG, Marconi, S, Aubry-Kientz, M, Vincent, G, Senyondo, H, White, EP. DeepForest: A Python package for RGB deep learning tree crown delineation. Methods Ecol Evol. 2020; 11: 1743– 1751. https://doi.org/10.1111/2041-210X.13472") 14 | ) 15 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 The Deepforestr Contributors and The University of Florida 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: deepforestr 2 | Title: R Interface to the Deepforest package 3 | Description: Provides an R interface to the Deepforest 4 | via the Deepforest's 5 | command line interface. The Deepforest is a python package for training and predicting individual tree crowns from airborne RGB imagery. 6 | Version: 0.0.2 7 | Authors@R: c(person("Ethan", "White", role = c("aut", "cre"), 8 | email = "ethan@weecology.org", 9 | comment = c(ORCID = "0000-0001-6728-7745")), 10 | person("Henry", "Senyondo", role = c("aut"), 11 | email = "henrykironde@gmail.com", 12 | comment = c(ORCID = "0000-0001-7105-5808")), 13 | person("Ben", "Weinstein", role = "aut", 14 | email = "benweinstein2010@gmail.com", 15 | comment = c(ORCID = "0000-0002-2176-7935"))) 16 | BugReports: https://github.com/weecology/DeepForest/issues 17 | URL: https://github.com/weecology/DeepForest/ (website), 18 | https://github.com/weecology/DeepForest/ 19 | Depends: 20 | R (>= 3.4.0) 21 | Imports: 22 | reticulate (>= 1.16) 23 | Suggests: 24 | testthat (>= 1.0.0), 25 | devtools, 26 | knitr, 27 | raster, 28 | rmarkdown 29 | VignetteBuilder: knitr 30 | SystemRequirements: Python (>= 3.0) (version must be listed to patch to allow parsing) 31 | License: MIT + file LICENSE 32 | LazyData: true 33 | RoxygenNote: 7.2.3 34 | Encoding: UTF-8 35 | -------------------------------------------------------------------------------- /.github/workflows/test-package.yml: -------------------------------------------------------------------------------- 1 | # R package release checking 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | name: R-CMD-check-release 12 | 13 | jobs: 14 | R-CMD-check: 15 | strategy: 16 | matrix: 17 | os: [ubuntu-latest, windows-latest, macos-13] 18 | fail-fast: false 19 | runs-on: ${{ matrix.os }} 20 | env: 21 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | CRAN_REPO: https://packagemanager.rstudio.com/all/__linux__/focal/latest 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: Setup R (Ubuntu) 26 | if: matrix.os == 'ubuntu-latest' 27 | uses: eddelbuettel/github-actions/r2u-setup@master 28 | - name: Setup R (Other) 29 | if: matrix.os != 'ubuntu-latest' 30 | uses: r-lib/actions/setup-r@v2 31 | - name: Install package dependencies 32 | run: | 33 | install.packages(c("remotes", "rcmdcheck", "reticulate")) 34 | remotes::install_deps(dependencies = TRUE, repos = c("CRAN" = Sys.getenv("CRAN_REPO"))) 35 | shell: Rscript {0} 36 | - name: Install DeepForest 37 | run: | 38 | reticulate::install_miniconda() 39 | reticulate::py_install('DeepForest', pip=TRUE) 40 | shell: Rscript {0} 41 | - name: Build and check deepforester 42 | uses: r-lib/actions/check-r-package@v2 43 | -------------------------------------------------------------------------------- /tests/testthat/test-integration.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | 3 | context("integration tests for DeepForest in R") 4 | 5 | skip_if_no_deepforest <- function() { 6 | deepforest_avail <- reticulate::py_module_available("deepforest") 7 | if (!deepforest_avail) 8 | skip("deepforest not available for testing") 9 | } 10 | 11 | test_that("deepforest model is installed", { 12 | skip_if_no_deepforest() 13 | deepforest_available = reticulate::py_module_available("deepforest") 14 | expect_identical(deepforest_available, TRUE) 15 | }) 16 | 17 | test_that("deepforest model exists when loaded", { 18 | skip_if_no_deepforest() 19 | model = df_model() 20 | expect_type(model, "closure") 21 | }) 22 | 23 | test_that("use_release model exists when loaded", { 24 | skip_if_no_deepforest() 25 | model = df_model() 26 | model$use_release() 27 | expect_type(model, "closure") 28 | }) 29 | 30 | test_that("image prediction works", { 31 | skip_if_no_deepforest() 32 | model = df_model() 33 | model$use_release() 34 | image_path = get_data("OSBS_029.png") 35 | bounding_boxes = model$predict_image(path=image_path, return_plot=FALSE) 36 | expect_equal(nrow(bounding_boxes), 56) 37 | }) 38 | 39 | test_that("training works", { 40 | skip_if_no_deepforest() 41 | model = df_model() 42 | model$use_release() 43 | annotations_file = get_data("testfile_deepforest.csv") 44 | model$config$epochs = 1 45 | model$config["save-snapshot"] = FALSE 46 | model$config$train$csv_file = annotations_file 47 | model$config$train$root_dir = get_data(".") 48 | model$config$train$fast_dev_run = TRUE 49 | model$create_trainer() 50 | model$trainer$fit(model) 51 | expect_type(model, "closure") 52 | }) 53 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rocker/tidyverse:latest 2 | 3 | MAINTAINER Weecology "https://github.com/weecology/deepforestr" 4 | 5 | # Write enviromental options to config files 6 | RUN echo "options(repos='https://cloud.r-project.org/')" >> ~/.Rprofile 7 | RUN echo "options(repos='https://cloud.r-project.org/')" >> ~/.Renviron 8 | RUN echo "R_LIBS=\"/usr/lib/R/library\"">> ~/.Rprofile 9 | RUN echo "R_LIBS=\"/usr/lib/R/library\"">> ~/.Renviron 10 | RUN echo "R_LIBS_USER=\"/usr/lib/R/library\"">> ~/.Renviron 11 | 12 | RUN apt-get update && apt-get install -y --no-install-recommends apt-utils 13 | RUN apt-get install -y --force-yes build-essential wget git locales locales-all > /dev/null 14 | 15 | # Set encoding 16 | ENV LC_ALL en_US.UTF-8 17 | ENV LANG en_US.UTF-8 18 | ENV LANGUAGE en_US.UTF-8 19 | 20 | # Remove python2 and install python3 21 | RUN apt-get remove -y python && apt-get install -y python3 python3-pip curl 22 | RUN rm -f /usr/bin/python && ln -s /usr/bin/python3 /usr/bin/python 23 | RUN rm -f /usr/bin/pip && ln -s /usr/bin/pip3 /usr/bin/pip 24 | 25 | RUN echo "export PATH="/usr/bin/python:$PATH"" >> ~/.profile 26 | RUN echo "export PYTHONPATH="/usr/bin/python:$PYTHONPATH"" >> ~/.profile 27 | 28 | 29 | # Add permissions to config files 30 | RUN chmod 0644 ~/.Renviron 31 | RUN chmod 0644 ~/.Rprofile 32 | RUN chmod 0644 ~/.profile 33 | 34 | # Install deepforest python package 35 | RUN pip install pillow 36 | # h5py, pillow, kaggle fail to install from the requirement file. 37 | RUN pip install git+https://git@github.com/weecology/DeepForest.git 38 | RUN R_RETICULATE_PYTHON="/usr/bin/python" | echo $R_RETICULATE_PYTHON >> ~/.Renviron 39 | 40 | COPY . ./deepforestr 41 | 42 | WORKDIR ./deepforestr 43 | 44 | 45 | CMD ["bash", "-c", "deepforestr -v"] 46 | -------------------------------------------------------------------------------- /paper/paper.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'DeepForestR: R Interface to the Data DeepForest' 3 | tags: 4 | - data retrieval, data processing, R, data, data science, datasets 5 | authors: 6 | - name: Henry Senyondo 7 | orcid: 0000-0001-7105-5808 8 | affiliation: 1, 3 9 | - name: Ben G Weinstein 10 | orcid: 0000-0002-2176-7935 11 | affiliation: 1,3 12 | - name: Ethan White 13 | affiliation: 1, 2, 3 14 | orcid: 0000-0001-6728-7745 15 | 16 | 17 | affiliations: 18 | - name: Department of Wildlife Ecology and Conservation, University of Florida 19 | index: 1 20 | - name: Informatics Institute, University of Florida 21 | index: 2 22 | - name: The University of Florida 23 | index: 3 24 | 25 | 26 | date: 16 June 2021 27 | bibliography: paper.bib 28 | --- 29 | 30 | # DeepForestR: An R package for training and predicting individual tree crowns from airborne RGB imagery. 31 | 32 | ## Summary 33 | 34 | The DeepForestR ... 35 | 36 | ## Statement of Need 37 | 38 | A pytorch implementation of the DeepForest model for individual tree crown detection in RGB images. DeepForest is a python package for training and predicting individual tree crowns from airborne RGB imagery. DeepForest comes with a prebuilt model trained on data from the National Ecological Observatory Network. Users can extend this model by annotating and training custom models starting from the prebuilt model. 39 | 40 | ## Implementation 41 | 42 | The main .... 43 | 44 | ## Acknowledgements 45 | 46 | Development of this software was funded by 47 | [the Gordon and Betty Moore Foundation's Data-Driven Discovery Initiative](http://www.moore.org/programs/science/data-driven-discovery) through 48 | [Grant GBMF4563](http://www.moore.org/grants/list/GBMF4563) to Ethan P. White. 49 | 50 | ## References 51 | 52 | -------------------------------------------------------------------------------- /R/deepforestr.R: -------------------------------------------------------------------------------- 1 | #' Install the DeepForest Python package 2 | #' 3 | #' @examples 4 | #' \dontrun{ 5 | #' deepforestr::install_deepforest()} 6 | #' 7 | #' @importFrom reticulate install_miniconda py_install conda_remove 8 | #' @export 9 | install_deepforest <- function() { 10 | miniconda_path = reticulate::miniconda_path() 11 | if (!dir.exists(miniconda_path)) { 12 | reticulate::install_miniconda() 13 | } else { 14 | print(sprintf("Using existing miniconda install at %s", miniconda_path)) 15 | } 16 | reticulate::py_install(c("gdal", "rasterio", "fiona"), method = "conda") 17 | #if (reticulate::py_module_available("mkl")) { 18 | # Remove package that has caused conflicts on Windows due to double install 19 | # The correct version of mkl will be installed with deepforest (below) 20 | # on systems where it is needed 21 | # reticulate::conda_remove("r-reticulate", packages = c("mkl")) 22 | #} 23 | reticulate::py_install("DeepForest", method = "conda", pip = TRUE) 24 | } 25 | 26 | #' Get example data 27 | #' 28 | #' @param path path to the example data file 29 | #' 30 | #' @examples 31 | #' \dontrun{ 32 | #' deepforestr::get_data("OSBS_029.png") 33 | #' } 34 | #' 35 | #' @importFrom reticulate import r_to_py 36 | #' @export 37 | get_data <- function(path) { 38 | deepforest$get_data(path) 39 | } 40 | 41 | #' Deepforest Model object 42 | #' 43 | #' @examples 44 | #' \dontrun{ 45 | #' model = deepforestr::df_model() 46 | #' } 47 | #' @importFrom reticulate import r_to_py 48 | #' @export 49 | df_model <- function() { 50 | deepforest$main$deepforest() 51 | } 52 | 53 | # global reference to python modules (will be initialized in .onLoad) 54 | deepforest <- NULL 55 | 56 | .onLoad <- function(libname, pkgname) { 57 | ## assignment in parent environment! 58 | try({ 59 | deepforest <<- reticulate::import("deepforest", delay_load = TRUE) 60 | # Disable due to failure to test on win cran dev platform 61 | # check_deepforest_availability() 62 | }, silent = TRUE) 63 | } 64 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at ethan@weecology.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: https://contributor-covenant.org 46 | [version]: https://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /paper/paper.bib: -------------------------------------------------------------------------------- 1 | @Article{rs11111309, 2 | AUTHOR = {Weinstein, Ben G. and Marconi, Sergio and Bohlman, Stephanie and Zare, Alina and White, Ethan}, 3 | TITLE = {Individual Tree-Crown Detection in RGB Imagery Using Semi-Supervised Deep Learning Neural Networks}, 4 | JOURNAL = {Remote Sensing}, 5 | VOLUME = {11}, 6 | YEAR = {2019}, 7 | NUMBER = {11}, 8 | ARTICLE-NUMBER = {1309}, 9 | URL = {https://www.mdpi.com/2072-4292/11/11/1309}, 10 | ISSN = {2072-4292}, 11 | ABSTRACT = {Remote sensing can transform the speed, scale, and cost of biodiversity and forestry surveys. Data acquisition currently outpaces the ability to identify individual organisms in high resolution imagery. We outline an approach for identifying tree-crowns in RGB imagery while using a semi-supervised deep learning detection network. Individual crown delineation has been a long-standing challenge in remote sensing and available algorithms produce mixed results. We show that deep learning models can leverage existing Light Detection and Ranging (LIDAR)-based unsupervised delineation to generate trees that are used for training an initial RGB crown detection model. Despite limitations in the original unsupervised detection approach, this noisy training data may contain information from which the neural network can learn initial tree features. We then refine the initial model using a small number of higher-quality hand-annotated RGB images. We validate our proposed approach while using an open-canopy site in the National Ecological Observation Network. Our results show that a model using 434,551 self-generated trees with the addition of 2848 hand-annotated trees yields accurate predictions in natural landscapes. Using an intersection-over-union threshold of 0.5, the full model had an average tree crown recall of 0.69, with a precision of 0.61 for the visually-annotated data. The model had an average tree detection rate of 0.82 for the field collected stems. The addition of a small number of hand-annotated trees improved the performance over the initial self-supervised model. This semi-supervised deep learning approach demonstrates that remote sensing can overcome a lack of labeled training data by generating noisy data for initial training using unsupervised methods and retraining the resulting models with high quality labeled data.}, 12 | DOI = {10.3390/rs11111309} 13 | } 14 | 15 | @article {Weinstein790071, 16 | author = {Weinstein, Ben. G. and Marconi, Sergio and Bohlman, Stephanie A. and Zare, Alina and White, Ethan P.}, 17 | title = {Geographic Generalization in Airborne RGB Deep Learning Tree Detection}, 18 | elocation-id = {790071}, 19 | year = {2019}, 20 | doi = {10.1101/790071}, 21 | publisher = {Cold Spring Harbor Laboratory}, 22 | abstract = {Tree detection is a fundamental task in remote sensing for forestry and ecosystem ecology applications. While many individual tree segmentation algorithms have been proposed, the development and testing of these algorithms is typically site specific, with few methods evaluated against data from multiple forest types simultaneously. This makes it difficult to determine the generalization of proposed approaches, and limits tree detection at broad scales. Using data from the National Ecological Observatory Network we extend a recently developed semi-supervised deep learning algorithm to include data from a range of forest types, determine whether information from one forest can be used for tree detection in other forests, and explore the potential for building a universal tree detection algorithm. We find that the deep learning approach works well for overstory tree detection across forest conditions, outperforming conventional LIDAR-only methods in all forest types. Performance was best in open oak woodlands and worst in alpine forests. When models were fit to one forest type and used to predict another, performance generally decreased, with better performance when forests were more similar in structure. However, when models were pretrained on data from other sites and then fine-tuned using a small amount of hand-labeled data from the evaluation site, they performed similarly to local site models. Most importantly, a universal model fit to data from all sites simultaneously performed as well or better than individual models trained for each local site. This result suggests that RGB tree detection models that can be applied to a wide array of forest types at broad scales should be possible.}, 23 | URL = {https://www.biorxiv.org/content/early/2019/10/02/790071}, 24 | eprint = {https://www.biorxiv.org/content/early/2019/10/02/790071.full.pdf}, 25 | journal = {bioRxiv} 26 | } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # deepforestr 2 | 3 | [![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html) 4 | [![R-CMD-check-release](https://github.com/weecology/deepforestr/actions/workflows/test-package.yml/badge.svg)](https://github.com/weecology/deepforestr/actions/workflows/test-package.yml) 5 | [![CRAN status](https://www.r-pkg.org/badges/version/deepforestr)](https://CRAN.R-project.org/package=deepforestr) 6 | 7 | R interface for [DeepForest](github.com/weecology/DeepForest) Python package, a deep learning package detecting individual organisms in airborne RGB images. 8 | 9 | ## Installation 10 | 11 | `deepforestr` is an R wrapper for the Python package, [DeepForest](https://deepforest.readthedocs.io/en/latest/). 12 | This means that *Python* and the `DeepForest` Python package need to be installed first. 13 | 14 | ### Basic Installation 15 | 16 | If you just want to use DeepForest from within R run the following commands in R. 17 | This will create a local Python installation that will only be used by R and install the needed Python package for you. 18 | If installing on Windows you need to [install RTools](https://cran.r-project.org/bin/windows/Rtools/) before installing the R package. 19 | 20 | ```R 21 | devtools::install_github('weecology/deepforestr') # Install the R package from GitHub 22 | deepforestr::install_deepforest() # Install Python & DeepForest; Takes ~3 minutes 23 | ``` 24 | 25 | **After running these commands restart R.** 26 | 27 | ### Advanced Installation for Python Users 28 | 29 | If you are using Python for other tasks you can use `deepforestr` with your existing Python installation 30 | (though the [basic installation](#basic-installation) above will still work by creating a separate miniconda install and Python environment). 31 | 32 | #### Install the `DeepForest` Python package 33 | 34 | Install the `DeepForest` Python package into your prefered Python environment 35 | using `pip`: 36 | 37 | ```bash 38 | pip install DeepForest 39 | ``` 40 | 41 | #### Select the Python environment to use in R 42 | 43 | `deepforestr` will try to find Python environments with `DeepForest` 44 | (see the `reticulate` documentation on [order of discovery](https://rstudio.github.io/reticulate/articles/versions.html#order-of-discovery-1) for more details) installed. 45 | Alternatively you can select a Python environment to use when working with `deepforestr` (and other packages using `reticulate`). 46 | 47 | The most robust way to do this is to set the `RETICULATE_PYTHON` environment 48 | variable to point to the preferred Python executable: 49 | 50 | ```R 51 | Sys.setenv(RETICULATE_PYTHON = "/path/to/python") 52 | ``` 53 | 54 | This command can be run interactively or placed in `.Renviron` in your home directory. 55 | 56 | Alternatively you can select the Python environment through the `reticulate` package for either `conda`: 57 | 58 | ```R 59 | library(reticulate) 60 | conda_create("name_of_environment") 61 | 62 | # install Python packages e.g SciPy, deepforest 63 | conda_install("name_of_environment", "scipy", "deepforest") 64 | use_condaenv('name_of_environment') 65 | ``` 66 | 67 | or `virtualenv`: 68 | 69 | ```R 70 | library(reticulate) 71 | # create a new environment 72 | virtualenv_create("name_of_environment") 73 | 74 | # install Python packages 75 | virtualenv_install("name_of_environment", "scipy", "deepforest") 76 | use_virtualenv("name_of_environment") 77 | ``` 78 | 79 | You can check to see which Python environment is being used with: 80 | 81 | ```R 82 | py_config() 83 | ``` 84 | 85 | #### Install the `deepforestr` R package 86 | 87 | ```R 88 | remotes::install_github("weecology/deepforestr") # development version from GitHub 89 | ``` 90 | 91 | ## Getting Started 92 | 93 | ### Load the current release model 94 | 95 | ```R 96 | library(deepforestr) 97 | 98 | model = df_model() 99 | model$use_release() 100 | ``` 101 | 102 | ### Predict a single image 103 | 104 | #### Return the bounding boxes in a data frame 105 | 106 | ```R 107 | image_path = get_data("OSBS_029.png") # Gets a path to an example image 108 | bounding_boxes = model$predict_image(path=image_path, return_plot=FALSE) 109 | head(bounding_boxes) 110 | ``` 111 | 112 | #### Return an image for visualization 113 | 114 | ```R 115 | image_path = get_data("OSBS_029.png") # Gets a path to an example image 116 | predicted_image = model$predict_image(path=image_path, return_plot=TRUE) 117 | plot(raster::as.raster(predicted_image[,,3:1]/255)) 118 | ``` 119 | 120 | ### Predict a tile 121 | 122 | #### Return the bounding boxes in a data frame 123 | 124 | ```R 125 | raster_path = get_data("OSBS_029.tif") # Gets a path to an example raster tile 126 | bounding_boxes = model$predict_tile(raster_path, return_plot=FALSE) 127 | head(bounding_boxes) 128 | ``` 129 | 130 | #### Return an image for visualization 131 | 132 | ```R 133 | raster_path = get_data("OSBS_029.tif") # Gets a path to an example raster tile 134 | predicted_raster = model$predict_tile(raster_path, return_plot=TRUE, patch_size=300L, patch_overlap=0.25) 135 | plot(raster::as.raster(predicted_raster[,,3:1]/255)) 136 | ``` 137 | 138 | Note at `patch_size` is an integer value in Python and therefore needs to have the `L` at the end of the number in R to make it an integer. 139 | 140 | ### Predict a set of annotations 141 | 142 | ```R 143 | csv_file = get_data("testfile_deepforest.csv") 144 | root_dir = get_data(".") 145 | boxes = model$predict_file(csv_file=csv_file, root_dir = root_dir, savedir=".") 146 | ``` 147 | 148 | ### Training 149 | 150 | #### Set the training configuration 151 | 152 | ```R 153 | annotations_file = get_data("testfile_deepforest.csv") 154 | 155 | model$config$epochs = 1 156 | model$config["save-snapshot"] = FALSE 157 | model$config$train$csv_file = annotations_file 158 | model$config$train$root_dir = get_data(".") 159 | ``` 160 | 161 | Optionally turn on `fast_dev_run` for testing and debugging: 162 | 163 | ```R 164 | model$config$train$fast_dev_run = TRUE 165 | ``` 166 | 167 | #### Train the model 168 | 169 | ```R 170 | model$create_trainer() 171 | model$trainer$fit(model) 172 | ``` 173 | 174 | ### Evaluation 175 | 176 | ```R 177 | csv_file = get_data("OSBS_029.csv") 178 | root_dir = get_data(".") 179 | results = model$evaluate(csv_file, root_dir, iou_threshold = 0.4) 180 | ``` 181 | 182 | ### Saving & Loading Models 183 | 184 | #### Saving a model after training 185 | 186 | ```R 187 | model$trainer$save_checkpoint("checkpoint.pl") 188 | ``` 189 | 190 | #### Loading a saved model 191 | 192 | ```R 193 | new_model = df_model() 194 | after = new_model$load_from_checkpoint("checkpoint.pl") 195 | pred_after_reload = after$predict_image(path = img_path) 196 | ``` 197 | 198 | *Note that when reloading models, you should carefully inspect the model parameters, such as the score_thresh and nms_thresh. 199 | These parameters are updated during model creation and the config file is not read when loading from checkpoint! 200 | It is best to be direct to specify after loading checkpoint.* --------------------------------------------------------------------------------