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 }