File indexing completed on 2025-03-09 03:55:01

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam
0004  *
0005  * Date        : 2010-06-16
0006  * Description : Identity management of recognition wrapper
0007  *
0008  * SPDX-FileCopyrightText:      2010 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0009  * SPDX-FileCopyrightText:      2010 by Aditya Bhatt <adityabhatt1991 at gmail dot com>
0010  * SPDX-FileCopyrightText: 2010-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  * SPDX-FileCopyrightText:      2019 by Thanh Trung Dinh <dinhthanhtrung1996 at gmail dot com>
0012  * SPDX-FileCopyrightText:      2020 by Nghia Duong <minhnghiaduong997 at gmail dot com>
0013  *
0014  * SPDX-License-Identifier: GPL-2.0-or-later
0015  *
0016  * ============================================================ */
0017 
0018 #include "facialrecognition_wrapper_p.h"
0019 
0020 namespace Digikam
0021 {
0022 /**
0023  * NOTE: Takes care that there may be multiple values of attribute in identity's attributes
0024  */
0025 bool FacialRecognitionWrapper::Private::identityContains(const Identity& identity,
0026                                                          const QString&  attribute,
0027                                                          const QString&  value)
0028 {
0029     const QMultiMap<QString, QString> map          = identity.attributesMap();
0030     QMultiMap<QString, QString>::const_iterator it = map.constFind(attribute);
0031 
0032     for ( ; (it != map.constEnd()) && (it.key() == attribute) ; ++it)
0033     {
0034         if (it.value() == value)
0035         {
0036             return true;
0037         }
0038     }
0039 
0040     return false;
0041 }
0042 
0043 Identity FacialRecognitionWrapper::Private::findByAttribute(const QString& attribute,
0044                                                             const QString& value) const
0045 {
0046     Q_FOREACH (const Identity& identity, identityCache)
0047     {
0048         if (identityContains(identity, attribute, value))
0049         {   // cppcheck-suppress useStlAlgorithm
0050             return identity;
0051         }
0052     }
0053 
0054     return Identity();
0055 }
0056 
0057 /**
0058  * NOTE: Takes care that there may be multiple values of attribute in valueMap
0059  */
0060 Identity FacialRecognitionWrapper::Private::findByAttributes(const QString& attribute,
0061                                                              const QMultiMap<QString, QString>& valueMap) const
0062 {
0063     QMultiMap<QString, QString>::const_iterator it = valueMap.find(attribute);
0064 
0065     for ( ; (it != valueMap.end()) && (it.key() == attribute) ; ++it)
0066     {
0067         Q_FOREACH (const Identity& identity, identityCache)
0068         {
0069             if (identityContains(identity, attribute, it.value()))
0070             {   // cppcheck-suppress useStlAlgorithm
0071                 return identity;
0072             }
0073         }
0074     }
0075 
0076     return Identity();
0077 }
0078 
0079 // -----------------------------------------------------------------------
0080 
0081 QList<Identity> FacialRecognitionWrapper::allIdentities() const
0082 {
0083     if (!d || !d->dbAvailable)
0084     {
0085         return QList<Identity>();
0086     }
0087 
0088     QMutexLocker lock(&d->mutex);
0089 
0090     return (d->identityCache.values());
0091 }
0092 
0093 Identity FacialRecognitionWrapper::identity(int id) const
0094 {
0095     if (!d || !d->dbAvailable)
0096     {
0097         return Identity();
0098     }
0099 
0100     QMutexLocker lock(&d->mutex);
0101 
0102     return (d->identityCache.value(id));
0103 }
0104 
0105 Identity FacialRecognitionWrapper::findIdentity(const QString& attribute, const QString& value) const
0106 {
0107     if (!d || !d->dbAvailable || attribute.isEmpty())
0108     {
0109         return Identity();
0110     }
0111 
0112     QMutexLocker lock(&d->mutex);
0113 
0114     return (d->findByAttribute(attribute, value));
0115 }
0116 
0117 Identity FacialRecognitionWrapper::findIdentity(const QMultiMap<QString, QString>& attributes) const
0118 {
0119     if (!d || !d->dbAvailable || attributes.isEmpty())
0120     {
0121         return Identity();
0122     }
0123 
0124     QMutexLocker lock(&d->mutex);
0125 
0126     Identity match;
0127 
0128     // First and foremost, UUID
0129 
0130     QString uuid = attributes.value(QLatin1String("uuid"));
0131     match        = d->findByAttribute(QLatin1String("uuid"), uuid);
0132 
0133     if (!match.isNull())
0134     {
0135         return match;
0136     }
0137 
0138     // A negative UUID match, with a given UUID, precludes any further search
0139 
0140     if (!uuid.isNull())
0141     {
0142         return Identity();
0143     }
0144 
0145     // full name
0146 
0147     match = d->findByAttributes(QLatin1String("fullName"), attributes);
0148 
0149     if (!match.isNull())
0150     {
0151         return match;
0152     }
0153 
0154     // name
0155 
0156     match = d->findByAttributes(QLatin1String("name"), attributes);
0157 
0158     if (!match.isNull())
0159     {
0160         return match;
0161     }
0162 
0163     QMultiMap<QString, QString>::const_iterator it;
0164 
0165     for (it = attributes.begin() ; it != attributes.end() ; ++it)
0166     {
0167         if (
0168             (it.key() == QLatin1String("uuid"))     ||
0169             (it.key() == QLatin1String("fullName")) ||
0170             (it.key() == QLatin1String("name"))
0171            )
0172         {
0173             continue;
0174         }
0175 
0176         match = d->findByAttribute(it.key(), it.value());
0177 
0178         if (!match.isNull())
0179         {
0180             return match;
0181         }
0182     }
0183 
0184     return Identity();
0185 }
0186 
0187 Identity FacialRecognitionWrapper::addIdentity(const QMultiMap<QString, QString>& attributes)
0188 {
0189     if (!d || !d->dbAvailable)
0190     {
0191         return Identity();
0192     }
0193 
0194     QMutexLocker lock(&d->mutex);
0195 
0196     if (attributes.contains(QLatin1String("uuid")))
0197     {
0198         Identity matchByUuid = findIdentity(QLatin1String("uuid"), attributes.value(QLatin1String("uuid")));
0199 
0200         if (!matchByUuid.isNull())
0201         {
0202             // This situation is not well defined.
0203 
0204             qCDebug(DIGIKAM_FACESENGINE_LOG) << "Called addIdentity with a given UUID, and there is such a UUID already in the database."
0205                                              << "The existing identity is returned without adjusting properties!";
0206 
0207             return matchByUuid;
0208         }
0209     }
0210 
0211     Identity identity;
0212     {
0213         FaceDbOperationGroup group;
0214         int id = FaceDbAccess().db()->addIdentity();
0215         identity.setId(id);
0216         identity.setAttributesMap(attributes);
0217         identity.setAttribute(QLatin1String("uuid"), QUuid::createUuid().toString());
0218         FaceDbAccess().db()->updateIdentity(identity);
0219     }
0220 
0221     d->identityCache[identity.id()] = identity;
0222 
0223     return identity;
0224 }
0225 
0226 Identity FacialRecognitionWrapper::addIdentityDebug(const QMultiMap<QString, QString>& attributes)
0227 {
0228     Identity identity;
0229     {
0230         identity.setId(attributes.value(QLatin1String("name")).toInt());
0231         identity.setAttributesMap(attributes);
0232         identity.setAttribute(QLatin1String("uuid"), QUuid::createUuid().toString());
0233     }
0234 
0235     d->identityCache[identity.id()] = identity;
0236 
0237     return identity;
0238 }
0239 
0240 void FacialRecognitionWrapper::addIdentityAttributes(int id, const QMultiMap<QString, QString>& attributes)
0241 {
0242     if (!d || !d->dbAvailable)
0243     {
0244         return;
0245     }
0246 
0247     QMutexLocker lock(&d->mutex);
0248 
0249     QHash<int, Identity>::iterator it = d->identityCache.find(id);
0250 
0251     if (it != d->identityCache.end())
0252     {
0253         QMultiMap<QString, QString> map = it->attributesMap();
0254         map.unite(attributes);
0255         it->setAttributesMap(map);
0256         FaceDbAccess().db()->updateIdentity(*it);
0257     }
0258 }
0259 
0260 void FacialRecognitionWrapper::addIdentityAttribute(int id, const QString& attribute, const QString& value)
0261 {
0262     if (!d || !d->dbAvailable)
0263     {
0264         return;
0265     }
0266 
0267     QMutexLocker lock(&d->mutex);
0268     QHash<int, Identity>::iterator it = d->identityCache.find(id);
0269 
0270     if (it != d->identityCache.end())
0271     {
0272         QMultiMap<QString, QString> map = it->attributesMap();
0273         map.insert(attribute, value);
0274         it->setAttributesMap(map);
0275         FaceDbAccess().db()->updateIdentity(*it);
0276     }
0277 }
0278 
0279 void FacialRecognitionWrapper::setIdentityAttributes(int id, const QMultiMap<QString, QString>& attributes)
0280 {
0281     if (!d || !d->dbAvailable)
0282     {
0283             return;
0284     }
0285 
0286     QMutexLocker lock(&d->mutex);
0287     QHash<int, Identity>::iterator it = d->identityCache.find(id);
0288 
0289     if (it != d->identityCache.end())
0290     {
0291         it->setAttributesMap(attributes);
0292         FaceDbAccess().db()->updateIdentity(*it);
0293     }
0294 }
0295 
0296 void FacialRecognitionWrapper::deleteIdentity(const Identity& identityToBeDeleted)
0297 {
0298     if (!d || !d->dbAvailable || identityToBeDeleted.isNull())
0299     {
0300         return;
0301     }
0302 
0303     QMutexLocker lock(&d->mutex);
0304 
0305     FaceDbAccess().db()->deleteIdentity(identityToBeDeleted.id());
0306     d->identityCache.remove(identityToBeDeleted.id());
0307 }
0308 
0309 void FacialRecognitionWrapper::deleteIdentities(QList<Identity> identitiesToBeDeleted)
0310 {
0311     QList<Identity>::iterator identity = identitiesToBeDeleted.begin();
0312 
0313     while (identity != identitiesToBeDeleted.end())
0314     {
0315         deleteIdentity(*identity);
0316 
0317         identity = identitiesToBeDeleted.erase(identity);
0318     }
0319 }
0320 
0321 } // namespace Digikam