File indexing completed on 2025-01-19 03:57:57

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2010-09-03
0007  * Description : Integrated, multithread face detection / recognition
0008  *
0009  * SPDX-FileCopyrightText: 2010-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0010  * SPDX-FileCopyrightText: 2012-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "trainerworker.h"
0017 
0018 // Local includes
0019 
0020 #include "digikam_debug.h"
0021 
0022 namespace Digikam
0023 {
0024 
0025 class Q_DECL_HIDDEN MapListTrainingDataProvider : public TrainingDataProvider
0026 {
0027 public:
0028 
0029     MapListTrainingDataProvider()
0030     {
0031     }
0032 
0033     ~MapListTrainingDataProvider() override
0034     {
0035         qDeleteAll(imagesToTrain);
0036         imagesToTrain.clear();
0037     }
0038 
0039     ImageListProvider* newImages(const Identity& identity) override
0040     {
0041         if (imagesToTrain.contains(identity.id()))
0042         {
0043             QListImageListProvider* const provider = imagesToTrain.value(identity.id());
0044 
0045             if (provider)
0046             {
0047                 provider->reset();
0048             }
0049 
0050             return provider;
0051         }
0052 
0053         return &empty;
0054     }
0055 
0056     ImageListProvider* images(const Identity&) override
0057     {
0058         // Not implemented. Would be needed if we use a backend with a "holistic" approach that needs all images to train.
0059 
0060         return &empty;
0061     }
0062 
0063 public:
0064 
0065     EmptyImageListProvider             empty;
0066     QMap<int, QListImageListProvider*> imagesToTrain;
0067 };
0068 
0069 // ----------------------------------------------------------------------------------------
0070 
0071 TrainerWorker::TrainerWorker(FacePipeline::Private* const dd)
0072     : imageRetriever(dd),
0073       d             (dd)
0074 {
0075 }
0076 
0077 TrainerWorker::~TrainerWorker()
0078 {
0079     wait();    // protect detector
0080 }
0081 
0082 /**
0083  * TODO: investigate this method
0084  */
0085 void TrainerWorker::process(const FacePipelineExtendedPackage::Ptr& package)
0086 {
0087 /*
0088     qCDebug(DIGIKAM_GENERAL_LOG) << "TrainerWorker: processing one package";
0089 */
0090     // Get a list of faces with type FaceForTraining (probably type is ConfirmedFace)
0091 
0092     QList<FaceTagsIface> toTrain;
0093     QList<int>           identities;
0094     QList<Identity>      identitySet;
0095     FaceUtils            utils;
0096 
0097     Q_FOREACH (const FacePipelineFaceTagsIface& face, package->databaseFaces)
0098     {
0099         if (face.roles & FacePipelineFaceTagsIface::ForTraining)
0100         {
0101             FaceTagsIface dbFace = face;
0102             dbFace.setType(FaceTagsIface::FaceForTraining);
0103             toTrain << dbFace;
0104 
0105             Identity identity    = utils.identityForTag(dbFace.tagId(), recognizer);
0106 
0107             identities << identity.id();
0108 
0109             if (!identitySet.contains(identity))
0110             {
0111                 identitySet << identity;
0112             }
0113         }
0114     }
0115 
0116     if (!toTrain.isEmpty())
0117     {
0118         QList<QImage*> images;
0119 
0120         if (package->image.isNull())
0121         {
0122             images = imageRetriever.getThumbnails(package->filePath, toTrain);
0123         }
0124         else
0125         {
0126             images = imageRetriever.getDetails(package->image, toTrain);
0127         }
0128 
0129         MapListTrainingDataProvider provider;
0130 
0131         // Group images by identity
0132 
0133         for (int i = 0 ; i < toTrain.size() ; ++i)
0134         {
0135             QListImageListProvider* const imageList = new QListImageListProvider;
0136             imageList->setImages(QList<QImage*>() << images[i]);
0137             provider.imagesToTrain[identities[i]] = imageList;
0138         }
0139 
0140         // NOTE: cropped faces will be deleted by training provider
0141 
0142         recognizer.train(identitySet, &provider, QLatin1String("digikam"));
0143     }
0144 
0145     utils.removeFaces(toTrain);
0146     package->databaseFaces.replaceRole(FacePipelineFaceTagsIface::ForTraining, FacePipelineFaceTagsIface::Trained);
0147     package->processFlags |= FacePipelinePackage::ProcessedByTrainer;
0148 
0149     Q_EMIT processed(package);
0150 }
0151 
0152 void TrainerWorker::aboutToDeactivate()
0153 {
0154     imageRetriever.cancel();
0155 }
0156 
0157 } // namespace Digikam
0158 
0159 #include "moc_trainerworker.cpp"