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

Barrel and Pincushion distortions are added. #113

Open
wants to merge 38 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
a885226
Part 1
devrimcavusoglu Sep 5, 2021
edb386e
Add barrel & pincushion distortion.
devrimcavusoglu Sep 6, 2021
61fda43
Fix typo in test fixtures.
devrimcavusoglu Sep 6, 2021
1cde0f7
Black formatting reverted.
devrimcavusoglu Sep 8, 2021
a8460c6
Revert the main state before black formatting.
devrimcavusoglu Sep 8, 2021
4a2a05f
revert to AugLy/main.
devrimcavusoglu Sep 8, 2021
57c010b
Docstring added for `imutils.distort`
devrimcavusoglu Sep 8, 2021
8d1dc24
Docstring added for `functional.distort_barrel` and `functional.disto…
devrimcavusoglu Sep 8, 2021
bcb489a
Revisions for functions and docstrings.
devrimcavusoglu Sep 13, 2021
451f226
Merge branch 'facebookresearch:main' into main
devrimcavusoglu Sep 15, 2021
e963303
Fix tests.
devrimcavusoglu Sep 17, 2021
788ae78
Merge remote-tracking branch 'origin/main' into main
devrimcavusoglu Sep 17, 2021
fe9e3ff
Docstrings revised as max line length is 90.
devrimcavusoglu Sep 17, 2021
8e9c7e5
Helper function added to intensity.
devrimcavusoglu Sep 17, 2021
f6eb08f
Typof fixed.
devrimcavusoglu Sep 17, 2021
2112434
Update augly/image/functional.py
devrimcavusoglu Sep 17, 2021
fd7e9dd
Update augly/image/functional.py
devrimcavusoglu Sep 17, 2021
5c50617
Update augly/image/functional.py
devrimcavusoglu Sep 17, 2021
ee06e38
Update augly/image/functional.py
devrimcavusoglu Sep 17, 2021
19a2edf
Update augly/image/functional.py
devrimcavusoglu Sep 17, 2021
4e3d3e8
Update augly/image/intensity.py
devrimcavusoglu Sep 17, 2021
f30c6be
Update augly/image/intensity.py
devrimcavusoglu Sep 17, 2021
70d159c
Apply suggestions from code review
devrimcavusoglu Sep 17, 2021
a63f9b8
Update augly/image/utils.py
devrimcavusoglu Sep 17, 2021
3bb1510
Docstrins are revised.
devrimcavusoglu Sep 17, 2021
ba6fe56
Docstring corrected.
devrimcavusoglu Sep 17, 2021
22c8d8f
Type hint extended for `distort_intensity`.
devrimcavusoglu Sep 17, 2021
5089547
Typo corrected.
devrimcavusoglu Sep 17, 2021
827fc0e
Requested changes.
devrimcavusoglu Sep 20, 2021
20e7dfb
Apply suggestions from code review
devrimcavusoglu Sep 20, 2021
0f0fa37
Return type added.
devrimcavusoglu Sep 20, 2021
3328c7e
Merge remote-tracking branch 'origin/main' into main
devrimcavusoglu Sep 20, 2021
d53d6d9
Docstring corrected.
devrimcavusoglu Sep 20, 2021
e49be96
Docstring corrected.
devrimcavusoglu Sep 20, 2021
4e1793d
Merge remote-tracking branch 'upstream/main' into main
devrimcavusoglu Sep 26, 2021
6b4d850
Bbox updates.
devrimcavusoglu Sep 30, 2021
7412e8f
Function signatures changed to support bboxes.
devrimcavusoglu Oct 1, 2021
c45c981
Apply suggestions from code review
devrimcavusoglu Oct 1, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions augly/image/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
contrast,
convert_color,
crop,
distort_barrel,
distort_pincushion,
encoding_quality,
grayscale,
hflip,
Expand Down Expand Up @@ -78,6 +80,7 @@
from augly.image.transforms import (
ApplyLambda,
ApplyPILFilter,
DistortBarrel,
Blur,
Brightness,
ChangeAspectRatio,
Expand All @@ -101,6 +104,7 @@
Pad,
PadSquare,
PerspectiveTransform,
DistortPincushion,
Pixelization,
RandomAspectRatio,
RandomBlur,
Expand All @@ -122,6 +126,7 @@
__all__ = [
"ApplyLambda",
"ApplyPILFilter",
"DistortBarrel",
devrimcavusoglu marked this conversation as resolved.
Show resolved Hide resolved
"Blur",
"Brightness",
"ChangeAspectRatio",
Expand All @@ -147,6 +152,7 @@
"Pad",
"PadSquare",
"PerspectiveTransform",
"DistortPincushion",
"Pixelization",
"RandomAspectRatio",
"RandomBlur",
Expand All @@ -173,6 +179,8 @@
"contrast",
"convert_color",
"crop",
"distort_barrel",
"distort_pincushion",
devrimcavusoglu marked this conversation as resolved.
Show resolved Hide resolved
"encoding_quality",
"grayscale",
"hflip",
Expand Down
147 changes: 147 additions & 0 deletions augly/image/functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,146 @@ def crop(
return imutils.ret_and_save_image(aug_image, output_path)


def distort_barrel(
image: Union[str, Image.Image],
output_path: Optional[str] = None,
a: float = 0.0,
b: float = 0.0,
c: float = 0.0,
d: float = 1.0,
metadata: Optional[List[Dict[str, Any]]] = None,
):
devrimcavusoglu marked this conversation as resolved.
Show resolved Hide resolved
"""
Applies barrel distortion to the image with the following equation.

To see effects of the coefficients in detail refer to https://legacy.imagemagick.org/Usage/distorts/#barrel.
Below is a direct quotation from the document describing how barrel distortion parameter works.

> The values basically form a distortion equation such that...

Rsrc = r * ( A*r3 + B*r2 + C*r + D )

Where "r" is the destination radius and "Rsrc" is the source pixel to get the pixel color from. the radii
are normalized so that radius = '1.0' for the half minimum width or height of the input image.
This may seem reversed but that is because the Reverse Pixel Mapping technique is used to ensure
complete coverage of the resulting image.

Setting A = B = C = 0 and D = 1 results in no change in the input image. Negative values of A, B and C will
result in reverse barrel effect closer to pincushion effect.

@param image: the path to an image or a variable of type PIL.Image.Image
to be augmented

@param output_path: the path in which the resulting image will be stored.
If None, the resulting PIL Image will still be returned

@param a: Coefficient A in the equation Rsrc(r). Larger values results in more barrel effect,
has higher effect than b and c.

@param b: Coefficient B in the equation Rsrc(r). Larger values results in more barrel effect,
has lower effect than a and higher effect than c.

@param c: Coefficient C in the equation Rsrc(r). Larger values results in more barrel effect,
has lower effect than a and b.

@param d: Coefficient D in the equation Rsrc(r). Controls the overall scaling of the image.
In a positive domain, values larger than 1 will shrink the image. Negative values would
result in both vertically and horizontally flipped image scaled in a mirrored way
of positive domain.

@param metadata: if set to be a list, metadata about the function execution
including its name, the source & dest width, height, etc. will be appended
to the inputted list. If set to None, no metadata will be appended or returned

@returns: the augmented PIL Image
"""
image = imutils.validate_and_load_image(image).convert("RGB")
func_kwargs = imutils.get_func_kwargs(metadata, locals())

aug_image = imutils.distort(
image=image,
method="barrel",
distortion_args=(a, b, c, d),
)

imutils.get_metadata(
metadata=metadata,
function_name="distort_barrel",
aug_image=aug_image,
**func_kwargs,
)

return imutils.ret_and_save_image(aug_image, output_path)


def distort_pincushion(
image: Union[str, Image.Image],
output_path: Optional[str] = None,
a: float = 0.0,
b: float = 0.0,
c: float = 0.0,
d: float = 1.0,
metadata: Optional[List[Dict[str, Any]]] = None,
):
"""
To see effects of the coefficients in detail refer to
https://legacy.imagemagick.org/Usage/distorts/#barrelinverse. Below is a direct quotation from the document
describing how pincushion (barrel inverse) distortion parameter works.

> The 'BarrelInverse' distortion method is very similar to the previous Barrel Distortion distortion method,
and in fact takes the same set of arguments. However the formula that is applied is slightly different,
with the main part of the equation dividing the radius. that is the Equation has been inverted.

Rsrc = r / ( A*r3 + B*r2 + C*r + D )

NOTE: This equation does NOT produce the 'reverse' the 'Barrel' distortion. You can NOT use it to 'undo'
the previous distortion.

@param image: the path to an image or a variable of type PIL.Image.Image
to be augmented

@param output_path: the path in which the resulting image will be stored.
If None, the resulting PIL Image will still be returned

@param a: Coefficient A in the equation Rsrc(r). Larger values results in more pincushion effect,
has higher effect than b and c.

@param b: Coefficient B in the equation Rsrc(r). Larger values results in more pincushion effect,
has higher effect than b and c.

@param c: Coefficient C in the equation Rsrc(r). Larger values results in more pincushion effect,
has higher effect than b and c.

@param d: Coefficient D in the equation Rsrc(r). Controls the overall scaling of the image.
In a positive domain, values larger than 1 will enlarge the image (zoomed in). Negative
values would result in both vertically and horizontally flipped image scaled in a mirrored way
of positive domain.

@param metadata: if set to be a list, metadata about the function execution
including its name, the source & dest width, height, etc. will be appended
to the inputted list. If set to None, no metadata will be appended or returned

@returns: the augmented PIL Image
"""
image = imutils.validate_and_load_image(image).convert("RGB")
func_kwargs = imutils.get_func_kwargs(metadata, locals())

aug_image = imutils.distort(
image=image,
method="barrel_inverse",
distortion_args=(a, b, c, d),
)

imutils.get_metadata(
metadata=metadata,
function_name="distort_pincushion",
aug_image=aug_image,
**func_kwargs,
)

return imutils.ret_and_save_image(aug_image, output_path)


def encoding_quality(
image: Union[str, Image.Image],
output_path: Optional[str] = None,
Expand Down Expand Up @@ -2011,3 +2151,10 @@ def vflip(
imutils.get_metadata(metadata=metadata, function_name="vflip", **func_kwargs)

return imutils.ret_and_save_image(aug_image, output_path)


if __name__ == "__main__":
aug_img = distort_pincushion(
image="/home/devrimcavusoglu/lab/gh/AugLy/augly/assets/tests/image/inputs/dfdc_1.jpg",
)
aug_img.show()
devrimcavusoglu marked this conversation as resolved.
Show resolved Hide resolved
14 changes: 14 additions & 0 deletions augly/image/intensity.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,20 @@ def crop_intensity(metadata: Dict[str, Any], **kwargs) -> float:
return resize_intensity_helper(metadata)


def distort_barrel_intensity(a: float, b: float, c: float, d: float, **kwargs) -> float:
barrel_coefficients_magnitude = abs(a) + abs(b) + abs(c)
scale = np.abs(d)
intensity = 100 * (barrel_coefficients_magnitude / scale)
return float(np.clip(intensity, 0, 100))
devrimcavusoglu marked this conversation as resolved.
Show resolved Hide resolved


def distort_pincushion_intensity(a: float, b: float, c: float, d: float, **kwargs) -> float:
pincushion_coefficients_magnitude = np.abs([a, b, c]).sum()
scale = np.abs(d)
intensity = 100 * (pincushion_coefficients_magnitude / scale)
return float(np.clip(intensity, 0, 100))


def encoding_quality_intensity(quality: int, **kwargs):
assert (
isinstance(quality, int) and 0 <= quality <= 100
Expand Down
70 changes: 70 additions & 0 deletions augly/image/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,76 @@ def apply_transform(
return F.apply_pil_filter(image, filter_type=self.filter_type, metadata=metadata)


class DistortBarrel(BaseTransform):
def __init__(self, a: float = 0.0, b: float = 0.0, c: float = 0.0, d: float = 1.0, p: float = 1):
"""
@param a: Coefficient A in the equation Rsrc(r).
devrimcavusoglu marked this conversation as resolved.
Show resolved Hide resolved

@param b: Coefficient B in the equation Rsrc(r).

@param c: Coefficient C in the equation Rsrc(r).

@param d: Coefficient D in the equation Rsrc(r).

@param p: the probability of the transform being applied; default value is 1.0
"""
super().__init__(p)
self.a = a
self.b = b
self.c = c
self.d = d

def apply_transform(self, image: Image.Image, metadata: Optional[List[Dict[str, Any]]] = None) -> Image.Image:
"""
Applies barrel distortion to the image.

@param image: PIL Image to be augmented

@param metadata: if set to be a list, metadata about the function execution
including its name, the source & dest width, height, etc. will be appended to
the inputted list. If set to None, no metadata will be appended or returned

@returns: Augmented PIL Image
"""
return F.distort_barrel(image, a=self.a, b=self.b, c=self.c, d=self.d, metadata=metadata)


class DistortPincushion(BaseTransform):
def __init__(self, a: float = 0.0, b: float = 0.0, c: float = 0.0, d: float = 1.0, p: float = 1):
"""


devrimcavusoglu marked this conversation as resolved.
Show resolved Hide resolved
@param a: Coefficient A in the equation Rsrc(r).
devrimcavusoglu marked this conversation as resolved.
Show resolved Hide resolved

@param b: Coefficient B in the equation Rsrc(r).

@param c: Coefficient C in the equation Rsrc(r).

@param d: Coefficient D in the equation Rsrc(r).

@param p: the probability of the transform being applied; default value is 1.0
"""
super().__init__(p)
self.a = a
self.b = b
self.c = c
self.d = d

def apply_transform(self, image: Image.Image, metadata: Optional[List[Dict[str, Any]]] = None) -> Image.Image:
"""
Applies pinchusion distortion to the image.

@param image: PIL Image to be augmented

@param metadata: if set to be a list, metadata about the function execution
including its name, the source & dest width, height, etc. will be appended to
the inputted list. If set to None, no metadata will be appended or returned

@returns: Augmented PIL Image
"""
return F.distort_pincushion(image, a=self.a, b=self.b, c=self.c, d=self.d, metadata=metadata)


class Blur(BaseTransform):
def __init__(self, radius: float = 2.0, p: float = 1.0):
"""
Expand Down
21 changes: 21 additions & 0 deletions augly/image/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import augly.utils as utils
import numpy as np
from PIL import Image
from wand.image import Image as wImage

JPEG_EXTENSIONS = [".jpg", ".JPG", ".jpeg", ".JPEG"]

Expand Down Expand Up @@ -279,3 +280,23 @@ def compute_stripe_mask(
binary_mask = softmax_mask > (math.cos(math.pi * line_width) + 1) / 2

return binary_mask


def distort(
image: Image.Image,
method: str,
distortion_args: Tuple,
devrimcavusoglu marked this conversation as resolved.
Show resolved Hide resolved
) -> Image.Image:
"""
Distorts the image with a specified type of distortion. This function wraps `Wand` package
`wand.image.Image.distort()` method to apply distortions. This function is a helper function
to apply lens distortions on the image, and it is not meant to be used for methods explicitly
written in AugLy. To see full set of distortion methods, see
https://docs.wand-py.org/en/0.5.3/wand/image.html#wand.image.DISTORTION_METHODS
"""
image = np.array(image)
wimage = wImage.from_array(image)
devrimcavusoglu marked this conversation as resolved.
Show resolved Hide resolved
wimage.distort(method, distortion_args)
aug_image = Image.fromarray(np.array(wimage))

return aug_image
6 changes: 6 additions & 0 deletions augly/tests/image_tests/functional_unit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ def test_blur(self):
def test_brightness(self):
self.evaluate_function(imaugs.brightness)

def test_distort_barrel(self):
self.evaluate_function(imaugs.distort_barrel, a=0.1)

def test_distort_pincushion(self):
self.evaluate_function(imaugs.distort_pincushion, a=0.1)

def test_change_aspect_ratio(self):
self.evaluate_function(imaugs.change_aspect_ratio)

Expand Down
6 changes: 6 additions & 0 deletions augly/tests/image_tests/transforms_unit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ def test_ApplyLambda(self):
def test_ApplyPILFilter(self):
self.evaluate_class(imaugs.ApplyPILFilter(), fname="apply_pil_filter")

def test_BarrelDistortion(self):
self.evaluate_class(imaugs.DistortBarrel(a=0.1), fname="distort_barrel")

def test_Blur(self):
self.evaluate_class(imaugs.Blur(), fname="blur")

Expand Down Expand Up @@ -144,6 +147,9 @@ def test_PerspectiveTransform(self):
imaugs.PerspectiveTransform(sigma=100.0), fname="perspective_transform"
)

def test_PincushionDistortion(self):
self.evaluate_class(imaugs.DistortPincushion(a=0.1), fname="distort_pincushion")

def test_Pixelization(self):
self.evaluate_class(imaugs.Pixelization(), fname="pixelization")

Expand Down
Loading