Skip to content

Commit

Permalink
Added OpenMP parallelism on image localization (openMVG#638)
Browse files Browse the repository at this point in the history
* Added OpenMP parallelism on image localization and possibility of defining different matching folder for new images

* Corrected critical areas for adding views and assigning view ids

* Removed temporal logging and added switch to decide if structure is exported

* Added missing description of export_structure parameter in help

* Added EXTRINSICS to default export option

* Updated documentation
  • Loading branch information
hcjghr authored and pmoulon committed Dec 26, 2016
1 parent 0dbd991 commit d749b68
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 18 deletions.
6 changes: 6 additions & 0 deletions docs/sphinx/rst/software/localization/localization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ Arguments description:

- **[-o|--out_dir]** path to save the found camera position

- **[-u|--match_out_dir]** path to the directory where new matches will be stored.
If empty matches are stored in match_dir directory.

- **[-q|--query_image_dir]** path to an image OR to the directory containing the images that must be localized
(the directory can also contain the images from the initial reconstruction)

Expand All @@ -30,6 +33,9 @@ Arguments description:
- **[-s|--single_intrinsics]** (switch) when switched on, the program will check if the input sfm_data
contains a single intrinsics and, if so, take this value as intrinsics for the query images.
(OFF by default)
- **[-e|--export_structure]** (switch) when switched on, the program will also export structure to output sfm_data while OFF will only export VIEWS, INTRINSICS and EXTRINSICS.
(OFF by default)
- **[-n|--numThreads]** number of thread(s)

.. code-block:: c++

Expand Down
93 changes: 75 additions & 18 deletions src/software/Localization/main_SfM_Localization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,28 @@ int main(int argc, char **argv)
std::string sSfM_Data_Filename;
std::string sMatchesDir;
std::string sOutDir = "";
std::string sMatchesOutDir;
std::string sQueryDir;
double dMaxResidualError = std::numeric_limits<double>::infinity();
bool bUseSingleIntrinsics = false;
bool bExportStructure = false;

#ifdef OPENMVG_USE_OPENMP
int iNumThreads = 0;
#endif

cmd.add( make_option('i', sSfM_Data_Filename, "input_file") );
cmd.add( make_option('m', sMatchesDir, "match_dir") );
cmd.add( make_option('o', sOutDir, "out_dir") );
cmd.add( make_option('u', sMatchesOutDir, "match_out_dir") );
cmd.add( make_option('q', sQueryDir, "query_image_dir"));
cmd.add( make_option('r', dMaxResidualError, "residual_error"));
cmd.add( make_switch('s', "single_intrinsics"));
cmd.add( make_switch('e', "export_structure"));
#ifdef OPENMVG_USE_OPENMP
cmd.add( make_option('n', iNumThreads, "numThreads") );
#endif


try {
if (argc == 1) throw std::string("Invalid parameter.");
Expand All @@ -69,6 +82,8 @@ int main(int argc, char **argv)
<< "[-m|--match_dir] path to the directory containing the matches\n"
<< " corresponding to the provided SfM_Data scene\n"
<< "[-o|--out_dir] path where the output data will be stored\n"
<< "[-u|--match_out_dir] path to the directory where new matches will be stored\n"
<< " (if empty the initial matching directory will be used)\n"
<< "[-q|--query_image_dir] path to an image OR to the directory containing the images that must be localized\n"
<< " (the directory can also contain the images from the initial reconstruction)\n"
<< "\n"
Expand All @@ -77,6 +92,11 @@ int main(int argc, char **argv)
<< "[-s|--single_intrinsics] (switch) when switched on, the program will check if the input sfm_data\n"
<< " contains a single intrinsics and, if so, take this value as intrinsics for the query images.\n"
<< " (OFF by default)\n"
<< "[-e|--export_structure] (switch) when switched on, the program will also export structure to output sfm_data.\n"
<< " if OFF only VIEWS, INTRINSICS and EXTRINSICS are exported (OFF by default)\n"
#ifdef OPENMVG_USE_OPENMP
<< "[-n|--numThreads] number of thread(s)\n"
#endif
<< std::endl;

std::cerr << s << std::endl;
Expand All @@ -91,13 +111,20 @@ int main(int argc, char **argv)
return EXIT_FAILURE;
}

if (sMatchesOutDir.empty())
{
sMatchesOutDir = sMatchesDir;
}

if (sfm_data.GetPoses().empty() || sfm_data.GetLandmarks().empty())
{
std::cerr << std::endl
<< "The input SfM_Data file have not 3D content to match with." << std::endl;
return EXIT_FAILURE;
}

bUseSingleIntrinsics = cmd.used('s');
bExportStructure = cmd.used('e');
// ---------------
// Initialization
// ---------------
Expand Down Expand Up @@ -162,7 +189,7 @@ int main(int argc, char **argv)
if (!stlplus::folder_exists(sOutDir))
stlplus::folder_create(sOutDir);

if (cmd.used('s') && sfm_data.GetIntrinsics().size() != 1)
if (bUseSingleIntrinsics && sfm_data.GetIntrinsics().size() != 1)
{
std::cout << "More than one intrinsics to compare to in input scene "
<< " => Consider intrinsics as unkown." << std::endl;
Expand Down Expand Up @@ -235,19 +262,25 @@ int main(int argc, char **argv)
const int num_initial_intrinsics = sfm_data.GetIntrinsics().size();

int total_num_images = 0;
for ( std::vector<std::string>::const_iterator iter_image = vec_image_new.begin();
iter_image != vec_image_new.end();
++iter_image)

#ifdef OPENMVG_USE_OPENMP
const unsigned int nb_max_thread = (iNumThreads == 0) ? 0 : omp_get_max_threads();
omp_set_num_threads(nb_max_thread);
#pragma omp parallel for schedule(dynamic)
#endif
for (int i = 0; i < static_cast<int>(vec_image_new.size()); ++i)
{
std::vector<std::string>::const_iterator iter_image = vec_image_new.begin();
std::advance(iter_image, i);


// Test if the image format is supported:
if (openMVG::image::GetFormat((*iter_image).c_str()) == openMVG::image::Unknown)
{
std::cerr << *iter_image << " : unknown image file format." << std::endl;
continue;
}

total_num_images++;

std::cout << "SfM::localization => try with image: " << *iter_image << std::endl;
std::unique_ptr<Regions> query_regions(regions_type->EmptyClone());
image::Image<unsigned char> imageGray;
Expand All @@ -261,8 +294,8 @@ int main(int argc, char **argv)
}

const std::string
sFeat = stlplus::create_filespec(sMatchesDir, stlplus::basename_part(sView_filename.c_str()), "feat"),
sDesc = stlplus::create_filespec(sMatchesDir, stlplus::basename_part(sView_filename.c_str()), "desc");
sFeat = stlplus::create_filespec(sMatchesOutDir, stlplus::basename_part(sView_filename.c_str()), "feat"),
sDesc = stlplus::create_filespec(sMatchesOutDir, stlplus::basename_part(sView_filename.c_str()), "desc");

// Compute features and descriptors and save them if they don't exist yet
if (!stlplus::file_exists(sFeat) || !stlplus::file_exists(sDesc))
Expand All @@ -278,7 +311,7 @@ int main(int argc, char **argv)
}

std::shared_ptr<cameras::IntrinsicBase> optional_intrinsic(nullptr);
if (cmd.used('s') && num_initial_intrinsics == 1)
if (bUseSingleIntrinsics && num_initial_intrinsics == 1)
{
optional_intrinsic = std::make_shared<cameras::Pinhole_Intrinsic_Radial_K3>(
imageGray.Width(), imageGray.Height(),
Expand All @@ -290,8 +323,7 @@ int main(int argc, char **argv)
sfm::Image_Localizer_Match_Data matching_data;
matching_data.error_max = dMaxResidualError;

// Build the view corresponding to the image
View v(*iter_image, views.size(), views.size(), views.size(), imageGray.Width(), imageGray.Height());
bool bSuccessfulLocalization = false;

// Try to localize the image in the database thanks to its regions
if (!localizer.Localize(
Expand All @@ -302,8 +334,7 @@ int main(int argc, char **argv)
&matching_data))
{
std::cerr << "Cannot locate the image " << *iter_image << std::endl;
v.id_intrinsic = UndefinedIndexT;
v.id_pose = UndefinedIndexT;
bSuccessfulLocalization = false;
}
else
{
Expand Down Expand Up @@ -335,19 +366,36 @@ int main(int argc, char **argv)
{
std::cerr << "Refining pose for image " << *iter_image << " failed." << std::endl;
}


bSuccessfulLocalization = true;

}
#ifdef OPENMVG_USE_OPENMP
#pragma omp critical
#endif
{
total_num_images++;

View v(*iter_image, views.size(), views.size(), views.size(), imageGray.Width(), imageGray.Height());
if(bSuccessfulLocalization)
{
vec_found_poses.push_back(pose.center());
// Build the view corresponding to the image

// Add the computed intrinsic to the sfm_container
intrinsics[v.id_intrinsic] = optional_intrinsic;

// Add the computed pose to the sfm_container
poses[v.id_pose] = pose;


}
else
{
v.id_intrinsic = UndefinedIndexT;
v.id_pose = UndefinedIndexT;
}

// Add the view to the sfm_container
views[v.id_view] = std::make_shared<View>(v);
}
}

GroupSharedIntrinsics(sfm_data);
Expand All @@ -359,10 +407,19 @@ int main(int argc, char **argv)
plyHelper::exportToPly(vec_found_poses, out_file_name);

// Export found camera poses along with original reconstruction in a new sfm_data file
ESfM_Data flag_save;
if(bExportStructure)
{
flag_save = ESfM_Data(ALL);
}
else
{
flag_save = ESfM_Data(VIEWS|INTRINSICS|EXTRINSICS);
}
if (!Save(
sfm_data,
stlplus::create_filespec( sOutDir, "sfm_data_expanded.json" ).c_str(),
ESfM_Data(ALL)))
flag_save))
{
return EXIT_FAILURE;
}
Expand Down

0 comments on commit d749b68

Please sign in to comment.