/* Copyright (C) 2014 Jerome Revaud This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see */ #include "std.h" #include "image.h" #include "io.h" #include "deep_matching.h" #include "main.h" #include void usage(const int language) { #define p(msg) std_printf(msg "\n"); p("usage:"); switch(language){ case EXE_OPTIONS: p("./deepmatching image1 image2 [options]"); p("Compute the 'DeepMatching' between two images and print a list of") p("pair-wise point correspondences:") p(" x1 y1 x2 y2 score index ...") p("(index refers to the local maximum from which the match was retrieved)") p("Images must be in PPM, PNG or JPG format. Version 1.2.2") break; case MATLAB_OPTIONS: p("matches = deepmatching(image1, image2 [, options])") p("Compute the 'DeepMatching' between two images.") p("Images must be HxWx3 single matrices.") p("Options is an optional string argument ('' by default).") p("The function returns a matrix with 6 columns, each row being x1 y1 x2 y2 score index.") p("(index refers to the local maximum from which the match was retrieved)") p("Version 1.2.2") break; case PYTHON_OPTIONS: p("matches = deepmatching.deepmatching(image1, image2, options='')") p("Compute the 'DeepMatching' between two images.") p("Images must be HxWx3 numpy arrays (converted to float32).") p("Options is an optional string argument ('' by default).") p("The function returns a numpy array with 6 columns, each row being x1 y1 x2 y2 score index.") p("(index refers to the local maximum from which the match was retrieved)") p("Version 1.2.2") break; } p("") p("Options:") p(" -h, --help print this message") //p(" HOG parameters (low-level pixel descriptor):") //p(" -png_settings (auto) recommended for uncompressed images") //p(" -jpg_settings (auto) recommended for compressed images") //p(" in more details: (for fine-tuning)") //p(" -hog.presm prior image smoothing") //p(" -hog.midsm intermediate HOG smoothing") //p(" -hog.sig sigmoid strength") //p(" -hog.postsm final HOG-smoothing") //p(" -hog.ninth robustness to pixel noise (eg. JPEG artifacts)") p("") p(" Matching parameters:") //p(" -iccv_settings settings used for the ICCV paper") //p(" -improved_settings (default) supposedly improved settings") //p(" in more details: (for fine-tuning)") p(" -downscale/-R downsize the input images by a factor 2^n") //p(" -overlap use overlapping patches in image1 from level n") //p(" -subref 0: denser sampling or 1: not of image1 patches") p(" -ngh_rad if n>0: restrict matching to n pxl neighborhood") p(" -nlpow non-linear rectification x := x^f") //p(" -maxima_mode 0: from all top cells / 1: from local maxima") //p(" -min_level skip maxima in levels [0, 1, ..., n-1]") p(" -mem if n>0: optimize memory footprint (bit unstable)") //p(" -scoring_mode type of correspondence scoring mode (0/1)") p("") p(" Fully scale & rotation invariant DeepMatching:") p(" if either one of these options is used, then this mode is activated:") p(" -max_scale max scaling factor") p(" -rot_range rotation range") p("") p(" Other parameters:") p(" -resize to resize input images beforehand") p(" -v increase verbosity") p(" -nt multi-threading with threads") if(language==EXE_OPTIONS) { p(" -out output correspondences in a file") exit(1);} } bool endswith(const char *str, const char *suffix) { if(!str || !suffix) return false; size_t lenstr = strlen(str); size_t lensuffix = strlen(suffix); if(lensuffix > lenstr) return false; return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; } image_t* rescale_image( image_t* im, int width, int height ) { image_t* res = image_new(width,height); image_resize_bilinear_newsize(res, im, width, height); image_delete(im); return res; } const char *parse_options(dm_params_t *params, scalerot_params_t *sr_params, bool *use_scalerot, float *fx, float *fy, const int argc, const char **argv, const int language, image_t **im1, image_t **im2) { int current_arg = 0; const char* out_filename = NULL; // parse options while(current_arg < argc) { const char* a = argv[current_arg++]; #define isarg(key) !strcmp(a,key) if(isarg("-h") || isarg("--help") ) usage(language); // HOG and patch parameters //else if(isarg("-hog.presm")) // params->desc_params.presmooth_sigma = atof(argv[current_arg++]); //else if(isarg("-hog.sig")) // params->desc_params.hog_sigmoid = atof(argv[current_arg++]); //else if(isarg("-hog.midsm")) // params->desc_params.mid_smoothing = atof(argv[current_arg++]); //else if(isarg("-hog.postsm")) // params->desc_params.post_smoothing = atof(argv[current_arg++]); //else if(isarg("-hog.ninth")) // params->desc_params.ninth_dim = atof(argv[current_arg++]); //else if(isarg("-hog.nrmpix")) // params->desc_params.norm_pixels = atof(argv[current_arg++]); else if(isarg("-png_settings")) { params->desc_params.presmooth_sigma = 0; // no image smoothing since the image is uncompressed params->desc_params.hog_sigmoid = 0.2; params->desc_params.mid_smoothing = 1.5; params->desc_params.post_smoothing = 1; params->desc_params.ninth_dim = 0.1; } // low ninth_dim since image PSNR is high else if(isarg("-jpg_settings")) { params->desc_params.presmooth_sigma = 1; // smooth the image to remove jpg artifacts params->desc_params.hog_sigmoid = 0.2; params->desc_params.mid_smoothing = 1.5; params->desc_params.post_smoothing = 1; params->desc_params.ninth_dim = 0.3; } // higher ninth_dim because of pixel noise // matching parameters else if(isarg("-R") || isarg("-downscale")) params->prior_img_downscale = atoi(argv[current_arg++]); //else if(isarg("-overlap")) // params->overlap = atoi(argv[current_arg++]); //else if(isarg("-subref")) // params->subsample_ref = atoi(argv[current_arg++]); else if(isarg("-nlpow")) params->nlpow = atof(argv[current_arg++]); else if(isarg("-ngh_rad")) params->ngh_rad = atoi(argv[current_arg++]); // maxima parameters //else if(isarg("-maxima_mode")) // params->maxima_mode = atoi(argv[current_arg++]); else if(isarg("-mem")) { params->low_mem = atoi(argv[current_arg++]); } //else if(isarg("-min_level")) // params->min_level = atoi(argv[current_arg++]); //else if(isarg("-scoring_mode")) // params->scoring_mode = atoi(argv[current_arg++]); //else if(isarg("-iccv_settings")) { // params->prior_img_downscale = 2; // params->overlap = 0; // overlap from level 0 // params->subsample_ref = 1; // params->nlpow = 1.6; // params->maxima_mode = 1; // params->low_mem = 0; // params->min_level = 2; // params->scoring_mode = 0; } //else if(isarg("-improved_settings")) { // params->prior_img_downscale = 1; // less down-scale // params->overlap = 999; // no overlap // params->subsample_ref = 0; // dense patch sampling at every level in first image // params->nlpow = 1.4; // params->maxima_mode = 0; // params->low_mem = 1; // params->min_level = 2; // params->scoring_mode = 1; } // improved scoring //else if(isarg("-max_psize")) { // params->max_psize = atoi(argv[current_arg++]); } // scale & rot invariant version else if(isarg("-scale") || isarg("-max_scale")) { *use_scalerot = true; float scale = atof(argv[current_arg++]); sr_params->max_sc0 = sr_params->max_sc1 = int(1 + 2*log2(scale)); } else if(isarg("-rot") || isarg("-rot_range")) { *use_scalerot = true; int min_rot = atoi(argv[current_arg++]); int max_rot = atoi(argv[current_arg++]); while( min_rot < 0 ) { min_rot += 360; max_rot += 360; } sr_params->min_rot = int(floor(0.5 + min_rot/45.)); sr_params->max_rot = int(floor(1.5 + max_rot/45.)); while( sr_params->max_rot - sr_params->min_rot > 8 ) sr_params->max_rot--; assert( sr_params->min_rot < sr_params->max_rot ); } // other parameters else if(isarg("-resize")) { assert((*im1)->width==(*im2)->width && (*im1)->height==(*im2)->height); int width = atoi(argv[current_arg++]); int height = atoi(argv[current_arg++]); *fx *= (*im1)->width / float(width); *fy *= (*im1)->height / float(height); *im1 = rescale_image(*im1, width, height); *im2 = rescale_image(*im2, width, height); } else if(isarg("-v")) params->verbose++; else if(isarg("-nt")) { params->n_thread = atoi(argv[current_arg++]); if (params->n_thread==0) params->n_thread = std::thread::hardware_concurrency(); } else if(language == EXE_OPTIONS && isarg("-out")) out_filename = argv[current_arg++]; else { err_printf("error: unexpected parameter '%s'", a); exit(-1); } } if( *use_scalerot ) assert( params->ngh_rad == 0 || !"max trans cannot be used in full scale and rotation mode"); else if( params->subsample_ref && (!ispowerof2((*im1)->width) || !ispowerof2((*im1)->height)) ) { err_printf("WARNING: first image has dimension which are not power-of-2\n"); err_printf("For improved results, you should consider resizing the images with '-resize '\n"); } return out_filename; } int main(int argc, const char ** argv) { if( argc<=2 || !strcmp(argv[1],"-h") || !strcmp(argv[1],"--help") ) usage(EXE_OPTIONS); int current_arg = 3; image_t *im1=NULL, *im2=NULL; { color_image_t *cim1 = color_image_load(argv[1]); color_image_t *cim2 = color_image_load(argv[2]); // Following deactivated because quite useless/dangerous in practice // default behavior == always using -jpg_settings //if( endswith(argv[1],"png") || endswith(argv[1],"PNG") ) // argv[--current_arg] = "-png_settings"; // set default //if( endswith(argv[1],"ppm") || endswith(argv[1],"PPM") ) // argv[--current_arg] = "-png_settings"; // set default //if( endswith(argv[1],"jpg") || endswith(argv[1],"JPG") ) // argv[--current_arg] = "-jpg_settings"; // set default //if( endswith(argv[1],"jpeg") || endswith(argv[1],"JPEG") ) // argv[--current_arg] = "-jpg_settings"; // set default im1 = image_gray_from_color(cim1); im2 = image_gray_from_color(cim2); color_image_delete(cim1); color_image_delete(cim2); } // set params to default dm_params_t params; set_default_dm_params(¶ms); scalerot_params_t sr_params; set_default_scalerot_params(&sr_params); bool use_scalerot = false; float fx=1, fy=1; // parse options const char* out_filename = parse_options(¶ms, &sr_params, &use_scalerot, &fx, &fy, argc-current_arg, &argv[current_arg], EXE_OPTIONS, &im1, &im2); // compute deep matching float_image* corres = use_scalerot ? deep_matching_scale_rot( im1, im2, ¶ms, &sr_params ) : deep_matching ( im1, im2, ¶ms, NULL ); // standard call // save result output_correspondences( out_filename, (corres_t*)corres->pixels, corres->ty, fx, fy ); free_image(corres); image_delete(im1); image_delete(im2); return 0; }