t29mato's picture
Initial deploy: AutoLineDigitizer Gradio API for HuggingFace Spaces
27c91ef
Raw
History Blame Contribute Delete
8.09 kB
import numpy as np
import cv2
from matplotlib import pyplot as plt
import scipy
# For line interpolation:
# Comment this out if not using 'get_interp_points' function
from bresenham import bresenham
def hsv_to_bgr(h, s, v):
# Get RGB values
c = v * s
x = c * (1 - abs((h * 6) % 2 - 1))
m = v - c
if h < 1/6:
r, g, b = c, x, 0
elif h < 1/3:
r, g, b = x, c, 0
elif h < 0.5:
r, g, b = 0, c, x
elif h < 2/3:
r, g, b = 0, x, c
elif h < 5/6:
r, g, b = x, 0, c
else:
r, g, b = c, 0, x
# Scale RGB values to 0-255 range and convert to integers
r = int((r + m) * 255)
g = int((g + m) * 255)
b = int((b + m) * 255)
return (b, g, r)
def get_distinct_colors(n):
huePartition = 1.0 / (n + 1)
return (hsv_to_bgr(huePartition * value, 1.0, 1.0) for value in range(0, n))
def is_color(img):
if img.ndim <=2:
return False
# img.ndim >=3
if img.shape[-1] == 1:
return False
return True
def show_img(img, color='gray', is_bgr=False, title='', figsize=None, final_show=True):
"""Show image using plt."""
if figsize:
if not isinstance(figsize, (tuple, list)):
figsize = (figsize, figsize)
plt.figure(figsize=figsize)
if is_color(img) and is_bgr:
img = img[...,::-1]
params = {'cmap':color}
if img.dtype == np.uint8:
params.update({'vmin': 0, 'vmax': 255})
plt.imshow(img, **params)
plt.title(title)
if final_show:
plt.show()
return
def draw_xrange(img, xrange):
annot_img = img.copy()
im_h, im_w = annot_img.shape[:2]
annot_img = cv2.line(annot_img, (xrange[0], 0), (xrange[0], im_h), (0,0,255), thickness=1)
annot_img = cv2.line(annot_img, (xrange[1], 0), (xrange[1], im_h), (0,0,255), thickness=1)
return annot_img
def get_xrange(bin_line_mask):
"""
bin_line_mask: np.ndarray => black and white binary mask of line
black => background => 0
white => foregrond line pixel => 255
returns: (x_start, x_end) where x_start and x_end represent the starting and ending points
for the binary line segment
"""
# print(bin_line_mask.sum(axis=0))
# np.save("problem_mask.npy", bin_line_mask)
smooth_signal = scipy.signal.medfilt(bin_line_mask.sum(axis=0), kernel_size=5)
# print(smooth_signal.shape)
# print(smooth_signal)
# print(np.nonzero(smooth_signal))
x_range = np.nonzero(smooth_signal)
if len(x_range) and len(x_range[0]): # To handle cases with empty masks
x_range = x_range[0][[0, -1]]
else:
x_range = None
return x_range
def get_kp(line_img, interval=10, x_range=None, get_num_lines=False, get_center=True):
"""
line_img: np.ndarray => black and white binary mask of line
black => background => 0
white => foregrond line pixel => 255
interval: delta_x at which x,y points are sampled across the line_img
x_range: Range of x values, [xmin, xmax), within which pred points (x,y) are to be sampled
returns: a list [{'x': <x_val>, 'y': <y_val>}, ....] of line points found in the binary line_img
"""
im_h, im_w = line_img.shape[:2]
kps = []
# delta = 2
if x_range is None:
x_range = (0, im_w)
# track the number of vertical binary components found at every x => estimate num lines
num_comps = []
for x in range(x_range[0], x_range[1], interval):
# get the corresponding white pixel in this column
fg_y = []
fg_y_center = []
all_y_points = np.where(line_img[:, x] == 255)[0]
if all_y_points.size != 0:
fg_y.append(all_y_points[0])
y = all_y_points[0]
n_comps = 1
for idx in range(1, len(all_y_points)):
y_next = all_y_points[idx]
# print(y, y_next)
if abs(y_next - y) > 2:
n_comps += 1
# break found b/w y_next and y, separate components
if fg_y[-1] != y:
# handle the case where (first component itself is broken, i.e found break at idx=1)
fg_y_center.append(round(y + fg_y[-1])//2)
fg_y.append(y)
else:
fg_y_center.append(y)
fg_y.append(y_next)
y = y_next
# print(fg_y,'\n', fg_y_center, '\n')
# print('last_point', y, y_next)
if fg_y[-1] != y:
# add the last point
fg_y_center.append(round(y + fg_y[-1])//2)
fg_y.append(y)
else:
fg_y_center.append(y)
num_comps.append(n_comps)
if (fg_y or fg_y_center) and (n_comps==1):
if get_center:
kps.extend([{'x':float(x), 'y':y} for y in fg_y_center])
else:
kps.extend([{'x':float(x), 'y':y} for y in fg_y])
res = kps
if get_num_lines:
res = kps, int(np.percentile(num_comps, 85))
return res
def draw_edge(img, edge):
inter_points = get_interp_points(edge[0], edge[1])
# print(inter_points)
# print(len(inter_points), inter_points)
annot_img = draw_kps(img, array_to_points(inter_points), color=(255,0,0))
return annot_img
def draw_kps(img, kps, color=(0,255,0), classes=None, **draw_options):
if is_color(img):
annot_img = img.copy()
else:
annot_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
if classes is None:
classes = [0]*len(kps)
color_map = {0: color}
else:
colors = list(get_distinct_colors(classes.max()+1))
color_map = dict(zip(range(classes.max()+1), colors))
# print(color_map)
for idx, kp in enumerate(kps):
options = dict(color=color_map[classes[idx]], markerType=cv2.MARKER_CROSS, markerSize=2, thickness=2, line_type=8)
options.update(draw_options)
annot_img = cv2.drawMarker(annot_img, (int(kp['x']), int(kp['y'])), **options)
return annot_img
def points_to_array(pred_ds):
res = []
for line in pred_ds:
line_arr = []
for pt in line:
line_arr.append([pt['x'], pt['y']])
res.append(line_arr)
return res
# res = line_utils.draw_lines(img, points_to_array(pred_ds))
def draw_lines(img, lines, classes=None):
if is_color(img):
annot_img = img.copy()
else:
annot_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
if classes is None:
classes = list(range(len(lines)))
if len(classes):
colors = list(get_distinct_colors(max(classes)+1))
color_map = dict(zip(range(max(classes)+1), colors))
# print('color_map:', color_map)
for line_idx, line in enumerate(lines):
options = dict(color=color_map[classes[line_idx]], thickness=2)
drawing_lines = []
for pt_idx in range(len(line)-1):
drawing_lines.append([line[pt_idx], line[pt_idx+1]])
annot_img = cv2.polylines(annot_img, np.array(drawing_lines), isClosed=False, **options)
return annot_img
# Get the line points that would lie between ptA and ptB, according to the bresenham algorithm
def get_interp_points(ptA, ptB, thickness=1):
# x_interp = np.arange(ptA[0], ptB[0])
# y_interp = np.interp(x_interp, [ptA[0], ptB[0]], [ptA[1], ptB[1]]).round().astype(int)
points = []
delta_range = (-thickness//2, thickness//2)
for delta in range(delta_range[0], delta_range[1]+1):
points.extend(list(bresenham(ptA[0], ptA[1]+delta, ptB[0], ptB[1]+delta)))
inter_points = np.array(points)
# inter_points = np.stack([x_interp,y_interp], axis=-1)
return inter_points
def array_to_points(pts_arr):
pts = [{'x': pt[0], 'y': pt[1]} for pt in pts_arr]
return pts