Basic Image AlgorithmS Library  2.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
ExampleTracker.cpp
1 /*
2 This file is part of the BIAS library (Basic ImageAlgorithmS).
3 
4 Copyright (C) 2003-2009 (see file CONTACT for details)
5  Multimediale Systeme der Informationsverarbeitung
6  Institut fuer Informatik
7  Christian-Albrechts-Universitaet Kiel
8 
9 
10 BIAS is free software; you can redistribute it and/or modify
11 it under the terms of the GNU Lesser General Public License as published by
12 the Free Software Foundation; either version 2.1 of the License, or
13 (at your option) any later version.
14 
15 BIAS is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Lesser General Public License for more details.
19 
20 You should have received a copy of the GNU Lesser General Public License
21 along with BIAS; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24 
25 
26 /** @example ExampleTracker.cpp
27  @brief This little example demonstrates the most simple case of usage of the
28  Tracker class. It also demonstrates that it is neccessary to extract the
29  tracking information and store them externally.
30  @ingroup g_examples
31  @author woelk */
32 
33 
34 #include <bias_config.h>
35 
36 #include <Base/Common/BIASpragma.hh>
37 
38 #include <Matcher2D/Tracker.hh>
39 #include <Base/ImageUtils/ImageDraw.hh>
40 #include <Base/Image/ImageConvert.hh>
41 #include <Base/Image/ImageIO.hh>
42 #include <Image/PyramidImage.hh>
43 #include <FeatureDetector/CornerDetectorKLT.hh>
44 #include <Base/Math/Random.hh>
45 
46 #define TIMING
47 #ifdef TIMING
48 #include <Base/Debug/TimeMeasure.hh>
49 #endif
50 
51 #include <deque>
52 
53 using namespace BIAS;
54 using namespace std;
55 
56 
57 #define StorageType float
58 #define CalculationType float
59 
60 //#define DEBUG false
61 #define DEBUG 1
62 
63 // the resulting data structs
64 deque<vector<HomgPoint2D> > p; // the corners
65 deque<vector<KLT_TYPE> > residui; // mean absolute grey difference
66 /* int representing success (=0) or failure reason (<0) see
67  TrackerBase::Track() for details */
68 deque<vector<int> > results;
69 // covariance matrices
70 deque<vector<Matrix<double> > > cov;
71 
72 // temporary vector for the
73 vector<HomgPoint2D> mp;
74 vector<QUAL> qual; // the displacement in the last iteration step
75 vector<HomgPoint2D> cp;
76 vector<KLT_TYPE> r; //
77 vector<int> res;
78 vector<Matrix<double> > tmp_cov;
79 
81 
82 const int max_num_points = 150;
83 
84 /** Draw the tracks with a circle around the actual corner position.
85  The covariances are visualized as ellipses. */
86 void Draw(Image<StorageType>& stim, Image<unsigned char>& im,
87  deque<vector<HomgPoint2D> >& p,
88  deque<vector<Matrix<double> > >& cov, int imnum)
89 {
90  // should the tracks be drawn as trailing lines ?
91  bool draw_tracks=true;
92  bool draw_cov=false;
93 
94  // draw the 3 sigma ellipsoid scaled with factor 100
95  double cov_scale=3e2;
96 
98 
99  if (st_type==ImageBase::ST_unsignedshortint){
100  Image<StorageType> scaled = stim;
101  scaled.ScaleShift(255.0/4095.0, 0.0);
103  } else {
105  }
107 
108  unsigned char col[]={255, 0, 0};
109  unsigned char colgreen[]={0, 255, 0};
110  const int nump=p[0].size();
111  unsigned start[2], end[2];
112  const unsigned radius=1;
113  int maxhist=0;
114  double center[2], a[2], b[2], eva, evb;
115  Vector2<double> na, nb;
116 
117  if (imnum>0){
118  for (int l=0; l<nump; l++){
119  if (!p[imnum][l].IsAtInfinity() && !p[imnum-1][l].IsAtInfinity()){
120  if (draw_cov){
121  center[0]=p[imnum][l][0];
122  center[1]=p[imnum][l][1];
123  Matrix2x2<KLT_TYPE> curcov;
124 
125  // we will get a nxn covariance matrix, depending on the number
126  // of parameters. dx,dy is always the last rows/cols in the
127  // covariance matrix, therefore copy these data for vizualization
128  int covoffset = cov[imnum][l].GetRows()-2;
129  for (int row=0; row<2; row++)
130  for (int col=0; col<2; col++)
131  curcov[row][col] = cov[imnum][l][row+covoffset][col+covoffset];
132 
133  curcov.EigenvalueDecomposition(eva, na, evb, nb);
134  // eigenvalues are the \sigma^2
135  eva=sqrt(eva);
136  evb=sqrt(evb);
137  //out << eva<<", "<<evb<<"\n";
138  a[0]=na[0]*eva*cov_scale;
139  a[1]=na[1]*eva*cov_scale;
140  b[0]=nb[0]*evb*cov_scale;
141  b[1]=nb[1]*evb*cov_scale;
142  //cout<<"drawing ellipse at "<<center<<" with a="<<a[0]<<" "
143  // <<a[1]<<" b="<<b[0]<<" "<<b[1]<<endl;
145  Ellipse(im, center, a, b, col);
146  }
147  // circle around corner in actual image
148  start[0]=(unsigned)rint(p[imnum][l][0]);
149  start[1]=(unsigned)rint(p[imnum][l][1]);
150  ImageDraw<unsigned char>::CircleCenterFilled(im, start[0], start[1],
151  radius, col);
152 
153  if (draw_tracks){
154  // lines for tracks
155  for (int i=imnum; i>0; i--){
156  if (p[i-1][l].IsAtInfinity()) {
157  if ((imnum-i)>maxhist) maxhist=imnum-i;
158  break;
159  }
160  start[0]=(unsigned)rint(p[i][l][0]);
161  start[1]=(unsigned)rint(p[i][l][1]);
162  end[0]=(unsigned)rint(p[i-1][l][0]);
163  end[1]=(unsigned)rint(p[i-1][l][1]);
164  if (i-1==0) maxhist=imnum;
165  ImageDraw<unsigned char>::Line(im, start, end, col);
166  }
167  }
168 
169  }
170  }
171  } else {
172  // draw initial poinst blue
173  for (int l=0; l<nump; l++){
174  if (!p[imnum][l].IsAtInfinity()){
175  // circle around corner in actual image
176  start[0]=(unsigned)rint(p[imnum][l][0]);
177  start[1]=(unsigned)rint(p[imnum][l][1]);
178  ImageDraw<unsigned char>::CircleCenterFilled(im, start[0], start[1],
179  radius, colgreen);
180  }
181  }
182  }
183 }
184 
185 
186 /** print tracks to stdout, just for debugging purposes */
187 void DumpTracks(deque<vector<HomgPoint2D> >& p)
188 {
189  int numim=0;
190  while (p[numim].size()>0 && numim<(int)p.size()) numim++;
191  for (unsigned l=0; l<p[0].size(); l++){
192  cout <<setw(3)<<l<<" : ";
193  for (int i=0; i<numim; i++){
194  cout.precision(4);
195  fprintf(stdout, "%i:(%5.1f, %5.1f) ", i, p[i][l][0], p[i][l][1]);
196  }
197  cout << endl;
198  }
199 }
200 
201 // the drawing algorithm needs one point at infinity between subsequent tracks,
202 // therefore the last point of the last track is set to (0, 0, 0)
203 void InsertZeroPoints(deque<vector<HomgPoint2D> >& p, int i)
204 {
205  if (i>0){
206  const int nump=p[i].size();
207 
208  for (int k=0; k<nump; k++){
209  if (p[i][k].IsAtInfinity() && !p[i-1][k].IsAtInfinity()){
210  p[i-1][k].Set(0.0, 0.0, 0.0);
211  }
212  }
213  }
214 }
215 
216 /** The main function */
217 int main(int argc, char *argv[])
218 {
219  bool prefilter = true;
220  bool testBrightnessVariation = true;
221 
222  ImageBase baseim;
223  Image<unsigned char> rgbim;
224  Image<StorageType> im, im2;
225 
226  // allocate the tracker
228  //tr.AddDebugLevel(D_TRACKER_INIT);
229  //tr.AddDebugLevel(D_TRACKER_PARAM);
230  //tr.AddDebugLevel(D_TRACKER_TRACK);
231  //tr.AddDebugLevel(D_TRACKER_WRITE_IM);
232  //tr.AddDebugLevel(D_TRACKER_FILL_UP);
233  Vector<int> hws(3);
234  hws.newsize(5);
235  hws[4] =hws[3] = hws[2] =hws[1] =hws[0] = 10;
236  tr.SetHalfWinSize(hws);
237  tr.SetRejectionType(-1);
238  tr.SetAffineBrightnessInvariance(testBrightnessVariation);
239 
240  Vector<int> maxit(3);
241  maxit.newsize(5);
242  maxit[4] =maxit[3] = maxit[2] =maxit[1] =maxit[0] = 50;
243  tr.SetMaxIterations(maxit);
244 
245 
246  // the pyramid images used by the tracker
247  PyramidImage<CalculationType> *pim[2]; // low pass filtered images
248  PyramidImage<CalculationType> *gx[2]; // horizontal gradient
249  PyramidImage<CalculationType> *gy[2]; // vertical gradient
250  // allocate the pyramid images
251 
252  for (int i=0; i<2; i++){
253  pim[i] = new PyramidImage<CalculationType>;
254  gx[i] = new PyramidImage<CalculationType>;
255  gy[i] = new PyramidImage<CalculationType>;
256  }
257 
258  // allocate the corner detector
260  //cd.AddDebugLevel(D_CD_WRITE_DEBUG_IM);
261  //cd.AddDebugLevel(D_CD_FEATURES);
262 
263  int argind = 1;
264 
265  // check if enough images were given on command line
266  if (argc-argind<2 || argind<1){
267  cerr << argv[0] << " [parameter] <im1> <im2> [ <im3> ... ] \n";
268  return -2;
269  }
270 
271  // fill up lost tracks every cdfreq-th frame
272  //int cdfreq=10;
273  // separate the pyramid images
274  int act=0;
275  // how many corners are successfully tracked
276  int num=0;
277 
278 #ifdef TIMING
279  TimeMeasure timer;
280  timer.Start();
281 #endif
282  Random R;
283  // loop for every image
284  for (int i=argind; i<argc; i++){
285  // act is the index into the actual pyramid image
286  // toggle act between 0 and 1
287  act=!act;
288 
289  if (ImageIO::Load(argv[i], baseim)!=0){
290  BIASERR("error loading image "<<argv[i]);
291  return -1;
292  } else {
293  if (DEBUG) cerr << "read "<<argv[i]<<endl;
294 #ifdef TIMING
295  cerr << "---------------"<<argv[i]<<"---------------\n";
296 #endif
297  }
298  st_type=baseim.GetStorageType();
299 
300  // convert to float
301  if (ImageConvert::ConvertST(baseim, im, ImageBase::ST_float)!=0){
302  BIASERR("error converting image "<<argv[i]);
303  }
304 
305  // convert to grey if necessairy
306  if (im.GetChannelCount()!=1){
307  if (ImageConvert::ToGrey(im, im)!=0){
308  BIASERR("error converting image to grey"<<argv[i]);
309  }
310  }
311  if (testBrightnessVariation) {
312  double scale = R.GetUniformDistributed(0.7, 1.3);
313  double shift = R.GetUniformDistributed(-30,30);
314  cout<<"scaling with "<<scale<<" and shifting "<<shift<<endl;
315  im.ScaleShift(scale, shift);
316  float* pimdata = im.GetImageData();
317  for (unsigned int i=0; i<im.GetPixelCount(); i++) {
318  if (*pimdata<0.0) *pimdata = 0.0;
319  if (*pimdata>255.0) *pimdata = 255.0;
320  pimdata++;
321  }
322  //ImageIO::Save("scaleshift.mip",im);
323  ImageIO::Save("scaleshift.mip",im);
324  }
325 
326 #ifdef TIMING
327  timer.Stop();
328  cerr << "loading and converting to float/grey took "<<timer.GetRealTime()<<" us\n";
329  timer.Reset();
330  timer.Start();
331 #endif
332 
333  if (prefilter)
334  {
335  // the first thing that needs to be done is to calculate
336  // the pyramid images
337  tr.PreparePyramide(im, *pim[act], *gx[act], *gy[act]);
338  }
339  else
340  {
341  tr.PreparePyramide(im, *pim[act]);
342  }
343 
344 #ifdef TIMING
345  timer.Stop();
346  cerr << "pyramid preparation took "<<timer.GetRealTime()<<" us\n";
347  timer.Reset();
348  timer.Start();
349 #endif
350 
351  // secondly the tracking process is started
352  if (prefilter)
353  {
354  num=tr.Track(*pim[act], *gx[act], *gy[act]);
355  }
356  else
357  {
358  num=tr.Track(*pim[act]);
359  }
360 
361 #ifdef TIMING
362  timer.Stop();
363  cerr << "tracking took "<<timer.GetRealTime()<<" us for "<<num<<" corners\n";
364  cerr << " = "<<timer.GetCycleCount()/num<<" fps/corner/frame\n";
365  timer.Reset();
366  timer.Start();
367 #endif
368 
369  // now initially tell the tracker where to track
370  if (i==argind){
371  if (!prefilter)
372  {
373  tr.PreparePyramide(im, *pim[act], *gx[act], *gy[act]);
374  }
375  cd.Detect(*(*gx[act])[0], *(*gy[act])[0], mp, qual);
376  cout << "initially found "<<mp.size()<<" points\n";
377 
378  // let the tracker know of the point positions
379  //tr.AddNumPoints(mp, qual, max_num_points);
380 
381  if ((int)mp.size() > (int)max_num_points)
382  {
383  mp.erase(mp.begin() + max_num_points, mp.end());
384  qual.erase(qual.begin() + max_num_points, qual.end());
385  }
386  tr.ReplaceAllPoints(mp, qual);
387 
388 #ifdef TIMING
389  timer.Stop();
390  cerr << "corner detection took "<<timer.GetRealTime()<<" us\n";
391  timer.Reset();
392  timer.Start();
393 #endif
394  }
395 
396  // remember the corners in the global data struct
397  tr.GetPoints(mp);
398  p.push_back(mp);
399 
400  // now remember the residui (mean grey value difference)
401  tr.GetResiduiMAD(r);
402  residui.push_back(r);
403  // and the success indicator
404  tr.GetResults(res);
405  results.push_back(res);
406 
407  tr.GetCovariances(tmp_cov);
408  cov.push_back(tmp_cov);
409 
410 #ifdef TIMING
411  timer.Stop();
412  cerr << "get results took "<<timer.GetRealTime()<<" us\n";
413  timer.Reset();
414  timer.Start();
415 #endif
416 
417 /*
418  // at every cdfreq-th frame the lost tracks indicated by points at infinity
419  // (i.e. p[2]==0) are filled up again
420  if (i!=argind && ((i-argind) % cdfreq)==0){
421 
422  if (!prefilter)
423  {
424  tr.PreparePyramide(im, *pim[act], *gx[act], *gy[act]);
425  }
426 
427  // We are not interested in corners already tracked,
428  // hence the corner detector must know about the points which are
429  // already tracked
430  tr.GetPoints(cp);
431 
432  cd.Detect(*(*gx[act])[0], *(*gy[act])[0], cp, qual);
433  // the track length in the globals data structs are determiend
434  // by 'zero points'
435  InsertZeroPoints(p, i-argind);
436 
437  // now only the points where tracking failed are filled up
438  int num=tr.FillUpPoints(cp, qual);
439  if (DEBUG) cout << "filled up "<<num<<" points\n";
440  // get the newly filled up points and remember them in the global deque
441  tr.GetPoints(mp);
442  // restrict the maximum track length in the global data struct (optional)
443  p.pop_back();
444  p.push_back(mp);
445 
446  }
447 */
448 
449 #ifdef TIMING
450  timer.Stop();
451  cerr << "corner detection took "<<timer.GetRealTime()<<" us\n";
452  timer.Reset();
453  timer.Start();
454 #endif
455 
456 
457  // now draw the tracks into rgbim
458  Draw(*(*pim[act])[0], rgbim, p, cov, i-argind);
459  // write result image to disk
460  ostringstream name;
461  name << "track-"<<setw(4)<<setfill('0')<<i-argind<<".mip";
462  //ImageIO::Save(name.str(), rgbim);
463  ImageIO::Save(name.str(), rgbim);
464 
465 
466 #ifdef TIMING
467  timer.Stop();
468  cerr << "drawing/writing took "<<timer.GetRealTime()<<" us\n";
469  timer.Reset();
470  timer.Start();
471 #endif
472  }
473 
474  /*
475  // write a Birchfeld style tracker file
476  if (DEBUG) tr.WriteTrackFile(p, results, "features.txt");
477 
478  if (DEBUG) cout << "\nTracks: "<<endl;
479  if (DEBUG) DumpTracks(p);
480  if (DEBUG) cout << "\n";
481  */
482 
483  return 0;
484 }
(16bit) unsigned integer image storage type
Definition: ImageBase.hh:114
float image storage type
Definition: ImageBase.hh:118
static int CircleCenterFilled(Image< StorageType > &im, unsigned int CenterX, unsigned int CenterY, unsigned int Radius, const StorageType Value[])
draws a filled circle using Value
Definition: ImageDraw.cpp:1023
int ScaleShift(double Scale, double Shift)
scales and shifts image (all channels simultanously)
Definition: Image.cpp:1064
double GetUniformDistributed(const double min, const double max)
on succesive calls return uniform distributed random variable between min and max ...
Definition: Random.hh:84
invalid not set image storage type
Definition: ImageBase.hh:111
static int Line(Image< StorageType > &im, const unsigned int start[2], const unsigned int end[2], const StorageType value[])
lines
Definition: ImageDraw.cpp:404
static int ConvertST(const BIAS::ImageBase &source, BIAS::ImageBase &dest, ImageBase::EStorageType targetST)
Function to convert the storage type of images e.g.
static int Ellipse(Image< StorageType > &im, double center[2], double a[2], double b[2], const StorageType Value[])
draws an ellipse at center with half axes a and b
Definition: ImageDraw.cpp:1255
unsigned int GetChannelCount() const
returns the number of Color channels, e.g.
Definition: ImageBase.hh:382
color values, 3 channels, order: red,green,blue
Definition: ImageBase.hh:131
Computes the cornerness as the smaller eigenvalue of the structure tensor matrix. ...
The image template class for specific storage types.
Definition: Image.hh:78
static int Save(const std::string &filename, const ImageBase &img, const enum TFileFormat FileFormat=FF_auto, const bool sync=BIAS_DEFAULT_SYNC, const int c_jpeg_quality=BIAS_DEFAULT_IMAGE_QUALITY, const bool forceNewID=BIAS_DEFAULT_FORCENEWID, const bool &writeMetaData=true)
Export image as file using extrnal libs.
Definition: ImageIO.cpp:725
double GetCycleCount() const
return number of cycles between all subsequent calls to Start() and Stop() since last call to Reset()...
double GetRealTime() const
return real time (=wall time clock) in usec JW For Win32: real-time is measured differently from user...
const StorageType * GetImageData() const
overloaded GetImageData() from ImageBase
Definition: Image.hh:137
int EigenvalueDecomposition(T &value1, Vector2< T > &vector1, T &value2, Vector2< T > &vector2) const
Eigenvalue decomposition.
Definition: Matrix2x2.cpp:131
Class for holding multiple downsampled images.
Definition: ImageCanvas.hh:30
static int Load(const std::string &FileName, ImageBase &img)
first tries a call to Read MIP image and if that fails, tries to Import Image with all other availabl...
Definition: ImageIO.cpp:141
static int Convert(BIAS::ImageBase &source, BIAS::ImageBase &dest, enum BIAS::ImageBase::EColorModel targetColorModel, bool bPlanar=false)
main general conversion function, calls desired specialized functions, always initializes the destIma...
enum EStorageType GetStorageType() const
Definition: ImageBase.hh:414
(8bit) unsigned char image storage type
Definition: ImageBase.hh:112
unsigned long int GetPixelCount() const
returns number of pixels in image
Definition: ImageBase.hh:422
This is the base class for images in BIAS.
Definition: ImageBase.hh:102
class for producing random numbers from different distributions
Definition: Random.hh:51
class TimeMeasure contains functions for timing real time and cpu time.
Definition: TimeMeasure.hh:111
High level tracking class.
Definition: Tracker.hh:72
virtual int Detect(const Image< StorageType > &image, std::vector< HomgPoint2D > &p, std::vector< QUAL > &quality, std::vector< Matrix2x2< double > > *cov=NULL)
detect corners in a grey image
static int ToGrey(const ImageBase &source, ImageBase &dest)
wrapper for the templated function ToGrey