File indexing completed on 2025-01-05 03:58:06

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2010-06-21
0007  * Description : GUI test program for FacesEngine
0008  *
0009  * SPDX-FileCopyrightText: 2010-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText:      2010 by Alex Jironkin <alexjironkin at gmail dot com>
0011  * SPDX-FileCopyrightText:      2010 by Aditya Bhatt <adityabhatt1991 at gmail dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "demomainwindow.h"
0018 #include "ui_demomainwindow.h"
0019 
0020 // Qt includes
0021 
0022 #include <QGraphicsScene>
0023 #include <QGraphicsView>
0024 #include <QGraphicsPixmapItem>
0025 #include <QElapsedTimer>
0026 #include <QStandardPaths>
0027 
0028 // Local includes
0029 
0030 #include "digikam_debug.h"
0031 #include "facialrecognition_wrapper.h"
0032 #include "facedetector.h"
0033 #include "demofaceitem.h"
0034 #include "dfiledialog.h"
0035 
0036 using namespace std;
0037 using namespace Digikam;
0038 
0039 namespace FaceEngineDemo
0040 {
0041 
0042 // --------------------------------------------------------------------------------------------------
0043 
0044 class Q_DECL_HIDDEN MainWindow::Private
0045 {
0046 public:
0047 
0048     explicit Private()
0049       : ui            (nullptr),
0050         myScene       (nullptr),
0051         myView        (nullptr),
0052         lastPhotoItem (nullptr),
0053         detector      (nullptr),
0054         scale         (0.0)
0055     {
0056     }
0057 
0058     Ui::MainWindow*          ui;
0059     QGraphicsScene*          myScene;
0060     QGraphicsView*           myView;
0061     QGraphicsPixmapItem*     lastPhotoItem;
0062     QList<FaceItem*>         faceitems;
0063 
0064     FacialRecognitionWrapper database;
0065     FaceDetector*            detector;
0066     QImage                   currentPhoto;
0067     double                   scale;
0068     QString                  lastFileOpenPath;
0069 };
0070 
0071 MainWindow::MainWindow(QWidget* const parent)
0072     : QMainWindow(parent),
0073       d(new Private)
0074 {
0075     d->ui = new Ui::MainWindow;
0076     d->ui->setupUi(this);
0077     d->ui->recogniseBtn->setEnabled(false);
0078     d->ui->updateDatabaseBtn->setEnabled(false);
0079     d->ui->detectFacesBtn->setEnabled(false);
0080     d->ui->configLocation->setReadOnly(true);
0081 
0082     connect(d->ui->openImageBtn, SIGNAL(clicked()),
0083             this, SLOT(slotOpenImage()));
0084 
0085     connect(d->ui->accuracySlider, SIGNAL(valueChanged(int)),
0086             this, SLOT(slotUpdateAccuracy()));
0087 
0088     connect(d->ui->sensitivitySlider, SIGNAL(valueChanged(int)),
0089             this, SLOT(slotUpdateSensitivity()));
0090 
0091     connect(d->ui->detectFacesBtn, SIGNAL(clicked()),
0092             this, SLOT(slotDetectFaces()));
0093 
0094     connect(d->ui->recogniseBtn, SIGNAL(clicked()),
0095             this, SLOT(slotRecognise()));
0096 
0097     connect(d->ui->updateDatabaseBtn, SIGNAL(clicked()),
0098             this, SLOT(slotUpdateDatabase()));
0099 
0100     d->myScene                = new QGraphicsScene();
0101     QGridLayout* const layout = new QGridLayout;
0102     d->myView                 = new QGraphicsView(d->myScene);
0103 
0104     d->myView->setCacheMode(QGraphicsView::CacheBackground);
0105     d->myScene->setItemIndexMethod(QGraphicsScene::NoIndex);
0106 
0107     setMouseTracking(true);
0108     layout->addWidget(d->myView);
0109 
0110     d->ui->widget->setLayout(layout);
0111 
0112     d->myView->show();
0113 
0114     d->detector = new FaceDetector();
0115 
0116     d->ui->accuracySlider->setValue(80);
0117     d->ui->sensitivitySlider->setValue(80);
0118 
0119     d->lastFileOpenPath = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).first();
0120 }
0121 
0122 MainWindow::~MainWindow()
0123 {
0124     delete d->ui;
0125     delete d->detector;
0126     delete d;
0127 }
0128 
0129 void MainWindow::changeEvent(QEvent* e)
0130 {
0131     QMainWindow::changeEvent(e);
0132 
0133     switch (e->type())
0134     {
0135         case QEvent::LanguageChange:
0136         {
0137             d->ui->retranslateUi(this);
0138             break;
0139         }
0140 
0141         default:
0142         {
0143             break;
0144         }
0145     }
0146 }
0147 
0148 void MainWindow::clearScene()
0149 {
0150     QList<QGraphicsItem*> list = d->myScene->items();
0151 
0152     for (int i = 0 ; i < list.size() ; ++i)
0153     {
0154         d->myScene->removeItem(list.at(i));
0155     }
0156 }
0157 
0158 void MainWindow::slotOpenImage()
0159 {
0160     QString file = DFileDialog::getOpenFileName(this, QLatin1String("Select Image to Open"),
0161                                                 d->lastFileOpenPath,
0162                                                 QString::fromLatin1("Image Files (*.png *.jpg *.bmp *.pgm)"));
0163 
0164     if (file.isEmpty())
0165     {
0166         return;
0167     }
0168 
0169     d->lastFileOpenPath = QFileInfo(file).absolutePath();
0170 
0171     clearScene();
0172 
0173     qCDebug(DIGIKAM_TESTS_LOG) << "Opened file " << file;
0174 
0175     d->currentPhoto.load(file);
0176     d->lastPhotoItem = new QGraphicsPixmapItem(QPixmap::fromImage(d->currentPhoto));
0177 
0178     if ((1.0 * d->ui->widget->width() / d->currentPhoto.width()) < (1.0 * d->ui->widget->height() / d->currentPhoto.height()))
0179     {
0180         d->scale = 1.0 * d->ui->widget->width() / d->currentPhoto.width();
0181     }
0182     else
0183     {
0184         d->scale = 1.0 * d->ui->widget->height() / d->currentPhoto.height();
0185     }
0186 
0187     d->lastPhotoItem->setScale(d->scale);
0188 
0189     d->myScene->addItem(d->lastPhotoItem);
0190     d->ui->detectFacesBtn->setEnabled(true);
0191 }
0192 
0193 void MainWindow::slotDetectFaces()
0194 {
0195     setCursor(Qt::WaitCursor);
0196 
0197     QList<QRectF> currentFaces = d->detector->detectFaces(d->currentPhoto);
0198 
0199     qCDebug(DIGIKAM_TESTS_LOG) << "FacesEngine detected : " << currentFaces.size() << " faces.";
0200     qCDebug(DIGIKAM_TESTS_LOG) << "Coordinates of detected faces : ";
0201 
0202     Q_FOREACH (const QRectF& r, currentFaces)
0203     {
0204         qCDebug(DIGIKAM_TESTS_LOG) << r;
0205     }
0206 
0207     Q_FOREACH (FaceItem* const item, d->faceitems)
0208     {
0209         item->setVisible(false);
0210     }
0211 
0212     d->faceitems.clear();
0213 
0214     for (int i = 0 ; i < currentFaces.size() ; ++i)
0215     {
0216         QRect face = d->detector->toAbsoluteRect(currentFaces[i], d->currentPhoto.size());
0217         d->faceitems.append(new FaceItem(nullptr, d->myScene, face, d->scale));
0218         qCDebug(DIGIKAM_TESTS_LOG) << face;
0219     }
0220 
0221     d->ui->recogniseBtn->setEnabled(true);
0222     d->ui->updateDatabaseBtn->setEnabled(true);
0223 
0224     unsetCursor();
0225 }
0226 
0227 void MainWindow::slotUpdateAccuracy()
0228 {
0229     int value = d->ui->accuracySlider->value();
0230     d->detector->setParameter(QString::fromLatin1("accuracy"), value/100.0);
0231 }
0232 
0233 void MainWindow::slotUpdateSensitivity()
0234 {
0235     int value = d->ui->sensitivitySlider->value();
0236     d->detector->setParameter(QString::fromLatin1("sensitivity"), value);
0237 }
0238 
0239 void MainWindow::slotRecognise()
0240 {
0241     setCursor(Qt::WaitCursor);
0242 
0243     int i = 0;
0244 
0245     Q_FOREACH (FaceItem* const item, d->faceitems)
0246     {
0247         QElapsedTimer timer;
0248         timer.start();
0249 
0250         QImage* face      = new QImage();
0251         *face             = d->currentPhoto.copy(item->originalRect());
0252 
0253         Identity identity = d->database.recognizeFace(face);
0254         int elapsed       = timer.elapsed();
0255 
0256         qCDebug(DIGIKAM_TESTS_LOG) << "Recognition took " << elapsed << " for Face #" << i+1;
0257 
0258         if (!identity.isNull())
0259         {
0260             item->suggest(identity.attribute(QString::fromLatin1("name")));
0261 
0262             qCDebug(DIGIKAM_TESTS_LOG) << "Face #" << i+1 << " is closest to the person with ID " << identity.id()
0263                      << " and name "<< identity.attribute(QString::fromLatin1("name"));
0264         }
0265         else
0266         {
0267             qCDebug(DIGIKAM_TESTS_LOG) << "Face #" << i+1 << " : no Identity match from database.";
0268         }
0269 
0270         i++;
0271     }
0272 
0273     unsetCursor();
0274 }
0275 
0276 void MainWindow::slotUpdateDatabase()
0277 {
0278     setCursor(Qt::WaitCursor);
0279 
0280     int i = 0;
0281 
0282     Q_FOREACH (FaceItem* const item, d->faceitems)
0283     {
0284         if (item->text() != QString::fromLatin1("?"))
0285         {
0286             QElapsedTimer timer;
0287             timer.start();
0288 
0289             QString name = item->text();
0290             qCDebug(DIGIKAM_TESTS_LOG) << "Face #" << i+1 << ": training name '" << name << "'";
0291 
0292             Identity identity = d->database.findIdentity(QString::fromLatin1("name"), name);
0293 
0294             if (identity.isNull())
0295             {
0296                 QMultiMap<QString, QString> attributes;
0297                 attributes.insert(QString::fromLatin1("name"), name);
0298                 identity                                = d->database.addIdentity(attributes);
0299                 qCDebug(DIGIKAM_TESTS_LOG) << "Adding new identity ID " << identity.id() << " to database for name " << name;
0300             }
0301             else
0302             {
0303                 qCDebug(DIGIKAM_TESTS_LOG) << "Found existing identity ID " << identity.id() << " from database for name " << name;
0304             }
0305 
0306             QImage* face = new QImage();
0307             *face        = d->currentPhoto.copy(item->originalRect());
0308 
0309             d->database.train(identity, face, QString::fromLatin1("test application"));
0310 
0311             int elapsed  = timer.elapsed();
0312 
0313             qCDebug(DIGIKAM_TESTS_LOG) << "Training took " << elapsed << " for Face #" << i+1;
0314         }
0315 
0316         ++i;
0317     }
0318 
0319     unsetCursor();
0320 }
0321 
0322 } // namespace FaceEngineDemo
0323 
0324 #include "moc_demomainwindow.cpp"