view c/Motion.cpp @ 398:3399bd48cb40

Ajout d'une méthode pour obtenir le nombre de FPS Méthode de capture des trames vidéos plus résistante aux erreur Utilisation d'un dictionnaire pour les fichier de configuration afin de garder le nom des sections
author Jean-Philippe Jodoin <jpjodoin@gmail.com>
date Mon, 29 Jul 2013 13:46:07 -0400
parents 03dbecd3a887
children f43bc0b0ba74
line wrap: on
line source

#include "Motion.hpp"
#include "cvutils.hpp"

#include "src/TrajectoryDBAccessList.h"

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"

#include <boost/graph/connected_components.hpp>

#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
#include <utility>

using namespace std;
using namespace cv;
using namespace boost;

/******************** FeatureTrajectory ********************/

FeatureTrajectory::FeatureTrajectory(const unsigned int& frameNum, const cv::Point2f& p, const Mat& homography)
  : firstInstant(frameNum), lastInstant(frameNum) {
  positions = TrajectoryPoint2fPtr(new TrajectoryPoint2f());
  velocities = TrajectoryPoint2fPtr(new TrajectoryPoint2f());
  addPoint(frameNum, p, homography);
}

FeatureTrajectory::FeatureTrajectory(TrajectoryPoint2fPtr& _positions, TrajectoryPoint2fPtr& _velocities) {
  positions = _positions;
  velocities = _velocities;
  positions->computeInstants(firstInstant, lastInstant);
}

FeatureTrajectory::FeatureTrajectory(const int& id, TrajectoryDBAccessList<Point2f>& trajectoryDB, const string& positionsTableName, const string& velocitiesTableName) {
  bool success = trajectoryDB.read(positions, id, positionsTableName);
  if (!success)
    cout << "problem loading positions" << endl;
  success = trajectoryDB.read(velocities, id, velocitiesTableName);
  if (!success)
    cout << "problem loading velocities" << endl;
  // take advantage to request first and last instant from database
  trajectoryDB.timeInterval(firstInstant, lastInstant, id);
}

bool FeatureTrajectory::isDisplacementSmall(const unsigned int& nDisplacements, const float& minTotalFeatureDisplacement) const {
  bool result = false;
  unsigned int nPositions = positions->size();
  if (nPositions > nDisplacements) {
    float disp = 0;
    for (unsigned int i=0; i<nDisplacements; i++)
      disp += displacementDistances[nPositions-2-i];
    result = disp < minTotalFeatureDisplacement;
  }
  return result;
}

bool FeatureTrajectory::isMotionSmooth(const int& accelerationBound, const int& deviationBound) const {
  bool result = true;
  unsigned int nPositions = positions->size();
  if (nPositions >= 3) {
    float ratio;
    if (displacementDistances[nPositions-2] > displacementDistances[nPositions-3])
      ratio = displacementDistances[nPositions-2] / displacementDistances[nPositions-3];
    else
      ratio = displacementDistances[nPositions-3] / displacementDistances[nPositions-2];

    float cosine = scalarProduct((*velocities)[nPositions-3],(*velocities)[nPositions-2]) / (displacementDistances[nPositions-3] * displacementDistances[nPositions-2]);
    
    result &= (ratio < accelerationBound) & (cosine > deviationBound);
  }
  return result;
}

bool FeatureTrajectory::minMaxSimilarity(const FeatureTrajectory& ft, const int& firstInstant, const int& lastInstant, const float& connectionDistance, const float& segmentationDistance) {
  float minDistance = norm(positions->getPointAtInstant(firstInstant)-ft.positions->getPointAtInstant(firstInstant));
  float maxDistance = minDistance;
  bool connected = (minDistance <= connectionDistance);
  int t=firstInstant+1;
  while (t <= lastInstant && connected) {
    float distance = norm(positions->getPointAtInstant(t)-ft.positions->getPointAtInstant(t));
    if (distance < minDistance)
      minDistance = distance;
    else if (distance > maxDistance)
      maxDistance = distance;
    connected = connected && (maxDistance-minDistance <= segmentationDistance);
    t++;
  }

  return connected;
}

void FeatureTrajectory::addPoint(const unsigned int& frameNum, const Point2f& p, const Mat& homography) {
  Point2f pp = p;
  if (!homography.empty())
    pp = project(p, homography);
  positions->add(frameNum, pp);
  if (frameNum < firstInstant)
    firstInstant = frameNum;
  if (frameNum > lastInstant)
    lastInstant = frameNum;
  computeMotionData(frameNum);
  assert(positions->size() == displacementDistances.size()+1);
  assert(positions->size() == velocities->size()+1);
}

void FeatureTrajectory::shorten(void) { 
  positions->pop_back(); 
  velocities->pop_back(); 
  displacementDistances.pop_back();
}

void FeatureTrajectory::write(TrajectoryDBAccess<Point2f>& trajectoryDB, const string& positionsTableName, const string& velocitiesTableName) const {
  trajectoryDB.write(*positions, positionsTableName);
  trajectoryDB.write(*velocities, velocitiesTableName);
}

#ifdef USE_OPENCV
/// \todo add option for anti-aliased drawing, thickness
void FeatureTrajectory::draw(Mat& img, const Mat& homography, const Scalar& color) const {
  Point2f p1, p2;
  if (!homography.empty())
    p1 = project((*positions)[0], homography);
  else
    p1 = (*positions)[0];
  for (unsigned int i=1; i<positions->size(); i++) {
    if (!homography.empty())
      p2 = project((*positions)[i], homography);
    else
      p2 = (*positions)[i];
    line(img, p1, p2, color, 1);
    p1 = p2;
  }
}
#endif

