From 95bde7aac8c3290a5ea10d10275823cc06e1d2ba Mon Sep 17 00:00:00 2001 From: Simon Garnier Date: Fri, 11 Aug 2023 21:36:17 +0200 Subject: [PATCH] Add affine transform matrix computation and string argument for streams. --- NAMESPACE | 1 + R/StreamClass.R | 28 ++++++++++++++++---- R/feature.R | 2 +- R/transform.R | 45 +++++++++++++++++++++++++++++++++ docs/pkgdown.yml | 2 +- docs/reference/convexHull.html | 2 +- docs/reference/fitEllipse.html | 8 +++--- docs/reference/index.html | 29 ++++++++++++--------- docs/reference/minAreaRect.html | 8 +++--- docs/sitemap.xml | 3 +++ man/getAffineTransform.Rd | 43 +++++++++++++++++++++++++++++++ man/stream.Rd | 17 ++++++++++--- src/Stream.h | 15 +++++++++++ src/transform.h | 20 +++++++++++++++ src/utils.h | 12 +++++++++ src/visionModule.cpp | 8 ++++-- 16 files changed, 209 insertions(+), 34 deletions(-) create mode 100644 man/getAffineTransform.Rd diff --git a/NAMESPACE b/NAMESPACE index 2079d710..b25ce1c5 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -145,6 +145,7 @@ export(fps) export(frame) export(full) export(gaussianBlur) +export(getAffineTransform) export(getGaborKernel) export(getPerspectiveTransform) export(getProp) diff --git a/R/StreamClass.R b/R/StreamClass.R index aad0ff61..81478f07 100644 --- a/R/StreamClass.R +++ b/R/StreamClass.R @@ -29,9 +29,18 @@ #' @description Function for creating \code{\link{Stream}} objects from video #' streams. #' -#' @param index An integer value corresponding to the index of the camera to -#' read a stream from (default: 0; 0 is usually the default webcam on most -#' computers). +#' @param ... Either: +#' \itemize{ +#' \item{index: }{An integer value corresponding to the index of the camera +#' to read a stream from (default: 0; 0 is usually the default webcam on +#' most computers). Or...} +#' \item{stream_string: }{...a character string corresponfing to the URL of +#' video stream (eg. protocol://host:port/script_name?script_params|auth) +#' or a GStreamer pipeline string in gst-launch tool format if GStreamer is +#' used as backend API. Note that each video stream or IP camera feed has +#' its own URL scheme. Please refer to the documentation of source stream +#' to know the right URL format.} +#' } #' #' @param api A character string corresponding to the API to use for reading the #' stream from the camera (see Note; default: "ANY"). @@ -77,8 +86,17 @@ #' } #' #' @export -stream <- function(index = 0, api = "ANY") { - new(Stream, index = index, api = api) +stream <- function(..., api = "ANY") { + args <- list(...) + + if (!inherits(args[[1]], "character")) + args[[1]] <- as.integer(args[[1]]) + + if (length(args) > 1) + if (inherits(args[[2]], "character")) + api <- args[[2]] + + new(Stream, args[[1]], api = api) } diff --git a/R/feature.R b/R/feature.R index 959429cb..5ff0431d 100644 --- a/R/feature.R +++ b/R/feature.R @@ -366,7 +366,7 @@ ORBkeypoints <- function(image, mask = NULL, n_features = 500, scale_factor = 1. st <- switch(score_type, "HARRIS" = 0, - "FAST" = 0, + "FAST" = 1, stop("Invalid score type.") ) diff --git a/R/transform.R b/R/transform.R index 28eccef2..ca98fcdf 100644 --- a/R/transform.R +++ b/R/transform.R @@ -453,6 +453,51 @@ getPerspectiveTransform <- function(from, to, from_dim, to_dim = from_dim) { } +#' @title Affine Transform +#' +#' @description \code{getAffineTransform} computes the matrix of an affine +#' transform from 4 pairs of corresponding points in a source and destination +#' image. +#' +#' @param from A 4x2 matrix indicating the location (x, y) of 4 points in the +#' source image. +#' +#' @param to A 4x2 matrix indicating the location (x, y) of 4 points in the +#' destination image. The order of the points must correspond to the order in +#' \code{from}. +#' +#' @param from_dim A vector which first two elements indicate the number of rows +#' and columns of the source image. +#' +#' @param to_dim A vector which first two elements indicate the number of rows +#' and columns of the destination image. If not specified, \code{from_dim} will +#' be used as a default. +#' +#' @return A 3x3 matrix. +#' +#' @author Simon Garnier, \email{garnier@@njit.edu} +#' +#' @seealso \code{\link{warpAffine}} +#' +#' @examples +#' from <- matrix(c(1, 1, 2, 5, 6, 5, 5, 1), nrow = 4, byrow = TRUE) +#' to <- matrix(c(1, 1, 1, 5, 5, 5, 5, 1), nrow = 4, byrow = TRUE) +#' getAffineTransform(from, to, c(1080, 1920), c(1080, 1920)) +#' +#' @export +getAffineTransform <- function(from, to, from_dim, to_dim = from_dim) { + if (any(dim(from) != c(4, 2)) | any(dim(to) != c(4, 2))) + stop("'from' and 'to' must be 4x2 matrices.") + + from[, 1] <- from[, 1] - 1 + from[, 2] <- -from[, 2] + from_dim[1] + to[, 1] <- to[, 1] - 1 + to[, 2] <- -to[, 2] + from_dim[1] - (from_dim[1] - to_dim[1]) + + `_getAffineTransform`(from, to) +} + + #' @title Perspective Transformation #' #' @description \code{warpPerspective} applies a perspective transformation to diff --git a/docs/pkgdown.yml b/docs/pkgdown.yml index d4024888..3e94647e 100644 --- a/docs/pkgdown.yml +++ b/docs/pkgdown.yml @@ -9,7 +9,7 @@ articles: z5_gpu: z5_gpu.html z6_queue: z6_queue.html z7_stack: z7_stack.html -last_built: 2023-08-08T12:51Z +last_built: 2023-08-09T18:21Z urls: reference: https://swarm-lab.github.io/Rvision/reference article: https://swarm-lab.github.io/Rvision/articles diff --git a/docs/reference/convexHull.html b/docs/reference/convexHull.html index 3ca17448..869052f0 100644 --- a/docs/reference/convexHull.html +++ b/docs/reference/convexHull.html @@ -98,7 +98,7 @@

