|
""" |
|
Code adapted from https://github.com/rpautrat/SuperPoint |
|
Module used to generate geometrical synthetic shapes |
|
""" |
|
import math |
|
import cv2 as cv |
|
import numpy as np |
|
import shapely.geometry |
|
from itertools import combinations |
|
|
|
random_state = np.random.RandomState(None) |
|
|
|
|
|
def set_random_state(state): |
|
global random_state |
|
random_state = state |
|
|
|
|
|
def get_random_color(background_color): |
|
"""Output a random scalar in grayscale with a least a small contrast |
|
with the background color.""" |
|
color = random_state.randint(256) |
|
if abs(color - background_color) < 30: |
|
color = (color + 128) % 256 |
|
return color |
|
|
|
|
|
def get_different_color(previous_colors, min_dist=50, max_count=20): |
|
"""Output a color that contrasts with the previous colors. |
|
Parameters: |
|
previous_colors: np.array of the previous colors |
|
min_dist: the difference between the new color and |
|
the previous colors must be at least min_dist |
|
max_count: maximal number of iterations |
|
""" |
|
color = random_state.randint(256) |
|
count = 0 |
|
while np.any(np.abs(previous_colors - color) < min_dist) and count < max_count: |
|
count += 1 |
|
color = random_state.randint(256) |
|
return color |
|
|
|
|
|
def add_salt_and_pepper(img): |
|
"""Add salt and pepper noise to an image.""" |
|
noise = np.zeros((img.shape[0], img.shape[1]), dtype=np.uint8) |
|
cv.randu(noise, 0, 255) |
|
black = noise < 30 |
|
white = noise > 225 |
|
img[white > 0] = 255 |
|
img[black > 0] = 0 |
|
cv.blur(img, (5, 5), img) |
|
return np.empty((0, 2), dtype=np.int) |
|
|
|
|
|
def generate_background( |
|
size=(960, 1280), |
|
nb_blobs=100, |
|
min_rad_ratio=0.01, |
|
max_rad_ratio=0.05, |
|
min_kernel_size=50, |
|
max_kernel_size=300, |
|
): |
|
"""Generate a customized background image. |
|
Parameters: |
|
size: size of the image |
|
nb_blobs: number of circles to draw |
|
min_rad_ratio: the radius of blobs is at least min_rad_size * max(size) |
|
max_rad_ratio: the radius of blobs is at most max_rad_size * max(size) |
|
min_kernel_size: minimal size of the kernel |
|
max_kernel_size: maximal size of the kernel |
|
""" |
|
img = np.zeros(size, dtype=np.uint8) |
|
dim = max(size) |
|
cv.randu(img, 0, 255) |
|
cv.threshold(img, random_state.randint(256), 255, cv.THRESH_BINARY, img) |
|
background_color = int(np.mean(img)) |
|
blobs = np.concatenate( |
|
[ |
|
random_state.randint(0, size[1], size=(nb_blobs, 1)), |
|
random_state.randint(0, size[0], size=(nb_blobs, 1)), |
|
], |
|
axis=1, |
|
) |
|
for i in range(nb_blobs): |
|
col = get_random_color(background_color) |
|
cv.circle( |
|
img, |
|
(blobs[i][0], blobs[i][1]), |
|
np.random.randint(int(dim * min_rad_ratio), int(dim * max_rad_ratio)), |
|
col, |
|
-1, |
|
) |
|
kernel_size = random_state.randint(min_kernel_size, max_kernel_size) |
|
cv.blur(img, (kernel_size, kernel_size), img) |
|
return img |
|
|
|
|
|
def generate_custom_background( |
|
size, background_color, nb_blobs=3000, kernel_boundaries=(50, 100) |
|
): |
|
"""Generate a customized background to fill the shapes. |
|
Parameters: |
|
background_color: average color of the background image |
|
nb_blobs: number of circles to draw |
|
kernel_boundaries: interval of the possible sizes of the kernel |
|
""" |
|
img = np.zeros(size, dtype=np.uint8) |
|
img = img + get_random_color(background_color) |
|
blobs = np.concatenate( |
|
[ |
|
np.random.randint(0, size[1], size=(nb_blobs, 1)), |
|
np.random.randint(0, size[0], size=(nb_blobs, 1)), |
|
], |
|
axis=1, |
|
) |
|
for i in range(nb_blobs): |
|
col = get_random_color(background_color) |
|
cv.circle(img, (blobs[i][0], blobs[i][1]), np.random.randint(20), col, -1) |
|
kernel_size = np.random.randint(kernel_boundaries[0], kernel_boundaries[1]) |
|
cv.blur(img, (kernel_size, kernel_size), img) |
|
return img |
|
|
|
|
|
def final_blur(img, kernel_size=(5, 5)): |
|
"""Gaussian blur applied to an image. |
|
Parameters: |
|
kernel_size: size of the kernel |
|
""" |
|
cv.GaussianBlur(img, kernel_size, 0, img) |
|
|
|
|
|
def ccw(A, B, C, dim): |
|
"""Check if the points are listed in counter-clockwise order.""" |
|
if dim == 2: |
|
return (C[:, 1] - A[:, 1]) * (B[:, 0] - A[:, 0]) > (B[:, 1] - A[:, 1]) * ( |
|
C[:, 0] - A[:, 0] |
|
) |
|
else: |
|
return (C[:, 1, :] - A[:, 1, :]) * (B[:, 0, :] - A[:, 0, :]) > ( |
|
B[:, 1, :] - A[:, 1, :] |
|
) * (C[:, 0, :] - A[:, 0, :]) |
|
|
|
|
|
def intersect(A, B, C, D, dim): |
|
"""Return true if line segments AB and CD intersect""" |
|
return np.any( |
|
(ccw(A, C, D, dim) != ccw(B, C, D, dim)) |
|
& (ccw(A, B, C, dim) != ccw(A, B, D, dim)) |
|
) |
|
|
|
|
|
def keep_points_inside(points, size): |
|
"""Keep only the points whose coordinates are inside the dimensions of |
|
the image of size 'size'""" |
|
mask = ( |
|
(points[:, 0] >= 0) |
|
& (points[:, 0] < size[1]) |
|
& (points[:, 1] >= 0) |
|
& (points[:, 1] < size[0]) |
|
) |
|
return points[mask, :] |
|
|
|
|
|
def get_unique_junctions(segments, min_label_len): |
|
"""Get unique junction points from line segments.""" |
|
|
|
junctions_all = np.concatenate((segments[:, :2], segments[:, 2:]), axis=0) |
|
if junctions_all.shape[0] == 0: |
|
junc_points = None |
|
line_map = None |
|
|
|
|
|
else: |
|
junc_points = np.unique(junctions_all, axis=0) |
|
|
|
line_map = get_line_map(junc_points, segments) |
|
|
|
return junc_points, line_map |
|
|
|
|
|
def get_line_map(points: np.ndarray, segments: np.ndarray) -> np.ndarray: |
|
"""Get line map given the points and segment sets.""" |
|
|
|
num_point = points.shape[0] |
|
line_map = np.zeros([num_point, num_point]) |
|
|
|
|
|
for idx in range(segments.shape[0]): |
|
|
|
seg = segments[idx, :] |
|
junction1 = seg[:2] |
|
junction2 = seg[2:] |
|
|
|
|
|
idx_junction1 = np.where((points == junction1).sum(axis=1) == 2)[0] |
|
idx_junction2 = np.where((points == junction2).sum(axis=1) == 2)[0] |
|
|
|
|
|
line_map[idx_junction1, idx_junction2] = 1 |
|
line_map[idx_junction2, idx_junction1] = 1 |
|
|
|
return line_map |
|
|
|
|
|
def get_line_heatmap(junctions, line_map, size=[480, 640], thickness=1): |
|
"""Get line heat map from junctions and line map.""" |
|
|
|
if not isinstance(thickness, int): |
|
thickness = int(thickness) |
|
|
|
|
|
if not junctions.dtype == np.int: |
|
junctions = (np.round(junctions)).astype(np.int) |
|
|
|
|
|
heat_map = np.zeros(size) |
|
|
|
if junctions.shape[0] > 0: |
|
|
|
for idx in range(junctions.shape[0]): |
|
|
|
if line_map[idx, :].sum() == 0: |
|
continue |
|
|
|
else: |
|
|
|
for idx2 in np.where(line_map[idx, :] == 1)[0]: |
|
point1 = junctions[idx, :] |
|
point2 = junctions[idx2, :] |
|
|
|
|
|
cv.line(heat_map, tuple(point1), tuple(point2), 1.0, thickness) |
|
|
|
return heat_map |
|
|
|
|
|
def draw_lines(img, nb_lines=10, min_len=32, min_label_len=32): |
|
"""Draw random lines and output the positions of the pair of junctions |
|
and line associativities. |
|
Parameters: |
|
nb_lines: maximal number of lines |
|
""" |
|
|
|
num_lines = random_state.randint(1, nb_lines) |
|
segments = np.empty((0, 4), dtype=np.int) |
|
points = np.empty((0, 2), dtype=np.int) |
|
background_color = int(np.mean(img)) |
|
min_dim = min(img.shape) |
|
|
|
|
|
if isinstance(min_len, float) and min_len <= 1.0: |
|
min_len = int(min_dim * min_len) |
|
if isinstance(min_label_len, float) and min_label_len <= 1.0: |
|
min_label_len = int(min_dim * min_label_len) |
|
|
|
|
|
for i in range(num_lines): |
|
x1 = random_state.randint(img.shape[1]) |
|
y1 = random_state.randint(img.shape[0]) |
|
p1 = np.array([[x1, y1]]) |
|
x2 = random_state.randint(img.shape[1]) |
|
y2 = random_state.randint(img.shape[0]) |
|
p2 = np.array([[x2, y2]]) |
|
|
|
|
|
line_length = np.sqrt(np.sum((p1 - p2) ** 2)) |
|
if line_length < min_len: |
|
continue |
|
|
|
|
|
if intersect(segments[:, 0:2], segments[:, 2:4], p1, p2, 2): |
|
continue |
|
|
|
col = get_random_color(background_color) |
|
thickness = random_state.randint(min_dim * 0.01, min_dim * 0.02) |
|
cv.line(img, (x1, y1), (x2, y2), col, thickness) |
|
|
|
|
|
seg_len = math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2) |
|
if seg_len >= min_label_len: |
|
segments = np.concatenate([segments, np.array([[x1, y1, x2, y2]])], axis=0) |
|
points = np.concatenate([points, np.array([[x1, y1], [x2, y2]])], axis=0) |
|
|
|
|
|
if points.shape[0] == 0: |
|
return draw_lines(img, nb_lines, min_len, min_label_len) |
|
|
|
|
|
line_map = get_line_map(points, segments) |
|
|
|
return {"points": points, "line_map": line_map} |
|
|
|
|
|
def check_segment_len(segments, min_len=32): |
|
"""Check if one of the segments is too short (True means too short).""" |
|
point1_vec = segments[:, :2] |
|
point2_vec = segments[:, 2:] |
|
diff = point1_vec - point2_vec |
|
|
|
dist = np.sqrt(np.sum(diff**2, axis=1)) |
|
if np.any(dist < min_len): |
|
return True |
|
else: |
|
return False |
|
|
|
|
|
def draw_polygon(img, max_sides=8, min_len=32, min_label_len=64): |
|
"""Draw a polygon with a random number of corners and return the position |
|
of the junctions + line map. |
|
Parameters: |
|
max_sides: maximal number of sides + 1 |
|
""" |
|
num_corners = random_state.randint(3, max_sides) |
|
min_dim = min(img.shape[0], img.shape[1]) |
|
rad = max(random_state.rand() * min_dim / 2, min_dim / 10) |
|
|
|
x = random_state.randint(rad, img.shape[1] - rad) |
|
y = random_state.randint(rad, img.shape[0] - rad) |
|
|
|
|
|
if isinstance(min_len, float) and min_len <= 1.0: |
|
min_len = int(min_dim * min_len) |
|
if isinstance(min_label_len, float) and min_label_len <= 1.0: |
|
min_label_len = int(min_dim * min_label_len) |
|
|
|
|
|
slices = np.linspace(0, 2 * math.pi, num_corners + 1) |
|
angles = [ |
|
slices[i] + random_state.rand() * (slices[i + 1] - slices[i]) |
|
for i in range(num_corners) |
|
] |
|
points = np.array( |
|
[ |
|
[ |
|
int(x + max(random_state.rand(), 0.4) * rad * math.cos(a)), |
|
int(y + max(random_state.rand(), 0.4) * rad * math.sin(a)), |
|
] |
|
for a in angles |
|
] |
|
) |
|
|
|
|
|
norms = [ |
|
np.linalg.norm(points[(i - 1) % num_corners, :] - points[i, :]) |
|
for i in range(num_corners) |
|
] |
|
mask = np.array(norms) > 0.01 |
|
points = points[mask, :] |
|
num_corners = points.shape[0] |
|
corner_angles = [ |
|
angle_between_vectors( |
|
points[(i - 1) % num_corners, :] - points[i, :], |
|
points[(i + 1) % num_corners, :] - points[i, :], |
|
) |
|
for i in range(num_corners) |
|
] |
|
mask = np.array(corner_angles) < (2 * math.pi / 3) |
|
points = points[mask, :] |
|
num_corners = points.shape[0] |
|
|
|
|
|
segments = np.zeros([0, 4]) |
|
|
|
segments_raw = np.zeros([0, 4]) |
|
for idx in range(num_corners): |
|
if idx == (num_corners - 1): |
|
p1 = points[idx] |
|
p2 = points[0] |
|
else: |
|
p1 = points[idx] |
|
p2 = points[idx + 1] |
|
|
|
segment = np.concatenate((p1, p2), axis=0) |
|
|
|
seg_len = np.sqrt(np.sum((p1 - p2) ** 2)) |
|
if seg_len >= min_label_len: |
|
segments = np.concatenate((segments, segment[None, ...]), axis=0) |
|
segments_raw = np.concatenate((segments_raw, segment[None, ...]), axis=0) |
|
|
|
|
|
if (num_corners < 3) or check_segment_len(segments_raw, min_len): |
|
return draw_polygon(img, max_sides, min_len, min_label_len) |
|
|
|
|
|
junctions_all = np.concatenate((segments[:, :2], segments[:, 2:]), axis=0) |
|
if junctions_all.shape[0] == 0: |
|
junc_points = None |
|
line_map = None |
|
|
|
else: |
|
junc_points = np.unique(junctions_all, axis=0) |
|
|
|
|
|
line_map = get_line_map(junc_points, segments) |
|
|
|
corners = points.reshape((-1, 1, 2)) |
|
col = get_random_color(int(np.mean(img))) |
|
cv.fillPoly(img, [corners], col) |
|
|
|
return {"points": junc_points, "line_map": line_map} |
|
|
|
|
|
def overlap(center, rad, centers, rads): |
|
"""Check that the circle with (center, rad) |
|
doesn't overlap with the other circles.""" |
|
flag = False |
|
for i in range(len(rads)): |
|
if np.linalg.norm(center - centers[i]) < rad + rads[i]: |
|
flag = True |
|
break |
|
return flag |
|
|
|
|
|
def angle_between_vectors(v1, v2): |
|
"""Compute the angle (in rad) between the two vectors v1 and v2.""" |
|
v1_u = v1 / np.linalg.norm(v1) |
|
v2_u = v2 / np.linalg.norm(v2) |
|
return np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0)) |
|
|
|
|
|
def draw_multiple_polygons( |
|
img, |
|
max_sides=8, |
|
nb_polygons=30, |
|
min_len=32, |
|
min_label_len=64, |
|
safe_margin=5, |
|
**extra |
|
): |
|
"""Draw multiple polygons with a random number of corners |
|
and return the junction points + line map. |
|
Parameters: |
|
max_sides: maximal number of sides + 1 |
|
nb_polygons: maximal number of polygons |
|
""" |
|
segments = np.empty((0, 4), dtype=np.int) |
|
label_segments = np.empty((0, 4), dtype=np.int) |
|
centers = [] |
|
rads = [] |
|
points = np.empty((0, 2), dtype=np.int) |
|
background_color = int(np.mean(img)) |
|
|
|
min_dim = min(img.shape[0], img.shape[1]) |
|
|
|
if isinstance(min_len, float) and min_len <= 1.0: |
|
min_len = int(min_dim * min_len) |
|
if isinstance(min_label_len, float) and min_label_len <= 1.0: |
|
min_label_len = int(min_dim * min_label_len) |
|
if isinstance(safe_margin, float) and safe_margin <= 1.0: |
|
safe_margin = int(min_dim * safe_margin) |
|
|
|
|
|
for i in range(nb_polygons): |
|
num_corners = random_state.randint(3, max_sides) |
|
min_dim = min(img.shape[0], img.shape[1]) |
|
|
|
|
|
rad = max(random_state.rand() * min_dim / 2, min_dim / 9) |
|
rad_real = rad - safe_margin |
|
|
|
|
|
x = random_state.randint(rad, img.shape[1] - rad) |
|
y = random_state.randint(rad, img.shape[0] - rad) |
|
|
|
|
|
slices = np.linspace(0, 2 * math.pi, num_corners + 1) |
|
angles = [ |
|
slices[i] + random_state.rand() * (slices[i + 1] - slices[i]) |
|
for i in range(num_corners) |
|
] |
|
|
|
|
|
new_points = [] |
|
new_points_real = [] |
|
for a in angles: |
|
x_offset = max(random_state.rand(), 0.4) |
|
y_offset = max(random_state.rand(), 0.4) |
|
new_points.append( |
|
[ |
|
int(x + x_offset * rad * math.cos(a)), |
|
int(y + y_offset * rad * math.sin(a)), |
|
] |
|
) |
|
new_points_real.append( |
|
[ |
|
int(x + x_offset * rad_real * math.cos(a)), |
|
int(y + y_offset * rad_real * math.sin(a)), |
|
] |
|
) |
|
new_points = np.array(new_points) |
|
new_points_real = np.array(new_points_real) |
|
|
|
|
|
norms = [ |
|
np.linalg.norm(new_points[(i - 1) % num_corners, :] - new_points[i, :]) |
|
for i in range(num_corners) |
|
] |
|
mask = np.array(norms) > 0.01 |
|
new_points = new_points[mask, :] |
|
new_points_real = new_points_real[mask, :] |
|
|
|
num_corners = new_points.shape[0] |
|
corner_angles = [ |
|
angle_between_vectors( |
|
new_points[(i - 1) % num_corners, :] - new_points[i, :], |
|
new_points[(i + 1) % num_corners, :] - new_points[i, :], |
|
) |
|
for i in range(num_corners) |
|
] |
|
mask = np.array(corner_angles) < (2 * math.pi / 3) |
|
new_points = new_points[mask, :] |
|
new_points_real = new_points_real[mask, :] |
|
num_corners = new_points.shape[0] |
|
|
|
|
|
if num_corners < 3: |
|
continue |
|
|
|
|
|
new_segments = np.zeros((1, 4, num_corners)) |
|
new_segments[:, 0, :] = [new_points[i][0] for i in range(num_corners)] |
|
new_segments[:, 1, :] = [new_points[i][1] for i in range(num_corners)] |
|
new_segments[:, 2, :] = [ |
|
new_points[(i + 1) % num_corners][0] for i in range(num_corners) |
|
] |
|
new_segments[:, 3, :] = [ |
|
new_points[(i + 1) % num_corners][1] for i in range(num_corners) |
|
] |
|
|
|
|
|
new_segments_real = np.zeros((1, 4, num_corners)) |
|
new_segments_real[:, 0, :] = [new_points_real[i][0] for i in range(num_corners)] |
|
new_segments_real[:, 1, :] = [new_points_real[i][1] for i in range(num_corners)] |
|
new_segments_real[:, 2, :] = [ |
|
new_points_real[(i + 1) % num_corners][0] for i in range(num_corners) |
|
] |
|
new_segments_real[:, 3, :] = [ |
|
new_points_real[(i + 1) % num_corners][1] for i in range(num_corners) |
|
] |
|
|
|
|
|
if intersect( |
|
segments[:, 0:2, None], |
|
segments[:, 2:4, None], |
|
new_segments[:, 0:2, :], |
|
new_segments[:, 2:4, :], |
|
3, |
|
) or overlap(np.array([x, y]), rad, centers, rads): |
|
continue |
|
|
|
|
|
if check_segment_len(new_segments_real, min_len): |
|
continue |
|
|
|
|
|
centers.append(np.array([x, y])) |
|
rads.append(rad) |
|
new_segments = np.reshape(np.swapaxes(new_segments, 0, 2), (-1, 4)) |
|
segments = np.concatenate([segments, new_segments], axis=0) |
|
|
|
|
|
new_segments_real = np.reshape(np.swapaxes(new_segments_real, 0, 2), (-1, 4)) |
|
points1 = new_segments_real[:, :2] |
|
points2 = new_segments_real[:, 2:] |
|
seg_len = np.sqrt(np.sum((points1 - points2) ** 2, axis=1)) |
|
new_label_segment = new_segments_real[seg_len >= min_label_len, :] |
|
label_segments = np.concatenate([label_segments, new_label_segment], axis=0) |
|
|
|
|
|
corners = new_points_real.reshape((-1, 1, 2)) |
|
mask = np.zeros(img.shape, np.uint8) |
|
custom_background = generate_custom_background( |
|
img.shape, background_color, **extra |
|
) |
|
|
|
cv.fillPoly(mask, [corners], 255) |
|
locs = np.where(mask != 0) |
|
img[locs[0], locs[1]] = custom_background[locs[0], locs[1]] |
|
points = np.concatenate([points, new_points], axis=0) |
|
|
|
|
|
junctions_all = np.concatenate( |
|
(label_segments[:, :2], label_segments[:, 2:]), axis=0 |
|
) |
|
if junctions_all.shape[0] == 0: |
|
junc_points = None |
|
line_map = None |
|
|
|
else: |
|
junc_points = np.unique(junctions_all, axis=0) |
|
|
|
|
|
line_map = get_line_map(junc_points, label_segments) |
|
|
|
return {"points": junc_points, "line_map": line_map} |
|
|
|
|
|
def draw_ellipses(img, nb_ellipses=20): |
|
"""Draw several ellipses. |
|
Parameters: |
|
nb_ellipses: maximal number of ellipses |
|
""" |
|
centers = np.empty((0, 2), dtype=np.int) |
|
rads = np.empty((0, 1), dtype=np.int) |
|
min_dim = min(img.shape[0], img.shape[1]) / 4 |
|
background_color = int(np.mean(img)) |
|
for i in range(nb_ellipses): |
|
ax = int(max(random_state.rand() * min_dim, min_dim / 5)) |
|
ay = int(max(random_state.rand() * min_dim, min_dim / 5)) |
|
max_rad = max(ax, ay) |
|
x = random_state.randint(max_rad, img.shape[1] - max_rad) |
|
y = random_state.randint(max_rad, img.shape[0] - max_rad) |
|
new_center = np.array([[x, y]]) |
|
|
|
|
|
diff = centers - new_center |
|
if np.any(max_rad > (np.sqrt(np.sum(diff * diff, axis=1)) - rads)): |
|
continue |
|
centers = np.concatenate([centers, new_center], axis=0) |
|
rads = np.concatenate([rads, np.array([[max_rad]])], axis=0) |
|
|
|
col = get_random_color(background_color) |
|
angle = random_state.rand() * 90 |
|
cv.ellipse(img, (x, y), (ax, ay), angle, 0, 360, col, -1) |
|
return np.empty((0, 2), dtype=np.int) |
|
|
|
|
|
def draw_star(img, nb_branches=6, min_len=32, min_label_len=64): |
|
"""Draw a star and return the junction points + line map. |
|
Parameters: |
|
nb_branches: number of branches of the star |
|
""" |
|
num_branches = random_state.randint(3, nb_branches) |
|
min_dim = min(img.shape[0], img.shape[1]) |
|
|
|
if isinstance(min_len, float) and min_len <= 1.0: |
|
min_len = int(min_dim * min_len) |
|
if isinstance(min_label_len, float) and min_label_len <= 1.0: |
|
min_label_len = int(min_dim * min_label_len) |
|
|
|
thickness = random_state.randint(min_dim * 0.01, min_dim * 0.025) |
|
rad = max(random_state.rand() * min_dim / 2, min_dim / 5) |
|
x = random_state.randint(rad, img.shape[1] - rad) |
|
y = random_state.randint(rad, img.shape[0] - rad) |
|
|
|
slices = np.linspace(0, 2 * math.pi, num_branches + 1) |
|
angles = [ |
|
slices[i] + random_state.rand() * (slices[i + 1] - slices[i]) |
|
for i in range(num_branches) |
|
] |
|
points = np.array( |
|
[ |
|
[ |
|
int(x + max(random_state.rand(), 0.3) * rad * math.cos(a)), |
|
int(y + max(random_state.rand(), 0.3) * rad * math.sin(a)), |
|
] |
|
for a in angles |
|
] |
|
) |
|
points = np.concatenate(([[x, y]], points), axis=0) |
|
|
|
|
|
segments = np.array([[x, y, _[0], _[1]] for _ in points[1:, :]]) |
|
if check_segment_len(segments, min_len): |
|
return draw_star(img, nb_branches, min_len, min_label_len) |
|
|
|
|
|
points1 = segments[:, :2] |
|
points2 = segments[:, 2:] |
|
seg_len = np.sqrt(np.sum((points1 - points2) ** 2, axis=1)) |
|
label_segments = segments[seg_len >= min_label_len, :] |
|
|
|
|
|
junctions_all = np.concatenate( |
|
(label_segments[:, :2], label_segments[:, 2:]), axis=0 |
|
) |
|
if junctions_all.shape[0] == 0: |
|
junc_points = None |
|
line_map = None |
|
|
|
|
|
else: |
|
junc_points = np.unique(junctions_all, axis=0) |
|
|
|
line_map = get_line_map(junc_points, label_segments) |
|
|
|
background_color = int(np.mean(img)) |
|
for i in range(1, num_branches + 1): |
|
col = get_random_color(background_color) |
|
cv.line( |
|
img, |
|
(points[0][0], points[0][1]), |
|
(points[i][0], points[i][1]), |
|
col, |
|
thickness, |
|
) |
|
return {"points": junc_points, "line_map": line_map} |
|
|
|
|
|
def draw_checkerboard_multiseg( |
|
img, |
|
max_rows=7, |
|
max_cols=7, |
|
transform_params=(0.05, 0.15), |
|
min_label_len=64, |
|
seed=None, |
|
): |
|
"""Draw a checkerboard and output the junctions + line segments |
|
Parameters: |
|
max_rows: maximal number of rows + 1 |
|
max_cols: maximal number of cols + 1 |
|
transform_params: set the range of the parameters of the transformations |
|
""" |
|
if seed is None: |
|
global random_state |
|
else: |
|
random_state = np.random.RandomState(seed) |
|
|
|
background_color = int(np.mean(img)) |
|
|
|
min_dim = min(img.shape) |
|
if isinstance(min_label_len, float) and min_label_len <= 1.0: |
|
min_label_len = int(min_dim * min_label_len) |
|
|
|
rows = random_state.randint(3, max_rows) |
|
cols = random_state.randint(3, max_cols) |
|
s = min((img.shape[1] - 1) // cols, (img.shape[0] - 1) // rows) |
|
x_coord = np.tile(range(cols + 1), rows + 1).reshape(((rows + 1) * (cols + 1), 1)) |
|
y_coord = np.repeat(range(rows + 1), cols + 1).reshape(((rows + 1) * (cols + 1), 1)) |
|
|
|
points = s * np.concatenate([x_coord, y_coord], axis=1) |
|
|
|
|
|
alpha_affine = np.max(img.shape) * ( |
|
transform_params[0] + random_state.rand() * transform_params[1] |
|
) |
|
center_square = np.float32(img.shape) // 2 |
|
min_dim = min(img.shape) |
|
square_size = min_dim // 3 |
|
pts1 = np.float32( |
|
[ |
|
center_square + square_size, |
|
[center_square[0] + square_size, center_square[1] - square_size], |
|
center_square - square_size, |
|
[center_square[0] - square_size, center_square[1] + square_size], |
|
] |
|
) |
|
pts2 = pts1 + random_state.uniform( |
|
-alpha_affine, alpha_affine, size=pts1.shape |
|
).astype(np.float32) |
|
affine_transform = cv.getAffineTransform(pts1[:3], pts2[:3]) |
|
pts2 = pts1 + random_state.uniform( |
|
-alpha_affine / 2, alpha_affine / 2, size=pts1.shape |
|
).astype(np.float32) |
|
perspective_transform = cv.getPerspectiveTransform(pts1, pts2) |
|
|
|
|
|
points = np.transpose( |
|
np.concatenate((points, np.ones(((rows + 1) * (cols + 1), 1))), axis=1) |
|
) |
|
warped_points = np.transpose(np.dot(affine_transform, points)) |
|
|
|
|
|
warped_col0 = np.add( |
|
np.sum(np.multiply(warped_points, perspective_transform[0, :2]), axis=1), |
|
perspective_transform[0, 2], |
|
) |
|
warped_col1 = np.add( |
|
np.sum(np.multiply(warped_points, perspective_transform[1, :2]), axis=1), |
|
perspective_transform[1, 2], |
|
) |
|
warped_col2 = np.add( |
|
np.sum(np.multiply(warped_points, perspective_transform[2, :2]), axis=1), |
|
perspective_transform[2, 2], |
|
) |
|
warped_col0 = np.divide(warped_col0, warped_col2) |
|
warped_col1 = np.divide(warped_col1, warped_col2) |
|
warped_points = np.concatenate([warped_col0[:, None], warped_col1[:, None]], axis=1) |
|
warped_points_float = warped_points.copy() |
|
warped_points = warped_points.astype(int) |
|
|
|
|
|
colors = np.zeros((rows * cols,), np.int32) |
|
for i in range(rows): |
|
for j in range(cols): |
|
|
|
if i == 0 and j == 0: |
|
col = get_random_color(background_color) |
|
else: |
|
neighboring_colors = [] |
|
if i != 0: |
|
neighboring_colors.append(colors[(i - 1) * cols + j]) |
|
if j != 0: |
|
neighboring_colors.append(colors[i * cols + j - 1]) |
|
col = get_different_color(np.array(neighboring_colors)) |
|
colors[i * cols + j] = col |
|
|
|
|
|
cv.fillConvexPoly( |
|
img, |
|
np.array( |
|
[ |
|
( |
|
warped_points[i * (cols + 1) + j, 0], |
|
warped_points[i * (cols + 1) + j, 1], |
|
), |
|
( |
|
warped_points[i * (cols + 1) + j + 1, 0], |
|
warped_points[i * (cols + 1) + j + 1, 1], |
|
), |
|
( |
|
warped_points[(i + 1) * (cols + 1) + j + 1, 0], |
|
warped_points[(i + 1) * (cols + 1) + j + 1, 1], |
|
), |
|
( |
|
warped_points[(i + 1) * (cols + 1) + j, 0], |
|
warped_points[(i + 1) * (cols + 1) + j, 1], |
|
), |
|
] |
|
), |
|
col, |
|
) |
|
|
|
label_segments = np.empty([0, 4], dtype=np.int) |
|
|
|
for row_idx in range(rows + 1): |
|
|
|
|
|
multi_seg_lst = [ |
|
np.array( |
|
[ |
|
warped_points_float[id1, 0], |
|
warped_points_float[id1, 1], |
|
warped_points_float[id2, 0], |
|
warped_points_float[id2, 1], |
|
] |
|
)[None, ...] |
|
for (id1, id2) in combinations( |
|
range(row_idx * (cols + 1), (row_idx + 1) * (cols + 1), 1), 2 |
|
) |
|
] |
|
multi_seg = np.concatenate(multi_seg_lst, axis=0) |
|
label_segments = np.concatenate((label_segments, multi_seg), axis=0) |
|
|
|
|
|
for col_idx in range(cols + 1): |
|
|
|
|
|
multi_seg_lst = [ |
|
np.array( |
|
[ |
|
warped_points_float[id1, 0], |
|
warped_points_float[id1, 1], |
|
warped_points_float[id2, 0], |
|
warped_points_float[id2, 1], |
|
] |
|
)[None, ...] |
|
for (id1, id2) in combinations( |
|
range(col_idx, col_idx + ((rows + 1) * (cols + 1)), cols + 1), 2 |
|
) |
|
] |
|
multi_seg = np.concatenate(multi_seg_lst, axis=0) |
|
label_segments = np.concatenate((label_segments, multi_seg), axis=0) |
|
|
|
label_segments_filtered = np.zeros([0, 4]) |
|
|
|
image_poly = shapely.geometry.Polygon( |
|
[ |
|
[0, 0], |
|
[img.shape[1] - 1, 0], |
|
[img.shape[1] - 1, img.shape[0] - 1], |
|
[0, img.shape[0] - 1], |
|
] |
|
) |
|
for idx in range(label_segments.shape[0]): |
|
|
|
seg_raw = label_segments[idx, :] |
|
seg = shapely.geometry.LineString([seg_raw[:2], seg_raw[2:]]) |
|
|
|
|
|
if seg.intersection(image_poly) == seg: |
|
label_segments_filtered = np.concatenate( |
|
(label_segments_filtered, seg_raw[None, ...]), axis=0 |
|
) |
|
|
|
|
|
elif seg.intersects(image_poly): |
|
|
|
try: |
|
p = np.array(seg.intersection(image_poly).coords).reshape([-1, 4]) |
|
|
|
except: |
|
continue |
|
segment = p |
|
label_segments_filtered = np.concatenate( |
|
(label_segments_filtered, segment), axis=0 |
|
) |
|
|
|
else: |
|
continue |
|
|
|
label_segments = np.round(label_segments_filtered).astype(np.int) |
|
|
|
|
|
points1 = label_segments[:, :2] |
|
points2 = label_segments[:, 2:] |
|
seg_len = np.sqrt(np.sum((points1 - points2) ** 2, axis=1)) |
|
label_segments = label_segments[seg_len >= min_label_len, :] |
|
|
|
|
|
junc_points, line_map = get_unique_junctions(label_segments, min_label_len) |
|
|
|
|
|
nb_rows = random_state.randint(2, rows + 2) |
|
nb_cols = random_state.randint(2, cols + 2) |
|
thickness = random_state.randint(min_dim * 0.01, min_dim * 0.015) |
|
for _ in range(nb_rows): |
|
row_idx = random_state.randint(rows + 1) |
|
col_idx1 = random_state.randint(cols + 1) |
|
col_idx2 = random_state.randint(cols + 1) |
|
col = get_random_color(background_color) |
|
cv.line( |
|
img, |
|
( |
|
warped_points[row_idx * (cols + 1) + col_idx1, 0], |
|
warped_points[row_idx * (cols + 1) + col_idx1, 1], |
|
), |
|
( |
|
warped_points[row_idx * (cols + 1) + col_idx2, 0], |
|
warped_points[row_idx * (cols + 1) + col_idx2, 1], |
|
), |
|
col, |
|
thickness, |
|
) |
|
for _ in range(nb_cols): |
|
col_idx = random_state.randint(cols + 1) |
|
row_idx1 = random_state.randint(rows + 1) |
|
row_idx2 = random_state.randint(rows + 1) |
|
col = get_random_color(background_color) |
|
cv.line( |
|
img, |
|
( |
|
warped_points[row_idx1 * (cols + 1) + col_idx, 0], |
|
warped_points[row_idx1 * (cols + 1) + col_idx, 1], |
|
), |
|
( |
|
warped_points[row_idx2 * (cols + 1) + col_idx, 0], |
|
warped_points[row_idx2 * (cols + 1) + col_idx, 1], |
|
), |
|
col, |
|
thickness, |
|
) |
|
|
|
|
|
points = keep_points_inside(warped_points, img.shape[:2]) |
|
return {"points": junc_points, "line_map": line_map} |
|
|
|
|
|
def draw_stripes_multiseg( |
|
img, |
|
max_nb_cols=13, |
|
min_len=0.04, |
|
min_label_len=64, |
|
transform_params=(0.05, 0.15), |
|
seed=None, |
|
): |
|
"""Draw stripes in a distorted rectangle |
|
and output the junctions points + line map. |
|
Parameters: |
|
max_nb_cols: maximal number of stripes to be drawn |
|
min_width_ratio: the minimal width of a stripe is |
|
min_width_ratio * smallest dimension of the image |
|
transform_params: set the range of the parameters of the transformations |
|
""" |
|
|
|
if seed is None: |
|
global random_state |
|
else: |
|
random_state = np.random.RandomState(seed) |
|
|
|
background_color = int(np.mean(img)) |
|
|
|
board_size = ( |
|
int(img.shape[0] * (1 + random_state.rand())), |
|
int(img.shape[1] * (1 + random_state.rand())), |
|
) |
|
|
|
|
|
col = random_state.randint(5, max_nb_cols) |
|
cols = np.concatenate( |
|
[board_size[1] * random_state.rand(col - 1), np.array([0, board_size[1] - 1])], |
|
axis=0, |
|
) |
|
cols = np.unique(cols.astype(int)) |
|
|
|
|
|
min_dim = min(img.shape) |
|
|
|
|
|
if isinstance(min_len, float) and min_len <= 1.0: |
|
min_len = int(min_dim * min_len) |
|
if isinstance(min_label_len, float) and min_label_len <= 1.0: |
|
min_label_len = int(min_dim * min_label_len) |
|
|
|
cols = cols[ |
|
(np.concatenate([cols[1:], np.array([board_size[1] + min_len])], axis=0) - cols) |
|
>= min_len |
|
] |
|
|
|
col = cols.shape[0] - 1 |
|
cols = np.reshape(cols, (col + 1, 1)) |
|
cols1 = np.concatenate([cols, np.zeros((col + 1, 1), np.int32)], axis=1) |
|
cols2 = np.concatenate( |
|
[cols, (board_size[0] - 1) * np.ones((col + 1, 1), np.int32)], axis=1 |
|
) |
|
points = np.concatenate([cols1, cols2], axis=0) |
|
|
|
|
|
alpha_affine = np.max(img.shape) * ( |
|
transform_params[0] + random_state.rand() * transform_params[1] |
|
) |
|
center_square = np.float32(img.shape) // 2 |
|
square_size = min(img.shape) // 3 |
|
pts1 = np.float32( |
|
[ |
|
center_square + square_size, |
|
[center_square[0] + square_size, center_square[1] - square_size], |
|
center_square - square_size, |
|
[center_square[0] - square_size, center_square[1] + square_size], |
|
] |
|
) |
|
pts2 = pts1 + random_state.uniform( |
|
-alpha_affine, alpha_affine, size=pts1.shape |
|
).astype(np.float32) |
|
affine_transform = cv.getAffineTransform(pts1[:3], pts2[:3]) |
|
pts2 = pts1 + random_state.uniform( |
|
-alpha_affine / 2, alpha_affine / 2, size=pts1.shape |
|
).astype(np.float32) |
|
perspective_transform = cv.getPerspectiveTransform(pts1, pts2) |
|
|
|
|
|
points = np.transpose(np.concatenate((points, np.ones((2 * (col + 1), 1))), axis=1)) |
|
warped_points = np.transpose(np.dot(affine_transform, points)) |
|
|
|
|
|
warped_col0 = np.add( |
|
np.sum(np.multiply(warped_points, perspective_transform[0, :2]), axis=1), |
|
perspective_transform[0, 2], |
|
) |
|
warped_col1 = np.add( |
|
np.sum(np.multiply(warped_points, perspective_transform[1, :2]), axis=1), |
|
perspective_transform[1, 2], |
|
) |
|
warped_col2 = np.add( |
|
np.sum(np.multiply(warped_points, perspective_transform[2, :2]), axis=1), |
|
perspective_transform[2, 2], |
|
) |
|
warped_col0 = np.divide(warped_col0, warped_col2) |
|
warped_col1 = np.divide(warped_col1, warped_col2) |
|
warped_points = np.concatenate([warped_col0[:, None], warped_col1[:, None]], axis=1) |
|
warped_points_float = warped_points.copy() |
|
warped_points = warped_points.astype(int) |
|
|
|
|
|
color = get_random_color(background_color) |
|
|
|
for i in range(col): |
|
|
|
color = (color + 128 + random_state.randint(-30, 30)) % 256 |
|
cv.fillConvexPoly( |
|
img, |
|
np.array( |
|
[ |
|
(warped_points[i, 0], warped_points[i, 1]), |
|
(warped_points[i + 1, 0], warped_points[i + 1, 1]), |
|
(warped_points[i + col + 2, 0], warped_points[i + col + 2, 1]), |
|
(warped_points[i + col + 1, 0], warped_points[i + col + 1, 1]), |
|
] |
|
), |
|
color, |
|
) |
|
|
|
segments = np.zeros([0, 4]) |
|
row = 1 |
|
|
|
for row_idx in range(row + 1): |
|
|
|
|
|
multi_seg_lst = [ |
|
np.array( |
|
[ |
|
warped_points_float[id1, 0], |
|
warped_points_float[id1, 1], |
|
warped_points_float[id2, 0], |
|
warped_points_float[id2, 1], |
|
] |
|
)[None, ...] |
|
for (id1, id2) in combinations( |
|
range(row_idx * (col + 1), (row_idx + 1) * (col + 1), 1), 2 |
|
) |
|
] |
|
multi_seg = np.concatenate(multi_seg_lst, axis=0) |
|
segments = np.concatenate((segments, multi_seg), axis=0) |
|
|
|
|
|
for col_idx in range(col + 1): |
|
|
|
|
|
multi_seg_lst = [ |
|
np.array( |
|
[ |
|
warped_points_float[id1, 0], |
|
warped_points_float[id1, 1], |
|
warped_points_float[id2, 0], |
|
warped_points_float[id2, 1], |
|
] |
|
)[None, ...] |
|
for (id1, id2) in combinations( |
|
range(col_idx, col_idx + (row * col) + 2, col + 1), 2 |
|
) |
|
] |
|
multi_seg = np.concatenate(multi_seg_lst, axis=0) |
|
segments = np.concatenate((segments, multi_seg), axis=0) |
|
|
|
|
|
segments_new = np.zeros([0, 4]) |
|
|
|
image_poly = shapely.geometry.Polygon( |
|
[ |
|
[0, 0], |
|
[img.shape[1] - 1, 0], |
|
[img.shape[1] - 1, img.shape[0] - 1], |
|
[0, img.shape[0] - 1], |
|
] |
|
) |
|
for idx in range(segments.shape[0]): |
|
|
|
seg_raw = segments[idx, :] |
|
seg = shapely.geometry.LineString([seg_raw[:2], seg_raw[2:]]) |
|
|
|
|
|
if seg.intersection(image_poly) == seg: |
|
segments_new = np.concatenate((segments_new, seg_raw[None, ...]), axis=0) |
|
|
|
|
|
elif seg.intersects(image_poly): |
|
|
|
try: |
|
p = np.array(seg.intersection(image_poly).coords).reshape([-1, 4]) |
|
|
|
except: |
|
continue |
|
segment = p |
|
segments_new = np.concatenate((segments_new, segment), axis=0) |
|
|
|
else: |
|
continue |
|
|
|
segments = (np.round(segments_new)).astype(np.int) |
|
|
|
|
|
points1 = segments[:, :2] |
|
points2 = segments[:, 2:] |
|
seg_len = np.sqrt(np.sum((points1 - points2) ** 2, axis=1)) |
|
label_segments = segments[seg_len >= min_label_len, :] |
|
|
|
|
|
junctions_all = np.concatenate( |
|
(label_segments[:, :2], label_segments[:, 2:]), axis=0 |
|
) |
|
if junctions_all.shape[0] == 0: |
|
junc_points = None |
|
line_map = None |
|
|
|
|
|
else: |
|
junc_points = np.unique(junctions_all, axis=0) |
|
|
|
line_map = get_line_map(junc_points, label_segments) |
|
|
|
|
|
nb_rows = random_state.randint(2, 5) |
|
nb_cols = random_state.randint(2, col + 2) |
|
thickness = random_state.randint(min_dim * 0.01, min_dim * 0.011) |
|
for _ in range(nb_rows): |
|
row_idx = random_state.choice([0, col + 1]) |
|
col_idx1 = random_state.randint(col + 1) |
|
col_idx2 = random_state.randint(col + 1) |
|
color = get_random_color(background_color) |
|
cv.line( |
|
img, |
|
( |
|
warped_points[row_idx + col_idx1, 0], |
|
warped_points[row_idx + col_idx1, 1], |
|
), |
|
( |
|
warped_points[row_idx + col_idx2, 0], |
|
warped_points[row_idx + col_idx2, 1], |
|
), |
|
color, |
|
thickness, |
|
) |
|
|
|
for _ in range(nb_cols): |
|
col_idx = random_state.randint(col + 1) |
|
color = get_random_color(background_color) |
|
cv.line( |
|
img, |
|
(warped_points[col_idx, 0], warped_points[col_idx, 1]), |
|
(warped_points[col_idx + col + 1, 0], warped_points[col_idx + col + 1, 1]), |
|
color, |
|
thickness, |
|
) |
|
|
|
|
|
|
|
return {"points": junc_points, "line_map": line_map} |
|
|
|
|
|
def draw_cube( |
|
img, |
|
min_size_ratio=0.2, |
|
min_label_len=64, |
|
scale_interval=(0.4, 0.6), |
|
trans_interval=(0.5, 0.2), |
|
): |
|
"""Draw a 2D projection of a cube and output the visible juntions. |
|
Parameters: |
|
min_size_ratio: min(img.shape) * min_size_ratio is the smallest |
|
achievable cube side size |
|
scale_interval: the scale is between scale_interval[0] and |
|
scale_interval[0]+scale_interval[1] |
|
trans_interval: the translation is between img.shape*trans_interval[0] |
|
and img.shape*(trans_interval[0] + trans_interval[1]) |
|
""" |
|
|
|
|
|
|
|
background_color = int(np.mean(img)) |
|
min_dim = min(img.shape[:2]) |
|
min_side = min_dim * min_size_ratio |
|
lx = min_side + random_state.rand() * 2 * min_dim / 3 |
|
ly = min_side + random_state.rand() * 2 * min_dim / 3 |
|
lz = min_side + random_state.rand() * 2 * min_dim / 3 |
|
cube = np.array( |
|
[ |
|
[0, 0, 0], |
|
[lx, 0, 0], |
|
[0, ly, 0], |
|
[lx, ly, 0], |
|
[0, 0, lz], |
|
[lx, 0, lz], |
|
[0, ly, lz], |
|
[lx, ly, lz], |
|
] |
|
) |
|
rot_angles = random_state.rand(3) * 3 * math.pi / 10.0 + math.pi / 10.0 |
|
rotation_1 = np.array( |
|
[ |
|
[math.cos(rot_angles[0]), -math.sin(rot_angles[0]), 0], |
|
[math.sin(rot_angles[0]), math.cos(rot_angles[0]), 0], |
|
[0, 0, 1], |
|
] |
|
) |
|
rotation_2 = np.array( |
|
[ |
|
[1, 0, 0], |
|
[0, math.cos(rot_angles[1]), -math.sin(rot_angles[1])], |
|
[0, math.sin(rot_angles[1]), math.cos(rot_angles[1])], |
|
] |
|
) |
|
rotation_3 = np.array( |
|
[ |
|
[math.cos(rot_angles[2]), 0, -math.sin(rot_angles[2])], |
|
[0, 1, 0], |
|
[math.sin(rot_angles[2]), 0, math.cos(rot_angles[2])], |
|
] |
|
) |
|
scaling = np.array( |
|
[ |
|
[scale_interval[0] + random_state.rand() * scale_interval[1], 0, 0], |
|
[0, scale_interval[0] + random_state.rand() * scale_interval[1], 0], |
|
[0, 0, scale_interval[0] + random_state.rand() * scale_interval[1]], |
|
] |
|
) |
|
trans = np.array( |
|
[ |
|
img.shape[1] * trans_interval[0] |
|
+ random_state.randint( |
|
-img.shape[1] * trans_interval[1], img.shape[1] * trans_interval[1] |
|
), |
|
img.shape[0] * trans_interval[0] |
|
+ random_state.randint( |
|
-img.shape[0] * trans_interval[1], img.shape[0] * trans_interval[1] |
|
), |
|
0, |
|
] |
|
) |
|
cube = trans + np.transpose( |
|
np.dot( |
|
scaling, |
|
np.dot( |
|
rotation_1, np.dot(rotation_2, np.dot(rotation_3, np.transpose(cube))) |
|
), |
|
) |
|
) |
|
|
|
|
|
|
|
cube = cube[:, :2] |
|
cube = cube.astype(int) |
|
points = cube[1:, :] |
|
|
|
|
|
faces = np.array([[7, 3, 1, 5], [7, 5, 4, 6], [7, 6, 2, 3]]) |
|
|
|
|
|
segments = np.zeros([0, 4]) |
|
|
|
for face_idx in range(faces.shape[0]): |
|
face = faces[face_idx, :] |
|
|
|
segment = np.array( |
|
[ |
|
np.concatenate((cube[face[0]], cube[face[1]]), axis=0), |
|
np.concatenate((cube[face[1]], cube[face[2]]), axis=0), |
|
np.concatenate((cube[face[2]], cube[face[3]]), axis=0), |
|
np.concatenate((cube[face[3]], cube[face[0]]), axis=0), |
|
] |
|
) |
|
segments = np.concatenate((segments, segment), axis=0) |
|
|
|
|
|
segments_new = np.zeros([0, 4]) |
|
|
|
image_poly = shapely.geometry.Polygon( |
|
[ |
|
[0, 0], |
|
[img.shape[1] - 1, 0], |
|
[img.shape[1] - 1, img.shape[0] - 1], |
|
[0, img.shape[0] - 1], |
|
] |
|
) |
|
for idx in range(segments.shape[0]): |
|
|
|
seg_raw = segments[idx, :] |
|
seg = shapely.geometry.LineString([seg_raw[:2], seg_raw[2:]]) |
|
|
|
|
|
if seg.intersection(image_poly) == seg: |
|
segments_new = np.concatenate((segments_new, seg_raw[None, ...]), axis=0) |
|
|
|
|
|
elif seg.intersects(image_poly): |
|
try: |
|
p = np.array(seg.intersection(image_poly).coords).reshape([-1, 4]) |
|
except: |
|
continue |
|
segment = p |
|
segments_new = np.concatenate((segments_new, segment), axis=0) |
|
|
|
else: |
|
continue |
|
|
|
segments = (np.round(segments_new)).astype(np.int) |
|
|
|
|
|
points1 = segments[:, :2] |
|
points2 = segments[:, 2:] |
|
seg_len = np.sqrt(np.sum((points1 - points2) ** 2, axis=1)) |
|
label_segments = segments[seg_len >= min_label_len, :] |
|
|
|
|
|
junctions_all = np.concatenate( |
|
(label_segments[:, :2], label_segments[:, 2:]), axis=0 |
|
) |
|
if junctions_all.shape[0] == 0: |
|
junc_points = None |
|
line_map = None |
|
|
|
|
|
else: |
|
junc_points = np.unique(junctions_all, axis=0) |
|
|
|
line_map = get_line_map(junc_points, label_segments) |
|
|
|
|
|
col_face = get_random_color(background_color) |
|
for i in [0, 1, 2]: |
|
cv.fillPoly(img, [cube[faces[i]].reshape((-1, 1, 2))], col_face) |
|
thickness = random_state.randint(min_dim * 0.003, min_dim * 0.015) |
|
for i in [0, 1, 2]: |
|
for j in [0, 1, 2, 3]: |
|
col_edge = ( |
|
col_face + 128 + random_state.randint(-64, 64) |
|
) % 256 |
|
cv.line( |
|
img, |
|
(cube[faces[i][j], 0], cube[faces[i][j], 1]), |
|
(cube[faces[i][(j + 1) % 4], 0], cube[faces[i][(j + 1) % 4], 1]), |
|
col_edge, |
|
thickness, |
|
) |
|
|
|
return {"points": junc_points, "line_map": line_map} |
|
|
|
|
|
def gaussian_noise(img): |
|
"""Apply random noise to the image.""" |
|
cv.randu(img, 0, 255) |
|
return {"points": None, "line_map": None} |
|
|