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

Add copy paste #1820

Closed
wants to merge 7 commits into from
Closed

Add copy paste #1820

wants to merge 7 commits into from

Conversation

ternaus
Copy link
Collaborator

@ternaus ternaus commented Jun 27, 2024

Fixes:

#745

#1716

#1784

#1676

#1297

#1225

#865

Summary by Sourcery

This pull request introduces a new CopyPaste transformation for image augmentation, enhances the OverlayElements transformation, and updates the test suite to cover the new functionality. Additionally, it includes a minor update to the pre-commit configuration.

  • New Features:
    • Introduced a new CopyPaste transformation for image augmentation, supporting image, mask, bounding boxes, and keypoints.
  • Enhancements:
    • Enhanced the OverlayElements transformation to improve metadata handling and added an example usage in the docstring.
    • Refactored apply_to_bboxes, apply_to_keypoints, and apply_to_masks methods to use List instead of Sequence for type annotations.
  • Build:
    • Updated pre-commit configuration to use mypy version v1.10.1.
  • Tests:
    • Added tests for the new CopyPaste transformation to ensure correct functionality.
    • Updated existing tests to include cases for the CopyPaste transformation.
    • Refactored tests to handle the new metadata requirements for OverlayElements and CopyPaste transformations.

@ternaus ternaus marked this pull request as draft June 27, 2024 01:50
Copy link
Contributor

sourcery-ai bot commented Jun 27, 2024

Reviewer's Guide by Sourcery

This pull request introduces a new 'CopyPaste' augmentation, updates the 'OverlayElements' class, and modifies related functions and classes to support the new functionality. It also includes comprehensive test cases to ensure the new features work as expected.

File-Level Changes

Files Changes
albumentations/augmentations/mixing/transforms.py
albumentations/augmentations/mixing/functional.py
albumentations/augmentations/geometric/functional.py
Introduced 'CopyPaste' augmentation and updated related functions and classes to support new functionality.
tests/test_augmentations.py
tests/test_serialization.py
tests/test_mixing.py
tests/test_functional_mixing.py
tests/test_transforms.py
tests/test_targets.py
Added and updated test cases to cover 'CopyPaste' augmentation and its integration with existing functionalities.

Tips
  • Trigger a new Sourcery review by commenting @sourcery-ai review on the pull request.
  • You can change your review settings at any time by accessing your dashboard:
    • Enable or disable the Sourcery-generated pull request summary or reviewer's guide;
    • Change the review language;
  • You can always contact us if you have any questions or feedback.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @ternaus - I've reviewed your changes and they look great!

Here's what I looked at during the review
  • 🟡 General issues: 5 issues found
  • 🟢 Security: all looks good
  • 🟡 Testing: 12 issues found
  • 🟢 Complexity: all looks good
  • 🟡 Documentation: 1 issue found

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment to tell me if it was helpful.

return ("metadata_key",)


class CopyPaste(DualTransform):
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Clarify the purpose of the CopyPaste class.

The CopyPaste class lacks a detailed docstring explaining its purpose and usage. Adding a comprehensive docstring would improve code readability and maintainability.

Suggested change
class CopyPaste(DualTransform):
class CopyPaste(DualTransform):
"""
Apply Copy-Paste augmentation to the input data.
This transformation is used to copy objects from one image and paste them onto another.
Targets: image, mask, bboxes, keypoints
"""

mask = cv2.resize(mask, (x_max - x_min, y_max - y_min), interpolation=cv2.INTER_NEAREST)
else:
mask = np.ones((y_max - y_min, x_max - x_min), dtype=np.uint8)
if overlay_height > image_height or overlay_width > image_width:
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Consider edge cases for overlay image resizing.

The current logic resizes the overlay image if its dimensions exceed the base image dimensions. Consider adding a check to handle cases where the overlay image is significantly larger, which might affect the quality of the transformation.

Suggested change
if overlay_height > image_height or overlay_width > image_width:
if overlay_height > image_height or overlay_width > image_width:
if overlay_height > 2 * image_height or overlay_width > 2 * image_width:
overlay = cv2.resize(overlay, (image_width, image_height), interpolation=cv2.INTER_AREA)
else:
overlay = cv2.resize(overlay, (image_width, image_height), interpolation=cv2.INTER_LINEAR)