Author<

Examples

convexHull(rnorm(100), rnorm(100))
-#>  [1]  3 80 86 43  5 87 76 93 30 70
+#> [1] 18 27 42 50 95 51 52
 
 
diff --git a/docs/reference/fitEllipse.html b/docs/reference/fitEllipse.html index f2b2fc0f..11aa0a99 100644 --- a/docs/reference/fitEllipse.html +++ b/docs/reference/fitEllipse.html @@ -119,16 +119,16 @@

Author<

Examples

fitEllipse(rnorm(100), rnorm(100))
 #> $angle
-#> [1] 59.89347
+#> [1] 68.14399
 #> 
 #> $height
-#> [1] 4.499532
+#> [1] 4.426012
 #> 
 #> $width
-#> [1] 3.404044
+#> [1] 3.899822
 #> 
 #> $center
-#> [1] -0.20713183 -0.01245182
+#> [1] -0.00229378 -0.37228656
 #> 
 
 
diff --git a/docs/reference/index.html b/docs/reference/index.html index 9a95aa9b..cf1fcce1 100644 --- a/docs/reference/index.html +++ b/docs/reference/index.html @@ -290,7 +290,7 @@

All functionsdestroyDisplay() destroyAllDisplays() -
Destroy Image Display
+
Destroy Image Display
dim(<Rcpp_Image>) @@ -325,7 +325,7 @@

All functionsdisplay()

-
Display Image Object
+
Display Image Object
distanceTransform() @@ -335,22 +335,22 @@

All functionsdrawArrow()

-
Draw Arrows on an Image
+
Draw Arrows on an Image
drawCircle()
-
Draw Circles on an Image
+
Draw Circles on an Image
drawEllipse()
-
Draw Ellipses on an Image
+
Draw Ellipses on an Image
drawLine()
-
Draw Lines on an Image
+
Draw Lines on an Image
drawPolyline() @@ -360,17 +360,17 @@

All functionsdrawRectangle()

-
Draw Rectangles on an Image
+
Draw Rectangles on an Image
drawRotatedRectangle()
-
Draw Rotated Rectangles on an Image
+
Draw Rotated Rectangles on an Image
drawText()
-
Draw Text on an Image
+
Draw Text on an Image
edgePreservingFilter() @@ -435,7 +435,7 @@

All functionsflip()

