36 #include "SiftGPU/SiftGPU.h"
44 void ScaleKeypoints(
const Bitmap& bitmap,
const Camera& camera,
46 if (
static_cast<size_t>(bitmap.Width()) != camera.Width() ||
47 static_cast<size_t>(bitmap.Height()) != camera.Height()) {
48 const float scale_x =
static_cast<float>(camera.Width()) / bitmap.Width();
49 const float scale_y =
static_cast<float>(camera.Height()) / bitmap.Height();
50 for (
auto& keypoint : *keypoints) {
51 keypoint.Rescale(scale_x, scale_y);
59 BitmapColor<uint8_t>
color;
60 for (
size_t i = 0; i < keypoints->size(); ++i) {
61 if (!mask.GetPixel(
static_cast<int>(keypoints->at(i).x),
62 static_cast<int>(keypoints->at(i).y), &
color) ||
69 keypoints->at(out_index) = keypoints->at(i);
70 for (
int col = 0; col <
descriptors->cols(); ++col) {
71 (*descriptors)(out_index, col) = (*
descriptors)(i, col);
78 keypoints->resize(out_index);
87 : reader_options_(reader_options),
88 sift_options_(sift_options),
89 database_(reader_options_.database_path),
90 image_reader_(reader_options_, &database_) {
91 CHECK(reader_options_.
Check());
92 CHECK(sift_options_.
Check());
94 std::shared_ptr<Bitmap> camera_mask;
96 camera_mask = std::shared_ptr<Bitmap>(
new Bitmap());
99 std::cerr <<
" ERROR: Cannot read camera mask file: "
101 <<
". No mask is going to be used." <<
std::endl;
107 CHECK_GT(num_threads, 0);
111 const int kQueueSize = 1;
117 for (
int i = 0; i < num_threads; ++i) {
120 extractor_queue_.get()));
126 std::vector<int> gpu_indices = CSVToVector<int>(sift_options_.
gpu_index);
127 CHECK_GT(gpu_indices.size(), 0);
130 if (gpu_indices.size() == 1 && gpu_indices[0] == -1) {
132 CHECK_GT(num_cuda_devices, 0);
133 gpu_indices.resize(num_cuda_devices);
134 std::iota(gpu_indices.begin(), gpu_indices.end(), 0);
138 auto sift_gpu_options = sift_options_;
139 for (
const auto& gpu_index : gpu_indices) {
142 sift_gpu_options, camera_mask, extractor_queue_.get(),
143 writer_queue_.get()));
151 <<
"WARNING: Your current options use the maximum number of "
152 "threads on the machine to extract features. Exracting SIFT "
153 "features on the CPU can consume a lot of RAM per thread for "
154 "large images. Consider reducing the maximum image size and/or "
155 "the first octave or manually limit the number of extraction "
156 "threads. Ignore this warning, if your machine has sufficient "
157 "memory for the current settings."
161 auto custom_sift_options = sift_options_;
162 custom_sift_options.
use_gpu =
false;
163 for (
int i = 0; i < num_threads; ++i) {
165 custom_sift_options, camera_mask, extractor_queue_.get(),
166 writer_queue_.get()));
171 image_reader_.
NumImages(), &database_, writer_queue_.get()));
174 void SiftFeatureExtractor::Run() {
177 for (
auto& resizer : resizers_) {
181 for (
auto& extractor : extractors_) {
187 for (
auto& extractor : extractors_) {
188 if (!extractor->CheckValidSetup()) {
195 resizer_queue_->Stop();
196 extractor_queue_->Stop();
197 resizer_queue_->Clear();
198 extractor_queue_->Clear();
202 internal::ImageData image_data;
204 image_reader_.
Next(&image_data.camera, &image_data.image,
205 &image_data.bitmap, &image_data.mask);
208 image_data.bitmap.Deallocate();
212 CHECK(resizer_queue_->Push(image_data));
214 CHECK(extractor_queue_->Push(image_data));
218 resizer_queue_->Wait();
219 resizer_queue_->Stop();
220 for (
auto& resizer : resizers_) {
224 extractor_queue_->Wait();
225 extractor_queue_->Stop();
226 for (
auto& extractor : extractors_) {
230 writer_queue_->Wait();
231 writer_queue_->Stop();
238 const std::string& import_path)
239 : reader_options_(reader_options), import_path_(import_path) {}
241 void FeatureImporter::Run() {
245 std::cerr <<
" ERROR: Import directory does not exist." <<
std::endl;
250 ImageReader image_reader(reader_options_, &database);
252 while (image_reader.NextIndex() < image_reader.NumImages()) {
258 image_reader.NextIndex() + 1,
259 image_reader.NumImages())
266 if (image_reader.Next(&camera, &
image, &bitmap,
nullptr) !=
278 std::cout <<
" Features: " << keypoints.size() <<
std::endl;
280 DatabaseTransaction database_transaction(&database);
286 if (!database.ExistsKeypoints(
image.ImageId())) {
287 database.WriteKeypoints(
image.ImageId(), keypoints);
290 if (!database.ExistsDescriptors(
image.ImageId())) {
294 std::cout <<
" SKIP: No features found at " <<
path <<
std::endl;
306 : max_image_size_(max_image_size),
307 input_queue_(input_queue),
308 output_queue_(output_queue) {}
310 void ImageResizerThread::Run() {
316 const auto input_job = input_queue_->Pop();
317 if (input_job.IsValid()) {
318 auto image_data = input_job.Data();
321 if (
static_cast<int>(image_data.bitmap.Width()) > max_image_size_ ||
322 static_cast<int>(image_data.bitmap.Height()) > max_image_size_) {
325 static_cast<double>(max_image_size_) /
326 std::max(image_data.bitmap.Width(), image_data.bitmap.Height());
327 const int new_width =
328 static_cast<int>(image_data.bitmap.Width() * scale);
329 const int new_height =
330 static_cast<int>(image_data.bitmap.Height() * scale);
332 image_data.bitmap.Rescale(new_width, new_height);
336 output_queue_->Push(image_data);
345 const std::shared_ptr<Bitmap>& camera_mask,
347 : sift_options_(sift_options),
348 camera_mask_(camera_mask),
349 input_queue_(input_queue),
350 output_queue_(output_queue) {
351 CHECK(sift_options_.
Check());
360 void SiftFeatureExtractorThread::Run() {
361 std::unique_ptr<SiftGPU> sift_gpu;
364 CHECK(opengl_context_);
365 opengl_context_->MakeCurrent();
368 sift_gpu.reset(
new SiftGPU);
370 std::cerr <<
"ERROR: SiftGPU not fully supported." <<
std::endl;
383 const auto input_job = input_queue_->Pop();
384 if (input_job.IsValid()) {
385 auto image_data = input_job.Data();
388 bool success =
false;
392 sift_options_, image_data.bitmap, &image_data.keypoints,
393 &image_data.descriptors);
394 }
else if (sift_options_.
use_gpu) {
396 sift_options_, image_data.bitmap, sift_gpu.get(),
397 &image_data.keypoints, &image_data.descriptors);
400 &image_data.keypoints,
401 &image_data.descriptors);
404 ScaleKeypoints(image_data.bitmap, image_data.camera,
405 &image_data.keypoints);
407 MaskKeypoints(*camera_mask_, &image_data.keypoints,
408 &image_data.descriptors);
410 if (image_data.mask.Data()) {
411 MaskKeypoints(image_data.mask, &image_data.keypoints,
412 &image_data.descriptors);
419 image_data.bitmap.Deallocate();
421 output_queue_->Push(image_data);
431 : num_images_(num_images), database_(database), input_queue_(input_queue) {}
433 void FeatureWriterThread::Run() {
434 size_t image_index = 0;
440 auto input_job = input_queue_->Pop();
441 if (input_job.IsValid()) {
442 auto& image_data = input_job.Data();
446 std::cout <<
StringPrintf(
"Processed file [%d/%d]", image_index,
451 image_data.image.Name().c_str())
455 std::cout <<
" SKIP: Features for image already extracted."
458 std::cout <<
" ERROR: Failed to read image file format." <<
std::endl;
459 }
else if (image_data.status ==
461 std::cout <<
" ERROR: Single camera specified, "
462 "but images have different dimensions."
464 }
else if (image_data.status ==
466 std::cout <<
" ERROR: Image previously processed, but current image "
467 "has different dimensions."
470 std::cout <<
" ERROR: Camera has invalid parameters." <<
std::endl;
472 std::cout <<
" ERROR: Failed to extract features." <<
std::endl;
480 image_data.camera.Width(),
481 image_data.camera.Height())
484 image_data.camera.CameraId(),
485 image_data.camera.ModelName().c_str())
488 image_data.camera.MeanFocalLength());
489 if (image_data.camera.HasPriorFocalLength()) {
494 if (image_data.image.HasTvecPrior()) {
496 " GPS: LAT=%.3f, LON=%.3f, ALT=%.3f",
497 image_data.image.TvecPrior(0),
498 image_data.image.TvecPrior(1),
499 image_data.image.TvecPrior(2))
503 image_data.keypoints.size())
506 DatabaseTransaction database_transaction(database_);
509 image_data.image.SetImageId(database_->
WriteImage(image_data.image));
514 image_data.keypoints);
519 image_data.descriptors);
std::shared_ptr< core::Tensor > image
void WriteDescriptors(const image_t image_id, const FeatureDescriptors &descriptors) const
void WriteKeypoints(const image_t image_id, const FeatureKeypoints &keypoints) const
bool ExistsKeypoints(const image_t image_id) const
bool ExistsDescriptors(const image_t image_id) const
image_t WriteImage(const Image &image, const bool use_image_id=false) const
FeatureImporter(const ImageReaderOptions &reader_options, const std::string &import_path)
@ CAMERA_SINGLE_DIM_ERROR
Status Next(Camera *camera, Image *image, Bitmap *bitmap, Bitmap *mask)
const Timer & GetTimer() const
void SignalInvalidSetup()
void PrintMinutes() const
FeatureWriterThread(const size_t num_images, Database *database, JobQueue< ImageData > *input_queue)
ImageResizerThread(const int max_image_size, JobQueue< ImageData > *input_queue, JobQueue< ImageData > *output_queue)
QTextStream & endl(QTextStream &stream)
static const std::string path
bool ExtractCovariantSiftFeaturesCPU(const SiftExtractionOptions &options, const Bitmap &bitmap, FeatureKeypoints *keypoints, FeatureDescriptors *descriptors)
bool CreateSiftGPUExtractor(const SiftExtractionOptions &options, SiftGPU *sift_gpu)
Eigen::Matrix< uint8_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > FeatureDescriptors
bool ExistsDir(const std::string &path)
bool ExistsFile(const std::string &path)
std::string JoinPaths(T const &... paths)
bool ExtractSiftFeaturesCPU(const SiftExtractionOptions &options, const Bitmap &bitmap, FeatureKeypoints *keypoints, FeatureDescriptors *descriptors)
void PrintHeading1(const std::string &heading)
std::vector< FeatureKeypoint > FeatureKeypoints
std::string StringPrintf(const char *format,...)
const image_t kInvalidImageId
bool ExtractSiftFeaturesGPU(const SiftExtractionOptions &options, const Bitmap &bitmap, SiftGPU *sift_gpu, FeatureKeypoints *keypoints, FeatureDescriptors *descriptors)
void LoadSiftFeaturesFromTextFile(const std::string &path, FeatureKeypoints *keypoints, FeatureDescriptors *descriptors)
int GetEffectiveNumThreads(const int num_threads)
std::string to_string(const T &n)
std::string camera_mask_path
std::string database_path