// ======================================== CGraph Building ======================================== void testGraphBuilding(CGraph& graph, byte nStates) { ASSERT_EQ(nStates, graph.getNumStates()); int nNodes = random::u<int>(100, 100000); Mat pots1 = random::U(Size(nStates, nNodes), CV_32FC1, 0.0, 100.0); ASSERT_EQ(0, graph.addNode()); ASSERT_EQ(1, graph.addNode(pots1.row(1).t())); graph.addNodes(pots1); ASSERT_EQ(nNodes + 2, graph.getNumNodes()); graph.setNode(0, pots1.row(0).t()); Mat pot0, pot1, pot2; graph.getNode(0, pot0); graph.getNode(1, pot1); graph.getNodes(0, 10, pot2); float *pPot0 = pots1.ptr<float>(0); float *pPot1 = pots1.ptr<float>(1); for (byte s = 0; s < nStates; s++) { ASSERT_EQ(pot0.at<float>(s, 0), pPot0[s]); ASSERT_EQ(pot1.at<float>(s, 0), pPot1[s]); ASSERT_EQ(pot2.at<float>(0, s), pPot0[s]); ASSERT_EQ(pot2.at<float>(1, s), pPot1[s]); } Mat pots2 = random::U(Size(nStates, static_cast<int>(graph.getNumNodes()) - 10), CV_32FC1, 0.0, 100.0); graph.setNodes(2, pots2); Mat pot; for (size_t n = 2; n < graph.getNumNodes(); n++) { graph.getNode(n, pot); float *pPot = (static_cast<int>(n) - 2 < pots2.rows) ? pots2.ptr<float>(static_cast<int>(n) - 2) : pots1.ptr<float>(static_cast<int>(n) - 2); for (byte s = 0; s < nStates; s++) ASSERT_EQ(pot.at<float>(s, 0), pPot[s]); } graph.reset(); ASSERT_EQ(graph.getNumEdges(), 0); }
void fillGraph( CGraph &graph, const Eigen::MatrixXd &graph_nodeFeatures, CNodeTypePtr nodeType, const Eigen::MatrixXi &graph_adj, CEdgeTypePtr edgeType, vector<Eigen::MatrixXi> &g1_relations, const Eigen::VectorXi &g1_groundTruth, std::map<size_t,size_t> &groundTruth // returned GT taking into account the node IDs ) { // 1. Add nodes to the graph // 2. Add edges size_t n_nodes = graph_nodeFeatures.rows(); size_t n_features = graph_nodeFeatures.cols(); vector<CNodePtr> nodes; // // ADD NODES // for ( size_t i = 0; i < n_nodes; i++ ) { Eigen::VectorXd node_feat(n_features); node_feat = graph_nodeFeatures.row(i); Eigen::VectorXd multipliers(5); multipliers << 90, 100, 50, 40, 50; //multipliers << 50, 60, 50, 50, 50; //node_feat = node_feat.cwiseProduct( multipliers ); CNodePtr nodePtr ( new CNode( nodeType, node_feat ) ); nodes.push_back( nodePtr ); graph.addNode( nodePtr ); groundTruth[ nodePtr->getID() ] = g1_groundTruth(i); } // // ADD EDGES // for ( size_t row = 0; row < n_nodes; row++ ) { for ( size_t col = row; col < n_nodes; col++ ) { if ( graph_adj(row,col) == 1 ) { // Retrieve the nodes linked by the edge and their features CNodePtr node1 = nodes.at(row); CNodePtr node2 = nodes.at(col); Eigen::VectorXd &feat1 = node1->getFeatures(); Eigen::VectorXd &feat2 = node2->getFeatures(); // Compute edge features Eigen::VectorXd edgeFeatures( edgeType->getWeights().size()); // Perpendicularity edgeFeatures(0) = (std::abs(feat1(0) - feat2(0))==0) ? 0 : 1; // Height distance between centers edgeFeatures(1) = std::abs((float)feat1(1) - (float)feat2(1)); // Ratio between areas float a1 = feat1(2); float a2 = feat2(2); if ( a1 < a2 ) { float aux = a2; a2 = a1; a1 = aux; } //edgeFeatures(2) = a1 / a2; // Difference between elongations //edgeFeatures(3) = std::abs(feat1(3) - feat2(3)); // Use the isOn semantic relation //edgeFeatures(4) = (g1_relations[0])(row,col); edgeFeatures(2) = (g1_relations[0])(row,col); // Coplanar semantic relation //edgeFeatures(5) = (g1_relations[1])(row,col); // Bias feature //edgeFeatures(6) = 1; edgeFeatures(3) = 1; //Eigen::VectorXd multipliers(7); //multipliers << 0, 0, 0, 0, 0, 0, 1; //multipliers << 0, 0, 0, 0, 0, 0, 0; Eigen::VectorXd multipliers(4); multipliers << 1, 1, 10, 1; //edgeFeatures = edgeFeatures.cwiseProduct( multipliers ); CEdgePtr edgePtr ( new CEdge( node1, node2, edgeType, edgeFeatures ) ); graph.addEdge( edgePtr ); } } } }
int main (int argc, char* argv[]) { cout << endl; cout << " " << "DIFFERENT NODE TYPES EXAMPLE"; cout << endl << endl; /*------------------------------------------------------------------------------ * * PREPARE TRAINING DATA * *----------------------------------------------------------------------------*/ // // 1. Generate the node and edge types // size_t N_classes_type_1 = 2; size_t N_nodeFeatures_type_1 = 3; size_t N_edgeFeatures_type_1 = 3; size_t N_classes_type_2 = 3; size_t N_nodeFeatures_type_2 = 2; size_t N_edgeFeatures_type_2 = 2; // PERSONS CNodeTypePtr simpleNodeType1Ptr( new CNodeType(N_classes_type_1, N_nodeFeatures_type_1, string("Persons") ) ); CEdgeTypePtr simpleEdgeType1Ptr ( new CEdgeType(N_edgeFeatures_type_1, simpleNodeType1Ptr, simpleNodeType1Ptr, string("Edges between two persons")) ); // ROOMS CNodeTypePtr simpleNodeType2Ptr( new CNodeType(N_classes_type_2, N_nodeFeatures_type_2, string("Rooms") ) ); CEdgeTypePtr simpleEdgeType2Ptr ( new CEdgeType(N_edgeFeatures_type_2, simpleNodeType1Ptr, simpleNodeType2Ptr, "Edges between a person and a room") ); // Add the node and edge types to the trainingDataset CTrainingDataSet trainingDataset; // Since the edge between a room and a person is asymetric and probably with // a different number of classes, we need to especify that. Eigen::VectorXi typeOfEdgeFeatures( N_edgeFeatures_type_2 ); typeOfEdgeFeatures << 1,1; trainingDataset.addNodeType( simpleNodeType1Ptr ); trainingDataset.addEdgeType( simpleEdgeType1Ptr ); trainingDataset.addNodeType( simpleNodeType2Ptr ); trainingDataset.addEdgeType( simpleEdgeType2Ptr, typeOfEdgeFeatures ); // // 2. Create some scenarios with nodes and edges and add them to the // training dataset. // /*--------------------------------- Graph 1 ----------------------------------*/ CGraph graph; std::map<size_t,size_t> groundTruth; Eigen::VectorXd nodeFeatures1(3); nodeFeatures1 << 15, 30, 1; Eigen::VectorXd nodeFeatures2(2); nodeFeatures2 << 10, 1; Eigen::VectorXd edgeFeatures1(2); edgeFeatures1 << 1.5, 1; CNodePtr nodePtr2 ( new CNode( simpleNodeType2Ptr, nodeFeatures2, string("room-1") ) ); CNodePtr nodePtr1 ( new CNode( simpleNodeType1Ptr, nodeFeatures1, string("object-1") ) ); CEdgePtr edgePtr1 ( new CEdge( nodePtr2, nodePtr1, simpleEdgeType2Ptr, edgeFeatures1 ) ) ; graph.addNode( nodePtr1 ); graph.addNode( nodePtr2 ); graph.addEdge( edgePtr1 ); groundTruth[ nodePtr1->getID() ] = 0; groundTruth[ nodePtr2->getID() ] = 0; trainingDataset.addGraph( graph ); trainingDataset.addGraphGroundTruth( groundTruth ); /*--------------------------------- Graph 2 ----------------------------------*/ CGraph graph2; std::map<size_t,size_t> groundTruth2; Eigen::VectorXd nodeFeatures12(3); nodeFeatures12 << 100, 70, 1; Eigen::VectorXd nodeFeatures22(2); nodeFeatures22 << 30, 1; Eigen::VectorXd edgeFeatures12(2); edgeFeatures12 << 3.33, 1; CNodePtr nodePtr12 ( new CNode( simpleNodeType1Ptr, nodeFeatures12 ) ); CNodePtr nodePtr22 ( new CNode( simpleNodeType2Ptr, nodeFeatures22 ) ); CEdgePtr edgePtr12 ( new CEdge( nodePtr12, nodePtr22, simpleEdgeType2Ptr, edgeFeatures12 ) ); graph2.addNode( nodePtr12 ); graph2.addNode( nodePtr22 ); graph2.addEdge( edgePtr12 ); groundTruth2[ nodePtr12->getID() ] = 1; groundTruth2[ nodePtr22->getID() ] = 1; trainingDataset.addGraph( graph2 ); trainingDataset.addGraphGroundTruth( groundTruth2 ); /*--------------------------------- Graph 3 ----------------------------------*/ CGraph graph3; std::map<size_t,size_t> groundTruth3; Eigen::VectorXd nodeFeatures13(3); nodeFeatures13 << 16, 28, 1; Eigen::VectorXd nodeFeatures23(3); nodeFeatures23 << 95, 72, 1; Eigen::VectorXd edgeFeatures13(3); edgeFeatures13 << 69, 44, 1; CNodePtr nodePtr13 ( new CNode( simpleNodeType1Ptr, nodeFeatures13 ) ); CNodePtr nodePtr23 ( new CNode( simpleNodeType1Ptr, nodeFeatures23 ) ); CEdgePtr edgePtr13 ( new CEdge( nodePtr13, nodePtr23, simpleEdgeType1Ptr, edgeFeatures13 ) ); graph3.addNode( nodePtr13 ); graph3.addNode( nodePtr23 ); graph3.addEdge( edgePtr13 ); groundTruth3[ nodePtr13->getID() ] = 0; groundTruth3[ nodePtr23->getID() ] = 1; trainingDataset.addGraph( graph3 ); trainingDataset.addGraphGroundTruth( groundTruth3 ); /*------------------------------------------------------------------------------ * * TRAINING! * *----------------------------------------------------------------------------*/ // // 3. Train a PGM with that scenarios. // cout << "---------------------------------------------------" << endl; cout << " Training" << endl; cout << "---------------------------------------------------" << endl; UPGMpp::TTrainingOptions to; to.l2Regularization = true; to.nodeLambda = 10; to.edgeLambda = 1; to.showTrainedWeights = false; to.showTrainingProgress = false; trainingDataset.setTrainingOptions( to ); trainingDataset.train(); /*------------------------------------------------------------------------------ * * TESTING! * *----------------------------------------------------------------------------*/ // // 4. Perform a MAP inference (decoding) to check that the system performance. // // // Build a graph to test the trained model // CGraph graph4; std::map<size_t,size_t> groundTruth4; Eigen::VectorXd nodeFeatures14(3); nodeFeatures14 << 17, 35, 1; Eigen::VectorXd nodeFeatures24(2); nodeFeatures24 << 12, 1; Eigen::VectorXd edgeFeatures14(2); edgeFeatures14 << 1.41, 1; CNodePtr nodePtr14 ( new CNode( simpleNodeType1Ptr, nodeFeatures14 ) ); CNodePtr nodePtr24 ( new CNode( simpleNodeType2Ptr, nodeFeatures24 ) ); CEdgePtr edgePtr14 ( new CEdge( nodePtr14, nodePtr24, simpleEdgeType2Ptr, edgeFeatures14 ) ); graph4.addNode( nodePtr14 ); graph4.addNode( nodePtr24 ); graph4.addEdge( edgePtr14 ); groundTruth4[ nodePtr14->getID() ] = 0; groundTruth4[ nodePtr24->getID() ] = 0; graph4.computePotentials(); // // Now compute the MAP decoding and show the results // CICMInferenceMAP decodeICM; TInferenceOptions options; options.maxIterations = 100; options.convergency = 0.0001; cout << "---------------------------------------------------" << endl; cout << " ICM decoding"<< endl; cout << "---------------------------------------------------" << endl; std::map<size_t,size_t> resultsMap; decodeICM.setOptions( options ); decodeICM.infer( graph4, resultsMap ); std::map<size_t,size_t>::iterator it; for ( it = resultsMap.begin(); it != resultsMap.end(); it++ ) { std::cout << "Node id " << it->first << " labeled as " << it->second << " being of class " << groundTruth4[it->first] << std::endl; } cout << "---------------------------------------------------" << endl; // We are ready to do some sport :) return 1; }
/** Method for getting a bound graph from the one stored into the object. * \param boundGraph: resulting graph, it must be empty when the method * is called. * \param nodesToBound: a map containing as key the id of a node, and * as value the class/state to be bound. */ void getBoundGraph( CGraph &boundGraph, std::map<size_t,size_t> nodesToBound ) { // Check that the graph is empty assert( boundGraph.getNodes().size() == 0 ); // Copy the graph into boundGraph // Copy nodes for ( size_t node = 0; node < m_nodes.size(); node++ ) { CNodePtr nodePtr(new CNode(*m_nodes[node])); boundGraph.addNode( nodePtr ); } // Copy edges for ( size_t edge = 0; edge < m_edges.size(); edge++ ) { CEdgePtr edgePtr( new CEdge(*m_edges[edge])); boundGraph.addEdge( edgePtr ); } // Okey, now iterate over the nodes to be bound, removing then from // the resulting graph by expanding the potential associated with // their bound state for ( std::map<size_t,size_t>::iterator it = nodesToBound.begin(); it != nodesToBound.end(); it++ ) { size_t nodeID = it->first; size_t nodeState = it->second; // Potential of the state to be bounded double nodePotential = boundGraph.getNodeWithID( nodeID )->getPotentials()( nodeState ); // Iterate over the neighbours, updating their node potentials // according to the state of the node to be bound and its potential pair<multimap<size_t,CEdgePtr>::iterator,multimap<size_t,CEdgePtr>::iterator > neighbors; neighbors = boundGraph.getEdgesF().equal_range(nodeID); for ( multimap<size_t,CEdgePtr>::iterator it = neighbors.first; it != neighbors.second; it++ ) { CEdgePtr edgePtr( (*it).second ); Eigen::MatrixXd edgePotentials = edgePtr->getPotentials(); size_t ID1, ID2; edgePtr->getNodesID(ID1,ID2); // If the node to be bound is the first one appearing in the // edge. if ( ID1 == nodeID ) { CNodePtr nodePtr = boundGraph.getNodeWithID( ID2 ); Eigen::VectorXd boundPotentials = edgePotentials.row(nodeState).transpose()*nodePotential; Eigen::VectorXd newPotentials = nodePtr->getPotentials().cwiseProduct(boundPotentials); nodePtr->setPotentials( newPotentials ); } else // If it is the second one in the edge { CNodePtr nodePtr = boundGraph.getNodeWithID( ID1 ); Eigen::VectorXd boundPotentials = edgePotentials.col( nodeState )*nodePotential; Eigen::VectorXd newPotentials = nodePtr->getPotentials().cwiseProduct(boundPotentials); nodePtr->setPotentials( newPotentials ); } } // Now that the potential of the state of the node to bound // has been expanded, delete the node from the graph. boundGraph.deleteNode( nodeID ); } }