Multithreaded sift computation

This commit is contained in:
King_DuckZ 2021-11-05 01:30:23 +01:00
parent 2eaabd7a95
commit cf8a06b321
3 changed files with 110 additions and 9 deletions

View file

@ -4,6 +4,7 @@ project('magicstore', 'cpp',
meson_version: '>=0.53.0',
)
cpp = meson.get_compiler('cpp')
fs = import('fs')
subdir('src')

View file

@ -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';

View file

@ -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,
)