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