diff --git a/image.ContourDetector/DESCRIPTION b/image.ContourDetector/DESCRIPTION index e8c4ccf..999ea98 100644 --- a/image.ContourDetector/DESCRIPTION +++ b/image.ContourDetector/DESCRIPTION @@ -9,7 +9,7 @@ Authors@R: c( person("BNOSAC", role = "cph", comment = "R wrapper"), person("Rafael Grompone von Gioi", role = c("ctb", "cph"), email = "grompone@gmail.com", comment = "src/smooth_contours"), person("Gregory Randall", role = c("ctb", "cph"), email = "randall@fing.edu.uy", comment = "src/smooth_contours"), - person("Niccolò Marchi", role = "ctb", email = "niccolo.marchi@unipd.it")) + person("Niccolò Marchi", role = "ctb", email = "sciurusurbanus@hotmail.it")) Encoding: UTF-8 License: AGPL-3 Version: 0.1.1 @@ -19,5 +19,7 @@ LinkingTo: Rcpp Suggests: pixmap, magick, - raster + raster, + sf, + terra RoxygenNote: 7.1.1 diff --git a/image.ContourDetector/NAMESPACE b/image.ContourDetector/NAMESPACE index 97f0294..1c90b27 100644 --- a/image.ContourDetector/NAMESPACE +++ b/image.ContourDetector/NAMESPACE @@ -1,6 +1,7 @@ # Generated by roxygen2: do not edit by hand S3method(image_contour_detector,RasterLayer) +S3method(image_contour_detector,SpatRaster) S3method(image_contour_detector,matrix) S3method(plot,cld) S3method(print,cld) diff --git a/image.ContourDetector/R/contour_detector.R b/image.ContourDetector/R/contour_detector.R index 5d1bee5..9f7aa07 100644 --- a/image.ContourDetector/R/contour_detector.R +++ b/image.ContourDetector/R/contour_detector.R @@ -11,7 +11,7 @@ #' an efficient algorithm is derived producing sub-pixel contours. #' @param x a matrix of image pixel values in the 0-255 range. #' @param Q numeric value with the pixel quantization step -#' @param ... further arguments, not used yet +#' @param ... further arguments passed on to \code{image_contour_detector.matrix}, \code{\link{image_contour_detector.RasterLayer}} or \code{\link{image_contour_detector.SpatRaster}}. #' @return an object of class cld which is a list with the following elements #' \itemize{ #' \item{curves: }{The number of contour lines found} @@ -61,26 +61,6 @@ #' \} #' # End of main if statement running only if the required packages are installed #' } -#' -#' ## -#' ## working with a RasterLayer -#' ## -#' \dontshow{ -#' if(require(raster)) -#' \{ -#' } -#' \donttest{ -#' library(raster) -#' x <- raster(system.file("extdata", "landscape.tif", package="image.ContourDetector")) -#' -#' contourlines <- image_contour_detector(x) -#' image(x) -#' plot(contourlines, add = TRUE, col = "blue", lwd = 10) -#' } -#' \dontshow{ -#' \} -#' # End of main if statement running only if the required packages are installed -#' } image_contour_detector <- function(x, Q=2.0, ...){ UseMethod("image_contour_detector") } @@ -103,25 +83,130 @@ image_contour_detector.matrix <- function(x, Q=2.0, ...){ return(contourlines) } +#' @title Unsupervised Smooth Contour Lines Detection for RasterLayer objects +#' @description Unsupervised Smooth Contour Detection +#' @param x a RasterLayer object +#' @param Q numeric value with the pixel quantization step +#' @param as_sf Boolean. Set to TRUE to export lines as sf spatial objects +#' @param ... further arguments passed on to \code{image_contour_detector.matrix} +#' @return +#' In case \code{as_sf} is \code{FALSE}: an object of class cld which as described in \code{\link{image_contour_detector}}\cr +#' In case \code{as_sf} is \code{TRUE}: an object of class sf containing the detected lines, a curve ID and its length (in the same units as the SpatRaster's CRS) +#' @seealso \code{\link{image_contour_detector}} #' @export -image_contour_detector.RasterLayer <- function(x, Q=2.0, ...){ +image_contour_detector.RasterLayer <- function(x, Q=2.0, as_sf=FALSE, ...){ requireNamespace("raster") minX = raster::extent(x)[1] minY = raster::extent(x)[3] resol = raster::res(x)[1] - x = raster::as.matrix(x) + xmat = raster::as.matrix(x) - if( anyNA(x) ){ - x[is.na(x)] = 0 + if( anyNA(xmat) ){ + x[is.na(xmat)] = 0 warning("NA values found and set to 0") } - contourlines = image_contour_detector.matrix(x, Q=Q) + contourlines = image_contour_detector.matrix( xmat, Q=Q ) contourlines$data$x = contourlines$data$x * resol + minX contourlines$data$y = contourlines$data$y * resol + minY + + # export object as sf + if(isTRUE(as_sf)){ + requireNamespace("sf") + + contourlines <- contourlines$data + contourlines = sf::st_as_sf( contourlines, coords = c("x", "y"), crs = sf::st_crs(x) ) + + out = list() + + for( i in unique(contourlines$curve) ){ + ss = subset(contourlines, curve == i ) + ss = sf::st_combine(ss) + out[[length(out)+1]] = sf::st_cast( sf::st_sf(ss), "LINESTRING") + } + + contourlines = do.call(rbind,out) + contourlines$curve_ID = 1:nrow(contourlines) + contourlines$length = round( sf::st_length(contourlines), 3) + } + return(contourlines) } +#' @title Unsupervised Smooth Contour Lines Detection for SpatRaster objects +#' @param x a SpatRaster object +#' @param Q numeric value with the pixel quantization step +#' @param as_sf Boolean. Set to TRUE to export lines as sf spatial objects +#' @param ... further arguments passed on to \code{image_contour_detector.matrix} +#' @return +#' In case \code{as_sf} is \code{FALSE}: an object of class cld which as described in \code{\link{image_contour_detector}}\cr +#' In case \code{as_sf} is \code{TRUE}: an object of class sf containing the detected lines, a curve ID and its length (in the same units as the SpatRaster's CRS) +#' @seealso \code{\link{image_contour_detector}} +#' @export +#' @examples +#' \dontshow{ +#' if(require(terra) && require(sf)) +#' \{ +#' } +#' \donttest{ +#' library(terra) +#' x <- rast(system.file("extdata", "landscape.tif", package="image.ContourDetector")) +#' +#' contourlines <- image_contour_detector(x) +#' plot(x) +#' plot(contourlines, add = TRUE, col = "blue", lwd = 10) +#' +#' contourlines <- image_contour_detector(x, as_sf = TRUE) +#' } +#' \dontshow{ +#' \} +#' # End of main if statement running only if the required packages are installed +#' } +image_contour_detector.SpatRaster <- function(x, Q=2.0, as_sf=FALSE, ...){ + requireNamespace("terra") + minX = terra::ext(x)[1] + minY = terra::ext(x)[3] + resol = terra::res(x)[1] + xmat = terra::as.matrix(x, wide=TRUE) + + if(anyNA(xmat)){ + xmat[is.na(xmat)] = 0 + warning("NA values found and set to 0") + } + + contourlines = image_contour_detector.matrix( xmat, Q=Q ) + + contourlines$data$x = contourlines$data$x * resol + minX + contourlines$data$y = contourlines$data$y * resol + minY + + # export object as sf + if(isTRUE(as_sf)){ + requireNamespace("sf") + + # contourlines <- contourlines$data %>% + # sf::st_as_sf(coords = c("x", "y"), crs = sf::st_crs(x) ) %>% + # dplyr::group_by( curve ) %>% + # dplyr::summarize(do_union=FALSE) %>% + # sf::st_cast("LINESTRING") + + contourlines <- contourlines$data + contourlines = sf::st_as_sf( contourlines, coords = c("x", "y"), crs = sf::st_crs(x) ) + + out = list() + + for( i in unique(contourlines$curve) ){ + ss = subset(contourlines, curve == i ) + ss = sf::st_combine(ss) + out[[length(out)+1]] = sf::st_cast( sf::st_sf(ss), "LINESTRING") + } + + contourlines = do.call(rbind,out) + contourlines$curve_ID = 1:nrow(contourlines) + contourlines$length = round( sf::st_length(contourlines), 3) + } + return(contourlines) +} + #' @export print.cld <- function(x, ...){ diff --git a/image.ContourDetector/man/image_contour_detector.RasterLayer.Rd b/image.ContourDetector/man/image_contour_detector.RasterLayer.Rd new file mode 100644 index 0000000..d577d6e --- /dev/null +++ b/image.ContourDetector/man/image_contour_detector.RasterLayer.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/contour_detector.R +\name{image_contour_detector.RasterLayer} +\alias{image_contour_detector.RasterLayer} +\title{Unsupervised Smooth Contour Lines Detection for RasterLayer objects} +\usage{ +\method{image_contour_detector}{RasterLayer}(x, Q = 2, as_sf = FALSE, ...) +} +\arguments{ +\item{x}{a RasterLayer object} + +\item{Q}{numeric value with the pixel quantization step} + +\item{as_sf}{Boolean. Set to TRUE to export lines as sf spatial objects} + +\item{...}{further arguments passed on to \code{image_contour_detector.matrix}} +} +\value{ +an object of class cld which as described in \code{\link{image_contour_detector}} +} +\description{ +Unsupervised Smooth Contour Detection +} +\seealso{ +\code{\link{image_contour_detector}} +} diff --git a/image.ContourDetector/man/image_contour_detector.Rd b/image.ContourDetector/man/image_contour_detector.Rd index fc13ad0..602bfbb 100644 --- a/image.ContourDetector/man/image_contour_detector.Rd +++ b/image.ContourDetector/man/image_contour_detector.Rd @@ -11,7 +11,7 @@ image_contour_detector(x, Q = 2, ...) \item{Q}{numeric value with the pixel quantization step} -\item{...}{further arguments, not used yet} +\item{...}{further arguments passed on to \code{image_contour_detector.matrix}, \code{\link{image_contour_detector.RasterLayer}} or \code{\link{image_contour_detector.SpatRaster}}.} } \value{ an object of class cld which is a list with the following elements @@ -73,26 +73,6 @@ plot(contourlines) \} # End of main if statement running only if the required packages are installed } - -## -## working with a RasterLayer -## -\dontshow{ -if(require(raster)) -\{ -} -\donttest{ -library(raster) -x <- raster(system.file("extdata", "landscape.tif", package="image.ContourDetector")) - -contourlines <- image_contour_detector(x) -image(x) -plot(contourlines, add = TRUE, col = "blue", lwd = 10) -} -\dontshow{ -\} -# End of main if statement running only if the required packages are installed -} } \references{ Rafael Grompone von Gioi, and Gregory Randall, Unsupervised Smooth Contour Detection, diff --git a/image.ContourDetector/man/image_contour_detector.SpatRaster.Rd b/image.ContourDetector/man/image_contour_detector.SpatRaster.Rd new file mode 100644 index 0000000..6e136d9 --- /dev/null +++ b/image.ContourDetector/man/image_contour_detector.SpatRaster.Rd @@ -0,0 +1,47 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/contour_detector.R +\name{image_contour_detector.SpatRaster} +\alias{image_contour_detector.SpatRaster} +\title{Unsupervised Smooth Contour Lines Detection for SpatRaster objects} +\usage{ +\method{image_contour_detector}{SpatRaster}(x, Q = 2, as_sf = FALSE, ...) +} +\arguments{ +\item{x}{a SpatRaster object} + +\item{Q}{numeric value with the pixel quantization step} + +\item{as_sf}{Boolean. Set to TRUE to export lines as sf spatial objects} + +\item{...}{further arguments passed on to \code{image_contour_detector.matrix}} +} +\value{ +In case \code{as_sf} is \code{FALSE}: an object of class cld which as described in \code{\link{image_contour_detector}}\cr +In case \code{as_sf} is \code{TRUE}: an object of class sf with columns ss and length_m +} +\description{ +Unsupervised Smooth Contour Lines Detection for SpatRaster objects +} +\examples{ +\dontshow{ +if(require(terra) && require(sf)) +\{ +} +\donttest{ +library(terra) +x <- rast(system.file("extdata", "landscape.tif", package="image.ContourDetector")) + +contourlines <- image_contour_detector(x) +image(x) +plot(contourlines, add = TRUE, col = "blue", lwd = 10) + +contourlines <- image_contour_detector(x, as_sf = TRUE) +} +\dontshow{ +\} +# End of main if statement running only if the required packages are installed +} +} +\seealso{ +\code{\link{image_contour_detector}} +} diff --git a/image.LineSegmentDetector/DESCRIPTION b/image.LineSegmentDetector/DESCRIPTION index 5cb72a4..5f7c262 100644 --- a/image.LineSegmentDetector/DESCRIPTION +++ b/image.LineSegmentDetector/DESCRIPTION @@ -7,7 +7,9 @@ Maintainer: Jan Wijffels Authors@R: c( person("Jan", "Wijffels", role = c("aut", "cre", "cph"), email = "jwijffels@bnosac.be", comment = "R wrapper"), person("BNOSAC", role = "cph", comment = "R wrapper"), - person("Rafael Grompone von Gioi", role = c("ctb", "cph"), email = "grompone@gmail.com", comment = "src/lsd")) + person("Rafael Grompone von Gioi", role = c("ctb", "cph"), email = "grompone@gmail.com", comment = "src/lsd"), + person("Niccolò Marchi", role = "ctb", email = "sciurusurbanus@hotmail.it")) +Encoding: UTF-8 License: AGPL-3 Version: 0.1.0 URL: https://github.com/bnosac/image @@ -15,5 +17,7 @@ Imports: Rcpp (>= 0.12.8), sp LinkingTo: Rcpp Suggests: pixmap, - magick + magick, + sf, + terra RoxygenNote: 7.1.0 diff --git a/image.LineSegmentDetector/R/line_segment_detector.R b/image.LineSegmentDetector/R/line_segment_detector.R index f3a1025..a365965 100644 --- a/image.LineSegmentDetector/R/line_segment_detector.R +++ b/image.LineSegmentDetector/R/line_segment_detector.R @@ -30,6 +30,7 @@ #' @param union_ang_th Numeric value with angle threshold in order to union #' @param union_use_NFA Logical indicating to use NFA to union #' @param union_log_eps Detection threshold to union +#' @param as_sf Boolean. Set to TRUE to export lines as sf spatial objects (WARNING: process may take several time) #' @return an object of class lsd which is a list with the following elements #' \itemize{ #' \item{n: }{The number of found line segments} @@ -78,13 +79,46 @@ #' mat <- drop(mat) #' linesegments <- image_line_segment_detector(mat) #' plot(linesegments, lwd = 2) +#' +#' ## +#' ## working with a SpatRaster +#' ## +#' \dontshow{ +#' if(require(terra)) +#' \{ +#' } +#' \donttest{ +#' library(terra) +#' x <- rast(system.file("extdata", "landscape.tif", package="image.ContourDetector")) +#' +#' linesegments <- image_line_segment_detector(x, as_sf=TRUE) +#' plot(x) +#' plot(linesegments, add = TRUE, col = "blue", lwd = 10) +#' } +#' \dontshow{ +#' \} +#' # End of main if statement running only if the required packages are installed +#' } + image_line_segment_detector <- function(x, scale = 0.8, - sigma_scale = 0.6, quant = 2.0, ang_th = 22.5, log_eps = 0.0, - density_th = 0.7, n_bins = 1024, - union = FALSE, union_min_length = 5, union_max_distance = 5, - union_ang_th=7, union_use_NFA=FALSE, union_log_eps = 0.0) { + sigma_scale = 0.6, quant = 2.0, ang_th = 22.5, log_eps = 0.0, + density_th = 0.7, n_bins = 1024, + union = FALSE, union_min_length = 5, union_max_distance = 5, + union_ang_th=7, union_use_NFA=FALSE, union_log_eps = 0.0, + as_sf=FALSE ){ + UseMethod("image_line_segment_detector") + } +#' @export +image_line_segment_detector.matrix <- function(x, scale = 0.8, + sigma_scale = 0.6, quant = 2.0, ang_th = 22.5, log_eps = 0.0, + density_th = 0.7, n_bins = 1024, + union = FALSE, union_min_length = 5, union_max_distance = 5, + union_ang_th=7, union_use_NFA=FALSE, union_log_eps = 0.0, + as_sf=FALSE ) { + + stopifnot(is.matrix(x)) linesegments <- detect_line_segments(as.numeric(x), X=nrow(x), @@ -107,6 +141,67 @@ image_line_segment_detector <- function(x, scale = 0.8, linesegments } + + +#' @export +image_line_segment_detector.SpatRaster <- function(x, scale = 0.8, + sigma_scale = 0.6, quant = 2.0, ang_th = 22.5, log_eps = 0.0, + density_th = 0.7, n_bins = 1024, + union = FALSE, union_min_length = 5, union_max_distance = 5, + union_ang_th=7, union_use_NFA=FALSE, union_log_eps = 0.0, + as_sf=FALSE ){ + requireNamespace("terra") + uprightX = terra::ext(x)[2] + uprightY = terra::ext(x)[4] + resol = terra::res(x)[1] + + xmat = t(terra::as.matrix(x,wide=TRUE)) + + if( anyNA(xmat) ){ + xmat[is.na(xmat)] = 0 + warning("NA values found and set to 0") } + + linesegments = image_line_segment_detector.matrix(xmat, scale = scale, + sigma_scale = sigma_scale, quant = quant, ang_th = ang_th, log_eps = log_eps, + density_th = density_th, n_bins = n_bins, + union = union, union_min_length = union_min_length, union_max_distance = union_max_distance, + union_ang_th=union_ang_th, union_use_NFA=union_use_NFA, union_log_eps = union_log_eps, + as_sf=as_sf ) + + # assign spatial coordinates + linesegments$lines[,1] = linesegments$lines[,1] * -resol + uprightY + linesegments$lines[,2] = linesegments$lines[,2] * -resol + uprightX + linesegments$lines[,3] = linesegments$lines[,3] * -resol + uprightY + linesegments$lines[,4] = linesegments$lines[,4] * -resol + uprightX + linesegments$lines[,5] = linesegments$lines[,5] * resol # width parameter + + # export object as sf + if(isTRUE(as_sf)){ + requireNamespace("sf") + + linesegments = as.data.frame(linesegments$lines) + + linesegments = apply(linesegments, 1, function(x) + { + v <- as.numeric(x[c(2,4,1,3)]) + m <- matrix(v, nrow = 2) + return(sf::st_sfc(sf::st_linestring(m))) + }) + print("Sf export in progress...") + linesegments = do.call(c, linesegments) + + linesegments = sf::st_sf(linesegments) + if( !is.na(sf::st_crs(x)) ){ sf::st_crs(linesegments) = sf::st_crs(x)} + st_geometry(linesegments) = 'geometry' + + linesegments$line_ID = 1:nrow(linesegments) + linesegments$length = round( sf::st_length(linesegments), 3) + } + + return(linesegments) +} + + #' @export print.lsd <- function(x, ...){ cat("Line Segment Detector", sep = "\n") @@ -118,7 +213,7 @@ print.lsd <- function(x, ...){ #' @description Plot the detected lines from the image_line_segment_detector #' @param x an object of class lsd as returned by \code{\link{image_line_segment_detector}} #' @param ... further arguments passed on to plot -#' @return invisibly a SpatialLines object with the lines +#' @return invisibly an sf object with the lines #' @export #' @method plot lsd #' @examples @@ -129,13 +224,23 @@ print.lsd <- function(x, ...){ #' plot(image) #' plot(linesegments, add = TRUE, col = "red") plot.lsd <- function(x, ...){ - requireNamespace("sp") - out <- sp::SpatialLines(lapply(seq_len(x$n), FUN=function(i){ - l <- rbind( - x$lines[i, c("x1", "y1")], - x$lines[i, c("x2", "y2")]) - sp::Lines(sp::Line(l), ID = i) - })) - sp::plot(out, ...) - invisible(out) + requireNamespace("sf") + + out = as.data.frame(x$lines) + + out = apply(out, 1, function(x) + { + v <- as.numeric(x[c(2,4,1,3)]) + m <- matrix(v, nrow = 2) + return( sf::st_sfc(sf::st_linestring(m) ) ) + }) + print("Loading in progress...") + out = do.call(c, out) + + out = sf::st_sf(out, crs = 'NA_crs_') + if( !is.na(sf::st_crs(x)) ){ sf::st_crs(out) = sf::st_crs(x)} + st_geometry(out) = 'geometry' + + plot(out$geometry, ...) + invisible(out$geometry) }