Skip to content

Fix: item_transform_rotate keeps tight bounding boxes - #385

Open
DerrickUnleashed wants to merge 5 commits into
mlverse:mainfrom
DerrickUnleashed:fix/rotateTransform
Open

Fix: item_transform_rotate keeps tight bounding boxes#385
DerrickUnleashed wants to merge 5 commits into
mlverse:mainfrom
DerrickUnleashed:fix/rotateTransform

Conversation

@DerrickUnleashed

Copy link
Copy Markdown
Contributor
  • item_transform_rotate now rotates detection boxes around the image centre without expanding them to their enclosing AABB returns xyxyr with accumulated angle.
  • draw_bounding_boxes.image_with_rotated_box no longer zigzags between multiple boxes - each rotated box is drawn as its own tight rectangle.

Closes #382

@DerrickUnleashed
DerrickUnleashed marked this pull request as draft August 11, 2026 15:06
@DerrickUnleashed

Copy link
Copy Markdown
Contributor Author
url <- "https://upload.wikimedia.org/wikipedia/commons/b/b6/Felis_catus-cat_on_snow.jpg"
angle <- 30

resize_pair <- function(a, b, size = c(600, 600)) {
  list(transform_resize(a, size), transform_resize(b, size))
}

# ========== 1. DETECTION ITEM ==========
img <- base_loader(url) |> transform_to_tensor()
boxes <- torch_tensor(matrix(c(600, 200, 2880, 1860), ncol = 4), dtype = torch_float32())
det_item <- list(x = img, y = list(boxes = boxes, labels = "CAT"))
class(det_item) <- c("image_with_bounding_box", "list")

det_rotated <- item_transform_rotate(det_item, angle = angle)

p1 <- draw_bounding_boxes(det_item, colors = "blue", width = 10)$to(torch_float())$div(255)
p2 <- draw_bounding_boxes(det_rotated, colors = "red", width = 10)$to(torch_float())$div(255)
grid1 <- do.call(vision_make_grid, c(torch_stack(resize_pair(p1, p2)), list(scale = TRUE)))
tensor_image_browse(grid1)

# ========== 2. DETECTION DATASET ==========
ds_det <- pascal_detection_dataset(year = "2007", split = "trainval",
                                   transform = transform_to_tensor, download = TRUE)
orig_det <- ds_det[1]
ds_det_rotated <- item_transform_rotate(ds_det, angle = angle)
rot_det <- ds_det_rotated[1]

p3 <- draw_bounding_boxes(orig_det, colors = "blue", width = 5)$to(torch_float())$div(255)
p4 <- draw_bounding_boxes(rot_det, colors = "red", width = 5)$to(torch_float())$div(255)
grid2 <- do.call(vision_make_grid, c(torch_stack(resize_pair(p3, p4)), list(scale = TRUE)))
tensor_image_browse(grid2)

# ========== 3. SEGMENTATION ITEM ==========
img2 <- base_loader(url) |> transform_to_tensor()
h <- img2$shape[2]; w <- img2$shape[3]

mask1 <- torch_zeros(h, w, dtype = torch_bool()); mask1[, 1:150] <- TRUE
mask2 <- torch_zeros(h, w, dtype = torch_bool()); mask2[, 350:500] <- TRUE
masks <- torch_stack(list(mask1, mask2))
seg_item <- list(x = img2, y = list(masks = masks, labels = torch_tensor(c(1L, 2L)),
                                    image_height = h, image_width = w))
class(seg_item) <- c("image_with_segmentation_mask", "list")

seg_rotated <- item_transform_rotate(seg_item, angle = angle)

p5 <- draw_segmentation_masks(seg_item, alpha = 0.5, colors = c("red", "blue"))$to(torch_float())$div(255)
p6 <- draw_segmentation_masks(seg_rotated, alpha = 0.5, colors = c("red", "blue"))$to(torch_float())$div(255)
grid3 <- do.call(vision_make_grid, c(torch_stack(resize_pair(p5, p6)), list(scale = TRUE)))
tensor_image_browse(grid3)

# ========== 4. SEGMENTATION DATASET ==========
ds_seg <- pascal_segmentation_dataset(year = "2007", split = "trainval",
                                      transform = transform_to_tensor, download = TRUE)
orig_seg <- ds_seg[1]
ds_seg_rotated <- item_transform_rotate(ds_seg, angle = angle)
rot_seg <- ds_seg_rotated[1]

p7 <- draw_segmentation_masks(orig_seg, alpha = 0.5)$to(torch_float())$div(255)
p8 <- draw_segmentation_masks(rot_seg, alpha = 0.5)$to(torch_float())$div(255)
grid4 <- do.call(vision_make_grid, c(torch_stack(resize_pair(p7, p8)), list(scale = TRUE)))
tensor_image_browse(grid4)

DETECTION ITEM

file3413a8f4331

DETECTION DATASET

file34113846400

SEGMENTATION ITEM

file34158c14602

SEGMENTATION DATASET

file34178db5ad3

@DerrickUnleashed
DerrickUnleashed marked this pull request as ready for review August 11, 2026 15:50
@DerrickUnleashed

Copy link
Copy Markdown
Contributor Author

After fix

library(torchvision)

url <- "https://upload.wikimedia.org/wikipedia/commons/b/b6/Felis_catus-cat_on_snow.jpg"

img <- base_loader(url) |> transform_to_tensor()
boxes <- torch_tensor(matrix(c(600, 200, 2880, 1860), ncol = 4), dtype = torch_float32())
det_item <- list(x = img, y = list(boxes = boxes, labels = "CAT"))
class(det_item) <- c("image_with_bounding_box", "list")

# expand = FALSE -> same canvas, box stays tight & angle preserved
det_rotated <- det_item |> item_transform_rotate(30, expand = FALSE)
# expand = TRUE -> bigger canvas, then crop back to original size
det_rotated_expand <- det_item |>
  item_transform_rotate(30, expand = TRUE) |>
  item_transform_crop(615, 300, 2000, 3000)

# draw_bounding_boxes returns uint8; convert to float [0,1] so
# vision_make_grid(scale = TRUE) doesn't choke on in-place float ops on Byte
p3 <- draw_bounding_boxes(det_item,           colors = "black",      width = 10)$to(torch_float())$div(255)
p4 <- draw_bounding_boxes(det_rotated,        colors = "darkgreen",  width = 10)$to(torch_float())$div(255)
p5 <- draw_bounding_boxes(det_rotated_expand, colors = "darkorange", width = 10)$to(torch_float())$div(255)

grid2 <- vision_make_grid(torch_stack(list(p3, p4, p5)))
tensor_image_browse(grid2)
file34161f1791

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

item_transform_rotate shall not expand the boxes

1 participant