File indexing completed on 2025-01-05 03:58:08
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2010-06-16 0007 * Description : Face Recognition CLI tool 0008 * NOTE: This tool is able to use ORL database which are 0009 * freely available set of images to test face recognition. 0010 * It contain 10 photos of 20 different peoples from slightly 0011 * different angles. See here for details: 0012 * www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html 0013 * 0014 * SPDX-FileCopyrightText: 2010 by Aditya Bhatt <adityabhatt1991 at gmail dot com> 0015 * 0016 * SPDX-License-Identifier: GPL-2.0-or-later 0017 * 0018 * ============================================================ */ 0019 0020 // Qt includes 0021 0022 #include <QCoreApplication> 0023 #include <QDir> 0024 #include <QImage> 0025 #include <QElapsedTimer> 0026 0027 // Local includes 0028 0029 #include "digikam_debug.h" 0030 #include "facialrecognition_wrapper.h" 0031 #include "coredbaccess.h" 0032 #include "dbengineparameters.h" 0033 0034 using namespace Digikam; 0035 0036 QStringList toPaths(char** argv, int startIndex, int argc) 0037 { 0038 QStringList files; 0039 0040 for (int i = startIndex ; i < argc ; ++i) 0041 { 0042 files << QString::fromLocal8Bit(argv[i]); 0043 } 0044 0045 return files; 0046 } 0047 0048 QList<QImage*> toImages(const QStringList& paths) 0049 { 0050 QList<QImage*> images; 0051 0052 Q_FOREACH (const QString& path, paths) 0053 { 0054 images << new QImage(path); 0055 } 0056 0057 return images; 0058 } 0059 0060 // -------------------------------------------------------------------------------------------------- 0061 0062 int main(int argc, char** argv) 0063 { 0064 if ((argc < 2) || ((QString::fromLatin1(argv[1]) == QString::fromLatin1("train")) && (argc < 3))) 0065 { 0066 qCDebug(DIGIKAM_TESTS_LOG) << "Bad Arguments!!!\nUsage: " << argv[0] 0067 << " identify <image1> <image2> ... | train name <image1> <image2> ... " 0068 "| ORL <path to orl_faces>"; 0069 return 0; 0070 } 0071 0072 QCoreApplication app(argc, argv); 0073 app.setApplicationName(QString::fromLatin1("digikam")); // for DB init. 0074 DbEngineParameters prm = DbEngineParameters::parametersFromConfig(); 0075 CoreDbAccess::setParameters(prm, CoreDbAccess::MainApplication); 0076 FacialRecognitionWrapper recognizer; 0077 0078 if (QString::fromLatin1(argv[1]) == QString::fromLatin1("identify")) 0079 { 0080 QStringList paths = toPaths(argv, 2, argc); 0081 QList<QImage*> images = toImages(paths); 0082 0083 QElapsedTimer timer; 0084 timer.start(); 0085 QList<Identity> identities = recognizer.recognizeFaces(images); 0086 int elapsed = timer.elapsed(); 0087 0088 qCDebug(DIGIKAM_TESTS_LOG) << "Recognition took " << elapsed 0089 << " for " << images.size() << ", " 0090 << ((float)elapsed/images.size()) << " per image"; 0091 0092 for (int i = 0 ; i < paths.size() ; ++i) 0093 { 0094 qCDebug(DIGIKAM_TESTS_LOG) << "Identified " << identities[i].attribute(QString::fromLatin1("name")) 0095 << " in " << paths[i]; 0096 } 0097 } 0098 else if (QString::fromLatin1(argv[1]) == QString::fromLatin1("train")) 0099 { 0100 QString name = QString::fromLocal8Bit(argv[2]); 0101 qCDebug(DIGIKAM_TESTS_LOG) << "Training " << name; 0102 0103 QStringList paths = toPaths(argv, 3, argc); 0104 QList<QImage*> images = toImages(paths); 0105 Identity identity = recognizer.findIdentity(QString::fromLatin1("name"), name); 0106 0107 if (identity.isNull()) 0108 { 0109 qCDebug(DIGIKAM_TESTS_LOG) << "Adding new identity to database for name " << name; 0110 QMultiMap<QString, QString> attributes; 0111 attributes.insert(QString::fromLatin1("name"), name); 0112 identity = recognizer.addIdentity(attributes); 0113 } 0114 0115 QElapsedTimer timer; 0116 timer.start(); 0117 0118 recognizer.train(identity, images, QString::fromLatin1("test application")); 0119 0120 int elapsed = timer.elapsed(); 0121 qCDebug(DIGIKAM_TESTS_LOG) << "Training took " << elapsed << " for " 0122 << images.size() << ", " 0123 << ((float)elapsed/images.size()) << " per image"; 0124 } 0125 else if (QString::fromLatin1(argv[1]) == QString::fromLatin1("orl")) 0126 { 0127 QString orlPath = QString::fromLocal8Bit(argv[2]); 0128 0129 if (orlPath.isEmpty()) 0130 { 0131 orlPath = QString::fromLatin1("orl_faces"); // relative to current dir 0132 } 0133 0134 QDir orlDir(orlPath); 0135 0136 if (!orlDir.exists()) 0137 { 0138 qCDebug(DIGIKAM_TESTS_LOG) << "Cannot find orl_faces directory"; 0139 return 0; 0140 } 0141 0142 const int OrlIdentities = 40; 0143 const int OrlSamples = 10; 0144 const QString trainingContext = QString::fromLatin1("test application"); 0145 0146 QMap<int, Identity> idMap; 0147 QList<Identity> trainingToBeCleared; 0148 0149 for (int i = 1 ; i <= OrlIdentities ; ++i) 0150 { 0151 QMultiMap<QString, QString> attributes; 0152 attributes.insert(QString::fromLatin1("name"), QString::number(i)); 0153 Identity identity = recognizer.findIdentity(attributes); 0154 0155 if (identity.isNull()) 0156 { 0157 Identity identity2 = recognizer.addIdentity(attributes); 0158 idMap[i] = identity2; 0159 qCDebug(DIGIKAM_TESTS_LOG) << "Created identity " << identity2.id() << " for ORL directory " << i; 0160 } 0161 else 0162 { 0163 qCDebug(DIGIKAM_TESTS_LOG) << "Already have identity for ORL directory " << i << ", clearing training data"; 0164 idMap[i] = identity; 0165 trainingToBeCleared << identity; 0166 } 0167 } 0168 0169 recognizer.clearTraining(trainingToBeCleared, trainingContext); 0170 QMap<int, QStringList> trainingImages, recognitionImages; 0171 0172 for (int i = 1 ; i <= OrlIdentities ; ++i) 0173 { 0174 for (int j = 1 ; j <= OrlSamples ; ++j) 0175 { 0176 QString path = orlDir.path() + QString::fromLatin1("/s%1/%2.pgm").arg(i).arg(j); 0177 0178 if (j <= OrlSamples / 2) 0179 { 0180 trainingImages[i] << path; 0181 } 0182 else 0183 { 0184 recognitionImages[i] << path; 0185 } 0186 } 0187 } 0188 0189 if (!QFileInfo::exists(trainingImages.value(1).first())) 0190 { 0191 qCDebug(DIGIKAM_TESTS_LOG) << "Could not find files of ORL database"; 0192 return 0; 0193 } 0194 0195 QElapsedTimer timer; 0196 timer.start(); 0197 0198 int correct = 0; 0199 int notRecognized = 0; 0200 int falsePositive = 0; 0201 int totalTrained = 0; 0202 int totalRecognized = 0; 0203 int elapsed = 0; 0204 0205 for (QMap<int, QStringList>::const_iterator it = trainingImages.constBegin() ; 0206 it != trainingImages.constEnd() ; ++it) 0207 { 0208 Identity identity = recognizer.findIdentity(QString::fromLatin1("name"), QString::number(it.key())); 0209 0210 if (identity.isNull()) 0211 { 0212 qCDebug(DIGIKAM_TESTS_LOG) << "Identity management failed for ORL person " << it.key(); 0213 } 0214 0215 QList<QImage*> images = toImages(it.value()); 0216 qCDebug(DIGIKAM_TESTS_LOG) << "Training ORL directory " << it.key(); 0217 recognizer.train(identity, images, trainingContext); 0218 totalTrained += images.size(); 0219 } 0220 0221 elapsed = timer.restart(); 0222 0223 if (totalTrained) 0224 { 0225 qCDebug(DIGIKAM_TESTS_LOG) << "Training 5/10 or ORL took " << elapsed 0226 << " ms, " << ((float)elapsed/totalTrained) 0227 << " ms per image"; 0228 } 0229 0230 for (QMap<int, QStringList>::const_iterator it = recognitionImages.constBegin() ; 0231 it != recognitionImages.constEnd() ; ++it) 0232 { 0233 Identity identity = idMap.value(it.key()); 0234 QList<QImage*> images = toImages(it.value()); 0235 QList<Identity> results = recognizer.recognizeFaces(images); 0236 0237 qCDebug(DIGIKAM_TESTS_LOG) << "Result for " << it.value().first() 0238 << " is identity " << results.first().id(); 0239 0240 Q_FOREACH (const Identity& foundId, results) 0241 { 0242 if (foundId.isNull()) 0243 { 0244 notRecognized++; 0245 } 0246 else if (foundId == identity) 0247 { 0248 correct++; 0249 } 0250 else 0251 { 0252 falsePositive++; 0253 } 0254 } 0255 0256 totalRecognized += images.size(); 0257 } 0258 0259 elapsed = timer.elapsed(); 0260 0261 if (totalRecognized) 0262 { 0263 qCDebug(DIGIKAM_TESTS_LOG) << "Recognition of 5/10 or ORL took " << elapsed << " ms, " << ((float)elapsed/totalRecognized) << " ms per image"; 0264 qCDebug(DIGIKAM_TESTS_LOG) << correct << " of 200 (" << (float(correct) / totalRecognized*100) << "%) were correctly recognized"; 0265 qCDebug(DIGIKAM_TESTS_LOG) << falsePositive << " of 200 (" << (float(falsePositive) / totalRecognized*100) << "%) were falsely assigned to an identity"; 0266 qCDebug(DIGIKAM_TESTS_LOG) << notRecognized << " of 200 (" << (float(notRecognized) / totalRecognized*100) << "%) were not recognized"; 0267 } 0268 else 0269 { 0270 qCDebug(DIGIKAM_TESTS_LOG) << "No face recognized"; 0271 } 0272 } 0273 0274 return 0; 0275 }