Utilisation des Graphes dans Morph-M Jean Stawiaski Centre de Morphologie Mathématique 2 décembre 2008 1 Sommaire Introduction Morph-M (CommonGraph32) et (BoostGraph) 1) Passage Graphe-Image et Image-Graphe 1.1 Graphe d’une image 1.2 Graphe d’une partition 2) Utilisation des Graphes (CommonGraph32) en Python 2.1 Fonctions de base 2.2 Exemples, opérations de base de morphologie mathématique 3) Utilisation des Graphes en C++ 3.1 Création d’un graphe en C++ 3.2 Fonction de base 3.3 Algorithmes 3.4 Exemples 4) Pour aller un peu plus loin Conclusion 2 Introduction : Vocabulaire Un graphe G est une paire G = (V,E) où V est un ensemble finis appelé nœuds du graphe et E est une ensemble finis appelé les arêtes du graphe. i ei,j j Il est possible de différencier les arêtes ei,j et ej,i, on parle alors de graphes orientés. Il est possible d’associer à un graphe G, un ensemble de valeurs associés aux nœuds ou un ensemble associé aux arêtes. On parle de graphes aux nœuds valués ou bien de graphes aux arêtes valuées. 3 Introduction : BoostGraph Les graphes de la librairie Boost, dénommés BoostGraph, est une structure générique pouvant représenté à peu près n’importe quel type de graphes. La structure BoostGraph représente des graphes orientés ou non, pouvant être valués aux nœuds et aux arrêtes. La valuation des arrêtes et des nœuds sont aussi représentés par des structures abstraites. BoostGraph Library met à disposition un grand nombre d’algorithmes (Dijkstra, Kruskal, Prim, Ford-Fulkerson, etc.) 4 Introduction : CommonGraph32 Les graphes sous Morph-M sont utilisés à travers la structure appelé CommonGraph32. La structure CommonGraph32 représente un graphe non-orienté, valué aux nœuds et aux arrêtes. La valuation des arrêtes et des nœuds sont des entiers codés sur 32 bits La structure CommonGraph32 est définie à partir de la librairie Boost. La structure CommonGraph32 est accessible en C++ et en Python. La structure CommonGraph32 est utilisée dans Morph-M pour représenter des images et des partitions. 5 1) Passage Graphe-Image et Image-Graphe 6 1) Passage Graphe-Image et Image-Graphe 1.1 Graphe d’une image Passage image graphe. Représentation d’une image par un graphe (nœuds = pixels, arrêtes = relations de voisinage). MorphoGraph.GetPixelGraph(im,nl,G) nl = CrossSE Passage graphe nl = SquareSE image. Projette les valeurs des nœuds sur les pixels de l’image. MorphoGraph.ProjectGraphOnPixel(G,imOut) Projection d’une image sur le graphe. Remplace la valeurs des nœuds du graphe par les valeurs des pixels. MorphoGraph.ProjectMarkersOnPixelGraph(ImIn, Gout) 7 1) Passage Graphe-Image et Image-Graphe 1.2 Graphe d’une partition Passage image graphe. Représentation d’une mosaïque par un graphe (nœuds = régions, arrêtes = relations de voisinage). G = Morphee.NeighborhoodGraphFromMosaic(imMosaic, nl) G = Morphee.NeighborhoodGraphFromMosaicWithPass(imMosaic, imGrad, nl) Passage graphe image. Projette les valeurs des nœuds sur les régions de la mosaïque. Morphee.ProjectGraphOnSegmentation(G, imMosaic,imOut) Projection d’une image sur le graphe. Remplace la valeurs des nœuds du graphe par les valeurs des régions. Morphee.ProjectMarkersOnGraph(Immarkers,imMosaic,Gout) 8 2) Utilisation des Graphes (CommonGraph32) en Python 9 2) Utilisation des (CommonGraph32) en Python 2.1 Fonctions de base La plupart des fonctions associées aux graphes sont accessibles à partir de Morph-M et d’un module spécifique Morpho-Graph : - Morph-M contient les outils associés à la segmentation (hiérarchie, mst, graphes de régions, etc.), - Morpho-Graph contient des fonctions de base de morphologie mathématique sur les graphes (Erosion, Dilatation, etc.) , ainsi que des fonctions de base pour graphes (labellisation, chemins les plus courts, coupe minimale, flots, etc.). 10 2) Utilisation des (CommonGraph32) en Python 2.1 Fonctions de base Création d’un graphe : G=morphee.CommonGraph32(numVertices) Les nœuds sont accéder par leurs descripteurs v, offset pour les graphes de pixels, labels de la régions -1 pour les graphes de régions) Récupérer le nombre de nœuds G.numVertices() Récupérer la valeur d’un nœud G.getVertexData(v) Imposer la valeur d’un nœud G.setVertexData(v,val) Obtenir les voisins d’un nœud G.getChildren(v1) Récupérer le nombre d’arrêtes G.numEdges() Récupérer la valeur d’une arrête G.getEdgeWeight(v1,v2) Imposer la valeur d’une arrête G.setEdgeWeight(v1,v2,val) Récupérer les arrêtes d’un graphe et leurs valeurs edgeList = G.getEdgesListAndWeights() 11 2) Utilisation des (CommonGraph32) en Python 2.2 Exemples for v1 in range( G.numVertices() ): G.setVertexData( v1, 0 ) edgeList = G.getEdgesListAndWeights() for v1,v2,w in edgeList: G.setEdgeWeight(v1,v2,0) for v1 in range( G.numVertices() ): for v2 in G.getChildren(v1): newVal = max (G.getVertexData(v2), newVal ) G2.setVertexData( v1, newVal ) 12 2) Utilisation des (CommonGraph32) en Python 2.2 Exemples edgeList = G.getEdgesListAndWeights() for s,t,w in edgeList: newW = max ( G.getVertexData(s) , G.getVertexData(t) ) G.setEdgeWeight(s,t,newW) for v1 in range( G.numVertices() ): for v2 in G.getChildren(v1): newVal = max ( G.getEdgeWeight( v1, v2 ), newVal ) G.setVertexData( v1, newVal ) 13 2) Utilisation des (CommonGraph32) en Python 2.2 Exemples : Opérations de base de morphologie mathématique ErosionNodesToEdges(graphIn,graphOut) DilationNodesToEdges(graphIn,graphOut) ErosionEdgesToNodes(graphIn,graphOut) DilationEdgesToNodes(graphIn,graphOut) ErosionNodesToNodes(graphIn,graphOut) DilationNodesToNodes(graphIn,graphOut) DilationEdgesToEdges(graphIn,graphOut) ErosionEdgesToEdges(graphIn,graphOut) 14 2) Utilisation des (CommonGraph32) en Python 2.2 Exemples def GetGraphFromFlatZones( im, nl ): imLab=morphee.getSameOf(im, morphee.dataCategory.dtScalar, morphee.scalarDataType.sdtUINT32) morphee.ImLabelFlatZones( im, nl, imLab ) G = morphee.NeighborhoodGraphFromMosaic_WithAverageAndDifference(imLab,im, nl ) return G,imLab def GetGraphFromWatershed( imIn, nl): imGrad = morphee.getSame( imIn ) morphee.ImMorphoGradient(imIn,nl,imGrad) imMinima32=morphee.getSameOf(imGrad, morphee.dataCategory.dtScalar, morphee.scalarDataType.sdtUINT32) morphee.ImMinima(imGrad,nl,imMinima32) imLabel32=morphee.getSame(imMinima32) morphee.ImLabel(imMinima32,nl,imLabel32) imWS32=morphee.getSame(imMinima32) morphee.ImBasinsConstrainedNoWSLine_v2(imGrad,imLabel32,nl,imWS32) G = morphee.NeighborhoodGraphFromMosaic_WithPass(imWS32,imGrad, nl ) return G, imWS32 15 2) Utilisation des (CommonGraph32) en Python 2.2 Exemples def GetGraphFromHierarchicalSegmentation( im, nl, numRegions, wsType): imGrad = morphee.getSame(im) morphee.ImMorphoGradient( im, nl, imGrad ) imMinima32=morphee.getSameOf(imGrad, morphee.dataCategory.dtScalar, morphee.scalarDataType.sdtUINT32) morphee.ImMinima(imGrad,nl,imMinima32) imLabel32=morphee.getSame(imMinima32) morphee.ImLabel(imMinima32,nl,imLabel32) imWS32=morphee.getSame(imMinima32) hierGraph=morphee.ImHierarchicalSegmentation32(imGrad,imLabel32,nl,wsType,imWS32) Forest=morphee.ImDisplayHierarchyLevel32(imWS32,hierGraph,numRegions,imOut32) return Forest, imOut32 16 3) Utilisation des Graphes en C++ 17 3) Utilisation des Graphes en C++ 3.1 Création d’un graphe en C++ avec Boost : Introduction Un graphe est représenté par une liste d’adjacence : Le type des entrées de la liste d’adjacence est spécifié par des paramètres de la classe graphe. 18 3) Utilisation des Graphes en C++ 3.1 Création d’un graphe en C++ avec Boost VertexList et OutEdgeList défini le type d’objet utilisé pour décrire le graphe (std::list, set, vect). Ce choix a un impact sur la complexité des algorithmes. Les propriétés du graphe comme les couleurs, les distances, les poids des arrêtes, sont définis par l’utilisateur dans les propriétés VertexProperties, EdgeProperties, GraphProperties. Les propriétés du graphe sont définies en C++ par des std::maps. 19 3) Utilisation des Graphes en C++ 3.1 Création d’un graphe en C++ avec Boost Exemple : Graphe non orienté, valué aux arrêtes, et valué aux nœuds par un double. typedef boost::adjacency_list < boost::vecS, boost::vecS, boost::undirectedS, boost::property < boost::vertex_distance_t, double >, boost::property < boost::edge_capacity_t, double > > Graph_d; 20 3) Utilisation des Graphes en C++ 3.2 Fonction de base : Méthodes de CommonGraph32 et Boost graph_traits<adjacency_list>::vertex_descriptor graph_traits<adjacency_list>::edge_descriptor graph_traits<adjacency_list>::vertex_iterator graph_traits<adjacency_list>::edge_iterator boost::tie(v_iter, v_end)= boost::vertices(G) boost::tie(e_iter, e_end)=boost::edges(G) graph_traits<adjacency_list>::out_edge_iterator boost::tie(e_iter, e_end)=boost::out_edges(v,G) property_map<adjacency_list, PropertyTag>::type get(PropertyTag, adjacency_list& g) 21 3) Utilisation des Graphes en C++ 3.2 Fonction de base : Méthodes de CommonGraph32 et Boost G.edgeFromVertices(v1,v2,&e) v1 = G.edgeSource(e) v2 = G.edgeTarget(e) G.vertexData(v, &vdata) G.edgeWeight(e,&edgeweight) G.setVertexData(v,val) G.setEdgeWeight(e, val ) G.removeEdge(v1,v2) G.addEdge(v1,v2) G.addVertex() G.removeVertex(v1) ( e, in ) = boost::edge(v1,v2,g) boost::source(e,g) Boost::target(e,g) property_map<Graph, edge_capacity_t>::type weight = boost::get(edge_capacity, g); boost::remove_edge( param , g) boost::add_edge(v1,v2,g) boost::add_vertex(g) boost::remove_vertex(v,g) g = G.getBoostGraph() 22 3) Utilisation des Graphes en C++ 3.3 Exemples for ( boost::tie(ed_it, ed_end) = boost::edges(GIn.getBoostGraph()) ;ed_it != ed_end ; ++ed_it , ++edout) { GIn.edgeWeight(*ed_it,&tmp); if( tmp > seuil){ Gout.removeEdge( typename Graph::VertexDescriptor(GIn.edgeSource(*ed_it)), typename Graph::VertexDescriptor(GIn.edgeTarget(*ed_it)) ); } } morphee::Morpho_Graph::ErosionEdgesToEdges(GIn, GEro); morphee::Morpho_Graph::DilationEdgesToEdges(GIn,GDil); for (boost::tie(ed_it, ed_end)=boost::edges(GIn.getBoostGraph()); ed_it != ed_end ; ++ed_it ) { GIn.edgeWeight(*ed_it,&tmp); GEro.edgeWeight(*edero_it,&tmp1); GDil.edgeWeight(*eddil_it,&tmp2); if(std::abs( (double) tmp - (double) tmp1) <= std::abs( (double) tmp2 - (double) tmp)) Gout.setEdgeWeight( *edout, tmp1 ); else Gout.setEdgeWeight( *edout, tmp2 ); } 23 3) Utilisation des Graphes en C++ 3.4 Algorithmes (liste non exhaustive) Breadth First Search Depth First Search Uniform Cost Search Dijkstra's Shortest Paths Bellman-Ford Shortest Paths Johnson's All-Pairs Shortest Paths Kruskal's Minimum Spanning Tree Prim's Minimum Spanning Tree Edmund Karp Maximal flow Tarjan Push relabel max flow Kolmogorov max flow Edmund max cardinality matching Connected Components Strongly Connected Components Sequential Vertex Coloring 24 3) Utilisation des Graphes en C++ 3.4 Algorithmes Composantes connexes std::vector<int> component(boost::num_vertices(GIn.getBoostGraph())); int num = connected_components(GIn.getBoostGraph(), &component[0]); for (boost::tie(u_iter, u_end)=boost::vertices(GIn.getBoostGraph()) ; u_iter != u_end; ++u_iter){ Gout.setVertexData(*u_iter,component[*u_iter]); } Arbre de recouvrement 1 std::vector< typename BoostGraph::EdgeDescriptor > v_spanning_tree; boost::kruskal_minimum_spanning_tree( (graphIn).getBoostGraph(), std::back_inserter(v_spanning_tree) ) ; Arbre de recouvrement 2 std::vector< typename BoostGraph::VertexDescriptor > p(boost::num_vertices((graphIn).getBoostGraph()); boost::prim_minimum_spanning_tree( (graphIn).getBoostGraph(), &p[0] ) ; 25 3) Utilisation des Graphes en C++ 3.4 Algorithmes Chemins les plus courts boost::property_map< typename BoostGraph, boost::edge_capacity_t>::type weightmap = boost::get(boost::edge_capacity, g ); std::vector < typename BoostGraph::vertex_descriptor > p(boost::num_vertices(g)); boost::property_map < typename BoostGraph, boost::vertex_distance_t>::type distancemap = boost::get(boost::vertex_distance, g); boost::property_map< typename BoostGraph, boost::vertex_index_t>::type indexmap2 = boost::get(boost::vertex_index, g); dijkstra_shortest_paths(g, vRoot, &p[0], distancemap, weightmap, indexmap2, std::less<double>(), boost::closed_plus<double>(), (std::numeric_limits<double>::max)(), 0, boost::default_dijkstra_visitor()); L’Algorithme de Dijkstra permet de calculer les chemins les plus courts dans l’algèbre de chemins ( type distancemap, comparaison, combinaison, 0, infini) 26 3) Utilisation des Graphes en C++ 3.4 Algorithmes Flot maximal et coupe minimale boost::property_map<Graph_d, boost::edge_capacity_t>::type capacity = boost::get(boost::edge_capacity, g); boost::property_map<Graph_d, boost::edge_reverse_t>::type rev = get(boost::edge_reverse, g); boost::property_map<Graph_d, boost::edge_residual_capacity_t>::type residual_capacity = get(boost::edge_residual_capacity, g) boost::property_map<Graph_d, boost::vertex_index_t>::type indexmap = boost::get(boost::vertex_index, g); std::vector<boost::default_color_type> color(boost::num_vertices(g)); flow = kolmogorov_max_flow(g, capacity, residual_capacity, rev, &color[0], indexmap, vSource, vSink); Les algorithmes de flots et coupes ne peuvent pas être directement utilisés sur des CommonGraph32…. 27 4) Pour aller un peu plus loin… 28 4) Pour aller un peu plus loin… 1) Tirer Avantage de la généricité des algorithmes : Quelle est la différence entre : dijkstra_shortest_paths(g, vRoot, &p[0], distancemap, weightmap, indexmap2, std::less<double>(), boost::closed_plus<double>(), (std::numeric_limits<double>::max)(), 0, boost::default_dijkstra_visitor()); dijkstra_shortest_paths(g, vRoot, &p[0], distancemap, weightmap, indexmap2, std::less<double>(), std::max<double>(), (std::numeric_limits<double>::max)(), 0, boost::default_dijkstra_visitor()); Le seul algorithme de Dijstra permet de calculer les arbres de recouvrement min et max, les chemins les plus courts, les chemins les plus probables, etc. 29 4) Pour aller un peu plus loin… 2) Tirer Avantage de la généricité des structures : typedef std::vector<double> vdouble; typedef boost::adjacency_list < boost::vecS, boost::vecS, boost::undirectedS, boost::property < boost::vertex_distance_t, vdouble >, boost::property < boost::edge_capacity_t, vdouble > > Graph_v; Graph_v est un type de graphe dont les arrêtes sont valués par des vecteurs, et les nœuds valués par des vecteurs. De plus, la taille de ces vecteurs n’est pas défini, et peut donc être variable selon les arrêtes ou selon les nœuds. Graph_v peut être utilisé pour représenter des images multi-valués ou le nombre de canaux varient selon les pixels, ou les disimilarités entre pixels sont représentés par des vecteurs de taille variable. 30 4) Pour aller un peu plus loin… 2) Tirer Avantage de la généricité des structures : typedef std::vector<double> vdouble; typedef boost::adjacency_list < boost::vecS, boost::vecS, boost::undirectedS, boost::property < boost::vertex_distance_t, vdouble >, boost::property < boost::edge_capacity_t, vdouble > > Graph_v; dijkstra_shortest_paths(g, vRoot, &p[0], distancemap, weightmap, indexmap2, boost::detail::lexico_compare< vdouble > () , boost::detail::lexico_addition< vdouble > (), infinite, zero, boost::default_dijkstra_visitor()); Comparaison lexico de vecteurs Concaténation de vecteurs Transformée en distance lexicographique, la distance est donnée par un vecteur de taille variable (la valeur des arrêtes en ordre décroissant le long d’un chemin) 31 4) Pour aller un peu plus loin… 2) Tirer Avantage de la généricité des structures : Dijkstra a été utilisé pour : - distance classique - mst - distance multi-critères lexicographique - distance lexicographique sur image à niveaux de gris - mst sur image couleur (max de deux vecteurs ordonnés) - distance lexicographique sur image couleur - chemins les plus probables - etc. 32 Conclusion 33 Conclusion Le développement actuel du module graphe de Morph-M donne accès à une large gamme d’algorithmes utilisable en C++ et en Python. La librairie Boost évolué régulièrement, nous utilisons actuellement seulement une petite partie des algorithmes codés dans Boost\Graph. Le module Morpho-Graph est en cours de migration (développement de tests unitaires, etc.) La structure de graphe est actuellement la structure la plus générique utilisable pour le traitement d’images (graphes valués par des vecteurs, opérations de voisinage, algorithmes d’optimisation combinatoire, etc.) Développement actuel : Boite à outils pour les arbres (suite du module Morphee\Commontree) LPE stochastique Filtrage topologique des graphes Filtrage par critères Filtre Morphologique etc. 34 Merci pour votre attention Questions ? 35