// protected

void FeatureTrajectory::computeMotionData(const int& frameNum) {
  unsigned int nPositions = positions->size();
  if (nPositions >= 2) {
    Point2f displacement = (*positions)[nPositions-1] - (*positions)[nPositions-2];
    //if (nPositions == 2) // duplicate first displacement so that positions and velocities have the same length
    //velocities.add(frameNum-1, displacement);
    velocities->add(frameNum, displacement);
    float dist = norm(displacement);
    displacementDistances.push_back(dist);
  }
}

/******************** FeatureGraph ********************/

void FeatureGraph::addFeature(const FeatureTrajectoryPtr& ft) {
  vertex_descriptor newVertex = add_vertex(graph);
  graph[newVertex].feature = ft;
  for (graph_traits<UndirectedGraph>::vertex_iterator vi = vertices(graph).first; 
       vi!=vertices(graph).second; ++vi) { // vi pointer to vertex_descriptor
    FeatureTrajectoryPtr ft2 = graph[*vi].feature;
    if (newVertex != *vi) {
      int lastInstant = static_cast<int>(MIN(ft->getLastInstant(), ft2->getLastInstant()));
      int firstInstant = static_cast<int>(MAX(ft->getFirstInstant(), ft2->getFirstInstant()));
      if (lastInstant-firstInstant > static_cast<int>(minFeatureTime)) { // equivalent to lastInstant-firstInstant+1 >= minFeatureTime
	if (ft->minMaxSimilarity(*ft2, firstInstant, lastInstant, connectionDistance, segmentationDistance)) {
	  UndirectedGraph::edge_descriptor e;
	  bool unused;
	  tie(e, unused) = add_edge(newVertex, *vi, graph);
	  // no need to add measures to graph[e] (edge properties)
	}
      }
    }
  }
}

void FeatureGraph::connectedComponents(const unsigned int& lastInstant) {
  computeVertexIndex();
  property_map<UndirectedGraph, int VertexInformation::*>::type components = get(&VertexInformation::index, graph);

  int num = connected_components(graph, components, vertex_index_map(get(&VertexInformation::index, graph)));
#ifdef DEBUG
  cout << "last instant threshold " << lastInstant << " Total number of components: " << num << endl;
#endif

  vector<unsigned int> lastInstants(num, 0); // last instant of component with id
  vector<vector<vertex_descriptor> > tmpobjects(num), objects; // vector of components (component = vector of vertex descriptors)

  graph_traits<UndirectedGraph>::vertex_iterator vi, vend;
  for(tie(vi,vend) = vertices(graph); vi != vend; ++vi) {
    unsigned int id = components[*vi];
    lastInstants[id] = max(lastInstants[id], graph[*vi].feature->getLastInstant());
    tmpobjects[id].push_back(*vi);
  }

  objectHypotheses.clear();
  for (int i = 0; i < num; ++i) {
#ifdef DEBUG
    cout << i << " " << lastInstants[i] << endl;
#endif
    if (lastInstants[i] < lastInstant)
      objectHypotheses.push_back(tmpobjects[i]);
  }
}

void FeatureGraph::getFeatureGroups(vector<vector<FeatureTrajectoryPtr> >& featureGroups) {
  featureGroups.clear();

  for (unsigned int i=0; i<objectHypotheses.size(); ++i) {
    // check that there is on average at least minNFeaturesPerGroup features at each frame in the group
    unsigned int totalFeatureTime= graph[objectHypotheses[i][0]].feature->length();
    unsigned int firstInstant = graph[objectHypotheses[i][0]].feature->getFirstInstant();
    unsigned int lastInstant = graph[objectHypotheses[i][0]].feature->getLastInstant();
      for (unsigned int j=1; j<objectHypotheses[i].size(); ++j) {
	totalFeatureTime += graph[objectHypotheses[i][j]].feature->length();
	firstInstant = MIN(firstInstant, graph[objectHypotheses[i][j]].feature->getFirstInstant());
	lastInstant = MAX(lastInstant, graph[objectHypotheses[i][j]].feature->getLastInstant());	
      }
    if (static_cast<float>(totalFeatureTime)/static_cast<float>(lastInstant-firstInstant+1) > minNFeaturesPerGroup) {
#if DEBUG
      cout << "save group " << i << " of " << objectHypotheses[i].size() << " features " << endl;
#endif
      featureGroups.push_back(vector<FeatureTrajectoryPtr>());
      for (unsigned int j=0; j<objectHypotheses[i].size(); ++j) {
	featureGroups.back().push_back(graph[objectHypotheses[i][j]].feature);
#if DEBUG
	cout << featureGroups.size() << " " << objectHypotheses[i][j] << endl;
#endif
	clear_vertex(objectHypotheses[i][j], graph);
	remove_vertex(objectHypotheses[i][j], graph);
      }
    }
  }
}

string FeatureGraph::informationString(void) const {
  stringstream ss;
  ss << num_vertices(graph) << " vertices, " << num_edges(graph) << " edges";
  return ss.str();
}

int FeatureGraph::getNVertices(void) const { return num_vertices(graph);}

int FeatureGraph::getNEdges(void) const  { return num_edges(graph);}

void FeatureGraph::computeVertexIndex(void) {
  graph_traits<FeatureGraph::UndirectedGraph>::vertex_iterator vi, vend;
  graph_traits<FeatureGraph::UndirectedGraph>::vertices_size_type cnt = 0;
  for(tie(vi,vend) = vertices(graph); vi != vend; ++vi)
    graph[*vi].index = cnt++;
}