Skip to content

[proto] Added functional rotate_bounding_box op #5638

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 23, 2022

Conversation

vfdev-5
Copy link
Collaborator

@vfdev-5 vfdev-5 commented Mar 17, 2022

Related to #5514

Description:

  • Added functional rotate_bounding_box op
  • Added warning if expand=True and center is not None and disabled provided center
  • Added tests

Results on synthetic images/bboxes:

Code
import numpy as np

import torch
import torchvision
from torchvision.prototype import features
from torchvision.prototype.transforms.functional import rotate_bounding_box, rotate_image_tensor


size = (64, 72)
# xyxy format
in_boxes = [
    [10, 15, 25, 35],
    [50, 5, 70, 22],
    [45, 46, 56, 62],
]

im1 = 255 * np.ones(size + (3, ), dtype=np.uint8)
for in_box in in_boxes:
    im1[in_box[1]:in_box[3], in_box[0]:in_box[2], :] = (127, 127, 127)
    
t_im1 = torch.tensor(im1).permute(2, 0, 1).view(1, 3, *size)

in_boxes = features.BoundingBox(
    in_boxes, format=features.BoundingBoxFormat.XYXY, image_size=size
)
    
angle = 22
center = None
expand = True

out_boxes = rotate_bounding_box(
    in_boxes, 
    in_boxes.format,
    in_boxes.image_size,
    angle,
    expand=expand,
    center=center
)
print(out_boxes)

t_im2 = rotate_image_tensor(t_im1, angle, expand=expand, center=center)


import cv2
import matplotlib.pyplot as plt
%matplotlib inline


plt.figure(figsize=(14, 10))

plt.subplot(1,2,1)
plt.title("Input image + bboxes")
r1 = t_im1[0, ...].permute(1, 2, 0).contiguous().cpu().numpy()
for in_box in in_boxes:    
    r1 = cv2.rectangle(r1, (in_box[0].item(), in_box[1].item()), (in_box[2].item(), in_box[3].item()), (255, 127, 0))
plt.imshow(r1)


plt.subplot(1,2,2)
plt.title("Output image + bboxes")
r2 = t_im2[0, ...].permute(1, 2, 0).contiguous().cpu().numpy()
for out_box in out_boxes:
    out_box = np.round(out_box.cpu().numpy()).astype("int32")
    r2 = cv2.rectangle(r2, (out_box[0], out_box[1]), (out_box[2], out_box[3]), (255, 127, 0), 0)
plt.imshow(r2)

image

Compare to D2:

TL;DR: matches the behaviour if expand=False and ~1 pixel difference otherwise.
This is due to cast to int : facebookresearch/detectron2@63ea900#diff-98378cbc4207da5ac829ceff03d62c48abbfc564911ac7d64c2f1d137b4f3474R130 and the way how affine matrix is computed : facebookresearch/detectron2@63ea900#diff-98378cbc4207da5ac829ceff03d62c48abbfc564911ac7d64c2f1d137b4f3474R171

Code
import numpy as np


size = image_size = (64, 64)
in_boxes = [
    [1, 1, 5, 5],
    [1, image_size[0] - 6, 5, image_size[0] - 2],
    [image_size[1] - 6, image_size[0] - 6, image_size[1] - 2, image_size[0] - 2],    
    [image_size[1] // 2 - 10, image_size[0] // 2 - 10, image_size[1] // 2 + 10, image_size[0] // 2 + 10],
]

im1 = 255 * np.ones(size + (3, ), dtype=np.uint8)
for in_box in in_boxes:
    im1[in_box[1]:in_box[3], in_box[0]:in_box[2], :] = (127, 127, 127)


angle = 34
expand = False
center = None if expand else [12, 23]

from detectron2.data.transforms import RotationTransform, AugmentationList
from detectron2.data.transforms import AugInput
import cv2

inpt = AugInput(im1, boxes=np.array(in_boxes, dtype="float32"))

augs = AugmentationList([RotationTransform(*size, angle, expand=expand, center=center, interp=cv2.INTER_NEAREST), ])
out = augs(inpt)
print(inpt.boxes)

import torch
import torchvision
from torchvision.prototype import features
from torchvision.prototype.transforms.functional import rotate_bounding_box, rotate_image_tensor


t_im1 = torch.tensor(im1).permute(2, 0, 1).view(1, 3, *size)

in_boxes = features.BoundingBox(
    in_boxes, format=features.BoundingBoxFormat.XYXY, image_size=size
)
    

out_boxes = rotate_bounding_box(
    in_boxes, 
    in_boxes.format,
    in_boxes.image_size,
    angle,
    expand=expand,
    center=center
)

from torchvision.prototype.transforms.functional import rotate_image_tensor

t_im2 = rotate_image_tensor(t_im1, angle, expand=expand, center=center)

import cv2
import matplotlib.pyplot as plt
%matplotlib inline


plt.figure(figsize=(14, 8))
plt.subplot(1,2,1)
plt.title("D2 input: image + bboxes")
r1 = im1
for in_box in in_boxes:    
    r1 = cv2.rectangle(r1, (in_box[0].item(), in_box[1].item()), (in_box[2].item(), in_box[3].item()), (255, 127, 0))
plt.imshow(r1)

ax = plt.subplot(1,2,2)
plt.title(f"D2 output: image + bboxes, angle={angle}, expand={expand}")
r2 = inpt.image
for out_box in inpt.boxes:
    out_box = np.round(out_box).astype("int32")
    r2 = cv2.rectangle(r2, (out_box[0], out_box[1]), (out_box[2], out_box[3]), (255, 127, 0), 1)
plt.imshow(r2)

plt.figure(figsize=(14, 8))

ax = plt.subplot(1,2,1)
plt.title("TV input: image + bboxes")
r1 = t_im1[0, ...].permute(1, 2, 0).contiguous().cpu().numpy()
for in_box in in_boxes:    
    r1 = cv2.rectangle(r1, (in_box[0].item(), in_box[1].item()), (in_box[2].item(), in_box[3].item()), (255, 127, 0))
plt.imshow(r1)


ax = plt.subplot(1,2,2)
plt.title(f"TV output: image + bboxes, angle={angle}, expand={expand}")
r2 = t_im2[0, ...].permute(1, 2, 0).contiguous().cpu().numpy()
for out_box in out_boxes:
    out_box = np.round(out_box.cpu().numpy()).astype("int32")
    r2 = cv2.rectangle(r2, (out_box[0], out_box[1]), (out_box[2], out_box[3]), (255, 127, 0), 0)
plt.imshow(r2)

@facebook-github-bot
Copy link

facebook-github-bot commented Mar 17, 2022

💊 CI failures summary and remediations

As of commit d355c7b (more details on the Dr. CI page):


None of the CI failures appear to be your fault 💚



🚧 3 ongoing upstream failures:

These were probably caused by upstream breakages that are not fixed yet.


This comment was automatically generated by Dr. CI (expand for details).

Please report bugs/suggestions to the (internal) Dr. CI Users group.

Click here to manually regenerate this comment.

@vfdev-5 vfdev-5 marked this pull request as draft March 17, 2022 18:05
@vfdev-5 vfdev-5 marked this pull request as ready for review March 18, 2022 09:13
@vfdev-5 vfdev-5 requested a review from datumbox March 21, 2022 12:01
Copy link
Contributor

@datumbox datumbox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! LGTM with a couple of non-blocking nits for your consideration.

@vfdev-5 vfdev-5 merged commit be462be into pytorch:main Mar 23, 2022
@vfdev-5 vfdev-5 deleted the proto-bbox-rotate branch March 23, 2022 11:10
facebook-github-bot pushed a commit that referenced this pull request Apr 5, 2022
Summary:
* [proto] Added functional rotate_bounding_box op

* Fix mypy

* Apply suggestions from code review

(Note: this ignores all push blocking failures!)

Reviewed By: datumbox

Differential Revision: D35216794

fbshipit-source-id: ce3f1421d9b0671d2b00d1fb2d1f86e0a0eddabb

Co-authored-by: Vasilis Vryniotis <[email protected]>
Co-authored-by: Vasilis Vryniotis <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants