print "\n# - Seeded watershed using seed EXPERT seed positions..." smooth_img = linear_filtering(img, std_dev=std_dev, method='gaussian_smoothing') seg_im = segmentation(smooth_img, seed_img) # Use largest bounding box to determine background value: background = get_background_value(seg_im, microscope_orientation) print "Detected background value:", background # world.add(seg_im,"seg_image", colormap="glasbey", alphamap="constant",voxelsize=microscope_orientation*voxelsize, bg_id=background) # -- Create a vertex_topomesh from detected cell positions: print "\n# - Extracting 'barycenter' & 'L1' properties from segmented image..." # --- Compute 'L1' and 'barycenter' properties using 'graph_from_image' img_graph = graph_from_image( seg_im, background=background, spatio_temporal_properties=['L1', 'barycenter'], ignore_cells_at_stack_margins=True) print img_graph.nb_vertices(), " cells detected" print "\n# - Creating a vertex_topomesh..." vtx = list(img_graph.vertices()) vtx2labels = img_graph.vertex_property('labels') # --- Get cell barycenters positions and L1 cells: bary = img_graph.vertex_property('barycenter') in_L1 = img_graph.vertex_property('L1') # --- Create a topomesh using detected cell barycenters: label_positions = { l: bary[v] * microscope_orientation for v, l in vtx2labels.items() } detected_topomesh = vertex_topomesh(label_positions)
nb_connectec_comp = len(np.unique(con_img.get_array())) print nb_connectec_comp, "seeds detected!" if nb_connectec_comp <= 3: print "Not enough seeds, aborting!" continue # world.add(con_img, 'labelled_seeds', voxelsize=vxs) del ext_img print " --> Seeded watershed..." seg_im = segmentation(img, con_img, method='seeded_watershed') # - Performs topomesh creation from detected seeds: print " --> Analysing segmented image...", img_graph = graph_from_image( seg_im, background=1, spatio_temporal_properties=['barycenter', 'L1'], ignore_cells_at_stack_margins=False) print img_graph.nb_vertices( ), "seeds extracted by 'graph_from_image'!" # -- Create a topomesh from 'seed_positions': print " --> Topomesh creation..." seed_positions = { v: img_graph.vertex_property('barycenter')[v] * microscope_orientation for v in img_graph.vertices() } detected_topomesh = vertex_topomesh(seed_positions) # -- Detect L1 using 'nuclei_layer': oriented_seeds = { k: np.array([1., 1., -1.]) * v
def compute_image_adjacency(self): """Compute the adjacency relationships between cells in the tissue image. By default, the DRACO adjacency complex is set to the cell_vertices tetrahedra (!!NOT A VALID CELL COMPLEX!!) !!STRONG RISK OF TOPOLOGICAL ERRORS IF DUALIZING AT THIS STAGE!! Updates: image_graph (PropertyGraph): adjacency graph connecting adjacent cells by edges image_cell_vertex (dict): cell vertices representing 4 co-adjacent cells by the point where they meet cell_layer (int): information whether the cell belong to the first layer of cells (L1), the second one (L2) or lower layers (0) layer_edge_topomesh (PropertyTopomesh): adjacency graphs restricted to the L1 and L2 layers layer_triangle_topomesh (PropertyTopomesh): adjacency triangles between L1 and L2 layers (**TO BE COMPLETED**) triangulation_topomesh (PropertyTopomesh): the tetrahedra representing adjacencies between cells Returns: None """ self.image_graph = graph_from_image( self.segmented_image, spatio_temporal_properties=["volume", "barycenter", "L1"], ignore_cells_at_stack_margins=False, property_as_real=True, ) self.image_labels = np.array(list(self.image_graph.vertices())) self.image_cell_volumes = array_dict( [self.image_graph.vertex_property("volume")[v] for v in self.image_labels], self.image_labels ) img_center = np.nanmean(self.image_graph.vertex_property("barycenter").values(), axis=0) self.positions = array_dict(self.image_graph.vertex_property("barycenter")) self.point_topomesh = vertex_topomesh(self.positions) img_analysis = SpatialImageAnalysis(self.segmented_image) exterior_cells = np.array(list(img_analysis.neighbors(1))) self.image_wall_surfaces = img_analysis.wall_areas(real=True) self.image_graph.add_vertex(1) for c in exterior_cells: self.image_graph.add_edge(1, c) for v in self.image_cell_vertex.keys(): self.image_cell_vertex[v] = self.image_cell_vertex[v] * self.resolution image_cell_vertex_tetrahedra = np.sort(self.image_cell_vertex.keys()) image_cell_vertex_tetrahedra = np.delete( image_cell_vertex_tetrahedra, np.where(image_cell_vertex_tetrahedra[:, 0] == 1)[0], axis=0 ) self.image_cell_vertex_topomesh = tetrahedra_topomesh(image_cell_vertex_tetrahedra, self.positions) self.triangulation_topomesh = deepcopy(self.image_cell_vertex_topomesh) truncated_image = self.segmented_image[:, :, :] truncated_image_graph = graph_from_image( truncated_image, spatio_temporal_properties=["barycenter", "L1"], background=1, ignore_cells_at_stack_margins=True, property_as_real=True, ) self.cell_layer = array_dict(np.zeros_like(self.positions.keys()), self.positions.keys()) for c in truncated_image_graph.vertices(): if c > 1 and truncated_image_graph.vertex_property("L1")[c]: self.cell_layer[c] = 1 for c in self.cell_layer.keys(): if c > 1 and self.cell_layer[c] == 1: for n in truncated_image_graph.neighbors(c): if n > 1 and self.cell_layer[n] != 1: self.cell_layer[n] = 2 self.point_topomesh.update_wisp_property( "layer", 0, self.cell_layer.values(list(self.point_topomesh.wisps(0))), list(self.point_topomesh.wisps(0)) ) self.layer_edge_topomesh = {} if (self.cell_layer.values() == 1).sum() > 1: L1_edges = np.array( [ [(c, n) for n in self.image_graph.neighbors(c) if n > 1 and self.cell_layer[n] == 1] for c in self.cell_layer.keys() if self.cell_layer[c] == 1 ] ) L1_edges = np.concatenate([e for e in L1_edges if len(e) > 0]) L1_edges = L1_edges[L1_edges[:, 1] > L1_edges[:, 0]] L1_edge_topomesh = edge_topomesh(L1_edges, self.positions) self.layer_edge_topomesh["L1"] = L1_edge_topomesh if (self.cell_layer.values() == 2).sum() > 1: L2_edges = np.array( [ [(c, n) for n in self.image_graph.neighbors(c) if n > 1 and self.cell_layer[n] == 2] for c in self.cell_layer.keys() if self.cell_layer[c] == 2 ] ) L2_edges = np.concatenate([e for e in L2_edges if len(e) > 0]) L2_edges = L2_edges[L2_edges[:, 1] > L2_edges[:, 0]] L2_edge_topomesh = edge_topomesh(L2_edges, self.positions) self.layer_edge_topomesh["L2"] = L2_edge_topomesh self.layer_triangle_topomesh = {} if (self.cell_layer.values() == 1).sum() > 1 and (self.cell_layer.values() == 2).sum() > 1: L1_L2_edges = np.array( [ [(c, n) for n in self.image_graph.neighbors(c) if n > 1 and self.cell_layer[n] in [1, 2]] for c in self.cell_layer.keys() if self.cell_layer[c] in [1, 2] ] ) L1_L2_edges = np.concatenate([e for e in L1_L2_edges if len(e) > 0]) L1_L2_edges = L1_L2_edges[L1_L2_edges[:, 1] > L1_L2_edges[:, 0]] L1_L2_additional_edges = np.array( [ [ (c, n) for n in np.unique( np.array(self.image_cell_vertex.keys())[ np.where(np.array(self.image_cell_vertex.keys()) == c)[0] ] ) if n > 1 and n != c and (n not in self.image_graph.neighbors(c)) and (self.cell_layer[n] in [1, 2]) ] for c in self.cell_layer.keys() if self.cell_layer[c] in [1, 2] ] ) L1_L2_additional_edges = np.concatenate([e for e in L1_L2_additional_edges if len(e) > 0]) L1_L2_additional_edges = L1_L2_additional_edges[L1_L2_additional_edges[:, 1] > L1_L2_additional_edges[:, 0]] self.layer_triangle_topomesh["L1_L2"] = triangle_topomesh( triangles_from_adjacency_edges(np.concatenate([L1_L2_edges, L1_L2_additional_edges])), self.positions )
def create_CGAL_topomesh(img, mesh_fineness=1.0): """ """ dirname = str(shared_data(openalea.cgal_meshing).parent.parent+'/') start_time = time() print "--> Input Image" img_graph = graph_from_image(img,spatio_temporal_properties=['volume','barycenter'],ignore_cells_at_stack_margins = False,property_as_real=False) img_labels = np.array(list(img_graph.vertices())) img_volumes = np.array([img_graph.vertex_property('volume')[v] for v in img_graph.vertices()]) print " --> ",img_labels.shape[0]," Objects, ", img_volumes[2:].mean()," microm3 per object" facet_distance = np.power(img_volumes[2:].mean(),1/3.)/(4.5*np.pi*mesh_fineness) print " --> Facet Distance : ",facet_distance print " --> Converting to 8bit" img_neighbors = np.array([list(img_graph.neighbors(v)) for v in img_graph.vertices()]) count_labels = np.zeros(256) img_graph.add_vertex_property('8bit_labels') if img_labels.shape[0] < 255: for i,v in enumerate(img_graph.vertices()): img_graph.vertex_property('8bit_labels')[v] = i+2 else: for v in img_graph.vertices(): possible_labels = np.array([]) select = 0 while possible_labels.size == 0: possible_labels = set(np.arange(2,256)) possible_labels = possible_labels.difference(set(np.where(count_labels>count_labels[2:].min()+select)[0])) #possible_labels = possible_labels.difference(set(np.where(count_labels>count_labels[2:].min()+1)[0])) neighbor_labels = set() for n in img_graph.neighbors(v): try: neighbor_label = {img_graph.vertex_property('8bit_labels')[n]} for n2 in img_graph.neighbors(n): if n2 != v: try: neighbor_label = {img_graph.vertex_property('8bit_labels')[n2]} except KeyError: neighbor_label = set() pass neighbor_labels = neighbor_labels.union(neighbor_label) except KeyError: neighbor_label = set() pass neighbor_labels = neighbor_labels.union(neighbor_label) possible_labels = np.array(list(possible_labels.difference(neighbor_labels)),np.uint8) if possible_labels.size == 0: select += 1 #print neighbor_labels new_label = possible_labels[np.random.randint(possible_labels.size)] img_graph.vertex_property('8bit_labels')[v] = new_label count_labels[new_label] += 1 #print v, ' -> ', new_label new_labels = np.ones(img.max()+1,np.uint8) new_labels[img_labels] = np.array([img_graph.vertex_property('8bit_labels')[v] for v in img_graph.vertices()],np.uint8) if np.unique(new_labels).shape[0]<255: label_index = np.ones(256) label_index[np.unique(new_labels)] = np.arange(new_labels.shape[0])+1 for v in img_graph.vertices(): img_graph.vertex_property('8bit_labels')[v] = label_index[img_graph.vertex_property('8bit_labels')[v]] new_labels = np.ones(img.max()+1,np.uint8) #new_labels[img_labels] = np.array([img_graph.vertex_property('8bit_labels')[v] for v in img_graph.vertices()],np.uint8) for v in img_graph.vertices(): new_labels[v] = img_graph.vertex_property('8bit_labels')[v] mod_img = np.array(new_labels[img],np.uint8) inrfile = dirname+"tmp/8bit_image.inr.gz" imsave(inrfile,SpatialImage(mod_img)) end_time = time() print "<-- Input Image [",end_time-start_time,"s]" facet_angle = 30.0 facet_size = 40.0 edge_ratio = 4.0 cell_size = 60.0 start_time = time() print "--> Building CGAL Mesh" outputfile = dirname+"tmp/CGAL_output_mesh.mesh" buildCGALMesh(inrfile,outputfile,facet_angle,facet_size,facet_distance,edge_ratio,cell_size) end_time = time() print "<-- Building CGAL Mesh [",end_time-start_time,"s]" mesh = CGALMesh() mesh.createMesh(outputfile) start_time = time() print "--> Re-Indexing Components" mesh.components = np.unique(mesh.tri_subdomains) new_mesh = CGALMesh() new_mesh.createMesh(outputfile) new_mesh.tri_subdomains = np.ones_like(mesh.tri_subdomains) new_mesh.tetra_subdomains = np.ones_like(mesh.tetra_subdomains) new_mesh.tri_subdomains[np.where(mesh.tri_subdomains == mesh.components[0])] = 1 new_mesh.tetra_subdomains[np.where(mesh.tetra_subdomains == mesh.components[0])] = 1 for c in mesh.components[1:]: cell_labels = np.where(new_labels == c)[0] n_cells = cell_labels.size if n_cells > 0: # print " --> Component ",c," -> ",n_cells," Cells" cell_tetrahedra = mesh.tetrahedra[np.where(mesh.tetra_subdomains==c)] #if n_cells == 1: if False: print " --> Component ",c," -> 1 Object (",n_cells," Cell) : ",cell_labels,"(",img_graph.vertex_property('8bit_labels')[cell_labels[0]],")" new_mesh.tetra_subdomains[np.where(mesh.tetra_subdomains == c)] = cell_labels[0] new_mesh.tri_subdomains[np.where(mesh.tri_subdomains == c)] = cell_labels[0] else: cell_tetrahedra_components = np.zeros(cell_tetrahedra.shape[0],int) tetrahedra_component_correspondance = {} tetra_centers = np.mean(mesh.vertices[cell_tetrahedra],axis=1) for t, tetra in enumerate(cell_tetrahedra): if cell_tetrahedra_components[t] == 0: neighbour_tetrahedra = np.unique(np.append(np.where(cell_tetrahedra==tetra[0])[0],np.append(np.where(cell_tetrahedra==tetra[1])[0],np.append(np.where(cell_tetrahedra==tetra[2])[0],np.where(cell_tetrahedra==tetra[3])[0])))) if (cell_tetrahedra_components[neighbour_tetrahedra].max()>0): neighbour_components = cell_tetrahedra_components[neighbour_tetrahedra][np.where(cell_tetrahedra_components[neighbour_tetrahedra]>0)] min_component = np.array([tetrahedra_component_correspondance[component] for component in neighbour_components]).min() for component in neighbour_components: tetrahedra_component_correspondance[tetrahedra_component_correspondance[component]] = min_component tetrahedra_component_correspondance[component] = min_component cell_tetrahedra_components[neighbour_tetrahedra] = min_component else: tetrahedra_component_correspondance[cell_tetrahedra_components.max()+1] = int(cell_tetrahedra_components.max()+1) cell_tetrahedra_components[neighbour_tetrahedra] = int(cell_tetrahedra_components.max()+1) for component in tetrahedra_component_correspondance: label = component while label != tetrahedra_component_correspondance[label]: label = tetrahedra_component_correspondance[label] tetrahedra_component_correspondance[component] = tetrahedra_component_correspondance[label] component_labels = np.unique([tetrahedra_component_correspondance[component] for component in cell_tetrahedra_components]) n_objects = component_labels.size # if n_objects != n_cells: # print tetrahedra_component_correspondance for component in tetrahedra_component_correspondance: tetrahedra_component_correspondance[component] = np.where(component_labels == tetrahedra_component_correspondance[component])[0][0] for component in tetrahedra_component_correspondance: cell_tetrahedra_components[np.where(cell_tetrahedra_components == component)] = tetrahedra_component_correspondance[component] tetrahedra_object_centers = np.zeros((n_objects,3)) tetrahedra_object_centers[:,0] = nd.sum(tetra_centers[:,0],cell_tetrahedra_components,index=xrange(n_objects))/nd.sum(np.ones_like(cell_tetrahedra_components),cell_tetrahedra_components,index=xrange(n_objects)) tetrahedra_object_centers[:,1] = nd.sum(tetra_centers[:,1],cell_tetrahedra_components,index=xrange(n_objects))/nd.sum(np.ones_like(cell_tetrahedra_components),cell_tetrahedra_components,index=xrange(n_objects)) tetrahedra_object_centers[:,2] = nd.sum(tetra_centers[:,2],cell_tetrahedra_components,index=xrange(n_objects))/nd.sum(np.ones_like(cell_tetrahedra_components),cell_tetrahedra_components,index=xrange(n_objects)) img_points = np.array([img_graph.vertex_property('barycenter')[v] for v in cell_labels]) tetrahedra_labels = cell_labels[vq(tetrahedra_object_centers,img_points)[0]] # img_points = np.array([img_graph.vertex_property('barycenter')[v] for v in img_labels]) # tetrahedra_labels = img_labels[vq(tetrahedra_object_centers,img_points)[0]] cell_triangles = mesh.triangles[np.where(mesh.tri_subdomains==c)] cell_triangles_components = np.array([np.unique(cell_tetrahedra_components[np.where(cell_tetrahedra==tri[0])[0]])[0] for tri in cell_triangles]) print " --> Component ",c," -> ",n_objects," Objects (",n_cells," Cells) : ",tetrahedra_labels new_mesh.tetra_subdomains[np.where(mesh.tetra_subdomains == c)] = tetrahedra_labels[cell_tetrahedra_components] new_mesh.tri_subdomains[np.where(mesh.tri_subdomains == c)] = tetrahedra_labels[cell_triangles_components] mesh.tri_subdomains = new_mesh.tri_subdomains mesh.tetra_subdomains = new_mesh.tetra_subdomains mesh.components = np.unique(mesh.tri_subdomains) print mesh.vertices.shape[0],"Vertices, ",mesh.triangles.shape[0],"Triangles, ",mesh.tetrahedra.shape[0],"Tetrahedra, ",mesh.components.shape[0]," Components" end_time = time() print "<-- Re-Indexing Components [",end_time-start_time,"s]" mesh.saveCGALfile(outputfile) start_time = time() print "--> Creating Topomesh" mesh = CGALMesh() mesh.createMesh(outputfile) mesh.generatePropertyTopomesh() positions = array_dict(mesh.vertex_positions.values()*np.array(img.resolution),keys=list(mesh.topo_mesh.wisps(0))) compute_topomesh_property(mesh.topo_mesh,'barycenter',degree=0,positions=positions) end_time = time() print "<-- Creating Topomesh [",end_time-start_time,"s]" return mesh.topo_mesh
def evaluate_topomesh_quality(topomesh,quality_criteria=["Mesh Complexity","Triangle Area Deviation","Triangle Eccentricity","Cell Volume Error","Vertex Distance","Cell Convexity","Epidermis Cell Angle","Vertex Valence","Cell 2 Adjacency"],image=None,image_cell_vertex=None,image_labels=None,image_cell_volumes=None,**kwargs): """ """ quality_data = {} if "Mesh Complexity" in quality_criteria: start_time = time() print "--> Computing mesh complexity" quality_data["Mesh Complexity"] = np.minimum(1.0,(152.*topomesh.nb_wisps(3))/np.sum([len(list(topomesh.border_neighbors(3,c))) for c in topomesh.wisps(3)])) end_time = time() print "<-- Computing mesh complexity [",end_time-start_time,"s]" compute_topomesh_property(topomesh,'length',degree=1) triangular = kwargs.get('triangular',True) display = kwargs.get('display',False) if triangular: compute_topomesh_triangle_properties(topomesh) compute_topomesh_property(topomesh,'normal',degree=2) compute_topomesh_property(topomesh,'angles',degree=2) if "Triangle Area Deviation" in quality_criteria: start_time = time() print "--> Computing triangle area deviation" # area_deviation = np.nanmean(np.abs(topomesh.wisp_property('area',degree=2).values()-np.nanmean(topomesh.wisp_property('area',degree=2).values())))/np.nanmean(topomesh.wisp_property('area',degree=2).values()) area_deviation = np.nanstd(topomesh.wisp_property('area',degree=2).values())/np.nanmean(topomesh.wisp_property('area',degree=2).values()) # quality_data["Triangle Area Deviation"] = np.minimum(1.0,1.0-area_deviation/np.sqrt(2)) # quality_data["Triangle Area Deviation"] = np.minimum(1.0,1.0-area_deviation/2.) quality_data["Triangle Area Deviation"] = np.minimum(1.0,np.sqrt(2)-area_deviation/np.sqrt(2)) end_time = time() print "<-- Computing triangle area deviation [",end_time-start_time,"s]" if "Triangle Eccentricity" in quality_criteria: start_time = time() print "--> Computing triangle eccentricity" quality_data["Triangle Eccentricity"] = 1.-2.*np.nanmean(topomesh.wisp_property('eccentricity',degree=2).values()) end_time = time() print "<-- Computing triangle eccentricity [",end_time-start_time,"s]" compute_topomesh_property(topomesh,'borders',degree=3) compute_topomesh_property(topomesh,'borders',degree=2) compute_topomesh_property(topomesh,'borders',degree=1) compute_topomesh_property(topomesh,'vertices',degree=3) compute_topomesh_property(topomesh,'vertices',degree=2) compute_topomesh_property(topomesh,'barycenter',degree=3) compute_topomesh_property(topomesh,'barycenter',degree=2) compute_topomesh_property(topomesh,'barycenter',degree=1) compute_topomesh_property(topomesh,'triangles',degree=0) compute_topomesh_property(topomesh,'cells',degree=0) compute_topomesh_property(topomesh,'cells',degree=1) compute_topomesh_property(topomesh,'cells',degree=2) compute_topomesh_property(topomesh,'epidermis',degree=0) compute_topomesh_property(topomesh,'epidermis',degree=1) compute_topomesh_property(topomesh,'epidermis',degree=3) if "Cell Volume Error" in quality_criteria or "Cell Convexity" in quality_criteria: compute_topomesh_property(topomesh,'volume',degree=3) compute_topomesh_property(topomesh,'convexhull_volume',degree=3) img_graph = kwargs.get('image_graph',None) if "Cell Volume Error" in quality_criteria: start_time = time() print "--> Computing cell volume error" from vplants.tissue_analysis.temporal_graph_from_image import graph_from_image if (image_cell_volumes == None) or (image_labels == None) or (image_cell_vertex == None): img_graph = graph_from_image(image, spatio_temporal_properties=['volume','barycenter'],background=0,ignore_cells_at_stack_margins = False,property_as_real=True) image_labels = np.array(list(img_graph.vertices())) image_cell_volumes = np.array([img_graph.vertex_property('volume')[v] for v in image_labels]) else: img_graph = None img_volumes = array_dict(image_cell_volumes,image_labels) if triangular: volume_error = array_dict([(c,(topomesh.wisp_property('volume',degree=3)[c] - img_volumes[c])/img_volumes[c]) for c in topomesh.wisps(3)]) else: volume_error = array_dict([(c,(topomesh.wisp_property('convexhull_volume',degree=3)[c] - img_volumes[c])/img_volumes[c]) for c in topomesh.wisps(3)]) quality_data["Cell Volume Error"] = np.maximum(1.-abs(volume_error.values()).mean(),0.0) end_time = time() print "<-- Computing cell volume error [",end_time-start_time,"s]" if "Image Accuracy" in quality_criteria: start_time = time() print "--> Computing image accuracy" from openalea.mesh.utils.image_tools import compute_topomesh_image topomesh_img = compute_topomesh_image(topomesh,image) cells_img = deepcopy(image) for c in set(np.unique(image)).difference(set(topomesh.wisps(3))): cells_img[image==c] = 1 true_positives = ((cells_img != 1)&(cells_img == topomesh_img)).sum() false_positives = ((cells_img == 1) & (topomesh_img != 1)).sum() + ((cells_img != 1)&(topomesh_img != 1)&(cells_img != topomesh_img)).sum() false_negatives = ((cells_img != 1) & (topomesh_img == 1)).sum() + ((cells_img != 1)&(topomesh_img != 1)&(cells_img != topomesh_img)).sum() true_negatives = ((cells_img == 1)&(cells_img == topomesh_img)).sum() estimators = {} estimators['Precision'] = float(true_positives/float(true_positives+false_positives)) estimators['Recall'] = float(true_positives/float(true_positives+false_negatives)) estimators['Dice'] = float(2*true_positives/float(2*true_positives+false_positives+false_negatives)) estimators['Jaccard'] = float(true_positives/float(true_positives+false_positives+false_negatives)) estimators['Accuracy'] = float(true_positives+true_negatives)/float(true_positives+true_negatives+false_positives+false_negatives) estimators['Identity'] = float((cells_img == topomesh_img).sum())/np.prod(cells_img.shape) print estimators quality_data["Image Accuracy"] = estimators['Dice'] end_time = time() print "<-- Computing image accuracy [",end_time-start_time,"s]" vertex_cell_neighbours = topomesh.wisp_property('cells',degree=0) vertex_cell_degree = np.array(map(len,vertex_cell_neighbours.values())) epidermis_vertices = np.array(list(topomesh.wisps(0)))[np.where(topomesh.wisp_property('epidermis',degree=0).values())] start_time = time() print "--> Computing mesh cell vertices" mesh_cell_vertex = {} for v in topomesh.wisps(0): if len(vertex_cell_neighbours[v]) == 5: for k in xrange(5): vertex_cell_labels = tuple([c for c in vertex_cell_neighbours[v]][:k])+tuple([c for c in vertex_cell_neighbours[v]][k+1:]) if not mesh_cell_vertex.has_key(vertex_cell_labels): mesh_cell_vertex[vertex_cell_labels] = v if len(vertex_cell_neighbours[v]) == 4: vertex_cell_labels = tuple([c for c in vertex_cell_neighbours[v]]) mesh_cell_vertex[vertex_cell_labels] = v if v in epidermis_vertices: for k in xrange(4): vertex_cell_labels = (1,) + tuple([c for c in vertex_cell_neighbours[v]][:k])+tuple([c for c in vertex_cell_neighbours[v]][k+1:]) if not mesh_cell_vertex.has_key(vertex_cell_labels): mesh_cell_vertex[vertex_cell_labels] = v if (len(vertex_cell_neighbours[v]) == 3) and (v in epidermis_vertices): vertex_cell_labels = (1,) + tuple([c for c in vertex_cell_neighbours[v]]) mesh_cell_vertex[vertex_cell_labels] = v end_time = time() print "<-- Computing mesh cell vertices [",end_time-start_time,"s]" cell_vertex = np.unique(mesh_cell_vertex.values()) mesh_cell_vertices = topomesh.wisp_property('barycenter',degree=0).values(cell_vertex) if "Vertex Distance" in quality_criteria: start_time = time() print "--> Computing vertex distance" if image_cell_vertex is None: image_cell_vertex = cell_vertex_extraction(image,hollow_out=False,verbose=False) if img_graph is None: img_graph = graph_from_image(image, spatio_temporal_properties=['volume','barycenter'],background=0,ignore_cells_at_stack_margins = False,property_as_real=True) img_edges = np.array([img_graph.edge_vertices(e) for e in img_graph.edges()]) tetra_edge_index_list = np.array([[0,1],[0,2],[0,3],[1,2],[1,3],[2,3]]) img_vertex_edges = np.concatenate(np.sort(image_cell_vertex.keys())[:,tetra_edge_index_list]) vertex_edge_matching = (vq(img_vertex_edges,img_edges)[1] > 0).reshape(len(image_cell_vertex),6).sum(axis=1) img_cell_vertex = np.array(image_cell_vertex.keys())[np.where(vertex_edge_matching==0)[0]] image_cell_vertex = array_dict(np.array([image_cell_vertex[tuple(vertex)] for vertex in img_cell_vertex]),np.sort(img_cell_vertex)) else: image_cell_vertex = deepcopy(image_cell_vertex) # for v in image_cell_vertex.keys(): # image_cell_vertex[v] = image_cell_vertex[v]*np.array(image.resolution) image_cell_vertices = np.array(image_cell_vertex.values()) print mesh_cell_vertices[np.where(1-np.isnan(mesh_cell_vertices)[:,0])]*image.resolution print image_cell_vertices*image.resolution vertex_distances_mesh = array_dict(vq(mesh_cell_vertices[np.where(1-np.isnan(mesh_cell_vertices)[:,0])]*image.resolution,image_cell_vertices*image.resolution)[1],cell_vertex) vertex_distances_image = vq(image_cell_vertices[np.where(1-np.isnan(image_cell_vertices)[:,0])],mesh_cell_vertices)[1] # quality_data["Vertex Distance"] = np.sqrt(3)/(np.maximum(np.sqrt(3),vertex_distances_image.mean())) quality_data["Vertex Distance"] = (0.25*np.sqrt(3))/(np.maximum((0.25*np.sqrt(3)),vertex_distances_image.mean())) end_time = time() print "<-- Computing vertex distance [",end_time-start_time,"s]" if "Cell Convexity" in quality_criteria: start_time = time() print "--> Computing cell convexity" # quality_data["Cell Convexity"] = (topomesh.wisp_property('volume',degree=3).values()/topomesh.wisp_property('convexhull_volume',degree=3).values()).mean() quality_data["Cell Convexity"] = 1 - (((topomesh.wisp_property('convexhull_volume',3).values() - topomesh.wisp_property('volume',3).values()).mean())/topomesh.wisp_property('volume',3).values().mean()) end_time = time() print "<-- Computing cell convexity [",end_time-start_time,"s]" if "Epidermis Cell Angle" in quality_criteria: start_time = time() print "--> Computing epidermis cell angle" epidermis_vertex_cell_angles=np.array([]) epidermis_vertex_angles = array_dict() angle_keys = np.concatenate([np.concatenate([[(t,v)] for v in topomesh.wisp_property('vertices',2)[t]],axis=0) for t in topomesh.wisps(2)],axis=0) angle_dict = dict([(tuple(k),0.) for k in angle_keys]) for v in cell_vertex: if topomesh.wisp_property('epidermis',0)[v]: vertex_triangles = np.array([t for t in topomesh.wisp_property('triangles',0)[v] if topomesh.wisp_property('epidermis',2)[t]]) vertex_triangle_cells = np.concatenate(topomesh.wisp_property('cells',degree=2).values(vertex_triangles)) vertex_cells = np.unique(vertex_triangle_cells) vertex_triangle_cell_directions = topomesh.wisp_property('barycenter',2).values(vertex_triangles) - topomesh.wisp_property('barycenter',3).values(vertex_triangle_cells) vertex_triangle_normals = topomesh.wisp_property('normal',2).values(vertex_triangles) reversed_normals = np.where(np.einsum('ij,ij->i',vertex_triangle_normals,vertex_triangle_cell_directions) < 0)[0] vertex_triangle_normals[reversed_normals] = -vertex_triangle_normals[reversed_normals] vertex_normal = np.mean(vertex_triangle_normals,axis=0) vertex_normal = vertex_normal/np.linalg.norm(vertex_normal) triangle_vertices = topomesh.wisp_property('vertices',degree=2).values(vertex_triangles) triangle_positions = topomesh.wisp_property('barycenter',degree=0).values(triangle_vertices) triangle_proj_vectors = triangle_positions - topomesh.wisp_property('barycenter',degree=0)[v] triangle_proj_dot = np.einsum('...ij,...j->...i',triangle_proj_vectors,vertex_normal) triangle_proj_vectors = -triangle_proj_dot[...,np.newaxis]*vertex_normal triangle_proj_positions = triangle_positions + triangle_proj_vectors edge_index_list = np.array([[1, 2],[0, 1],[0, 2]]) triangle_edge_positions = triangle_proj_positions[:,edge_index_list] triangle_edge_vectors = triangle_edge_positions[:,:,1] - triangle_edge_positions[:,:,0] triangle_edge_lengths = np.linalg.norm(triangle_edge_vectors,axis=2) triangle_cosines = np.zeros_like(triangle_edge_lengths,np.float32) triangle_cosines[:,0] = (triangle_edge_lengths[:,1]**2+triangle_edge_lengths[:,2]**2-triangle_edge_lengths[:,0]**2)/(2.0*triangle_edge_lengths[:,1]*triangle_edge_lengths[:,2]) triangle_cosines[:,2] = (triangle_edge_lengths[:,2]**2+triangle_edge_lengths[:,0]**2-triangle_edge_lengths[:,1]**2)/(2.0*triangle_edge_lengths[:,2]*triangle_edge_lengths[:,0]) triangle_cosines[:,1] = (triangle_edge_lengths[:,0]**2+triangle_edge_lengths[:,1]**2-triangle_edge_lengths[:,2]**2)/(2.0*triangle_edge_lengths[:,0]*triangle_edge_lengths[:,1]) triangle_angles = 180.*np.arccos(triangle_cosines)/np.pi vertex_triangle_angles = triangle_angles[np.where(triangle_vertices==v)] vertex_cell_angles = array_dict(nd.sum(vertex_triangle_angles,vertex_triangle_cells,index=vertex_cells),vertex_cells) for t,c in zip(vertex_triangles,vertex_triangle_cells): angle_dict[(t,v)] = vertex_cell_angles[c] epidermis_vertex_angles[v] = np.sum(vertex_cell_angles.values()) if abs(epidermis_vertex_angles[v]-360)<10: epidermis_vertex_cell_angles = np.concatenate([epidermis_vertex_cell_angles,vertex_cell_angles.values()]) # quality_data["Epidermis Cell Angle"] = 1.0 - ((epidermis_vertex_cell_angles>180).sum()+(epidermis_vertex_cell_angles<90).sum())/(float(epidermis_vertex_cell_angles.shape[0])) quality_data["Epidermis Cell Angle"] = 1 - (np.sum(1 - np.exp(-np.power(np.maximum(100-epidermis_vertex_cell_angles,0.0)/20.,2.0))) + np.sum(1 - np.exp(-np.power(np.maximum(epidermis_vertex_cell_angles-170,0.0)/20.,2.0))))/(float(epidermis_vertex_cell_angles.shape[0])) # np.sqrt(np.sum(np.power(np.maximum(90-epidermis_vertex_cell_angles,0.0)/30.,2.0))) + np.sum(np.maximum(epidermis_vertex_cell_angles-180,0.0)/30.) # quality_data["Epidermis Cell Angle"] = np.minimum(1.0,3-np.sqrt(np.power(epidermis_vertex_cell_angles-120.,2.0).mean())/30.) # quality_data["Epidermis Cell Angle"] = np.minimum(1.0,1.0-np.sqrt(np.power(epidermis_vertex_cell_angles-120.,2.0).mean())/120.) # quality_data["Epidermis Cell Angle"] = 1.0-np.power(np.power(np.abs(epidermis_vertex_cell_angles-120.),1.0).mean(),1.0)/120. end_time = time() print "<-- Computing epidermis cell angle [",end_time-start_time,"s]" if "Vertex Valence" in quality_criteria: start_time = time() print "--> Computing vertex degree" compute_topomesh_property(topomesh,'region_neighbors',degree=0) compute_topomesh_property(topomesh,'border_neighbors',degree=3) # interface_vertices = np.array(list(topomesh.wisps(0)))[np.where((np.array(map(len,topomesh.wisp_property('cells',0).values())) == 1) # | ((np.array(map(len,topomesh.wisp_property('cells',0).values())) == 2) # & (1- topomesh.wisp_property('epidermis',0).values())))] # interface_vertices_degree = np.array(map(len,topomesh.wisp_property('neighbors',0).values(interface_vertices))) target_neighborhood = array_dict((np.array(map(len,topomesh.wisp_property('cells',0).values())) + topomesh.wisp_property('epidermis',0).values())*3,list(topomesh.wisps(0))) vertices_neighborhood = array_dict(map(len,topomesh.wisp_property('neighbors',0).values()),list(topomesh.wisps(0))) interface_vertices = target_neighborhood.keys_where("==6") # np.power(np.power(target_neighborhood.values(interface_vertices)-vertices_neighborhood.values(interface_vertices),2.0).mean(),0.5)/6.0 quality_data["Vertex Valence"] = np.minimum(1.0,1.0-np.abs(target_neighborhood.values(interface_vertices)-vertices_neighborhood.values(interface_vertices)).mean()/6.0) # quality_data["Vertex Valence"] = np.minimum(1.0,1.0-np.nanmean(np.abs(interface_vertices_degree-6.0))) # quality_data["Vertex Valence"] = np.minimum(1.0,1.0-np.power(interface_vertices_degree-6.0,2.0).mean()/6.0) end_time = time() print "<-- Computing vertex degree [",end_time-start_time,"s]" if "Cell 4 Adjacency" in quality_criteria: start_time = time() print "--> Computing cell adjacency" if image_cell_vertex==None: image_cell_vertex = cell_vertex_extraction(img,hollow_out=False,verbose=False) if img_graph == None: img_graph = graph_from_image(img, spatio_temporal_properties=['volume','barycenter'],background=0,ignore_cells_at_stack_margins = False,property_as_real=False,min_contact_surface=0.5) img_edges = np.array([img_graph.edge_vertices(e) for e in img_graph.edges()]) tetra_edge_index_list = np.array([[0,1],[0,2],[0,3],[1,2],[1,3],[2,3]]) img_vertex_edges = np.concatenate(np.sort(image_cell_vertex.keys())[:,tetra_edge_index_list]) vertex_edge_matching = (vq(img_vertex_edges,img_edges)[1] > 0).reshape(len(image_cell_vertex),6).sum(axis=1) img_cell_vertex = np.array(image_cell_vertex.keys())[np.where(vertex_edge_matching==0)[0]] image_cell_vertex = array_dict(np.array([image_cell_vertex[tuple(vertex)] for vertex in img_cell_vertex]),np.sort(img_cell_vertex)) cell_vertex_VP = (vq(np.sort(array_dict(mesh_cell_vertex).keys()),np.sort(array_dict(image_cell_vertex).keys()))[1]==0).sum() cell_vertex_FP = (vq(np.sort(array_dict(mesh_cell_vertex).keys()),np.sort(array_dict(image_cell_vertex).keys()))[1]>0).sum() cell_vertex_FN = (vq(np.sort(array_dict(image_cell_vertex).keys()),np.sort(array_dict(mesh_cell_vertex).keys()))[1]>0).sum() cell_vertex_jaccard = cell_vertex_VP/float(cell_vertex_VP+cell_vertex_FP+cell_vertex_FN) cell_vertex_dice = 2*cell_vertex_VP/float(2*cell_vertex_VP+cell_vertex_FP+cell_vertex_FN) quality_data["Cell 4 Adjacency"] = cell_vertex_jaccard end_time = time() print "<-- Computing cell adjacency [",end_time-start_time,"s]" if "Cell 2 Adjacency" in quality_criteria: start_time = time() print "--> Computing cell adjacency" tetra_edge_index_list = np.array([[0,1],[0,2],[0,3],[1,2],[1,3],[2,3]]) if image_cell_vertex==None: image_cell_vertex = cell_vertex_extraction(img,hollow_out=False,verbose=False) if img_graph == None: img_graph = graph_from_image(img, spatio_temporal_properties=['volume','barycenter'],background=0,ignore_cells_at_stack_margins = False,property_as_real=False,min_contact_surface=0.5) img_edges = np.array([img_graph.edge_vertices(e) for e in img_graph.edges()]) img_vertex_edges = np.concatenate(np.sort(image_cell_vertex.keys())[:,tetra_edge_index_list]) vertex_edge_matching = (vq(img_vertex_edges,img_edges)[1] > 0).reshape(len(image_cell_vertex),6).sum(axis=1) img_cell_vertex = np.array(image_cell_vertex.keys())[np.where(vertex_edge_matching==0)[0]] image_cell_vertex = array_dict(np.array([image_cell_vertex[tuple(vertex)] for vertex in img_cell_vertex]),np.sort(img_cell_vertex)) from vplants.meshing.array_tools import array_unique mesh_cell_edges = array_unique(np.concatenate(np.sort(array_dict(mesh_cell_vertex).keys())[:,tetra_edge_index_list])) image_cell_edges = array_unique(np.concatenate(np.sort(array_dict(image_cell_vertex).keys())[:,tetra_edge_index_list])) from vplants.meshing.evaluation_tools import jaccard_index cell_edge_jaccard = jaccard_index(image_cell_edges,mesh_cell_edges) quality_data["Cell 2 Adjacency"] = cell_edge_jaccard end_time = time() print "<-- Computing cell adjacency [",end_time-start_time,"s]" if "Cell Cliques" in quality_criteria: vertex_cells = np.array([len(list(topomesh.regions(0,v,3))) for v in topomesh.wisps(0)]) vertex_epidermis = topomesh.wisp_property('epidermis',degree=0).values() cell_vertices = ((vertex_cells>=3)*(vertex_epidermis)+(vertex_cells>=4)*(True-vertex_epidermis)).sum() clique_cell_vertices = ((vertex_cells>3)*(vertex_epidermis)+(vertex_cells>4)*(True-vertex_epidermis)).sum() quality_data["Cell Cliques"] = 1.0 - float(clique_cell_vertices)/float(cell_vertices) print quality_data # if display: # spider_figure = plt.figure(kwargs.get('figure_title',"Topomesh Quality")) # spider_figure.clf() # spider_data = np.array([quality_data[c] for c in quality_criteria]) # spider_fields= quality_criteria # # spider_targets = [0.8,0.7,0.8,0.8,0.9,0.8,0.7] # spider_targets = 0.8 * np.ones_like(quality_criteria,float) # spider_plot(spider_figure,spider_data,color1=np.array([0.3,0.6,1.]),color2=np.array([1.,0.,0.]),xlabels=spider_fields,ytargets=spider_targets,n_points=100*len(quality_criteria),linewidth=2,smooth_factor=0.0,spline_order=1) # plt.show(block=False) return quality_data
def evaluate_topomesh_quality(topomesh, quality_criteria=[ "Mesh Complexity", "Triangle Area Deviation", "Triangle Eccentricity", "Cell Volume Error", "Vertex Distance", "Cell Convexity", "Epidermis Cell Angle", "Vertex Valence", "Cell 2 Adjacency" ], image=None, image_cell_vertex=None, image_labels=None, image_cell_volumes=None, **kwargs): """ """ quality_data = {} if "Mesh Complexity" in quality_criteria: start_time = time() print "--> Computing mesh complexity" quality_data["Mesh Complexity"] = np.minimum( 1.0, (152. * topomesh.nb_wisps(3)) / np.sum([ len(list(topomesh.border_neighbors(3, c))) for c in topomesh.wisps(3) ])) end_time = time() print "<-- Computing mesh complexity [", end_time - start_time, "s]" compute_topomesh_property(topomesh, 'length', degree=1) triangular = kwargs.get('triangular', True) display = kwargs.get('display', False) if triangular: compute_topomesh_triangle_properties(topomesh) compute_topomesh_property(topomesh, 'normal', degree=2) compute_topomesh_property(topomesh, 'angles', degree=2) if "Triangle Area Deviation" in quality_criteria: start_time = time() print "--> Computing triangle area deviation" # area_deviation = np.nanmean(np.abs(topomesh.wisp_property('area',degree=2).values()-np.nanmean(topomesh.wisp_property('area',degree=2).values())))/np.nanmean(topomesh.wisp_property('area',degree=2).values()) area_deviation = np.nanstd( topomesh.wisp_property('area', degree=2).values()) / np.nanmean( topomesh.wisp_property('area', degree=2).values()) # quality_data["Triangle Area Deviation"] = np.minimum(1.0,1.0-area_deviation/np.sqrt(2)) # quality_data["Triangle Area Deviation"] = np.minimum(1.0,1.0-area_deviation/2.) quality_data["Triangle Area Deviation"] = np.minimum( 1.0, np.sqrt(2) - area_deviation / np.sqrt(2)) end_time = time() print "<-- Computing triangle area deviation [", end_time - start_time, "s]" if "Triangle Eccentricity" in quality_criteria: start_time = time() print "--> Computing triangle eccentricity" quality_data["Triangle Eccentricity"] = 1. - 2. * np.nanmean( topomesh.wisp_property('eccentricity', degree=2).values()) end_time = time() print "<-- Computing triangle eccentricity [", end_time - start_time, "s]" compute_topomesh_property(topomesh, 'borders', degree=3) compute_topomesh_property(topomesh, 'borders', degree=2) compute_topomesh_property(topomesh, 'borders', degree=1) compute_topomesh_property(topomesh, 'vertices', degree=3) compute_topomesh_property(topomesh, 'vertices', degree=2) compute_topomesh_property(topomesh, 'barycenter', degree=3) compute_topomesh_property(topomesh, 'barycenter', degree=2) compute_topomesh_property(topomesh, 'barycenter', degree=1) compute_topomesh_property(topomesh, 'triangles', degree=0) compute_topomesh_property(topomesh, 'cells', degree=0) compute_topomesh_property(topomesh, 'cells', degree=1) compute_topomesh_property(topomesh, 'cells', degree=2) compute_topomesh_property(topomesh, 'epidermis', degree=0) compute_topomesh_property(topomesh, 'epidermis', degree=1) compute_topomesh_property(topomesh, 'epidermis', degree=3) if "Cell Volume Error" in quality_criteria or "Cell Convexity" in quality_criteria: compute_topomesh_property(topomesh, 'volume', degree=3) compute_topomesh_property(topomesh, 'convexhull_volume', degree=3) img_graph = kwargs.get('image_graph', None) if "Cell Volume Error" in quality_criteria: start_time = time() print "--> Computing cell volume error" from vplants.tissue_analysis.temporal_graph_from_image import graph_from_image if (image_cell_volumes == None) or (image_labels == None) or (image_cell_vertex == None): img_graph = graph_from_image( image, spatio_temporal_properties=['volume', 'barycenter'], background=0, ignore_cells_at_stack_margins=False, property_as_real=True) image_labels = np.array(list(img_graph.vertices())) image_cell_volumes = np.array( [img_graph.vertex_property('volume')[v] for v in image_labels]) else: img_graph = None img_volumes = array_dict(image_cell_volumes, image_labels) if triangular: volume_error = array_dict([ (c, (topomesh.wisp_property('volume', degree=3)[c] - img_volumes[c]) / img_volumes[c]) for c in topomesh.wisps(3) ]) else: volume_error = array_dict([ (c, (topomesh.wisp_property('convexhull_volume', degree=3)[c] - img_volumes[c]) / img_volumes[c]) for c in topomesh.wisps(3) ]) quality_data["Cell Volume Error"] = np.maximum( 1. - abs(volume_error.values()).mean(), 0.0) end_time = time() print "<-- Computing cell volume error [", end_time - start_time, "s]" if "Image Accuracy" in quality_criteria: start_time = time() print "--> Computing image accuracy" from openalea.mesh.utils.image_tools import compute_topomesh_image topomesh_img = compute_topomesh_image(topomesh, image) cells_img = deepcopy(image) for c in set(np.unique(image)).difference(set(topomesh.wisps(3))): cells_img[image == c] = 1 true_positives = ((cells_img != 1) & (cells_img == topomesh_img)).sum() false_positives = ((cells_img == 1) & (topomesh_img != 1)).sum() + ( (cells_img != 1) & (topomesh_img != 1) & (cells_img != topomesh_img)).sum() false_negatives = ((cells_img != 1) & (topomesh_img == 1)).sum() + ( (cells_img != 1) & (topomesh_img != 1) & (cells_img != topomesh_img)).sum() true_negatives = ((cells_img == 1) & (cells_img == topomesh_img)).sum() estimators = {} estimators['Precision'] = float( true_positives / float(true_positives + false_positives)) estimators['Recall'] = float(true_positives / float(true_positives + false_negatives)) estimators['Dice'] = float( 2 * true_positives / float(2 * true_positives + false_positives + false_negatives)) estimators['Jaccard'] = float( true_positives / float(true_positives + false_positives + false_negatives)) estimators['Accuracy'] = float(true_positives + true_negatives) / float( true_positives + true_negatives + false_positives + false_negatives) estimators['Identity'] = float( (cells_img == topomesh_img).sum()) / np.prod(cells_img.shape) print estimators quality_data["Image Accuracy"] = estimators['Dice'] end_time = time() print "<-- Computing image accuracy [", end_time - start_time, "s]" vertex_cell_neighbours = topomesh.wisp_property('cells', degree=0) vertex_cell_degree = np.array(map(len, vertex_cell_neighbours.values())) epidermis_vertices = np.array(list(topomesh.wisps(0)))[np.where( topomesh.wisp_property('epidermis', degree=0).values())] start_time = time() print "--> Computing mesh cell vertices" mesh_cell_vertex = {} for v in topomesh.wisps(0): if len(vertex_cell_neighbours[v]) == 5: for k in xrange(5): vertex_cell_labels = tuple([ c for c in vertex_cell_neighbours[v] ][:k]) + tuple([c for c in vertex_cell_neighbours[v]][k + 1:]) if not mesh_cell_vertex.has_key(vertex_cell_labels): mesh_cell_vertex[vertex_cell_labels] = v if len(vertex_cell_neighbours[v]) == 4: vertex_cell_labels = tuple([c for c in vertex_cell_neighbours[v]]) mesh_cell_vertex[vertex_cell_labels] = v if v in epidermis_vertices: for k in xrange(4): vertex_cell_labels = (1, ) + tuple( [c for c in vertex_cell_neighbours[v]][:k]) + tuple( [c for c in vertex_cell_neighbours[v]][k + 1:]) if not mesh_cell_vertex.has_key(vertex_cell_labels): mesh_cell_vertex[vertex_cell_labels] = v if (len(vertex_cell_neighbours[v]) == 3) and (v in epidermis_vertices): vertex_cell_labels = (1, ) + tuple( [c for c in vertex_cell_neighbours[v]]) mesh_cell_vertex[vertex_cell_labels] = v end_time = time() print "<-- Computing mesh cell vertices [", end_time - start_time, "s]" cell_vertex = np.unique(mesh_cell_vertex.values()) mesh_cell_vertices = topomesh.wisp_property('barycenter', degree=0).values(cell_vertex) if "Vertex Distance" in quality_criteria: start_time = time() print "--> Computing vertex distance" if image_cell_vertex is None: image_cell_vertex = cell_vertex_extraction(image, hollow_out=False, verbose=False) if img_graph is None: img_graph = graph_from_image( image, spatio_temporal_properties=['volume', 'barycenter'], background=0, ignore_cells_at_stack_margins=False, property_as_real=True) img_edges = np.array( [img_graph.edge_vertices(e) for e in img_graph.edges()]) tetra_edge_index_list = np.array([[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]) img_vertex_edges = np.concatenate( np.sort(image_cell_vertex.keys())[:, tetra_edge_index_list]) vertex_edge_matching = (vq(img_vertex_edges, img_edges)[1] > 0).reshape(len(image_cell_vertex), 6).sum(axis=1) img_cell_vertex = np.array(image_cell_vertex.keys())[np.where( vertex_edge_matching == 0)[0]] image_cell_vertex = array_dict( np.array([ image_cell_vertex[tuple(vertex)] for vertex in img_cell_vertex ]), np.sort(img_cell_vertex)) else: image_cell_vertex = deepcopy(image_cell_vertex) # for v in image_cell_vertex.keys(): # image_cell_vertex[v] = image_cell_vertex[v]*np.array(image.voxelsize) image_cell_vertices = np.array(image_cell_vertex.values()) print mesh_cell_vertices[np.where( 1 - np.isnan(mesh_cell_vertices)[:, 0])] * image.voxelsize print image_cell_vertices * image.voxelsize vertex_distances_mesh = array_dict( vq( mesh_cell_vertices[np.where( 1 - np.isnan(mesh_cell_vertices)[:, 0])] * image.voxelsize, image_cell_vertices * image.voxelsize)[1], cell_vertex) vertex_distances_image = vq( image_cell_vertices[np.where(1 - np.isnan(image_cell_vertices)[:, 0])], mesh_cell_vertices)[1] # quality_data["Vertex Distance"] = np.sqrt(3)/(np.maximum(np.sqrt(3),vertex_distances_image.mean())) quality_data["Vertex Distance"] = (0.25 * np.sqrt(3)) / (np.maximum( (0.25 * np.sqrt(3)), vertex_distances_image.mean())) end_time = time() print "<-- Computing vertex distance [", end_time - start_time, "s]" if "Cell Convexity" in quality_criteria: start_time = time() print "--> Computing cell convexity" # quality_data["Cell Convexity"] = (topomesh.wisp_property('volume',degree=3).values()/topomesh.wisp_property('convexhull_volume',degree=3).values()).mean() quality_data["Cell Convexity"] = 1 - ( ((topomesh.wisp_property('convexhull_volume', 3).values() - topomesh.wisp_property('volume', 3).values()).mean()) / topomesh.wisp_property('volume', 3).values().mean()) end_time = time() print "<-- Computing cell convexity [", end_time - start_time, "s]" if "Epidermis Cell Angle" in quality_criteria: start_time = time() print "--> Computing epidermis cell angle" epidermis_vertex_cell_angles = np.array([]) epidermis_vertex_angles = array_dict() angle_keys = np.concatenate([ np.concatenate([[(t, v)] for v in topomesh.wisp_property('vertices', 2)[t]], axis=0) for t in topomesh.wisps(2) ], axis=0) angle_dict = dict([(tuple(k), 0.) for k in angle_keys]) for v in cell_vertex: if topomesh.wisp_property('epidermis', 0)[v]: vertex_triangles = np.array([ t for t in topomesh.wisp_property('triangles', 0)[v] if topomesh.wisp_property('epidermis', 2)[t] ]) vertex_triangle_cells = np.concatenate( topomesh.wisp_property('cells', degree=2).values(vertex_triangles)) vertex_cells = np.unique(vertex_triangle_cells) vertex_triangle_cell_directions = topomesh.wisp_property( 'barycenter', 2).values(vertex_triangles) - topomesh.wisp_property( 'barycenter', 3).values(vertex_triangle_cells) vertex_triangle_normals = topomesh.wisp_property( 'normal', 2).values(vertex_triangles) reversed_normals = np.where( np.einsum('ij,ij->i', vertex_triangle_normals, vertex_triangle_cell_directions) < 0)[0] vertex_triangle_normals[ reversed_normals] = -vertex_triangle_normals[ reversed_normals] vertex_normal = np.mean(vertex_triangle_normals, axis=0) vertex_normal = vertex_normal / np.linalg.norm(vertex_normal) triangle_vertices = topomesh.wisp_property( 'vertices', degree=2).values(vertex_triangles) triangle_positions = topomesh.wisp_property( 'barycenter', degree=0).values(triangle_vertices) triangle_proj_vectors = triangle_positions - topomesh.wisp_property( 'barycenter', degree=0)[v] triangle_proj_dot = np.einsum('...ij,...j->...i', triangle_proj_vectors, vertex_normal) triangle_proj_vectors = -triangle_proj_dot[ ..., np.newaxis] * vertex_normal triangle_proj_positions = triangle_positions + triangle_proj_vectors edge_index_list = np.array([[1, 2], [0, 1], [0, 2]]) triangle_edge_positions = triangle_proj_positions[:, edge_index_list] triangle_edge_vectors = triangle_edge_positions[:, :, 1] - triangle_edge_positions[:, :, 0] triangle_edge_lengths = np.linalg.norm(triangle_edge_vectors, axis=2) triangle_cosines = np.zeros_like(triangle_edge_lengths, np.float32) triangle_cosines[:, 0] = (triangle_edge_lengths[:, 1]**2 + triangle_edge_lengths[:, 2]**2 - triangle_edge_lengths[:, 0]**2) / ( 2.0 * triangle_edge_lengths[:, 1] * triangle_edge_lengths[:, 2]) triangle_cosines[:, 2] = (triangle_edge_lengths[:, 2]**2 + triangle_edge_lengths[:, 0]**2 - triangle_edge_lengths[:, 1]**2) / ( 2.0 * triangle_edge_lengths[:, 2] * triangle_edge_lengths[:, 0]) triangle_cosines[:, 1] = (triangle_edge_lengths[:, 0]**2 + triangle_edge_lengths[:, 1]**2 - triangle_edge_lengths[:, 2]**2) / ( 2.0 * triangle_edge_lengths[:, 0] * triangle_edge_lengths[:, 1]) triangle_angles = 180. * np.arccos(triangle_cosines) / np.pi vertex_triangle_angles = triangle_angles[np.where( triangle_vertices == v)] vertex_cell_angles = array_dict( nd.sum(vertex_triangle_angles, vertex_triangle_cells, index=vertex_cells), vertex_cells) for t, c in zip(vertex_triangles, vertex_triangle_cells): angle_dict[(t, v)] = vertex_cell_angles[c] epidermis_vertex_angles[v] = np.sum( vertex_cell_angles.values()) if abs(epidermis_vertex_angles[v] - 360) < 10: epidermis_vertex_cell_angles = np.concatenate([ epidermis_vertex_cell_angles, vertex_cell_angles.values() ]) # quality_data["Epidermis Cell Angle"] = 1.0 - ((epidermis_vertex_cell_angles>180).sum()+(epidermis_vertex_cell_angles<90).sum())/(float(epidermis_vertex_cell_angles.shape[0])) quality_data["Epidermis Cell Angle"] = 1 - (np.sum(1 - np.exp(-np.power( np.maximum(100 - epidermis_vertex_cell_angles, 0.0) / 20., 2.0))) + np.sum(1 - np.exp(-np.power( np.maximum(epidermis_vertex_cell_angles - 170, 0.0) / 20., 2.0)))) / (float(epidermis_vertex_cell_angles.shape[0])) # np.sqrt(np.sum(np.power(np.maximum(90-epidermis_vertex_cell_angles,0.0)/30.,2.0))) + np.sum(np.maximum(epidermis_vertex_cell_angles-180,0.0)/30.) # quality_data["Epidermis Cell Angle"] = np.minimum(1.0,3-np.sqrt(np.power(epidermis_vertex_cell_angles-120.,2.0).mean())/30.) # quality_data["Epidermis Cell Angle"] = np.minimum(1.0,1.0-np.sqrt(np.power(epidermis_vertex_cell_angles-120.,2.0).mean())/120.) # quality_data["Epidermis Cell Angle"] = 1.0-np.power(np.power(np.abs(epidermis_vertex_cell_angles-120.),1.0).mean(),1.0)/120. end_time = time() print "<-- Computing epidermis cell angle [", end_time - start_time, "s]" if "Vertex Valence" in quality_criteria: start_time = time() print "--> Computing vertex degree" compute_topomesh_property(topomesh, 'region_neighbors', degree=0) compute_topomesh_property(topomesh, 'border_neighbors', degree=3) # interface_vertices = np.array(list(topomesh.wisps(0)))[np.where((np.array(map(len,topomesh.wisp_property('cells',0).values())) == 1) # | ((np.array(map(len,topomesh.wisp_property('cells',0).values())) == 2) # & (1- topomesh.wisp_property('epidermis',0).values())))] # interface_vertices_degree = np.array(map(len,topomesh.wisp_property('neighbors',0).values(interface_vertices))) target_neighborhood = array_dict( (np.array(map(len, topomesh.wisp_property('cells', 0).values())) + topomesh.wisp_property('epidermis', 0).values()) * 3, list(topomesh.wisps(0))) vertices_neighborhood = array_dict( map(len, topomesh.wisp_property('neighbors', 0).values()), list(topomesh.wisps(0))) interface_vertices = target_neighborhood.keys_where("==6") # np.power(np.power(target_neighborhood.values(interface_vertices)-vertices_neighborhood.values(interface_vertices),2.0).mean(),0.5)/6.0 quality_data["Vertex Valence"] = np.minimum( 1.0, 1.0 - np.abs( target_neighborhood.values(interface_vertices) - vertices_neighborhood.values(interface_vertices)).mean() / 6.0) # quality_data["Vertex Valence"] = np.minimum(1.0,1.0-np.nanmean(np.abs(interface_vertices_degree-6.0))) # quality_data["Vertex Valence"] = np.minimum(1.0,1.0-np.power(interface_vertices_degree-6.0,2.0).mean()/6.0) end_time = time() print "<-- Computing vertex degree [", end_time - start_time, "s]" if "Cell 4 Adjacency" in quality_criteria: start_time = time() print "--> Computing cell adjacency" if image_cell_vertex == None: image_cell_vertex = cell_vertex_extraction(img, hollow_out=False, verbose=False) if img_graph == None: img_graph = graph_from_image( img, spatio_temporal_properties=['volume', 'barycenter'], background=0, ignore_cells_at_stack_margins=False, property_as_real=False, min_contact_surface=0.5) img_edges = np.array( [img_graph.edge_vertices(e) for e in img_graph.edges()]) tetra_edge_index_list = np.array([[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]) img_vertex_edges = np.concatenate( np.sort(image_cell_vertex.keys())[:, tetra_edge_index_list]) vertex_edge_matching = (vq(img_vertex_edges, img_edges)[1] > 0).reshape(len(image_cell_vertex), 6).sum(axis=1) img_cell_vertex = np.array(image_cell_vertex.keys())[np.where( vertex_edge_matching == 0)[0]] image_cell_vertex = array_dict( np.array([ image_cell_vertex[tuple(vertex)] for vertex in img_cell_vertex ]), np.sort(img_cell_vertex)) cell_vertex_VP = (vq( np.sort(array_dict(mesh_cell_vertex).keys()), np.sort(array_dict(image_cell_vertex).keys()))[1] == 0).sum() cell_vertex_FP = (vq(np.sort(array_dict(mesh_cell_vertex).keys()), np.sort(array_dict(image_cell_vertex).keys()))[1] > 0).sum() cell_vertex_FN = (vq(np.sort(array_dict(image_cell_vertex).keys()), np.sort(array_dict(mesh_cell_vertex).keys()))[1] > 0).sum() cell_vertex_jaccard = cell_vertex_VP / float(cell_vertex_VP + cell_vertex_FP + cell_vertex_FN) cell_vertex_dice = 2 * cell_vertex_VP / float(2 * cell_vertex_VP + cell_vertex_FP + cell_vertex_FN) quality_data["Cell 4 Adjacency"] = cell_vertex_jaccard end_time = time() print "<-- Computing cell adjacency [", end_time - start_time, "s]" if "Cell 2 Adjacency" in quality_criteria: start_time = time() print "--> Computing cell adjacency" tetra_edge_index_list = np.array([[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]) if image_cell_vertex == None: image_cell_vertex = cell_vertex_extraction(img, hollow_out=False, verbose=False) if img_graph == None: img_graph = graph_from_image( img, spatio_temporal_properties=['volume', 'barycenter'], background=0, ignore_cells_at_stack_margins=False, property_as_real=False, min_contact_surface=0.5) img_edges = np.array( [img_graph.edge_vertices(e) for e in img_graph.edges()]) img_vertex_edges = np.concatenate( np.sort(image_cell_vertex.keys())[:, tetra_edge_index_list]) vertex_edge_matching = (vq(img_vertex_edges, img_edges)[1] > 0).reshape(len(image_cell_vertex), 6).sum(axis=1) img_cell_vertex = np.array(image_cell_vertex.keys())[np.where( vertex_edge_matching == 0)[0]] image_cell_vertex = array_dict( np.array([ image_cell_vertex[tuple(vertex)] for vertex in img_cell_vertex ]), np.sort(img_cell_vertex)) from vplants.meshing.array_tools import array_unique mesh_cell_edges = array_unique( np.concatenate( np.sort(array_dict(mesh_cell_vertex).keys()) [:, tetra_edge_index_list])) image_cell_edges = array_unique( np.concatenate( np.sort(array_dict(image_cell_vertex).keys()) [:, tetra_edge_index_list])) from vplants.meshing.evaluation_tools import jaccard_index cell_edge_jaccard = jaccard_index(image_cell_edges, mesh_cell_edges) quality_data["Cell 2 Adjacency"] = cell_edge_jaccard end_time = time() print "<-- Computing cell adjacency [", end_time - start_time, "s]" if "Cell Cliques" in quality_criteria: vertex_cells = np.array( [len(list(topomesh.regions(0, v, 3))) for v in topomesh.wisps(0)]) vertex_epidermis = topomesh.wisp_property('epidermis', degree=0).values() cell_vertices = ((vertex_cells >= 3) * (vertex_epidermis) + (vertex_cells >= 4) * (True - vertex_epidermis)).sum() clique_cell_vertices = ((vertex_cells > 3) * (vertex_epidermis) + (vertex_cells > 4) * (True - vertex_epidermis)).sum() quality_data["Cell Cliques"] = 1.0 - float( clique_cell_vertices) / float(cell_vertices) print quality_data # if display: # spider_figure = plt.figure(kwargs.get('figure_title',"Topomesh Quality")) # spider_figure.clf() # spider_data = np.array([quality_data[c] for c in quality_criteria]) # spider_fields= quality_criteria # # spider_targets = [0.8,0.7,0.8,0.8,0.9,0.8,0.7] # spider_targets = 0.8 * np.ones_like(quality_criteria,float) # spider_plot(spider_figure,spider_data,color1=np.array([0.3,0.6,1.]),color2=np.array([1.,0.,0.]),xlabels=spider_fields,ytargets=spider_targets,n_points=100*len(quality_criteria),linewidth=2,smooth_factor=0.0,spline_order=1) # plt.show(block=False) return quality_data
background_adjacency = False img = imread(filename) if 'world' in dir(): world.add(img, 'segmented_image', colormap='glasbey', alphamap='constant', alpha=0.2) cell_vertex = cell_vertex_extraction(img) tetras = np.array(cell_vertex.keys()) img_graph = graph_from_image( img, spatio_temporal_properties=['barycenter', 'volume'], ignore_cells_at_stack_margins=True) positions = img_graph.vertex_property('barycenter') if background_adjacency: positions[1] = np.array([0, 0, 0], float) else: tetras = tetras[tetras[:, 0] != 1] topomesh = tetrahedra_topomesh(tetras, positions) if 'world' in dir(): world.add(topomesh, 'adjacency') world['adjacency_cells']['polydata_colormap'] = load_colormaps()['grey'] world['adjacency_cells']['intensity_range'] = (-1, 0) world['adjacency_cells']['polydata_alpha'] = 0.5