-
Flip an Image
+
Flip an Image
floodFill() @@ -468,6 +468,11 @@

All functionsgetAffineTransform() +

+
Affine Transform
+
+ getGaborKernel()
Gabor Filter Kernels
@@ -695,7 +700,7 @@

All functionsnewDisplay() -
Open New Image Display
+
Open New Image Display

niBlackThreshold() @@ -820,7 +825,7 @@

All functionsresize()

-
Resize an Image
+
Resize an Image
rotateScale() diff --git a/docs/reference/minAreaRect.html b/docs/reference/minAreaRect.html index 5f157e10..b336ffe4 100644 --- a/docs/reference/minAreaRect.html +++ b/docs/reference/minAreaRect.html @@ -100,16 +100,16 @@

Author<

Examples

minAreaRect(rnorm(100), rnorm(100))
 #> $angle
-#> [1] 81.68349
+#> [1] 81.83121
 #> 
 #> $height
-#> [1] 5.16795
+#> [1] 5.80548
 #> 
 #> $width
-#> [1] 4.448408
+#> [1] 4.489648
 #> 
 #> $center
-#> [1] -0.5567193  0.2278657
+#> [1] -0.6323334  0.1881826
 #> 
 
 
diff --git a/docs/sitemap.xml b/docs/sitemap.xml index c3df2791..9c31b6af 100644 --- a/docs/sitemap.xml +++ b/docs/sitemap.xml @@ -279,6 +279,9 @@ https://swarm-lab.github.io/Rvision/reference/gaussianBlur.html + + https://swarm-lab.github.io/Rvision/reference/getAffineTransform.html + https://swarm-lab.github.io/Rvision/reference/getGaborKernel.html diff --git a/man/getAffineTransform.Rd b/man/getAffineTransform.Rd new file mode 100644 index 00000000..3c66af45 --- /dev/null +++ b/man/getAffineTransform.Rd @@ -0,0 +1,43 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/transform.R +\name{getAffineTransform} +\alias{getAffineTransform} +\title{Affine Transform} +\usage{ +getAffineTransform(from, to, from_dim, to_dim = from_dim) +} +\arguments{ +\item{from}{A 4x2 matrix indicating the location (x, y) of 4 points in the +source image.} + +\item{to}{A 4x2 matrix indicating the location (x, y) of 4 points in the +destination image. The order of the points must correspond to the order in +\code{from}.} + +\item{from_dim}{A vector which first two elements indicate the number of rows +and columns of the source image.} + +\item{to_dim}{A vector which first two elements indicate the number of rows +and columns of the destination image. If not specified, \code{from_dim} will +be used as a default.} +} +\value{ +A 3x3 matrix. +} +\description{ +\code{getAffineTransform} computes the matrix of an affine + transform from 4 pairs of corresponding points in a source and destination + image. +} +\examples{ +from <- matrix(c(1, 1, 2, 5, 6, 5, 5, 1), nrow = 4, byrow = TRUE) +to <- matrix(c(1, 1, 1, 5, 5, 5, 5, 1), nrow = 4, byrow = TRUE) +getAffineTransform(from, to, c(1080, 1920), c(1080, 1920)) + +} +\seealso{ +\code{\link{warpAffine}} +} +\author{ +Simon Garnier, \email{garnier@njit.edu} +} diff --git a/man/stream.Rd b/man/stream.Rd index cf7598f3..e70ef8c9 100644 --- a/man/stream.Rd +++ b/man/stream.Rd @@ -4,12 +4,21 @@ \alias{stream} \title{Create an Object of Class \code{Stream}} \usage{ -stream(index = 0, api = "ANY") +stream(..., api = "ANY") } \arguments{ -\item{index}{An integer value corresponding to the index of the camera to -read a stream from (default: 0; 0 is usually the default webcam on most -computers).} +\item{...}{Either: +\itemize{ + \item{index: }{An integer value corresponding to the index of the camera + to read a stream from (default: 0; 0 is usually the default webcam on + most computers). Or...} + \item{stream_string: }{...a character string corresponfing to the URL of + video stream (eg. protocol://host:port/script_name?script_params|auth) + or a GStreamer pipeline string in gst-launch tool format if GStreamer is + used as backend API. Note that each video stream or IP camera feed has + its own URL scheme. Please refer to the documentation of source stream + to know the right URL format.} +}} \item{api}{A character string corresponding to the API to use for reading the stream from the camera (see Note; default: "ANY").} diff --git a/src/Stream.h b/src/Stream.h index d5fdf444..dbf45471 100644 --- a/src/Stream.h +++ b/src/Stream.h @@ -1,7 +1,9 @@ class Stream : public Capture { public: Stream(int index, std::string api); + Stream(std::string stream_string, std::string api); bool open(int index, std::string api); + bool openStr(std::string stream_string, std::string api); private: }; @@ -12,9 +14,22 @@ Stream::Stream(int index, std::string api) { } } +Stream::Stream(std::string stream_string, std::string api) { + if (!this->cap.open(stream_string, getAPIId(api))) { + Rcpp::stop("Could not open the stream."); + } +} + bool Stream::open(int index, std::string api) { if (!this->cap.open(index, getAPIId(api))) Rcpp::stop("Could not open the stream."); return true; } + +bool Stream::openStr(std::string stream_string, std::string api) { + if (!this->cap.open(stream_string, getAPIId(api))) + Rcpp::stop("Could not open the stream."); + + return true; +} diff --git a/src/transform.h b/src/transform.h index 731649b6..5d0997ae 100644 --- a/src/transform.h +++ b/src/transform.h @@ -171,6 +171,26 @@ arma::Mat< float > _getPerspectiveTransform(arma::Mat< float > from, arma::Mat< return out; } +arma::Mat< float > _getAffineTransform(arma::Mat< float > from, arma::Mat< float > to) { + arma::Mat< float > out; + + cv::Point2f from_p[] = { + cv::Point2f(from(0, 0), from(0, 1)), + cv::Point2f(from(1, 0), from(1, 1)), + cv::Point2f(from(2, 0), from(2, 1)), + cv::Point2f(from(3, 0), from(3, 1)) }; + + cv::Point2f to_p[] = { + cv::Point2f(to(0, 0), to(0, 1)), + cv::Point2f(to(1, 0), to(1, 1)), + cv::Point2f(to(2, 0), to(2, 1)), + cv::Point2f(to(3, 0), to(3, 1)) }; + + cv::Mat_< float > affineMatrix = getAffineTransform(from_p, to_p); + cv2arma(affineMatrix, out); + return out; +} + void _warpPerspective(Image& image, arma::Mat< float > m, int interpMode, int borderType, Rcpp::NumericVector borderColor, Image& target) { cv::Mat_< float > warpMatrix; diff --git a/src/utils.h b/src/utils.h index 2f040bed..be878d3a 100644 --- a/src/utils.h +++ b/src/utils.h @@ -30,6 +30,18 @@ bool ImageConst5(SEXP* args, int nargs) { return true ; } +bool StreamConst1(SEXP* args, int nargs) { + if(nargs != 2) return false; + if(TYPEOF(args[0]) != INTSXP) return false ; + return true ; +} + +bool StreamConst2(SEXP* args, int nargs) { + if(nargs != 2) return false; + if(TYPEOF(args[0]) != STRSXP) return false ; + return true ; +} + bool QueueConst1(SEXP* args, int nargs) { if(nargs != 4) return false; if(!Rf_inherits(args[0], "Rcpp_Video")) return false ; diff --git a/src/visionModule.cpp b/src/visionModule.cpp index da4d5625..6f315f99 100644 --- a/src/visionModule.cpp +++ b/src/visionModule.cpp @@ -94,8 +94,10 @@ RCPP_MODULE(class_Capture) { Rcpp::class_("Stream") .derives("Capture") - .constructor() + .constructor< int, std::string > ("", &StreamConst1) + .constructor< std::string, std::string > ("", &StreamConst2) .method("open", &Stream::open) + .method("openStr", &Stream::openStr) ; Rcpp::class_("Queue") @@ -374,7 +376,9 @@ RCPP_MODULE(methods_Transform) { function("_getRotationMatrix2D", &_getRotationMatrix2D, List::create(_["center"], _["angle"], _["scale"]), ""); function("_getPerspectiveTransform", &_getPerspectiveTransform, - List::create(_["from"], _["to"]), ""); + List::create(_["from"], _["to"]), ""); + function("_getAffineTransform", &_getAffineTransform, + List::create(_["from"], _["to"]), ""); function("_warpAffine", &_warpAffine, List::create(_["image"], _["m"], _["interpMode"], _["borderType"], _["borderColor"], _["target"]), ""); function("_warpPerspective", &_warpPerspective, List::create(_["image"], _["m"],