File indexing completed on 2025-01-19 03:53:34
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2003-01-17 0007 * Description : Haar Database interface 0008 * 0009 * SPDX-FileCopyrightText: 2016-2018 by Mario Frank <mario dot frank at uni minus potsdam dot de> 0010 * SPDX-FileCopyrightText: 2003 by Ricardo Niederberger Cabral <nieder at mail dot ru> 0011 * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0012 * SPDX-FileCopyrightText: 2009-2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0013 * SPDX-FileCopyrightText: 2009-2011 by Andi Clemens <andi dot clemens at gmail dot com> 0014 * 0015 * SPDX-License-Identifier: GPL-2.0-or-later 0016 * 0017 * ============================================================ */ 0018 0019 #ifndef DIGIKAM_HAAR_IFACE_H 0020 #define DIGIKAM_HAAR_IFACE_H 0021 0022 // Qt includes 0023 0024 #include <QSet> 0025 #include <QString> 0026 #include <QMap> 0027 #include <QList> 0028 0029 // Local includes 0030 0031 #include "haar.h" 0032 #include "digikam_export.h" 0033 0034 class QImage; 0035 0036 namespace Digikam 0037 { 0038 0039 class DImg; 0040 class ItemInfo; 0041 0042 class HaarProgressObserver 0043 { 0044 public: 0045 0046 HaarProgressObserver() = default; 0047 virtual ~HaarProgressObserver() = default; 0048 0049 virtual void imageProcessed() = 0; 0050 virtual bool isCanceled() 0051 { 0052 return false; 0053 }; 0054 0055 private: 0056 0057 Q_DISABLE_COPY(HaarProgressObserver) 0058 }; 0059 0060 // -------------------------------------------------------------------------- 0061 0062 class DIGIKAM_DATABASE_EXPORT HaarIface 0063 { 0064 0065 public: 0066 0067 enum SketchType 0068 { 0069 ScannedSketch = 0, 0070 HanddrawnSketch = 1 0071 }; 0072 0073 enum DuplicatesSearchRestrictions 0074 { 0075 None = 0, 0076 SameAlbum = 1, 0077 DifferentAlbum = 2 0078 }; 0079 0080 enum AlbumTagRelation 0081 { 0082 NoMix = 0, 0083 Union = 1, 0084 Intersection = 2, 0085 AlbumExclusive = 3, 0086 TagExclusive = 4 0087 }; 0088 0089 /*! 0090 * \brief The RefImageSelMethod enum 0091 * Selection method to determine which image 0092 * will be the reference in the duplicate search 0093 * 0094 * When adding method here, update also 0095 * HaarIface::findDuplicates() 0096 */ 0097 enum class RefImageSelMethod: unsigned int 0098 { 0099 OlderOrLarger = 0, ///< Original 0100 PreferFolder = 1, ///< Prefer select folder to be the reference 0101 ExcludeFolder = 2, ///< Prefer image not in the selected folder 0102 NewerCreationDate = 3, ///< Prefer newer creation date image 0103 NewerModificationDate = 4 ///< Prefer newer modification date image 0104 }; 0105 0106 using DuplicatesResultsMap = QMap<qlonglong, QPair<double, QList<qlonglong> > >; 0107 0108 public: 0109 0110 explicit HaarIface(); 0111 explicit HaarIface(const QSet<qlonglong>& images2Scan); 0112 ~HaarIface(); 0113 0114 static int preferredSize(); 0115 0116 /** 0117 * Adds an image to the index in the database. 0118 */ 0119 bool indexImage(const QString& filename); 0120 bool indexImage(const QString& filename, const QImage& image); 0121 bool indexImage(const QString& filename, const DImg& image); 0122 bool indexImage(qlonglong imageid, const QImage& image); 0123 bool indexImage(qlonglong imageid, const DImg& image); 0124 0125 QMap<qlonglong, double> bestMatchesForSignature(const QString& signature, 0126 const QList<int>& targetAlbums, 0127 int numberOfResults = 20, 0128 SketchType type = ScannedSketch); 0129 0130 /** 0131 * Searches the database for the best matches for the specified query image. 0132 * All matches with a similarity in a given threshold interval are returned. 0133 * The threshold is in the range requiredPercentage..maximumPercentage. 0134 */ 0135 QPair<double, QMap<qlonglong, double> > bestMatchesForImageWithThreshold( 0136 qlonglong imageid, 0137 double requiredPercentage, 0138 double maximumPercentage, 0139 const QList<int>& targetAlbums, 0140 DuplicatesSearchRestrictions searchResultRestriction = DuplicatesSearchRestrictions::None, 0141 SketchType type = ScannedSketch 0142 ); 0143 0144 /** 0145 * Searches the database for the best matches for the specified query image. 0146 * All matches with a similarity in a given threshold interval are returned. 0147 * The threshold is in the range requiredPercentage..maximumPercentage. 0148 */ 0149 QPair<double, QMap<qlonglong, double> > bestMatchesForImageWithThreshold( 0150 const QString& imagePath, 0151 double requiredPercentage, 0152 double maximumPercentage, 0153 const QList<int>& targetAlbums, 0154 DuplicatesSearchRestrictions searchResultRestriction = DuplicatesSearchRestrictions::None, 0155 SketchType type = ScannedSketch 0156 ); 0157 0158 /** 0159 * Calculates the Haar signature, bring it in a form as stored in the DB, 0160 * and encode it to Ascii data. Can be used for bestMatchesForSignature. 0161 */ 0162 QString signatureAsText(const QImage& image); 0163 0164 /** 0165 * Checks whether the image with the given imageId fulfills all restrictions given in 0166 * targetAlbums and in respect to searchResultRestriction. 0167 */ 0168 bool fulfillsRestrictions(qlonglong imageId, 0169 int albumId, 0170 qlonglong originalImageId, 0171 int originalAlbumId, 0172 const QList<int>& targetAlbums, 0173 DuplicatesSearchRestrictions searchResultRestriction); 0174 0175 /** 0176 * For a given signature, find out the highest and lowest possible score 0177 * that any other signature could reach, compared to the given signature. 0178 */ 0179 void getBestAndWorstPossibleScore(Haar::SignatureData* const querySig, 0180 SketchType type, 0181 double* const lowestAndBestScore, 0182 double* const highestAndWorstScore); 0183 0184 /** 0185 * Fill a map of duplicates images found over a list of images to scan. 0186 * For each map item, the result values is list of candidate images which are duplicates of the key image. 0187 * All images are referenced by id from database. 0188 * The threshold is in the range 0..1, with 1 meaning identical signature. 0189 */ 0190 DuplicatesResultsMap findDuplicates( 0191 const QSet<qlonglong>& images2Scan, 0192 const QSet<qlonglong>::const_iterator& rangeBegin, 0193 const QSet<qlonglong>::const_iterator& rangeEnd, 0194 RefImageSelMethod refImageSelectionMethod, 0195 const QSet<qlonglong>& refs, 0196 double requiredPercentage, 0197 double maximumPercentage, 0198 DuplicatesSearchRestrictions searchResultRestriction = DuplicatesSearchRestrictions::None, 0199 HaarProgressObserver* const observer = nullptr 0200 ); 0201 0202 /** 0203 * Collects all images from the given album and tag ids according to their relation. 0204 */ 0205 static QSet<qlonglong> imagesFromAlbumsAndTags(const QList<int>& albums2Scan, 0206 const QList<int>& tags2Scan, 0207 AlbumTagRelation relation); 0208 0209 /** 0210 * This method rebuilds the given SAlbums using the given results. 0211 * @param results Map of duplicates images found over a list of images. 0212 */ 0213 static void rebuildDuplicatesAlbums(const DuplicatesResultsMap& results, bool isAlbumUpdate); 0214 0215 /** 0216 * Retrieve the Haar signature from database using image id. 0217 * Return true if item signature exist else false. 0218 */ 0219 bool retrieveSignatureFromDB(qlonglong imageid, Haar::SignatureData& sig); 0220 0221 /** 0222 * Give a list of albumRoots to which the search shall be limited. 0223 * Calling with an empty list will disable filtering. 0224 */ 0225 void setAlbumRootsToSearch(const QList<int>& albumRootIds); 0226 void setAlbumRootsToSearch(const QSet<int>& albumRootIds); 0227 0228 /** 0229 * This method loads a QImage from the given filename. 0230 * @param filename the name of the file (path) 0231 * @return A QImage, non-null on success. 0232 */ 0233 QImage loadQImage(const QString& filename); 0234 0235 private: 0236 0237 bool indexImage(qlonglong imageid); 0238 0239 /** 0240 * This method writes the search results to the SearchXml structure. 0241 * @param searchResults The results to write as XML. 0242 */ 0243 static QMap<QString, QString> writeSAlbumQueries(const DuplicatesResultsMap& searchResults); 0244 0245 QMultiMap<double, qlonglong> bestMatches(Haar::SignatureData* const data, 0246 int numberOfResults, 0247 const QList<int>& targetAlbums, 0248 SketchType type); 0249 0250 /** 0251 * @brief bestMatchesWithThreshold 0252 * @param imageid 0253 * @param querySig 0254 * @param requiredPercentage 0255 * @param maximumPercentage 0256 * @param targetAlbums 0257 * @param searchResultRestriction 0258 * @param type 0259 * @return (av. similarity, Maps with images and similarity) 0260 */ 0261 QPair<double, QMap<qlonglong, double> > bestMatchesWithThreshold(qlonglong imageid, 0262 Haar::SignatureData* const querySig, 0263 double requiredPercentage, 0264 double maximumPercentage, 0265 const QList<int>& targetAlbums, 0266 DuplicatesSearchRestrictions searchResultRestriction, 0267 SketchType type); 0268 0269 /** 0270 * This method is the core functionality: It assigns a score to every image in the database. 0271 * @param data The signature of the original image for score calculation. 0272 * @param type The type of the sketch, e.g. scanned. 0273 * @param searchResultRestriction restrictions to apply to the generated map, i.e. None (default), same album or different album. 0274 * @param originalImageId the id of the original image to compare to other images. -1 is only used for sketch search. 0275 * @param albumId The album which images must or must not belong to (depending on searchResultRestriction). 0276 * @return The map of image ids and scores which fulfill the restrictions, if any. 0277 */ 0278 QMap<qlonglong, double> searchDatabase(Haar::SignatureData* const data, 0279 SketchType type, 0280 const QList<int>& targetAlbums, 0281 DuplicatesSearchRestrictions searchResultRestriction = None, 0282 qlonglong originalImageId = -1, 0283 int albumId = -1); 0284 0285 double calculateScore(const Haar::SignatureData& querySig, 0286 const Haar::SignatureData& targetSig, 0287 const Haar::Weights& weights, 0288 std::reference_wrapper<Haar::SignatureMap>* const queryMaps); 0289 0290 private: 0291 0292 // Disable 0293 HaarIface(const HaarIface&) = delete; 0294 HaarIface& operator=(const HaarIface&) = delete; 0295 0296 private: 0297 0298 class Private; 0299 Private* const d; 0300 }; 0301 0302 } // namespace Digikam 0303 0304 #endif // DIGIKAM_HAAR_IFACE_H