scdrand23's picture
not working version
814a594
raw
history blame
5.21 kB
import random
import numpy as np
import torch
from scipy.special import binom
from scipy import ndimage
import matplotlib.pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg
bernstein = lambda n, k, t: binom(n,k)* t**k * (1.-t)**(n-k)
def bezier(points, num=200):
N = len(points)
t = np.linspace(0, 1, num=num)
curve = np.zeros((num, 2))
for i in range(N):
curve += np.outer(bernstein(N - 1, i, t), points[i])
return curve
class Segment():
def __init__(self, p1, p2, angle1, angle2, **kw):
self.p1 = p1; self.p2 = p2
self.angle1 = angle1; self.angle2 = angle2
self.numpoints = kw.get("numpoints", 100)
r = kw.get("r", 0.3)
d = np.sqrt(np.sum((self.p2-self.p1)**2))
self.r = r*d
self.p = np.zeros((4,2))
self.p[0,:] = self.p1[:]
self.p[3,:] = self.p2[:]
self.calc_intermediate_points(self.r)
def calc_intermediate_points(self,r):
self.p[1,:] = self.p1 + np.array([self.r*np.cos(self.angle1),
self.r*np.sin(self.angle1)])
self.p[2,:] = self.p2 + np.array([self.r*np.cos(self.angle2+np.pi),
self.r*np.sin(self.angle2+np.pi)])
self.curve = bezier(self.p,self.numpoints)
def get_curve(points, **kw):
segments = []
for i in range(len(points)-1):
seg = Segment(points[i,:2], points[i+1,:2], points[i,2],points[i+1,2],**kw)
segments.append(seg)
curve = np.concatenate([s.curve for s in segments])
return segments, curve
def ccw_sort(p):
d = p-np.mean(p,axis=0)
s = np.arctan2(d[:,0], d[:,1])
return p[np.argsort(s),:]
def get_bezier_curve(a, rad=0.2, edgy=0):
""" given an array of points *a*, create a curve through
those points.
*rad* is a number between 0 and 1 to steer the distance of
control points.
*edgy* is a parameter which controls how "edgy" the curve is,
edgy=0 is smoothest."""
p = np.arctan(edgy)/np.pi+.5
a = ccw_sort(a)
a = np.append(a, np.atleast_2d(a[0,:]), axis=0)
d = np.diff(a, axis=0)
ang = np.arctan2(d[:,1],d[:,0])
f = lambda ang : (ang>=0)*ang + (ang<0)*(ang+2*np.pi)
ang = f(ang)
ang1 = ang
ang2 = np.roll(ang,1)
ang = p*ang1 + (1-p)*ang2 + (np.abs(ang2-ang1) > np.pi )*np.pi
ang = np.append(ang, [ang[0]])
a = np.append(a, np.atleast_2d(ang).T, axis=1)
s, c = get_curve(a, r=rad, method="var")
x,y = c.T
return x,y,a
class Polygon:
def __init__(self, cfg, is_train):
self.max_points = cfg['STROKE_SAMPLER']['POLYGON']['MAX_POINTS']
self.eval_points = cfg['STROKE_SAMPLER']['EVAL']['MAX_ITER']
self.is_train = is_train
def get_random_points_from_mask(self, mask, n=3):
h,w = mask.shape
view_mask = mask.reshape(h*w)
non_zero_idx = view_mask.nonzero()[:,0]
selected_idx = torch.randperm(len(non_zero_idx))[:n]
non_zero_idx = non_zero_idx[selected_idx]
y = (non_zero_idx // w)*1.0/(h+1)
x = (non_zero_idx % w)*1.0/(w+1)
return torch.cat((x[:,None],y[:,None]), dim=1).numpy()
def draw(self, mask=None, box=None):
if mask.sum() < 10:
return torch.zeros(mask.shape).bool() # if mask is empty
if not self.is_train:
return self.draw_eval(mask=mask, box=box)
# box: x1,y1,x2,y2
x1,y1,x2,y2 = box.int().unbind()
rad = 0.2
edgy = 0.05
num_points = random.randint(1, min(self.max_points, mask.sum().item()))
a = self.get_random_points_from_mask(mask[y1:y2,x1:x2], n=num_points)
x,y, _ = get_bezier_curve(a,rad=rad, edgy=edgy)
x = x.clip(0.0, 1.0)
y = y.clip(0.0, 1.0)
points = torch.from_numpy(np.concatenate((y[None,]*(y2-y1-1).item(),x[None,]*(x2-x1-1).item()))).int()
canvas = torch.zeros((y2-y1, x2-x1))
canvas[points.long().tolist()] = 1
rand_mask = torch.zeros(mask.shape)
rand_mask[y1:y2,x1:x2] = canvas
return rand_mask.bool()
def draw_eval(self, mask=None, box=None):
# box: x1,y1,x2,y2
x1,y1,x2,y2 = box.int().unbind()
rad = 0.2
edgy = 0.05
num_points = min(self.eval_points, mask.sum().item())
a = self.get_random_points_from_mask(mask[y1:y2,x1:x2], n=num_points)
rand_masks = []
for i in range(len(a)):
x,y, _ = get_bezier_curve(a[:i+1],rad=rad, edgy=edgy)
x = x.clip(0.0, 1.0)
y = y.clip(0.0, 1.0)
points = torch.from_numpy(np.concatenate((y[None,]*(y2-y1-1).item(),x[None,]*(x2-x1-1).item()))).int()
canvas = torch.zeros((y2-y1, x2-x1))
canvas[points.long().tolist()] = 1
rand_mask = torch.zeros(mask.shape)
rand_mask[y1:y2,x1:x2] = canvas
struct = ndimage.generate_binary_structure(2, 2)
rand_mask = torch.from_numpy((ndimage.binary_dilation(rand_mask, structure=struct, iterations=5).astype(rand_mask.numpy().dtype)))
rand_masks += [rand_mask.bool()]
return torch.stack(rand_masks)
def __repr__(self,):
return 'polygon'