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

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 "facepipeline_p.h"
0017 
0018 // KDE includes
0019 
0020 #include <klocalizedstring.h>
0021 
0022 // Local includes
0023 
0024 #include "digikam_debug.h"
0025 #include "facepreviewloader.h"
0026 #include "parallelpipes.h"
0027 #include "scanstatefilter.h"
0028 
0029 namespace Digikam
0030 {
0031 
0032 FacePipeline::Private::Private(FacePipeline* const q)
0033     : databaseFilter        (nullptr),
0034       previewThread         (nullptr),
0035       detectionWorker       (nullptr),
0036       parallelDetectors     (nullptr),
0037       recognitionWorker     (nullptr),
0038       databaseWriter        (nullptr),
0039       trainerWorker         (nullptr),
0040       detectionBenchmarker  (nullptr),
0041       recognitionBenchmarker(nullptr),
0042       priority              (QThread::LowPriority),
0043       started               (false),
0044       waiting               (false),
0045       infosForFiltering     (0),
0046       packagesOnTheRoad     (0),
0047       maxPackagesOnTheRoad  (30),
0048       totalPackagesAdded    (0),
0049       q                     (q)
0050 {
0051 }
0052 
0053 void FacePipeline::Private::processBatch(const QList<ItemInfo>& infos)
0054 {
0055     if (databaseFilter)
0056     {
0057         infosForFiltering += infos.size();
0058         databaseFilter->process(infos);
0059     }
0060     else
0061     {
0062         Q_FOREACH (const ItemInfo& info, infos)
0063         {
0064             send(buildPackage(info));
0065         }
0066     }
0067 }
0068 
0069 /// called by filter
0070 void FacePipeline::Private::sendFromFilter(const QList<FacePipelineExtendedPackage::Ptr>& packages)
0071 {
0072     infosForFiltering -= packages.size();
0073 
0074     Q_FOREACH (const FacePipelineExtendedPackage::Ptr& package, packages)
0075     {
0076         send(package);
0077     }
0078 }
0079 
0080 /// called by filter
0081 void FacePipeline::Private::skipFromFilter(const QList<ItemInfo>& infosForSkipping)
0082 {
0083     infosForFiltering -= infosForSkipping.size();
0084 
0085     Q_EMIT q->skipped(infosForSkipping);
0086 
0087     // everything skipped?
0088 
0089     checkFinished();
0090 }
0091 
0092 FacePipelineExtendedPackage::Ptr FacePipeline::Private::filterOrBuildPackage(const ItemInfo& info)
0093 {
0094     if (databaseFilter)
0095     {
0096         return databaseFilter->filter(info);
0097     }
0098     else
0099     {
0100         return buildPackage(info);
0101     }
0102 }
0103 
0104 FacePipelineExtendedPackage::Ptr FacePipeline::Private::buildPackage(const ItemInfo& info)
0105 {
0106     FacePipelineExtendedPackage::Ptr package(new FacePipelineExtendedPackage);
0107     package->info     = info;
0108     package->filePath = info.filePath();
0109 
0110     return package;
0111 }
0112 
0113 FacePipelineExtendedPackage::Ptr FacePipeline::Private::buildPackage(const ItemInfo& info,
0114                                                                      const FacePipelineFaceTagsIface& face,
0115                                                                      const DImg& image)
0116 {
0117     FacePipelineFaceTagsIfaceList faces;
0118     faces << face;
0119 
0120     return buildPackage(info, faces, image);
0121 }
0122 
0123 FacePipelineExtendedPackage::Ptr FacePipeline::Private::buildPackage(const ItemInfo& info,
0124                                                                      const FacePipelineFaceTagsIfaceList& faces,
0125                                                                      const DImg& image)
0126 {
0127     FacePipelineExtendedPackage::Ptr package = buildPackage(info);
0128     package->databaseFaces                   = faces;
0129     package->image                           = image;
0130 
0131     return package;
0132 }
0133 
0134 void FacePipeline::Private::send(const FacePipelineExtendedPackage::Ptr& package)
0135 {
0136     start();
0137     ++totalPackagesAdded;
0138     Q_EMIT q->processing(*package);
0139 
0140     if (senderFlowControl(package))
0141     {
0142         ++packagesOnTheRoad;
0143         Q_EMIT startProcess(package);
0144     }
0145 }
0146 
0147 void FacePipeline::Private::finishProcess(FacePipelineExtendedPackage::Ptr package)
0148 {
0149     --packagesOnTheRoad;
0150 
0151     Q_EMIT q->processed(*package);
0152     Q_EMIT q->progressValueChanged(1.0F - (float(packagesOnTheRoad + delayedPackages.size()) / totalPackagesAdded));
0153 
0154     package = nullptr;
0155 
0156     if (previewThread)
0157     {
0158         previewThread->checkRestart();
0159     }
0160 
0161     receiverFlowControl();
0162 
0163     checkFinished();
0164 }
0165 
0166 bool FacePipeline::Private::senderFlowControl(const FacePipelineExtendedPackage::Ptr& package)
0167 {
0168     if (packagesOnTheRoad > maxPackagesOnTheRoad)
0169     {
0170         delayedPackages << package;
0171 
0172         return false;
0173     }
0174 
0175     return true;
0176 }
0177 
0178 void FacePipeline::Private::receiverFlowControl()
0179 {
0180     if (!delayedPackages.isEmpty() && packagesOnTheRoad <= maxPackagesOnTheRoad)
0181     {
0182         --totalPackagesAdded; // do not add twice
0183         send(delayedPackages.takeFirst());
0184     }
0185 }
0186 
0187 bool FacePipeline::Private::hasFinished() const
0188 {
0189     return !packagesOnTheRoad && !infosForFiltering;
0190 }
0191 
0192 void FacePipeline::Private::checkFinished()
0193 {
0194     qCDebug(DIGIKAM_GENERAL_LOG) << "Check for finish: " << packagesOnTheRoad << "packages,"
0195                                  << infosForFiltering << "infos to filter, hasFinished()" << hasFinished();
0196 
0197     if (hasFinished())
0198     {
0199         totalPackagesAdded = 0;
0200         Q_EMIT q->finished();
0201 
0202         // stop threads
0203 
0204         stop();
0205     }
0206 }
0207 
0208 void FacePipeline::Private::start()
0209 {
0210     if (started)
0211     {
0212         return;
0213     }
0214 
0215     Q_EMIT q->scheduled();
0216 
0217     WorkerObject*  workerObject = nullptr;
0218     ParallelPipes* pipes        = nullptr;
0219 
0220     Q_FOREACH (QObject* const element, pipeline)
0221     {
0222         if      ((workerObject = qobject_cast<WorkerObject*>(element)))
0223         {
0224             workerObject->schedule();
0225         }
0226         else if ((pipes = qobject_cast<ParallelPipes*>(element)))
0227         {
0228             pipes->schedule();
0229         }
0230     }
0231 
0232     started = true;
0233     waiting = false;
0234     Q_EMIT q->started(i18n("Applying face changes"));
0235 }
0236 
0237 void FacePipeline::Private::stop()
0238 {
0239     if (!started)
0240     {
0241         return;
0242     }
0243 
0244     if (previewThread)
0245     {
0246         previewThread->cancel();
0247     }
0248 
0249     Q_FOREACH (ThumbnailLoadThread* const thread, thumbnailLoadThreads)
0250     {
0251         thread->stopAllTasks();
0252     }
0253 
0254     WorkerObject* workerObject = nullptr;
0255     ParallelPipes* pipes       = nullptr;
0256     DynamicThread* thread      = nullptr;
0257 
0258     Q_FOREACH (QObject* const element, pipeline)
0259     {
0260         if      ((workerObject = qobject_cast<WorkerObject*>(element)))
0261         {
0262             workerObject->deactivate();
0263         }
0264         else if ((pipes = qobject_cast<ParallelPipes*>(element)))
0265         {
0266             pipes->deactivate();
0267         }
0268         else if ((thread = qobject_cast<DynamicThread*>(element)))
0269         {
0270             thread->stop();
0271         }
0272     }
0273 
0274     started = false;
0275     waiting = true;
0276 }
0277 
0278 void FacePipeline::Private::wait()
0279 {
0280     if (!waiting)
0281     {
0282         return;
0283     }
0284 
0285     if (previewThread)
0286     {
0287         previewThread->wait();
0288     }
0289 
0290     Q_FOREACH (ThumbnailLoadThread* const thread, thumbnailLoadThreads)
0291     {
0292         thread->wait();
0293     }
0294 
0295     WorkerObject* workerObject = nullptr;
0296     ParallelPipes* pipes       = nullptr;
0297     DynamicThread* thread      = nullptr;
0298 
0299     Q_FOREACH (QObject* const element, pipeline)
0300     {
0301         if      ((workerObject = qobject_cast<WorkerObject*>(element)))
0302         {
0303             workerObject->wait();
0304         }
0305         else if ((pipes = qobject_cast<ParallelPipes*>(element)))
0306         {
0307             pipes->wait();
0308         }
0309         else if ((thread = qobject_cast<DynamicThread*>(element)))
0310         {
0311             thread->wait();
0312         }
0313     }
0314 
0315     waiting = false;
0316 }
0317 
0318 void FacePipeline::Private::applyPriority()
0319 {
0320     WorkerObject*  workerObject = nullptr;
0321     ParallelPipes* pipes        = nullptr;
0322 
0323     Q_FOREACH (QObject* const element, pipeline)
0324     {
0325         if      ((workerObject = qobject_cast<WorkerObject*>(element)))
0326         {
0327             workerObject->setPriority(priority);
0328         }
0329         else if ((pipes = qobject_cast<ParallelPipes*>(element)))
0330         {
0331             pipes->setPriority(priority);
0332         }
0333     }
0334 
0335     Q_FOREACH (ThumbnailLoadThread* const thread, thumbnailLoadThreads)
0336     {
0337         thread->setPriority(priority);
0338     }
0339 }
0340 
0341 ThumbnailLoadThread* FacePipeline::Private::createThumbnailLoadThread()
0342 {
0343     ThumbnailLoadThread* const thumbnailLoadThread = new ThumbnailLoadThread;
0344     thumbnailLoadThread->setPixmapRequested(false);
0345     thumbnailLoadThread->setThumbnailSize(ThumbnailLoadThread::maximumThumbnailSize());
0346 
0347     // Image::recommendedSizeForRecognition()
0348 
0349     thumbnailLoadThread->setPriority(priority);
0350 
0351     thumbnailLoadThreads << thumbnailLoadThread;
0352 
0353     return thumbnailLoadThread;
0354 }
0355 
0356 } // namespace Digikam
0357 
0358 #include "moc_facepipeline_p.cpp"