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 curves based edge texture in PaperFactory. #394

Merged
merged 1 commit into from
Nov 7, 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
93 changes: 90 additions & 3 deletions augraphy/augmentations/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,8 +391,8 @@ def generate_texture(ysize, xsize, channel, value=255, sigma=1, turbulence=2):
return image_output


def generate_edge_texture(oxsize, oysize):
"""Generate a mask of edges based texture.
def generate_broken_edge_texture(oxsize, oysize):
"""Generate a mask of broken edges based texture.

:param oxsize: The width of the output mask.
:type oxsize: int
Expand Down Expand Up @@ -640,7 +640,7 @@ def generate_granular_texture(oxsize, oysize):
x_grid, y_grid = np.meshgrid(x_array, y_array)

# iterations for adding waves
iterations = random.randint(3, 5)
iterations = random.randint(1, 1)
wave_grid = np.zeros((ysize, xsize), dtype="float")
for i in range(iterations):

Expand Down Expand Up @@ -707,6 +707,93 @@ def generate_granular_texture(oxsize, oysize):
return wave_grid_output


def generate_curvy_edge_texture(oxsize, oysize):
"""Generate a masked of curves based edge texture using FFT.

:param oxsize: The width of the output texture image.
:type oxsize: int
:param oysize: The height of the output texture image.
:type oysize: int
"""

# fixed internal resolution
ysize, xsize = 500, 500

wave_grid_output = np.zeros((ysize, xsize), dtype="uint8")

for i in range(random.randint(1, 1)):
# fixed resolution of the wave image
resolution = random.uniform(0.9, 0.95)

# Create a 2D grid of coordinates
x_array = np.arange(-xsize / 2, xsize / 2) * resolution
y_array = np.arange(-ysize / 2, ysize / 2) * resolution
x_grid, y_grid = np.meshgrid(x_array, y_array)

wave_grid_fft_shifted = np.zeros((ysize, xsize), dtype="complex")
for i in range(random.randint(1, 1)):
# iterations for adding waves
iterations = random.randint(3, 5)
wave_grid = np.zeros((ysize, xsize), dtype="float")
for i in range(iterations):

# Calculate the wave height using a sine function
A = np.random.uniform(5, 15) # Amplitude
f = np.random.uniform(0.05, 0.1) # Frequency
p = np.random.uniform(0, 2 * np.pi) # Phase
kx = np.random.uniform(-1, 1) # x-component of wave vector
ky = np.random.uniform(-1, 1) # y-component of wave vector
h_sine = A * np.sin(2 * np.pi * (f * (kx * x_grid + ky * y_grid) - p))

# Calculate the wave height using a cosine function
A = np.random.uniform(5, 15) # Amplitude
f = np.random.uniform(0.05, 0.1) # Frequency
p = np.random.uniform(0, 2 * np.pi) # Phase
kx = np.random.uniform(-1, 1) # x-component of wave vector
ky = np.random.uniform(-1, 1) # y-component of wave vector
h_cosine = A * np.cos(2 * np.pi * (f * (kx * x_grid + ky * y_grid) - p))

# combine heights from sine and cosine
wave_grid += h_sine + h_cosine

# Compute the FFT of the wave heights, shift the zero-frequency component to the center and then sum them
wave_grid_fft_shifted += np.fft.fftshift(np.fft.fft2(wave_grid))

# unshift the FFT component
new_wave_grid = np.fft.ifft2(np.fft.ifftshift((wave_grid_fft_shifted)))

# get the real part only
new_wave_grid = np.real(new_wave_grid)

# scale to 0 -1
new_wave_grid = (new_wave_grid - new_wave_grid.min()) / (new_wave_grid.max() - new_wave_grid.min())

# convert to uint8
new_wave_grid = np.uint8(new_wave_grid * 255)

# merge into output
wave_grid_output += new_wave_grid

# blur to smoothen texture
wave_grid_output = cv2.GaussianBlur(wave_grid_output, (3, 3), 0)

# remove frequency > 100
frequency = 100
wave_grid_output = remove_frequency(wave_grid_output, frequency=frequency)

# rescale
wave_grid_output = (wave_grid_output - wave_grid_output.min()) / (wave_grid_output.max() - wave_grid_output.min())
wave_grid_output = np.uint8(wave_grid_output * 255)

# blur to smoothen trexture
wave_grid_output = cv2.GaussianBlur(wave_grid_output, (9, 9), 0)

# resize to output size
wave_grid_output = cv2.resize(wave_grid_output, (oxsize, oysize), interpolation=cv2.INTER_LINEAR)

return wave_grid_output


def remove_frequency(wave_grid_output, frequency):
"""Remove image area bigger than the input frequency by using FFT.

Expand Down
54 changes: 33 additions & 21 deletions augraphy/base/paperfactory.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
from augraphy.augmentations.brightness import Brightness
from augraphy.augmentations.colorpaper import ColorPaper
from augraphy.augmentations.lib import generate_average_intensity
from augraphy.augmentations.lib import generate_edge_texture
from augraphy.augmentations.lib import generate_broken_edge_texture
from augraphy.augmentations.lib import generate_curvy_edge_texture
from augraphy.augmentations.lib import generate_granular_texture
from augraphy.augmentations.lib import generate_stains_texture
from augraphy.augmentations.lib import generate_strange_texture
Expand Down Expand Up @@ -154,31 +155,42 @@ def generate_random_texture(self, image):
patch_number_height = int(ysize / patch_size)
texture = quilt_texture(texture, patch_size, patch_number_width, patch_number_height)

# add edges based texture
texture_edge = generate_edge_texture(texture.shape[1], texture.shape[0])
# randomize edge type
edge_type = random.choice([0, 1])

# get mask of edge texture
_, texture_edge_binary = cv2.threshold(texture_edge, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# generate broken edge texture
if edge_type == 0:

contours, hierarchy = cv2.findContours(
texture_edge_binary,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_NONE,
)
# add edges based texture
texture_edge = generate_broken_edge_texture(texture.shape[1], texture.shape[0])

# initialize mask of edge texture
texture_edge_mask = np.zeros_like(texture_edge, dtype="uint8")
# get mask of edge texture
_, texture_edge_binary = cv2.threshold(texture_edge, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

# find largest inner contour as edge texture
for contour in contours:
x0, y0, width, height = cv2.boundingRect(contour)
area = cv2.contourArea(contour)
if area > (ysize * xsize * 0.6) and width != xsize and height != ysize:
texture_edge_mask = cv2.drawContours(texture_edge_mask, [contour], -1, (255), cv2.FILLED)
break
contours, hierarchy = cv2.findContours(
texture_edge_binary,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_NONE,
)

# initialize mask of edge texture
texture_edge_mask = np.zeros_like(texture_edge, dtype="uint8")

# find largest inner contour as edge texture
for contour in contours:
x0, y0, width, height = cv2.boundingRect(contour)
area = cv2.contourArea(contour)
if area > (ysize * xsize * 0.6) and width != xsize and height != ysize:
texture_edge_mask = cv2.drawContours(texture_edge_mask, [contour], -1, (255), cv2.FILLED)
break

# remove area outside edge texture
texture[texture_edge_mask <= 0] = 0
# remove area outside edge texture
texture[texture_edge_mask <= 0] = 0

# generate curvy edge texture
else:
texture_edge = generate_curvy_edge_texture(texture.shape[1], texture.shape[0])
texture = cv2.multiply(texture, texture_edge, scale=1 / 255)

# randomly crop 1 or 2 side of edge
crop_x = int(xsize / 20)
Expand Down
Loading