from numpy import * from scipy.spatial import ConvexHull, Delaunay from scipy.sparse import coo_matrix def RGBXY_weights( RGB_palette, RGBXY_data ): RGBXY_hull_vertices = RGBXY_data[ ConvexHull( RGBXY_data ).vertices ] W_RGBXY = Delaunay_coordinates( RGBXY_hull_vertices, RGBXY_data ) # Optional: Project outside RGBXY_hull_vertices[:,:3] onto RGB_palette convex hull. W_RGB = Star_coordinates( RGB_palette, RGBXY_hull_vertices[:,:3] ) return W_RGBXY.dot( W_RGB ) def Star_coordinates( vertices, data ): ## Find the star vertex star = argmin( linalg.norm( vertices, axis=1 ) ) ## Make a mesh for the palette hull = ConvexHull( vertices ) ## Star tessellate the faces of the convex hull simplices = [ [star] + list(face) for face in hull.simplices if star not in face ] barycoords = -1*ones( ( data.shape[0], len(vertices) ) ) ## Barycentric coordinates for the data in each simplex for s in simplices: s0 = vertices[s[:1]] b = linalg.solve( (vertices[s[1:]]-s0).T, (data-s0).T ).T b = append( 1-b.sum(axis=1)[:,None], b, axis=1 ) ## Update barycoords whenever data is inside the current simplex (with threshold). mask = (b>=-1e-8).all(axis=1) barycoords[mask] = 0. barycoords[ix_(mask,s)] = b[mask] return barycoords def Delaunay_coordinates( vertices, data ): # Adapted from Gareth Rees # Compute Delaunay tessellation. tri = Delaunay( vertices ) # Find the tetrahedron containing each target (or -1 if not found). simplices = tri.find_simplex(data, tol=1e-6) assert (simplices != -1).all() # data contains outside vertices. # Affine transformation for simplex containing each datum. X = tri.transform[simplices, :data.shape[1]] # Offset of each datum from the origin of its simplex. Y = data - tri.transform[simplices, data.shape[1]] # Compute the barycentric coordinates of each datum in its simplex. b = einsum( '...jk,...k->...j', X, Y ) barycoords = c_[b,1-b.sum(axis=1)] # Return the weights as a sparse matrix. rows = repeat(arange(len(data)).reshape((-1,1)), len(tri.simplices[0]), 1).ravel() cols = tri.simplices[simplices].ravel() vals = barycoords.ravel() return coo_matrix( (vals,(rows,cols)), shape=(len(data),len(vertices)) ).tocsr()