import os
import random
import cv2
import numpy as np
from augraphy import *
from augraphy.augmentations.lib import rotate_image_PIL
from augraphy.base.augmentation import Augmentation
from augraphy.utilities import *
[docs]
class BindingsAndFasteners(Augmentation):
"""Creates binding and fastener mark in the input image.
:param overlay_types: Types of overlay method.
:type overlay_types: string
:param foreground: Path to foreground image or the foreground image.
:type foreground: string or numpy array, optional
:param effect_type: Types of binding effect, select from either
"random", "punch_holes", binding_holes", "clips" or "triangle_clips".
:type effect_type: string, optional
:param width_range: Pair of ints to determine the width of the binder mark canvas.
:type width_range: tuple, optional
:param height_range: Pair of ints to determine the height of the binder mark canvas.
:type height_range: tuple, optional
:param angle_range: Pair of ints to determine the rotating angle of the binder marks.
:type angle_range: tuple, optional
:param ntimes: Pair of ints to determine number of repetition to draw foreground image.
:type ntimes: tuple, optional
:param nscales: Pair of floats to determine scale of foreground image size.
:type nscales: tuple, optional
:param edge: Which edge of the page the foreground copies should be placed on.
The current supported edges are "left", "right", "top", "bottom" or "random".
:type edge: string, optional
:param edge_offset: Pair of values to determine how far from the edge of the page to draw the copies.
The offset value will be in percentage of the image shorter edge if the value is less than 1.
:type edge_offset: tuple, optional
:param use_figshare_library: Flag to download foreground images from figshare library.
:type use_figshare_library: int, optional
:param p: The probability this Augmentation will be applied.
:type p: float, optional
"""
def __init__(
self,
overlay_types="random",
foreground=None,
effect_type="random",
width_range="random",
height_range="random",
angle_range=(-30, 30),
ntimes=(2, 6),
nscales=(1.0, 1.5),
edge="random",
edge_offset=(5, 20),
use_figshare_library=0,
p=1,
):
"""Constructor method"""
super().__init__(p=p)
self.overlay_types = overlay_types
self.foreground = foreground
self.effect_type = effect_type
self.width_range = width_range
self.height_range = height_range
self.angle_range = angle_range
self.ntimes = ntimes
self.nscales = nscales
self.edge = edge
self.edge_offset = edge_offset
self.use_figshare_library = use_figshare_library
# Constructs a string representation of this Augmentation.
def __repr__(self):
return f"BindingsAndFasteners(overlay_types={self.overlay_types}, foreground={self.foreground}, effect_type={self.effect_type}, width_range={self.width_range}, height_range={self.height_range}, angle_range={self.angle_range}, ntimes={self.ntimes}, nscales={self.nscales}, edge={self.edge}, edge_offset={self.edge_offset}, use_figshare_library={self.use_figshare_library}, p={self.p})"
[docs]
def add_noise(self, image, noise_probability, noise_value_range):
"""Add noise to black pixels of the image.
:param image: The image to apply the function.
:type image: numpy.array (numpy.uint8)
:param noise_probability: The probability of applied noise.
:type noise_probability: float
:param noise_value: The value of applied noise.
:type noise_value: tuple
"""
# create mask of noise
noise_mask = np.random.randint(noise_value_range[0], noise_value_range[1], size=image.shape).astype("uint8")
noise_mask = cv2.GaussianBlur(noise_mask, random.choice([[5, 5], [7, 7]]), 0)
# create probability mask
probability_mask = np.random.random(image.shape)
# get indices of mask
indices = np.logical_and(probability_mask < noise_probability, image < 255)
# apply noise mask to image
image[indices] = noise_mask[indices]
[docs]
def create_punch_holes(self, ysize, xsize, ntimes):
"""Create effect of punch holes mark.
:param ysize: The height of the input image.
:type ysize: int
:param xsize: The width of the input image.
:type xsize: int
:param ntimes: The number of applied binding effect.
:type ntimes: int
"""
# reset
self.foreground = []
template_size = template_size_ori = 60
# scale template size based on image size
# 1000 * 800 is normal image size for template size = 30
# use max to prevent small template and min to prevent large template
template_size = int(
max(template_size_ori / 4, 30 * ((ysize * xsize) / (900 * 700))),
)
template_size = int(min(template_size, template_size_ori * 2))
for _ in range(ntimes):
current_template_size = random.randint(int(template_size * 0.75), int(template_size * 1.25))
if self.width_range == "random":
template_size_x = current_template_size
else:
template_size_x = random.randint(self.width_range[0], self.width_range[1])
if self.height_range == "random":
template_size_y = int(current_template_size / 3)
else:
template_size_y = random.randint(self.height_range[0], self.height_range[1])
# create random location to merge 2 circles
min_value = min(10, int(min(template_size_x, template_size_y) / 2))
random_x = random.randint(min_value, template_size_x - min_value)
random_y = random.randint(min_value, template_size_y - min_value)
# draw circle
image_circle = np.full(
(template_size, template_size),
fill_value=255,
dtype="uint8",
)
circle_centroid = (int(template_size / 2), int(template_size / 2))
circle_radius = max(int(template_size / 4) - 5, 5)
cv2.circle(image_circle, circle_centroid, circle_radius, 0, -1)
# add small blob noise effect
if random.random() > 0.7:
angle = random.randint(0, 360)
circle_centroid_small = (
int(
circle_centroid[0] + circle_radius * np.cos(np.radians(angle)),
),
int(
circle_centroid[1] + circle_radius * np.sin(np.radians(angle)),
),
)
circle_radius_small = max(
int(circle_radius * random.uniform(0.1, 0.5)),
2,
)
cv2.circle(
image_circle,
circle_centroid_small,
circle_radius_small,
0,
-1,
)
# random ring effect
new_centroid = [
circle_centroid[0] + (random.choice([1, -1]) * random.randint(1, 5)),
circle_centroid[1] + (random.choice([1, -1]) * random.randint(1, 5)),
]
image_circle = cv2.circle(
image_circle,
new_centroid,
int(circle_radius * random.uniform(0.8, 0.99)),
random.randint(0, 10),
random.randint(1, 5),
)
# add noise
self.add_noise(
image_circle,
random.uniform(0.01, 0.21),
(0, 255),
)
# create another copy of complement image
image_circle_complement = 255 - image_circle
# merge 2 circles to create non-perfect circle effect
image_circle[random_y:, random_x:] = np.maximum(
image_circle[random_y:, random_x:],
image_circle_complement[:-random_y, :-random_x],
)
# randomly rotate to get different direction effect
image_circle = np.rot90(image_circle, random.randint(1, 3))
# gaussian blur
image_circle = cv2.GaussianBlur(image_circle, (3, 3), 0)
# convert to bgr
image_circle_bgr = cv2.cvtColor(image_circle, cv2.COLOR_GRAY2BGR)
self.foreground.append(image_circle_bgr)
[docs]
def create_binding_holes(self, edge, ysize, xsize, ntimes):
"""Create effect of binding holes mark.
:param edge: The side of the binding effect.
:type edge: string
:param ysize: The height of the input image.
:type ysize: int
:param xsize: The width of the input image.
:type xsize: int
:param ntimes: The number of applied binding effect.
:type ntimes: int
"""
# reset
self.foreground = []
template_size = template_size_ori = 40
# scale template size based on image size
# 1000 * 800 is normal image size for template size = 40
# use max to prevent small template and min to prevent large template
template_size = int(
max(template_size_ori / 2, 40 * ((ysize * xsize) / (1000 * 800))),
)
template_size = int(min(template_size, template_size_ori * 2))
binding_effect = random.randint(0, 8)
offset_direction = random.choice([1, -1])
for i in range(ntimes):
random_scale = random.uniform(1, 2)
current_template_size = random.randint(int(template_size * 0.75), int(template_size * 1.25))
if self.width_range == "random":
template_size_x = current_template_size
else:
template_size_x = random.randint(self.width_range[0], self.width_range[1])
if self.height_range == "random":
template_size_y = int(current_template_size / 3)
else:
template_size_y = random.randint(self.height_range[0], self.height_range[1])
# draw rectangle
offset = int(min(template_size_x, template_size_y) / random.uniform(7, 8))
image_rectangle = np.full(
(template_size_y, int(template_size_x / random_scale)),
fill_value=255,
dtype="uint8",
)
image_rectangle[offset:-offset:, offset:-offset] = 0
new_offset = offset + random.randint(3, 6)
image_rectangle_complement = np.full(
(template_size_y, int(template_size_x / random_scale)),
fill_value=0,
dtype="uint8",
)
if binding_effect == 0:
x1 = new_offset + (offset_direction * random.randint(1, 3))
x2 = image_rectangle_complement.shape[1]
y1 = new_offset + (offset_direction * random.randint(1, 3))
y2 = image_rectangle_complement.shape[1]
elif binding_effect == 1:
x1 = new_offset + (offset_direction * random.randint(1, 3))
x2 = image_rectangle_complement.shape[1]
y1 = 0
y2 = -new_offset + (offset_direction * random.randint(1, 3))
elif binding_effect == 2:
x1 = 0
x2 = -new_offset + (offset_direction * random.randint(1, 3))
y1 = new_offset + (offset_direction * random.randint(1, 3))
y2 = image_rectangle_complement.shape[1]
elif binding_effect == 3:
x1 = 0
x2 = -new_offset + (offset_direction * random.randint(1, 3))
y1 = 0
y2 = -new_offset + (offset_direction * random.randint(1, 3))
elif binding_effect == 4:
x1 = new_offset + (offset_direction * random.randint(1, 3))
x2 = -new_offset + (offset_direction * random.randint(1, 3))
y1 = new_offset + (offset_direction * random.randint(1, 3))
y2 = -new_offset + (offset_direction * random.randint(1, 3))
elif binding_effect == 5:
x1 = new_offset
x2 = -new_offset
y1 = new_offset
y2 = -new_offset
else:
x1 = 0
x2 = 0
y1 = 0
y2 = 0
image_rectangle_complement[y1:y2:, x1:x2] = 255
self.add_noise(
image_rectangle,
random.uniform(0.01, 0.21),
(0, 255),
)
# merge 2 image to create offset effect
image_rectangle = np.maximum(
image_rectangle,
image_rectangle_complement,
)
# add noise and apply blur
self.add_noise(
image_rectangle,
random.uniform(0.01, 0.21),
(0, 255),
)
image_rectangle = cv2.GaussianBlur(
image_rectangle.astype("uint8"),
(3, 3),
cv2.BORDER_DEFAULT,
)
# rotate rectangle to create a more proper binding holes
if edge == "left":
image_rectangle = np.rot90(image_rectangle, 1)
elif edge == "right":
image_rectangle = np.rot90(image_rectangle, 1)
# convert to bgr
image_rectangle_bgr = cv2.cvtColor(image_rectangle, cv2.COLOR_GRAY2BGR)
self.foreground.append(image_rectangle_bgr)
[docs]
def create_clips(self, edge, ysize, xsize, ntimes, edge_offset):
"""Create effect of clip mark.
:param edge: The side of the binding effect.
:type edge: string
:param ysize: The height of the input image.
:type ysize: int
:param xsize: The width of the input image.
:type xsize: int
:param ntimes: The number of applied binding effect.
:type ntimes: int
:param edge_offset: Offset value from each edge.
:type edge_offset: int
"""
# reset
self.foreground = []
# minimum size
template_size = template_size_ori = 120
# scale template size based on image size
# 1000 * 800 is normal image size for template size = 60
# use max to prevent small template and min to prevent large template
template_size = int(
max(template_size_ori / 2, 120 * ((ysize * xsize) / (1000 * 800))),
)
template_size = int(min(template_size, template_size_ori * 2))
for _ in range(ntimes):
current_template_size = random.randint(int(template_size * 0.75), int(template_size * 1.25))
if self.width_range == "random":
template_size_x = current_template_size
else:
template_size_x = random.randint(self.width_range[0], self.width_range[1])
if self.height_range == "random":
template_size_y = int(current_template_size / 3)
else:
template_size_y = random.randint(self.height_range[0], self.height_range[1])
# canvas
image_clip = np.full(
(template_size_y, template_size_x),
fill_value=255,
dtype="uint8",
)
# canvas for inner circle
image_clip_inner = np.full(
(template_size_y, template_size_x),
fill_value=255,
dtype="uint8",
)
cy1 = int(template_size_y / 6)
cy2 = int(template_size_y * 5 / 6)
cx = int(template_size_x / 2)
# random thickness
current_thickness = random.randint(1, 4)
thickness_range = (current_thickness, current_thickness + 1)
# draw circle
circle_radius = int(min(template_size_y / 6, cx)) - 1
# top circle
cv2.circle(image_clip, (cx, cy1), circle_radius, 0, random.randint(thickness_range[0], thickness_range[1]))
# bottom circle
cv2.circle(image_clip, (cx, cy2), circle_radius, 0, random.randint(thickness_range[0], thickness_range[1]))
# create half circle
image_clip[cy1:cy2, :] = 255
# inner circle centroid
icx = cx
icy = cy2 - int(circle_radius / 2)
i_circle_radius = int(circle_radius * (2 / 3))
cv2.circle(
image_clip_inner,
(icx, icy),
i_circle_radius,
0,
random.randint(thickness_range[0], thickness_range[1]),
)
image_clip_inner[cy1:icy, :] = 255
# draw lines connecting 2 circles
left_x = int(np.floor(cx - circle_radius))
right_x = int(np.ceil(cx + circle_radius))
half_y_distance = int((cy2 - cy1) / 2)
x1 = right_x
x2 = left_x
y11 = cy1
y12 = cy2
y21 = cy1
y22 = cy2
top_bottom = random.choice([0, 1]) > 0
top_bottom = 0
if top_bottom:
y11 += half_y_distance
else:
y21 += half_y_distance
image_clip = cv2.line(
image_clip,
(x1, y11),
(x1, y12),
0,
random.randint(thickness_range[0], thickness_range[1]),
)
image_clip = cv2.line(
image_clip,
(x2, y21),
(x2, y22),
0,
random.randint(thickness_range[0], thickness_range[1]),
)
# draw inner circle lines
if top_bottom:
x11 = right_x
x12 = icx + i_circle_radius
y11 = cy1
y12 = icy
x21 = icx - i_circle_radius
x22 = icx - i_circle_radius
y21 = icy
y22 = cy1
else:
x11 = left_x
x12 = icx - i_circle_radius
y11 = cy1
y12 = icy
x21 = icx + i_circle_radius
x22 = icx + i_circle_radius
y21 = icy
y22 = cy1
image_clip_inner = cv2.line(
image_clip_inner,
(x11, y11),
(x12, y12),
0,
random.randint(thickness_range[0], thickness_range[1]),
)
image_clip_inner = cv2.line(
image_clip_inner,
(x21, y21),
(x22, y22),
0,
random.randint(thickness_range[0], thickness_range[1]),
)
# different clip mark effect if located at edges of image
select_inner = 0
if edge_offset == 0:
# remove the top part, to create an actual clip clipping the paper if edge offset is <5
image_clip = image_clip[y11:, :]
select_inner = random.random() > 0.5
# randomly choose between inner or exterior clips
if select_inner:
image_clip = image_clip_inner
else:
# merge 2 clips mark
image_clip = cv2.multiply(image_clip, image_clip_inner, scale=1 / 255)
# add a little bit of noise
self.add_noise(image_clip, 0.01, (220, 255))
# rotate the clip to create a more realistic effect
image_clip = rotate_image_PIL(
image_clip,
random.randint(self.angle_range[0], self.angle_range[1]),
background_value=255,
expand=1,
)
coordinates = np.where(image_clip == 0)
y_min = np.min(coordinates[0])
if edge_offset == 0 and select_inner:
y_min += i_circle_radius
image_clip = image_clip[y_min:, :]
# randomly flip
if random.choice([0, 1]) > 0:
image_clip = np.fliplr(image_clip)
# rotate the direction so that it looks like clipping the paper
if edge == "left":
image_clip = np.rot90(image_clip, 1)
elif edge == "right":
image_clip = np.rot90(image_clip, 3)
elif edge == "bottom":
image_clip = np.rot90(image_clip, 2)
# convert to bgr
image_clip_bgr = cv2.cvtColor(image_clip, cv2.COLOR_GRAY2BGR)
self.foreground.append(image_clip_bgr)
[docs]
def create_triangle_clips(self, edge, ysize, xsize, ntimes, edge_offset):
"""Create effect of triangle clip mark.
:param edge: The side of the binding effect.
:type edge: string
:param ysize: The height of the input image.
:type ysize: int
:param xsize: The width of the input image.
:type xsize: int
:param ntimes: The number of applied binding effect.
:type ntimes: int
:param edge_offset: Offset value from each edge.
:type edge_offset: int
"""
# reset
self.foreground = []
# minimum size
template_size = template_size_ori = 80
# scale template size based on image size
# 1000 * 800 is normal image size for template size = 60
# use max to prevent small template and min to prevent large template
template_size = int(
max(template_size_ori / 2, 80 * ((ysize * xsize) / (1000 * 800))),
)
template_size = int(min(template_size, template_size_ori * 2))
for _ in range(ntimes):
current_template_size = random.randint(int(template_size * 0.75), int(template_size * 1.25))
if self.width_range == "random":
template_size_x = int(current_template_size / 3)
else:
template_size_x = random.randint(self.width_range[0], self.width_range[1])
if self.height_range == "random":
template_size_y = current_template_size
else:
template_size_y = random.randint(self.height_range[0], self.height_range[1])
# canvas
image_clip = np.full(
(template_size_y, template_size_x),
fill_value=255,
dtype="uint8",
)
# canvas for inner circle
image_clip_inner = np.full(
(template_size_y, template_size_x),
fill_value=255,
dtype="uint8",
)
cy1 = int(template_size_y * 1 / 6)
cy2 = int(template_size_y * 5 / 6)
cx1 = int(template_size_x * 1 / 6)
cx2 = int(template_size_x * 1 / 2)
cx3 = int(template_size_x * 5 / 6)
# random thickness
current_thickness = random.randint(1, 4)
thickness_range = (current_thickness, current_thickness + 1)
# radius of circle
circle_radius = int(min(template_size_y / 6, template_size_x / 6)) - 1
# draw 3 circles
cv2.circle(image_clip, (cx1, cy1), circle_radius, 0, random.randint(thickness_range[0], thickness_range[1]))
cv2.circle(image_clip, (cx3, cy1), circle_radius, 0, random.randint(thickness_range[0], thickness_range[1]))
cv2.circle(image_clip, (cx2, cy2), circle_radius, 0, random.randint(thickness_range[0], thickness_range[1]))
image_clip[cy1 : cy1 + circle_radius + current_thickness + 1, :] = 255
image_clip[: cy1 + circle_radius + current_thickness + 1, cx1:cx3] = 255
image_clip[cy1:cy2, :] = 255
# draw inner circle
icx = cx2
icy = cy2 - int(circle_radius / 1)
i_circle_radius = int(circle_radius * 2 / 3)
cv2.circle(
image_clip_inner,
(icx, icy),
i_circle_radius,
0,
random.randint(thickness_range[0], thickness_range[1]),
)
image_clip_inner[cy1:icy, :] = 255
# lines
cv2.line(
image_clip,
(cx1, cy1 - circle_radius),
(cx3, cy1 - circle_radius),
0,
random.randint(thickness_range[0], thickness_range[1]),
)
cv2.line(
image_clip,
(cx1 - circle_radius, cy1 + circle_radius),
(cx2 - circle_radius, cy2),
0,
random.randint(thickness_range[0], thickness_range[1]),
)
cv2.line(
image_clip,
(cx3 + circle_radius, cy1),
(cx2 + circle_radius, cy2),
0,
random.randint(thickness_range[0], thickness_range[1]),
)
# inner lines
cv2.line(
image_clip_inner,
(cx1 - circle_radius, cy1),
(icx - i_circle_radius, icy),
0,
random.randint(thickness_range[0], thickness_range[1]),
)
cv2.line(
image_clip_inner,
(cx3, cy1 + circle_radius),
(icx + i_circle_radius, icy),
0,
random.randint(thickness_range[0], thickness_range[1]),
)
# different clip mark effect if located at edges of image
select_inner = 0
if edge_offset == 0:
image_clip = image_clip[cy1:, :]
select_inner = random.random() > 0.5
if select_inner:
image_clip = image_clip_inner
else:
# merge 2 clips mark
image_clip = cv2.multiply(image_clip, image_clip_inner)
# add a little bit of noise
self.add_noise(image_clip, 0.01, (220, 255))
# rotate the clip to create a more realistic effect
image_clip = rotate_image_PIL(
image_clip,
random.randint(self.angle_range[0], self.angle_range[1]),
background_value=255,
expand=1,
)
coordinates = np.where(image_clip == 0)
y_min = np.min(coordinates[0])
image_clip = image_clip[y_min:, :]
# randomly flip
if random.choice([0, 1]) > 0:
image_clip = np.fliplr(image_clip)
# rotate the direction so that it looks like clipping the paper
if edge == "left":
image_clip = np.rot90(image_clip, 1)
elif edge == "right":
image_clip = np.rot90(image_clip, 3)
elif edge == "bottom":
image_clip = np.rot90(image_clip, 2)
# image_clip = cv2.GaussianBlur(image_clip, (3,3), 0)
# convert to bgr
image_clip_bgr = cv2.cvtColor(image_clip, cv2.COLOR_GRAY2BGR)
self.foreground.append(image_clip_bgr)
[docs]
def create_foreground(self, image, edge, edge_offset):
"""Create foreground based on current input effect type.
:param image: The image to apply the function.
:type image: numpy.array (numpy.uint8)
:param image: The edge of the foreground.
:type image: string
"""
ysize, xsize = image.shape[:2]
if self.effect_type == "random" or self.effect_type not in (
"punch_holes",
"binding_holes",
"clips",
"triangle_clips",
):
effect_type = random.choice(("punch_holes", "binding_holes", "clips", "triangle_clips"))
else:
effect_type = self.effect_type
ntimes = random.randint(self.ntimes[0], self.ntimes[1])
if effect_type == "punch_holes":
self.create_punch_holes(ysize, xsize, ntimes)
elif effect_type == "binding_holes":
self.create_binding_holes(edge, ysize, xsize, ntimes)
elif effect_type == "clips":
self.create_clips(edge, ysize, xsize, ntimes, edge_offset)
elif effect_type == "triangle_clips":
self.create_triangle_clips(edge, ysize, xsize, ntimes, edge_offset)
[docs]
def retrieve_foreground(self):
"""Retrieve template foreground based on current input effect type."""
# Id for figshare published template files
article_ID = "16668964"
# create figshare downloader
fsdl = FigshareDownloader(directory="figshare_BindingsAndFasteners/")
# download files
fsdl.download_all_files_from_article(article_ID)
if self.effect_type == "random":
effect_type = random.choice(("punch_holes", "binding_holes", "clips"))
else:
effect_type = self.effect_type
# read foreground
if self.effect_type == "punch_holes":
foreground_path = os.path.join(
os.getcwd() + "/figshare_BindingsAndFasteners/punch_hole.png",
)
elif self.effect_type == "binding_holes":
foreground_path = os.path.join(
os.getcwd() + "/figshare_BindingsAndFasteners/binding_hole.png",
)
elif self.effect_type == "clips" or self.effect_type == "triangle_clips":
foreground_path = os.path.join(
os.getcwd() + "/figshare_BindingsAndFasteners/clip.png",
)
self.foreground = cv2.imread(foreground_path)
# Applies the Augmentation to input data.
def __call__(self, image, layer=None, mask=None, keypoints=None, bounding_boxes=None, force=False):
if force or self.should_run():
# reset foreground when the same class instance called twice
if not isinstance(self.foreground, str) and not isinstance(
self.foreground,
np.ndarray,
):
self.foreground = None
image = image.copy()
ysize, xsize = image.shape[:2]
# check for alpha layer
has_alpha = 0
if len(image.shape) > 2:
if image.shape[2] == 4:
has_alpha = 1
image, image_alpha = image[:, :, :3], image[:, :, 3]
# generate randomized overlay types
if self.overlay_types == "random":
overlay_types = random.choice(
(
"min",
"max",
"mix",
"normal",
"lighten",
"darken",
"addition",
"screen",
"dodge",
"multiply",
"divide",
"hard_light",
"grain_merge",
"overlay",
),
)
else:
overlay_types = self.overlay_types
# generate randomized edge
if self.edge == "random":
edge = random.choice(("left", "right", "top", "bottom"))
else:
edge = self.edge
# generate randomized ntimes
ntimes = random.randint(self.ntimes[0], self.ntimes[1])
# generate randomized offset
if self.edge_offset[0] < 1 and self.edge_offset[1] < 1:
self.edge_offset = list(self.edge_offset)
self.edge_offset[0] = np.ceil(self.edge_offset[0] * min(ysize, xsize))
self.edge_offset[1] = np.ceil(self.edge_offset[1] * min(ysize, xsize))
edge_offset = random.randint(self.edge_offset[0], self.edge_offset[1])
# if user input image path
if isinstance(self.foreground, str) and os.path.isfile(self.foreground):
self.foreground = cv2.imread(self.foreground)
ob = OverlayBuilder(
overlay_types,
self.foreground,
image,
ntimes,
self.nscales,
edge,
edge_offset,
1,
)
# if user input image
elif isinstance(self.foreground, np.ndarray):
ob = OverlayBuilder(
overlay_types,
self.foreground,
image,
ntimes,
self.nscales,
edge,
edge_offset,
1,
)
else:
# user didn't input foreground or not readable file, try to download from Figshare
use_figshare_library = self.use_figshare_library
if use_figshare_library:
try:
self.retrieve_foreground()
ob = OverlayBuilder(
overlay_types,
self.foreground,
image,
ntimes,
self.nscales,
edge,
edge_offset,
1,
)
# if failed to download from Figshare, set to create own foreground
except Exception:
use_figshare_library = 0
if not use_figshare_library:
self.create_foreground(image, edge, edge_offset)
ob = OverlayBuilder(
overlay_types,
self.foreground,
image,
ntimes,
self.nscales,
edge,
edge_offset,
1,
)
image_output = ob.build_overlay()
if has_alpha:
ysize, xsize = image_output.shape[:2]
if ysize != image_alpha.shape[0] or xsize != image_alpha.shape[1]:
image_alpha = cv2.resize(image_alpha, (xsize, ysize), interpolation=cv2.INTER_AREA)
image_output = np.dstack((image_output, image_alpha))
# check for additional output of mask, keypoints and bounding boxes
outputs_extra = []
if mask is not None or keypoints is not None or bounding_boxes is not None:
outputs_extra = [mask, keypoints, bounding_boxes]
# returns additional mask, keypoints and bounding boxes if there is additional input
if outputs_extra:
# returns in the format of [image, mask, keypoints, bounding_boxes]
return [image_output] + outputs_extra
else:
return image_output