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