def linear_operator_from_mesh(mesh_coord, mesh_triangles, mask=None, offset=0, weights=None): """Generates the linear operator for the total variation Nesterov function from a mesh. Parameters ---------- mesh_coord : Numpy array [n, 3] of float. mesh_triangles : Numpy array, n_triangles-by-3. The (integer) indices of the three nodes forming the triangle. mask : Numpy array (shape (n,)) of integers/boolean. Non-null values correspond to columns of X. Groups may be defined using different values in the mask. TV will be applied within groups of the same value in the mask. offset : Non-negative integer. The index of the first column, variable, where TV applies. This is different from penalty_start which define where the penalty applies. The offset defines where TV applies within the penalised variables. Example: X := [Intercept, Age, Weight, Image]. Intercept is not penalized, TV does not apply on Age and Weight but only on Image. Thus: penalty_start = 1, offset = 2 (skip Age and Weight). weights : Numpy array. The weight put on the gradient of every point. Default is weight 1 for each point, or equivalently, no weight. The weights is a numpy array of the same shape as mask. Returns ------- out1 : List or sparse matrices. Linear operator for the total variation Nesterov function computed over a mesh. out2 : Integer. The number of compacts. Examples -------- >>> import numpy as np >>> import parsimony.functions.nesterov.l1tv as tv_helper >>> mesh_coord = np.array([[0, 0], [1, 0], [0, 1], [1, 1], [0, 2], [1, 2]]) >>> mesh_triangles = np.array([[0 ,1, 3], [0, 2 ,3], [2, 3, 5], [2, 4, 5]]) >>> A = tv_helper.linear_operator_from_mesh(mesh_coord,mesh_triangles) """ Atv = tv.linear_operator_from_mesh(mesh_coord=mesh_coord, mesh_triangles=mesh_triangles, mask=mask, offset=offset, weights=weights) num_variables = mask.sum() if not mask is None else mesh_coord.shape[0] Al1 = l1.linear_operator_from_variables(num_variables, penalty_start=offset) A = LinearOperator(Al1[0], *Atv) return A
def linear_operator_from_mask(mask, num_variables, penalty_start=0): """Generates the linear operator for the total variation Nesterov function from a mask for a 3D image. Parameters ---------- mask : Numpy array. The mask. The mask does not involve any intercept variables. num_variables : Positive integer. The total number of variables, including the intercept variable(s). penalty_start : Non-negative integer. The number of variables to exempt from penalisation. Equivalently, the first index to be penalised. Default is 0, all variables are included. """ Atv = tv.linear_operator_from_mask(mask) Al1 = l1.A_from_variables(num_variables, penalty_start=penalty_start) A = LinearOperator(Al1[0], *Atv) return A
def linear_operator_from_shape(shape, num_variables, penalty_start=0): """Generates the linear operator for the total variation Nesterov function from the shape of a 3D image. Parameters ---------- shape : List or tuple with 1, 2 or 3 elements. The shape of the 1D, 2D or 3D image. shape has the form (Z, Y, X), where Z is the number of "layers", Y is the number of rows and X is the number of columns. The shape does not involve any intercept variables. num_variables : Positive integer. The total number of variables, including the intercept variable(s). penalty_start : Non-negative integer. The number of variables to exempt from penalisation. Equivalently, the first index to be penalised. Default is 0, all variables are included. """ Atv = tv.linear_operator_from_shape(shape) Al1 = l1.linear_operator_from_variables(num_variables, penalty_start=penalty_start) A = LinearOperator(Al1[0], *Atv) return A
def linear_operator_from_mesh(mesh_coord, mesh_triangles, mask=None, offset=0, weights=None): """Generates the linear operator for the total variation Nesterov function from a mesh. Parameters ---------- mesh_coord : Numpy array [n, 3] of float. mesh_triangles : Numpy array, n_triangles-by-3. The (integer) indices of the three nodes forming the triangle. mask : Numpy array (shape (n,)) of integers/boolean. Non-null values correspond to columns of X. Groups may be defined using different values in the mask. TV will be applied within groups of the same value in the mask. offset : Non-negative integer. The index of the first column, variable, where TV applies. This is different from penalty_start which define where the penalty applies. The offset defines where TV applies within the penalised variables. Example: X := [Intercept, Age, Weight, Image]. Intercept is not penalized, TV does not apply on Age and Weight but only on Image. Thus: penalty_start = 1, offset = 2 (skip Age and Weight). weights : Numpy array. The weight put on the gradient of every point. Default is weight 1 for each point, or equivalently, no weight. The weights is a numpy array of the same shape as mask. Returns ------- out1 : List or sparse matrices. Linear operator for the total variation Nesterov function computed over a mesh. out2 : Integer. The number of compacts. Examples -------- >>> import numpy as np >>> import parsimony.functions.nesterov.tv as tv_helper >>> mesh_coord = np.array([[0, 0], [1, 0], [0, 1], [1, 1], [0, 2], [1, 2]]) >>> mesh_triangles = np.array([[0 ,1, 3], [0, 2 ,3], [2, 3, 5], [2, 4, 5]]) >>> A = tv_helper.linear_operator_from_mesh(mesh_coord, mesh_triangles) """ if mask is None: mask = np.ones(mesh_coord.shape[0], dtype=bool) assert mask.shape[0] == mesh_coord.shape[0] mask_bool = mask != 0 mask_idx = np.where(mask_bool)[0] # Mapping from full array to masked array. map_full2masked = np.zeros(mask.shape, dtype=int) map_full2masked[:] = -1 map_full2masked[mask_bool] = np.arange(np.sum(mask_bool)) + offset ## 1) Associate edges to nodes nodes_with_edges = [[] for i in xrange(mesh_coord.shape[0])] def connect_edge_to_node(node_idx1, node_idx2, nodes_with_edges): # Attach edge to first node. if np.sum(mesh_coord[node_idx1] - mesh_coord[node_idx2]) >= 0: edge = [node_idx1, node_idx2] if not edge in nodes_with_edges[node_idx1]: nodes_with_edges[node_idx1].append(edge) else: # attach edge to second node edge = [node_idx2, node_idx1] if not edge in nodes_with_edges[node_idx2]: nodes_with_edges[node_idx2].append(edge) for i in xrange(mesh_triangles.shape[0]): t = mesh_triangles[i, :] connect_edge_to_node(t[0], t[1], nodes_with_edges) connect_edge_to_node(t[0], t[2], nodes_with_edges) connect_edge_to_node(t[1], t[2], nodes_with_edges) max_connectivity = np.max(np.array([len(n) for n in nodes_with_edges])) # 3. build sparse matrices # 1..max_connectivity of i, j and value A = [[[], [], []] for i in xrange(max_connectivity)] n_compacts = 0 for node_idx in mask_idx: #node_idx = 0 found = False node = nodes_with_edges[node_idx] for i, v in enumerate(node): found = False if weights is not None: w = weights[i] else: w = 1.0 #print i, v node1_idx, node2_idx = v if mask_bool[node1_idx] and mask_bool[node2_idx]: found = True A[i][0] += [map_full2masked[node1_idx], map_full2masked[node1_idx]] A[i][1] += [map_full2masked[node1_idx], map_full2masked[node2_idx]] A[i][2] += [-w, w] if found: n_compacts += 1 p = mask.sum() A = [sparse.csr_matrix((A[i][2], (A[i][0], A[i][1])), shape=(p, p)) for i in xrange(len(A))] A = LinearOperator(*A) A.n_compacts = n_compacts return A
def linear_operator_from_shape(shape, weights=None): """Generates the linear operator for the total variation Nesterov function from the shape of a 1D, 2D or 3D image. Parameters ---------- shape : List or tuple with 1, 2 or 3 integers. The shape of the 1D, 2D or 3D image. shape has the form X, (X,), (Y, X) or (Z, Y, X), where Z is the number of "layers", Y is the number of rows and X is the number of columns. The shape does not involve any intercept variables. weights : Sequence, e.g. list or numpy (p-by-1) array. Weights put on the groups. Default is weight 1 for each group, i.e. no weight. """ if not isinstance(shape, (list, tuple)): shape = [shape] while len(shape) < 3: shape = tuple([1] + list(shape)) nz = shape[0] ny = shape[1] nx = shape[2] p = nx * ny * nz ind = np.arange(p).reshape((nz, ny, nx)) if weights is not None: weights = np.array(weights) weights = weights.ravel() # w = sparse.spdiags(weights.ravel(), 0, p, p) if nx > 1: if weights is not None: Ax = sparse.spdiags(weights, -1, p, p).T - \ sparse.spdiags(weights, 0, p, p) Ax = Ax.tocsr() else: Ax = sparse.eye(p, p, 1, format='csr') - \ sparse.eye(p, p) zind = ind[:, :, -1].ravel() for i in zind: Ax.data[Ax.indptr[i]: \ Ax.indptr[i + 1]] = 0 Ax.eliminate_zeros() else: Ax = sparse.csr_matrix((p, p), dtype=float) if ny > 1: if weights is not None: Ay = sparse.spdiags(weights, -nx, p, p).T - \ sparse.spdiags(weights, 0, p, p) Ay = Ay.tocsr() else: Ay = sparse.eye(p, p, nx, format='csr') - \ sparse.eye(p, p) yind = ind[:, -1, :].ravel() for i in yind: Ay.data[Ay.indptr[i]: \ Ay.indptr[i + 1]] = 0 Ay.eliminate_zeros() else: Ay = sparse.csr_matrix((p, p), dtype=float) if nz > 1: if weights is not None: Az = sparse.spdiags(weights, -(ny * nx), p, p).T - \ sparse.spdiags(weights, 0, p, p) Az = Az.tocsr() else: Az = (sparse.eye(p, p, ny * nx, format='csr') - \ sparse.eye(p, p)) xind = ind[-1, :, :].ravel() for i in xind: Az.data[Az.indptr[i]: \ Az.indptr[i + 1]] = 0 Az.eliminate_zeros() else: Az = sparse.csr_matrix((p, p), dtype=float) A = LinearOperator(Ax, Ay, Az) A.n_compacts = (nz * ny * nx - 1) return A
def linear_operator_from_subset_mask(mask, weights=None): """Generates the linear operator for the total variation Nesterov function from a mask for a 3D image. The binary mask marks a subset of the variables that are supposed to be smoothed. The mask has the same size as the input and output image. Parameters ---------- mask : Numpy array. The mask. The mask does not involve any intercept variables. weights : Numpy array. The weight put on the gradient of every point. Default is weight 1 for each point, or equivalently, no weight. The weights is a numpy array of the same shape as mask. """ while len(mask.shape) < 3: mask = mask[np.newaxis, :] if weights is not None: while len(weights.shape) < 3: weights = weights[np.newaxis, :] nz, ny, nx = mask.shape mask = mask.astype(bool) zyx_mask = np.where(mask) Ax_i = list() Ax_j = list() Ax_v = list() Ay_i = list() Ay_j = list() Ay_v = list() Az_i = list() Az_j = list() Az_v = list() num_compacts = 0 # p = np.sum(mask) # Mapping from image coordinate to flat masked array. def im2flat(sub, dims): return sub[0] * dims[2] * dims[1] + \ sub[1] * dims[2] + \ sub[2] # im2flat = np.zeros(mask.shape, dtype=int) # im2flat[:] = -1 # im2flat[mask] = np.arange(p) # im2flat[np.arange(p)] = np.arange(p) for pt in xrange(len(zyx_mask[0])): found = False z, y, x = zyx_mask[0][pt], zyx_mask[1][pt], zyx_mask[2][pt] i_pt = im2flat((z, y, x), mask.shape) if weights is not None: w = weights[z, y, x] else: w = 1.0 if z + 1 < nz and mask[z + 1, y, x]: found = True Az_i += [i_pt, i_pt] Az_j += [i_pt, im2flat((z + 1, y, x), mask.shape)] Az_v += [-w, w] if y + 1 < ny and mask[z, y + 1, x]: found = True Ay_i += [i_pt, i_pt] Ay_j += [i_pt, im2flat((z, y + 1, x), mask.shape)] Ay_v += [-w, w] if x + 1 < nx and mask[z, y, x + 1]: found = True Ax_i += [i_pt, i_pt] Ax_j += [i_pt, im2flat((z, y, x + 1), mask.shape)] Ax_v += [-w, w] if found: num_compacts += 1 p = np.prod(mask.shape) Az = sparse.csr_matrix((Az_v, (Az_i, Az_j)), shape=(p, p)) Ay = sparse.csr_matrix((Ay_v, (Ay_i, Ay_j)), shape=(p, p)) Ax = sparse.csr_matrix((Ax_v, (Ax_i, Ax_j)), shape=(p, p)) A = LinearOperator(Ax, Ay, Az) A.n_compacts = num_compacts return A
def linear_operator_from_mask(mask, offset=0, weights=None): """Generates the linear operator for the total variation Nesterov function from a mask for a 3D image. Parameters ---------- mask : Numpy array of integers. The mask has the same shape as the original data. Non-null values correspond to columns of X. Groups may be defined using different values in the mask. TV will be applied within groups of the same value in the mask. offset: Non-negative integer. The index of the first column, variable, where TV applies. This is different from penalty_start which define where the penalty applies. The offset defines where TV applies within the penalised variables. Example: X := [Intercept, Age, Weight, Image]. Intercept is not penalized, TV does not apply on Age and Weight but only on Image. Thus: penalty_start = 1, offset = 2 (skip Age and Weight). weights : Numpy array. The weight put on the gradient of every point. Default is weight 1 for each point, or equivalently, no weight. The weights is a numpy array of the same shape as mask. """ while len(mask.shape) < 3: mask = mask[..., np.newaxis] if weights is not None: while len(weights.shape) < 3: weights = weights[..., np.newaxis] nx, ny, nz = mask.shape mask_bool = mask != 0 xyz_mask = np.where(mask_bool) Ax_i = list() Ax_j = list() Ax_v = list() Ay_i = list() Ay_j = list() Ay_v = list() Az_i = list() Az_j = list() Az_v = list() n_compacts = 0 p = np.sum(mask_bool) + offset # Mapping from image coordinate to flat masked array. im2flat = np.zeros(mask.shape, dtype=int) im2flat[:] = -1 im2flat[mask_bool] = np.arange(np.sum(mask_bool)) + offset for pt in xrange(len(xyz_mask[0])): found = False x, y, z = xyz_mask[0][pt], xyz_mask[1][pt], xyz_mask[2][pt] i_pt = im2flat[x, y, z] val = mask[x, y, z] if weights is not None: w = weights[x, y, z] else: w = 1.0 if x + 1 < nx and (mask[x + 1, y, z] == val): found = True Ax_i += [i_pt, i_pt] Ax_j += [i_pt, im2flat[x + 1, y, z]] Ax_v += [-w, w] if y + 1 < ny and (mask[x, y + 1, z] == val): found = True Ay_i += [i_pt, i_pt] Ay_j += [i_pt, im2flat[x, y + 1, z]] Ay_v += [-w, w] if z + 1 < nz and (mask[x, y, z + 1] == val): found = True Az_i += [i_pt, i_pt] Az_j += [i_pt, im2flat[x, y, z + 1]] Az_v += [-w, w] if found: n_compacts += 1 Ax = sparse.csr_matrix((Ax_v, (Ax_i, Ax_j)), shape=(p, p)) Ay = sparse.csr_matrix((Ay_v, (Ay_i, Ay_j)), shape=(p, p)) Az = sparse.csr_matrix((Az_v, (Az_i, Az_j)), shape=(p, p)) A = LinearOperator(Ax, Ay, Az) A.n_compacts = n_compacts return A
def linear_operator_from_mesh(mesh_coord, mesh_triangles, mask=None, offset=0, weights=None): """Generates the linear operator for the total variation Nesterov function from a mesh. Parameters ---------- mesh_coord : Numpy array [n, 3] of float. mesh_triangles : Numpy array, n_triangles-by-3. The (integer) indices of the three nodes forming the triangle. mask : Numpy array (shape (n,)) of integers/boolean. Non-null values correspond to columns of X. Groups may be defined using different values in the mask. TV will be applied within groups of the same value in the mask. offset : Non-negative integer. The index of the first column, variable, where TV applies. This is different from penalty_start which define where the penalty applies. The offset defines where TV applies within the penalised variables. Example: X := [Intercept, Age, Weight, Image]. Intercept is not penalized, TV does not apply on Age and Weight but only on Image. Thus: penalty_start = 1, offset = 2 (skip Age and Weight). weights : Numpy array. The weight put on the gradient of every point. Default is weight 1 for each point, or equivalently, no weight. The weights is a numpy array of the same shape as mask. Returns ------- out1 : List or sparse matrices. Linear operator for the total variation Nesterov function computed over a mesh. out2 : Integer. The number of compacts. Examples -------- >>> import numpy as np >>> import parsimony.functions.nesterov.tv as tv_helper >>> mesh_coord = np.array([[0, 0], [1, 0], [0, 1], [1, 1], [0, 2], [1, 2]]) >>> mesh_triangles = np.array([[0 ,1, 3], [0, 2 ,3], [2, 3, 5], [2, 4, 5]]) >>> A = tv_helper.linear_operator_from_mesh(mesh_coord, mesh_triangles) """ if mask is None: mask = np.ones(mesh_coord.shape[0], dtype=bool) assert mask.shape[0] == mesh_coord.shape[0] mask_bool = mask != 0 mask_idx = np.where(mask_bool)[0] # Mapping from full array to masked array. map_full2masked = np.zeros(mask.shape, dtype=int) map_full2masked[:] = -1 map_full2masked[mask_bool] = np.arange(np.sum(mask_bool)) + offset ## 1) Associate edges to nodes nodes_with_edges = [[] for i in xrange(mesh_coord.shape[0])] def connect_edge_to_node(node_idx1, node_idx2, nodes_with_edges): # Attach edge to first node. if np.sum(mesh_coord[node_idx1] - mesh_coord[node_idx2]) >= 0: edge = [node_idx1, node_idx2] if not edge in nodes_with_edges[node_idx1]: nodes_with_edges[node_idx1].append(edge) else: # attach edge to second node edge = [node_idx2, node_idx1] if not edge in nodes_with_edges[node_idx2]: nodes_with_edges[node_idx2].append(edge) for i in xrange(mesh_triangles.shape[0]): t = mesh_triangles[i, :] connect_edge_to_node(t[0], t[1], nodes_with_edges) connect_edge_to_node(t[0], t[2], nodes_with_edges) connect_edge_to_node(t[1], t[2], nodes_with_edges) max_connectivity = np.max(np.array([len(n) for n in nodes_with_edges])) # 3. build sparse matrices # 1..max_connectivity of i, j and value A = [[[], [], []] for i in xrange(max_connectivity)] n_compacts = 0 for node_idx in mask_idx: #node_idx = 0 found = False node = nodes_with_edges[node_idx] for i, v in enumerate(node): found = False if weights is not None: w = weights[i] else: w = 1.0 #print i, v node1_idx, node2_idx = v if mask_bool[node1_idx] and mask_bool[node2_idx]: found = True A[i][0] += [ map_full2masked[node1_idx], map_full2masked[node1_idx] ] A[i][1] += [ map_full2masked[node1_idx], map_full2masked[node2_idx] ] A[i][2] += [-w, w] if found: n_compacts += 1 p = mask.sum() A = [ sparse.csr_matrix((A[i][2], (A[i][0], A[i][1])), shape=(p, p)) for i in xrange(len(A)) ] A = LinearOperator(*A) A.n_compacts = n_compacts return A