/* * GenericGraph2D.cpp * * Created on: Nov 23, 2010 * Author: nikai * Description: generic graph types used in partitioning */ #include #include #include #include #include "GenericGraph.h" using namespace std; namespace gtsam { namespace partition { /** * Note: Need to be able to handle a graph with factors that involve variables not in the given {keys} */ list > findIslands(const GenericGraph2D& graph, const vector& keys, WorkSpace& workspace, const int minNrConstraintsPerCamera, const int minNrConstraintsPerLandmark) { typedef pair IntPair; typedef list FactorList; typedef map Connections; // create disjoin set forest DSFVector dsf(workspace.dsf, keys); FactorList factors(graph.begin(), graph.end()); size_t nrFactors = factors.size(); FactorList::iterator itEnd; workspace.prepareDictionary(keys); while (nrFactors) { Connections connections; bool succeed = false; itEnd = factors.end(); list toErase; for (FactorList::iterator itFactor=factors.begin(); itFactor!=itEnd; itFactor++) { // remove invalid factors GenericNode2D key1 = (*itFactor)->key1, key2 = (*itFactor)->key2; if (workspace.dictionary[key1.index]==-1 || workspace.dictionary[key2.index]==-1) { toErase.push_back(itFactor); nrFactors--; continue; } size_t label1 = dsf.findSet(key1.index); size_t label2 = dsf.findSet(key2.index); if (label1 == label2) { toErase.push_back(itFactor); nrFactors--; continue; } // merge two trees if the connection is strong enough, otherwise cache it // an odometry factor always merges two islands if (key1.type == NODE_POSE_2D && key2.type == NODE_POSE_2D) { toErase.push_back(itFactor); nrFactors--; dsf.makeUnionInPlace(label1, label2); succeed = true; break; } // single landmark island only need one measurement if ((dsf.isSingleton(label1)==1 && key1.type == NODE_LANDMARK_2D) || (dsf.isSingleton(label2)==1 && key2.type == NODE_LANDMARK_2D)) { toErase.push_back(itFactor); nrFactors--; dsf.makeUnionInPlace(label1, label2); succeed = true; break; } // stack the current factor with the cached constraint IntPair labels = (label1 < label2) ? make_pair(label1, label2) : make_pair(label2, label1); Connections::iterator itCached = connections.find(labels); if (itCached == connections.end()) { connections.insert(make_pair(labels, itFactor)); continue; } else { GenericNode2D key21 = (*itCached->second)->key1, key22 = (*itCached->second)->key2; // if observe the same landmark, we can not merge, abandon the current factor if ((key1.index == key21.index && key1.type == NODE_LANDMARK_2D) || (key1.index == key22.index && key1.type == NODE_LANDMARK_2D) || (key2.index == key21.index && key2.type == NODE_LANDMARK_2D) || (key2.index == key22.index && key2.type == NODE_LANDMARK_2D)) { toErase.push_back(itFactor); nrFactors--; continue; } else { toErase.push_back(itFactor); nrFactors--; toErase.push_back(itCached->second); nrFactors--; dsf.makeUnionInPlace(label1, label2); connections.erase(itCached); succeed = true; break; } } } // erase unused factors for(const FactorList::iterator& it: toErase) factors.erase(it); if (!succeed) break; } list > islands; map > arrays = dsf.arrays(); for(const auto& kv : arrays) islands.push_back(kv.second); return islands; } /* ************************************************************************* */ void print(const GenericGraph2D& graph, const std::string name) { cout << name << endl; for(const sharedGenericFactor2D& factor_: graph) cout << factor_->key1.index << " " << factor_->key2.index << endl; } /* ************************************************************************* */ void print(const GenericGraph3D& graph, const std::string name) { cout << name << endl; for(const sharedGenericFactor3D& factor_: graph) cout << factor_->key1.index << " " << factor_->key2.index << " (" << factor_->key1.type << ", " << factor_->key2.type <<")" << endl; } /* ************************************************************************* */ // create disjoin set forest DSFVector createDSF(const GenericGraph3D& graph, const vector& keys, const WorkSpace& workspace) { DSFVector dsf(workspace.dsf, keys); typedef list FactorList; FactorList factors(graph.begin(), graph.end()); size_t nrFactors = factors.size(); FactorList::iterator itEnd; while (nrFactors) { bool succeed = false; itEnd = factors.end(); list toErase; for (FactorList::iterator itFactor=factors.begin(); itFactor!=itEnd; itFactor++) { // remove invalid factors if (graph.size() == 178765) cout << "kai21" << endl; GenericNode3D key1 = (*itFactor)->key1, key2 = (*itFactor)->key2; if (graph.size() == 178765) cout << "kai21: " << key1.index << " " << key2.index << endl; if (workspace.dictionary[key1.index]==-1 || workspace.dictionary[key2.index]==-1) { toErase.push_back(itFactor); nrFactors--; continue; } if (graph.size() == 178765) cout << "kai22" << endl; size_t label1 = dsf.findSet(key1.index); size_t label2 = dsf.findSet(key2.index); if (label1 == label2) { toErase.push_back(itFactor); nrFactors--; continue; } if (graph.size() == 178765) cout << "kai23" << endl; // merge two trees if the connection is strong enough, otherwise cache it // an odometry factor always merges two islands if ((key1.type == NODE_POSE_3D && key2.type == NODE_LANDMARK_3D) || (key1.type == NODE_POSE_3D && key2.type == NODE_POSE_3D)) { toErase.push_back(itFactor); nrFactors--; dsf.makeUnionInPlace(label1, label2); succeed = true; break; } if (graph.size() == 178765) cout << "kai24" << endl; } // erase unused factors for(const FactorList::iterator& it: toErase) factors.erase(it); if (!succeed) break; } return dsf; } /* ************************************************************************* */ // first check the type of the key (pose or landmark), and then check whether it is singular inline bool isSingular(const set& singularCameras, const set& singularLandmarks, const GenericNode3D& node) { switch(node.type) { case NODE_POSE_3D: return singularCameras.find(node.index) != singularCameras.end(); break; case NODE_LANDMARK_3D: return singularLandmarks.find(node.index) != singularLandmarks.end(); break; default: throw runtime_error("unrecognized key type!"); } } /* ************************************************************************* */ void findSingularCamerasLandmarks(const GenericGraph3D& graph, const WorkSpace& workspace, const vector& isCamera, const vector& isLandmark, set& singularCameras, set& singularLandmarks, vector& nrConstraints, bool& foundSingularCamera, bool& foundSingularLandmark, const int minNrConstraintsPerCamera, const int minNrConstraintsPerLandmark) { // compute the constraint number per camera std::fill(nrConstraints.begin(), nrConstraints.end(), 0); for(const sharedGenericFactor3D& factor_: graph) { const int& key1 = factor_->key1.index; const int& key2 = factor_->key2.index; if (workspace.dictionary[key1] != -1 && workspace.dictionary[key2] != -1 && !isSingular(singularCameras, singularLandmarks, factor_->key1) && !isSingular(singularCameras, singularLandmarks, factor_->key2)) { nrConstraints[key1]++; nrConstraints[key2]++; // a single pose constraint is sufficient for stereo, so we add 2 to the counter // for a total of 3, i.e. the same as 3 landmarks fully constraining the camera if(factor_->key1.type == NODE_POSE_3D && factor_->key2.type == NODE_POSE_3D){ nrConstraints[key1]+=2; nrConstraints[key2]+=2; } } } // find singular cameras and landmarks foundSingularCamera = false; foundSingularLandmark = false; for (size_t i=0; i > findIslands(const GenericGraph3D& graph, const vector& keys, WorkSpace& workspace, const size_t minNrConstraintsPerCamera, const size_t minNrConstraintsPerLandmark) { // create disjoint set forest workspace.prepareDictionary(keys); DSFVector dsf = createDSF(graph, keys, workspace); const bool verbose = false; bool foundSingularCamera = true; bool foundSingularLandmark = true; list > islands; set singularCameras, singularLandmarks; vector isCamera(workspace.dictionary.size(), false); vector isLandmark(workspace.dictionary.size(), false); // check the constraint number of every variable // find the camera and landmark keys for(const sharedGenericFactor3D& factor_: graph) { //assert(factor_->key2.type == NODE_LANDMARK_3D); // only VisualSLAM should come here, not StereoSLAM if (workspace.dictionary[factor_->key1.index] != -1) { if (factor_->key1.type == NODE_POSE_3D) isCamera[factor_->key1.index] = true; else isLandmark[factor_->key1.index] = true; } if (workspace.dictionary[factor_->key2.index] != -1) { if (factor_->key2.type == NODE_POSE_3D) isCamera[factor_->key2.index] = true; else isLandmark[factor_->key2.index] = true; } } vector nrConstraints(workspace.dictionary.size(), 0); // iterate until all singular variables have been removed. Removing a singular variable // can cause another to become singular, so this will probably run several times while (foundSingularCamera || foundSingularLandmark) { findSingularCamerasLandmarks(graph, workspace, isCamera, isLandmark, // input singularCameras, singularLandmarks, nrConstraints, // output foundSingularCamera, foundSingularLandmark, // output minNrConstraintsPerCamera, minNrConstraintsPerLandmark); // input } // add singular variables directly as islands if (!singularCameras.empty()) { if (verbose) cout << "singular cameras:"; for(const size_t i: singularCameras) { islands.push_back(vector(1, i)); // <--------------------------- if (verbose) cout << i << " "; } if (verbose) cout << endl; } if (!singularLandmarks.empty()) { if (verbose) cout << "singular landmarks:"; for(const size_t i: singularLandmarks) { islands.push_back(vector(1, i)); // <--------------------------- if (verbose) cout << i << " "; } if (verbose) cout << endl; } // regenerating islands map > labelIslands = dsf.arrays(); size_t label; vector island; for(const auto& li: labelIslands) { tie(label, island) = li; vector filteredIsland; // remove singular cameras from array filteredIsland.reserve(island.size()); for(const size_t key: island) { if ((isCamera[key] && singularCameras.find(key) == singularCameras.end()) || // not singular (isLandmark[key] && singularLandmarks.find(key) == singularLandmarks.end()) || // not singular (!isCamera[key] && !isLandmark[key])) { // the key is not involved in any factor, so the type is undertermined filteredIsland.push_back(key); } } islands.push_back(filteredIsland); } // sanity check size_t nrKeys = 0; for(const vector& island: islands) nrKeys += island.size(); if (nrKeys != keys.size()) { cout << nrKeys << " vs " << keys.size() << endl; throw runtime_error("findIslands: the number of keys is inconsistent!"); } if (verbose) cout << "found " << islands.size() << " islands!" << endl; return islands; } /* ************************************************************************* */ // return the number of intersection between two **sorted** landmark vectors inline int getNrCommonLandmarks(const vector& landmarks1, const vector& landmarks2){ size_t i1 = 0, i2 = 0; int nrCommonLandmarks = 0; while (i1 < landmarks1.size() && i2 < landmarks2.size()) { if (landmarks1[i1] < landmarks2[i2]) i1 ++; else if (landmarks1[i1] > landmarks2[i2]) i2 ++; else { i1++; i2++; nrCommonLandmarks ++; } } return nrCommonLandmarks; } /* ************************************************************************* */ void reduceGenericGraph(const GenericGraph3D& graph, const std::vector& cameraKeys, const std::vector& landmarkKeys, const std::vector& dictionary, GenericGraph3D& reducedGraph) { typedef size_t CameraKey; typedef pair CameraPair; typedef size_t LandmarkKey; // get a mapping from each landmark to its connected cameras vector > cameraToLandmarks(dictionary.size()); // for odometry xi-xj where i cameraToCamera(dictionary.size(), -1); size_t key_i, key_j; for(const sharedGenericFactor3D& factor_: graph) { if (factor_->key1.type == NODE_POSE_3D) { if (factor_->key2.type == NODE_LANDMARK_3D) {// projection factor cameraToLandmarks[factor_->key1.index].push_back(factor_->key2.index); } else { // odometry factor if (factor_->key1.index < factor_->key2.index) { key_i = factor_->key1.index; key_j = factor_->key2.index; } else { key_i = factor_->key2.index; key_j = factor_->key1.index; } cameraToCamera[key_i] = key_j; } } } // sort the landmark keys for the late getNrCommonLandmarks call for(vector &landmarks: cameraToLandmarks){ if (!landmarks.empty()) std::sort(landmarks.begin(), landmarks.end()); } // generate the reduced graph reducedGraph.clear(); int factorIndex = 0; int camera1, camera2, nrTotalConstraints; bool hasOdometry; for (size_t i1=0; i1 0 || hasOdometry) { nrTotalConstraints = 2 * nrCommonLandmarks + (hasOdometry ? 6 : 0); reducedGraph.push_back(boost::make_shared(camera1, camera2, factorIndex++, NODE_POSE_3D, NODE_POSE_3D, nrTotalConstraints)); } } } } /* ************************************************************************* */ void checkSingularity(const GenericGraph3D& graph, const std::vector& frontals, WorkSpace& workspace, const size_t minNrConstraintsPerCamera, const size_t minNrConstraintsPerLandmark) { workspace.prepareDictionary(frontals); vector nrConstraints(workspace.dictionary.size(), 0); // summarize the constraint number const vector& dictionary = workspace.dictionary; vector isValidCamera(workspace.dictionary.size(), false); vector isValidLandmark(workspace.dictionary.size(), false); for(const sharedGenericFactor3D& factor_: graph) { assert(factor_->key1.type == NODE_POSE_3D); //assert(factor_->key2.type == NODE_LANDMARK_3D); const size_t& key1 = factor_->key1.index; const size_t& key2 = factor_->key2.index; if (dictionary[key1] == -1 || dictionary[key2] == -1) continue; isValidCamera[key1] = true; if(factor_->key2.type == NODE_LANDMARK_3D) isValidLandmark[key2] = true; else isValidCamera[key2] = true; nrConstraints[key1]++; nrConstraints[key2]++; // a single pose constraint is sufficient for stereo, so we add 2 to the counter // for a total of 3, i.e. the same as 3 landmarks fully constraining the camera if(factor_->key1.type == NODE_POSE_3D && factor_->key2.type == NODE_POSE_3D){ nrConstraints[key1]+=2; nrConstraints[key2]+=2; } } // find the minimum constraint for cameras and landmarks size_t minFoundConstraintsPerCamera = 10000; size_t minFoundConstraintsPerLandmark = 10000; for (size_t i=0; i(minFoundConstraintsPerCamera)); if (minFoundConstraintsPerLandmark < minNrConstraintsPerLandmark) throw runtime_error("checkSingularity:minConstraintsPerLandmark < " + boost::lexical_cast(minFoundConstraintsPerLandmark)); } }} // namespace