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

Major updates in Folding. #327

Merged
merged 1 commit into from
Jul 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
67 changes: 51 additions & 16 deletions augraphy/augmentations/folding.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import numpy as np

from augraphy.augmentations.lib import warp_fold_left_side
from augraphy.augmentations.lib import warp_fold_right_side
from augraphy.augmentations.lib import rotate_image_PIL
from augraphy.augmentations.lib import warp_fold
from augraphy.base.augmentation import Augmentation


Expand All @@ -17,15 +17,19 @@ class Folding(Augmentation):
:param fold count: Number of applied foldings
:type fold_count: int, optional
:param fold_noise: Level of noise added to folding area. Range from
value of 0 to 1.
value of 0 to 1.
:type fold_noise: float, optional
:param fold_angle_range: Tuple of ints determining the angle to rotate the image
before applying a varying angle folding effect.
:type fold_angle_range: tuple, optional
:param gradient_width: Tuple (min, max) Measure of the space affected
by fold prior to being warped (in units of
percentage of width of page)
by fold prior to being warped (in units of percentage of width of page).
:type gradient_width: tuple, optional
:param gradient_height: Tuple (min, max) Measure of depth of fold (unit
measured as percentage page height)
measured as percentage page height)
:type gradient_height: tuple, optional
:param backdrop_color: The backdrop color (BGR) of the folding effect.
:type backdrop_color: tuple, optional
:param p: The probability this Augmentation will be applied.
:type p: float, optional
"""
Expand All @@ -36,21 +40,25 @@ def __init__(
fold_deviation=(0, 0),
fold_count=2,
fold_noise=0.1,
fold_angle_range=(0, 0),
gradient_width=(0.1, 0.2),
gradient_height=(0.01, 0.02),
backdrop_color=(0, 0, 0),
p=1,
):
super().__init__(p=p)
self.fold_x = fold_x
self.fold_deviation = fold_deviation
self.fold_count = fold_count
self.fold_noise = fold_noise
self.fold_angle_range = fold_angle_range
self.gradient_width = gradient_width
self.gradient_height = gradient_height
self.backdrop_color = backdrop_color

# Constructs a string representation of this Augmentation.
def __repr__(self):
return f"Folding(fold_x={self.fold_x}, fold_deviation={self.fold_deviation}, fold_count={self.fold_count}, fold_noise={self.fold_noise}, gradient_width={self.gradient_width}, gradient_height={self.gradient_height},p={self.p})"
return f"Folding(fold_x={self.fold_x}, fold_deviation={self.fold_deviation}, fold_count={self.fold_count}, fold_noise={self.fold_noise}, fold_angle_range={self.fold_angle_range}, gradient_width={self.gradient_width}, gradient_height={self.gradient_height}, backdrop_color={self.backdrop_color}, p={self.p})"

def apply_folding(
self,
Expand Down Expand Up @@ -114,21 +122,25 @@ def apply_folding(
) # y distortion in folding (support positive y value for now)

if (fold_width_one_side != 0) and (fold_y_shift != 0):
img_fold_l = warp_fold_left_side(
img_fold_l = warp_fold(
img,
ysize,
fold_noise,
fold_x,
fold_width_one_side,
fold_y_shift,
side="left",
backdrop_color=self.backdrop_color,
)
img_fold_r = warp_fold_right_side(
img_fold_r = warp_fold(
img_fold_l,
ysize,
fold_noise,
fold_x,
fold_width_one_side,
fold_y_shift,
side="right",
backdrop_color=self.backdrop_color,
)
return img_fold_r
else:
Expand All @@ -145,24 +157,47 @@ def apply_folding(
# Applies the Augmentation to input data.
def __call__(self, image, layer=None, force=False):
if force or self.should_run():
image = image.copy()

# get image dimension
if len(image.shape) > 2:
ysize, xsize, _ = image.shape
else:
ysize, xsize = image.shape
ysize, xsize = image.shape[:2]

# apply folding multiple times
image_fold = image.copy()
for _ in range(self.fold_count):
# random fold angle
fold_angle = random.randint(self.fold_angle_range[0], self.fold_angle_range[1])
# rotate image before the folding
image_fold = rotate_image_PIL(
image_fold,
angle=fold_angle,
background_value=self.backdrop_color,
expand=1,
)
image_fold = self.apply_folding(
image_fold,
ysize,
xsize,
image_fold.shape[0],
image_fold.shape[1],
self.gradient_width,
self.gradient_height,
self.fold_noise,
)
# rotate back the image
image_fold = rotate_image_PIL(
image_fold,
angle=-fold_angle,
background_value=self.backdrop_color,
expand=1,
)
# get the image without the padding area, we will get extra padding area after the rotation
rysize, rxsize = image_fold.shape[:2]
if fold_angle != 0:
# center of x and y
cx = int(rxsize / 2)
cy = int(rysize / 2)
start_x = cx - int(xsize / 2)
start_y = cy - int(ysize / 2)
end_x = start_x + xsize
end_y = start_y + ysize
image_fold = image_fold[start_y:end_y, start_x:end_x]

return image_fold
136 changes: 51 additions & 85 deletions augraphy/augmentations/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ def rotate_image(mat, angle, white_background=1):
def rotate_image_PIL(image, angle, background_value=(0, 0, 0), expand=0):
"""Rotates an image (angle in degrees) by converting them to PIL image first."""

# for single channel
if len(image.shape) < 3 and isinstance(background_value, tuple):
background_value = int(np.mean(background_value))
image_PIL = Image.fromarray(image)
rotated_image_PIL = image_PIL.rotate(angle, expand=expand, fillcolor=background_value)

Expand Down Expand Up @@ -201,15 +204,16 @@ def four_point_transform(image, pts, dst, xs, ys):


# Transform left side of folding area
def warp_fold_left_side(
def warp_fold(
img,
ysize,
fold_noise,
fold_x,
fold_width_one_side,
fold_y_shift,
side,
backdrop_color,
):

img_fuse = img.copy()

# 4 vectices of folding area
Expand All @@ -224,17 +228,30 @@ def warp_fold_left_side(
bottom_left = [xs, ye]
bottom_right = [xe, ye]

# after distortion
dtop_left = [xs, ys]
dtop_right = [xe, ys + fold_y_shift]
dbottom_left = [xs, ye]
dbottom_right = [xe, ye + fold_y_shift]

# image cropping points
cxs = fold_x
cxe = fold_x + fold_width_one_side
cys = 0
cye = ysize
if side == "left":
# after distortion
dtop_left = [xs, ys]
dtop_right = [xe, ys + fold_y_shift]
dbottom_left = [xs, ye]
dbottom_right = [xe, ye + fold_y_shift]

# image cropping points
cxs = fold_x
cxe = fold_x + fold_width_one_side
cys = 0
cye = ysize
else:
# after distortion
dtop_left = [xs, ys + (fold_y_shift)]
dtop_right = [xe, ys]
dbottom_left = [xs, ye + (fold_y_shift)]
dbottom_right = [xe, ye]

# image cropping points
cxs = fold_x + fold_width_one_side
cxe = fold_x + (fold_width_one_side * 2)
cys = 0
cye = ysize

# points of folding area
source_pts = np.array(
Expand All @@ -256,89 +273,38 @@ def warp_fold_left_side(
cysize, cxsize = img_crop.shape
cdim = 2

# darken the folded area
darken_ratio = random.uniform(0.99, 1.0)

# warp folding area
img_warped = four_point_transform(
img_crop,
img_crop * darken_ratio,
source_pts,
destination_pts,
cxsize,
cysize + fold_y_shift,
)
img_warped = add_folding_noise(img_warped, 1, fold_noise / 2)

if cdim > 2:
img_fuse[cys:cye, cxs:cxe, :] = img_warped[:-fold_y_shift, :, :]
else:
img_fuse[cys:cye, cxs:cxe] = img_warped[:-fold_y_shift, :]

return img_fuse


# Transform right side of folding area
def warp_fold_right_side(
img,
ysize,
fold_noise,
fold_x,
fold_width_one_side,
fold_y_shift,
):

img_fuse = img.copy()

# 4 vectices of folding area
xs = 0 # xleft
xe = fold_width_one_side # xright
ys = 0 # ytop
ye = ysize # ybottom
).astype("uint8")

# before distortion
top_left = [xs, ys]
top_right = [xe, ys]
bottom_left = [xs, ye]
bottom_right = [xe, ye]

# after distortion
dtop_left = [xs, ys + (fold_y_shift)]
dtop_right = [xe, ys]
dbottom_left = [xs, ye + (fold_y_shift)]
dbottom_right = [xe, ye]

# image cropping points
cxs = fold_x + fold_width_one_side
cxe = fold_x + (fold_width_one_side * 2)
cys = 0
cye = ysize

# points of folding area
source_pts = np.array(
[top_left, bottom_left, bottom_right, top_right],
dtype=np.float32,
)
destination_pts = np.array(
[dtop_left, dbottom_left, dbottom_right, dtop_right],
dtype=np.float32,
)

# crop section of folding area
img_crop = img[cys:cye, cxs:cxe]

# get image dimension of cropped image
if len(img_crop.shape) > 2:
cysize, cxsize, cdim = img_crop.shape
else:
cysize, cxsize = img_crop.shape
cdim = 2

# warp folding area
img_warped = four_point_transform(
img_crop,
# mask of warping
img_mask = np.full_like(img_crop, fill_value=255, dtype="uint8")
img_mask_warped = four_point_transform(
img_mask,
source_pts,
destination_pts,
cxsize,
cysize + fold_y_shift,
)
img_warped = add_folding_noise(img_warped, 0, fold_noise / 2)
).astype("uint8")

# update color
if cdim < 3:
backdrop_color = np.mean(backdrop_color)
img_warped[img_mask_warped < 255] = backdrop_color
else:
for i in range(3):
img_warped[:, :, i][img_mask_warped[:, :, i] < 255] = backdrop_color[i]

if fold_noise != 0:
img_warped = add_folding_noise(img_warped, 1, fold_noise / 2)

if cdim > 2:
img_fuse[cys:cye, cxs:cxe, :] = img_warped[:-fold_y_shift, :, :]
Expand Down
14 changes: 9 additions & 5 deletions augraphy/augmentations/pageborder.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@

from augraphy.augmentations.lib import load_image_from_cache
from augraphy.augmentations.lib import rotate_image_PIL
from augraphy.augmentations.lib import warp_fold_left_side
from augraphy.augmentations.lib import warp_fold_right_side
from augraphy.augmentations.lib import warp_fold
from augraphy.base.augmentation import Augmentation


Expand All @@ -31,6 +30,7 @@ class PageBorder(Augmentation):
:param page_border_use_cache_images: Flag to enable the usage of cache images in creating page border effect.
:type page_border_use_cache_images: int, optional
:param page_border_trim_sides: Tuple of 4 (left, top, right, bottom) determining which sides of the image to be trimmed.
This is valid only if same_page_border is false.
:type page_border_trim_sides: int, optional
:param page_numbers: An integer determining the number of pages in the border.
:type page_numbers: int, optional
Expand Down Expand Up @@ -126,22 +126,26 @@ def random_folding(self, image):
curve_noise = 0

# warp image to produce curvy effect
image_curve_left = warp_fold_left_side(
image_curve_left = warp_fold(
image,
ysize,
curve_noise,
curve_x,
curve_width_one_side,
curve_y_shift,
side="left",
backdrop_color=self.page_border_background_color,
)

image_curve_right = warp_fold_right_side(
image_curve_right = warp_fold(
image_curve_left,
ysize,
curve_noise,
curve_x,
curve_width_one_side,
curve_y_shift,
side="right",
backdrop_color=self.page_border_background_color,
)

image_color = np.full_like(image_curve_right, fill_value=self.page_border_color, dtype="uint8")
Expand Down Expand Up @@ -481,7 +485,7 @@ def create_page_borders(
dx_left = abs(xcenter - bxcenter)
dx_right = abs(abs(xsize - xcenter) - abs(bxsize - bxcenter))

if sum(page_border_trim_sides) > 0:
if not self.same_page_border and sum(page_border_trim_sides) > 0:
half_border_width = int(np.ceil(border_width / 2))
half_border_height = int(np.ceil(border_height / 2))
dy_top += half_border_height
Expand Down
6 changes: 4 additions & 2 deletions augraphy/default/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,12 @@ def default_augraphy_pipeline():
Folding(
fold_x=None,
fold_deviation=(0, 0),
fold_count=random.randint(1, 6),
fold_noise=random.uniform(0, 0.2),
fold_count=random.randint(5, 10),
fold_noise=random.uniform(0, 0.01),
fold_angle_range=(-360, 360),
gradient_width=(0.1, 0.2),
gradient_height=(0.01, 0.02),
backdrop_color=(0, 0, 0),
p=0.33,
),
Markup(
Expand Down
Binary file modified doc/source/augmentations/augmentations/folding/folding.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading