1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17// $Id: dbregtest.cpp,v 1.24 2011/06/17 14:04:33 mbansal Exp $
18#include "stdafx.h"
19#include "PgmImage.h"
20#include "../dbreg/dbreg.h"
21#include "../dbreg/dbstabsmooth.h"
22#include <db_utilities_camera.h>
23
24#include <iostream>
25#include <iomanip>
26
27#if PROFILE
28    #include <sys/time.h>
29#endif
30
31
32using namespace std;
33
34const int DEFAULT_NR_CORNERS=500;
35const double DEFAULT_MAX_DISPARITY=0.2;
36const int DEFAULT_MOTION_MODEL=DB_HOMOGRAPHY_TYPE_AFFINE;
37//const int DEFAULT_MOTION_MODEL=DB_HOMOGRAPHY_TYPE_R_T;
38//const int DEFAULT_MOTION_MODEL=DB_HOMOGRAPHY_TYPE_TRANSLATION;
39const bool DEFAULT_QUARTER_RESOLUTION=false;
40const unsigned int DEFAULT_REFERENCE_UPDATE_PERIOD=3;
41const bool DEFAULT_DO_MOTION_SMOOTHING = false;
42const double DEFAULT_MOTION_SMOOTHING_GAIN = 0.75;
43const bool DEFAULT_LINEAR_POLISH = false;
44const int DEFAULT_MAX_ITERATIONS = 10;
45
46void usage(string name) {
47
48  const char *helpmsg[] = {
49    "Function: point-based frame to reference registration.",
50    "  -m [rt,a,p]  : motion model, rt = rotation+translation, a = affine (default = affine).",
51    "  -c <int>   : number of corners (default 1000).",
52    "  -d <double>: search disparity as portion of image size (default 0.1).",
53    "  -q         : quarter the image resolution (i.e. half of each dimension) (default on)",
54    "  -r <int>   : the period (in nr of frames) for reference frame updates (default = 5)",
55    "  -s <0/1>   : motion smoothing (1 activates motion smoothing, 0 turns it off - default value = 1)",
56    "  -g <double>: motion smoothing gain, only used if smoothing is on (default value =0.75)",
57    NULL
58  };
59
60  cerr << "Usage: " << name << " [options] image_list.txt" << endl;
61
62  const char **p = helpmsg;
63
64  while (*p)
65  {
66    cerr << *p++ << endl;
67  }
68}
69
70void parse_cmd_line(stringstream& cmdline,
71            const int argc,
72            const string& progname,
73            string& image_list_file_name,
74            int& nr_corners,
75            double& max_disparity,
76            int& motion_model_type,
77            bool& quarter_resolution,
78            unsigned int& reference_update_period,
79            bool& do_motion_smoothing,
80            double& motion_smoothing_gain
81            );
82
83int main(int argc, char* argv[])
84{
85  int    nr_corners = DEFAULT_NR_CORNERS;
86  double max_disparity = DEFAULT_MAX_DISPARITY;
87  int    motion_model_type = DEFAULT_MOTION_MODEL;
88  bool   quarter_resolution = DEFAULT_QUARTER_RESOLUTION;
89
90  unsigned int reference_update_period = DEFAULT_REFERENCE_UPDATE_PERIOD;
91
92  bool   do_motion_smoothing = DEFAULT_DO_MOTION_SMOOTHING;
93  double motion_smoothing_gain = DEFAULT_MOTION_SMOOTHING_GAIN;
94  const bool DEFAULT_USE_SMALLER_MATCHING_WINDOW = true;
95
96  int default_nr_samples = DB_DEFAULT_NR_SAMPLES/5;
97
98  bool   use_smaller_matching_window = DEFAULT_USE_SMALLER_MATCHING_WINDOW;
99
100
101  bool   linear_polish = DEFAULT_LINEAR_POLISH;
102
103  if (argc < 2) {
104    usage(argv[0]);
105    exit(1);
106  }
107
108  stringstream cmdline;
109  string progname(argv[0]);
110  string image_list_file_name;
111
112#if PROFILE
113  timeval ts1, ts2, ts3, ts4;
114#endif
115
116  // put the options and image list file name into the cmdline stringstream
117  for (int c = 1; c < argc; c++)
118  {
119    cmdline << argv[c] << " ";
120  }
121
122  parse_cmd_line(cmdline, argc, progname, image_list_file_name, nr_corners, max_disparity, motion_model_type,quarter_resolution,reference_update_period,do_motion_smoothing,motion_smoothing_gain);
123
124  ifstream in(image_list_file_name.c_str(),ios::in);
125
126  if ( !in.is_open() )
127  {
128    cerr << "Could not open file " << image_list_file_name << ".  Exiting" << endl;
129
130    return false;
131  }
132
133  // feature-based image registration class:
134  db_FrameToReferenceRegistration reg;
135//  db_StabilizationSmoother stab_smoother;
136
137  // input file name:
138  string file_name;
139
140  // look-up tables for image warping:
141  float ** lut_x = NULL, **lut_y = NULL;
142
143  // if the images are color, the input is saved in color_ref:
144  PgmImage color_ref(0,0);
145
146  // image width, height:
147  int w,h;
148
149  int frame_number = 0;
150
151  while ( !in.eof() )
152  {
153    getline(in,file_name);
154
155    PgmImage ref(file_name);
156
157    if ( ref.GetDataPointer() == NULL )
158    {
159      cerr << "Could not open image" << file_name << ". Exiting." << endl;
160      return -1;
161    }
162
163    cout << ref << endl;
164
165    // color format:
166    int format = ref.GetFormat();
167
168    // is the input image color?:
169    bool color = format == PgmImage::PGM_BINARY_PIXMAP;
170
171    w = ref.GetWidth();
172    h = ref.GetHeight();
173
174    if ( !reg.Initialized() )
175    {
176      reg.Init(w,h,motion_model_type,DEFAULT_MAX_ITERATIONS,linear_polish,quarter_resolution,DB_POINT_STANDARDDEV,reference_update_period,do_motion_smoothing,motion_smoothing_gain,default_nr_samples,DB_DEFAULT_CHUNK_SIZE,nr_corners,max_disparity,use_smaller_matching_window);
177      lut_x = db_AllocImage_f(w,h);
178      lut_y = db_AllocImage_f(w,h);
179
180    }
181
182    if ( color )
183    {
184      // save the color image:
185      color_ref = ref;
186    }
187
188    // make a grayscale image:
189    ref.ConvertToGray();
190
191    // compute the homography:
192    double H[9],Hinv[9];
193    db_Identity3x3(Hinv);
194    db_Identity3x3(H);
195
196    bool force_reference = false;
197
198#if PROFILE
199    gettimeofday(&ts1, NULL);
200#endif
201
202    reg.AddFrame(ref.GetRowPointers(),H,false,false);
203    cout << reg.profile_string << std::endl;
204
205#if PROFILE
206    gettimeofday(&ts2, NULL);
207
208    double elapsedTime = (ts2.tv_sec - ts1.tv_sec)*1000.0; // sec to ms
209    elapsedTime += (ts2.tv_usec - ts1.tv_usec)/1000.0; // us to ms
210    cout <<"\nelapsedTime for Reg<< "<<elapsedTime<<" ms >>>>>>>>>>>>>\n";
211#endif
212
213    if (frame_number == 0)
214    {
215      reg.UpdateReference(ref.GetRowPointers());
216    }
217
218
219    //std::vector<int> &inlier_indices = reg.GetInliers();
220    int *inlier_indices = reg.GetInliers();
221    int num_inlier_indices = reg.GetNrInliers();
222    printf("[%d] #Inliers = %d\n",frame_number,num_inlier_indices);
223
224    reg.Get_H_dref_to_ins(H);
225
226    db_GenerateHomographyLut(lut_x,lut_y,w,h,H);
227
228    // create a new image and warp:
229    PgmImage warped(w,h,format);
230
231#if PROFILE
232    gettimeofday(&ts3, NULL);
233#endif
234
235    if ( color )
236      db_WarpImageLutBilinear_rgb(color_ref.GetRowPointers(),warped.GetRowPointers(),w,h,lut_x,lut_y);
237    else
238      db_WarpImageLut_u(ref.GetRowPointers(),warped.GetRowPointers(),w,h,lut_x,lut_y,DB_WARP_FAST);
239
240#if PROFILE
241    gettimeofday(&ts4, NULL);
242    elapsedTime = (ts4.tv_sec - ts3.tv_sec)*1000.0; // sec to ms
243    elapsedTime += (ts4.tv_usec - ts3.tv_usec)/1000.0;     // us to ms
244    cout <<"\nelapsedTime for Warp <<"<<elapsedTime<<" ms >>>>>>>>>>>>>\n";
245#endif
246
247    // write aligned image: name is aligned_<corresponding input file name>
248    stringstream s;
249    s << "aligned_" << file_name;
250    warped.WritePGM(s.str());
251
252    /*
253    // Get the reference and inspection corners to write to file
254    double *ref_corners = reg.GetRefCorners();
255    double *ins_corners = reg.GetInsCorners();
256
257    // get the image file name (without extension), so we
258    // can generate the corresponding filenames for matches
259    // and inliers
260    string file_name_root(file_name.substr(0,file_name.rfind(".")));
261
262    // write matches to file
263    s.str(string(""));
264    s << "Matches_" << file_name_root << ".txt";
265
266    ofstream  match_file(s.str().c_str());
267
268    for (int i = 0; i < reg.GetNrMatches(); i++)
269    {
270      match_file << ref_corners[3*i] << " " << ref_corners[3*i+1] << " " << ins_corners[3*i] << " " << ins_corners[3*i+1] << endl;
271    }
272
273    match_file.close();
274
275    // write the inlier matches to file
276    s.str(string(""));
277    s << "InlierMatches_" << file_name_root << ".txt";
278
279    ofstream inlier_match_file(s.str().c_str());
280
281    for(int i=0; i<num_inlier_indices; i++)
282    {
283      int k = inlier_indices[i];
284      inlier_match_file << ref_corners[3*k] << " "
285            << ref_corners[3*k+1] << " "
286            << ins_corners[3*k] << " "
287            << ins_corners[3*k+1] << endl;
288    }
289    inlier_match_file.close();
290    */
291
292    frame_number++;
293  }
294
295  if ( reg.Initialized() )
296  {
297    db_FreeImage_f(lut_x,h);
298    db_FreeImage_f(lut_y,h);
299  }
300
301  return 0;
302}
303
304void parse_cmd_line(stringstream& cmdline,
305            const int argc,
306            const string& progname,
307            string& image_list_file_name,
308            int& nr_corners,
309            double& max_disparity,
310            int& motion_model_type,
311            bool& quarter_resolution,
312            unsigned int& reference_update_period,
313            bool& do_motion_smoothing,
314            double& motion_smoothing_gain)
315{
316  // for counting down the parsed arguments.
317  int c = argc;
318
319  // a holder
320  string token;
321
322  while (cmdline >> token)
323  {
324    --c;
325
326    int pos = token.find("-");
327
328    if (pos == 0)
329    {
330      switch (token[1])
331      {
332      case 'm':
333    --c; cmdline >> token;
334    if (token.compare("rt") == 0)
335    {
336      motion_model_type = DB_HOMOGRAPHY_TYPE_R_T;
337    }
338    else if (token.compare("a") == 0)
339    {
340      motion_model_type = DB_HOMOGRAPHY_TYPE_AFFINE;
341    }
342    else if (token.compare("p") == 0)
343    {
344      motion_model_type = DB_HOMOGRAPHY_TYPE_PROJECTIVE;
345    }
346    else
347    {
348      usage(progname);
349      exit(1);
350    }
351    break;
352      case 'c':
353    --c; cmdline >> nr_corners;
354    break;
355      case 'd':
356    --c; cmdline >> max_disparity;
357    break;
358      case 'q':
359    quarter_resolution = true;
360    break;
361      case 'r':
362    --c; cmdline >> reference_update_period;
363    break;
364      case 's':
365    --c; cmdline >> do_motion_smoothing;
366    break;
367      case 'g':
368    --c; cmdline >> motion_smoothing_gain;
369    break;
370      default:
371    cerr << progname << "illegal option " << token << endl;
372      case 'h':
373    usage(progname);
374    exit(1);
375    break;
376      }
377    }
378    else
379    {
380      if (c != 1)
381      {
382    usage(progname);
383    exit(1);
384      }
385      else
386      {
387    --c;
388    image_list_file_name = token;
389      }
390    }
391  }
392
393  if (c != 0)
394  {
395    usage(progname);
396    exit(1);
397  }
398}
399
400