00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <assert.h>
00021 #include <highgui.h>
00022 #include "pic_randomizer.h"
00023 #include <iostream>
00024
00025 #ifndef WIN32
00026 #include <sys/file.h>
00027 #else
00028 #include <float.h>
00029 static inline double drand48() {
00030 return (double)rand()/(double)RAND_MAX;
00031 }
00032
00033 static inline int finite(float f) {
00034 return _finite(f);
00035 }
00036 #endif
00037
00038
00039 #ifdef _OPENMP
00040 #include <omp.h>
00041 #endif
00042
00043 #ifndef M_PI
00044 #define M_PI CV_PI
00045 #endif
00046
00047 using namespace std;
00048
00049 #ifdef _OPENMP
00050 static omp_lock_t lock;
00051 bool lock_initialized=false;
00052 #endif
00053
00054 FILE *open_and_lock(const char *fn)
00055 {
00056 #ifdef _OPENMP
00057 if (!lock_initialized) {
00058 omp_init_lock(&lock);
00059 lock_initialized = true;
00060 }
00061 omp_set_lock(&lock);
00062 #endif
00063 FILE *ofile = fopen(fn,"a");
00064 if (!ofile) return 0;
00065
00066 #ifndef WIN32
00067 if (flock(fileno(ofile), LOCK_EX) < 0) {
00068 fprintf(stderr, "locking ");
00069 perror(fn);
00070 fclose(ofile);
00071 return 0;
00072 }
00073 #endif
00074 return ofile;
00075 }
00076
00077 void unlock_and_close(FILE *f)
00078 {
00079 #ifndef WIN32
00080 flock(fileno(f), LOCK_UN);
00081 #endif
00082 fclose(f);
00083
00084 #ifdef _OPENMP
00085 omp_unset_lock(&lock);
00086 #endif
00087 }
00088
00089
00090 pic_randomizer::pic_randomizer()
00091 {
00092 base_image = 0;
00093 view = 0;
00094 points = 0;
00095 tracker=0;
00096 save_descriptors_only= false;
00097 tree_fn = 0;
00098 output_fn = 0;
00099 visualdb_fn = 0;
00100
00101 min_view_rate = .3;
00102 nb_views = 1000;
00103 }
00104
00105 pic_randomizer::~pic_randomizer() {
00106 if (base_image) cvReleaseImage(&base_image);
00107 if (view) cvReleaseImage(&view);
00108 if (points) delete points;
00109 if (tracker) delete tracker;
00110 }
00111
00112
00113 bool pic_randomizer::load_image(const char *fn){
00114 cvReleaseImage(&base_image);
00115 cvReleaseImage(&view);
00116 if (points) delete points;
00117 points=0;
00118
00119 base_image = cvLoadImage(fn, 0);
00120 if (!base_image) return false;
00121
00122 while (base_image->width>1024) {
00123 IplImage *smaller = cvCreateImage(cvSize(base_image->width/2,base_image->height/2),
00124 IPL_DEPTH_8U, 1);
00125 cvPyrDown(base_image, smaller);
00126 cvReleaseImage(&base_image);
00127 base_image = smaller;
00128 }
00129
00130 points = new bucket2d<point_cluster>(base_image->width, base_image->height, 4);
00131
00132 view=0;
00133 return true;
00134 }
00135
00136 float rand_range(float min, float max)
00137 {
00138 if (min>max) {
00139 float t = min;
00140 min = max;
00141 max = t;
00142 }
00143 return drand48()*(max-min)+min;
00144 }
00145
00146 void Corners::interpolate(const Corners &a, const Corners &b, float t)
00147 {
00148 float tm = 1.0f - t;
00149 for (int i=0; i<4; i++) {
00150 p[i][0] = a.p[i][0]*t + a.p[i][0]*tm;
00151 p[i][1] = a.p[i][1]*t + a.p[i][1]*tm;
00152 }
00153 }
00154
00155 void Corners::random_homography(float src_width, float src_height, float min_angle, float max_angle, float min_scale, float max_scale, float h_range, float v_range)
00156 {
00157 float angle = rand_range(min_angle, max_angle);
00158 float scale = rand_range(min_scale, max_scale);
00159
00160 p[0][0] = rand_range(0,h_range); p[0][1] = rand_range(0,v_range);
00161 p[1][0] = rand_range(src_width-h_range, src_width); p[1][1] = rand_range(0,v_range);
00162 p[2][0] = rand_range(src_width-h_range, src_width); p[2][1] = rand_range(src_height-v_range, src_height);
00163 p[3][0] = rand_range(0,h_range); p[3][1] = rand_range(src_height-v_range, src_height);
00164
00165 float center[2] = {0,0};
00166 for (int i=0; i<4; i++) {
00167 center[0] += p[i][0]/4;
00168 center[1] += p[i][1]/4;
00169 }
00170 for (int i=0; i<4; i++) {
00171 p[i][0] -= center[0];
00172 p[i][1] -= center[1];
00173 }
00174
00175
00176 for (int i=0; i<4; i++) {
00177 p[i][0] *= scale;
00178 p[i][1] *= scale;
00179 }
00180
00181
00182 float rot[4][2];
00183 for (int i=0; i<4; i++) {
00184 float a = cos(angle)*p[i][0] - sin(angle)*p[i][1];
00185 float b = sin(angle)*p[i][0] + cos(angle)*p[i][1];
00186 p[i][0] = a;
00187 p[i][1] = b;
00188 }
00189 }
00190
00191 void Corners::get_min_max(float *minx, float *miny, float *maxx, float *maxy)
00192 {
00193
00194 float xmin=1e8,xmax=-1e8;
00195 float ymin=1e8,ymax=-1e8;
00196 for (int i=0; i<4; i++) {
00197 if (p[i][0] < xmin) xmin = p[i][0];
00198 if (p[i][0] > xmax) xmax = p[i][0];
00199 if (p[i][1] < ymin) ymin = p[i][1];
00200 if (p[i][1] > ymax) ymax = p[i][1];
00201 }
00202 if (minx) *minx = xmin;
00203 if (miny) *miny = ymin;
00204 if (maxx) *maxx = xmax;
00205 if (maxy) *maxy = ymax;
00206 }
00207
00208
00209 void Corners::fit_in_image(int *width, int *height)
00210 {
00211 float xmin,ymin;
00212 float xmax,ymax;
00213 get_min_max(&xmin, &ymin,&xmax,&ymax);
00214 for (int i=0;i<4; i++) {
00215 p[i][0] -= xmin;
00216 p[i][1] -= ymin;
00217 }
00218 if (width)
00219 *width = ((int)(xmax - xmin) + 7) & 0xFFFFFFF8;
00220 if (height)
00221 *height = ((int)(ymax - ymin) + 7) & 0xFFFFFFF8;
00222 }
00223
00224 void Corners::get_homography(CvMat *H, int src_width, int src_height)
00225 {
00226 float src_pts[4][2] = {
00227 {0,0},
00228 {src_width,0},
00229 {src_width,src_height},
00230 {0,src_height}
00231 };
00232
00233 CvMat msrc; cvInitMatHeader(&msrc, 4, 2, CV_32FC1, src_pts);
00234 CvMat mdst; cvInitMatHeader(&mdst, 4, 2, CV_32FC1, p);
00235 cvFindHomography(&mdst, &msrc, H);
00236 }
00237
00238 void SyntheticViewPath::generatePath(int nbLoc, float min_angle, float max_angle, float min_scale, float max_scale, float h_range, float v_range)
00239 {
00240 path.clear();
00241 if (nbLoc<2) return;
00242 path.reserve(nbLoc);
00243 for (int i=0; i<nbLoc; i++) {
00244 path.push_back(Corners());
00245 path[i].random_homography(im->width, im->height, min_angle, max_angle, min_scale, max_scale, h_range, v_range);
00246 }
00247 }
00248
00249 void SyntheticViewPath::getDstImSize(int *width, int *height)
00250 {
00251 int max_w=0, max_h=0;
00252 for (int i=0; i<path.size(); i++) {
00253 int w,h;
00254 path[i].fit_in_image(&w,&h);
00255 if (w>max_w) max_w=w;
00256 if (h>max_h) max_h=h;
00257 }
00258 *width = max_w;
00259 *height = max_h;
00260 }
00261
00262 void SyntheticViewPath::genView(float _t, IplImage *dst)
00263 {
00264
00265 float t = _t;
00266 if (_t<0) t=0;
00267 if (_t>=1) t=1;
00268 float n = t*path.size();
00269 int pos = floorf(n);
00270 float sub = n-floorf(n);
00271 if (pos == path.size()) {
00272 --pos;
00273 t+=1;
00274 }
00275 Corners c;
00276 c.interpolate(path[pos], path[pos+1], t);
00277
00278 CvMat H;
00279 float _H[3][3];
00280 cvInitMatHeader(&H, 3, 3, CV_32FC1, _H);
00281
00282 c.get_homography(&H, im->width, im->height);
00283
00284 cvWarpPerspective(im, dst, &H, CV_WARP_FILL_OUTLIERS+ CV_WARP_INVERSE_MAP);
00285
00286 }
00287
00288 void random_homography(CvMat *H, int *width, int *height, float min_angle, float max_angle, float min_scale, float max_scale, float h_range, float v_range)
00289 {
00290 float angle = rand_range(min_angle, max_angle);
00291 float scale = rand_range(min_scale, max_scale);
00292
00293 float pts[4][2]= {
00294 {rand_range(0,h_range),rand_range(0,v_range)},
00295 {rand_range(*width-h_range, *width),rand_range(0,v_range)},
00296 {rand_range(*width-h_range, *width),rand_range(*height-v_range, *height)},
00297 {rand_range(0,h_range),rand_range(*height-v_range, *height)},
00298 };
00299 float center[2] = {0,0};
00300 for (int i=0; i<4; i++) {
00301 center[0] += pts[i][0]/4;
00302 center[1] += pts[i][1]/4;
00303 }
00304 for (int i=0; i<4; i++) {
00305 pts[i][0] -= center[0];
00306 pts[i][1] -= center[1];
00307 }
00308
00309
00310 for (int i=0; i<4; i++) {
00311 pts[i][0] *= scale;
00312 pts[i][1] *= scale;
00313 }
00314
00315
00316 float rot[4][2];
00317 for (int i=0; i<4; i++) {
00318 rot[i][0] = cos(angle)*pts[i][0] - sin(angle)*pts[i][1];
00319 rot[i][1] = sin(angle)*pts[i][0] + cos(angle)*pts[i][1];
00320 }
00321
00322
00323 float xmin=1e8,xmax=-1e8;
00324 float ymin=1e8,ymax=-1e8;
00325 for (int i=0; i<4; i++) {
00326 if (rot[i][0] < xmin) xmin = rot[i][0];
00327 if (rot[i][0] > xmax) xmax = rot[i][0];
00328 if (rot[i][1] < ymin) ymin = rot[i][1];
00329 if (rot[i][1] > ymax) ymax = rot[i][1];
00330 }
00331 for (int i=0;i<4; i++) {
00332 rot[i][0] -= xmin;
00333 rot[i][1] -= ymin;
00334 }
00335
00336 float src_pts[4][2] = {
00337 {0,0},
00338 {*width,0},
00339 {*width,*height},
00340 {0,*height}
00341 };
00342
00343 *width = ((int)(xmax - xmin) + 7) & 0xFFFFFFF8;
00344 *height = ((int)(ymax - ymin) + 7) & 0xFFFFFFF8;
00345
00346 CvMat msrc; cvInitMatHeader(&msrc, 4, 2, CV_32FC1, src_pts);
00347 CvMat mdst; cvInitMatHeader(&mdst, 4, 2, CV_32FC1, rot);
00348 cvFindHomography(&mdst, &msrc, H);
00349 }
00350
00351 IplImage *pic_randomizer::gen_view()
00352 {
00353 int width = base_image->width;
00354 int height = base_image->height;
00355
00356 cvInitMatHeader(&mH, 3, 3, CV_32FC1, H);
00357
00358 random_homography(&mH, &width, &height, 0, 2*M_PI, .5, 2, width*.2, height*.2);
00359
00360 cvReleaseImage(&view);
00361 view = cvCreateImage(cvSize(width,height), base_image->depth, base_image->nChannels);
00362 cvWarpPerspective(base_image, view, &mH, CV_WARP_FILL_OUTLIERS+ CV_WARP_INVERSE_MAP);
00363
00364 return view;
00365 }
00366
00367 pic_randomizer::point_cluster *pic_randomizer::find_cluster(const point2d &p, float r)
00368 {
00369 float best_d=r;
00370 point_cluster *best_c=0;
00371 for (bucket2d<point_cluster>::iterator it(points->search(p.u, p.v, r)); !it.end(); ++it)
00372 {
00373 float d = it.elem()->dist(p);
00374 if (d<best_d) {
00375 best_c = it.elem();
00376 best_d = d;
00377 }
00378 }
00379 return best_c;
00380 }
00381
00382 void pic_randomizer::detect_kpts()
00383 {
00384 if (!base_image) return;
00385 if (!view) gen_view();
00386 assert(view!=0);
00387
00388 if (!tracker) {
00389 tracker = new kpt_tracker(view->width, view->height, 4, 10);
00390
00391 if (!save_descriptors_only) {
00392 if (!tree_fn && !visualdb_fn) {
00393 cerr << "please specify a tree file or a database with -t or -v, or use the -D option.\n";
00394 exit(-4);
00395 }
00396 if (tree_fn && !tracker->load_tree(tree_fn))
00397 {
00398 cerr << tree_fn << ": can't load tree.\n";
00399 exit(-3);
00400 }
00401 if (visualdb_fn) {
00402 sqlite3 *db;
00403 int rc = sqlite3_open_v2(visualdb_fn, &db, SQLITE_OPEN_READONLY, 0);
00404 if (rc || !tracker->load_tree(db))
00405 {
00406 if (!rc) sqlite3_close(db);
00407 cerr << visualdb_fn << ": can't load tree.\n";
00408 exit(-3);
00409 }
00410 sqlite3_close(db);
00411 }
00412 }
00413 } else
00414 tracker->set_size(view->width, view->height, 4, 10);
00415
00416 pyr_frame *frame = tracker->add_frame(view);
00417 tracker->detect_keypoints(frame);
00418 tracker->traverse_tree(frame);
00419 }
00420
00421 bool write_descriptor(FILE *descrf, pyr_keypoint *k, long ptr)
00422 {
00423 static const unsigned descr_size=kmean_tree::descriptor_size;
00424 if ((sizeof(long)+descr_size*sizeof(float)) != sizeof(kmean_tree::descr_file_packet)) {
00425 cerr << "wrong structure size!!\n";
00426 exit(-1);
00427 }
00428
00429 #ifdef WITH_SURF
00430 if (k->surf_descriptor.descriptor[0]==-1) return false;
00431 for (unsigned i=0;i<64;i++) {
00432 if (!finite(k->surf_descriptor.descriptor[i])) {
00433 cerr << "surf descr[" << i << "] = " << k->surf_descriptor.descriptor[i] << endl;
00434 return false;
00435 }
00436 }
00437
00438 fwrite(&ptr, sizeof(long), 1, descrf);
00439 if (fwrite(k->surf_descriptor.descriptor, 64*sizeof(float), 1, descrf)!=1) {
00440 cerr << "write error!\n";
00441 return false;
00442 }
00443 #endif
00444
00445 #ifdef WITH_SIFTGPU
00446 for (unsigned i=0;i<128;i++) {
00447 if (!finite(k->sift_descriptor.descriptor[i])) {
00448 cerr << "sift descr[" << i << "] = " << k->sift_descriptor.descriptor[i] << endl;
00449 return false;
00450 }
00451 }
00452
00453 fwrite(&ptr, sizeof(long), 1, descrf);
00454 if (fwrite(k->sift_descriptor.descriptor, 128*sizeof(float), 1, descrf)!=1) {
00455 cerr << "write error!\n";
00456 return false;
00457 }
00458 #endif
00459
00460
00461 #ifdef WITH_YAPE
00462 if (k->descriptor.total==0) {
00463 std::cout << "save_descriptors: total==0!\n";
00464 return false;
00465 }
00466
00467 fwrite(&ptr, sizeof(long), 1, descrf);
00468 float array[descr_size];
00469 k->descriptor.array(array);
00470 fwrite(array, descr_size * sizeof(float), 1,descrf);
00471 #endif
00472
00473 return true;
00474 }
00475
00476
00477 bool pic_randomizer::save_keypoints()
00478 {
00479 FILE *ofile = open_and_lock(output_fn);
00480 if (!ofile) return false;
00481
00482 pyr_frame *frame = (pyr_frame *)tracker->get_nth_frame(0);
00483
00484 for (tracks::keypoint_frame_iterator it(frame->points.begin()); !it.end(); ++it) {
00485 write_descriptor(ofile, (pyr_keypoint *)it.elem(), 0);
00486 }
00487 unlock_and_close(ofile);
00488
00489 tracker->remove_frame(frame);
00490 view=0;
00491 return true;
00492 }
00493
00494
00495 void pic_randomizer::group_ids()
00496 {
00497
00498
00499
00500
00501
00502 pyr_frame *frame = (pyr_frame *)tracker->get_nth_frame(0);
00503
00504 assert(tracker->tree!=0);
00505 for (tracks::keypoint_frame_iterator it(frame->points.begin()); !it.end(); ++it) {
00506
00507 float _u = it.elem()->u;
00508 float _v = it.elem()->v;
00509 float z = H[2][0]*_u + H[2][1]*_v + H[2][2];
00510 float u = (H[0][0]*_u + H[0][1]*_v + H[0][2])/z;
00511 float v = (H[1][0]*_u + H[1][1]*_v + H[1][2])/z;
00512
00513
00514 point2d p(u,v);
00515
00516 point_cluster *c = find_cluster(p, 3);
00517 if (!c) {
00518 c = new point_cluster;
00519 c->u = u;
00520 c->v = v;
00521 c->n=1;
00522 points->add_pt(c);
00523
00524
00525
00526
00527
00528
00529
00530
00531 } else {
00532
00533 c->n++;
00534 float nu = (u + c->u*(c->n-1))/c->n;
00535 float nv = (v + c->v*(c->n-1))/c->n;
00536
00537
00538 points->move_pt(c, nu, nv);
00539
00540
00541
00542
00543
00544
00545
00546 }
00547 c->add(((pyr_keypoint *)(it.elem()))->id, 1);
00548 }
00549
00550
00551
00552
00553 tracker->remove_frame(frame);
00554 view=0;
00555 }
00556
00557 void pic_randomizer::prune()
00558 {
00559
00560 unsigned min = nb_views*min_view_rate;
00561 int removed=0;
00562 int remaining=0;
00563 for (bucket2d<point_cluster>::iterator it(*points); !it.end(); )
00564 {
00565 bucket2d<point_cluster>::iterator a(it);
00566 ++it;
00567 if (a.elem()->total<min) {
00568 points->rm_pt(a.elem());
00569 removed++;
00570 } else
00571 remaining++;
00572 }
00573 std::cout << "Removed " << removed << " clusters with less than " << min << " views. "
00574 << points->size() << " points left (" << remaining << ")\n";
00575 }
00576
00577 bool pic_randomizer::save_points()
00578 {
00579 if (!points) return true;
00580
00581 FILE *ofile = open_and_lock(output_fn);
00582 if (!ofile) return false;
00583
00584 id_cluster_collection clusters(id_cluster_collection::QUERY_NORMALIZED_FREQ);
00585 for (bucket2d<point_cluster>::iterator it(*points); !it.end(); ++it)
00586 {
00587 clusters.add_cluster(it.elem());
00588 }
00589
00590 clusters.save(ofile);
00591
00592 delete points;
00593 points=0;
00594
00595 unlock_and_close(ofile);
00596 return true;
00597 }
00598
00599 bool pic_randomizer::run()
00600 {
00601 for (int i=0; i<nb_views; i++) {
00602 gen_view();
00603 detect_kpts();
00604 if (save_descriptors_only) {
00605 if (!save_keypoints()) return false;
00606 } else
00607 group_ids();
00608 }
00609 if (!save_descriptors_only) {
00610 prune();
00611 if (!save_points()) return false;
00612 }
00613 return true;
00614 }