Darwin  1.10(beta)
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
drwnNNGraphMoves.h
1 /*****************************************************************************
2 ** DARWIN: A FRAMEWORK FOR MACHINE LEARNING RESEARCH AND DEVELOPMENT
3 ** Distributed under the terms of the BSD license (see the LICENSE file)
4 ** Copyright (c) 2007-2015, Stephen Gould
5 ** All rights reserved.
6 **
7 ******************************************************************************
8 ** FILENAME: drwnNNGraphMoves.h
9 ** AUTHOR(S): Stephen Gould <stephen.gould@anu.edu.au>
10 **
11 *****************************************************************************/
12 
13 #pragma once
14 
15 #include "drwnNNGraph.h"
16 
17 using namespace std;
18 using namespace Eigen;
19 
20 // drwnNNGraph node distance metrics -----------------------------------------
23 
25  public:
26  bool isMatchable(const drwnNNGraphNode& a) const { return true; }
27  bool isFinite(const drwnNNGraphNode& a, const drwnNNGraphNode& b) const { return true; }
28  float score(const drwnNNGraphNode& a, const drwnNNGraphNode& b) const {
29  return (a.features - b.features).squaredNorm();
30  //return a.squaredNorm + b.squaredNorm - 2.0 * a.features.dot(b.features);
31  }
32 };
33 
35  public:
36  bool isMatchable(const drwnNNGraphNode& a) const { return true; }
37  bool isFinite(const drwnNNGraphNode& a, const drwnNNGraphNode& b) const {
38  return (a.label == b.label) || (a.label == -1) || (b.label == -1);
39  }
40  float score(const drwnNNGraphNode& a, const drwnNNGraphNode& b) const {
41  return (a.features - b.features).squaredNorm();
42  }
43 };
44 
46  public:
47  bool isMatchable(const drwnNNGraphNode& a) const { return true; }
48  bool isFinite(const drwnNNGraphNode& a, const drwnNNGraphNode& b) const {
49  return (a.label != b.label);
50  }
51  float score(const drwnNNGraphNode& a, const drwnNNGraphNode& b) const {
52  return (a.features - b.features).squaredNorm();
53  }
54 };
55 
57  public:
58  bool isMatchable(const drwnNNGraphNode& a) const { return (a.label != -1); }
59  bool isFinite(const drwnNNGraphNode& a, const drwnNNGraphNode& b) const {
60  return (a.label == b.label) && (a.label != -1);
61  }
62  float score(const drwnNNGraphNode& a, const drwnNNGraphNode& b) const {
63  return (a.features - b.features).squaredNorm();
64  }
65 };
66 
68  public:
69  bool isMatchable(const drwnNNGraphNode& a) const { return (a.label != -1); }
70  bool isFinite(const drwnNNGraphNode& a, const drwnNNGraphNode& b) const {
71  return (a.label != b.label) && (a.label != -1) && (b.label != -1);
72  }
73  float score(const drwnNNGraphNode& a, const drwnNNGraphNode& b) const {
74  return (a.features - b.features).squaredNorm();
75  }
76 };
77 
78 // drwnNNGraphMoves ----------------------------------------------------------
80 
81 namespace drwnNNGraphMoves {
84  template<class DistanceMetric>
85  void initialize(drwnNNGraph& graph, const DistanceMetric& M);
86 
89  inline void initialize(drwnNNGraph& graph) { initialize(graph, drwnNNGraphDefaultMetric()); }
90 
93  template<class DistanceMetric>
94  void initialize(drwnNNGraph& graph, unsigned imgIndx, const DistanceMetric& M);
95 
98  template<class DistanceMetric>
99  double rescore(drwnNNGraph& graph, const DistanceMetric& M);
100 
103  inline double rescore(drwnNNGraph& graph) { return rescore(graph, drwnNNGraphDefaultMetric()); }
104 
107  template<class DistanceMetric>
108  double rescore(drwnNNGraph& graph, unsigned imgIndx, const DistanceMetric& M);
109 
112  template<class DistanceMetric>
113  void flann(drwnNNGraph& graph, const DistanceMetric& M);
114 
117  template<class DistanceMetric>
118  void update(drwnNNGraph& graph, const DistanceMetric& M);
119 
122  inline void update(drwnNNGraph& graph) { update(graph, drwnNNGraphDefaultMetric()); }
123 
126  template<class DistanceMetric>
127  bool propagate(drwnNNGraph& graph, const drwnNNGraphNodeIndex& u, const DistanceMetric& M);
128 
131  template<class DistanceMetric>
132  bool search(drwnNNGraph& graph, const drwnNNGraphNodeIndex& u, const DistanceMetric& M);
133 
136  template<class DistanceMetric>
137  bool local(drwnNNGraph& graph, const drwnNNGraphNodeIndex& u, const DistanceMetric& M);
138 
142  template<class DistanceMetric>
143  bool randproj(drwnNNGraph& graph, const DistanceMetric& M);
144 
147  template<class DistanceMetric>
148  bool enrichment(drwnNNGraph& graph, const DistanceMetric& M);
149 
152  template<class DistanceMetric>
153  bool exhaustive(drwnNNGraph& graph, const drwnNNGraphNodeIndex& u, const DistanceMetric& M);
154 };
155 
156 // drwnNNGraphMoves implementation -------------------------------------------
157 
158 template<class DistanceMetric>
159 void drwnNNGraphMoves::initialize(drwnNNGraph& graph, const DistanceMetric& M)
160 {
161  for (unsigned imgIndx = 0; imgIndx < graph.numImages(); imgIndx++) {
162  if (!graph[imgIndx].bSourceMatchable) {
163  DRWN_LOG_DEBUG("...skipping initialization of " << graph[imgIndx].name());
164  continue;
165  }
166 
167  initialize(graph, imgIndx, M);
168  }
169 }
170 
171 template<class DistanceMetric>
172 void drwnNNGraphMoves::initialize(drwnNNGraph& graph, unsigned imgIndx, const DistanceMetric& M)
173 {
174  DRWN_FCN_TIC;
175  DRWN_LOG_DEBUG("...initializing " << graph[imgIndx].name());
176 
177  // random number generator state
178 #if !(defined(_WIN32)||defined(WIN32)||defined(__WIN32__)||defined(__VISUALC__))
179  unsigned short int xsubi[3] = {0, 0, 0};
180  xsubi[0] = random();
181 #endif
182 
183  // initialize image indexes for fast subsampling
184  vector<unsigned> indexes;
185  indexes.reserve(graph.numImages() - 1);
186  for (unsigned i = 0; i < graph.numImages(); i++) {
187  if (!graph[i].bTargetMatchable) continue;
188  if (graph.inSameEqvClass(i, imgIndx)) continue;
189  indexes.push_back(i);
190  }
191 
192  bool bLessMatches = false;
193  const unsigned kMatches = std::min((unsigned)indexes.size(), drwnNNGraph::K);
194 
195  // randomly match all superpixels in the image
196  for (unsigned segId = 0; segId < graph[imgIndx].numNodes(); segId++) {
197  // check if node is matchable
198  if (!M.isMatchable(graph[imgIndx][segId])) continue;
199 
200  // check for existing edges (i.e., image is being re-initialized)
201  const bool bHasExistingMatches = !graph[imgIndx][segId].edges.empty();
202 
203 #if 0
204  drwn::shuffle(indexes);
205 #elif defined(_WIN32)||defined(WIN32)||defined(__WIN32__)||defined(__VISUALC__)
206  drwn::shuffle(indexes);
207 #else
208  {
209  // thread safe shuffle
210  unsigned int seed = (unsigned int)erand48(xsubi);
211  const size_t n = indexes.size();
212  for (size_t i = 0; i < n - 1; i++) {
213  size_t j = rand_r(&seed) % (n - i);
214  std::swap(indexes[i], indexes[i + j]);
215  }
216  }
217 #endif
218  unsigned matchesAdded = 0;
219  for (unsigned k = 0; k < indexes.size(); k++) {
220 
221  // determine valid nodes to match against
222  vector<unsigned> segIndexes;
223  segIndexes.reserve(graph[indexes[k]].numNodes());
224  for (unsigned i = 0; i < graph[indexes[k]].numNodes(); i++) {
225  if (M.isFinite(graph[imgIndx][segId], graph[indexes[k]][i])) {
226  segIndexes.push_back(i);
227  }
228  }
229  if (segIndexes.empty()) continue;
230 
231  drwnNNGraphEdge e;
232  e.targetNode.imgIndx = indexes[k];
233 #if defined(_WIN32)||defined(WIN32)||defined(__WIN32__)||defined(__VISUALC__)
234  e.targetNode.segId = (uint16_t)segIndexes[drand48() * segIndexes.size()];
235 #else
236  e.targetNode.segId = (uint16_t)segIndexes[erand48(xsubi) * segIndexes.size()];
237 #endif
238  e.weight = M.score(graph[imgIndx][segId], graph[e.targetNode]);
239  graph[imgIndx][segId].edges.push_back(e);
240 
241  // check if we've added the required number of matches
242  if (++matchesAdded >= kMatches)
243  break;
244  }
245 
246  // check if not enough matches were found
247  bLessMatches = bLessMatches || (graph[imgIndx][segId].edges.size() < kMatches);
248 
249  // remove excess matches if existing
250  if (bHasExistingMatches) {
251  // remove duplicate target images
252  graph[imgIndx][segId].edges.sort(drwnNNGraphSortByImage());
253  drwnNNGraphEdgeList::iterator kt = graph[imgIndx][segId].edges.begin();
254  drwnNNGraphEdgeList::iterator jt = kt++;
255  while (kt != graph[imgIndx][segId].edges.end()) {
256  if (kt->targetNode.imgIndx == jt->targetNode.imgIndx) {
257  kt = graph[imgIndx][segId].edges.erase(kt);
258  } else {
259  jt = kt++;
260  }
261  }
262  }
263 
264  // sort matches from best to worst
265  graph[imgIndx][segId].edges.sort();
266 
267  // resize to correct number of matches per node
268  if (bHasExistingMatches) {
269  graph[imgIndx][segId].edges.resize(kMatches);
270  }
271  }
272 
273  // issue warning for too few matches
274  if (bLessMatches) {
275  DRWN_LOG_WARNING("some nodes in " << graph[imgIndx].name()
276  << " have out-degree less than " << drwnNNGraph::K);
277  }
278 
279  DRWN_FCN_TOC;
280 }
281 
282 template<class DistanceMetric>
283 double drwnNNGraphMoves::rescore(drwnNNGraph& graph, const DistanceMetric& M)
284 {
285  DRWN_FCN_TIC;
286  double energy = 0.0;
287 
288  for (unsigned imgIndx = 0; imgIndx < graph.numImages(); imgIndx++) {
289  energy += rescore(graph, imgIndx, M);
290  }
291 
292  DRWN_FCN_TOC;
293  return energy;
294 }
295 
296 template<class DistanceMetric>
297 double drwnNNGraphMoves::rescore(drwnNNGraph& graph, unsigned imgIndx, const DistanceMetric& M)
298 {
299  double energy = 0.0;
300 
301  for (unsigned segId = 0; segId < graph[imgIndx].numNodes(); segId++) {
302  drwnNNGraphEdgeList& e = graph[imgIndx][segId].edges;
303  for (drwnNNGraphEdgeList::iterator kt = e.begin(); kt != e.end(); ++kt) {
304  if (M.isFinite(graph[imgIndx][segId], graph[kt->targetNode])) {
305  kt->weight = M.score(graph[imgIndx][segId], graph[kt->targetNode]);
306  energy += kt->weight;
307  } else {
309  DRWN_TODO;
310  }
311  }
312  e.sort();
313  }
314 
315  return energy;
316 }
317 
318 template<class DistanceMetric>
319 void drwnNNGraphMoves::flann(drwnNNGraph& graph, const DistanceMetric& M)
320 {
321  DRWN_FCN_TIC;
322 
323  // extract features to match
324  const size_t numFeatures = graph[0][0].features.size();
325  vector<drwnNNGraphNodeIndex> sampleIndexes;
326  for (unsigned i = 0; i < graph.numImages(); i++) {
327  if (!graph[i].bTargetMatchable) continue;
328 
329  sampleIndexes.reserve(sampleIndexes.size() + graph[i].numNodes());
330  for (unsigned j = 0; j < graph[i].numNodes(); j++) {
331  sampleIndexes.push_back(drwnNNGraphNodeIndex(i, j));
332  }
333  }
334 
335  cv::Mat features(sampleIndexes.size(), numFeatures, CV_32FC1);
336  for (unsigned i = 0; i < sampleIndexes.size(); i++) {
337  for (unsigned d = 0; d < numFeatures; d++) {
338  features.at<float>(i, d) = graph[sampleIndexes[i]].features[d];
339  }
340  }
341 
342  // build kd-tree
343  cv::flann::KDTreeIndexParams indexParams;
344  cv::flann::Index kdtree(features, indexParams);
345 
346  // run queries
347  for (unsigned imgIndx = 0; imgIndx < graph.numImages(); imgIndx++) {
348  if (!graph[imgIndx].bSourceMatchable) continue;
349 
350  const unsigned numQueries = graph[imgIndx].numNodes();
351  cv::Mat queries(numQueries, numFeatures, CV_32FC1);
352  for (unsigned i = 0; i < numQueries; i++) {
353  for (unsigned d = 0; d < numFeatures; d++) {
354  queries.at<float>(i, d) = graph[imgIndx][i].features[d];
355  }
356  }
357 
358  cv::Mat indexes(numQueries, drwnNNGraph::K, CV_32SC1);
359  cv::Mat dists(numQueries, drwnNNGraph::K, CV_32FC1);
360  kdtree.knnSearch(queries, indexes, dists, drwnNNGraph::K, cv::flann::SearchParams(64));
361 
362  // extract nearest neighbours
363  for (unsigned i = 0; i < numQueries; i++) {
364  const drwnNNGraphNodeIndex u(imgIndx, i);
365  for (unsigned k = 0; k < drwnNNGraph::K; k++) {
366  const drwnNNGraphNodeIndex v(sampleIndexes[indexes.at<int>(i, k)]);
367  if (graph.inSameEqvClass(u.imgIndx, v.imgIndx)) continue;
368  if (!M.isMatchable(graph[v])) continue;
369  if (M.isFinite(graph[u], graph[v])) {
370  const float w = M.score(graph[u], graph[v]);
371  graph[u].insert(drwnNNGraphEdge(v, w));
372  }
373  }
374  }
375  }
376 
377  DRWN_FCN_TOC;
378 }
379 
380 template<class DistanceMetric>
381 void drwnNNGraphMoves::update(drwnNNGraph& graph, const DistanceMetric& M)
382 {
383  DRWN_FCN_TIC;
384 
385  // try improve a bad match (from a random (active) image)
386  if (drwnNNGraph::DO_EXHAUSTIVE > 0) {
387  // sum scores of nodes from active images
388  double totalWeight = 0.0;
389  for (unsigned imgIndx = 0; imgIndx < graph.numImages(); imgIndx++) {
390  if (!graph[imgIndx].bSourceMatchable) continue;
391  for (unsigned segId = 0; segId < graph[imgIndx].numNodes(); segId++) {
392  const drwnNNGraphEdgeList& e = graph[imgIndx][segId].edges;
393  if (e.empty()) continue;
394 
395  totalWeight += (double)e.front().weight;
396  }
397  }
398 
399  // randomly sample n nodes
400  set<drwnNNGraphNodeIndex> sampledNodes;
401  for (int i = 0; i < drwnNNGraph::DO_EXHAUSTIVE; i++) {
402 
403  // sample the weight
404  double weight = totalWeight * drand48();
405 
406  // find the node
407  for (unsigned imgIndx = 0; imgIndx < graph.numImages(); imgIndx++) {
408  if (!graph[imgIndx].bSourceMatchable) continue;
409  for (unsigned segId = 0; segId < graph[imgIndx].numNodes(); segId++) {
410  const drwnNNGraphEdgeList& e = graph[imgIndx][segId].edges;
411  if (e.empty()) continue;
412 
413  // skip if already sampled this node
414  if (sampledNodes.find(drwnNNGraphNodeIndex(imgIndx, segId)) != sampledNodes.end())
415  continue;
416 
417  weight -= (double)e.front().weight;
418  if (weight <= 0.0) {
419  // add node to set of samples and remove from totalWeight
420  sampledNodes.insert(drwnNNGraphNodeIndex(imgIndx, segId));
421  totalWeight -= (double)e.front().weight;
422  break;
423  }
424  }
425  if (weight <= 0.0) break;
426  }
427  }
428 
429  // run exhaustive search on nodes
430  for (set<drwnNNGraphNodeIndex>::const_iterator it = sampledNodes.begin(); it != sampledNodes.end(); ++it) {
431  exhaustive(graph, *it, M);
432  }
433  }
434 
435  // random projection
436  if (drwnNNGraph::DO_RANDPROJ > 0) {
437  randproj(graph, M);
438  }
439 
440  // propagation moves
443  for (u.imgIndx = 0; u.imgIndx < graph.numImages(); u.imgIndx++) {
444  // check if image is in the active set
445  if (!graph[u.imgIndx].bSourceMatchable) {
446  DRWN_LOG_DEBUG("...skipping " << graph[u.imgIndx].name());
447  continue;
448  }
449 
450  // perform update on each node
451  for (u.segId = 0; u.segId < graph[u.imgIndx].numNodes(); u.segId++) {
452  if (drwnNNGraph::DO_LOCAL) {
453  local(graph, u, M);
454  }
456  propagate(graph, u, M);
457  }
459  search(graph, u, M);
460  }
461  }
462  }
463  }
464 
465  // enrichment (forward and inverse)
467  enrichment(graph, M);
468  }
469 
470  DRWN_FCN_TOC;
471 }
472 
473 template<class DistanceMetric>
474 bool drwnNNGraphMoves::propagate(drwnNNGraph& graph, const drwnNNGraphNodeIndex& u, const DistanceMetric& M)
475 {
476  drwnNNGraphEdgeList& el = graph[u].edges;
477  if (el.empty()) return false;
478 
479  bool bChanged = false;
480  for (drwnNNGraphEdgeList::iterator kt = el.begin(); kt != el.end(); ++kt) {
481 
482  // check if match has already been processed in a previous iteration
483  if (kt->status == DRWN_NNG_DIRTY) {
484  kt->status = DRWN_NNG_PROCESSED_ONCE;
485  } else if (kt->status == DRWN_NNG_PROCESSED_ONCE) {
486  kt->status = DRWN_NNG_PROCESSED_TWICE;
487  continue;
488  } else continue;
489 
490  // iterate around neighbours of (u,v)
491  const drwnNNGraphNodeIndex& v(kt->targetNode);
492 
493  for (set<uint16_t>::const_iterator ut = graph[u].spatialNeighbours.begin();
494  ut != graph[u].spatialNeighbours.end(); ++ut) {
495 
496  // check if this node has edges associated with it
497  if (graph[u.imgIndx][*ut].edges.empty()) continue;
498 
499  // look at all neighbours of current edge
500  for (set<uint16_t>::const_iterator vt = graph[v].spatialNeighbours.begin();
501  vt != graph[v].spatialNeighbours.end(); ++vt) {
502 
503  // check that edge is legal
504  if (!M.isFinite(graph[u.imgIndx][*ut], graph[v.imgIndx][*vt]))
505  continue;
506 
507  // score the edge
508  const float w = M.score(graph[u.imgIndx][*ut], graph[v.imgIndx][*vt]);
509  const drwnNNGraphEdge e(drwnNNGraphNodeIndex(v.imgIndx, *vt), w);
510  if (graph[u.imgIndx][*ut].insert(e)) {
511  bChanged = true;
512  }
513  }
514  }
515  }
516 
517  return bChanged;
518 }
519 
520 template<class DistanceMetric>
521 bool drwnNNGraphMoves::search(drwnNNGraph& graph, const drwnNNGraphNodeIndex& u, const DistanceMetric& M)
522 {
523  // construct list of valid target images
524  vector<unsigned> validIndexes;
525  validIndexes.reserve(graph.numImages() - 1);
526  for (unsigned imgIndx = 0; imgIndx < graph.numImages(); imgIndx++) {
527  if (!graph[imgIndx].bTargetMatchable) continue;
528  if (!graph.inSameEqvClass(imgIndx, u.imgIndx)) {
529  validIndexes.push_back(imgIndx);
530  }
531  }
532 
533  if (validIndexes.empty()) return false;
534 
535  // randomly sample a valid segment from a valid image
536  drwnNNGraphEdge e;
537  e.targetNode.imgIndx = validIndexes[(unsigned)(drand48() * validIndexes.size())];
538 
539  // determine valid nodes to match against
540  vector<unsigned> segIndexes;
541  segIndexes.reserve(graph[e.targetNode.imgIndx].numNodes());
542  for (unsigned i = 0; i < graph[e.targetNode.imgIndx].numNodes(); i++) {
543  if (M.isFinite(graph[u], graph[e.targetNode.imgIndx][i])) {
544  segIndexes.push_back(i);
545  }
546  }
547  if (segIndexes.empty()) return false;
548 
549  e.targetNode.segId = (uint16_t)segIndexes[drand48() * segIndexes.size()];
550  e.weight = M.score(graph[u], graph[e.targetNode]);
551  const bool bChanged = graph[u].insert(e);
552 
553  return bChanged;
554 }
555 
556 template<class DistanceMetric>
557 bool drwnNNGraphMoves::local(drwnNNGraph& graph, const drwnNNGraphNodeIndex& u, const DistanceMetric& M)
558 {
559  drwnNNGraphEdgeList& el = graph[u].edges;
560 
561  bool bChanged = false;
562  for (drwnNNGraphEdgeList::iterator kt = el.begin(); kt != el.end(); ++kt) {
563  if (kt->status != DRWN_NNG_DIRTY) continue;
564 
565  const drwnNNGraphNodeIndex v(kt->targetNode);
566  for (set<uint16_t>::const_iterator it = graph[v].spatialNeighbours.begin();
567  it != graph[v].spatialNeighbours.end(); ++it) {
568 
569  if (!M.isFinite(graph[u], graph[v.imgIndx][*it]))
570  continue;
571 
572  const float w = M.score(graph[u], graph[v.imgIndx][*it]);
573  if (w < kt->weight) {
574  kt->weight = w;
575  kt->targetNode.segId = *it;
576  kt->status = DRWN_NNG_DIRTY;
577  bChanged = true;
578  }
579  }
580  }
581 
582  // sort edges by weight
583  if (bChanged) {
584  el.sort(drwnNNGraphSortByScore());
585  }
586 
587  return bChanged;
588 }
589 
590 template<class DistanceMetric>
591 bool drwnNNGraphMoves::randproj(drwnNNGraph& graph, const DistanceMetric& M)
592 {
593  DRWN_FCN_TIC;
594  bool bChanged = false;
595 
596  // pick a random direction
597  VectorXf mu(graph[0][0].features.size());
598  for (int i = 0; i < mu.rows(); i++) {
599  mu[i] = float(drand48() - 0.5);
600  }
601  mu /= mu.norm();
602 
603  // project all superpixels
604  vector<pair<float, drwnNNGraphNodeIndex> > projections;
605  projections.reserve(graph.numNodes());
606 
608  for (u.imgIndx = 0; u.imgIndx < graph.numImages(); u.imgIndx++) {
609  for (u.segId = 0; u.segId < graph[u.imgIndx].numNodes(); u.segId++) {
610  const float x = mu.dot(graph[u].features);
611  projections.push_back(make_pair(x, u));
612  }
613  }
614 
615  // sort
616  sort(projections.begin(), projections.end());
617 
618  // score
619  unsigned dbCountChanged = 0;
620  unsigned inext = 0;
621  for (unsigned i = 0; i < projections.size() - 1; i++) {
622  const drwnNNGraphNodeIndex &u = projections[i].second;
623 
624  // don't update inactive images
625  if (!graph[u.imgIndx].bSourceMatchable || graph[u].edges.empty()) {
626  if (inext <= i) inext += 1;
627  continue;
628  }
629 
630  // skip if images are in the same equivalence class or not matchable
631  while (inext != projections.size()) {
632  if (graph[projections[inext].second.imgIndx].bTargetMatchable &&
633  !graph.inSameEqvClass(u.imgIndx, projections[inext].second.imgIndx))
634  break;
635  inext += 1;
636  }
637 
638  if (inext == projections.size()) break;
639 
640  float uworst = sqrt(graph[u].edges.back().weight);
641  for (unsigned j = inext; j < std::min(projections.size(), (size_t)(inext + drwnNNGraph::DO_RANDPROJ)); j++) {
642  // cauchy-schwarz check
643  if ((projections[j].first - projections[i].first) >= uworst) {
644  break;
645  }
646 
647  // equivalence class check
648  if (!graph[projections[j].second.imgIndx].bTargetMatchable) continue;
649  if (graph.inSameEqvClass(u.imgIndx, projections[j].second.imgIndx))
650  continue;
651 
652  // evaluate edge candidate
653  const drwnNNGraphNodeIndex &v = projections[j].second;
654 
655  if (M.isFinite(graph[u], graph[v])) {
656  const float w = M.score(graph[u], graph[v]);
657  if (graph[u].insert(drwnNNGraphEdge(v, w))) {
658  uworst = sqrt(graph[u].edges.back().weight);
659  dbCountChanged += 1;
660  bChanged = true;
661  }
662  }
663  }
664  }
665 
666  DRWN_LOG_DEBUG("randproj() improved " << dbCountChanged << " matches");
667  DRWN_FCN_TOC;
668  return bChanged;
669 }
670 
671 template<class DistanceMetric>
672 bool drwnNNGraphMoves::enrichment(drwnNNGraph& graph, const DistanceMetric& M)
673 {
674  DRWN_FCN_TIC;
675  bool bChanged = false;
676 
677  // inverse enrichment
678  for (unsigned imgIndx = 0; imgIndx < graph.numImages(); imgIndx++) {
679  // skip image if not target matchable
680  if (!graph[imgIndx].bTargetMatchable) continue;
681 
682  for (unsigned segId = 0; segId < graph[imgIndx].numNodes(); segId++) {
683  const drwnNNGraphEdgeList& el = graph[imgIndx][segId].edges;
684  for (drwnNNGraphEdgeList::const_iterator kt = el.begin(); kt != el.end(); ++kt) {
685 
686  // skip if not active
687  if (!graph[kt->targetNode.imgIndx].bSourceMatchable)
688  continue;
689 
690  // evaluate reverse edge
691  const drwnNNGraphEdge e(drwnNNGraphNodeIndex(imgIndx, segId), kt->weight);
692  if (graph[kt->targetNode].insert(e)) {
693  bChanged = true;
694  }
695  }
696  }
697  }
698 
699  // forward enrichment
700  for (unsigned imgIndx = 0; imgIndx < graph.numImages(); imgIndx++) {
701  // only do forward enrichment on active images
702  if (!graph[imgIndx].bSourceMatchable) continue;
703 
704  for (unsigned segId = 0; segId < graph[imgIndx].numNodes(); segId++) {
705  // needed to prevent invalid iterators when updating e
706  const drwnNNGraphEdgeList el(graph[imgIndx][segId].edges);
707 
708  int nCompared = (int)drwnNNGraph::K; // prevent quadratic growth in K
709  for (drwnNNGraphEdgeList::const_iterator it = el.begin(); it != el.end(); ++it) {
710  const drwnNNGraphEdgeList& r = graph[it->targetNode].edges;
711 
712  for (drwnNNGraphEdgeList::const_iterator kt = r.begin(); kt != r.end(); ++kt) {
713  // check that we're not matching back to ourself (or any other
714  // image in the same equivalence class) and is matchable
715  if (!graph[kt->targetNode.imgIndx].bTargetMatchable) continue;
716  if (graph.inSameEqvClass(kt->targetNode.imgIndx, imgIndx)) continue;
717 
718  // check that we have processed this pair previously
719  if ((it->status == DRWN_NNG_PROCESSED_TWICE) &&
720  (kt->status == DRWN_NNG_PROCESSED_TWICE)) continue;
721 
722  // check that edge is legal
723  if (!M.isFinite(graph[imgIndx][segId], graph[kt->targetNode]))
724  continue;
725 
726  const float w = M.score(graph[imgIndx][segId], graph[kt->targetNode]);
727  if (graph[imgIndx][segId].insert(drwnNNGraphEdge(kt->targetNode, w))) {
728  bChanged = true;
729  }
730 
731  nCompared -= 1;
732  if (nCompared < 0) break;
733  }
734 
735  if (nCompared < 0) break;
736  }
737  }
738  }
739 
740  DRWN_FCN_TOC;
741  return bChanged;
742 }
743 
744 template<class DistanceMetric>
745 bool drwnNNGraphMoves::exhaustive(drwnNNGraph& graph, const drwnNNGraphNodeIndex& u, const DistanceMetric& M)
746 {
747  DRWN_FCN_TIC;
748  drwnNNGraphEdgeList& el = graph[u].edges;
749 
750  bool bChanged = false;
751  for (unsigned imgIndx = 0; imgIndx < graph.numImages(); imgIndx++) {
752  if (!graph[imgIndx].bTargetMatchable) continue;
753  if (graph.inSameEqvClass(imgIndx, u.imgIndx)) continue;
754 
755  drwnNNGraphEdge bestEdge;
756  if (!el.empty()) bestEdge = el.back();
757 
758  for (unsigned segId = 0; segId < graph[imgIndx].numNodes(); segId++) {
759 
760  const drwnNNGraphNodeIndex v(imgIndx, segId);
761  if (!M.isFinite(graph[u], graph[v]))
762  continue;
763 
764  const float w = M.score(graph[u], graph[v]);
765  if (w < bestEdge.weight) {
766  bestEdge = drwnNNGraphEdge(v, w);
767  }
768  }
769 
770  if (graph[u].insert(bestEdge)) {
771  bChanged = true;
772  }
773  }
774 
775  DRWN_FCN_TOC;
776  return bChanged;
777 }
static int DO_RANDPROJ
execute random projection move to given horizon
Definition: drwnNNGraph.h:315
bool local(drwnNNGraph &graph, const drwnNNGraphNodeIndex &u, const DistanceMetric &M)
local neighbourhood search around current match for superpixel u (returns true if a better match was ...
Definition: drwnNNGraphMoves.h:557
drwnNNGraphNodeIndex targetNode
index of the target node
Definition: drwnNNGraph.h:84
uint16_t imgIndx
image index for this node
Definition: drwnNNGraph.h:47
uint16_t segId
superpixel identifier this node
Definition: drwnNNGraph.h:48
bool exhaustive(drwnNNGraph &graph, const drwnNNGraphNodeIndex &u, const DistanceMetric &M)
exhaustive search for best match across entire graph — use sparingly (returns true if a better match ...
Definition: drwnNNGraphMoves.h:745
Definition: drwnNNGraph.h:45
Implements the scoring functions needed by the search moves. Required member functions are isMatchabl...
Definition: drwnNNGraphMoves.h:24
bool enrichment(drwnNNGraph &graph, const DistanceMetric &M)
enrichment: inverse (from target to source) and forward (from source to target's target) (returns tru...
Definition: drwnNNGraphMoves.h:672
void reserve(size_t n)
reserve a given number of images (reduced memory allocation)
Definition: drwnNNGraph.h:337
Definition: drwnNNGraphMoves.h:45
bool propagate(drwnNNGraph &graph, const drwnNNGraphNodeIndex &u, const DistanceMetric &M)
propagate good matches from supeprixel u to its neighbours (returns true if a better match was found)...
Definition: drwnNNGraphMoves.h:474
int32_t label
label for this node
Definition: drwnNNGraph.h:133
static bool DO_PROPAGATE
execute propagate move
Definition: drwnNNGraph.h:312
static bool DO_SEARCH
execute random search move
Definition: drwnNNGraph.h:314
bool search(drwnNNGraph &graph, const drwnNNGraphNodeIndex &u, const DistanceMetric &M)
random search across all images for superpixel u (returns true if a better match was found) ...
Definition: drwnNNGraphMoves.h:521
bool randproj(drwnNNGraph &graph, const DistanceMetric &M)
random projection moves that project all superpixels onto a random direction, sort and compare adjace...
Definition: drwnNNGraphMoves.h:591
static unsigned int K
default number of matches per node
Definition: drwnNNGraph.h:311
size_t numNodes() const
number of nodes (superpixels) in the graph
Definition: drwnNNGraph.cpp:814
bool inSameEqvClass(unsigned imgIndxA, unsigned imgIndxB) const
return true if two images are in the same equivalence class
Definition: drwnNNGraph.h:367
Definition: drwnNNGraphMoves.h:67
VectorXf features
features for this node
Definition: drwnNNGraph.h:132
Encapsulates a superpixel node in a drwnNNGraph.
Definition: drwnNNGraph.h:130
Definition: drwnNNGraph.h:114
Definition: drwnNNGraphMoves.h:56
Definition: drwnNNGraph.h:107
size_t numImages() const
number of images in the graph
Definition: drwnNNGraph.h:343
float weight
weight ot score for this edge
Definition: drwnNNGraph.h:85
Encapsulates an outgoing edge in a drwnNNGraph.
Definition: drwnNNGraph.h:82
void update(drwnNNGraph &graph, const DistanceMetric &M)
perform one update iteration (including enrichment and exhaustive) on all active images ...
Definition: drwnNNGraphMoves.h:381
static int DO_EXHAUSTIVE
execute n exhaustive search moves per iteration
Definition: drwnNNGraph.h:317
static bool DO_LOCAL
execute local move
Definition: drwnNNGraph.h:313
double rescore(drwnNNGraph &graph, const DistanceMetric &M)
rescore all matches based on current features (which may have changed since graph was created) ...
Definition: drwnNNGraphMoves.h:283
static bool DO_ENRICHMENT
execute enrichment moves
Definition: drwnNNGraph.h:316
Class for maintaining a nearest neighbour graph over superpixel images. Search moves are implemented ...
Definition: drwnNNGraph.h:309
void flann(drwnNNGraph &graph, const DistanceMetric &M)
update initial edges using FLANN (with possible self-matches in both source and destination matchable...
Definition: drwnNNGraphMoves.h:319
void initialize(drwnNNGraph &graph, const DistanceMetric &M)
randomly initialize edges (matched) for all active images (keeps existing matches unless an improveme...
Definition: drwnNNGraphMoves.h:159
Definition: drwnNNGraphMoves.h:34