1 #ifndef VIGRA_EXPORT_GRAPH_HIERARCHICAL_CLUSTERING_VISTITOR_HXX 2 #define VIGRA_EXPORT_GRAPH_HIERARCHICAL_CLUSTERING_VISTITOR_HXX 3 //#define NO_IMPORT_ARRAY 4 5 /*boost python before anything else*/ 6 #include <boost/python.hpp> 7 8 /*std*/ 9 #include <sstream> 10 #include <string> 11 12 /*vigra*/ 13 #include <vigra/numpy_array.hxx> 14 #include <vigra/numpy_array_converters.hxx> 15 #include <vigra/graphs.hxx> 16 #include <vigra/graph_maps.hxx> 17 #include <vigra/python_graph.hxx> 18 #include <vigra/graph_algorithms.hxx> 19 #include <vigra/metrics.hxx> 20 #include <vigra/multi_gridgraph.hxx> 21 #include <vigra/error.hxx> 22 #include <vigra/merge_graph_adaptor.hxx> 23 #include <vigra/hierarchical_clustering.hxx> 24 #include <vigra/timing.hxx> 25 namespace python = boost::python; 26 27 namespace vigra{ 28 29 30 31 template<class GRAPH> 32 class LemonGraphHierachicalClusteringVisitor 33 : public boost::python::def_visitor<LemonGraphHierachicalClusteringVisitor<GRAPH> > 34 { 35 public: 36 37 friend class def_visitor_access; 38 39 typedef GRAPH Graph; 40 typedef MergeGraphAdaptor<Graph> MergeGraph; 41 42 typedef LemonGraphHierachicalClusteringVisitor<GRAPH> VisitorType; 43 // Lemon Graph Typedefs 44 45 typedef typename Graph::index_type index_type; 46 typedef typename Graph::Edge Edge; 47 typedef typename Graph::Node Node; 48 typedef typename Graph::Arc Arc; 49 typedef typename Graph::NodeIt NodeIt; 50 typedef typename Graph::EdgeIt EdgeIt; 51 typedef typename Graph::ArcIt ArcIt; 52 53 54 typedef EdgeHolder<Graph> PyEdge; 55 typedef NodeHolder<Graph> PyNode; 56 typedef ArcHolder<Graph> PyArc; 57 58 59 // predefined array (for map usage) 60 const static unsigned int EdgeMapDim = IntrinsicGraphShape<Graph>::IntrinsicEdgeMapDimension; 61 const static unsigned int NodeMapDim = IntrinsicGraphShape<Graph>::IntrinsicNodeMapDimension; 62 63 typedef NumpyArray<EdgeMapDim, Singleband<float > > FloatEdgeArray; 64 typedef NumpyArray<NodeMapDim, Singleband<float > > FloatNodeArray; 65 typedef NumpyArray<NodeMapDim, Singleband<UInt32> > UInt32NodeArray; 66 typedef NumpyArray<NodeMapDim, Singleband<Int32 > > Int32NodeArray; 67 typedef NumpyArray<NodeMapDim +1,Multiband <float > > MultiFloatNodeArray; 68 69 typedef NumpyScalarEdgeMap<Graph,FloatEdgeArray> FloatEdgeArrayMap; 70 typedef NumpyScalarNodeMap<Graph,FloatNodeArray> FloatNodeArrayMap; 71 typedef NumpyScalarNodeMap<Graph,UInt32NodeArray> UInt32NodeArrayMap; 72 typedef NumpyScalarNodeMap<Graph,Int32NodeArray> Int32NodeArrayMap; 73 typedef NumpyMultibandNodeMap<Graph,MultiFloatNodeArray> MultiFloatNodeArrayMap; 74 75 76 typedef cluster_operators::EdgeWeightNodeFeatures< 77 MergeGraph, 78 FloatEdgeArrayMap, 79 FloatEdgeArrayMap, 80 MultiFloatNodeArrayMap, 81 FloatNodeArrayMap, 82 FloatEdgeArrayMap, 83 UInt32NodeArrayMap 84 > DefaultClusterOperator; 85 86 87 //typedef cluster_operators::EdgeWeightNodeFeatures2< 88 // MergeGraph, 89 // FloatEdgeArrayMap, 90 // FloatEdgeArrayMap, 91 // MultiFloatNodeArrayMap, 92 // FloatNodeArrayMap, 93 // FloatEdgeArrayMap 94 //> NeuroClusterOperator; 95 96 typedef cluster_operators::PythonOperator<MergeGraph> PythonClusterOperator; 97 98 99 100 LemonGraphHierachicalClusteringVisitor(const std::string clsName)101 LemonGraphHierachicalClusteringVisitor(const std::string clsName) 102 :clsName_(clsName){ 103 104 } 105 106 107 exportMergeGraph() const108 void exportMergeGraph()const{ 109 const std::string mgAdaptorClsName = clsName_ + std::string("MergeGraph"); 110 python::class_<MergeGraph,boost::noncopyable>( 111 mgAdaptorClsName.c_str(),python::init<const Graph &>()[python::with_custodian_and_ward<1 /*custodian == self*/, 2 /*ward == const InputLabelingView & */>()] 112 ) 113 .def(LemonUndirectedGraphCoreVisitor<MergeGraph>(mgAdaptorClsName)) 114 .def("inactiveEdgesNode",&pyInactiveEdgesNode) 115 .def("graph",&pyMergeGraphsGraph, python::return_internal_reference<>()) 116 .def("contractEdge",&pyContractEdgeA) 117 .def("contractEdge",&pyContractEdgeB) 118 .def("hasEdgeId",&pyHasEdgeId) 119 120 .def("graphLabels",registerConverters(&pyCurrentLabeling<MergeGraph>), 121 ( 122 python::arg("out")=python::object() 123 ) 124 ) 125 126 ; 127 128 python::def("__mergeGraph",&pyMergeGraphConstructor , 129 python::with_custodian_and_ward_postcall< 0,1 , 130 python::return_value_policy< python::manage_new_object > >() 131 ) 132 ; 133 } 134 setLiftedEdges(DefaultClusterOperator & op,NumpyArray<1,uint32_t> liftedEdgeIds)135 static void setLiftedEdges(DefaultClusterOperator & op, 136 NumpyArray<1, uint32_t> liftedEdgeIds 137 ){ 138 op.setLiftedEdges(liftedEdgeIds.begin(), liftedEdgeIds.end()); 139 } 140 exportHierarchicalClusteringOperators() const141 void exportHierarchicalClusteringOperators()const{ 142 { 143 const std::string operatorName = clsName_ + std::string("MergeGraph") + std::string("MinEdgeWeightNodeDistOperator"); 144 python::class_<DefaultClusterOperator >(operatorName.c_str(),python::no_init) 145 .def("__init__", python::make_constructor(&pyEdgeWeightNodeFeaturesConstructor)) 146 .def("setLiftedEdges",registerConverters(&setLiftedEdges)) 147 .def("enableStopWeight",&DefaultClusterOperator::enableStopWeight) 148 ; 149 python::def("__minEdgeWeightNodeDistOperator",registerConverters(&pyEdgeWeightNodeFeaturesConstructor), 150 python::with_custodian_and_ward_postcall< 0,1 , 151 python::with_custodian_and_ward_postcall< 0 ,2, 152 python::with_custodian_and_ward_postcall< 0 ,3, 153 python::with_custodian_and_ward_postcall< 0 ,4, 154 python::with_custodian_and_ward_postcall< 0 ,5, 155 python::with_custodian_and_ward_postcall< 0 ,6, 156 python::with_custodian_and_ward_postcall< 0 ,7, 157 python::return_value_policy< python::manage_new_object 158 > > > > > > > >() 159 ); 160 161 } 162 //{ 163 // const std::string operatorName = clsName_ + std::string("MergeGraph") + std::string("NeuroOperator"); 164 // python::class_<NeuroClusterOperator >(operatorName.c_str(),python::no_init) 165 // .def("__init__", python::make_constructor(&pyNeuroConstructor)) 166 // ; 167 // python::def("__neuroOperator",registerConverters(&pyNeuroConstructor), 168 // python::with_custodian_and_ward_postcall< 0,1 , 169 // python::with_custodian_and_ward_postcall< 0 ,2, 170 // python::with_custodian_and_ward_postcall< 0 ,3, 171 // python::with_custodian_and_ward_postcall< 0 ,4, 172 // python::with_custodian_and_ward_postcall< 0 ,5, 173 // python::with_custodian_and_ward_postcall< 0 ,6, 174 // python::return_value_policy< python::manage_new_object 175 // > > > > > > >() 176 // ); 177 //} 178 { 179 180 const std::string operatorName = clsName_ + std::string("MergeGraph") + std::string("PythonOperator"); 181 python::class_<PythonClusterOperator >(operatorName.c_str(),python::no_init) 182 .def("__init__", python::make_constructor(&pyPythonOperatorConstructor))//, 183 //python::return_value_policy<python::manage_new_object>() ) 184 ; 185 python::def("__pythonClusterOperator",registerConverters(&pyPythonOperatorConstructor), 186 python::with_custodian_and_ward_postcall< 0,1 , 187 python::with_custodian_and_ward_postcall< 0,2 , 188 python::return_value_policy< python::manage_new_object > > >() 189 ); 190 } 191 } 192 193 template<class CLUSTER_OPERATOR> exportHierarchicalClustering(const std::string & opClsName) const194 void exportHierarchicalClustering(const std::string & opClsName)const{ 195 typedef CLUSTER_OPERATOR ClusterOperator; 196 typedef HierarchicalClusteringImpl<ClusterOperator> HCluster; 197 198 const std::string clsName = std::string("HierarchicalClustering")+ opClsName; 199 python::class_<HCluster,boost::noncopyable>( 200 clsName.c_str(),python::init<ClusterOperator &>()[python::with_custodian_and_ward<1 /*custodian == self*/, 2 /*ward == const InputLabelingView & */>()] 201 ) 202 .def("cluster",&HCluster::cluster) 203 .def("reprNodeIds",registerConverters(&pyReprNodeIds<HCluster>)) 204 .def("ucmTransform",registerConverters(&pyUcmTransform<HCluster>)) 205 .def("resultLabels",registerConverters(&pyResultLabels<HCluster>), 206 ( 207 python::arg("out")=python::object() 208 ) 209 ) 210 ; 211 212 // free function 213 python::def("__hierarchicalClustering",registerConverters(&pyHierarchicalClusteringConstructor<ClusterOperator>), 214 python::with_custodian_and_ward_postcall< 0,1 , 215 python::return_value_policy< python::manage_new_object > >() 216 ); 217 } 218 pyMergeGraphsGraph(const MergeGraph & mg)219 static const Graph & pyMergeGraphsGraph(const MergeGraph & mg){ 220 return mg.graph(); 221 } 222 223 pyMergeGraphConstructor(const GRAPH & graph)224 static MergeGraph * pyMergeGraphConstructor(const GRAPH & graph){ 225 return new MergeGraphAdaptor<GRAPH>(graph); 226 } 227 228 229 template<class MG> pyCurrentLabeling(const MG & mergeGraph,UInt32NodeArray resultArray)230 static NumpyAnyArray pyCurrentLabeling( 231 const MG & mergeGraph, 232 UInt32NodeArray resultArray 233 ){ 234 resultArray.reshapeIfEmpty(IntrinsicGraphShape<Graph>::intrinsicNodeMapShape(mergeGraph.graph())); 235 236 UInt32NodeArrayMap resultArrayMap(mergeGraph.graph(),resultArray); 237 //std::cout<<"find result labels\n"; 238 239 //USETICTOC; 240 241 //TIC; 242 for(NodeIt iter(mergeGraph.graph());iter!=lemon::INVALID;++iter ){ 243 resultArrayMap[*iter]=mergeGraph.reprNodeId(mergeGraph.graph().id(*iter)); 244 } 245 //TOC; 246 return resultArray; 247 } 248 249 250 template <class classT> visit(classT &) const251 void visit(classT& /*c*/) const 252 { 253 // the merge graph itself and factory functions to get a merge graph 254 exportMergeGraph(); 255 256 // export the clustering operators 257 exportHierarchicalClusteringOperators(); 258 259 // export Hierarchical Clustering (for all cluster operators) 260 //{ 261 // const std::string operatorName = clsName_ + std::string("MergeGraph") + std::string("NeuroOperator"); 262 // exportHierarchicalClustering<NeuroClusterOperator>(operatorName); 263 //} 264 { 265 const std::string operatorName = clsName_ + std::string("MergeGraph") + std::string("MinEdgeWeightNodeDistOperator"); 266 exportHierarchicalClustering<DefaultClusterOperator>(operatorName); 267 } 268 { 269 const std::string operatorName = clsName_ + std::string("MergeGraph") + std::string("PythonOperator"); 270 exportHierarchicalClustering<PythonClusterOperator>(operatorName); 271 } 272 273 } 274 275 276 pyInactiveEdgesNode(const MergeGraph & mg,const EdgeHolder<MergeGraph> & edge)277 static NodeHolder<MergeGraph> pyInactiveEdgesNode( 278 const MergeGraph & mg, 279 const EdgeHolder<MergeGraph> & edge 280 ){ 281 return NodeHolder<MergeGraph>(mg,mg.inactiveEdgesNode(edge)); 282 } 283 pyReprEdgeID(const MergeGraph & mg,const EdgeHolder<MergeGraph> & edge)284 static EdgeHolder<MergeGraph> pyReprEdgeID( 285 const MergeGraph & mg, 286 const EdgeHolder<MergeGraph> & edge 287 ){ 288 return EdgeHolder<MergeGraph>(mg,mg.reprEdge(edge)); 289 } 290 291 pyContractEdgeA(MergeGraph & mg,const EdgeHolder<MergeGraph> & edge)292 static void pyContractEdgeA( 293 MergeGraph & mg, 294 const EdgeHolder<MergeGraph> & edge 295 ){ 296 mg.contractEdge(edge); 297 } 298 pyContractEdgeB(MergeGraph & mg,const EdgeHolder<Graph> & graphEdge)299 static void pyContractEdgeB( 300 MergeGraph & mg, 301 const EdgeHolder<Graph> & graphEdge 302 ){ 303 mg.contractEdge(mg.reprEdge(graphEdge)); 304 } 305 pyHasEdgeId(MergeGraph & mg,typename MergeGraph::index_type id)306 static bool pyHasEdgeId( 307 MergeGraph & mg, 308 typename MergeGraph::index_type id 309 ){ 310 return mg.hasEdgeId(id); 311 } 312 313 314 315 template<class CLUSTER_OP> pyHierarchicalClusteringConstructor(CLUSTER_OP & clusterOp,const size_t nodeNumStopCond,const bool buildMergeTreeEncoding)316 static HierarchicalClusteringImpl<CLUSTER_OP> * pyHierarchicalClusteringConstructor( 317 CLUSTER_OP & clusterOp, 318 const size_t nodeNumStopCond, 319 const bool buildMergeTreeEncoding 320 321 322 ){ 323 typename HierarchicalClusteringImpl<CLUSTER_OP>::Parameter param; 324 param.nodeNumStopCond_=nodeNumStopCond; 325 param.buildMergeTreeEncoding_=buildMergeTreeEncoding; 326 param.verbose_=true; 327 return new HierarchicalClusteringImpl<CLUSTER_OP>(clusterOp,param); 328 } 329 330 331 332 333 static DefaultClusterOperator * pyEdgeWeightNodeFeaturesConstructor(MergeGraph & mergeGraph,FloatEdgeArray edgeIndicatorMapArray,FloatEdgeArray edgeSizeMapArray,MultiFloatNodeArray nodeFeatureMapArray,FloatNodeArray nodeSizeMapArray,FloatEdgeArray edgeMinWeightMapArray,UInt32NodeArray nodeLabelArray,const float beta,const metrics::MetricType nodeDistType,const float wardness,const float gamma)334 pyEdgeWeightNodeFeaturesConstructor( 335 MergeGraph & mergeGraph, 336 FloatEdgeArray edgeIndicatorMapArray, 337 FloatEdgeArray edgeSizeMapArray, 338 MultiFloatNodeArray nodeFeatureMapArray, 339 FloatNodeArray nodeSizeMapArray, 340 FloatEdgeArray edgeMinWeightMapArray, 341 UInt32NodeArray nodeLabelArray, 342 const float beta, 343 const metrics::MetricType nodeDistType, 344 const float wardness, 345 const float gamma 346 ){ 347 348 FloatEdgeArrayMap edgeIndicatorMap(mergeGraph.graph(),edgeIndicatorMapArray); 349 FloatEdgeArrayMap edgeSizeMap(mergeGraph.graph(),edgeSizeMapArray); 350 MultiFloatNodeArrayMap nodeFeatureMap(mergeGraph.graph(),nodeFeatureMapArray); 351 FloatNodeArrayMap nodeSizeMap(mergeGraph.graph(),nodeSizeMapArray); 352 FloatEdgeArrayMap edgeMinWeightMap(mergeGraph.graph(),edgeMinWeightMapArray); 353 UInt32NodeArrayMap nodeLabelMap(mergeGraph.graph(),nodeLabelArray); 354 355 356 return new DefaultClusterOperator(mergeGraph, 357 edgeIndicatorMap,edgeSizeMap, 358 nodeFeatureMap, nodeSizeMap, 359 edgeMinWeightMap,nodeLabelMap, 360 beta,nodeDistType,wardness, gamma 361 ); 362 } 363 364 365 366 367 368 369 static PythonClusterOperator * pyPythonOperatorConstructor(MergeGraph & mergeGraph,python::object object,const bool useMergeNodeCallback,const bool useMergeEdgesCallback,const bool useEraseEdgeCallback)370 pyPythonOperatorConstructor( 371 MergeGraph & mergeGraph, 372 python::object object, 373 const bool useMergeNodeCallback, 374 const bool useMergeEdgesCallback, 375 const bool useEraseEdgeCallback 376 ){ 377 return new PythonClusterOperator(mergeGraph,object,useMergeNodeCallback,useMergeEdgesCallback,useEraseEdgeCallback); 378 } 379 380 381 382 template<class HCLUSTER> pyReprNodeIds(const HCLUSTER & hcluster,NumpyArray<1,UInt32> labels)383 static void pyReprNodeIds( 384 const HCLUSTER & hcluster, 385 NumpyArray<1,UInt32> labels 386 ){ 387 for(MultiArrayIndex i=0; i<labels.shape(0); ++i) 388 labels(i)=hcluster.reprNodeId(labels(i)); 389 } 390 391 392 template<class HCLUSTER> pyResultLabels(const HCLUSTER & hcluster,UInt32NodeArray resultArray)393 static NumpyAnyArray pyResultLabels( 394 const HCLUSTER & hcluster, 395 UInt32NodeArray resultArray 396 ){ 397 resultArray.reshapeIfEmpty(IntrinsicGraphShape<Graph>::intrinsicNodeMapShape(hcluster.graph())); 398 399 UInt32NodeArrayMap resultArrayMap(hcluster.graph(),resultArray); 400 //std::cout<<"find result labels\n"; 401 402 //USETICTOC; 403 404 //TIC; 405 for(NodeIt iter(hcluster.graph());iter!=lemon::INVALID;++iter ){ 406 resultArrayMap[*iter]=hcluster.mergeGraph().reprNodeId(hcluster.graph().id(*iter)); 407 } 408 //TOC; 409 return resultArray; 410 } 411 412 template<class HCLUSTER> pyUcmTransform(const HCLUSTER & hcluster,FloatEdgeArray inputArray)413 static void pyUcmTransform( 414 const HCLUSTER & hcluster, 415 FloatEdgeArray inputArray 416 ){ 417 FloatEdgeArrayMap inputArrayMap(hcluster.graph(),inputArray); 418 hcluster.ucmTransform(inputArrayMap); 419 } 420 421 422 template<class HCLUSTER> mergeTreeEncodingAsNumpyArray(const HCLUSTER & hcluster)423 static python::tuple mergeTreeEncodingAsNumpyArray(const HCLUSTER & hcluster) { 424 typedef typename HCLUSTER::MergeTreeEncoding MergeTreeEncoding; 425 typedef typename HCLUSTER::MergeGraphIndexType MergeGraphIndexType; 426 typedef typename HCLUSTER::ValueType ValueType; 427 const MergeTreeEncoding & encoding = hcluster.mergeTreeEndcoding(); 428 const MergeGraphIndexType numMerges = encoding.size(); 429 //CPP BUG?!? 430 NumpyArray<1,ValueType> w = NumpyArray<1,ValueType>(typename NumpyArray<1,ValueType>::difference_type(numMerges)); 431 NumpyArray<2,MergeGraphIndexType> indices = NumpyArray<2,MergeGraphIndexType>(typename NumpyArray<2,MergeGraphIndexType>::difference_type(numMerges,3)); 432 for(MergeGraphIndexType m=0;m<numMerges;++m){ 433 w(int(m))=encoding[m].w_; 434 indices(m,0)=encoding[m].a_; 435 indices(m,1)=encoding[m].b_; 436 indices(m,2)=encoding[m].r_; 437 } 438 return python::make_tuple(indices,w); 439 } 440 441 442 template<class HCLUSTER> leafNodeIdsAsNumpyArray(const HCLUSTER & hcluster,const typename HCLUSTER::MergeGraphIndexType treeNodeId,NumpyArray<1,UInt32> leafes=(NumpyArray<1,UInt32> ()))443 static python::tuple leafNodeIdsAsNumpyArray( 444 const HCLUSTER & hcluster, 445 const typename HCLUSTER::MergeGraphIndexType treeNodeId, 446 NumpyArray<1,UInt32> leafes = (NumpyArray<1,UInt32>()) 447 ) { 448 leafes.reshapeIfEmpty( typename NumpyArray<1,UInt32>::difference_type( hcluster.graph().nodeNum()) ); 449 if(leafes.shape(0)!=hcluster.graph().nodeNum()){ 450 throw std::runtime_error("out.shape(0) must be equal nodeNum"); 451 } 452 453 // todo make leafes size check 454 const size_t leafNum=hcluster.leafNodeIds(treeNodeId,leafes.begin()); 455 return python::make_tuple(leafes,leafNum); 456 } 457 458 /* 459 460 template<class T> 461 static NumpyAnyArray pyRagProjectNodeFeaturesToBaseGraph( 462 const RagGraph & rag, 463 const Graph & graph, 464 const typename PyNodeMapTraits<Graph, UInt32>::Array & labelsWhichGeneratedRagArray, 465 const typename PyNodeMapTraits<RagGraph,T >::Array & ragNodeFeaturesArray, 466 const Int32 ignoreLabel=-1, 467 typename PyNodeMapTraits<Graph,T>::Array graphNodeFeaturesArray=typename PyNodeMapTraits<Graph,T>::Array() // out 468 ){ 469 // reshape out ( last argument (out) will be reshaped if empty, and #channels is taken from second argument) 470 reshapeNodeMapIfEmpty(graph,ragNodeFeaturesArray,graphNodeFeaturesArray); 471 // numpy arrays => lemon maps 472 typename PyNodeMapTraits<Graph, UInt32>::Map labelsWhichGeneratedRagArrayMap(graph, labelsWhichGeneratedRagArray); 473 typename PyNodeMapTraits<RagGraph,T >::Map ragNodeFeaturesArrayMap(rag,ragNodeFeaturesArray); 474 typename PyNodeMapTraits<Graph, T >::Map graphNodeFeaturesArrayMap(graph,graphNodeFeaturesArray); 475 // run algorithm 476 for(typename Graph::NodeIt iter(graph);iter!=lemon::INVALID;++iter){ 477 if(ignoreLabel==-1 || static_cast<Int32>(labelsWhichGeneratedRagArrayMap[*iter])!=ignoreLabel) 478 graphNodeFeaturesArrayMap[*iter]=ragNodeFeaturesArrayMap[rag.nodeFromId(labelsWhichGeneratedRagArrayMap[*iter])]; 479 else{ 480 // ??? 481 // aks U. Koethe here 482 } 483 } 484 return graphNodeFeaturesArray; // out 485 } 486 */ 487 private: 488 std::string clsName_; 489 }; 490 491 492 493 494 } // end namespace vigra 495 496 #endif // VIGRA_EXPORT_GRAPH_HIERARCHICAL_CLUSTERING_VISTITOR_HXX 497