metadata = params[self.metadata_key]
img_shape = params["image"].shape

if isinstance(metadata, list):
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Clarify handling of metadata as a list.

The code handles metadata as a list but does not specify how this list is expected to be structured. Clarifying this would help future maintainers understand the expected input format.

Suggested change
if isinstance(metadata, list):
if isinstance(metadata, list) and all(isinstance(md, dict) for md in metadata):

) -> np.ndarray:
y_offset, x_offset = offset
if overlay_image.size == 0:
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (testing): Check for empty overlay image.

The check for an empty overlay image is a good addition. Ensure that this condition is tested to avoid unexpected behavior.

Suggested change
if overlay_image.size == 0:
if overlay_image is None or overlay_image.size == 0:

return blended_image


def mask2bbox(binary_mask: np.ndarray) -> Tuple[int, int, int, int]:
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Consider edge cases for mask2bbox.

The mask2bbox function should handle cases where the mask is entirely zero. Ensure that the returned coordinates are valid and do not cause issues downstream.

Suggested change
def mask2bbox(binary_mask: np.ndarray) -> Tuple[int, int, int, int]:
def mask2bbox(binary_mask: np.ndarray) -> Tuple[int, int, int, int]:
y_indices, x_indices = np.where(binary_mask)
if y_indices.size == 0 or x_indices.size == 0:
return 0, 0, 0, 0

Comment on lines 496 to +501
if augmentation_cls == A.OverlayElements:
data = {
"image": image_3ch,
"overlay_metadata": [],
"mask": mask_3ch,
}
data["overlay_metadata"] = []
elif augmentation_cls == A.FromFloat:
data = {
"image": SQUARE_FLOAT_IMAGE,
"mask": mask_3ch,
}
else:
data = {
"image": image_3ch,
"mask": mask_3ch,
}
data["image"] = SQUARE_FLOAT_IMAGE
elif augmentation_cls == A.CopyPaste:
data["copypaste_metadata"] = []
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines 619 to +622
if augmentation_cls == A.OverlayElements:
data = {
"image": image,
"overlay_metadata": [],
}
else:
data = {
"image": image,
}
data["overlay_metadata"] = []
elif augmentation_cls == A.CopyPaste:
data["copypaste_metadata"] = []
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines 707 to +710
if augmentation_cls == A.OverlayElements:
data = {
"image": image,
"overlay_metadata": [],
}
else:
data = {
"image": image,
}
data["overlay_metadata"] = []
elif augmentation_cls == A.CopyPaste:
data["copypaste_metadata"] = []
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines 788 to +791
if augmentation_cls == A.OverlayElements:
data = {
"image": image,
"overlay_metadata": [],
}
else:
data = {
"image": image,
}
data["overlay_metadata"] = []
elif augmentation_cls == A.CopyPaste:
data["copypaste_metadata"] = []
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

Comment on lines 871 to +874
if augmentation_cls == A.OverlayElements:
data = {
"image": image,
"overlay_metadata": [],
}
else:
data = {
"image": image,
}
data["overlay_metadata"] = []
elif augmentation_cls == A.CopyPaste:
data["copypaste_metadata"] = []
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (code-quality): Avoid conditionals in tests. (no-conditionals-in-tests)

ExplanationAvoid complex code, like conditionals, in test functions.

Google's software engineering guidelines says:
"Clear tests are trivially correct upon inspection"
To reach that avoid complex code in tests:

  • loops
  • conditionals

Some ways to fix this:

  • Use parametrized tests to get rid of the loop.
  • Move the complex logic into helpers.
  • Move the complex part into pytest fixtures.

Complexity is most often introduced in the form of logic. Logic is defined via the imperative parts of programming languages such as operators, loops, and conditionals. When a piece of code contains logic, you need to do a bit of mental computation to determine its result instead of just reading it off of the screen. It doesn't take much logic to make a test more difficult to reason about.

Software Engineering at Google / Don't Put Logic in Tests

@ternaus ternaus closed this Oct 8, 2024
@ternaus ternaus deleted the add_copy_paste branch November 17, 2024 22:36
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.

1 participant