scdrand23's picture
not working version
814a594
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'