aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--imageutil.cpp16
-rw-r--r--imageutil.hpp15
-rw-r--r--mingui/main.cpp32
-rw-r--r--mingui/mingui.cpp47
-rw-r--r--mingui/mingui.hpp13
-rw-r--r--signature.cpp7
-rw-r--r--signature.hpp3
-rw-r--r--tests/testdrive.cpp160
8 files changed, 183 insertions, 110 deletions
diff --git a/imageutil.cpp b/imageutil.cpp
index b3609d4..fd78ccf 100644
--- a/imageutil.cpp
+++ b/imageutil.cpp
@@ -1,8 +1,12 @@
#include <algorithm>
#include <cmath>
+#include <fstream>
#include <iterator>
#include <limits>
//#include <cstdio>
+
+#include <opencv2/imgcodecs.hpp>
+
#include "imageutil.hpp"
//libpuzzle uses a contrast-based cropping, and clamps the cropped area to a given percentage.
@@ -111,3 +115,15 @@ cv::Mat image_util::blend_white(cv::Mat m)
}
return ret;
}
+
+cv::Mat image_util::imread_path(const std::filesystem::path &p, int flags)
+{
+ auto size = std::filesystem::file_size(p);
+ std::fstream fst(p, std::ios::binary | std::ios::in);
+ std::vector<char> dat;
+ dat.resize(size);
+ fst.read(dat.data(), size);
+ fst.close();
+ cv::Mat img = cv::imdecode(dat, flags);
+ return img;
+}
diff --git a/imageutil.hpp b/imageutil.hpp
index 15be98b..d453311 100644
--- a/imageutil.hpp
+++ b/imageutil.hpp
@@ -2,6 +2,7 @@
#define IMAGEUTIL_HPP
#include <cstdlib>
+#include <filesystem>
#include <vector>
#include <opencv2/core.hpp>
@@ -9,13 +10,15 @@
#define sqr(x) ((x) * (x))
-class image_util
+
+
+namespace image_util
{
-public:
- static cv::Mat crop(cv::InputArray s, double contrast_threshold, double max_crop_ratio);
- static cv::Range crop_axis(cv::InputArray s, int axis, double contrast_threshold, double max_crop_ratio);
- static double median(std::vector<double> &v);
- static cv::Mat blend_white(cv::Mat m);
+ cv::Mat crop(cv::InputArray s, double contrast_threshold, double max_crop_ratio);
+ cv::Range crop_axis(cv::InputArray s, int axis, double contrast_threshold, double max_crop_ratio);
+ double median(std::vector<double> &v);
+ cv::Mat blend_white(cv::Mat m);
+ cv::Mat imread_path(const std::filesystem::path &p, int flags);
template<class T, int B>
static double length(const compressed_vector<T, B> &v, T center)
diff --git a/mingui/main.cpp b/mingui/main.cpp
index 8274b6f..6856cad 100644
--- a/mingui/main.cpp
+++ b/mingui/main.cpp
@@ -1,5 +1,6 @@
#include <cstdio>
#include <algorithm>
+#include <filesystem>
#include <map>
#include <string>
#include <vector>
@@ -11,9 +12,10 @@
#include "mingui.hpp"
using std::size_t;
+namespace fs = std::filesystem;
-std::unordered_map<std::string, size_t> p;
-std::vector<std::string> fns;
+std::unordered_map<fs::path, size_t> fnmap;
+std::vector<fs::path> fns;
std::map<std::pair<size_t, size_t>, double> dist;
std::vector<size_t> par;
std::vector<std::vector<size_t>> lists;
@@ -42,25 +44,25 @@ void load_result(const char* rp)
{
int l;
double d;
- std::string s1, s2;
+ fs::path::string_type s1, s2;
if (feof(f)) break;
fread(&l, sizeof(int), 1, f);
s1.resize(l);
- fread(s1.data(), 1, l, f);
- p.try_emplace(s1, p.size() + 1);
+ fread(s1.data(), sizeof(fs::path::value_type), l, f);
+ fnmap.try_emplace(s1, fnmap.size() + 1);
fread(&l, sizeof(int), 1, f);
s2.resize(l);
- fread(s2.data(), 1, l, f);
- p.try_emplace(s2, p.size() + 1);
+ fread(s2.data(), sizeof(fs::path::value_type), l, f);
+ fnmap.try_emplace(s2, fnmap.size() + 1);
fread(&d, sizeof(double), 1, f);
- dist[std::make_pair(p[s1], p[s2])] = d;
+ dist[std::make_pair(fnmap[s1], fnmap[s2])] = d;
}
fclose(f);
}
-std::vector<std::string> build_list(const std::vector<size_t> &l)
+std::vector<fs::path> build_list(const std::vector<size_t> &l)
{
- std::vector<std::string> ret;
+ std::vector<fs::path> ret;
for (auto &x : l)
ret.push_back(fns[x]);
return ret;
@@ -88,12 +90,12 @@ int main(int argc, char **argv)
if (argc < 2) return 1;
load_result(argv[1]);
- printf("%lu known files\n", p.size());
+ printf("%lu known files\n", fnmap.size());
- par.resize(p.size() + 1);
- fns.resize(p.size() + 1);
- lists.resize(p.size() + 1);
- for (auto &kp : p)
+ par.resize(fnmap.size() + 1);
+ fns.resize(fnmap.size() + 1);
+ lists.resize(fnmap.size() + 1);
+ for (auto &kp : fnmap)
fns[kp.second] = kp.first;
for (size_t i = 1; i < par.size(); ++i)
diff --git a/mingui/mingui.cpp b/mingui/mingui.cpp
index b421512..58a6ca5 100644
--- a/mingui/mingui.cpp
+++ b/mingui/mingui.cpp
@@ -1,7 +1,8 @@
#include "mingui.hpp"
#include <cstdio>
-#include <filesystem>
+#include <cwchar>
+#include <type_traits>
#include <QDebug>
#include <QKeyEvent>
@@ -28,6 +29,15 @@ const std::vector<int> keys = {
Qt::Key::Key_U, Qt::Key::Key_I, Qt::Key::Key_O, Qt::Key::Key_P
};
+QString fsstr_to_qstring(const fs::path::string_type &s)
+{
+#ifdef _WIN32 //the degenerate platform
+ return QString::fromStdWString(s);
+#else
+ return QString::fromStdString(s);
+#endif
+}
+
MinGuiWidget::MinGuiWidget()
{
this->setFont(QFontDatabase::systemFont(QFontDatabase::SystemFont::FixedFont));
@@ -52,7 +62,7 @@ MinGuiWidget::MinGuiWidget()
infopanel->setSizePolicy(QSizePolicy::Policy::Minimum, QSizePolicy::Policy::Minimum);
}
-void MinGuiWidget::show_images(const std::vector<std::string> &fns)
+void MinGuiWidget::show_images(const std::vector<fs::path> &fns)
{
current_set = fns;
marks.clear();
@@ -61,14 +71,14 @@ void MinGuiWidget::show_images(const std::vector<std::string> &fns)
imgcontainer->setLayout(new QVBoxLayout(imgcontainer));
int max_height = (this->screen()->size().height() / fns.size() * 0.75 - 24) * this->screen()->devicePixelRatio();
int max_width = this->screen()->size().width() * 0.8 * this->screen()->devicePixelRatio();
- std::string common_pfx = common_prefix(fns);
+ fs::path::string_type common_pfx = common_prefix(fns);
size_t idx = 0;
if (fns.size() > keys.size())
QMessageBox::warning(this, "Too many duplicates", "Too many duplicates found. Some couldn't be assigned a hotkey.");
for (auto &f : fns)
{
marks.push_back(marked.find(f) != marked.end());
- ImageWidget *tw = new ImageWidget(f, f.substr(common_pfx.length()), idx, max_width, max_height, this);
+ ImageWidget *tw = new ImageWidget(f, f.native().substr(common_pfx.length()), idx, max_width, max_height, this);
QObject::connect(tw, &ImageWidget::clicked, [this, idx] { this->mark_toggle(idx); });
imgw.push_back(tw);
imgcontainer->layout()->addWidget(tw);
@@ -103,7 +113,11 @@ void MinGuiWidget::save_list()
QString fn = QFileDialog::getSaveFileName(this, "Save list", QString(), "*.txt");
FILE *f = fopen(fn.toStdString().c_str(), "w");
for (auto &x : this->marked)
+#ifdef _WIN32
+ fwprintf(f, L"%ls\n", x.c_str());
+#else
fprintf(f, "%s\n", x.c_str());
+#endif
fclose(f);
}
@@ -180,23 +194,24 @@ void MinGuiWidget::mark_view_update(bool update_msg)
sb->showMessage(QString("%1 of %2 marked for deletion").arg(m).arg(current_set.size()), 1000);
}
-std::string MinGuiWidget::common_prefix(const std::vector<std::string> &fns)
+fs::path::string_type MinGuiWidget::common_prefix(const std::vector<fs::path> &fns)
{
- std::string ret;
- std::string shortest = *std::min_element(fns.begin(), fns.end(), [](auto &a, auto &b){return a.length() < b.length();});
+ using fsstr = fs::path::string_type;
+ fsstr ret;
+ fsstr shortest = *std::min_element(fns.begin(), fns.end(), [](auto &a, auto &b){return a.native().length() < b.native().length();});
for (size_t i = 0; i < shortest.length(); ++i)
{
- char c = shortest[i];
+ fs::path::value_type c = shortest[i];
bool t = true;
- for (auto &s : fns) if (s[i] != c) {t = false; break;}
+ for (auto &s : fns) if (s.c_str()[i] != c) {t = false; break;}
if (!t) break;
ret.push_back(c);
}
if (!ret.empty())
{
- auto p = ret.rfind((char)std::filesystem::path::preferred_separator);
- if (p != std::string::npos)
- return ret.substr(0, p + 1);
+ auto p = ret.rfind(std::filesystem::path::preferred_separator);
+ if (p != fsstr::npos)
+ return fs::path(ret.substr(0, p + 1));
}
return ret;
}
@@ -227,15 +242,15 @@ void MinGuiWidget::keyReleaseEvent(QKeyEvent *e)
}
}
-ImageWidget::ImageWidget(std::string f, std::string dispf, size_t _idx, int max_width, int max_height, QWidget *par)
+ImageWidget::ImageWidget(fs::path f, fs::path::string_type dispf, size_t _idx, int max_width, int max_height, QWidget *par)
: QWidget(par), fn(QString::fromStdString(f)), idx(_idx)
{
this->setLayout(new QVBoxLayout(this));
this->layout()->setMargin(10);
im = new QLabel(this);
this->layout()->addWidget(im);
- QFile imgf(QString::fromStdString(f));
- QPixmap pm(QString::fromStdString(f));
+ QFile imgf(fsstr_to_qstring(f.native()));
+ QPixmap pm(fsstr_to_qstring(f.native()));
int imw = pm.width();
int imh = pm.height();
pm.setDevicePixelRatio(this->screen()->devicePixelRatio());
@@ -247,7 +262,7 @@ ImageWidget::ImageWidget(std::string f, std::string dispf, size_t _idx, int max_
this->layout()->addWidget(lb);
QString s = QString("<%1>: %2, %3 x %4, %5")
.arg(idx < keys.size() ? QKeySequence(keys[idx]).toString(): QString("(No hotkey available)"))
- .arg(QString::fromStdString(dispf/*f.substr(common_pfx.length())*/))
+ .arg(fsstr_to_qstring(dispf))
.arg(imw).arg(imh)
.arg(QLocale::system().formattedDataSize(imgf.size(), 3));
lb->setTextFormat(Qt::TextFormat::PlainText);
diff --git a/mingui/mingui.hpp b/mingui/mingui.hpp
index 7ed0eb1..3a5dddc 100644
--- a/mingui/mingui.hpp
+++ b/mingui/mingui.hpp
@@ -1,6 +1,7 @@
#ifndef MINGUI_HPP
#define MINGUI_HPP
+#include <filesystem>
#include <vector>
#include <string>
#include <unordered_set>
@@ -11,6 +12,8 @@ class QHBoxLayout;
class QLabel;
class QStatusBar;
+namespace fs = std::filesystem;
+
class MinGuiWidget : public QWidget
{
Q_OBJECT
@@ -25,17 +28,17 @@ private:
void mark_all();
void mark_none();
void mark_view_update(bool update_msg = true);
- std::string common_prefix(const std::vector<std::string> &fns);
+ fs::path::string_type common_prefix(const std::vector<fs::path> &fns);
std::vector<QWidget*> imgw;
std::vector<bool> marks;
- std::unordered_set<std::string> marked;
- std::vector<std::string> current_set;
+ std::unordered_set<fs::path> marked;
+ std::vector<fs::path> current_set;
protected:
void keyPressEvent(QKeyEvent *e) override;
void keyReleaseEvent(QKeyEvent *e) override;
public:
MinGuiWidget();
- void show_images(const std::vector<std::string> &fns);
+ void show_images(const std::vector<std::filesystem::path> &fns);
void update_distances(const std::map<std::pair<std::size_t, std::size_t>, double> &d);
void update_permamsg(std::size_t cur, std::size_t size);
void save_list();
@@ -55,7 +58,7 @@ private:
protected:
void mouseReleaseEvent(QMouseEvent *event) override;
public:
- ImageWidget(std::string f, std::string dispfn, std::size_t _idx, int max_width, int max_height, QWidget *par);
+ ImageWidget(fs::path f, fs::path::string_type dispfn, std::size_t _idx, int max_width, int max_height, QWidget *par);
void set_marked(bool marked);
Q_SIGNALS:
void clicked();
diff --git a/signature.cpp b/signature.cpp
index fb2e757..8a5579e 100644
--- a/signature.cpp
+++ b/signature.cpp
@@ -8,6 +8,7 @@
*/
#include <algorithm>
+#include <fstream>
#include <cmath>
#include <cstdint>
#include <vector>
@@ -281,6 +282,12 @@ signature signature::from_file(const char *fn, const signature_config &cfg)
return signature::from_cvmatrix(img, cfg);
}
+signature signature::from_path(const std::filesystem::path &path, const signature_config &cfg)
+{
+ cv::Mat img = image_util::imread_path(path, cv::IMREAD_UNCHANGED);
+ return signature::from_cvmatrix(img, cfg);
+}
+
signature_config signature::default_cfg()
{
return _default_cfg;
diff --git a/signature.hpp b/signature.hpp
index b3c5c40..0197e75 100644
--- a/signature.hpp
+++ b/signature.hpp
@@ -2,6 +2,7 @@
#define SIGNATURE_HPP
#include <memory>
+#include <filesystem>
#include <opencv2/core.hpp>
struct signature_config
@@ -36,6 +37,8 @@ public:
double distance(const signature &o) const;
bool operator ==(const signature &o) const;
+ static signature from_path(const std::filesystem::path &path, const signature_config &cfg);
+
static signature from_file(const char *fn, const signature_config &cfg);
/*
diff --git a/tests/testdrive.cpp b/tests/testdrive.cpp
index c0d1038..2d8b0eb 100644
--- a/tests/testdrive.cpp
+++ b/tests/testdrive.cpp
@@ -1,5 +1,3 @@
-#include "signature.hpp"
-
#include <cstdio>
#include <cstring>
@@ -16,44 +14,55 @@
#include <getopt.h>
+#ifdef _WIN32 //for the superior operating system
+#include <cwchar>
+#include <processenv.h>
+#include <shellapi.h>
+#endif
+
+#include "signature.hpp"
+#include "imageutil.hpp"
+
#include "thread_pool.hpp"
#define DEBUG 0
+namespace fs = std::filesystem;
+
int ctr;
int recursive;
-int njobs=1;
-double threshold=0.3;
-std::vector<std::string> paths;
-std::vector<std::string> files;
+int njobs = 1;
+double threshold = 0.3;
+std::vector<fs::path> paths;
+std::vector<fs::path> files;
int nsliceh = 3;
int nslicev = 3;
signature_config cfg_full =
{
- 9, //slices
- 3, //blur_window
- 2, //min_window
- true, //crop
- true, //comp
- 0.5, //pr
- 1./128,//noise_threshold
- 0.05, //contrast_threshold
- 0.25 //max_cropping
+ 9, //slices
+ 3, //blur_window
+ 2, //min_window
+ true, //crop
+ true, //comp
+ 0.5, //pr
+ 1./128, //noise_threshold
+ 0.05, //contrast_threshold
+ 0.25 //max_cropping
};
signature_config cfg_subslice =
{
- 4, //slices
- 16, //blur_window
- 2, //min_window
- false, //crop
- true, //comp
- 0.5, //pr
- 1./64, //noise_threshold
- 0.05, //contrast_threshold
- 0.25 //max_cropping
+ 4, //slices
+ 16, //blur_window
+ 2, //min_window
+ false, //crop
+ true, //comp
+ 0.5, //pr
+ 1./64, //noise_threshold
+ 0.05, //contrast_threshold
+ 0.25 //max_cropping
};
struct sig_eq
@@ -73,48 +82,59 @@ std::vector<std::pair<size_t, size_t>> out;
int parse_arguments(int argc,char **argv)
{
- recursive=0;
- int help=0;
+ recursive = 0;
+ int help = 0;
option longopt[]=
{
- {"recursive",no_argument ,&recursive,1},
-// {"destdir" ,required_argument,0 ,'D'},
- {"jobs" ,required_argument,0 ,'j'},
-// {"threshold",required_argument,0 ,'d'},
- {"help" ,no_argument ,&help ,1},
- {0 ,0 ,0 ,0}
+ {"recursive", no_argument , &recursive, 1},
+// {"destdir" , required_argument, 0 , 'D'},
+ {"jobs" , required_argument, 0 , 'j'},
+// {"threshold", required_argument, 0 , 'd'},
+ {"help" , no_argument , &help , 1},
+ {0 , 0 , 0 , 0}
};
while(1)
{
- int idx=0;
- int c=getopt_long(argc,argv,"rhj:",longopt,&idx);
- if(!~c)break;
- switch(c)
+ int idx = 0;
+ int c = getopt_long(argc, argv, "rhj:", longopt, &idx);
+ if (!~c) break;
+ switch (c)
{
case 0:
- if(longopt[idx].flag)break;
- if(std::string("jobs")==longopt[idx].name)
- sscanf(optarg,"%d",&njobs);
- //if(std::string("threshold")==longopt[idx].name)
- //sscanf(optarg,"%lf",&threshold);
+ if (longopt[idx].flag) break;
+ if (std::string("jobs") == longopt[idx].name)
+ sscanf(optarg, "%d", &njobs);
+ //if(std::string("threshold") == longopt[idx].name)
+ //sscanf(optarg, "%lf", &threshold);
break;
case 'r':
- recursive=1;
+ recursive = 1;
break;
case 'h':
- help=1;
+ help = 1;
break;
case 'j':
- sscanf(optarg,"%d",&njobs);
+ sscanf(optarg, "%d", &njobs);
break;
case 'd':
- sscanf(optarg,"%lf",&threshold);
+ //sscanf(optarg, "%lf", &threshold);
break;
}
}
- for(;optind<argc;++optind)
+#ifdef _WIN32 //w*ndows, ugh
+ wchar_t *args = GetCommandLineW();
+ int wargc;
+ wchar_t **wargv = CommandLineToArgvW(args, &wargc);
+ if (wargv && wargc == argc)
+ {
+ for (; optind < argc; ++optind)
+ path.push_back(wargv[optind]);
+ }
+#else
+ for (; optind < argc; ++optind)
paths.push_back(argv[optind]);
- if(help||argc<2)
+#endif
+ if (help || argc < 2)
{
printf(
"Usage: %s [OPTION] PATH...\n"
@@ -127,13 +147,13 @@ int parse_arguments(int argc,char **argv)
);
return 1;
}
- if(threshold>1||threshold<0)
+ if (threshold > 1 || threshold < 0)
{
puts("Invalid threshold value.");
return 2;
}
- if(threshold<1e-6)threshold=1e-6;
- if(!paths.size())
+ if (threshold < 1e-6) threshold = 1e-6;
+ if (!paths.size())
{
puts("Missing image path.");
return 2;
@@ -141,12 +161,12 @@ int parse_arguments(int argc,char **argv)
return 0;
}
-void build_file_list(std::filesystem::path path,bool recursive,std::vector<std::string>&out)
+void build_file_list(fs::path path, bool recursive, std::vector<fs::path> &out)
{
- if(recursive)
+ if (recursive)
{
- auto dirit=std::filesystem::recursive_directory_iterator(path);
- for(auto &p:dirit)
+ auto dirit = fs::recursive_directory_iterator(path);
+ for (auto &p : dirit)
{
FILE* fp = fopen(p.path().c_str(),"r");
char c[8];
@@ -164,8 +184,8 @@ void build_file_list(std::filesystem::path path,bool recursive,std::vector<std::
}
else
{
- auto dirit=std::filesystem::directory_iterator(path);
- for(auto &p:dirit)
+ auto dirit = fs::directory_iterator(path);
+ for(auto &p : dirit)
{
FILE* fp = fopen(p.path().c_str(),"r");
char c[8];
@@ -185,7 +205,7 @@ void build_file_list(std::filesystem::path path,bool recursive,std::vector<std::
void job_func(int thid, size_t id)
{
- cv::Mat img = cv::imread(files[id].c_str(), cv::IMREAD_UNCHANGED);
+ cv::Mat img = image_util::imread_path(files[id], cv::IMREAD_UNCHANGED);
signature s = signature::from_cvmatrix(img, cfg_full);
#if DEBUG > 1
s.dump();
@@ -250,36 +270,40 @@ void job_func(int thid, size_t id)
void run()
{
thread_pool tp(njobs);
- for(size_t i=0;i<files.size();++i)
+ for(size_t i = 0; i < files.size(); ++i)
{
- tp.create_task(job_func,i);
+ tp.create_task(job_func, i);
}
tp.wait();
}
int main(int argc,char** argv)
{
- if(int pr=parse_arguments(argc,argv))return pr-1;
+ if (int pr = parse_arguments(argc, argv)) return pr - 1;
puts("building list of files to compare...");
- for(auto&p:paths)
- build_file_list(p,recursive,files);
- printf("%lu files to compare.\n",files.size());
+ for (auto &p : paths)
+ build_file_list(p, recursive, files);
+ printf("%lu files to compare.\n", files.size());
puts("computing signature vectors...");
signatures.resize(files.size());
run();
FILE *outf = fopen("result", "wb");
- for(auto &p : out)
+ for (auto &p : out)
{
+#ifdef _WIN32
+ wprintf(L"%ls %ls %f\n", files[p.first].c_str(), files[p.second].c_str(), signatures[p.first].distance(signatures[p.second]));
+#else
printf("%s %s %f\n", files[p.first].c_str(), files[p.second].c_str(), signatures[p.first].distance(signatures[p.second]));
+#endif
int t;
double ts;
- t = (int)files[p.first].length();
+ t = (int)files[p.first].native().length();
fwrite(&t, sizeof(int), 1, outf);
- fwrite(files[p.first].c_str(), 1, files[p.first].length(), outf);
- t = (int)files[p.second].length();
+ fwrite(files[p.first].c_str(), sizeof(fs::path::value_type), t, outf);
+ t = (int)files[p.second].native().length();
fwrite(&t, sizeof(int), 1, outf);
- fwrite(files[p.second].c_str(), 1, files[p.second].length(), outf);
+ fwrite(files[p.second].c_str(), sizeof(fs::path::value_type), t, outf);
ts = signatures[p.first].distance(signatures[p.second]);
fwrite(&ts, sizeof(double), 1, outf);
}