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 "databasewriter.h" 0017 0018 // Local includes 0019 0020 #include "digikam_debug.h" 0021 0022 namespace Digikam 0023 { 0024 0025 DatabaseWriter::DatabaseWriter(FacePipeline::WriteMode mode, FacePipeline::Private* const dd) 0026 : mode (mode), 0027 thumbnailLoadThread(dd->createThumbnailLoadThread()), 0028 d (dd) 0029 { 0030 } 0031 0032 DatabaseWriter::~DatabaseWriter() 0033 { 0034 } 0035 0036 void DatabaseWriter::process(const FacePipelineExtendedPackage::Ptr& package) 0037 { 0038 if (package->databaseFaces.isEmpty()) 0039 { 0040 // Detection / Recognition 0041 0042 FaceUtils utils; 0043 0044 // OverwriteUnconfirmed means that a new scan discarded unconfirmed results of previous scans 0045 // (assuming at least equal or new, better methodology is in use compared to the previous scan) 0046 0047 if ((mode == FacePipeline::OverwriteUnconfirmed) && 0048 (package->processFlags & FacePipelinePackage::ProcessedByDetector)) 0049 { 0050 QList<FaceTagsIface> oldEntries = utils.unconfirmedFaceTagsIfaces(package->info.id()); 0051 qCDebug(DIGIKAM_GENERAL_LOG) << "Removing old entries" << oldEntries; 0052 utils.removeFaces(oldEntries); 0053 } 0054 else if ((mode == FacePipeline::OverwriteAllFaces) && 0055 (package->processFlags & FacePipelinePackage::ProcessedByDetector)) 0056 { 0057 utils.removeAllFaces(package->info.id()); 0058 } 0059 0060 // mark the whole image as scanned-for-faces 0061 0062 utils.markAsScanned(package->info); 0063 0064 if (!package->info.isNull() && !package->detectedFaces.isEmpty()) 0065 { 0066 package->databaseFaces = utils.writeUnconfirmedResults(package->info.id(), 0067 package->detectedFaces, 0068 package->recognitionResults, 0069 package->image.originalSize()); 0070 package->databaseFaces.setRole(FacePipelineFaceTagsIface::DetectedFromImage); 0071 0072 if (!package->image.isNull()) 0073 { 0074 utils.storeThumbnails(thumbnailLoadThread, package->filePath, 0075 package->databaseFaces.toFaceTagsIfaceList(), package->image); 0076 } 0077 } 0078 } 0079 else if (package->processFlags & FacePipelinePackage::ProcessedByRecognizer) 0080 { 0081 FaceUtils utils; 0082 0083 for (int i = 0 ; i < package->databaseFaces.size() ; ++i) 0084 { 0085 if (package->databaseFaces[i].roles & FacePipelineFaceTagsIface::ForRecognition) 0086 { 0087 // Allow to overwrite existing recognition with new, possibly valid, "not recognized" status 0088 0089 int tagId = FaceTags::unknownPersonTagId(); 0090 0091 // NOTE: See bug #338485 : check if index is not outside of container size. 0092 0093 if ((i < package->recognitionResults.size()) && 0094 !package->recognitionResults[i].isNull()) 0095 { 0096 // Only perform this call if recognition as results, to prevent crash in QMap. See bug #335624 0097 0098 tagId = FaceTags::getOrCreateTagForIdentity(package->recognitionResults[i].attributesMap()); 0099 } 0100 0101 package->databaseFaces[i] = FacePipelineFaceTagsIface(utils.changeSuggestedName(package->databaseFaces[i], tagId)); 0102 package->databaseFaces[i].roles &= ~FacePipelineFaceTagsIface::ForRecognition; 0103 } 0104 } 0105 } 0106 else 0107 { 0108 // Editing database entries 0109 0110 FaceUtils utils; 0111 FacePipelineFaceTagsIfaceList add; 0112 FacePipelineFaceTagsIfaceList::iterator it; 0113 0114 for (it = package->databaseFaces.begin() ; it != package->databaseFaces.end() ; ++it) 0115 { 0116 if (it->roles & FacePipelineFaceTagsIface::ForConfirmation) 0117 { 0118 FacePipelineFaceTagsIface confirmed = FacePipelineFaceTagsIface(utils.confirmName(*it, it->assignedTagId, it->assignedRegion)); 0119 confirmed.roles |= FacePipelineFaceTagsIface::Confirmed | FacePipelineFaceTagsIface::ForTraining; 0120 add << confirmed; 0121 } 0122 else if (it->roles & FacePipelineFaceTagsIface::ForEditing) 0123 { 0124 if (it->isNull()) 0125 { 0126 // add Manually 0127 0128 FaceTagsIface newFace = utils.unconfirmedEntry(package->info.id(), it->assignedTagId, it->assignedRegion); 0129 utils.addManually(newFace); 0130 add << FacePipelineFaceTagsIface(newFace); 0131 } 0132 else if (it->assignedRegion.isValid()) 0133 { 0134 add << FacePipelineFaceTagsIface(utils.changeRegion(*it, it->assignedRegion)); 0135 } 0136 else if (FaceTags::isPerson(it->assignedTagId)) 0137 { 0138 // Change Tag operation. 0139 0140 add << FacePipelineFaceTagsIface(utils.changeTag(*it, it->assignedTagId)); 0141 } 0142 else 0143 { 0144 utils.removeFace(*it); 0145 } 0146 0147 it->roles &= ~FacePipelineFaceTagsIface::ForEditing; 0148 it->roles |= FacePipelineFaceTagsIface::Edited; 0149 } 0150 0151 // Training is done by trainerWorker 0152 } 0153 0154 if (!package->image.isNull()) 0155 { 0156 utils.storeThumbnails(thumbnailLoadThread, package->filePath, add.toFaceTagsIfaceList(), package->image); 0157 } 0158 0159 package->databaseFaces << add; 0160 } 0161 0162 package->processFlags |= FacePipelinePackage::WrittenToDatabase; 0163 0164 Q_EMIT processed(package); 0165 } 0166 0167 } // namespace Digikam 0168 0169 #include "moc_databasewriter.cpp"