From fe38f903fe344790ff7bd847a8f59f4b38031cfd Mon Sep 17 00:00:00 2001 From: cregouby Date: Tue, 17 Sep 2024 16:01:23 +0200 Subject: [PATCH] relax the constraint for uint8 tensor dtype for the 2 display functions (#115) * add a `image display` roxygen family * relax the constraint on torch_tensor type in display functions add tests * add NEWS * use a more secure function * Revert "use a more secure function" This reverts commit 9e0fe3722838fe93a6bdd27d6077d6db63ec2e59. * remove unexpected normalization --------- Co-authored-by: Daniel Falbel --- NEWS.md | 1 + R/vision_utils.R | 37 ++++++++++++++++++++++-------- man/draw_bounding_boxes.Rd | 9 ++++++++ man/draw_keypoints.Rd | 9 ++++++++ man/draw_segmentation_masks.Rd | 9 ++++++++ man/tensor_image_browse.Rd | 13 +++++++++-- man/tensor_image_display.Rd | 13 +++++++++-- man/vision_make_grid.Rd | 9 ++++++++ tests/testthat/test-vision-utils.R | 15 ++++++++---- 9 files changed, 96 insertions(+), 19 deletions(-) diff --git a/NEWS.md b/NEWS.md index b4af264..d4553ca 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,6 @@ # torchvision (development version) +- `tensor_image_display` and `tensor_image_browse` now accept all tensor_image dtypes. (#115, @cregouby) - fix `transform_affine` help to remove confusion with `transforme_random_affine` help (#116, @cregouby) - add message translation in french (#112, @cregouby) diff --git a/R/vision_utils.R b/R/vision_utils.R index 0ba70dd..de691f4 100644 --- a/R/vision_utils.R +++ b/R/vision_utils.R @@ -12,6 +12,7 @@ NULL #' @param padding amount of padding between batch images (default 2). #' @param pad_value pixel value to use for padding. #' +#' @family image display #' @export vision_make_grid <- function(tensor, scale = TRUE, @@ -91,6 +92,7 @@ vision_make_grid <- function(tensor, #' tensor_image_browse(bboxed) #' } #' } +#' @family image display #' @export draw_bounding_boxes <- function(image, boxes, @@ -193,6 +195,7 @@ draw_bounding_boxes <- function(image, #' masked_image <- draw_segmentation_masks(image, mask, alpha = 0.2) #' tensor_image_browse(masked_image) #' } +#' @family image display #' @export draw_segmentation_masks <- function(image, masks, @@ -261,6 +264,7 @@ draw_segmentation_masks <- function(image, #' tensor_image_browse(keypoint_image) #' } #' } +#' @family image display #' @export draw_keypoints <- function(image, keypoints, @@ -314,19 +318,25 @@ draw_keypoints <- function(image, #' Display image tensor #' #' Display image tensor onto the X11 device -#' @param image `torch_tensor()` of shape (1, W, H) for grayscale image or (3, W, H) for color image, -#' of type `torch_uint8()` to display +#' @param image `torch_tensor()` of shape (1, W, H) for grayscale image or (3, W, H) for +#' color image to display #' @param animate support animations in the X11 display #' +#' @family image display #' @export tensor_image_display <- function(image, animate = TRUE) { - stopifnot("`image` is expected to be of dtype torch_uint8" = image$dtype == torch::torch_uint8()) stopifnot("Pass individual images, not batches" = image$ndim == 3) stopifnot("Only grayscale and RGB images are supported" = image$size(1) %in% c(1, 3)) - img_to_draw <- image$permute(c(2, 3, 1))$to(device = "cpu", dtype = torch::torch_long()) %>% as.array + if (image$dtype == torch::torch_uint8()) { + img_to_draw <- image$permute(c(2, 3, 1))$to(device = "cpu", dtype = torch::torch_long()) %>% + as.array() / 255 - png::writePNG(img_to_draw / 255) %>% magick::image_read() %>% magick::image_display(animate = animate) + } else { + img_to_draw <- image$permute(c(2, 3, 1))$to(device = "cpu") %>% + as.array() + } + png::writePNG(img_to_draw) %>% magick::image_read() %>% magick::image_display(animate = animate) invisible(NULL) } @@ -335,19 +345,26 @@ tensor_image_display <- function(image, animate = TRUE) { #' Display image tensor #' #' Display image tensor into browser -#' @param image `torch_tensor()` of shape (1, W, H) for grayscale image or (3, W, H) for color image, -#' of type `torch_uint8()` to display +#' @param image `torch_tensor()` of shape (1, W, H) for grayscale image or (3, W, H) for +#' color image to display #' @param browser argument passed to [browseURL] #' +#' @family image display #' @export tensor_image_browse <- function(image, browser = getOption("browser")) { - stopifnot("`image` is expected to be of dtype torch_uint8" = image$dtype == torch::torch_uint8()) stopifnot("Pass individual images, not batches" = image$ndim == 3) stopifnot("Only grayscale and RGB images are supported" = image$size(1) %in% c(1, 3)) - img_to_draw <- image$permute(c(2, 3, 1))$to(device = "cpu", dtype = torch::torch_long()) %>% as.array + if (image$dtype == torch::torch_uint8()) { + img_to_draw <- image$permute(c(2, 3, 1))$to(device = "cpu", dtype = torch::torch_long()) %>% + as.array() / 255 + + } else { + img_to_draw <- image$permute(c(2, 3, 1))$to(device = "cpu") %>% + as.array() + } - png::writePNG(img_to_draw / 255) %>% magick::image_read() %>% magick::image_browse(browser = browser) + png::writePNG(img_to_draw) %>% magick::image_read() %>% magick::image_browse(browser = browser) invisible(NULL) } diff --git a/man/draw_bounding_boxes.Rd b/man/draw_bounding_boxes.Rd index ae0f579..903495c 100644 --- a/man/draw_bounding_boxes.Rd +++ b/man/draw_bounding_boxes.Rd @@ -54,3 +54,12 @@ tensor_image_browse(bboxed) } } } +\seealso{ +Other image display: +\code{\link{draw_keypoints}()}, +\code{\link{draw_segmentation_masks}()}, +\code{\link{tensor_image_browse}()}, +\code{\link{tensor_image_display}()}, +\code{\link{vision_make_grid}()} +} +\concept{image display} diff --git a/man/draw_keypoints.Rd b/man/draw_keypoints.Rd index 75eadbc..e12893a 100644 --- a/man/draw_keypoints.Rd +++ b/man/draw_keypoints.Rd @@ -44,3 +44,12 @@ tensor_image_browse(keypoint_image) } } } +\seealso{ +Other image display: +\code{\link{draw_bounding_boxes}()}, +\code{\link{draw_segmentation_masks}()}, +\code{\link{tensor_image_browse}()}, +\code{\link{tensor_image_display}()}, +\code{\link{vision_make_grid}()} +} +\concept{image display} diff --git a/man/draw_segmentation_masks.Rd b/man/draw_segmentation_masks.Rd index 83d97a4..8ad10c9 100644 --- a/man/draw_segmentation_masks.Rd +++ b/man/draw_segmentation_masks.Rd @@ -31,3 +31,12 @@ masked_image <- draw_segmentation_masks(image, mask, alpha = 0.2) tensor_image_browse(masked_image) } } +\seealso{ +Other image display: +\code{\link{draw_bounding_boxes}()}, +\code{\link{draw_keypoints}()}, +\code{\link{tensor_image_browse}()}, +\code{\link{tensor_image_display}()}, +\code{\link{vision_make_grid}()} +} +\concept{image display} diff --git a/man/tensor_image_browse.Rd b/man/tensor_image_browse.Rd index c8468b1..09cc101 100644 --- a/man/tensor_image_browse.Rd +++ b/man/tensor_image_browse.Rd @@ -7,11 +7,20 @@ tensor_image_browse(image, browser = getOption("browser")) } \arguments{ -\item{image}{\code{torch_tensor()} of shape (1, W, H) for grayscale image or (3, W, H) for color image, -of type \code{torch_uint8()} to display} +\item{image}{\code{torch_tensor()} of shape (1, W, H) for grayscale image or (3, W, H) for +color image to display} \item{browser}{argument passed to \link{browseURL}} } \description{ Display image tensor into browser } +\seealso{ +Other image display: +\code{\link{draw_bounding_boxes}()}, +\code{\link{draw_keypoints}()}, +\code{\link{draw_segmentation_masks}()}, +\code{\link{tensor_image_display}()}, +\code{\link{vision_make_grid}()} +} +\concept{image display} diff --git a/man/tensor_image_display.Rd b/man/tensor_image_display.Rd index 6186c62..cb92182 100644 --- a/man/tensor_image_display.Rd +++ b/man/tensor_image_display.Rd @@ -7,11 +7,20 @@ tensor_image_display(image, animate = TRUE) } \arguments{ -\item{image}{\code{torch_tensor()} of shape (1, W, H) for grayscale image or (3, W, H) for color image, -of type \code{torch_uint8()} to display} +\item{image}{\code{torch_tensor()} of shape (1, W, H) for grayscale image or (3, W, H) for +color image to display} \item{animate}{support animations in the X11 display} } \description{ Display image tensor onto the X11 device } +\seealso{ +Other image display: +\code{\link{draw_bounding_boxes}()}, +\code{\link{draw_keypoints}()}, +\code{\link{draw_segmentation_masks}()}, +\code{\link{tensor_image_browse}()}, +\code{\link{vision_make_grid}()} +} +\concept{image display} diff --git a/man/vision_make_grid.Rd b/man/vision_make_grid.Rd index eb3a1da..534c35b 100644 --- a/man/vision_make_grid.Rd +++ b/man/vision_make_grid.Rd @@ -27,3 +27,12 @@ vision_make_grid( Arranges a batch of (image) tensors in a grid, with optional padding between images. Expects a 4d mini-batch tensor of shape (B x C x H x W). } +\seealso{ +Other image display: +\code{\link{draw_bounding_boxes}()}, +\code{\link{draw_keypoints}()}, +\code{\link{draw_segmentation_masks}()}, +\code{\link{tensor_image_browse}()}, +\code{\link{tensor_image_display}()} +} +\concept{image display} diff --git a/tests/testthat/test-vision-utils.R b/tests/testthat/test-vision-utils.R index 5ca77a6..ee7a874 100644 --- a/tests/testthat/test-vision-utils.R +++ b/tests/testthat/test-vision-utils.R @@ -69,15 +69,20 @@ test_that("draw_keypoints works", { test_that("tensor_image_browse works", { skip_on_cran() skip_on_ci() - # color image + # uint8 color image image <- (255 - (torch::torch_randint(low = 1, high = 200, size = c(3, 360, 360))))$to(torch::torch_uint8()) expect_no_error(tensor_image_browse(image)) - # grayscale image + # uint8 grayscale image image <- (255 - (torch::torch_randint(low = 1, high = 200, size = c(1, 360, 360))))$to(torch::torch_uint8()) expect_no_error(tensor_image_browse(image)) - # error cases : dtype - image_int16 <- image$to(torch::torch_int16()) - expect_error(tensor_image_browse(image_int16), "dtype torch_uint8") + + # float color image + image <- torch::torch_rand(size = c(3, 360, 360)) + expect_no_error(tensor_image_browse(image)) + # float grayscale image + image <- torch::torch_rand(size = c(1, 360, 360)) + expect_no_error(tensor_image_browse(image)) + # error cases : shape image <- torch::torch_randint(low = 1, high = 200, size = c(4, 3, 360, 360))$to(torch::torch_uint8()) expect_error(tensor_image_browse(image), "individual images")