Multithreaded sift computation
This commit is contained in:
parent
2eaabd7a95
commit
cf8a06b321
3 changed files with 110 additions and 9 deletions
|
@ -4,6 +4,7 @@ project('magicstore', 'cpp',
|
||||||
meson_version: '>=0.53.0',
|
meson_version: '>=0.53.0',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cpp = meson.get_compiler('cpp')
|
||||||
fs = import('fs')
|
fs = import('fs')
|
||||||
|
|
||||||
subdir('src')
|
subdir('src')
|
||||||
|
|
|
@ -1,12 +1,107 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include <taskflow/taskflow.hpp>
|
||||||
#include <opencv2/flann.hpp>
|
#include <opencv2/flann.hpp>
|
||||||
#include <opencv2/imgcodecs.hpp>
|
#include <opencv2/imgcodecs.hpp>
|
||||||
#include <opencv2/highgui.hpp>
|
#include <opencv2/features2d.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <vector>
|
||||||
|
#include <utility>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string_view>
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
namespace mgs {
|
namespace mgs {
|
||||||
|
struct CVFeature {
|
||||||
|
std::vector<cv::KeyPoint> keypoints;
|
||||||
|
cv::Mat descriptors;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, CVFeature>> process_directory (const fs::path& base_dir) {
|
||||||
|
using std::runtime_error;
|
||||||
|
typedef std::vector<std::pair<std::string, CVFeature>> RetVector;
|
||||||
|
|
||||||
|
auto sift = cv::SIFT::create();
|
||||||
|
RetVector features;
|
||||||
|
|
||||||
|
features.reserve(
|
||||||
|
std::count_if(
|
||||||
|
fs::directory_iterator{base_dir},
|
||||||
|
fs::directory_iterator{},
|
||||||
|
[](const fs::path& p) -> bool {return fs::is_regular_file(p);}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
//First just init the key part of each array entry to be the path of the
|
||||||
|
//picture we're going to inspect
|
||||||
|
std::atomic_size_t total_mem {sizeof(RetVector::value_type) * features.size()};
|
||||||
|
for (const fs::directory_entry& curr : fs::directory_iterator(base_dir)) {
|
||||||
|
if (not fs::is_regular_file(curr))
|
||||||
|
continue;
|
||||||
|
if (features.capacity() == features.size())
|
||||||
|
break; //quit early, the directory is expanding and we don't care
|
||||||
|
|
||||||
|
std::string key = curr.path();
|
||||||
|
total_mem += key.size() * sizeof(decltype(key)::value_type);
|
||||||
|
features.emplace_back(std::move(key), CVFeature{});
|
||||||
|
}
|
||||||
|
|
||||||
|
tf::Taskflow taskflow;
|
||||||
|
std::mutex print_mutex;
|
||||||
|
std::atomic_size_t count{};
|
||||||
|
|
||||||
|
//features.resize(1000);
|
||||||
|
for (RetVector::value_type& item : features) {
|
||||||
|
fs::path curr{base_dir / item.first};
|
||||||
|
if (not fs::is_regular_file(curr))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
taskflow.emplace([&item, &base_dir, &print_mutex, &total_mem, &sift, &count]() {
|
||||||
|
fs::path curr{base_dir / item.first};
|
||||||
|
const std::string filename = curr;
|
||||||
|
cv::Mat image = cv::imread(filename, cv::IMREAD_GRAYSCALE);
|
||||||
|
|
||||||
|
if (image.empty()) {
|
||||||
|
std::unique_lock<std::mutex> lock(print_mutex);
|
||||||
|
std::cerr << "Unable to load image \"" << filename << "\"\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CVFeature& feature = item.second;
|
||||||
|
sift->detectAndCompute(image, cv::noArray(), feature.keypoints, feature.descriptors);
|
||||||
|
const auto curr_total_mem = total_mem +=
|
||||||
|
item.first.size() * sizeof(decltype(item.first)::value_type) +
|
||||||
|
feature.keypoints.size() * sizeof(decltype(feature.keypoints)::value_type) +
|
||||||
|
feature.descriptors.step * feature.descriptors.rows;
|
||||||
|
|
||||||
|
const auto curr_count = ++count;
|
||||||
|
if (curr_count % 500 == 0)
|
||||||
|
std::cout << "Processed " << curr_count << " inputs, total mem = "
|
||||||
|
<< static_cast<double>(curr_total_mem) / 1024.0 / 1024.0 << " MiB\n";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
tf::Executor executor;
|
||||||
|
executor.run(taskflow).wait();
|
||||||
|
|
||||||
|
for (std::size_t z = features.size(); z > 0; --z) {
|
||||||
|
const CVFeature& feature = features[z - 1].second;
|
||||||
|
if (feature.keypoints.empty()) {
|
||||||
|
total_mem -= features[z - 1].first.size() * sizeof(decltype(features[z - 1].first)::value_type);
|
||||||
|
features.erase(features.begin() + (z - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Total used memory: " << static_cast<float>(total_mem) / (1024.0f * 1024.0f) << " MiB\n";
|
||||||
|
//cv::Mat index_mat{features.size(), CV_32FC1};
|
||||||
|
//cv::flann::Index flann_index{index_mat, cv::flann::KDTreeIndexParams(4), cvflann::FLANN_DIST_EUCLIDEAN};
|
||||||
|
return features;
|
||||||
|
}
|
||||||
} //namespace mgs
|
} //namespace mgs
|
||||||
|
|
||||||
int main (int argc, char* argv[]) {
|
int main (int argc, char* argv[]) {
|
||||||
|
@ -19,15 +114,16 @@ int main (int argc, char* argv[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
cv::Mat image = cv::imread(argv[1]);
|
std::cout << PROGRAM_NAME << " started\n";
|
||||||
if (image.empty())
|
auto features = mgs::process_directory(argv[1]);
|
||||||
throw runtime_error(string("Unable to load image \"") + argv[1] + "\"");
|
std::cout << "Received " << features.size() << " features\n";
|
||||||
|
|
||||||
cv::String win_name{PROGRAM_NAME};
|
//cv::flann::Index flann_index(
|
||||||
cv::namedWindow(win_name);
|
// features,
|
||||||
cv::imshow(win_name, image);
|
// cv::flann::KDTreeIndexParams(4),
|
||||||
cv::waitKey(0);
|
// cvflann::FLANN_DIST_EUCLIDEAN
|
||||||
cv::destroyWindow(win_name);
|
//);
|
||||||
|
//flann_index.save("flann_index.bin");
|
||||||
}
|
}
|
||||||
catch (const runtime_error& err) {
|
catch (const runtime_error& err) {
|
||||||
std::cerr << "Unhandled exception: " << err.what() << '\n';
|
std::cerr << "Unhandled exception: " << err.what() << '\n';
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
project_name = fs.name(meson.current_source_dir())
|
project_name = fs.name(meson.current_source_dir())
|
||||||
|
|
||||||
libopencv_dep = dependency('opencv4')
|
libopencv_dep = dependency('opencv4')
|
||||||
|
fslib_dep = cpp.find_library('stdc++fs', required: false)
|
||||||
|
taskflow_dep = dependency('Taskflow', method: 'cmake', version: '>=3.2.0')
|
||||||
|
|
||||||
conf = configuration_data()
|
conf = configuration_data()
|
||||||
conf.set('PROGRAM_NAME', project_name)
|
conf.set('PROGRAM_NAME', project_name)
|
||||||
|
@ -10,6 +12,8 @@ executable(project_name,
|
||||||
'main.cpp',
|
'main.cpp',
|
||||||
dependencies: [
|
dependencies: [
|
||||||
libopencv_dep,
|
libopencv_dep,
|
||||||
|
fslib_dep,
|
||||||
|
taskflow_dep,
|
||||||
],
|
],
|
||||||
install: true,
|
install: true,
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue