Source code for skgtimage.utils.recognition

from skgtimage.core.graph import relabel_nodes
from skgtimage.core.photometry import grey_levels
from skgtimage.core.filtering import size_filtering,merge_photometry_gray,merge_photometry_color
from skgtimage.core.isomorphism import common_subgraphisomorphisms
from skgtimage.core.criterion import best_common_subgraphisomorphism
from skgtimage.core.propagation import propagate
from skgtimage.core.factory import from_string,from_labelled_image
from skgtimage.core.background import remove_background
from skgtimage.utils.rag import rag_merge,rag_merge_until_commonisomorphism
from skgtimage.utils.color import rgb2chsv


import time
import copy
import numpy as np

class RecognitionException(Exception):
    def __init__(self,recognizer,message):
        self.recognizer=recognizer
        self.message=message

[docs]def recognize(image, label, t_desc, p_desc, mc=False, roi=None, min_size=None, bg=False, bound_thickness=0, rag=None, merge=None, verbose=False): """ Compute and return identified regions, specified in qualitative descriptions (t_desc, p_desc), from the provided over-segmentation (label) of the image (image) :param image: input image (numpy array), can be 2D, 3D, grayscale, color :param label: input oversegmentation (numpy array) :param t_desc: description of inclusion relationships (string) :param p_desc: description of photometric relationships (string) :param mc: specifies if image is multi-component (True - color in our case) or not (False - grayscale). :param roi: region of interest (numpy array), corresponding to non zeros. :param min_size: minimum size (in pixels) of considered regions. Regions smaller than min_size are removed. :param bg: specifies whether background must be removed :param bound_thickness: thickness of the enveloppe surrounding the roi (if roi is not none) :param rag: if not None, a preliminary merging of photometrically similar neighboring regions is performed. The parameter specifies the similarity threshold (threshold the in merge_hierarchical function of scikit-image) :param merge: if not None, a preliminary merging of photometrically similar regions is performed (not necessarily neighboring regions). The parameter specifies the number of finally expected regions. :param verbose: if True, details of the procedure are printed :return: a mapping "id - regions" (python mapping type - dictionnary) and the object in charge of managing the entire procedure. "id" are names specified in the description (t_desc, p_desc), regions are "binary images" (numpy array). The object embedded many intermediate informations (e.g. graphs, isomorphisms,...) """ #Create recognizer instance and trigger recognition recognizer = Recognizer(image, label, t_desc, p_desc, mc, roi, min_size, bg, bound_thickness, rag, merge, verbose) recognizer.process() #Retrieve regions and return them for external use not requiring 'recognizer' id2regions = {} if recognizer.relabelled_final_t_graph is not None: for n in recognizer.relabelled_final_t_graph.nodes(): id2regions[n] = recognizer.relabelled_final_t_graph.get_region(n) return id2regions,recognizer
class Recognizer: def __init__(self, image,label, t_desc, p_desc,mc=False,roi=None,size_min=None,bg=False,bound_thickness=0,prerag=None,premnoragmerging=None,verbose=False): """ :param image: input image (color or gray, nd) :param label: labelled image :param t_desc: string :param p_desc: string :param mc: True is color, False otherwise :param roi: region of interest :param bound_thickness: thickness of the boundary to be added (none if thickness is 0) """ #Model self.t_desc,self.p_desc=t_desc,p_desc self.ref_t_graph,self.ref_p_graph=from_string(self.t_desc),from_string(self.p_desc) #Context self.mc=mc self.raw_image=image self.image=image if mc : #input color image self.image=0.2125*image[:,:,0]+ 0.7154*image[:,:,1]+0.0721*image[:,:,2] self.label=label self.spatial_dim=len(self.label.shape) #Optional elements self.roi=roi self.bound_thickness=bound_thickness self.size_min=size_min self.remove_background=bg self.verbose=verbose self.pre_ragmerging=prerag self.pre_photmerging=premnoragmerging # Produced intermediate labelling (simplification of the initial labelled image) self.label_pre_rag= None self.label_pre_photomerge = None # Produced intermediate operations (strings), labelled image and graphs self.intermediate_operations=[] self.intermediate_labels=[] self.intermediate_graphs=[] self.operation_step=0 self.t_graph, self.p_graph = None, None # Graphs for computing isomorphisms and merging, can be after rag, filt, background removal #Common isomorphisms and best isomorphism self.common_isomorphisms=None self.matching, self.eies=None,None #Merging self.ordered_merges=None #Final result self.final_t_graph,self.final_p_graph=None,None self.relabelled_final_t_graph, self.relabelled_final_p_graph = None, None #Runtimes self.action2runtime={} def preliminary_processing(self): if self.pre_ragmerging is not None: if self.verbose: print("Preprocessing: RAG merging") #self.label_pre_rag=np.copy(self.label) self.label=rag_merge(self.raw_image, self.label, self.pre_ragmerging, self.mc, self.roi) self.intermediate_operations+=[str(self.operation_step)+"_After_preliminary_RAG_merging"] self.intermediate_labels+= [np.copy(self.label)] self.intermediate_graphs+=[None] self.operation_step+=1 if self.pre_photmerging is not None: if self.verbose: print("Preprocessing: Photometry merging") #self.label_pre_photomerge = np.copy(self.label) nb = len(grey_levels(self.label)) times = nb - self.pre_photmerging if self.mc is True: tmp_chsv=rgb2chsv(self.raw_image) self.label = merge_photometry_color(tmp_chsv, self.label, self.roi, times, self.verbose) else: self.label = merge_photometry_gray(self.raw_image, self.label, times) self.intermediate_operations += [str(self.operation_step) + "_After_preliminary_Photometry_merging"] self.intermediate_labels += [np.copy(self.label)] self.intermediate_graphs += [None] self.operation_step += 1 def process(self): #Initial status self.intermediate_operations+=[str(self.operation_step)+"_Initial"] self.intermediate_labels+= [np.copy(self.label)] self.intermediate_graphs+=[None] self.operation_step+=1 #Step -1: preliminary rag merging (cut threshold) self.preliminary_processing() #Step 0: built graphs from label t0 = time.clock() self.t_graph, self.p_graph=from_labelled_image(self.image,self.label,self.roi,self.bound_thickness,self.bound_thickness) t1 = time.clock() self.action2runtime["Build."]=t1-t0 self.intermediate_operations+=[str(self.operation_step)+"_Initial_graph"] self.intermediate_labels+= [np.copy(self.label)] self.intermediate_graphs+=[(copy.deepcopy(self.t_graph),copy.deepcopy(self.p_graph))] self.operation_step+=1 #Step 1: merge adjacent region until at least one common isomorphism is found self.rag_merging() #Step 2: size filtering by removing region smaller than self.size_min (in pixels) if self.size_min is not None: self.filtering() #Step 3: remove background if self.remove_background: self.extract_from_background() #Step 4: compute common iso and best iso self.compute_common_iso() #Step 5: merge region and relabelled graph nodes to identify each expected region self.compute_merge() def build_graphs(self): self.t_graph, self.p_graph=from_labelled_image(self.image,self.label,self.roi,self.bound_thickness,self.bound_thickness) def rag_merging(self): common_isomorphisms = common_subgraphisomorphisms([self.t_graph, self.p_graph], [self.ref_t_graph, self.ref_p_graph]) if len(common_isomorphisms) == 0: if self.verbose: print("Starting RAG merge until common iso is found...") #self.t_graph_before_rag, self.p_graph_before_rag=copy.deepcopy(self.t_graph),copy.deepcopy(self.p_graph) try: self.t_graph, self.p_graph = rag_merge_until_commonisomorphism(self.t_graph, self.p_graph, self.ref_t_graph,self.ref_p_graph,self.raw_image,self.roi,self.mc,self.verbose) self.intermediate_operations += [str(self.operation_step) + "_Initial_graph_after_RAG_for_common_isos"] self.label=self.t_graph.get_labelled() self.intermediate_labels += [np.copy(self.label)] self.intermediate_graphs += [(copy.deepcopy(self.t_graph), copy.deepcopy(self.p_graph))] self.operation_step += 1 if self.verbose: print("Ending RAG merge: common iso is found...") except: self.t_graph,self.p_graph=None,None raise RecognitionException(self,"Unefficient rag merging") def filtering(self): if self.verbose: print("Filtering by removing regions smaller than ", self.size_min, " pixels") #self.t_graph_before_filtering, self.p_graph_before_filtering = copy.deepcopy(self.t_graph), copy.deepcopy(self.p_graph) size_filtering(self.t_graph, self.p_graph, self.size_min,self.verbose) self.intermediate_operations += [str(self.operation_step) + "_Initial_graph_size_filtered"] self.label = self.t_graph.get_labelled() self.intermediate_labels += [np.copy(self.label)] self.intermediate_graphs += [(copy.deepcopy(self.t_graph), copy.deepcopy(self.p_graph))] self.operation_step += 1 def extract_from_background(self): if self.verbose: print("Removing background") self.t_graph_before_background, self.p_graph_before_background = copy.deepcopy(self.t_graph), copy.deepcopy(self.p_graph) roi, self.t_graph,self.p_graph = remove_background(self.image, self.t_graph, self.p_graph, self.ref_t_graph, self.ref_p_graph) self.intermediate_operations += [str(self.operation_step) + "_Initial_background_removed"] self.label = self.t_graph.get_labelled() self.intermediate_labels += [np.copy(self.label)] self.intermediate_graphs += [(copy.deepcopy(self.t_graph), copy.deepcopy(self.p_graph))] self.operation_step += 1 def compute_common_iso(self): if self.verbose: print("Searching for common isomorphisms") t0 = time.clock() self.common_isomorphisms = common_subgraphisomorphisms([self.t_graph, self.p_graph], [self.ref_t_graph, self.ref_p_graph]) t1 = time.clock() self.action2runtime["Iso."]=t1-t0 if self.verbose: print("Searching for the best common isomorphism") self.matching, self.eies,_ = best_common_subgraphisomorphism(self.common_isomorphisms, self.p_graph,self.ref_p_graph) def compute_merge(self): if self.verbose: print("Merging regions") t0 = time.clock() self.final_t_graph, self.final_p_graph, self.ordered_merges = propagate(self.t_graph,self.p_graph, self.ref_t_graph,self.ref_p_graph, self.matching,verbose=self.verbose) t1 = time.clock() self.action2runtime["Merge"] = t1 - t0 (self.relabelled_final_t_graph, self.relabelled_final_p_graph) = relabel_nodes([self.final_t_graph, self.final_p_graph], self.matching) self.relabelled_final_t_graph.set_image(self.t_graph.get_image()) self.relabelled_final_p_graph.set_image(self.t_graph.get_image())