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"