Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ Imports:
withr,
cli,
glue,
zeallot
zeallot,
Rcpp (>= 1.0.0)
Suggests:
arrow,
magick,
Expand All @@ -66,7 +67,9 @@ Suggests:
knitr,
rmarkdown
BugReports: https://github.com/mlverse/torchvision/issues
LinkingTo: Rcpp
Collate:
'RcppExports.R'
'collection-catalog.R'
'folder-dataset.R'
'collection-rf100-doc.R'
Expand Down Expand Up @@ -115,6 +118,7 @@ Collate:
'models-vit.R'
'ops-box_convert.R'
'ops-boxes.R'
'box_operations_cpp.R'
'transforms-array.R'
'transforms-defaults.R'
'transforms-generics.R'
Expand Down
4 changes: 4 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ S3method(transform_vflip,torch_tensor)
export(base_loader)
export(batched_nms)
export(box_area)
export(box_area_cpp)
export(box_area_fast)
export(box_convert)
export(box_iou)
export(caltech101_dataset)
Expand Down Expand Up @@ -248,6 +250,7 @@ export(voc_segmentation_classes)
export(whoi_plankton_dataset)
export(whoi_small_coralnet_dataset)
export(whoi_small_plankton_dataset)
importFrom(Rcpp,evalCpp)
importFrom(grDevices,dev.off)
importFrom(graphics,polygon)
importFrom(jsonlite,fromJSON)
Expand Down Expand Up @@ -305,3 +308,4 @@ importFrom(torch,torch_zeros_like)
importFrom(utils,read.delim)
importFrom(utils,tail)
importFrom(zeallot,"%<-%")
useDynLib(torchvision, .registration = TRUE)
20 changes: 20 additions & 0 deletions R/RcppExports.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by using Rcpp::compileAttributes() -> do not edit by hand
# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393

#' Calculate Box Areas (C++)
#'
#' Calculates bounding box areas using C++. Provides performance benefits
#' for very large batches of boxes when working with plain matrices.
#'
#' @param boxes Numeric matrix with N rows and 4 columns (x1, y1, x2, y2)
#' @return Numeric vector of areas
#'
#' @details
#' This is a low-level function. Most users should use \code{box_area_fast()}
#' or \code{box_area()} instead.
#'
#' @export
box_area_cpp <- function(boxes) {
.Call(`_torchvision_box_area_cpp`, boxes)
}

55 changes: 55 additions & 0 deletions R/box_operations_cpp.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#' @useDynLib torchvision, .registration = TRUE
#' @importFrom Rcpp evalCpp
NULL

#' Box Area Calculation (Rcpp demonstration)
#'
#' Alternative C++ implementation of box area calculation. This demonstrates
#' Rcpp integration and provides a performance boost for very large batches
#' (N > 100k boxes) when working with plain matrices instead of torch tensors.
#'
#' @param boxes Matrix (N x 4) with box coordinates (x1, y1, x2, y2), or
#' torch tensor (will be converted to matrix)
#' @return Vector of areas (or torch tensor if input was torch tensor)
#'
#' @details
#' For most use cases, the standard \code{\link{box_area}} function is recommended.
#' This C++ version is useful when:
#' \itemize{
#' \item Working with very large matrices (N > 100k)
#' \item Input is already a plain R matrix
#' \item Avoiding torch overhead is important
#' }
#'
#' @examples
#' \dontrun{
#' # For normal use, prefer box_area()
#' boxes_tensor <- torch_tensor(matrix(c(0,0,10,10), ncol=4))
#' box_area(boxes_tensor)
#'
#' # This function is faster for large plain matrices
#' boxes_matrix <- matrix(runif(1e6 * 4), ncol=4)
#' box_area_fast(boxes_matrix)
#' }
#'
#' @seealso \code{\link{box_area}}
#' @export
box_area_fast <- function(boxes) {
is_torch <- inherits(boxes, "torch_tensor")

if (is_torch) {
boxes <- as.matrix(boxes$cpu())
} else if (is.data.frame(boxes)) {
boxes <- as.matrix(boxes)
} else if (!is.matrix(boxes)) {
stop("boxes must be a matrix, data.frame, or torch tensor")
}

areas <- box_area_cpp(boxes)

if (is_torch && requireNamespace("torch", quietly = TRUE)) {
areas <- torch::torch_tensor(areas, dtype = torch::torch_float())
}

areas
}
22 changes: 22 additions & 0 deletions man/box_area_cpp.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 44 additions & 0 deletions man/box_area_fast.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions src/RcppExports.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Generated by using Rcpp::compileAttributes() -> do not edit by hand
// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393

