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',
|
||||
)
|
||||
|
||||
cpp = meson.get_compiler('cpp')
|
||||
fs = import('fs')
|
||||
|
||||
subdir('src')
|
||||
|
|
|
@ -1,12 +1,107 @@
|
|||
#include "config.h"
|
||||
#include <taskflow/taskflow.hpp>
|
||||
#include <opencv2/flann.hpp>
|
||||
#include <opencv2/imgcodecs.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/features2d.hpp>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
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
|
||||
|
||||
int main (int argc, char* argv[]) {
|
||||
|
@ -19,15 +114,16 @@ int main (int argc, char* argv[]) {
|
|||
}
|
||||
|
||||
try {
|
||||
cv::Mat image = cv::imread(argv[1]);
|
||||
if (image.empty())
|
||||
throw runtime_error(string("Unable to load image \"") + argv[1] + "\"");
|
||||
std::cout << PROGRAM_NAME << " started\n";
|
||||
auto features = mgs::process_directory(argv[1]);
|
||||
std::cout << "Received " << features.size() << " features\n";
|
||||
|
||||
cv::String win_name{PROGRAM_NAME};
|
||||
cv::namedWindow(win_name);
|
||||
cv::imshow(win_name, image);
|
||||
cv::waitKey(0);
|
||||
cv::destroyWindow(win_name);
|
||||
//cv::flann::Index flann_index(
|
||||
// features,
|
||||
// cv::flann::KDTreeIndexParams(4),
|
||||
// cvflann::FLANN_DIST_EUCLIDEAN
|
||||
//);
|
||||
//flann_index.save("flann_index.bin");
|
||||
}
|
||||
catch (const runtime_error& err) {
|
||||
std::cerr << "Unhandled exception: " << err.what() << '\n';
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
project_name = fs.name(meson.current_source_dir())
|
||||
|
||||
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.set('PROGRAM_NAME', project_name)
|
||||
|
@ -10,6 +12,8 @@ executable(project_name,
|
|||
'main.cpp',
|
||||
dependencies: [
|
||||
libopencv_dep,
|
||||
fslib_dep,
|
||||
taskflow_dep,
|
||||
],
|
||||
install: true,
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue