Skip to content
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

Added shift method #9

Merged
merged 11 commits into from
Jun 21, 2022
Merged

Conversation

harshraj22
Copy link
Contributor

Linked with issue #5

All bounding box types convert to a VocBoundingBox object. The shifting is done in the VOC coordinates, and a new object of same class is being returned. All other properties are kept as passed during the initialisation of the current object.

@devrimcavusoglu
Copy link
Owner

Thanks for the PR @harshraj22 👍 I have one minor comment, that we can accept a tuple of ints as Tuple[int, int] for shift() method, and we can give the description in the docstring like (horizontal_shift, vertical_shift), it'd be better suited for future (as potential 3d boxes implementations). It can be like the following, wdyt ?

def shift(self, shift_amount):
    horizontal_shift, vertical_shift = shift_amount
    ...

Also, can you add test cases for the new method shift() ?

@@ -34,6 +34,28 @@ def to_voc(self, return_values: bool = False) -> Union[Tuple[int, int, int, int]
return x_tl, y_tl, x_br, y_br
return BoundingBox(x_tl, y_tl, x_br, y_br, image_size=self.image_size, strict=self.strict)

def shift(self, horizontal_threshold: float, vertical_threshold: float) -> "AlbumentationsBoundingBox":
Copy link
Owner

Choose a reason for hiding this comment

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

Please, take this comment as for other methods as well.

(1) Since we convert to VOC, the type hints as well as the docstring needs to be changed, only integers are accepted since we use VOC type under the hood for shifting.
(2) We need to clearly desribe and explicitly write in the docstring that shifting is performed in pixel values not in normalized values.

Copy link
Contributor Author

@harshraj22 harshraj22 Jun 13, 2022

Choose a reason for hiding this comment

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

@devrimcavusoglu I did not understand what do you mean by "pixel values not in normalised values". Could you clarify a little more ?

Copy link
Owner

Choose a reason for hiding this comment

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

Sure. I meant unnormalized values, like those shift values (horizontal_threshold, vertical_threshold) must be always in pixel values integers because VOC style is in pixel values (x,y coordinates). For example YOLO style is (x-center, y-center, width, height) but these values are normalized (w.r.t image_size) values and not pixel coordinates.

Currently, in the implementation in this PR, regardless of the box style the box is first converted to VOC style, and addition is then applied. That's why, for example, we cannot give normalized shift values to YOLO style values (normalized). Alternatively, if we take the input shift values in that particular box style, then VOC style conversion must be omitted. Further, see the pseudo snippet below

yolo_values = [0.4046875, 0.840625, 0.503125, 0.24375]
yolo_bbox = BoundingBox.from_yolo(*yolo_values, image_size=(640,480))

# I want to shift my YOLO box
shifted_yolo_box = yolo_bbox.shift(0.07, 0.02)

Now, when I call .shift() internally these steps happen; (1) yolo_values are converted to voc values, (2) my shift values are added to the associated values 0.07, 0.02 respectively.

At step (2) yolo shift values 0.07, 0.02 are added to VOC values which are [98, 345, 420, 462] which is not desired. There are two solutions that comes up,

Solution 1 (Current)

Accept shift values as unnormalized. Convert the box values to voc, add the shift values, and return.

Solution 2

Omit converting values to VOC, directly apply the shift values and then instantiate/construct the object and return.

Both solutions are fine, and actually I guided you towards solution 1 although currently when I consider the case again, solution 2 seems a better alternative. In this case, I kinda misguided you towards solution 1. Thus, I see currently two options,

  • Keep solution 1 (current implementation), but fix the accept type (all should be int because of converting VOC) and also add an info to the docstring about shift values must be unnormalized.
  • Switch to solution 2 (might be better).

@harshraj22 wdyt of these alternatives ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I too would prefer solution 2. So if I understand correctly, it would be overwriting shift method in each BoundingBox type.

Copy link
Owner

@devrimcavusoglu devrimcavusoglu Jun 13, 2022

Choose a reason for hiding this comment

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

Yes, I too would prefer solution 2. So if I understand correctly, it would be overwriting shift method in each BoundingBox type.

Your implementation so far will remain as is except the to_voc conversion at step 1 since we'd like to update the values associated with each style. In short, simply remove to_voc parts in your implementation and all is fine. Also, your return part changes directly as cls() instead of cls.from_voc()

Copy link
Owner

@devrimcavusoglu devrimcavusoglu Jun 13, 2022

Choose a reason for hiding this comment

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

Well, in your fork for YOLO box everything seems fine, with the code snippet below, I got the desired output:

>>> yolo_values = [0.4046875, 0.840625, 0.503125, 0.24375]
>>> yolo_bbox = YoloBoundingBox.from_array(yolo_values, image_size=(640, 480))
>>> shifted_yolo_bbox = yolo_bbox.shift((0.08, 0.03))
>>> print(yolo_bbox)
>>> print(shifted_yolo_bbox)
<[0.4047 0.8406 0.5031 0.2437] (322x117) | Image: (640x480)>
<[0.4847 0.8706 0.5031 0.2437] (322x117) | Image: (640x480)>

,and this

>>> oob_bbox = yolo_bbox.shift((0.08, 0.2))
>>> print(oob_bbox)
Traceback (most recent call last):
  ...
ValueError: Given bounding box values is out of bounds.

All in all, everything seems fine. Can you give minimal example to reproduce your case ? Could you be accidentally shifted the box out of bounds ? @harshraj22 and can you give platform info, what OS and version, python version, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's strange ! macOS Monterey, python3.8.9
to reproduce, from the latest commit on my branch, run pytest from the pytest directory.

Copy link
Owner

Choose a reason for hiding this comment

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

Hi @harshraj22, sorry for late reply. Well, I've checked it. The tests failing simply because the shift amount tries to shift the boxes out of bounds,

# Albumentations box
<[0.1531 0.7188 0.6562 0.9625] (322x117) | Image: (640x480)>

# the shift amount
(2, 2)

Albumentations, fiftyone and YOLO are bounding box styles with normalized values meaning that the bounding boxes that are not OOB needs to be in (0,1) range, clearly with the shift (2,2) they are pushed towards OOB. My advise is that, define two distinct fixtures for normalized shift, and unnormalized shift and apply accordingly.

Also, test case for COCO is failing due to mismatch between actual being list, and desired being tuple, simply just match the types either make both lists or both tuples, make them consistent among all test cases so chose one type for all test cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The shift( ) method takes Tuple[int, int] as arg right ? We can't pass a tuple of values in (0, 1) ?

Copy link
Owner

Choose a reason for hiding this comment

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

The shift( ) method takes Tuple[int, int] as arg right ? We can't pass a tuple of values in (0, 1) ?

No, we've picked the second option (see Solution 2), and thus on shift() method every box takes their associated values. That is, normalized box styles (Albumentations, Fiftyone, YOLO) will take floats between (0,1), and unnormalized box styles (COCO, VOC) will take integers.

@harshraj22
Copy link
Contributor Author

image

Test script specified in readme fails. Any idea why ?

@harshraj22 harshraj22 marked this pull request as draft June 13, 2022 06:03
@devrimcavusoglu
Copy link
Owner

devrimcavusoglu commented Jun 13, 2022

image

Test script specified in readme fails. Any idea why ?

Yes, it is missing in the readme (I'll add). You have to install like

pip install -e .[dev]

Afterwards, the test cases should be running without any problem.

@harshraj22 harshraj22 marked this pull request as ready for review June 16, 2022 09:37
@devrimcavusoglu
Copy link
Owner

devrimcavusoglu commented Jun 18, 2022

@harshraj22 sorry for late review. Thanks for the effort, I think we are almost there. I have two major comments:

  • Let's rename threshold to amount as "threshold" may be misleading. Also, after this change please update the docstrings accordingly.
  • shift() is missing for bbox.BoundingBox

Additionaly, there are some conflicts due to the release 0.1.1, can you please solve the conflicts also.

@@ -1,5 +1,7 @@
from typing import Tuple, Union

from cv2 import threshold
Copy link
Owner

Choose a reason for hiding this comment

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

Unused import should be removed

@@ -3,6 +3,8 @@
from pybboxes.types.base import BaseBoundingBox
from pybboxes.types.bbox import BoundingBox

# from pybboxes.functional import convert_bbox
Copy link
Owner

Choose a reason for hiding this comment

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

Remove unused import.

@@ -34,6 +36,35 @@ def to_voc(self, return_values: bool = False) -> Union[Tuple[int, int, int, int]
return x_tl, y_tl, x_br, y_br
return BoundingBox(x_tl, y_tl, x_br, y_br, image_size=self.image_size, strict=self.strict)

def shift(self, threshold: Tuple[int, int]) -> "AlbumentationsBoundingBox":
Copy link
Owner

Choose a reason for hiding this comment

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

Tuple[float,float] for Albumentations style, update the docstring also.

@harshraj22
Copy link
Contributor Author

@devrimcavusoglu Made the required changes. Please review again :)

@devrimcavusoglu
Copy link
Owner

devrimcavusoglu commented Jun 20, 2022

@devrimcavusoglu Made the required changes. Please review again :)

Thanks for the effort appreciated 🚀 I'll run the tests now though I have a single minor comment/suggestion in one naming.

Edit: Tests are failing due to formatting, please see code-style guide.

AlbumentationsBoundingBox
The new bounding box.
"""
horizontal_threshold, vertical_threshold = amount
Copy link
Owner

Choose a reason for hiding this comment

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

Can we rename these also (take this comment into consideration for all box types please)

horizontal_threshold, vertical_threshold -> horizontal_shift, vertical_shift

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done :)

* Formatted code
* Passes tests
@devrimcavusoglu devrimcavusoglu merged commit 4abdab9 into devrimcavusoglu:main Jun 21, 2022
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.

2 participants