123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- /*
- * markerAssociationDP.cpp
- *
- * Created on: Feb 6, 2013
- * Author: koerner
- */
- #include <boost/config.hpp>
- #include <boost/graph/adjacency_list.hpp>
- #include <boost/graph/dijkstra_shortest_paths.hpp>
- #include <boost/graph/graph_traits.hpp>
- #include <boost/graph/iteration_macros.hpp>
- #include <boost/graph/properties.hpp>
- #include <boost/property_map/property_map.hpp>
- #include <boost/program_options.hpp>
- #include <boost/format.hpp>
- #include <opencv2/opencv.hpp>
- #include <limits>
- #include <iostream>
- #include <fstream>
- #include <utility>
- #include <vector>
- // command line parameters
- struct Params {
- // filename of 3d point filen
- std::string strPointClouds;
- // file in which the results are saved
- std::string outputFilename;
- // max link distance
- double maxLinkDistance;
- // max angular distance
- double maxAngularDistance;
- // number of tracklets to be extracted
- size_t trackletCount;
- // number of allowed frames which can be skipped within one tracklet
- size_t allowedFrameSkip;
- };
- // each node contains this struct to provide information about the 3d point
- struct NodeInfo {
- // creates virtual marker
- NodeInfo() {
- this->position = cv::Point3d(0, 0, 0);
- this->frame = std::numeric_limits<size_t>::max();
- }
- // create normal marker info
- NodeInfo(const cv::Point3d &_position, const size_t &_frame) {
- this->position = _position;
- this->frame = _frame;
- }
- //
- bool isVirtual() {
- return (this->frame == std::numeric_limits<size_t>::max());
- }
- //
- cv::Point3d position;
- size_t frame;
- };
- // get command line parameters
- bool evalCmdLine(int argc, char **argv, Params &p) {
- // define parameters
- boost::program_options::options_description desc("Allowed options");
- desc.add_options()
- ("help", "produce help message")
- ("pointFile,p",
- boost::program_options::value< std::string >(),
- "3d point clouds file containing the detections for each frame")
- ("outputFile,o",
- boost::program_options::value< std::string >(),
- "output file for the generated tracklets")
- ("trackletCount,c",
- boost::program_options::value< size_t >()->default_value(1),
- "number of tracklets to be extracted")
- ("maxLinkDistance,d",
- boost::program_options::value< double >()->default_value(1.0),
- "maximum Euclidean distance (in px) allowed for two detections to get linked")
- ("maxAngularDistance,a",
- boost::program_options::value< double >()->default_value(20.0),
- "maximum angular distance (in deg) allowed for two detections to get linked")
- ("allowedFrameSkip,s",
- boost::program_options::value< size_t >()->default_value(1),
- "number of allowed frames which can be skipped within one tracklet (>= 1)")
- ;
- // parse parameters
- boost::program_options::variables_map vm;
- boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm);
- boost::program_options::notify(vm);
- if (vm.count("help")) {
- std::cout << desc << std::endl;
- exit(EXIT_SUCCESS);
- }
- // check parameters
- if (vm.count("pointFile")) {
- p.strPointClouds = vm["pointFile"].as< std::string >();
- } else {
- std::cerr << "No points file passed!" << std::endl;
- std::cout << desc << std::endl;
- exit(EXIT_SUCCESS);
- }
- if (vm.count("outputFile")) {
- p.outputFilename = vm["outputFile"].as< std::string >();
- } else {
- std::cerr << "No output file passed!" << std::endl;
- std::cout << desc << std::endl;
- exit(EXIT_SUCCESS);
- }
- p.maxLinkDistance = vm["maxLinkDistance"].as< double >();
- p.maxAngularDistance = vm["maxAngularDistance"].as< double >();
- p.trackletCount = vm["trackletCount"].as< size_t >();
- p.allowedFrameSkip = vm["allowedFrameSkip"].as< size_t >();
- return true;
- }
- void readPoints3d(const std::string &filename,
- std::vector< std::vector< cv::Point3d > > &clouds) {
- std::cout << "Reading points from file " << filename << std::endl;
- std::ifstream pointFile(filename.c_str(), std::ios_base::in);
- if (!pointFile.is_open()) {
- std::cerr << "Could not open file " << std::endl;
- return;
- }
- uint frame = 0;
- uint detections = 0;
- std::string line;
- while (std::getline(pointFile, line)) {
- std::cerr << "\rReading points of frame " << frame + 1 << "... ";
- std::vector< cv::Point3d > cloud;
- std::stringstream split(line + " ");
- double tmpValue;
- int tmpDim = 0;
- cv::Point3d tmpPoint;
- while ((split >> tmpValue).good()) {
- switch (tmpDim) {
- case 0:
- tmpPoint.x = tmpValue;
- break;
- case 1:
- tmpPoint.y = tmpValue;
- break;
- case 2:
- tmpPoint.z = tmpValue;
- cloud.push_back(tmpPoint);
- //std::cout << cv::Mat(tmpPoint) << std::endl;
- detections++;
- break;
- }
- tmpDim = (tmpDim + 1) % 3;
- }
- clouds.push_back(cloud);
- std::cout << cloud.size() << std::flush;
- frame++;
- }
- uint nFrames = clouds.size();
- std::cerr << std::endl << "Read " << nFrames << " frames (" << detections << " detections)" << std::endl;
- }
- void createTracklets(const std::vector< std::vector< cv::Point3d > > &clouds, const Params ¶ms) {
- typedef double Weight;
- typedef boost::property< boost::edge_weight_t, Weight > WeightProperty;
- // typedef boost::property< boost::vertex_name_t, std::string > NameProperty;
- typedef boost::property< boost::vertex_name_t, NodeInfo > NameProperty;
- typedef boost::adjacency_list<
- boost::listS,
- boost::vecS,
- boost::directedS,
- NameProperty,
- WeightProperty > Graph;
- typedef boost::graph_traits< Graph >::vertex_descriptor Vertex;
- typedef boost::property_map< Graph, boost::vertex_index_t >::type IndexMap;
- typedef boost::property_map< Graph, boost::vertex_name_t >::type NameMap;
- typedef boost::iterator_property_map< Vertex*, IndexMap, Vertex, Vertex& > PredecessorMap;
- typedef boost::iterator_property_map< Weight*, IndexMap, Weight, Weight& > DistanceMap;
- // Create a graph
- Graph g;
- std::vector< std::vector< Vertex > > vertices;
- size_t frameStart = 0;
- size_t frameMax = clouds.size();
- // frameMax = 900;
- // adding vertices
- for (size_t frame = frameStart; frame < frameMax; ++frame) {
- std::cout << "\rAdding layer for frame " << frame + 1; // << std::flush;
- std::vector< cv::Point3d > p = clouds[frame];
- std::vector< Vertex > layer;
- for (size_t marker = 0; marker < p.size(); ++marker) {
- Vertex v = boost::add_vertex(NodeInfo(p[marker], frame), g);
- layer.push_back(v);
- }
- vertices.push_back(layer);
- std::cout << ": " << p.size() << ", " << layer.size() << ", " << vertices.rbegin()->size() << std::flush;
- }
- // virtual source and sink
- Vertex vSource = boost::add_vertex(NodeInfo(), g);
- Vertex vSink = boost::add_vertex(NodeInfo(), g);
- NameMap nameMap = boost::get(boost::vertex_name, g);
- std::cout << std::endl;
- // adding edges (layer eq frame)
- for (size_t iLayers = 0; iLayers < vertices.size(); ++iLayers) {
- // all points of frame "iLayers"
- std::vector< Vertex > p = vertices[iLayers];
- // for each point of layer "iLayers"
- for (size_t iP = 0; iP < p.size(); ++iP) {
- Vertex vP = p[iP];
- // create edges to source and sink
- std::cout << "\rAdding edges " << iLayers << "-> vSink" << std::flush;
- if (iLayers <= 100)
- boost::add_edge(vSource, vP, (iLayers + 1) * (params.maxLinkDistance + params.maxAngularDistance), g);
- if (iLayers + 100 >= vertices.size())
- boost::add_edge(vP, vSink, (vertices.size() - iLayers) * (params.maxLinkDistance + params.maxAngularDistance), g);
- // create shortcuts to next-next layers
- for (size_t iOffset = 1; iOffset <= params.allowedFrameSkip; ++iOffset) {
- if (iLayers + iOffset < vertices.size() - 1) {
- std::cout << "\rAdding edges for frames " << iLayers << "->" << (iLayers + iOffset) << std::flush;
- // all points of frame "iLayers" + "iOffset"
- std::vector< Vertex > q = vertices[iLayers + iOffset];
- // for each node of neighboring layers (in time)
- for (size_t iQ = 0; iQ < q.size(); ++iQ) {
- Vertex vQ = q[iQ];
- // euclidean distance between iP and iQ
- cv::Point3d ptP = nameMap[vP].position;
- cv::Point3d ptQ = nameMap[vQ].position;
- double dx = ptP.x - ptQ.x;
- double dy = ptP.y - ptQ.y;
- double dz = ptP.z - ptQ.z;
- Weight distPosition = sqrt(dx * dx + dy * dy);
- Weight distAngular = std::max(0.0, abs(dz) - 5.0);
- // edge weight
- //std::cout << " x" << params.maxLinkDistance << "," << params.maxAngularDistance << "; " << distPosition << "," << distAngular << "," << iOffset << " = " << 0.1 * (distPosition + 0.5 * distAngular) * double(iOffset) << std::endl;
- if (distPosition < params.maxLinkDistance && distAngular < params.maxAngularDistance) {
- // if we discard one layer, the edge weight should be higher!
- boost::add_edge(vP, vQ, 0.1 * (distPosition + 0.5 * distAngular) * double(iOffset), g);
- //boost::add_edge(vP, vQ, 0.001 * Weight((1.0 + 0.33 * distPosition) * (1.0 + distAngular) * Weight(1.0 + iOffset)), g);
- //std::cout << "x" << params.maxAngularDistance << "," << distPosition << "," << distAngular << "," << iOffset << std::endl;
- //boost::add_edge(vP, vQ, Weight(dist * Weight(1 + 0.5 * iOffset)), g);
- // boost::add_edge(vP, vQ, Weight(dist), g);
- }
- }
- }
- }
- }
- }
- std::cout << std::endl;
- // Create things for Dijkstra
- std::vector< Vertex > predecessors(boost::num_vertices(g)); // To store parents
- std::vector< Weight > distances(boost::num_vertices(g)); // To store distances
- IndexMap indexMap = boost::get(boost::vertex_index, g);
- PredecessorMap predecessorMap(&predecessors[0], indexMap);
- DistanceMap distanceMap(&distances[0], indexMap);
- // output file
- std::ofstream f(params.outputFilename.c_str());
- // Compute shortest paths from input layer vertices to the sink
- for (size_t tracklet = 0; tracklet < params.trackletCount; ++tracklet) {
- boost::dijkstra_shortest_paths(g, // the graph
- vSource, // the source vertex
- boost::distance_map(distanceMap).predecessor_map(predecessorMap));
- // Output results
- // std::cout << "distances and parents:" << std::endl;
- // NameMap nameMap = boost::get(boost::vertex_name, g);
- // BGL_FORALL_VERTICES(v, g, Graph) {
- // for (size_t i = 0; i < vertices.size(); ++i) {
- // for (size_t j = 0; j < vertices[i].size(); ++j) {
- // Vertex *v = &vertices[i][j];
- // std::cout << "distance(" << nameMap[vSink] << ", " << nameMap[*v] << ") = " << distanceMap[*v] << ", ";
- // std::cout << "predecessor(" << nameMap[*v] << ") = " << nameMap[predecessorMap[*v]] << std::endl;
- // }
- // }
- //
- // std::cout << std::endl;
- // Extract the shortest path
- typedef std::vector< Graph::edge_descriptor > PathType;
- PathType path;
- Vertex v = vSink; // We want to start at the sink and work our way back to the source
- for (Vertex u = predecessorMap[v]; u != v; v = u, u = predecessorMap[v]) {
- std::pair< Graph::edge_descriptor, bool > edgePair = boost::edge(u, v, g);
- Graph::edge_descriptor edge = edgePair.first;
- path.push_back(edge);
- }
- // Write shortest path
- //int c = 0;
- bool vFirst = true;
- //std::cout << "Shortest path from vSource to vSink: " << std::endl;
- for (PathType::reverse_iterator pathIterator = path.rbegin(); pathIterator != path.rend(); ++pathIterator) {
- // print all non-virtual nodes of this path
- if (!nameMap[boost::source(*pathIterator, g)].isVirtual()) {
- //std::cout << nameMap[boost::source(*pathIterator, g)].position << " -> " << nameMap[boost::target(*pathIterator, g)].position
- // << " = "
- // << boost::get(boost::edge_weight, g, *pathIterator) << std::endl;
- f << nameMap[boost::source(*pathIterator, g)].position.x << " "
- << nameMap[boost::source(*pathIterator, g)].position.y << " "
- << nameMap[boost::source(*pathIterator, g)].position.z << " "
- << nameMap[boost::source(*pathIterator, g)].frame << " ";
- }
- // for each node of the path, set the weights of all outgoing edges to Inf (for Dijkstra equivalent to deletion of the node)
- if (!vFirst) {
- std::pair<Graph::out_edge_iterator, Graph::out_edge_iterator> edgeIts = boost::out_edges(boost::source(*pathIterator, g), g);
- //size_t nEdges = 0;
- for (Graph::out_edge_iterator edgeIt = edgeIts.first; edgeIt != edgeIts.second; edgeIt++) {
- boost::get(boost::edge_weight, g, *edgeIt) = std::numeric_limits<Weight>::infinity();
- //std::cout << "w = " << boost::get(boost::edge_weight, g, *edgeIt) << std::endl;
- //nEdges++;
- }
- //std::cout << "Found " << nEdges << " out edges for this node" << std::endl;
- //getchar();
- }
- vFirst = false;
- }
- f << "\n";
- std::cout << "Found tracklet #" << tracklet + 1 << "/" << params.trackletCount << ": " << path.size() << " nodes, distance: " << distanceMap[vSink] << std::endl;
- }
- f.close();
- }
- int main(int argc, char** argv) {
- // get command line parameters
- Params p;
- if (!evalCmdLine(argc, argv, p)) {
- std::cerr << "Error while parsing arguments!" << std::endl;
- return 1;
- }
- std::vector< std::vector< cv::Point3d > > points3dLists;
- if (!p.strPointClouds.empty()) {
- readPoints3d(p.strPointClouds, points3dLists);
- }
- createTracklets(points3dLists, p);
- return EXIT_SUCCESS;
- }
|