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 #include "haariface_p.h"
0020 
0021 namespace Digikam
0022 {
0023 
0024 void DatabaseBlob::read(const QByteArray& array, Haar::SignatureData& data)
0025 {
0026     QDataStream stream(array);
0027 
0028     // check version
0029 
0030     qint32 version;
0031     stream >> version;
0032 
0033     if (version != Version)
0034     {
0035         qCDebug(DIGIKAM_DATABASE_LOG) << "Unsupported binary version of Haar Blob in database";
0036         return;
0037     }
0038 
0039     stream.setVersion(QDataStream::Qt_4_3);
0040 
0041     // read averages
0042 
0043     for (int i = 0 ; i < 3 ; ++i)
0044     {
0045         stream >> data.avg[i];
0046     }
0047 
0048     // read coefficients
0049 
0050     for (int i = 0 ; i < 3 ; ++i)
0051     {
0052         for (int j = 0 ; j < Haar::NumberOfCoefficients ; ++j)
0053         {
0054             stream >> data.sig[i][j];
0055         }
0056     }
0057 }
0058 
0059 QByteArray DatabaseBlob::write(Haar::SignatureData& data)
0060 {
0061     QByteArray array;
0062     array.reserve(sizeof(qint32) + 3*sizeof(double) + 3*sizeof(qint32)*Haar::NumberOfCoefficients);
0063     QDataStream stream(&array, QIODevice::WriteOnly);
0064     stream.setVersion(QDataStream::Qt_4_3);
0065 
0066     // write version
0067 
0068     stream << (qint32)Version;
0069 
0070     // write averages
0071 
0072     for (int i = 0 ; i < 3 ; ++i)
0073     {
0074         stream << data.avg[i];
0075     }
0076 
0077     // write coefficients
0078 
0079     for (int i = 0 ; i < 3 ; ++i)
0080     {
0081         for (int j = 0 ; j < Haar::NumberOfCoefficients ; ++j)
0082         {
0083             stream << data.sig[i][j];
0084         }
0085     }
0086 
0087     return array;
0088 }
0089 
0090 // -----------------------------------------------------------------------------------------------------
0091 
0092 const QString HaarIface::Private::signatureQuery = QString::fromUtf8("SELECT imageid, matrix FROM ImageHaarMatrix;");
0093 const Haar::WeightBin HaarIface::Private::weightBin;
0094 
0095 HaarIface::Private::Private()
0096     : m_data(new Haar::ImageData)
0097 {
0098 }
0099 
0100 HaarIface::Private::~Private()
0101 {
0102 }
0103 
0104 void HaarIface::Private::rebuildSignatureCache(const QSet<qlonglong>& imageIds)
0105 {
0106     m_signatureCache.reset(new SignatureCache);
0107     m_albumCache.reset(new AlbumCache);
0108 
0109     // Variables for data read from DB
0110 
0111     DatabaseBlob        blob;
0112     qlonglong           imageid;
0113     int                 albumid;
0114     Haar::SignatureData targetSig;
0115 
0116     // reference for easier access
0117 
0118     SignatureCache& sigCache = *m_signatureCache;
0119     AlbumCache&     albCache = *m_albumCache;
0120 
0121     DbEngineSqlQuery query   = SimilarityDbAccess().backend()->prepareQuery(signatureQuery);
0122 
0123     if (!SimilarityDbAccess().backend()->exec(query))
0124     {
0125         return;
0126     }
0127 
0128     QHash<qlonglong, QPair<int, int> > itemAlbumHash = CoreDbAccess().db()->getAllItemsWithAlbum();
0129 
0130     // Remove all ids from the fully created itemAlbumHash that are not needed for the duplicates search.
0131     // This is usually faster then starting a query for every single id in imageIds.
0132 
0133     if (!imageIds.isEmpty())
0134     {
0135         for (auto it = itemAlbumHash.begin() ; it != itemAlbumHash.end() ; )
0136         {
0137             if (!imageIds.contains(it.key()))
0138             {
0139                 it = itemAlbumHash.erase(it);
0140             }
0141             else
0142             {
0143                 ++it;
0144             }
0145         }
0146     }
0147 
0148     const bool filterByAlbumRoots = !m_albumRootsToSearch.isEmpty();
0149 
0150     while (query.next())
0151     {
0152         imageid = query.value(0).toLongLong();
0153 
0154         if (itemAlbumHash.contains(imageid))
0155         {
0156             // Pair storage of <albumroootid, albumid>
0157 
0158             const QPair<int, int>& albumPair = itemAlbumHash.value(imageid);
0159 
0160             if (filterByAlbumRoots)
0161             {
0162                 if (!m_albumRootsToSearch.contains(albumPair.first))
0163                 {
0164                     continue;
0165                 }
0166             }
0167 
0168             blob.read(query.value(1).toByteArray(), targetSig);
0169             albumid           = albumPair.second;
0170             sigCache[imageid] = targetSig;
0171             albCache[imageid] = albumid;
0172         }
0173     }
0174 }
0175 
0176 bool HaarIface::Private::hasSignatureCache() const
0177 {
0178     return !(m_signatureCache.isNull() || m_signatureCache->isEmpty());
0179 }
0180 
0181 bool HaarIface::Private::retrieveSignatureFromCache(qlonglong imageId, Haar::SignatureData& data)
0182 {
0183     if (!hasSignatureCache())
0184     {
0185         return false;
0186     }
0187 
0188     if (m_signatureCache->contains(imageId))
0189     {
0190         data = m_signatureCache.data()->value(imageId);
0191 
0192         return true;
0193     }
0194 
0195     return false;
0196 }
0197 
0198 void HaarIface::Private::setImageDataFromImage(const QImage& image)
0199 {
0200     m_data->fillPixelData(image);
0201 }
0202 
0203 void HaarIface::Private::setImageDataFromImage(const DImg& image)
0204 {
0205     m_data->fillPixelData(image);
0206 }
0207 
0208 SignatureCache* HaarIface::Private::signatureCache() const
0209 {
0210     return m_signatureCache.data();
0211 }
0212 
0213 AlbumCache* HaarIface::Private::albumCache() const
0214 {
0215     return m_albumCache.data();
0216 }
0217 
0218 Haar::ImageData* HaarIface::Private::imageData() const
0219 {
0220     return m_data.data();
0221 }
0222 
0223 void HaarIface::Private::setAlbumRootsToSearch(const QSet<int>& albumRootIds)
0224 {
0225     m_albumRootsToSearch = albumRootIds;
0226 }
0227 
0228 const QSet<int>& HaarIface::Private::albumRootsToSearch() const
0229 {
0230     return m_albumRootsToSearch;
0231 }
0232 
0233 } // namespace Digikam