├── .gitignore ├── .Rbuildignore ├── .dockerignore ├── man ├── imgLAB.Rd ├── imgEBI.Rd ├── imgRAST.Rd ├── fieldInterpolate.Rd ├── fieldHeight.Rd ├── fieldArea.Rd ├── fieldMap.Rd ├── fieldInfo.Rd ├── fieldAUC.Rd ├── fieldCount.Rd ├── fieldIndex.Rd ├── fieldCrop.Rd ├── fieldPlot.Rd ├── fieldRotate.Rd ├── fieldPolygon.Rd ├── fieldMask.Rd ├── fieldObject.Rd └── fieldDraw.Rd ├── R ├── utils_RGB.R ├── fct_imgLAB.R ├── fct_fieldInterpolate.R ├── fct_fieldMap.R ├── fct_imgRAST.R ├── fct_fieldHeight.R ├── fct_fieldArea.R ├── fct_imgEBI.R ├── fct_fieldInfo.R ├── fct_fieldAUC.R ├── fct_fieldCrop.R ├── fct_fieldIndex.R ├── fct_fieldPlot.R ├── fct_fieldCount.R ├── fct_fieldMask.R ├── fct_fieldDraw.R ├── fct_fieldRotate.R ├── fct_fieldPolygon.R └── fct_fieldObject.R ├── FIELDimageR.Rproj ├── inst ├── extdata │ └── Indices.txt └── CITATION ├── Dockerfile ├── DESCRIPTION ├── NAMESPACE ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^Dockerfile$ 4 | ^\.dockerignore$ 5 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .RData 2 | .Rhistory 3 | .git 4 | .gitignore 5 | manifest.json 6 | rsconnect/ 7 | Rproj.user 8 | -------------------------------------------------------------------------------- /man/imgLAB.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_imgLAB.R 3 | \name{imgLAB} 4 | \alias{imgLAB} 5 | \title{Set min and max values and crs to terra object} 6 | \usage{ 7 | imgLAB(img) 8 | } 9 | \arguments{ 10 | \item{img}{image/object class \code{terra}} 11 | } 12 | \value{ 13 | \code{terra} object 14 | } 15 | \description{ 16 | Set minmax values and crs to terra object 17 | } 18 | \details{ 19 | imgLAB 20 | } 21 | -------------------------------------------------------------------------------- /man/imgEBI.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_imgEBI.R 3 | \name{imgEBI} 4 | \alias{imgEBI} 5 | \title{Converts a terra object to an EBImage object} 6 | \usage{ 7 | imgEBI(img) 8 | } 9 | \arguments{ 10 | \item{img}{image/object class \code{terra}} 11 | } 12 | \value{ 13 | \code{EBImage} object 14 | } 15 | \description{ 16 | Converts a terra object to an EBImage object 17 | } 18 | \details{ 19 | imgEBI 20 | } 21 | -------------------------------------------------------------------------------- /R/utils_RGB.R: -------------------------------------------------------------------------------- 1 | #' RGB.rescale 2 | #' 3 | #' @description A utils function 4 | #' 5 | #' @return The return value, if any, from executing the utility. 6 | #' 7 | #' @importFrom raster values 8 | #' 9 | #' @noRd 10 | RGB.rescale <- function(mosaic, num.band) { 11 | for (i in 1:num.band) { 12 | values_i <- values(mosaic[[i]]) 13 | values_i <- pmin(pmax(values_i, 0), 255) 14 | values(mosaic[[i]]) <- values_i 15 | } 16 | return(mosaic) 17 | } 18 | -------------------------------------------------------------------------------- /FIELDimageR.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | 17 | BuildType: Package 18 | PackageUseDevtools: Yes 19 | PackageInstallArgs: --no-multiarch --with-keep.source 20 | PackageRoxygenize: rd,collate,namespace,vignette 21 | -------------------------------------------------------------------------------- /man/imgRAST.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_imgRAST.R 3 | \name{imgRAST} 4 | \alias{imgRAST} 5 | \title{Converts an EBImage object to a terra object} 6 | \usage{ 7 | imgRAST(img, ref) 8 | } 9 | \arguments{ 10 | \item{img}{image/object class \code{EBImage}} 11 | 12 | \item{ref}{SpatRaster with the crs that rast_obj should be assigned} 13 | } 14 | \value{ 15 | \code{terra} object 16 | } 17 | \description{ 18 | Converts an EBImage object to a terra object 19 | } 20 | \details{ 21 | imgRAST 22 | } 23 | -------------------------------------------------------------------------------- /inst/extdata/Indices.txt: -------------------------------------------------------------------------------- 1 | index eq band 2 | BI sqrt((R^2+G^2+B^2)/3) C 3 | BIM sqrt((R*2+G*2+B*2)/3) C 4 | SCI (R-G)/(R+G) C 5 | GLI (2*G-R-B)/(2*G+R+B) C 6 | HI (2*R-G-B)/(G-B) C 7 | NGRDI (G-R)/(G+R) C 8 | SI (R-B)/(R+B) C 9 | VARI (G-R)/(G+R-B) C 10 | HUE atan(2*(B-G-R)/30.5*(G-R)) C 11 | BGI B/G C 12 | PSRI (R-G)/(RE) RedEdge 13 | NDVI (NIR1-R)/(NIR1+R) NIR 14 | GNDVI (NIR1-G)/(NIR1+G) NIR 15 | RVI NIR1/R NIR 16 | NDRE (NIR1-RE)/(NIR1+RE) NIR 17 | TVI (0.5*(120*(NIR1-G)-200*(R-G))) NIR 18 | CVI (NIR1*R)/(G^2) NIR 19 | CIG (NIR1/G)-1 NIR 20 | CIRE (NIR1/RE)-1 NIR 21 | DVI NIR1-RE NIR 22 | EVI 2.5*(NIR1-R)/(NIR1+6*R-7.5*B+1) NIR 23 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rocker/tidyverse:4.1.0 2 | LABEL maintainer="Kenyon Ng " 3 | 4 | # libfftw3-dev (EBImage), libgdal-dev (s2), libudunits2-dev (units), the rest (sf), libglpk40(igraph) 5 | 6 | RUN apt-get update && apt-get install -y --no-install-recommends \ 7 | libfftw3-dev \ 8 | libgdal-dev \ 9 | libgeos++-dev \ 10 | libproj-dev \ 11 | libsqlite3-dev \ 12 | libudunits2-dev \ 13 | libglpk40 14 | 15 | RUN Rscript -e 'BiocManager::install(version = "3.13", ask = FALSE)' 16 | RUN Rscript -e 'BiocManager::install("EBImage", version = "3.13")' 17 | 18 | RUN install2.r --error \ 19 | s2 \ 20 | sf \ 21 | units 22 | 23 | RUN installGithub.r OpenDroneMap/FIELDimageR 24 | -------------------------------------------------------------------------------- /man/fieldInterpolate.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_fieldInterpolate.R 3 | \name{fieldInterpolate} 4 | \alias{fieldInterpolate} 5 | \title{Creating an interpolated raster based on sampled poits.} 6 | \usage{ 7 | fieldInterpolate(mosaic, poit_shp) 8 | } 9 | \arguments{ 10 | \item{mosaic}{input raster layer containing the field orthomosaic with 1 layer.} 11 | 12 | \item{poit_shp}{Points shape file layer to be used as reference.} 13 | } 14 | \value{ 15 | A new \code{SpatRaster} object. 16 | } 17 | \description{ 18 | Making raster based on interpolated values from sampled poits (x and y, or longitude and latitude) as predictors (independent variables). 19 | } 20 | \details{ 21 | fieldInterpolate 22 | } 23 | -------------------------------------------------------------------------------- /man/fieldHeight.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_fieldHeight.R 3 | \name{fieldHeight} 4 | \alias{fieldHeight} 5 | \title{Mosaic height and volume based on the digital surface model (DSM)} 6 | \usage{ 7 | fieldHeight(dsm_before, dsm_after) 8 | } 9 | \arguments{ 10 | \item{dsm_before}{digital surface model (DSM) mosaic class SpatRaster to be used as base line (minumun height or soil level).} 11 | 12 | \item{dsm_after}{digital surface model (DSM) mosaic class SpatRaster to be used as aimed line (maximum height or plants in the field).} 13 | } 14 | \value{ 15 | list with \code{height} and {volume} as a new \code{SpatRaster} object. 16 | } 17 | \description{ 18 | It calculates pixel height and volume in the entire mosaic using the digital surface model (DSM). 19 | } 20 | \details{ 21 | fieldHeight 22 | } 23 | -------------------------------------------------------------------------------- /man/fieldArea.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_fieldArea.R 3 | \name{fieldArea} 4 | \alias{fieldArea} 5 | \title{Percentage of object area} 6 | \usage{ 7 | fieldArea(mosaic, fieldShape, field = NULL) 8 | } 9 | \arguments{ 10 | \item{mosaic}{object mask of class stack from the function \code{\link{fieldMask}}.} 11 | 12 | \item{fieldShape}{evaluate the object area percentage per plot using the fieldShape as reference.} 13 | 14 | \item{field}{character (field name) or numeric (length nrow) at fieldShape. Default: order by PlotID. Check \code{\link{terra::rasterize}}.} 15 | } 16 | \value{ 17 | \code{AreaPercentage} in the new \code{fieldShape} . 18 | } 19 | \description{ 20 | It calculates the percentage of object area in the entire mosaic or per plot using the fieldShape file. 21 | } 22 | \details{ 23 | fieldArea 24 | } 25 | -------------------------------------------------------------------------------- /man/fieldMap.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_fieldMap.R 3 | \name{fieldMap} 4 | \alias{fieldMap} 5 | \title{Organazing the field map in function of plot order.} 6 | \usage{ 7 | fieldMap(fieldPlot, fieldColumn, fieldRow, decreasing = FALSE) 8 | } 9 | \arguments{ 10 | \item{fieldPlot}{vector with plot ID} 11 | 12 | \item{fieldColumn}{vector with column ID} 13 | 14 | \item{fieldRow}{vector with row ID} 15 | 16 | \item{decreasing}{if FALSE the plots will be order left to right and right to left every other line following the breeding field design.} 17 | } 18 | \value{ 19 | \itemize{ 20 | \item The function returns a matrix with plots ID identified by rows and column 21 | } 22 | } 23 | \description{ 24 | Field map ID identification to align with plots shapefile built with fieldShape(). The plots are numbered from left to right and top to bottom. 25 | } 26 | \details{ 27 | fieldMap 28 | } 29 | -------------------------------------------------------------------------------- /R/fct_imgLAB.R: -------------------------------------------------------------------------------- 1 | #' imgLAB 2 | #' 3 | #' @title Set min and max values and crs to terra object 4 | #' 5 | #' @description Set minmax values and crs to terra object 6 | #' 7 | #' @param img image/object class \code{terra} 8 | #' 9 | #' @importFrom terra crs nlyr setMinMax hasMinMax plotRGB 10 | #' 11 | #' @return \code{terra} object 12 | #' 13 | #' @export 14 | imgLAB <- function(img){ 15 | if(class(img)%in%c("SpatRaster")){ 16 | if(hasMinMax(img[[1]])==FALSE){ 17 | img <- setMinMax(img) 18 | } 19 | if (crs(img)=='') { 20 | if(nlyr(img) == 3){ 21 | crs(img)<- 'epsg:3057' 22 | names(img)<-c('Red','Green','Blue') 23 | } 24 | else if(nlyr(img)==1){ 25 | crs(img)<- 'epsg:3057' 26 | names(img)<-c('band') 27 | } 28 | } 29 | return(img) 30 | return(plotRGB(img)) 31 | }else { 32 | stop("Input is not a SpatRaster object.") 33 | } 34 | } -------------------------------------------------------------------------------- /R/fct_fieldInterpolate.R: -------------------------------------------------------------------------------- 1 | #' fieldInterpolate 2 | #' 3 | #' @title Creating an interpolated raster based on sampled poits. 4 | #' 5 | #' @description Making raster based on interpolated values from sampled poits (x and y, or longitude and latitude) as predictors (independent variables). 6 | #' 7 | #' @param mosaic input raster layer containing the field orthomosaic with 1 layer. 8 | #' @param poit_shp Points shape file layer to be used as reference. 9 | #' 10 | #' @importFrom terra rast extract mask interpolate 11 | #' @importFrom fields Tps 12 | #' 13 | #' @return A new \code{SpatRaster} object. 14 | #' 15 | #' 16 | #' @export 17 | fieldInterpolate<-function(mosaic,poit_shp){ 18 | extra<-extract(mosaic,poit_shp, xy=TRUE) 19 | xy<-extra[,c(3,4)] 20 | v<-as.numeric(extra[,2]) 21 | tps <- Tps(xy, v) 22 | Out <- rast(mosaic) 23 | Out <- interpolate(Out, tps) 24 | Out <- mask(Out, mosaic) 25 | return(Out) 26 | } 27 | -------------------------------------------------------------------------------- /inst/CITATION: -------------------------------------------------------------------------------- 1 | note <- sprintf("R package version %s", meta$Version) 2 | 3 | citHeader("The following are references to the package FIELDimageR. You should also reference the individual methods used, as detailed in the reference section of the help files for each function.") 4 | 5 | bibentry(bibtype="article", 6 | title = "{FIELDimageR}: An {R} package to analyze orthomosaic images from agricultural field trials", 7 | author = c(person(c("Filipe", "I."), "Matias"),person(c("Maria", "V."), "Caraza-Harter"),person(c("Jeffrey", "B."), "Endelman")), 8 | journal = "The Plant Phenome J.", 9 | year = "2020", 10 | pages = "1--6", 11 | url = "https://doi.org/10.1002/ppj2.20005" 12 | ) 13 | 14 | bibentry(bibtype="Manual", 15 | title = "{FIELDimageR}: An {R} package to analyze orthomosaic images from agricultural field trials", 16 | author = c(person(c("Filipe", "I."), "Matias"),person(c("Maria", "V."), "Caraza-Harter"),person(c("Jeffrey", "B."), "Endelman")), 17 | year = "2020", 18 | url = "https://doi.org/10.1002/ppj2.20005", 19 | note = note 20 | ) 21 | 22 | citFooter('To get Bibtex entries use: x<-citation("FIELDimageR"); toBibtex(x)') 23 | -------------------------------------------------------------------------------- /R/fct_fieldMap.R: -------------------------------------------------------------------------------- 1 | #' fieldMap 2 | #' 3 | #' @title Organazing the field map in function of plot order. 4 | #' 5 | #' @description Field map ID identification to align with plots shapefile built with fieldShape(). The plots are numbered from left to right and top to bottom. 6 | #' 7 | #' @param fieldPlot vector with plot ID 8 | #' @param fieldColumn vector with column ID 9 | #' @param fieldRow vector with row ID 10 | #' @param decreasing if FALSE the plots will be order left to right and right to left every other line following the breeding field design. 11 | #' 12 | #'@return 13 | #' \itemize{ 14 | #' \item The function returns a matrix with plots ID identified by rows and column 15 | #' } 16 | #' 17 | #' 18 | #' @export 19 | fieldMap <- function(fieldPlot, fieldColumn, fieldRow, decreasing = FALSE){ 20 | if(length(fieldPlot)!=length(fieldRow)|length(fieldPlot)!=length(fieldColumn)|length(fieldColumn)!=length(fieldRow)){ 21 | stop("Plot, Column and Row vectors must have the same length.") 22 | } 23 | map <- NULL 24 | for(i in 1:length(fieldRow)){ 25 | r1<-as.character(fieldPlot[fieldRow==i][order(as.numeric(fieldColumn[fieldRow==i]),decreasing = decreasing)]) 26 | map<-rbind(map,r1) 27 | } 28 | colnames(map) <- NULL 29 | rownames(map) <- NULL 30 | return(as.matrix(map)) 31 | } 32 | -------------------------------------------------------------------------------- /R/fct_imgRAST.R: -------------------------------------------------------------------------------- 1 | #' imgRAST 2 | #' 3 | #' @title Converts an EBImage object to a terra object 4 | #' 5 | #' @description Converts an EBImage object to a terra object 6 | #' 7 | #' @param img image/object class \code{EBImage} 8 | #' 9 | #' @param ref SpatRaster with the crs that rast_obj should be assigned 10 | #' 11 | #' @importFrom terra rast 12 | #' 13 | #' @return \code{terra} object 14 | #' 15 | #' @export 16 | imgRAST<-function(img,ref){ 17 | if (!requireNamespace("EBImage", quietly = TRUE)) { 18 | stop("EBImage package is required for this function.") 19 | } 20 | if(class(img)=='Image') { 21 | if(!is.na(dim(img)[3])){ 22 | b1<-t(imageData(img)[,,1]*255) 23 | b2<-t(imageData(img)[,,2]*255) 24 | b3<-t(imageData(img)[,,3]*255) 25 | rgb<-array(c(b1,b2,b3),dim = c(dim(b1)[1],dim(b1)[2],dim(img)[3])) 26 | } else if (is.na(dim(img)[3])){ 27 | b<-t(imageData(img)*255) 28 | rgb<-as.array(b) 29 | } else { 30 | stop("Invalid number of channels in the image.") 31 | } 32 | rast_obj <- rast(rgb) 33 | crs(rast_obj)<-crs(ref) 34 | ext(rast_obj)<-ext(ref) 35 | rast_obj[rast_obj==0] <- NA 36 | return(rast_obj) 37 | } else { 38 | stop("Input is not an EBImage object.") 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /man/fieldInfo.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_fieldInfo.R 3 | \name{fieldInfo} 4 | \alias{fieldInfo} 5 | \title{Extract information from image using the fieldShape file as reference} 6 | \usage{ 7 | fieldInfo( 8 | mosaic, 9 | fieldShape, 10 | fun = "mean", 11 | plot = FALSE, 12 | buffer = NULL, 13 | n.core = NULL, 14 | projection = TRUE 15 | ) 16 | } 17 | \arguments{ 18 | \item{mosaic}{object of class stack.} 19 | 20 | \item{fieldShape}{plot shape file, please use first the function \code{\link{fieldShape}}.} 21 | 22 | \item{fun}{to summarize the values (e.g. mean).} 23 | 24 | \item{plot}{if is TRUE the original and crop image will be plotted.} 25 | 26 | \item{buffer}{negative values should be used to remove boundaries from neighbor plot 27 | (normally the unit is meters, please use values as 0.1 = 10 cm).} 28 | 29 | \item{n.core}{number of cores to use for multicore processing (Parallel).} 30 | 31 | \item{projection}{if is FALSE projection will be ignored.} 32 | } 33 | \value{ 34 | A list with a data frame with values by plot and experimental field image with format stack. 35 | } 36 | \description{ 37 | Function that use \code{raster::extract()} to extract information from the original 38 | image using fieldShape file as reference. 39 | } 40 | \details{ 41 | fieldInfo 42 | } 43 | -------------------------------------------------------------------------------- /man/fieldAUC.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_fieldAUC.R 3 | \name{fieldAUC} 4 | \alias{fieldAUC} 5 | \title{Area Under the Curve} 6 | \usage{ 7 | fieldAUC( 8 | data, 9 | trait, 10 | keep.columns = c("NAME", "ROW", "RANGE"), 11 | method = "trapezoid", 12 | x.start = 0, 13 | y.start = 0, 14 | frame = "long" 15 | ) 16 | } 17 | \arguments{ 18 | \item{data}{data table from \code{\link{fieldInfo}}.} 19 | 20 | \item{trait}{one or more traits to be evaluated.} 21 | 22 | \item{keep.columns}{columns names to be maintained in the output dataset.} 23 | 24 | \item{method}{the type of interpolation. Can be "trapezoid" (default), "step", "linear" or "spline". More information on ??DescTools::AUC.} 25 | 26 | \item{x.start}{value of x to start the AUC (default is 0 days after planting).} 27 | 28 | \item{y.start}{value of y to start the AUC (default is 0).} 29 | 30 | \item{frame}{format of output data. "long" is used for AUC values on the 1st column and traits ID on the2nd column. While "wide" is for objects with dimension n \times m where traits must be in columns and plots/sample in rows.} 31 | } 32 | \value{ 33 | A list with a data frame with values by plot and experimental field image with format stack. 34 | } 35 | \description{ 36 | Calculate the area under the curve given by vectors of xy-coordinates. 37 | } 38 | \details{ 39 | fieldAUC 40 | } 41 | -------------------------------------------------------------------------------- /R/fct_fieldHeight.R: -------------------------------------------------------------------------------- 1 | #' fieldHeight 2 | #' 3 | #' @title Mosaic height and volume based on the digital surface model (DSM) 4 | #' 5 | #' @description It calculates pixel height and volume in the entire mosaic using the digital surface model (DSM). 6 | #' 7 | #' @param dsm_before digital surface model (DSM) mosaic class SpatRaster to be used as base line (minumun height or soil level). 8 | #' @param dsm_after digital surface model (DSM) mosaic class SpatRaster to be used as aimed line (maximum height or plants in the field). 9 | #' 10 | #' @importFrom terra resample cellSize 11 | #' 12 | #' 13 | #' @return list with \code{height} and {volume} as a new \code{SpatRaster} object. 14 | #' 15 | #' 16 | #' @export 17 | fieldHeight<-function(dsm_before,dsm_after){ 18 | if (!inherits(dsm_before,"SpatRaster") || !nlyr(dsm_before) == 1 || terra::is.bool(dsm_before) || is.list(dsm_before)) { 19 | stop("Error: Invalid 'dsm_before' raster object.") 20 | } 21 | if (!inherits(dsm_after,"SpatRaster") || !nlyr(dsm_after) == 1 || terra::is.bool(dsm_after) || is.list(dsm_after)) { 22 | stop("Error: Invalid 'dsm_after' raster object.") 23 | } 24 | dsm.c <- resample(dsm_before, dsm_after) 25 | height <- dsm_after-dsm.c 26 | names(height)<-"height" 27 | volume<-terra::cellSize(height)*height 28 | names(volume)<-"volume" 29 | mosaic<-append(height,volume) 30 | return(mosaic) 31 | } 32 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: FIELDimageR 2 | Title: A Tool to Analyze Orthomosaic Images From Agricultural Field Trials 3 | Version: 0.6.2 4 | Authors@R: 5 | c(person(given = "Filipe", 6 | family = "Matias", 7 | role = c("cre", "aut"), 8 | email = "filipematias23@gmail.com")) 9 | Description: A compilation of functions to analyze orthomosaic images 10 | from research fields. To prepare the image it first allows to crop the image, remove soil 11 | and weeds and rotate the image. The package also builds a plot shapefile in order to extract 12 | information for each plot to evaluate different wavelengths, vegetation indices, stand count, 13 | canopy percentage, and plant height. 14 | License: GPL-3 + file LICENSE 15 | biocViews: 16 | Imports: 17 | EBImage, 18 | attempt, 19 | config (>= 0.3.1), 20 | DT, 21 | fftwtools, 22 | git2r, 23 | glue, 24 | DescTools, 25 | methods, 26 | raster, 27 | terra, 28 | scales, 29 | sp, 30 | xml2, 31 | foreach, 32 | doParallel, 33 | plyr, 34 | processx, 35 | grDevices 36 | Encoding: UTF-8 37 | LazyData: true 38 | RoxygenNote: 7.2.3 39 | Author: Filipe Matias [cre, aut] 40 | Maintainer: Filipe Matias 41 | Suggests: 42 | spelling, 43 | testthat (>= 3.0.0) 44 | Config/testthat/edition: 3 45 | Language: en-US 46 | -------------------------------------------------------------------------------- /man/fieldCount.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_fieldCount.R 3 | \name{fieldCount} 4 | \alias{fieldCount} 5 | \title{Calculating number of objects per plot} 6 | \usage{ 7 | fieldCount( 8 | mosaic, 9 | fieldShape = NULL, 10 | watershed = TRUE, 11 | plot = FALSE, 12 | pch = "+", 13 | col = "red" 14 | ) 15 | } 16 | \arguments{ 17 | \item{mosaic}{object mask of class stack from the function \code{\link{fieldMask}}.} 18 | 19 | \item{fieldShape}{plot shape file.} 20 | 21 | \item{watershed}{TRUE (defaut) or FALSE. Use or not watershed to identify objects. Otherwise, all touching objects will be considered as one polygon.} 22 | 23 | \item{plot}{if it is TRUE the original and segmented image will be plotted with identified objects.} 24 | 25 | \item{pch}{point symbol, please check \code{help("points")}.} 26 | 27 | \item{col}{color code or name, please check \code{help("points")}.} 28 | } 29 | \value{ 30 | A list with two elements when fieldShape is provided: 31 | \itemize{ 32 | \item \code{fieldShape$plot_level} is the new shapeFile with objects in the plot area, perimeter, count, and mean_width. 33 | \item \code{fieldShape$object_level} is the new shapeFile of single objects area, perimeter, width, x and y position. 34 | } 35 | } 36 | \description{ 37 | The mask from function \code{\link{fieldMask}} is used to identify the number of objects per plot.. 38 | } 39 | \details{ 40 | fieldCount 41 | } 42 | -------------------------------------------------------------------------------- /man/fieldIndex.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_fieldIndex.R 3 | \name{fieldIndex} 4 | \alias{fieldIndex} 5 | \title{Building vegetation indices using Red, Green, Blue, Red Edge, and NIR band} 6 | \usage{ 7 | fieldIndex( 8 | mosaic, 9 | Red = 1, 10 | Green = 2, 11 | Blue = 3, 12 | RedEdge = NULL, 13 | NIR = NULL, 14 | index = "HUE", 15 | myIndex = NULL, 16 | plot = TRUE 17 | ) 18 | } 19 | \arguments{ 20 | \item{mosaic}{object of class stack with at least 3 bands.} 21 | 22 | \item{Red}{vector with the vegetation indices to be calculated.} 23 | 24 | \item{Green}{user can calculate a diferent index using the bands names, e.g. "(Green+Blue)/Red-NIR/RedEdge"} 25 | 26 | \item{Blue}{if is TRUE the original and crop image will be plotted.} 27 | 28 | \item{RedEdge}{vector with the vegetation indices to be calculated.} 29 | 30 | \item{NIR}{if is TRUE the original and crop image will be plotted.} 31 | 32 | \item{index}{vector with the vegetation indices to be calculated.} 33 | 34 | \item{myIndex}{user can calculate a diferent index using the bands names, e.g. "(Green+Blue)/Red-NIR/RedEdge"} 35 | 36 | \item{plot}{if is TRUE the original and crop image will be plotted.} 37 | } 38 | \value{ 39 | A Image format stack. 40 | } 41 | \description{ 42 | Different vegetation indices can be calculated using at least 3 bands. 43 | For the list of indices please visit the FIELDimageR manual at link 44 | } 45 | \details{ 46 | fieldIndex 47 | } 48 | -------------------------------------------------------------------------------- /man/fieldCrop.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_fieldCrop.R 3 | \name{fieldCrop} 4 | \alias{fieldCrop} 5 | \title{Selecting experimental field from original image} 6 | \usage{ 7 | fieldCrop( 8 | mosaic, 9 | fieldShape = NULL, 10 | nPoint = 4, 11 | plot = TRUE, 12 | remove = FALSE, 13 | type = "l", 14 | lty = 2, 15 | lwd = 3, 16 | fast.plot = FALSE 17 | ) 18 | } 19 | \arguments{ 20 | \item{mosaic}{object mask of class stack from the function \code{\link{fieldMask}}.} 21 | 22 | \item{fieldShape}{crop the image using the fieldShape as reference. If fieldShape=NULL, four points should be selected 23 | directly on the original image to determine the experimental field.} 24 | 25 | \item{nPoint}{number of points necessary to select field boundaries or area to remove (4 >= nPoint <= 50).} 26 | 27 | \item{plot}{if \code{TRUE} (by default) plots the original and cropped image.} 28 | 29 | \item{remove}{if TRUE the selected area will be removed from the image.} 30 | 31 | \item{type}{character indicating the type of plotting, please check help("lines").} 32 | 33 | \item{lty}{line types, please check help("lines").} 34 | 35 | \item{lwd}{line width, please check help("lines").} 36 | 37 | \item{fast.plot}{if TRUE only the grey scale image will be plotted as reference (faster approach). 38 | if TRUE only the grey scale image will be plotted as reference (faster approach).} 39 | } 40 | \value{ 41 | A image format stack. 42 | } 43 | \description{ 44 | It calculates the percentage of object area in the entire mosaic or per plot using the fieldShape file. 45 | } 46 | \details{ 47 | fieldCrop 48 | } 49 | -------------------------------------------------------------------------------- /man/fieldPlot.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_fieldPlot.R 3 | \name{fieldPlot} 4 | \alias{fieldPlot} 5 | \title{Plot of fieldShape file filled with trait value for each plot} 6 | \usage{ 7 | fieldPlot( 8 | fieldShape, 9 | fieldAttribute, 10 | mosaic = NULL, 11 | color = c("white", "black"), 12 | min.lim = NULL, 13 | max.lim = NULL, 14 | alpha = 0.5, 15 | legend.position = "right", 16 | na.color = "gray", 17 | classes = 5, 18 | round = 3, 19 | horiz = FALSE 20 | ) 21 | } 22 | \arguments{ 23 | \item{fieldShape}{plot shape file, please use first the function getInfo().} 24 | 25 | \item{fieldAttribute}{attribute or trait which the values will fill the plots, please use first the function getInfo().} 26 | 27 | \item{mosaic}{object of class stack that is not necessary, but if provided will be plotted with the fieldShape file.} 28 | 29 | \item{color}{colors to interpolate, must be a valid argument.} 30 | 31 | \item{min.lim}{lowest limit of the color range. If is NULL the lowest value of the data will be used.} 32 | 33 | \item{max.lim}{upper limit of the color range. If is NULL the highest value of the data will be used.} 34 | 35 | \item{alpha}{transparency with values between 0 and 1.} 36 | 37 | \item{legend.position}{legend position.} 38 | 39 | \item{na.color}{color of missing values "NA".} 40 | 41 | \item{classes}{number of classes at the legend.} 42 | 43 | \item{round}{number of decimal digits at the legend.} 44 | 45 | \item{horiz}{if TRUE will plot a horizontal legend.} 46 | } 47 | \value{ 48 | A list with two element 49 | \itemize{ 50 | \item The function returns a image with the \code{fieldShape} file filled with trait value for each plot. 51 | } 52 | } 53 | \description{ 54 | Graphic visualization of trait values for each plot using the \code{\link{fieldShape}} file and original image. 55 | } 56 | \details{ 57 | fieldPlot 58 | } 59 | -------------------------------------------------------------------------------- /R/fct_fieldArea.R: -------------------------------------------------------------------------------- 1 | #' fieldArea 2 | #' 3 | #' @title Percentage of object area 4 | #' 5 | #' @description It calculates the percentage of object area in the entire mosaic or per plot using the fieldShape file. 6 | #' 7 | #' @param mosaic object mask of class stack from the function \code{\link{fieldMask}}. 8 | #' @param fieldShape evaluate the object area percentage per plot using the fieldShape as reference. 9 | #' @param field character (field name) or numeric (length nrow) at fieldShape. Default: order by PlotID. Check \code{\link{terra::rasterize}}. 10 | #' 11 | #' @importFrom terra rasterize vect 12 | #' @importFrom exactextractr exact_extract 13 | #' 14 | #' @return \code{AreaPercentage} in the new \code{fieldShape} . 15 | #' 16 | #' 17 | #' @export 18 | fieldArea <- function(mosaic, fieldShape, field = NULL) { 19 | if (is.null(field)) { 20 | terra_vect <- vect(fieldShape) 21 | terra_rast <- rasterize(terra_vect, mosaic, field = "PlotID") 22 | total_pixelcount <- exactextractr::exact_extract(terra_rast, st_as_sf(as.polygons(terra_rast)), fun = "count",force_df = TRUE) 23 | area_pixel <- exactextractr::exact_extract(mosaic[[1]], st_as_sf(as.polygons(terra_rast)), fun = "count",force_df = TRUE) 24 | } else { 25 | terra_vect <- vect(fieldShape) 26 | terra_rast <- rasterize(terra_vect, mosaic, field = field) 27 | total_pixelcount <- exactextractr::exact_extract(terra_rast, st_as_sf(as.polygons(terra_rast)), fun = "count",force_df = TRUE) 28 | area_pixel <- exactextractr::exact_extract(mosaic[[1]], st_as_sf(as.polygons(terra_rast)), fun = "count",force_df = TRUE) 29 | } 30 | area_percentage <- round(area_pixel/ total_pixelcount * 100,3) 31 | names(area_percentage)<-"AreaPercentage" 32 | names(area_pixel)<-"PixelCount" 33 | area_percentage<- cbind(st_as_sf(as.polygons(terra_rast)), AreaPixel=area_pixel, area_percentage) 34 | return(area_percentage) 35 | } 36 | -------------------------------------------------------------------------------- /R/fct_imgEBI.R: -------------------------------------------------------------------------------- 1 | #' imgEBI 2 | #' 3 | #' @title Converts a terra object to an EBImage object 4 | #' 5 | #' @description Converts a terra object to an EBImage object 6 | #' 7 | #' @param img image/object class \code{terra} 8 | #' 9 | #' @importFrom EBImage Image 10 | #' 11 | #' @return \code{EBImage} object 12 | #' 13 | #' @export 14 | imgEBI <- function(img) { 15 | if (class(img) %in% c("SpatRaster")) { 16 | nBand <- nlyr(img) 17 | 18 | if (nBand == 1) { 19 | if (length(dim(img)) >= 2) { 20 | if (terra::is.bool(img) || is.factor(img)||isClass(img)) { # Check if img is boolean or factor 21 | img[is.na(img)] <- 0 22 | x <- terra::as.array(img) 23 | x <- t(x[, , 1] * 255) # Multiply by 255 24 | x <- EBImage::Image(x) 25 | } 26 | else if (!(minmax(img)[1] == 1)) { 27 | x <- terra::as.array(img) 28 | x <- t(x[, , 1] / 255) 29 | x <- EBImage::Image(x) 30 | } else if (minmax(img)[1] == 1) { 31 | x <- terra::as.array(img) 32 | x <- t(x[, , 1]) 33 | x <- EBImage::Image(x) 34 | } 35 | return(x) 36 | } else { 37 | stop("Input 'img' must have at least two dimensions.") 38 | } 39 | } else if (nBand == 3) { 40 | # Handle the 3-band case 41 | if (length(dim(img)) >= 3) { 42 | band1 <- terra::as.array(t(img[[1]] / 255)) 43 | band2 <- terra::as.array(t(img[[2]] / 255)) 44 | band3 <- terra::as.array(t(img[[3]] / 255)) 45 | rgb <- array(c(band1, band2, band3), dim = c(dim(band1)[1], dim(band1)[2], nlyr(img))) 46 | rgb <- EBImage::Image(rgb) 47 | colorMode(rgb) = Color 48 | return(rgb) 49 | } else { 50 | stop("Input 'img' must have at least three dimensions for a 3-band image.") 51 | } 52 | } else { 53 | stop("Unsupported number of bands: ", nBand) 54 | } 55 | } else { 56 | stop("Input 'img' must be a SpatRaster.") 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /man/fieldRotate.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_fieldRotate.R 3 | \name{fieldRotate} 4 | \alias{fieldRotate} 5 | \title{Rotating the image to biuld the \code{\link{fieldShape}} file} 6 | \usage{ 7 | fieldRotate( 8 | mosaic, 9 | theta = NULL, 10 | clockwise = TRUE, 11 | h = FALSE, 12 | n.core = NULL, 13 | extentGIS = FALSE, 14 | DSMmosaic = NULL, 15 | plot = TRUE, 16 | type = "l", 17 | lty = 2, 18 | lwd = 3, 19 | fast.plot = FALSE 20 | ) 21 | } 22 | \arguments{ 23 | \item{mosaic}{object of class stack.} 24 | 25 | \item{theta}{angle of rotation, if negativo the rotation will be for the right. 26 | If not provided the user should select two points from left to right to determine the angle of rotation.} 27 | 28 | \item{clockwise}{if it is TRUE, clockwise rotation.} 29 | 30 | \item{h}{if it is TRUE, the drawn line will be at horizontal, if FALSE (90-theta).} 31 | 32 | \item{n.core}{number of cores to use for multicore processing (Parallel).} 33 | 34 | \item{extentGIS}{if TRUE the rotated image will have an adjusted extent 35 | based on the original image (It is an important step to fit the fieldShape on the original image).} 36 | 37 | \item{DSMmosaic}{DSM should be used if the file of height is provided.} 38 | 39 | \item{plot}{if it is TRUE the original and rotated image will be plotted.} 40 | 41 | \item{type}{character indicating the type of plotting, please check help("lines").} 42 | 43 | \item{lty}{line types, please check help("lines").} 44 | 45 | \item{lwd}{line width, please check help("lines").} 46 | 47 | \item{fast.plot}{if TRUE only the grey scale image will be plotted as reference (faster approach).} 48 | } 49 | \value{ 50 | A list with two element 51 | \itemize{ 52 | \item \code{mosaic} rotated image format stack with the base of experimental field parallel to axis X. 53 | } 54 | } 55 | \description{ 56 | The image should be rotated to use the function \code{\link{fieldShape}}. 57 | The base of experimental field should be parallel to axis X. 58 | } 59 | \details{ 60 | fieldRotate 61 | } 62 | -------------------------------------------------------------------------------- /man/fieldPolygon.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_fieldPolygon.R 3 | \name{fieldPolygon} 4 | \alias{fieldPolygon} 5 | \title{Building \code{shapefile} with polygons} 6 | \usage{ 7 | fieldPolygon( 8 | mosaic, 9 | nPolygon = 1, 10 | nPoint = 4, 11 | polygonID = NULL, 12 | polygonData = NULL, 13 | ID = NULL, 14 | cropPolygon = FALSE, 15 | remove = FALSE, 16 | plot = TRUE, 17 | fast.plot = FALSE, 18 | extent = FALSE 19 | ) 20 | } 21 | \arguments{ 22 | \item{mosaic}{object of class stack.} 23 | 24 | \item{nPolygon}{number of polygons.} 25 | 26 | \item{nPoint}{number of points necessary to select field boundaries or area to remove (4 >= nPoint <= 50).} 27 | 28 | \item{polygonID}{a vector with polygon names with same order of drawing. If is NULL the ID will be the sequence of drawing.} 29 | 30 | \item{polygonData}{data frame with polygon ID and all attributes of each polygon (Traits as columns and polygon as rows).} 31 | 32 | \item{ID}{the column in polygonData with polygons names (ID) which the data will be combined with fieldShape.} 33 | 34 | \item{cropPolygon}{if TRUE the mosaic will be crooped using polygons shape.} 35 | 36 | \item{remove}{if TRUE the selected area will be removed from the image.} 37 | 38 | \item{plot}{if is TRUE the crop image and fieldShape will be plotted.} 39 | 40 | \item{fast.plot}{if TRUE only the grey scale image will be plotted as reference (faster approach).} 41 | 42 | \item{extent}{if is TRUE the entire image area will be the fieldShape (one unique plot).} 43 | } 44 | \value{ 45 | A list with two element 46 | \itemize{ 47 | \item The function returns the \code{fieldShape} format \code{SpatialPolygonsDataFrame} with plots 48 | numbered by the sequence of drawings and a new reduced image with format \code{stack}. 49 | The \code{polygonID} parameter can be used to identify each polygon. 50 | } 51 | } 52 | \description{ 53 | The user should select points to make polygons in the image. Shapefile with polygons will be automatically built. 54 | Attention: \code{fieldRotate()} is not necessary. 55 | } 56 | \details{ 57 | fieldPolygon 58 | } 59 | -------------------------------------------------------------------------------- /man/fieldMask.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_fieldMask.R 3 | \name{fieldMask} 4 | \alias{fieldMask} 5 | \title{Removing background (e.g., soil) using vegetation index.} 6 | \usage{ 7 | fieldMask( 8 | mosaic, 9 | Red = 1, 10 | Green = 2, 11 | Blue = 3, 12 | RedEdge = NULL, 13 | NIR = NULL, 14 | mask = NULL, 15 | index = "HUE", 16 | myIndex = NULL, 17 | cropValue = 0, 18 | cropAbove = TRUE, 19 | projection = TRUE, 20 | DSMmosaic = NULL, 21 | DSMcropAbove = TRUE, 22 | DSMcropValue = 0, 23 | plot = TRUE 24 | ) 25 | } 26 | \arguments{ 27 | \item{mosaic}{object of class stack with at least 3 bands.} 28 | 29 | \item{Red, Green, Blue, RedEdge, NIR}{respective position of the band at the original image file.} 30 | 31 | \item{mask}{if avaliable the soil will be removed following this mask and not the vegetation index, cropValue and cropAbove must be used.} 32 | 33 | \item{index}{vector with the vegetation indices to be calculated. For the list of indices please visit the FIELDimageR manual at link:} 34 | 35 | \item{myIndex}{user can calculate a diferent index using the bands names, e.g. "(Green+Blue)/Red-NIR/RedEdge"} 36 | 37 | \item{cropValue}{referent value of soil in the image.} 38 | 39 | \item{cropAbove}{if TRUE all values above the cropValue will be accounted to make the mask.} 40 | 41 | \item{projection}{if TRUE the projection will not be accounted to the mask.} 42 | 43 | \item{DSMmosaic, DSMcropAbove, DSMcropValue}{DSM should be used if the file of height is provided.} 44 | 45 | \item{plot}{if is TRUE the original and crop image will be plotted.} 46 | } 47 | \value{ 48 | The return value, if any, from executing the function. 49 | 50 | A list with elements: 51 | \itemize{ 52 | \item The function returns a image format stack with the original bands (layers) without the background and mask with logical values of 0 and 1 for vegetation or soil. 53 | } 54 | } 55 | \description{ 56 | A fct function 57 | 58 | Different vegetation indices can be used to remove images background. For the list of indices please visit the FIELDimageR manual at link: https://github.com/OpenDroneMap/FIELDimageR#P6 59 | } 60 | \details{ 61 | fieldMask 62 | } 63 | -------------------------------------------------------------------------------- /man/fieldObject.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_fieldObject.R 3 | \name{fieldObject} 4 | \alias{fieldObject} 5 | \title{Evaluate object area, "x" distance, "y" distance, number, extent, etc.} 6 | \usage{ 7 | fieldObject( 8 | mosaic, 9 | fieldShape = NULL, 10 | minArea = 0.01, 11 | areaValue = 0, 12 | watershed = FALSE, 13 | dissolve = TRUE, 14 | n.rem = 1, 15 | na.rm = FALSE, 16 | plot = TRUE 17 | ) 18 | } 19 | \arguments{ 20 | \item{mosaic}{object mask of class stack from the function \code{\link{fieldMask}}.} 21 | 22 | \item{fieldShape}{evaluate the object per plot using the fieldShape as reference. 23 | If fieldShape=NULL, the object will be evaluated directly for the entire original image.} 24 | 25 | \item{minArea}{used to set the minimum size percentage of plant canopy (to remove weeds and more).} 26 | 27 | \item{areaValue}{referent value of object area in the image.} 28 | 29 | \item{watershed}{if TRUE the "watershed" algorithm will be used to differentiate objects that are touching each other.} 30 | 31 | \item{dissolve}{if TRUE, polygons with the same attribute value will be dissolved into multi-polygon regions. 32 | This option requires the rgeos package.} 33 | 34 | \item{n.rem}{number of objects that should be removed by decreasing size (n.rem=1 is the background).} 35 | 36 | \item{na.rm}{logical. Should missing values (including NaN) be removed?.} 37 | 38 | \item{plot}{if TRUE the crop image and fieldShape will be plotted.} 39 | } 40 | \value{ 41 | A list with elements: 42 | \itemize{ 43 | \item \code{mosaic} (cropped by plot) 44 | \item \code{Dimension} (area, x.dist, y.dist) 45 | \item \code{numObjects} (number of objects) 46 | \item \code{Objects} (all objects polygon shape) 47 | \item \code{Polygons} (all extent polygons shape) 48 | \item \code{single.obj} (single object polygon shape) 49 | \item \code{obj.extent} (each object extent) 50 | \item \code{x.position} (coordinates of "x" length per object) 51 | \item \code{y.position} (coordinates of "y" length per object). 52 | } 53 | } 54 | \description{ 55 | Calculating the object dimensions (e.g., area, "x" distance, "y" distance, number, extent, etc.) 56 | in the entire mosaic or per plot using the \code{\link{fieldShape}} file. 57 | } 58 | \details{ 59 | fieldObject 60 | } 61 | -------------------------------------------------------------------------------- /man/fieldDraw.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_fieldDraw.R 3 | \name{fieldDraw} 4 | \alias{fieldDraw} 5 | \title{Making draws (lines or polygons)} 6 | \usage{ 7 | fieldDraw( 8 | mosaic, 9 | line = TRUE, 10 | ndraw = 1, 11 | dist = FALSE, 12 | distSel = 0.5, 13 | round = 5, 14 | value = 1, 15 | pch = 16, 16 | cex = 0.7, 17 | col = "red", 18 | lwd = 1 19 | ) 20 | } 21 | \arguments{ 22 | \item{mosaic}{object mask of class stack from the function \code{\link{fieldMask}}.} 23 | 24 | \item{line}{if it is TRUE the selected area will be a line otherwise it will be a polygon (line=FALSE).} 25 | 26 | \item{ndraw}{number of drawings (lines or polygons).} 27 | 28 | \item{dist}{if it is TRUE the distance among objects or objects length will be calculated 29 | (mosaic should be the mask from function \code{\link{fieldMask}} and line should be line=TRUE).} 30 | 31 | \item{distSel}{integer indicating which proportion of estimated distances should be selected (default 0.5). 32 | If \code{distSel = 1} all estimated distances will be saved. Some distances can be artifacts caused by the mask 33 | estimation and can inflate the number of distances.} 34 | 35 | \item{round}{integer indicating the number of decimal places (round) or significant digits (signif) to be used.} 36 | 37 | \item{value}{integer indicating the value in the mask. If value=1 the objects length will be estimated. 38 | If \code{value = 0} the distance between objects will be estimated.} 39 | 40 | \item{pch}{point symbol, please check help("points").} 41 | 42 | \item{cex}{character (or symbol) expansion: a numerical vector, please check help("points").} 43 | 44 | \item{col}{color code or name, please check help("points").} 45 | 46 | \item{lwd}{line width, please check help("lines").} 47 | } 48 | \value{ 49 | A list four elements 50 | \itemize{ 51 | \item \code{drawData} is a matrix with each cell position "x" and "y" (line/polygon) and the cell value "layer". 52 | \item \code{drawObject} is the the new \code{SpatialPolygons} or \code{SpatialLines} object. 53 | \item \code{drawSegments} is a simplified drawData matrix with segments defined in paramter "value". 54 | \item \code{drawDist} is the distance or length between objects. 55 | } 56 | } 57 | \description{ 58 | Extracting information (x, y, value) for all cells in a line or polygon. Also, this function can 59 | be used to evaluate distances between objects or objects length. 60 | } 61 | \details{ 62 | fieldDraw 63 | } 64 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(fieldAUC) 4 | export(fieldArea) 5 | export(fieldCount) 6 | export(fieldCrop) 7 | export(fieldDraw) 8 | export(fieldHeight) 9 | export(fieldIndex) 10 | export(fieldInfo) 11 | export(fieldInterpolate) 12 | export(fieldMap) 13 | export(fieldMask) 14 | export(fieldObject) 15 | export(fieldPlot) 16 | export(fieldPolygon) 17 | export(fieldRotate) 18 | export(imgEBI) 19 | export(imgLAB) 20 | export(imgRAST) 21 | importFrom(DescTools,AUC) 22 | importFrom(EBImage,Image) 23 | importFrom(EBImage,distmap) 24 | importFrom(EBImage,watershed) 25 | importFrom(dplyr,group_by) 26 | importFrom(dplyr,n) 27 | importFrom(dplyr,summarize) 28 | importFrom(exactextractr,exact_extract) 29 | importFrom(fields,Tps) 30 | importFrom(grDevices,col2rgb) 31 | importFrom(grDevices,colorRamp) 32 | importFrom(grDevices,grey) 33 | importFrom(grDevices,rgb) 34 | importFrom(graphics,abline) 35 | importFrom(graphics,axis) 36 | importFrom(graphics,legend) 37 | importFrom(graphics,lines) 38 | importFrom(graphics,locator) 39 | importFrom(graphics,par) 40 | importFrom(graphics,plot) 41 | importFrom(graphics,points) 42 | importFrom(methods,as) 43 | importFrom(methods,slot) 44 | importFrom(parallel,stopCluster) 45 | importFrom(raster,as.data.frame) 46 | importFrom(raster,as.list) 47 | importFrom(raster,as.matrix) 48 | importFrom(raster,atan2) 49 | importFrom(raster,clump) 50 | importFrom(raster,crop) 51 | importFrom(raster,crs) 52 | importFrom(raster,drawLine) 53 | importFrom(raster,drawPoly) 54 | importFrom(raster,extent) 55 | importFrom(raster,extract) 56 | importFrom(raster,mask) 57 | importFrom(raster,plotRGB) 58 | importFrom(raster,projectRaster) 59 | importFrom(raster,projection) 60 | importFrom(raster,raster) 61 | importFrom(raster,rasterToPolygons) 62 | importFrom(raster,res) 63 | importFrom(raster,values) 64 | importFrom(raster,xyFromCell) 65 | importFrom(sp,Polygon) 66 | importFrom(sp,Polygons) 67 | importFrom(sp,SpatialPointsDataFrame) 68 | importFrom(sp,SpatialPolygons) 69 | importFrom(sp,SpatialPolygonsDataFrame) 70 | importFrom(sp,bbox) 71 | importFrom(sp,over) 72 | importFrom(sp,proj4string) 73 | importFrom(sp,spsample) 74 | importFrom(stats,dist) 75 | importFrom(terra,as.array) 76 | importFrom(terra,cellSize) 77 | importFrom(terra,crds) 78 | importFrom(terra,crs) 79 | importFrom(terra,extract) 80 | importFrom(terra,hasMinMax) 81 | importFrom(terra,ifel) 82 | importFrom(terra,interpolate) 83 | importFrom(terra,is.bool) 84 | importFrom(terra,mask) 85 | importFrom(terra,nlyr) 86 | importFrom(terra,plotRGB) 87 | importFrom(terra,rast) 88 | importFrom(terra,rasterize) 89 | importFrom(terra,resample) 90 | importFrom(terra,setMinMax) 91 | importFrom(terra,vect) 92 | importFrom(utils,read.csv) 93 | -------------------------------------------------------------------------------- /R/fct_fieldInfo.R: -------------------------------------------------------------------------------- 1 | #' fieldInfo 2 | #' 3 | #' @title Extract information from image using the fieldShape file as reference 4 | #' 5 | #' @description Function that use \code{raster::extract()} to extract information from the original 6 | #' image using fieldShape file as reference. 7 | #' 8 | #' @param mosaic object of class stack. 9 | #' @param fieldShape plot shape file, please use first the function \code{\link{fieldShape}}. 10 | #' @param fun to summarize the values (e.g. mean). 11 | #' @param plot if is TRUE the original and crop image will be plotted. 12 | #' @param buffer negative values should be used to remove boundaries from neighbor plot 13 | #' (normally the unit is meters, please use values as 0.1 = 10 cm). 14 | #' @param n.core number of cores to use for multicore processing (Parallel). 15 | #' @param projection if is FALSE projection will be ignored. 16 | #' 17 | #' @importFrom parallel stopCluster 18 | #' @importFrom graphics par 19 | #' @importFrom utils read.csv 20 | #' 21 | #' @return A list with a data frame with values by plot and experimental field image with format stack. 22 | #' 23 | #' @export 24 | fieldInfo <- function(mosaic, fieldShape, fun = "mean", plot = FALSE, buffer = NULL, 25 | n.core = NULL, projection = TRUE) { # buffer is in the mosaic unit, usually in meters. 26 | if(projection){if(projection(fieldShape)!=projection(mosaic)){stop("fieldShape and mosaic must have the same projection CRS. Use fieldRotate() for both files.")}} 27 | mosaic <- stack(mosaic) 28 | num.band<-length(mosaic@layers) 29 | print(paste( "Extracting: ", num.band, " layers.", sep = "")) 30 | print(paste("You can speed up this step using n.core=", detectCores(), " or less.", sep = "")) 31 | CropPlot <- crop(x = mosaic, y = fieldShape) 32 | if(is.null(n.core)){ 33 | plotValue <- extract(x = CropPlot, y = fieldShape, fun = eval(parse(text = fun)), 34 | buffer = buffer, na.rm = T, df = T)} 35 | if(!is.null(n.core)){ 36 | if(n.core>detectCores()){stop(paste(" 'n.core' must be less than ",detectCores(),sep = ""))} 37 | cl <- parallel::makeCluster(n.core, output = "", setup_strategy = "sequential") 38 | registerDoParallel(cl) 39 | plotValue <- foreach(i=1:length(fieldShape), .packages= c("raster"), .combine = rbind) %dopar% { 40 | single <- fieldShape[i,] 41 | CropPlot <- crop(x = mosaic, y = single) 42 | extract(x = CropPlot, y = single, fun = eval(parse(text = fun)),buffer = buffer, na.rm = T, df = T)} 43 | plotValue$ID<-1:length(fieldShape) 44 | parallel::stopCluster(cl)} 45 | fieldShape@data<-cbind.data.frame(fieldShape@data,plotValue) 46 | Out<-list(fieldShape=fieldShape,plotValue=plotValue,cropPlot=CropPlot) 47 | if(plot){ 48 | if(num.band>2){plotRGB(RGB.rescale(CropPlot, num.band=3), r = 1, g = 2, b = 3)} 49 | if(num.band<3){raster::plot(CropPlot, axes=FALSE, box=FALSE)} 50 | sp::plot(fieldShape, add=T) 51 | } 52 | return(Out) 53 | } 54 | -------------------------------------------------------------------------------- /R/fct_fieldAUC.R: -------------------------------------------------------------------------------- 1 | #' fieldAUC 2 | #' 3 | #' @title Area Under the Curve 4 | #' 5 | #' @description Calculate the area under the curve given by vectors of xy-coordinates. 6 | #' 7 | #' @param data data table from \code{\link{fieldInfo}}. 8 | #' @param trait one or more traits to be evaluated. 9 | #' @param keep.columns columns names to be maintained in the output dataset. 10 | #' @param method the type of interpolation. Can be "trapezoid" (default), "step", "linear" or "spline". More information on ??DescTools::AUC. 11 | #' @param x.start value of x to start the AUC (default is 0 days after planting). 12 | #' @param y.start value of y to start the AUC (default is 0). 13 | #' @param frame format of output data. "long" is used for AUC values on the 1st column and traits ID on the2nd column. While "wide" is for objects with dimension n \times m where traits must be in columns and plots/sample in rows. 14 | #' 15 | #' @importFrom DescTools AUC 16 | #' 17 | #' @return A list with a data frame with values by plot and experimental field image with format stack. 18 | #' 19 | #' @export 20 | fieldAUC<-function(data, 21 | trait, 22 | keep.columns=c("NAME","ROW","RANGE"), 23 | method ="trapezoid", 24 | x.start=0, 25 | y.start=0, 26 | frame = "long"){ 27 | if(!c("DAP"%in%as.character(colnames(data)))){ 28 | stop("Missing one column with days after planting named 'DAP'") 29 | } 30 | DataAUC<-NULL 31 | for(i in 1:length(trait)){ 32 | trait1<-trait[i] 33 | print(paste("Evaluating AUC for ",trait1,sep="")) 34 | Plot<-as.character(unique(data$PlotName)) 35 | DataAUC.1 <-NULL 36 | for(a1 in 1:length(Plot)){ 37 | D1<-data[as.character(data$PlotName)==Plot[a1],] 38 | x1<-c(x.start,as.numeric(D1$DAP)) 39 | y1<-c(y.start,as.numeric(D1[,trait1])) 40 | if(frame == c("long")){ 41 | DataAUC <- rbind(DataAUC, 42 | c(as.matrix(D1[1,c(keep.columns)]), 43 | TRAIT=trait1, 44 | AUC=AUC(x = x1[!is.na(y1)], 45 | y = y1[!is.na(y1)], 46 | method = method))) 47 | } 48 | if(frame=="wide"){ 49 | if(i==1){ 50 | DataAUC <- rbind(DataAUC, 51 | c(as.matrix(D1[1,c(keep.columns)]), 52 | AUC(x = x1[!is.na(y1)], 53 | y = y1[!is.na(y1)], 54 | method = method))) 55 | } 56 | if(i!=1){ 57 | DataAUC.1 <- c(DataAUC.1,AUC(x = x1[!is.na(y1)], 58 | y = y1[!is.na(y1)], 59 | method = method)) 60 | } 61 | } 62 | } 63 | if(frame=="wide"){ 64 | DataAUC<-cbind(DataAUC,DataAUC.1) 65 | colnames(DataAUC)[dim(DataAUC)[2]]<-paste(trait1,"AUC",sep="_")} 66 | } 67 | namesAUC<-colnames(DataAUC) 68 | namesAUC[1:length(keep.columns)]<-c(keep.columns) 69 | DataAUC<-as.data.frame(matrix(unlist(DataAUC), ncol = dim(DataAUC)[2], nrow = dim(DataAUC)[1])) 70 | colnames(DataAUC)<-namesAUC 71 | return(DataAUC)} 72 | -------------------------------------------------------------------------------- /R/fct_fieldCrop.R: -------------------------------------------------------------------------------- 1 | #' fieldCrop 2 | #' 3 | #' @title Selecting experimental field from original image 4 | #' 5 | #' @description It calculates the percentage of object area in the entire mosaic or per plot using the fieldShape file. 6 | #' 7 | #' @param mosaic object mask of class stack from the function \code{\link{fieldMask}}. 8 | #' @param fieldShape crop the image using the fieldShape as reference. If fieldShape=NULL, four points should be selected 9 | #' directly on the original image to determine the experimental field. 10 | #' @param nPoint number of points necessary to select field boundaries or area to remove (4 >= nPoint <= 50). 11 | #' @param remove if TRUE the selected area will be removed from the image. 12 | #' @param plot if \code{TRUE} (by default) plots the original and cropped image. 13 | #' @param type character indicating the type of plotting, please check help("lines"). 14 | #' @param lty line types, please check help("lines"). 15 | #' @param lwd line width, please check help("lines"). 16 | #' @param fast.plot if TRUE only the grey scale image will be plotted as reference (faster approach). 17 | #' if TRUE only the grey scale image will be plotted as reference (faster approach). 18 | #' 19 | #' @importFrom raster plotRGB mask 20 | #' @importFrom graphics locator lines 21 | #' @importFrom sp Polygons Polygon SpatialPolygonsDataFrame SpatialPolygons 22 | #' 23 | #' 24 | #' @return A image format stack. 25 | #' 26 | #' @export 27 | fieldCrop <- function(mosaic, fieldShape = NULL, nPoint = 4, plot = TRUE, remove = FALSE, type = "l", 28 | lty = 2, lwd = 3, fast.plot = FALSE) { 29 | mosaic <- stack(mosaic) 30 | num.band <- length(mosaic@layers) 31 | print(paste(num.band," layers available", sep = "")) 32 | if(nPoint<4|nPoint>50){stop("nPoint must be >= 4 and <= 50")} 33 | par(mfrow=c(1,2)) 34 | if(is.null(fieldShape)|plot){ 35 | if(fast.plot){ 36 | raster::plot(mosaic[[1]], col=grey(1:100/100), axes=FALSE, box=FALSE, legend=FALSE)} 37 | if(!fast.plot){ 38 | if(num.band>2){plotRGB(RGB.rescale(mosaic,num.band=3), r = 1, g = 2, b = 3)} 39 | if(num.band<3){raster::plot(mosaic, axes=FALSE, box=FALSE)}} 40 | } 41 | if(!is.null(fieldShape)){ 42 | if(raster::projection(fieldShape) != raster::projection(mosaic)){ 43 | stop("fieldShape and mosaic must have the same raster::projection CRS. Use fieldRotate() for both files.") 44 | } 45 | r <- crop(x = mosaic, y = fieldShape) 46 | } 47 | c1 <- NULL 48 | if(is.null(fieldShape)) { 49 | print(paste("Select ",nPoint," points at the corners of field of interest in the plots space.",sep = "")) 50 | for(i in 1:nPoint){ 51 | c1.1<-locator(type="p",n = 1, col="red",pch=19) 52 | c1<-rbind(c1,c(c1.1$x,c1.1$y)) 53 | } 54 | c1<-rbind(c1,c1[1,]) 55 | colnames(c1)<-c("x","y") 56 | lines(c1, col= "red", type=type, lty=lty, lwd=lwd) 57 | } 58 | if(!is.null(c1)) { 59 | p1 <- Polygons(list(Polygon(c1)), "x") 60 | f1 <- SpatialPolygonsDataFrame( SpatialPolygons(list(p1)), data.frame( z=1, row.names=c("x") ) ) 61 | raster::projection(f1) <- raster::projection(mosaic) 62 | if(!remove){r <- crop(x = mosaic, y = f1)} 63 | if(remove){r <- mask(x = mosaic, mask = f1, inverse = remove)} 64 | } 65 | r <- stack(r) 66 | if(plot){ 67 | if(fast.plot) { 68 | raster::plot(r[[1]], col=grey(1:100/100), axes=FALSE, box=FALSE, legend=FALSE)} 69 | if(!fast.plot){ 70 | if(num.band>2){plotRGB(RGB.rescale(r,num.band=3), r = 1, g = 2, b = 3)} 71 | if(num.band<3){raster::plot(r, axes=FALSE, box=FALSE)}}} 72 | par(mfrow=c(1,1)) 73 | return(r) 74 | } 75 | -------------------------------------------------------------------------------- /R/fct_fieldIndex.R: -------------------------------------------------------------------------------- 1 | #' fieldIndex 2 | #' 3 | #' @title Building vegetation indices using Red, Green, Blue, Red Edge, and NIR band 4 | #' 5 | #' @description Different vegetation indices can be calculated using at least 3 bands. 6 | #' For the list of indices please visit the FIELDimageR manual at link 7 | #' 8 | #' @param mosaic object of class stack with at least 3 bands. 9 | #' @param Red vector with the vegetation indices to be calculated. 10 | #' @param Green user can calculate a diferent index using the bands names, e.g. "(Green+Blue)/Red-NIR/RedEdge" 11 | #' @param Blue if is TRUE the original and crop image will be plotted. 12 | #' @param RedEdge vector with the vegetation indices to be calculated. 13 | #' @param NIR if is TRUE the original and crop image will be plotted. 14 | #' @param index vector with the vegetation indices to be calculated. 15 | #' @param myIndex user can calculate a diferent index using the bands names, e.g. "(Green+Blue)/Red-NIR/RedEdge" 16 | #' @param plot if is TRUE the original and crop image will be plotted. 17 | #' 18 | #' @importFrom utils read.csv 19 | #' 20 | #' @return A Image format stack. 21 | #' 22 | #' @export 23 | fieldIndex<- function(mosaic, Red = 1, Green = 2, Blue = 3, RedEdge = NULL, NIR = NULL, index = "HUE", 24 | myIndex = NULL, plot = TRUE) { 25 | # Load index data 26 | Ind <- read.csv(file = system.file("extdata", "Indices.txt", package = "FIELDimageR", mustWork = TRUE), 27 | header = TRUE, sep = "\t") 28 | 29 | num.band <- nlyr(mosaic) 30 | print(paste(num.band, " layers available", sep = "")) 31 | 32 | if (num.band < 3) { 33 | stop("At least 3 bands (RGB) are necessary to calculate indices") 34 | } 35 | 36 | if (!is.null(RedEdge) || !is.null(NIR)) { 37 | if (num.band < 4) { 38 | stop("RedEdge and/or NIR is/are not available in your mosaic") 39 | } 40 | } 41 | 42 | IRGB <- as.character(Ind$index) 43 | 44 | if (is.null(index)) { 45 | stop("Choose one or more indices") 46 | } 47 | 48 | if (!all(index %in% IRGB)) { 49 | stop(paste("Index: ", index[!index %in% IRGB], " is not available in FIELDimageR")) 50 | } 51 | 52 | NIR.RE <- as.character(Ind$index[Ind$band %in% c("RedEdge", "NIR")]) 53 | 54 | if (any(NIR.RE %in% index) && is.null(NIR)) { 55 | stop(paste("Index: ", NIR.RE[NIR.RE %in% index], " needs NIR/RedEdge band to be calculated", sep = "")) 56 | } 57 | 58 | B <- mosaic[[Blue]] 59 | G <- mosaic[[Green]] 60 | R <- mosaic[[Red]] 61 | names(mosaic)[c(Blue, Green, Red)] <- c("Blue", "Green", "Red") 62 | 63 | if (!is.null(RedEdge)) { 64 | RE <- mosaic[[RedEdge]] 65 | names(mosaic)[RedEdge] <- "RedEdge" 66 | } 67 | 68 | if (!is.null(NIR)) { 69 | NIR1 <- mosaic[[NIR]] 70 | names(mosaic)[NIR] <- "NIR" 71 | } 72 | 73 | for (i in 1:length(index)) { 74 | new_layer <- eval(parse(text = as.character(Ind$eq[Ind$index == index[i]]))) 75 | mosaic <- append(mosaic,new_layer) 76 | names(mosaic)[num.band + i] <- as.character(index[i]) 77 | } 78 | 79 | if(!is.null(myIndex)){ 80 | Blue<-B 81 | Green<-G 82 | Red<-R 83 | if(!is.null(NIR)){NIR<-NIR1} 84 | if(!is.null(RedEdge)){RedEdge<-RE} 85 | for(m1 in 1:length(myIndex)){ 86 | my_ind <- eval(parse(text = as.character(myIndex[m1]))) 87 | mosaic<-append(mosaic,my_ind) 88 | if(length(myIndex)==1){names(mosaic)[(nlyr(mosaic))] <- "myIndex"} 89 | if(length(myIndex)>1){names(mosaic)[(nlyr(mosaic))] <- paste("myIndex", m1)} 90 | } 91 | } 92 | 93 | if (plot) { 94 | terra::plot(mosaic, axes = FALSE, box = FALSE) 95 | } 96 | return(mosaic) 97 | } 98 | -------------------------------------------------------------------------------- /R/fct_fieldPlot.R: -------------------------------------------------------------------------------- 1 | #' fieldPlot 2 | #' 3 | #' @title Plot of fieldShape file filled with trait value for each plot 4 | #' 5 | #' @description Graphic visualization of trait values for each plot using the \code{\link{fieldShape}} file and original image. 6 | #' @param fieldShape plot shape file, please use first the function getInfo(). 7 | #' @param fieldAttribute attribute or trait which the values will fill the plots, please use first the function getInfo(). 8 | #' @param mosaic object of class stack that is not necessary, but if provided will be plotted with the fieldShape file. 9 | #' @param color colors to interpolate, must be a valid argument. 10 | #' @param min.lim lowest limit of the color range. If is NULL the lowest value of the data will be used. 11 | #' @param max.lim upper limit of the color range. If is NULL the highest value of the data will be used. 12 | #' @param alpha transparency with values between 0 and 1. 13 | #' @param legend.position legend position. 14 | #' @param na.color color of missing values "NA". 15 | #' @param classes number of classes at the legend. 16 | #' @param round number of decimal digits at the legend. 17 | #' @param horiz if TRUE will plot a horizontal legend. 18 | #' 19 | #' @importFrom grDevices colorRamp 20 | #' 21 | #' @return A list with two element 22 | #' \itemize{ 23 | #' \item The function returns a image with the \code{fieldShape} file filled with trait value for each plot. 24 | #' } 25 | #' 26 | #' 27 | #' @export 28 | fieldPlot <- function(fieldShape, fieldAttribute, mosaic = NULL, color=c("white","black"), min.lim = NULL, max.lim = NULL, 29 | alpha = 0.5, legend.position = "right", na.color = "gray", classes = 5, round = 3, horiz = FALSE) { 30 | if(length(fieldAttribute)>1){stop("Choose ONE attribute")} 31 | attribute<-colnames(fieldShape@data) 32 | if(!fieldAttribute%in%attribute){stop(paste("Attribute ",fieldAttribute," is not valid. Choose one among: ", unique(attribute), sep = ""))} 33 | val<-as.numeric(fieldShape@data[,which(attribute%in%fieldAttribute)[1]]) 34 | if(!c(is.null(min.lim)&is.null(max.lim))){ 35 | if (!c(is.numeric(min.lim)&is.numeric(max.lim))) { 36 | stop("Limit need to be numeric e.g. min.lim=0 and max.lim=1") 37 | } 38 | if (min.lim > min(val, na.rm = T)) { 39 | stop(paste("Choose minimum limit (min.lim) equal or lower than ",min(val,na.rm = T), sep="")) 40 | } 41 | if (max.lim < max(val, na.rm = T)) { 42 | stop(paste("Choose maximum limit (max.lim) equal or greater than ",max(val,na.rm = T), sep="")) 43 | } 44 | val<-c(min.lim,val,max.lim) 45 | } 46 | na.pos<-is.na(val) 47 | rr <- range(val,na.rm=T) 48 | svals <- (val-rr[1])/diff(rr) 49 | f <- colorRamp(color) 50 | svals[na.pos] <- 0 51 | valcol <- rgb(f(svals)/255, alpha = alpha) 52 | valcol[na.pos] <- rgb(t(col2rgb(col = na.color, alpha = FALSE))/255,alpha = alpha) 53 | if(!c(is.null(min.lim)&is.null(max.lim))){valcol<-valcol[-c(1,length(valcol))]} 54 | if(!is.null(mosaic)){ 55 | if(projection(fieldShape)!=projection(mosaic)){stop("fieldShape and mosaic must have the same projection CRS. Use fieldRotate() for both files.")} 56 | mosaic <- stack(mosaic) 57 | num.band<-length(mosaic@layers) 58 | if(num.band>2){plotRGB(RGB.rescale(mosaic,num.band=3), r = 1, g = 2, b = 3)} 59 | if(num.band<3){raster::plot(mosaic, axes=FALSE, box=FALSE)} 60 | sp::plot(fieldShape, col= valcol, add = T) 61 | } 62 | if(is.null(mosaic)){ 63 | sp::plot(fieldShape, col= valcol)} 64 | pos <- round(seq(min(val,na.rm = T), max(val,na.rm = T), length.out = classes),round) 65 | if(any(na.pos)){pos=c(pos,"NA")} 66 | col<-rgb(f(seq(0, 1, length.out = classes))/255, alpha = alpha) 67 | if(any(na.pos)){col=c(col,rgb(t(col2rgb(col = na.color, alpha = FALSE))/255,alpha = alpha))} 68 | legend(legend.position, 69 | title= fieldAttribute, 70 | legend = pos, 71 | fill = col, 72 | bty = "n", 73 | horiz = horiz) 74 | } 75 | -------------------------------------------------------------------------------- /R/fct_fieldCount.R: -------------------------------------------------------------------------------- 1 | #' fieldCount 2 | #' 3 | #' @title Calculating number of objects per plot 4 | #' 5 | #' @description The mask from function \code{\link{fieldMask}} is used to identify the number of objects per plot.. 6 | #' 7 | #' @param mosaic object mask of class stack from the function \code{\link{fieldMask}}. 8 | #' @param fieldShape plot shape file. 9 | #' @param watershed TRUE (defaut) or FALSE. Use or not watershed to identify objects. Otherwise, all touching objects will be considered as one polygon. 10 | #' @param plot if it is TRUE the original and segmented image will be plotted with identified objects. 11 | #' @param pch point symbol, please check \code{help("points")}. 12 | #' @param col color code or name, please check \code{help("points")}. 13 | #' 14 | #' @importFrom EBImage distmap watershed 15 | #' @importFrom terra ifel is.bool crds as.array 16 | #' @importFrom dplyr summarize group_by n 17 | #' 18 | #' @return A list with two elements when fieldShape is provided: 19 | #' \itemize{ 20 | #' \item \code{fieldShape$plot_level} is the new shapeFile with objects in the plot area, perimeter, count, and mean_width. 21 | #' \item \code{fieldShape$object_level} is the new shapeFile of single objects area, perimeter, width, x and y position. 22 | #' } 23 | #' 24 | #' 25 | #' @export 26 | fieldCount<-function(mosaic, 27 | fieldShape=NULL, 28 | watershed=TRUE, 29 | plot=FALSE, 30 | pch ="+", 31 | col = 'red') { 32 | # Check if the input is a valid terra raster object 33 | if ((!isTRUE(terra::is.bool(mosaic))) | !length(dim(mosaic)) >= 2) { 34 | stop("fieldCount requires a 2-dimensional terra raster object (mask layer)") 35 | } 36 | 37 | # Define variables 38 | all_attri <- NULL 39 | rast_obj <- NULL 40 | 41 | if(!is.null(watershed) && !is.null(fieldShape)){ 42 | binay<-terra::ifel(mosaic,0,1) 43 | img<-terra::as.array(t(as.matrix(binay, wide=TRUE))) 44 | img[is.na(img)] <- TRUE 45 | dis<-EBImage::distmap(img) 46 | seg<-EBImage::watershed(dis,watershed) 47 | ebi<-terra::as.array(seg) 48 | rast_obj <- terra::rast(t(as.matrix(rast(ebi), wide=TRUE))) 49 | rast_obj[rast_obj== 0] <- NA 50 | crs(rast_obj)<-crs(mosaic) 51 | ext(rast_obj)<-ext(mosaic) 52 | # Convert to polygons 53 | poly <- as.polygons(rast_obj) 54 | # Plot and add text labels 55 | if(plot){ 56 | par(mfrow=c(1,2)) 57 | terra::plot(rast_obj) 58 | terra::plot(fieldShape$geometry,col='#00008800',alpha=0,add=TRUE) 59 | suppressWarnings(terra::plot(st_geometry(st_centroid(st_as_sf(poly))), 60 | pch = pch, col = col)) 61 | terra::plot(fieldShape$geometry,col='#00008800',alpha=0,add=TRUE) 62 | par(mfrow=c(1,1)) 63 | } 64 | perimeter<-perim(poly) 65 | area<-expanse(poly) 66 | width<-width(poly) 67 | attri<-st_as_sf(poly) 68 | suppressWarnings(xy<-terra::crds(vect(st_centroid(attri)))) 69 | attributes<-cbind(attri[,-1],area,perimeter,width,xy) 70 | att<-cbind(ID = 1:nrow(attri[,-1]),attri[,-1],area,perimeter,width,xy) 71 | c<-st_join(fieldShape, st_as_sf(attributes)) 72 | all<- c%>% group_by(ID) %>% 73 | summarize(area =round(sum(area),3), 74 | perimeter=round(sum(perimeter),3),count=n(),mean_width=round(mean(width),3)) 75 | all_attri<-list(plot_level=all, 76 | object_level=att) 77 | }else if(is.null(watershed) || is.null(fieldShape)){ 78 | logi<-terra::ifel(mosaic,NA,1) 79 | rast_obj<- patches(logi, directions = 4) 80 | poly <- as.polygons(rast_obj) 81 | poly$ID <- seq.int(nrow(poly)) 82 | if(plot){ 83 | par(mfrow=c(1,2)) 84 | terra::plot(rast_obj) 85 | terra::plot(poly) 86 | par(mfrow=c(1,1))} 87 | perimeter<-perim(poly) 88 | area<-expanse(poly) 89 | width<-width(poly) 90 | attri<-st_as_sf(poly) 91 | attributes<-cbind(attri[,-1],area,perimeter,width) 92 | all_attri<-st_as_sf(attributes) 93 | }else if(!is.null(watershed) && is.null(fieldShape)){ 94 | binay<-terra::ifel(mosaic,0,1) 95 | img<-terra::as.array(t(as.matrix(binay,wide=TRUE))) 96 | img[is.na(img)] <- TRUE 97 | dis<-EBImage::distmap(img) 98 | seg<-EBImage::watershed(dis,watershed) 99 | ebi<-terra::as.array(seg) 100 | rast_obj <- terra::rast(t(as.matrix(rast(ebi), wide=TRUE))) 101 | rast_obj[rast_obj== 0] <- NA 102 | crs(rast_obj)<-crs(mosaic) 103 | ext(rast_obj)<-ext(mosaic) 104 | # Convert to polygons 105 | poly <- as.polygons(rast_obj) 106 | # Plot and add text labels 107 | if(plot){ 108 | par(mfrow=c(1,2)) 109 | terra::plot(rast_obj) 110 | suppressWarnings(terra::plot(st_geometry(st_centroid(st_as_sf(poly))), 111 | pch = pch,cex=0.05,col = col)) 112 | par(mfrow=c(1,1))} 113 | perimeter<-perim(poly) 114 | area<-expanse(poly) 115 | width<-width(poly) 116 | attri<-st_as_sf(poly) 117 | attributes<-cbind(attri[,-1],area,perimeter,width) 118 | all_attri<-st_as_sf(attributes) 119 | } 120 | 121 | return(all_attri) 122 | } 123 | -------------------------------------------------------------------------------- /R/fct_fieldMask.R: -------------------------------------------------------------------------------- 1 | #' fieldMask 2 | #' 3 | #' @description A fct function 4 | #' 5 | #' @return The return value, if any, from executing the function. 6 | #' 7 | #' @importFrom utils read.csv 8 | 9 | #' 10 | #' @title Removing background (e.g., soil) using vegetation index. 11 | #' 12 | #' @description Different vegetation indices can be used to remove images background. For the list of indices please visit the FIELDimageR manual at link: https://github.com/OpenDroneMap/FIELDimageR#P6 13 | #' 14 | #' @param mosaic object of class stack with at least 3 bands. 15 | #' @param Red,Green,Blue,RedEdge,NIR respective position of the band at the original image file. 16 | #' @param index vector with the vegetation indices to be calculated. For the list of indices please visit the FIELDimageR manual at link: 17 | #' @param myIndex user can calculate a diferent index using the bands names, e.g. "(Green+Blue)/Red-NIR/RedEdge" 18 | #' @param mask if avaliable the soil will be removed following this mask and not the vegetation index, cropValue and cropAbove must be used. 19 | #' @param cropValue referent value of soil in the image. 20 | #' @param cropAbove if TRUE all values above the cropValue will be accounted to make the mask. 21 | #' @param projection if TRUE the projection will not be accounted to the mask. 22 | #' @param DSMmosaic,DSMcropAbove,DSMcropValue DSM should be used if the file of height is provided. 23 | #' @param plot if is TRUE the original and crop image will be plotted. 24 | #' 25 | #' @importFrom utils read.csv 26 | #' @importFrom terra nlyr 27 | #' 28 | #'@return A list with elements: 29 | #' \itemize{ 30 | #' \item The function returns a image format stack with the original bands (layers) without the background and mask with logical values of 0 and 1 for vegetation or soil. 31 | #' } 32 | #' 33 | #' 34 | #' @export 35 | fieldMask<- function(mosaic, Red = 1, Green = 2, Blue = 3, RedEdge = NULL, NIR = NULL, mask = NULL, index = "HUE", 36 | myIndex = NULL, cropValue = 0, cropAbove = TRUE, projection = TRUE, DSMmosaic = NULL, 37 | DSMcropAbove = TRUE, DSMcropValue = 0, plot = TRUE) { 38 | Ind <- read.csv(file=system.file("extdata", "Indices.txt", package = "FIELDimageR", mustWork = TRUE), 39 | header = TRUE, sep = "\t") 40 | num.band <- nlyr(mosaic) 41 | print(paste(num.band," layers available", sep = "")) 42 | if(is.null(mask)){ 43 | if(num.band<3){stop("At least 3 bands (RGB) are necessary to calculate indices available in FIELDimageR")} 44 | if(!is.null(RedEdge)|!is.null(RedEdge)){ 45 | if(num.band<4){ 46 | stop("RedEdge and/or NIR is/are not available in your mosaic") 47 | }} 48 | IRGB = as.character(Ind$index) 49 | if(is.null(index)|length(index)>1){stop("Only one index must be chosen for this step")} 50 | if(!all(index%in%IRGB)){stop(paste("Index: ",as.character(index[!index%in%IRGB])," is not available in FIELDimageR",sep = ""))} 51 | NIR.RE<-as.character(Ind$index[Ind$band%in%c("RedEdge","NIR")]) 52 | if(any(NIR.RE%in%index)&is.null(NIR)){stop(paste("Index: ",as.character(NIR.RE[NIR.RE%in%index])," need NIR/RedEdge band to be calculated",sep = ""))} 53 | B <- mosaic[[Blue]] 54 | G <- mosaic[[Green]] 55 | R <- mosaic[[Red]] 56 | names(mosaic)[c(Blue, Green, Red)] <- c("Blue", "Green", "Red") 57 | if (!is.null(RedEdge)) { 58 | RE <- mosaic[[RedEdge]] 59 | names(mosaic)[RedEdge] <- "RedEdge" 60 | } 61 | 62 | if (!is.null(NIR)) { 63 | NIR1 <- mosaic[[NIR]] 64 | names(mosaic)[NIR] <- "NIR" 65 | } 66 | mr<-eval(parse(text = as.character(Ind$eq[as.character(Ind$index)==index]))) 67 | if(!is.null(myIndex)){ 68 | print(paste("Mask equation myIndex=",myIndex, sep = "")) 69 | Blue<-B 70 | Green<-G 71 | Red<-R 72 | if(!is.null(NIR)){NIR<-NIR1} 73 | if(!is.null(RedEdge)){RedEdge<-RE} 74 | mr<-eval(parse(text = as.character(myIndex))) 75 | } 76 | } 77 | if(!is.null(mask)){ 78 | if(nlyr(mask)>1){stop("Mask must have only one band.")} 79 | mr<-mask 80 | mosaic<-terra::crop(x = mosaic, y = mr) 81 | if(projection){ 82 | mosaic <- project(mosaic, mask, method = "near") 83 | } 84 | } 85 | 86 | if(cropAbove){ 87 | m<-mr>cropValue 88 | } 89 | if(!cropAbove){ 90 | m<-mr2){plotRGB(RGB.rescale(mosaic,num.band=3), r = 1, g = 2, b = 3)} 99 | if(num.band<3){terra::plot(mosaic, axes=FALSE, box=FALSE)} 100 | terra::plot(m, col=grey(1:100/100), axes=FALSE, box=FALSE)} 101 | 102 | mosaic <- terra::mask(mosaic, m, maskvalue=TRUE) 103 | 104 | if(plot){if(num.band>2){plotRGB(RGB.rescale(mosaic,num.band=3), r = 1, g = 2, b = 3)} 105 | if(num.band<3){terra::plot(mosaic, axes=FALSE, box=FALSE)}} 106 | 107 | Out<-list(newMosaic=mosaic,mask=m) 108 | if(!is.null(DSMmosaic)){ 109 | DSMmosaic <-terra::crop(x = DSMmosaic, y = m) 110 | if(DSMcropAbove){ 111 | mDEM<-m>DSMcropValue 112 | } 113 | if(!DSMcropAbove){ 114 | mDEM<-m 1) { 46 | stop("For dist=T only mask with values of 1 and 0 can be processed, use the mask output from fieldMask()") 47 | } 48 | if (!value %in% c(1, 0)) { 49 | stop("Values in the mask must be 1 or 0 to represent the objects, use the mask output from fieldMask()") 50 | } 51 | if (!all(c(raster::minValue(mosaic), raster::maxValue(mosaic)) %in% 52 | c(1, 0))) { 53 | stop("Values in the mask must be 1 or 0 to represent the objects, use the mask output from fieldMask()") 54 | } 55 | if (distSel<=0|distSel>1) { 56 | stop("distSel must be a vlaue between 1 or 0 ") 57 | } 58 | par(mfrow=c(1,2), mai = c(1, 1, 1, 1)) 59 | } 60 | if (num.band > 2) { 61 | plotRGB(RGB.rescale(mosaic, num.band = 3), r = 1, 62 | g = 2, b = 3) 63 | } 64 | if (num.band < 3) { 65 | raster::plot(mosaic, col = grey(1:100/100)) 66 | } 67 | print("Use the image in the plot space to draw a line/polygon (2 or more points)...") 68 | Out2<-list() 69 | for(d1 in 1:ndraw){ 70 | print(paste("Make the draw number=", d1," and press 'ESC' when it is done.",sep="")) 71 | if(line){draw1<- raster::drawLine(sp = T,col = col,lwd = lwd)} 72 | if(!line){draw1<- raster::drawPoly(sp = T,col = col,lwd = lwd)} 73 | draw2 <- do.call(as.data.frame,raster::extract(x = mosaic, y = draw1,cellnumbers=TRUE)) 74 | draw2 <- data.frame(raster::xyFromCell(object = mosaic,cell = draw2$cell),draw2) 75 | if (abs(max(draw2$x)-min(draw2$x)) >= abs(max(draw2$y) - min(draw2$y))) { 76 | ord1 <- order(draw2$x) 77 | } 78 | if (abs(max(draw2$x)-min(draw2$x)) < abs(max(draw2$y) - min(draw2$y))) { 79 | ord1 <- order(draw2$y) 80 | } 81 | draw2 <- draw2[ord1, ] 82 | Out1<-list(drawData=draw2,drawObject=draw1) 83 | if(dist){ 84 | df1<-draw2[draw2$layer==value,] 85 | df<-draw2[draw2$layer==c(0,1)[c(0,1)!=value],] 86 | out <- t(sapply(1:(nrow(df)-1), function(i) { 87 | d <- round(stats::dist(df[i:(i+1),c("x","y")]),round) 88 | t <- mean(df[i:(i+1),"layer"]) 89 | c(df[i,"x"],df[i,"y"],df[i+1,"x"],df[i+1,"y"],d,t) 90 | })) 91 | colnames(out)<-c("x1","y1","x2","y2","dist","mean") 92 | out<-data.frame(out) 93 | if (abs(max(out$x1)-min(out$x1)) >= abs(max(out$y1) - min(out$y1))) { 94 | ord <- order(out$x1) 95 | } 96 | if (abs(max(out$x1)-min(out$x1)) < abs(max(out$y1) - min(out$y1))) { 97 | ord <- order(out$y1) 98 | } 99 | out <- out[ord, ] 100 | freq<-table(out$dist) 101 | freqSel<-as.numeric(names(table(freq))[1:round(length(table(freq))*distSel,0)]) 102 | out<-out[as.numeric(out$dist)%in%as.numeric(names(freq[freq%in%freqSel])),] 103 | if(d1==1){ 104 | if (num.band > 2) { 105 | plotRGB(RGB.rescale(mosaic, num.band = 3), r = 1, 106 | g = 2, b = 3) 107 | } 108 | if (num.band < 3) { 109 | raster::plot(mosaic, col = grey(1:100/100)) 110 | } 111 | } 112 | points(df1$x,df1$y, pch = pch, cex = cex, col = col) 113 | Out1<-list(drawData=draw2,drawObject=draw1,drawSegments=df1,drawDist=out) 114 | } 115 | Out2[[d1]]<-Out1 116 | } 117 | names(Out2)<-paste("Draw",c(1:ndraw),sep="") 118 | if(ndraw==1){Out2<-Out1} 119 | par(mfrow=c(1,1)) 120 | return(Out2) 121 | } 122 | -------------------------------------------------------------------------------- /R/fct_fieldRotate.R: -------------------------------------------------------------------------------- 1 | #' fieldRotate 2 | #' 3 | #' @title Rotating the image to biuld the \code{\link{fieldShape}} file 4 | #' 5 | #' @description The image should be rotated to use the function \code{\link{fieldShape}}. 6 | #' The base of experimental field should be parallel to axis X. 7 | #' 8 | #' @param mosaic object of class stack. 9 | #' @param theta angle of rotation, if negativo the rotation will be for the right. 10 | #' If not provided the user should select two points from left to right to determine the angle of rotation. 11 | #' @param clockwise if it is TRUE, clockwise rotation. 12 | #' @param h if it is TRUE, the drawn line will be at horizontal, if FALSE (90-theta). 13 | #' @param n.core number of cores to use for multicore processing (Parallel). 14 | #' @param extentGIS if TRUE the rotated image will have an adjusted extent 15 | #' based on the original image (It is an important step to fit the fieldShape on the original image). 16 | #' @param DSMmosaic DSM should be used if the file of height is provided. 17 | #' @param plot if it is TRUE the original and rotated image will be plotted. 18 | #' @param type character indicating the type of plotting, please check help("lines"). 19 | #' @param lty line types, please check help("lines"). 20 | #' @param lwd line width, please check help("lines"). 21 | #' @param fast.plot if TRUE only the grey scale image will be plotted as reference (faster approach). 22 | #' 23 | #' 24 | #' @importFrom raster raster projection plotRGB res projectRaster crs extent atan2 as.list as.data.frame as.matrix 25 | #' @importFrom raster crop rasterToPolygons mask extract clump drawLine drawPoly xyFromCell 26 | #' @importFrom graphics abline axis lines par plot points locator legend 27 | #' @importFrom grDevices grey rgb col2rgb 28 | #' @importFrom sp bbox Polygons Polygon SpatialPolygonsDataFrame SpatialPolygons spsample SpatialPointsDataFrame over proj4string 29 | #' 30 | #' 31 | #' @return A list with two element 32 | #' \itemize{ 33 | #' \item \code{mosaic} rotated image format stack with the base of experimental field parallel to axis X. 34 | #' } 35 | #' 36 | #' 37 | #' @export 38 | fieldRotate <- function(mosaic, theta = NULL, clockwise = TRUE, h = FALSE, n.core = NULL, extentGIS = FALSE, 39 | DSMmosaic = NULL, plot = TRUE, type = "l", lty = 2, lwd = 3, fast.plot = FALSE) { 40 | mosaic <- raster::stack(mosaic) 41 | num.band<-length(mosaic@layers) 42 | print(paste(num.band," layers available", sep = "")) 43 | par(mfrow=c(1,2)) 44 | if(!is.null(DSMmosaic)){ 45 | par(mfrow=c(1,3)) 46 | if(raster::projection(DSMmosaic)!=raster::projection(mosaic)){stop("DSMmosaic and RGBmosaic must have the same projection CRS")}} 47 | if(plot|is.null(theta)){ 48 | if(fast.plot){ 49 | raster::plot(mosaic[[1]], col=grey(1:100/100), axes=FALSE, box=FALSE, legend=FALSE)} 50 | if(!fast.plot){ 51 | if(num.band>2){plotRGB(RGB.rescale(mosaic,num.band=3), r = 1, g = 2, b = 3)} 52 | if(num.band<3){raster::plot(mosaic, axes=FALSE, box=FALSE)}}} 53 | rotate <- function(x, angle=0, resolution=res(x)) { 54 | y <- x 55 | raster::crs(y) <- "+proj=aeqd +ellps=sphere +lat_0=90 +lon_0=0" 56 | projectRaster(y, res=resolution, crs=paste0("+proj=aeqd +ellps=sphere +lat_0=90 +lon_0=", -angle))} 57 | if(is.null(theta)){ 58 | print("Select 2 points from left to right on image in the plots space. Use any horizontal line in the field trial of interest as a reference.") 59 | c1.a <- locator(type="p",n = 1, col="red",pch=19) 60 | c1.b <- locator(type="p",n = 1, col="red",pch=19) 61 | c1<-as.data.frame(mapply(c, c1.a, c1.b)) 62 | colnames(c1)<-c("x","y") 63 | lines(c1, col= "red", type=type, lty=lty, lwd=lwd) 64 | if((c1$y[1]>=c1$y[2])&(c1$x[2]>=c1$x[1])){theta = (atan2((c1$y[1] - c1$y[2]), (c1$x[2] - c1$x[1])))*(180/pi)} 65 | if((c1$y[2]>=c1$y[1])&(c1$x[2]>=c1$x[1])){theta = (atan2((c1$y[2] - c1$y[1]), (c1$x[2] - c1$x[1])))*(180/pi)} 66 | if((c1$y[1]>=c1$y[2])&(c1$x[1]>=c1$x[2])){theta = (atan2((c1$y[1] - c1$y[2]), (c1$x[1] - c1$x[2])))*(180/pi)} 67 | if((c1$y[2]>=c1$y[1])&(c1$x[1]>=c1$x[2])){theta = (atan2((c1$y[2] - c1$y[1]), (c1$x[1] - c1$x[2])))*(180/pi)} 68 | if(!h){theta=90-theta} 69 | if(clockwise){theta=-theta} 70 | theta=round(theta,3) 71 | print(paste("Theta rotation: ",theta,sep = "")) 72 | } 73 | if (is.null(n.core)) { 74 | r<-rotate(mosaic,angle = theta) 75 | } 76 | if (!is.null(n.core)) { 77 | if (n.core > detectCores()) { 78 | stop(paste(" 'n.core' must be less than ", detectCores(),sep = "")) 79 | } 80 | cl <- parallel::makeCluster(n.core, output = "", setup_strategy = "sequential") 81 | registerDoParallel(cl) 82 | r <- foreach(i=1:length(mosaic@layers), .packages = c("raster")) %dopar% {rotate(mosaic[[i]], angle = theta)} 83 | parallel::stopCluster(cl) 84 | } 85 | r <- raster::stack(r) 86 | if(extentGIS){ 87 | m11<-apply(matrix(as.numeric(as.matrix(raster::extent(mosaic))),2),1,function(x){mean(x)}) 88 | m22<-apply(matrix(as.numeric(as.matrix(raster::extent(r))),2),1,function(x){abs(diff(c(x[2],x[1]))/2)}) 89 | raster::extent(r)<-c(as.numeric(c(m11[1]-m22[1])), as.numeric(c(m11[1]+m22[1])), as.numeric(c(m11[2]-m22[2])), as.numeric(c(m11[2]+m22[2]))) 90 | raster::crs(r)<-raster::crs(mosaic) 91 | } 92 | Out<-rast(r) 93 | if(plot){ 94 | if(fast.plot){ 95 | raster::plot(r[[1]], col=grey(1:100/100), axes=FALSE, box=FALSE, legend=FALSE)} 96 | if(!fast.plot){ 97 | if(num.band > 2){ 98 | X_GB <- RGB.rescale(r,num.band=3) 99 | raster::plotRGB(X_GB, r = 1, g = 2, b = 3) 100 | } 101 | if(num.band<3){raster::plot(r, axes=FALSE, box=FALSE)}}} 102 | if(!is.null(DSMmosaic)){ 103 | DSMmosaic <- raster::stack(DSMmosaic) 104 | DSMmosaic <- rotate(DSMmosaic,angle = theta) 105 | raster::plot(DSMmosaic, axes=FALSE, box=FALSE) 106 | if(extentGIS){ 107 | raster::extent(DSMmosaic)<-c(as.numeric(c(m11[1]-m22[1])), as.numeric(c(m11[1]+m22[1])), as.numeric(c(m11[2]-m22[2])), as.numeric(c(m11[2]+m22[2]))) 108 | raster::crs(DSMmosaic)<-raster::crs(mosaic) 109 | } 110 | Out<-list(rotatedMosaic=rast(r),rotatedDSM=rast(DSMmosaic)) 111 | } 112 | par(mfrow=c(1,1)) 113 | return(Out) 114 | } 115 | -------------------------------------------------------------------------------- /R/fct_fieldPolygon.R: -------------------------------------------------------------------------------- 1 | #' fieldPolygon 2 | #' 3 | #' @title Building \code{shapefile} with polygons 4 | #' 5 | #' @description The user should select points to make polygons in the image. Shapefile with polygons will be automatically built. 6 | #' Attention: \code{fieldRotate()} is not necessary. 7 | #' 8 | #' 9 | #' @param mosaic object of class stack. 10 | #' @param nPolygon number of polygons. 11 | #' @param nPoint number of points necessary to select field boundaries or area to remove (4 >= nPoint <= 50). 12 | #' @param polygonID a vector with polygon names with same order of drawing. If is NULL the ID will be the sequence of drawing. 13 | #' @param polygonData data frame with polygon ID and all attributes of each polygon (Traits as columns and polygon as rows). 14 | #' @param ID the column in polygonData with polygons names (ID) which the data will be combined with fieldShape. 15 | #' @param cropPolygon if TRUE the mosaic will be crooped using polygons shape. 16 | #' @param remove if TRUE the selected area will be removed from the image. 17 | #' @param plot if is TRUE the crop image and fieldShape will be plotted. 18 | #' @param fast.plot if TRUE only the grey scale image will be plotted as reference (faster approach). 19 | #' @param extent if is TRUE the entire image area will be the fieldShape (one unique plot). 20 | #' 21 | #' @importFrom raster raster projection plotRGB res projectRaster crs extent 22 | #' @importFrom raster atan2 crop rasterToPolygons mask extract clump drawLine drawPoly xyFromCell 23 | #' @importFrom graphics abline axis lines par plot points locator legend 24 | #' @importFrom grDevices grey rgb col2rgb 25 | #' @importFrom sp bbox Polygons Polygon SpatialPolygonsDataFrame SpatialPolygons spsample SpatialPointsDataFrame over proj4string 26 | #' @importFrom methods as 27 | #' 28 | #' @return A list with two element 29 | #' \itemize{ 30 | #' \item The function returns the \code{fieldShape} format \code{SpatialPolygonsDataFrame} with plots 31 | #' numbered by the sequence of drawings and a new reduced image with format \code{stack}. 32 | #' The \code{polygonID} parameter can be used to identify each polygon. 33 | #' } 34 | #' 35 | #' 36 | #' @export 37 | fieldPolygon <- function(mosaic, nPolygon = 1, nPoint = 4, polygonID = NULL, polygonData = NULL, ID = NULL, 38 | cropPolygon = FALSE, remove = FALSE, plot = TRUE, fast.plot = FALSE, extent = FALSE) { 39 | mosaic <- stack(mosaic) 40 | num.band <- length(mosaic@layers) 41 | print(paste(num.band, " layers available", sep = "")) 42 | if(!extent){ 43 | if (nPoint < 4 | nPoint > 50) { 44 | stop("nPoint must be >= 4 and <= 50") 45 | } 46 | par(mfrow = c(1, 2)) 47 | if (fast.plot) { 48 | raster::plot(mosaic[[1]], col = grey(1:100/100), axes = FALSE, 49 | box = FALSE, legend = FALSE) 50 | } 51 | if (!fast.plot) { 52 | if (num.band > 2) { 53 | plotRGB(RGB.rescale(mosaic, num.band = 3), r = 1, 54 | g = 2, b = 3) 55 | } 56 | if (num.band < 3) { 57 | raster::plot(mosaic, axes = FALSE, box = FALSE) 58 | } 59 | } 60 | for(np in 1:nPolygon){ 61 | print(paste("Select ", nPoint, " points around of polygon (",np,") in the plots space.", 62 | sep = "")) 63 | c1 <- NULL 64 | for (i in 1:nPoint) { 65 | c1.1 <- locator(type = "p", n = 1, col = np, pch = 19) 66 | c1 <- rbind(c1, c(c1.1$x, c1.1$y)) 67 | } 68 | c1 <- rbind(c1, c1[1, ]) 69 | colnames(c1) <- c("x", "y") 70 | lines(c1, col = np, type = "l", lty = 2, lwd = 3) 71 | p1 <- Polygons(list(Polygon(c1)), "x") 72 | f1 <- SpatialPolygonsDataFrame(SpatialPolygons(list(p1)), 73 | data.frame(z = 1, row.names = c("x"))) 74 | raster::projection(f1) <- raster::projection(mosaic) 75 | if(np==1){fieldShape<-f1} 76 | if(np!=1){fieldShape<-rbind(fieldShape,f1)} 77 | } 78 | if(cropPolygon){ 79 | print("This step takes time, please wait ... cropping") 80 | r <- mask(x = mosaic, mask = fieldShape, inverse = remove) 81 | } 82 | if(!cropPolygon){ 83 | r <- crop(x = mosaic, y = fieldShape) 84 | } 85 | r <- stack(r) 86 | fieldShape@data <- data.frame(polygonID = as.character(seq(1,nPolygon))) 87 | if (!is.null(polygonID)) { 88 | if (length(polygonID)!=nPolygon) { 89 | stop("Number of polygonID is different than nPolygon") 90 | } 91 | fieldShape@data <- data.frame(polygonID = as.character(c(t(polygonID)))) 92 | } 93 | if (!is.null(polygonData)) { 94 | polygonData <- as.data.frame(polygonData) 95 | if (is.null(ID)) { 96 | stop("Choose one ID (column) to combine polygonData with fiedShape") 97 | } 98 | if (length(ID) > 1) { 99 | stop("Choose only one ID") 100 | } 101 | if (is.null(polygonID)) { 102 | stop("polygonID is necessary") 103 | } 104 | if (!as.character(ID) %in% as.character(colnames(polygonData))) { 105 | stop(paste("ID: ", ID, " is not valid.")) 106 | } 107 | polygonData$polygonID <- as.character(polygonData[, colnames(polygonData) == 108 | ID]) 109 | fieldShape@data <- plyr::join(fieldShape@data, polygonData, 110 | by = "polygonID") 111 | } 112 | raster::projection(fieldShape) <- raster::projection(r) 113 | Out <- list(fieldShape = fieldShape, cropField = r)} 114 | if(extent){ 115 | r <- stack(mosaic) 116 | fieldShape <- as(extent(r), 'SpatialPolygons') 117 | fieldShape <- SpatialPolygonsDataFrame(fieldShape,data.frame(z = 1)) 118 | raster::projection(fieldShape) <- raster::projection(r) 119 | Out <- list(fieldShape = fieldShape) 120 | } 121 | if (plot) { 122 | if (fast.plot) { 123 | raster::plot(r[[1]], col = grey(1:100/100), axes = FALSE, 124 | box = FALSE, legend = FALSE) 125 | sp::plot(fieldShape, add = T) 126 | } 127 | if (!fast.plot) { 128 | if (num.band > 2) { 129 | plotRGB(RGB.rescale(r, num.band = 3), r = 1, 130 | g = 2, b = 3) 131 | sp::plot(fieldShape, add = T) 132 | } 133 | if (num.band < 3) { 134 | raster::plot(r, axes = FALSE, box = FALSE) 135 | sp::plot(fieldShape, add = T) 136 | } 137 | } 138 | } 139 | par(mfrow = c(1, 1)) 140 | return(Out) 141 | } 142 | -------------------------------------------------------------------------------- /R/fct_fieldObject.R: -------------------------------------------------------------------------------- 1 | #' fieldObject 2 | #' 3 | #' @title Evaluate object area, "x" distance, "y" distance, number, extent, etc. 4 | #' 5 | #' @description Calculating the object dimensions (e.g., area, "x" distance, "y" distance, number, extent, etc.) 6 | #' in the entire mosaic or per plot using the \code{\link{fieldShape}} file. 7 | #' @param mosaic object mask of class stack from the function \code{\link{fieldMask}}. 8 | #' @param fieldShape evaluate the object per plot using the fieldShape as reference. 9 | #' If fieldShape=NULL, the object will be evaluated directly for the entire original image. 10 | #' @param minArea used to set the minimum size percentage of plant canopy (to remove weeds and more). 11 | #' @param areaValue referent value of object area in the image. 12 | #' @param watershed if TRUE the "watershed" algorithm will be used to differentiate objects that are touching each other. 13 | #' @param dissolve if TRUE, polygons with the same attribute value will be dissolved into multi-polygon regions. 14 | #' This option requires the rgeos package. 15 | #' @param n.rem number of objects that should be removed by decreasing size (n.rem=1 is the background). 16 | #' @param na.rm logical. Should missing values (including NaN) be removed?. 17 | #' @param plot if TRUE the crop image and fieldShape will be plotted. 18 | #' 19 | #' @importFrom raster raster projection values 20 | #' @importFrom methods slot 21 | #' @importFrom stats dist 22 | #' 23 | #'@return A list with elements: 24 | #' \itemize{ 25 | #' \item \code{mosaic} (cropped by plot) 26 | #' \item \code{Dimension} (area, x.dist, y.dist) 27 | #' \item \code{numObjects} (number of objects) 28 | #' \item \code{Objects} (all objects polygon shape) 29 | #' \item \code{Polygons} (all extent polygons shape) 30 | #' \item \code{single.obj} (single object polygon shape) 31 | #' \item \code{obj.extent} (each object extent) 32 | #' \item \code{x.position} (coordinates of "x" length per object) 33 | #' \item \code{y.position} (coordinates of "y" length per object). 34 | #' } 35 | #' 36 | #' 37 | #' @export 38 | fieldObject <- function(mosaic, fieldShape = NULL, minArea = 0.01, areaValue = 0, watershed = FALSE, 39 | dissolve = TRUE, n.rem = 1, na.rm = FALSE, plot = TRUE) { 40 | mosaic <- stack(mosaic) 41 | num.band <- length(mosaic@layers) 42 | print(paste(num.band, " layer available", sep = "")) 43 | if (num.band > 1) { 44 | stop("Only mask mosaic with values of 1 and 0 can be evaluated, please use the mask output from fieldMask()") 45 | } 46 | if (!areaValue %in% c(1, 0)) { 47 | stop("The value must be 1 or 0 to represent the objects in the mask mosaic, please use the mask output from fieldMask()") 48 | } 49 | if (na.rm) { 50 | mosaic[is.na(mosaic)] <- c(0, 1)[c(0, 1) != areaValue] 51 | } 52 | print("Identifying objects ... ") 53 | if (is.null(fieldShape)){ 54 | r <- stack(mosaic) 55 | fieldShape <- as(extent(r), "SpatialPolygons") 56 | fieldShape <- SpatialPolygonsDataFrame(fieldShape, data.frame(z = 1)) 57 | raster::projection(fieldShape) <- raster::projection(r)} 58 | if (plot) { 59 | par(mfrow=c(1,1)) 60 | raster::plot(mosaic, col = grey(1:100/100), axes = FALSE, 61 | box = FALSE, legend=FALSE) 62 | sp::plot(fieldShape, add = T) 63 | } 64 | if(watershed){ 65 | names(mosaic) <- "mask" 66 | mask <- raster::as.matrix(mosaic$mask) == areaValue 67 | dd <- distmap(mask) 68 | mosaic$watershed <- watershed(dd) 69 | n.obj<-unique(values(mosaic$watershed))[-1] 70 | if (plot) { 71 | par(mfrow=c(1,1)) 72 | raster::plot(mosaic$watershed, col = grey((length(n.obj)+10):1/(length(n.obj)+10)), axes = FALSE, 73 | box = FALSE, add=T, legend=FALSE) 74 | sp::plot(fieldShape, add = T) 75 | }} 76 | Out<-list() 77 | numObjects<-NULL 78 | for(s1 in 1:dim(fieldShape)[1]){ 79 | single <- fieldShape[s1, ] 80 | CropPlot <- crop(x = mosaic, y = single) 81 | if(watershed){ 82 | n.obj <- as.numeric(names(table(values(CropPlot$watershed))[order(table(values(CropPlot$watershed)),decreasing = T)]))[-c(1:n.rem)] 83 | SP <-rasterToPolygons(clump(CropPlot$watershed==n.obj[1]), dissolve = dissolve) 84 | if(length(n.obj)>1){ 85 | for(m1 in 2:length(n.obj)){ 86 | SP <- rbind(SP,rasterToPolygons(clump(CropPlot$watershed==n.obj[m1]), dissolve = dissolve)) 87 | }} 88 | SP_df<- as.data.frame(sapply(slot(SP, "polygons"), function(x) slot(x, "ID"))) 89 | row.names(SP_df) <- sapply(slot(SP, "polygons"), function(x) slot(x, "ID")) 90 | colnames(SP_df) <- "ID" 91 | SP <- SpatialPolygonsDataFrame(SP, data =SP_df) 92 | } 93 | if(!watershed){ 94 | SP <- rasterToPolygons(clump(CropPlot==areaValue), dissolve = dissolve) 95 | } 96 | sps2 <- lapply(SP@polygons, function(x) SpatialPolygons(list(x))) 97 | print("Taking measurements...") 98 | obj.extent<-list() 99 | x.position<-list() 100 | y.position<-list() 101 | single.obj<-list() 102 | Dimension<-NULL 103 | Polygons<-NULL 104 | for(f1 in 1:length(sps2)){ 105 | sps3<-sps2[[f1]] 106 | raster::projection(sps3)<-raster::projection(mosaic) 107 | P<-rasterToPolygons(raster(extent(sps3)),dissolve=TRUE) 108 | area<-raster::area(sps3) 109 | if(area>minArea){ 110 | xy1<-cbind(x=c(extent(sps3)[c(1)], 111 | extent(sps3)[c(2)]), 112 | y=c(sum(extent(sps3)[c(3,4)])/2, 113 | sum(extent(sps3)[c(3,4)])/2)) 114 | xy2<-cbind(x=c(sum(extent(sps3)[c(1,2)])/2, 115 | sum(extent(sps3)[c(1,2)])/2), 116 | y=c(extent(sps3)[c(3)], 117 | extent(sps3)[c(4)])) 118 | obj.extent[[f1]]<-extent(sps3) 119 | x.position[[f1]]<-xy1 120 | y.position[[f1]]<-xy2 121 | Dimension<-rbind(Dimension,c(area=area,x.dist=dist(xy1),y.dist=dist(xy2))) 122 | single.obj[[f1]]<-sps3 123 | if(!is.null(Polygons)){Polygons<-rbind(Polygons,P);Objects<-rbind(Objects,sps3)} 124 | if(is.null(Polygons)){Polygons<-P;Objects<-sps3} 125 | if (plot) { 126 | raster::plot(P, add=T, border="blue") 127 | lines(xy1,col="red",lty=2) 128 | lines(xy2,col="red",lty=2)} 129 | } 130 | } 131 | Objects_df<- as.data.frame(sapply(slot(Objects, "polygons"), function(x) slot(x, "ID"))) 132 | row.names(Objects_df) <- sapply(slot(Objects, "polygons"), function(x) slot(x, "ID")) 133 | colnames(Objects_df) <- "ID" 134 | Objects <- SpatialPolygonsDataFrame(Objects, data =Objects_df) 135 | Polygons_df<- as.data.frame(sapply(slot(Polygons, "polygons"), function(x) slot(x, "ID"))) 136 | row.names(Polygons_df) <- sapply(slot(Polygons, "polygons"), function(x) slot(x, "ID")) 137 | colnames(Polygons_df) <- "ID" 138 | Polygons <- SpatialPolygonsDataFrame(Polygons, data =Polygons_df) 139 | print(paste("number of objects on plot ",s1,": ",length(Objects),sep = "")) 140 | if(dim(fieldShape)[1]==1){Out<-list(mosaic=mosaic,Dimension=as.data.frame(Dimension),numObjects=c(numObjects,length(Objects)),Objects=Objects,Polygons=Polygons,single.obj=single.obj,obj.extent=obj.extent,x.position=x.position,y.position=y.position)} 141 | if(dim(fieldShape)[1]!=1){Out[[s1]]<-list(mosaic=CropPlot,Dimension=as.data.frame(Dimension),numObjects=c(numObjects,length(Objects)),Objects=Objects,Polygons=Polygons,single.obj=single.obj,obj.extent=obj.extent,x.position=x.position,y.position=y.position)} 142 | } 143 | if(dim(fieldShape)[1]!=1){names(Out)<-fieldShape$fieldID} 144 | if(dim(fieldShape)[1]!=1){if("PlotName"%in%as.character(names(fieldShape))){names(Out)<-fieldShape$PlotName}} 145 | return(Out) 146 | } 147 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## [FIELDimageR](https://github.com/OpenDroneMap/FIELDimageR): A Tool to Analyze Images From Agricultural Field Trials and Lab in [R](https://www.r-project.org). 3 | 4 | > This package is a compilation of functions to analyze orthomosaic images from research fields. To prepare the image it first allows to crop the image, remove soil and weeds and rotate the image. The package also builds a plot shapefile in order to extract information for each plot to evaluate different wavelengths, vegetation indices, stand count, canopy percentage, and plant height. 5 | 6 |

7 | 8 |

9 | 10 |