#include <Rcpp.h>

using namespace Rcpp;

#ifdef RCPP_USE_GLOBAL_ROSTREAM
Rcpp::Rostream<true>& Rcpp::Rcout = Rcpp::Rcpp_cout_get();
Rcpp::Rostream<false>& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get();
#endif

// box_area_cpp
NumericVector box_area_cpp(NumericMatrix boxes);
RcppExport SEXP _torchvision_box_area_cpp(SEXP boxesSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< NumericMatrix >::type boxes(boxesSEXP);
rcpp_result_gen = Rcpp::wrap(box_area_cpp(boxes));
return rcpp_result_gen;
END_RCPP
}

static const R_CallMethodDef CallEntries[] = {
{"_torchvision_box_area_cpp", (DL_FUNC) &_torchvision_box_area_cpp, 1},
{NULL, NULL, 0}
};

RcppExport void R_init_torchvision(DllInfo *dll) {
R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
R_useDynamicSymbols(dll, FALSE);
}
Binary file added src/RcppExports.o
Binary file not shown.
34 changes: 34 additions & 0 deletions src/box_operations.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include <Rcpp.h>
using namespace Rcpp;

//' Calculate Box Areas (C++)
//'
//' Calculates bounding box areas using C++. Provides performance benefits
//' for very large batches of boxes when working with plain matrices.
//'
//' @param boxes Numeric matrix with N rows and 4 columns (x1, y1, x2, y2)
//' @return Numeric vector of areas
//'
//' @details
//' This is a low-level function. Most users should use \code{box_area_fast()}
//' or \code{box_area()} instead.
//'
//' @export
// [[Rcpp::export]]
NumericVector box_area_cpp(NumericMatrix boxes) {
int n = boxes.nrow();

if (boxes.ncol() != 4) {
stop("Expected 4 columns for box coordinates");
}

NumericVector areas(n);

for (int i = 0; i < n; i++) {
double w = boxes(i, 2) - boxes(i, 0);
double h = boxes(i, 3) - boxes(i, 1);
areas[i] = (w < 0 || h < 0) ? 0.0 : w * h;
}

return areas;
}
Binary file added src/box_operations.o
Binary file not shown.
Binary file added src/torchvision.dll
Binary file not shown.
56 changes: 56 additions & 0 deletions tests/testthat/test-box-cpp.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
context("Rcpp Box Operations")

test_that("box_area_cpp works correctly", {
boxes <- matrix(c(
0, 0, 10, 10,
5, 5, 15, 20,
0, 0, 100, 50
), ncol = 4, byrow = TRUE)

areas <- box_area_cpp(boxes)
expect_equal(areas, c(100, 150, 5000))
})

test_that("single box works", {
boxes <- matrix(c(0, 0, 20, 30), ncol = 4)
expect_equal(box_area_cpp(boxes), 600)
})

test_that("float coordinates work", {
boxes <- matrix(c(0.5, 0.5, 10.5, 10.5), ncol = 4)
expect_equal(box_area_cpp(boxes), 100)
})

test_that("invalid boxes return zero", {
boxes <- matrix(c(10, 10, 5, 5), ncol = 4)
expect_equal(box_area_cpp(boxes), 0)
})

test_that("wrong dimensions throw error", {
boxes <- matrix(c(0, 0, 10), ncol = 3)
expect_error(box_area_cpp(boxes))
})

test_that("wrapper works with matrix", {
boxes <- matrix(c(0, 0, 10, 10, 5, 5, 15, 20), ncol = 4, byrow = TRUE)
expect_equal(box_area_fast(boxes), c(100, 150))
})

test_that("wrapper works with data.frame", {
boxes_df <- data.frame(x1 = c(0, 5), y1 = c(0, 5),
x2 = c(10, 15), y2 = c(10, 20))
expect_equal(box_area_fast(boxes_df), c(100, 150))
})

test_that("wrapper validates input", {
expect_error(box_area_fast(c(0, 0, 10, 10)))
})

test_that("matches existing box_area function", {
skip_if_not_installed("torch")

boxes <- matrix(c(0, 0, 10, 10, 5, 5, 15, 20), ncol = 4, byrow = TRUE)
boxes_tensor <- torch::torch_tensor(boxes, dtype = torch::torch_float())

expect_equal(as.numeric(box_area(boxes_tensor)), box_area_fast(boxes))
})
Loading