PolyFormer / data /poly_utils.py
jiang
init commit
650c5f6
import re
import numpy as np
from itertools import groupby
from PIL import Image
import math
from math import ceil, floor
from skimage import draw
from random import sample
import base64
from io import BytesIO
convert = lambda text: int(text) if text.isdigit() else text.lower()
natrual_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
def points_to_token_string(box, polygons):
polygon_strings = []
for polygon in polygons:
polygon_string = " ".join([f"<bin_{int(p[0])}_{int(p[1])}>" for p in polygon])
polygon_strings.append(polygon_string)
polygon_string = " <separator> ".join(polygon_strings)
box_string = " ".join([f"<bin_{int(p[0])}_{int(p[1])}>" for p in box])
token_string = " ".join([box_string, polygon_string])
token_type = []
for token in token_string.split(" "):
if "bin" in token:
token_type.append(0) # 0 for coordinate tokens
else:
token_type.append(1) # 1 for separator tokens
return token_string, token_type
def resize_binary_mask(array, new_size):
image = Image.fromarray(array.astype(np.uint8) * 255)
image = image.resize(new_size)
return np.asarray(image).astype(np.bool_)
def close_contour(contour):
if not np.array_equal(contour[0], contour[-1]):
contour = np.vstack((contour, contour[0]))
return contour
def binary_mask_to_rle(binary_mask):
rle = {'counts': [], 'size': list(binary_mask.shape)}
counts = rle.get('counts')
for i, (value, elements) in enumerate(groupby(binary_mask.ravel(order='F'))):
if i == 0 and value == 1:
counts.append(0)
counts.append(len(list(elements)))
return rle
def revert_direction(poly):
poly = np.array(poly).reshape(int(len(poly) / 2), 2)
poly = poly[::-1, :]
return list(poly.flatten())
def reorder_points(poly):
poly = np.array(poly)
xs = poly[::2]
ys = poly[1::2]
points = np.array(poly).reshape(int(len(poly) / 2), 2)
start = np.argmin(xs ** 2 + ys ** 2) # smallest distance to the origin
poly_reordered = np.concatenate([points[start:], points[:start]], 0)
return list(poly_reordered.flatten())
def convert_pts(coeffs):
pts = []
for i in range(len(coeffs) // 2):
pts.append([coeffs[2 * i + 1], coeffs[2 * i]]) # y, x
return np.array(pts, np.int32)
def get_mask_from_codes(codes, img_size):
masks = [np.zeros(img_size)]
for code in codes:
if len(code) > 0:
mask = draw.polygon2mask(img_size, convert_pts(code))
mask = np.array(mask, np.uint8)
masks.append(mask)
mask = sum(masks)
mask = mask > 0
return mask.astype(np.uint8)
def is_clockwise(poly):
n = len(poly) // 2
xs = poly[::2]
xs.append(xs[0])
ys = poly[1::2]
ys.append(ys[0])
area = 0
for i in range(n):
x1, y1 = xs[i], ys[i]
x2, y2 = xs[i + 1], ys[i + 1]
area += (x2 - x1) * (y2 + y1)
return area < 0
def close_polygon_contour(poly):
poly = np.array(poly).reshape(int(len(poly) / 2), 2)
x1, y1 = poly[0]
x2, y2 = poly[-1]
if x1 != x2:
poly = np.concatenate([poly, [poly[0]]], 0)
return list(poly.flatten())
def close_polygons_contour(polygons):
polygons_closed = []
for polygon in polygons:
polygon_closed = close_polygon_contour(polygon)
polygons_closed.append(polygon_closed)
return polygons_closed
def image_to_base64(img, format):
output_buffer = BytesIO()
img.save(output_buffer, format=format)
byte_data = output_buffer.getvalue()
base64_str = base64.b64encode(byte_data)
base64_str = str(base64_str, encoding='utf-8')
return base64_str
def process_polygons(polygons, redirection=True, reorder=True, close=False):
polygons_processed = []
for polygon in polygons:
if redirection and not is_clockwise(polygon):
polygon = revert_direction(polygon)
if reorder:
polygon = reorder_points(polygon)
if close:
polygon = close_polygon_contour(polygon)
polygons_processed.append(polygon)
polygons = sorted(polygons_processed, key=lambda x: (x[0] ** 2 + x[1] ** 2, x[0], x[1]))
return polygons
def string_to_polygons(pts_strings):
pts_strings = pts_strings.split(" ")[:-1]
polygons = []
for pts_string in pts_strings:
polygon = pts_string.split(",")
polygon = [float(p) for p in polygon]
polygons.append(polygon)
return polygons
def downsample_polygon(polygon, ds_rate=25):
points = np.array(polygon).reshape(int(len(polygon) / 2), 2)
points = points[::ds_rate]
return list(points.flatten())
def downsample_polygons(polygons, ds_rate=25):
polygons_ds = []
for polygon in polygons:
polygons_ds.append(downsample_polygon(polygon, ds_rate))
return polygons_ds
def check_length(polygons):
length = 0
for polygon in polygons:
length += len(polygon)
return length
def approximate_polygon(poly, tolerance=2):
poly = np.array(poly).reshape(int(len(poly) / 2), 2)
new_poly = [poly[0]]
for i in range(1, len(poly)):
x1, y1 = new_poly[-1]
x2, y2 = poly[i]
dist = math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
if dist > tolerance:
new_poly.append(poly[i])
new_poly = np.array(new_poly)
return list(new_poly.flatten())
def approximate_polygons(polys, tolerance=1.0, max_length=400):
tol = tolerance
while check_length(polys) > max_length:
polys_new = []
for poly in polys:
polys_new.append(approximate_polygon(poly, tolerance=tol))
polys = polys_new
tol += 2.0
return polys
def random_int(low, high):
if low < high:
return np.random.randint(low, high)
else:
return max(low, high)
def interpolate_points(ps, pe):
xs, ys = ps
xe, ye = pe
points = []
dx = xe - xs
dy = ye - ys
if dx != 0:
scale = dy / dx
if xe > xs:
x_interpolated = list(range(ceil(xs), floor(xe) + 1))
else:
x_interpolated = list(range(floor(xs), ceil(xe) - 1, -1))
for x in x_interpolated:
y = ys + (x - xs) * scale
points.append([x, y])
if dy != 0:
scale = dx / dy
if ye > ys:
y_interpolated = list(range(ceil(ys), floor(ye) + 1))
else:
y_interpolated = list(range(floor(ys), ceil(ye) - 1, -1))
for y in y_interpolated:
x = xs + (y - ys) * scale
points.append([x, y])
if xe > xs:
points = sorted(points, key=lambda x: x[0])
else:
points = sorted(points, key=lambda x: -x[0])
return points
def interpolate_polygon(polygon):
points = np.array(polygon).reshape(int(len(polygon) / 2), 2)
points_interpolated = []
points_interpolated.append(points[0])
for i in range(0, len(points) - 1):
points_i = interpolate_points(points[i], points[i + 1])
points_interpolated += points_i
points_interpolated.append(points[i + 1])
points_interpolated = prune_points(points_interpolated)
polygon_interpolated = np.array(points_interpolated)
return list(polygon_interpolated.flatten())
def prune_points(points, th=0.1):
points_pruned = [points[0]]
for i in range(1, len(points)):
x1, y1 = points_pruned[-1]
x2, y2 = points[i]
dist = (x2 - x1) ** 2 + (y2 - y1) ** 2
if dist > th:
points_pruned.append(points[i])
return points_pruned
def interpolate_polygons(polygons):
polygons_i = []
for polygon in polygons:
polygons_i.append(interpolate_polygon(polygon))
return polygons_i
def sample_polygon(polygon, sample_rate=0.5):
points = np.array(polygon).reshape(int(len(polygon) / 2), 2)
k = int(len(points) * sample_rate)
index = sorted(sample(list(range(len(points))), k))
points_sampled = points[index]
return list(np.array(points_sampled).flatten())
def sample_polygons(polygons, max_length=400.0):
n = check_length(polygons)
k = max_length / n
polygons_s = []
for polygon in polygons:
polygons_s.append(sample_polygon(polygon, k))
return polygons_s
def polygons_to_string(polygons):
pts_strings = []
for polygon in polygons:
pts_string = ','.join([str(num) for num in polygon])
pts_string += " " # separator
pts_strings.append(pts_string)
pts_strings = "".join(pts_strings)
return pts_strings