Bläddra i källkod

Add: DatabaseManager::getImagesInDirectories()

Satoshi Yoneda 2 veckor sedan
förälder
incheckning
cc354a5ec9
4 ändrade filer med 67 tillägg och 22 borttagningar
  1. 3 0
      include/DatabaseManager.hpp
  2. 43 0
      src/DatabaseManager.cpp
  3. 15 17
      src/MainWindow.cpp
  4. 6 5
      src/SimilaritySearch.cpp

+ 3 - 0
include/DatabaseManager.hpp

@@ -31,6 +31,9 @@ public:
     bool addImage(const ImageData& data);
     // DB内の全画像情報を取得する
     std::vector<ImageData> getAllImages();
+    // 指定されたディレクトリ群のいずれかに属する画像をSQLで高速に取得する
+    std::vector<ImageData> getImagesInDirectories(const std::vector<std::string>& dirPaths);
+
     // 指定されたパスの画像をDBから削除する
     bool removeImage(const std::string& path);
     // 実体ファイルが削除済みの古いエントリをDBから消去する

+ 43 - 0
src/DatabaseManager.cpp

@@ -92,6 +92,49 @@ std::vector<ImageData> DatabaseManager::getAllImages() {
     return results;
 }
 
+std::vector<ImageData> DatabaseManager::getImagesInDirectories(const std::vector<std::string>& dirPaths) {
+    if (dirPaths.empty()) return {};
+
+    std::vector<ImageData> results;
+    std::stringstream sql;
+    sql << "SELECT id, path, dhash, phash, timestamp, file_size, is_searched FROM images WHERE ";
+
+    for (size_t i = 0; i < dirPaths.size(); ++i) {
+        if (i > 0) sql << " OR ";
+        sql << "(path LIKE ? OR path = ?)";
+    }
+    sql << ";";
+
+    sqlite3_stmt* stmt;
+    if (sqlite3_prepare_v2(m_db, sql.str().c_str(), -1, &stmt, nullptr) != SQLITE_OK) return results;
+
+    for (size_t i = 0; i < dirPaths.size(); ++i) {
+        std::string dirPath = dirPaths[i];
+        std::string pattern = dirPath;
+        if (!pattern.empty() && (pattern.back() != '/' && pattern.back() != '\\')) {
+            pattern += "/";
+        }
+        std::string likePattern = pattern + "%";
+        
+        sqlite3_bind_text(stmt, static_cast<int>(i * 2 + 1), likePattern.c_str(), -1, SQLITE_TRANSIENT);
+        sqlite3_bind_text(stmt, static_cast<int>(i * 2 + 2), dirPath.c_str(), -1, SQLITE_TRANSIENT);
+    }
+
+    while (sqlite3_step(stmt) == SQLITE_ROW) {
+        ImageData data;
+        data.id = sqlite3_column_int64(stmt, 0);
+        data.path = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
+        data.dhash = static_cast<uint64_t>(sqlite3_column_int64(stmt, 2));
+        data.phash = static_cast<uint64_t>(sqlite3_column_int64(stmt, 3));
+        data.timestamp = sqlite3_column_int64(stmt, 4);
+        data.file_size = sqlite3_column_int64(stmt, 5);
+        data.is_searched = sqlite3_column_int(stmt, 6) != 0;
+        results.push_back(data);
+    }
+    sqlite3_finalize(stmt);
+    return results;
+}
+
 bool DatabaseManager::removeImage(const std::string& path) {
     const char* sql = "DELETE FROM images WHERE path = ?;";
     sqlite3_stmt* stmt;

+ 15 - 17
src/MainWindow.cpp

@@ -74,27 +74,25 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
 MainWindow::~MainWindow() {}
 
 std::vector<ImageData> MainWindow::getFilteredImages() {
-  auto allImages = m_dbManager->getAllImages();
   if (m_dirList->count() == 0)
     return {};
 
+  std::vector<std::string> dirPaths;
+  for (int i = 0; i < m_dirList->count(); ++i) {
+    dirPaths.push_back(m_dirList->item(i)->text().toStdString());
+  }
+
+  auto images = m_dbManager->getImagesInDirectories(dirPaths);
+
+  if (m_ignoredPaths.empty()) {
+    return images;
+  }
+
   std::vector<ImageData> filtered;
-  for (const auto &img : allImages) {
-    bool match = false;
-    for (int i = 0; i < m_dirList->count(); ++i) {
-      std::string dirPath = m_dirList->item(i)->text().toStdString();
-      std::string dirPathWithSlash = dirPath;
-      if (!dirPathWithSlash.empty() && dirPathWithSlash.back() != '/' &&
-          dirPathWithSlash.back() != '\\') {
-        dirPathWithSlash += "/";
-      }
-      if (img.path.find(dirPathWithSlash) == 0 || img.path == dirPath) {
-        match = true;
-        break;
-      }
-    }
-    if (match && m_ignoredPaths.find(img.path) == m_ignoredPaths.end()) {
-      filtered.push_back(img);
+  filtered.reserve(images.size());
+  for (auto &&img : images) {
+    if (m_ignoredPaths.find(img.path) == m_ignoredPaths.end()) {
+      filtered.push_back(std::move(img));
     }
   }
   return filtered;

+ 6 - 5
src/SimilaritySearch.cpp

@@ -82,7 +82,7 @@ SimilaritySearch::findDuplicates(const std::vector<ImageData> &images,
                               : (distD <= threshold || distP <= threshold);
         if (similar) {
           local_edges.emplace_back(i, j);
-          
+
           // バッファが一定量溜まったら、排他制御でDSUにマージして解放
           // 全結果を最後に結合するのを避け、メモリ使用量とヒープの競合を極小化
           if (local_edges.size() >= 1024) {
@@ -124,10 +124,11 @@ SimilaritySearch::findDuplicates(const std::vector<ImageData> &images,
   return results;
 }
 
-
-std::vector<ImageData> SimilaritySearch::findSimilarImages(
-    const ImageData &image, const std::vector<ImageData> &images, int threshold,
-    bool strict) {
+// imagesからimageに類似する画像を検索する
+std::vector<ImageData>
+SimilaritySearch::findSimilarImages(const ImageData &image,
+                                    const std::vector<ImageData> &images,
+                                    int threshold, bool strict) {
   auto FilterFunc = [&](const ImageData &cand) {
     if (cand.path == image.path)
       return false;