File indexing completed on 2025-03-09 03:55:01

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam
0004  *
0005  * Date        : 2020-05-22
0006  * Description : Wrapper of face recognition using OpenFace
0007  *
0008  * SPDX-FileCopyrightText: 2019      by Thanh Trung Dinh <dinhthanhtrung1996 at gmail dot com>
0009  * SPDX-FileCopyrightText: 2020-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText: 2020      by Nghia Duong <minhnghiaduong997 at gmail dot com>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "opencvdnnfacerecognizer_p.h"
0017 
0018 namespace Digikam
0019 {
0020 
0021 OpenCVDNNFaceRecognizer::OpenCVDNNFaceRecognizer(Classifier method)
0022     : d(new Private(method))
0023 {
0024 }
0025 
0026 OpenCVDNNFaceRecognizer::~OpenCVDNNFaceRecognizer()
0027 {
0028     delete d;
0029 }
0030 
0031 void OpenCVDNNFaceRecognizer::setNbNeighBors(int k)
0032 {
0033     d->kNeighbors = k;
0034 }
0035 
0036 void OpenCVDNNFaceRecognizer::setThreshold(float threshold)
0037 {
0038     d->threshold = threshold;
0039 }
0040 
0041 cv::Mat OpenCVDNNFaceRecognizer::prepareForRecognition(QImage& inputImage)
0042 {
0043     cv::Mat cvImage;    // = cv::Mat(image.height(), image.width(), CV_8UC3);
0044     cv::Mat cvImageWrapper;
0045 
0046     if (inputImage.format() != QImage::Format_ARGB32_Premultiplied)
0047     {
0048         inputImage = inputImage.convertToFormat(QImage::Format_ARGB32_Premultiplied);
0049     }
0050 
0051     cvImageWrapper = cv::Mat(inputImage.height(), inputImage.width(), CV_8UC4, inputImage.scanLine(0), inputImage.bytesPerLine());
0052     cv::cvtColor(cvImageWrapper, cvImage, CV_RGBA2RGB);
0053 
0054 /*
0055     resize(cvImage, cvImage, Size(256, 256), (0, 0), (0, 0), cv::INTER_LINEAR);
0056     equalizeHist(cvImage, cvImage);
0057 */
0058     return cvImage;
0059 }
0060 
0061 cv::Mat OpenCVDNNFaceRecognizer::prepareForRecognition(const cv::Mat& cvInputImage)
0062 {
0063     int TargetInputSize = 256;
0064 
0065     cv::Mat cvOutputImage;
0066 
0067     cv::resize(cvInputImage, cvOutputImage, cv::Size(TargetInputSize, TargetInputSize));
0068 
0069     return cvOutputImage;
0070 }
0071 
0072 
0073 void OpenCVDNNFaceRecognizer::train(const QList<QImage*>& images,
0074                                     const int             label,
0075                                     const QString&        context)
0076 {
0077     cv::parallel_for_(cv::Range(0, images.size()), Private::ParallelTrainer(d, images, label, context));
0078 
0079     d->newDataAdded = true;
0080 }
0081 
0082 int OpenCVDNNFaceRecognizer::recognize(QImage* inputImage)
0083 {
0084     int id = -1;
0085 
0086     cv::Mat faceEmbedding = d->extractors[0]->getFaceEmbedding(prepareForRecognition(*inputImage));
0087 
0088     switch (d->method)
0089     {
0090         case SVM:
0091         {
0092             id = d->predictSVM(faceEmbedding);
0093             break;
0094         }
0095 
0096         case OpenCV_KNN:
0097         {
0098             id = d->predictKNN(faceEmbedding);
0099             break;
0100         }
0101 
0102         case Tree:
0103         {
0104             id = d->predictKDTree(faceEmbedding);
0105             break;
0106         }
0107 
0108         case DB:
0109         {
0110             id = d->predictDb(faceEmbedding);
0111             break;
0112         }
0113 
0114         default:
0115         {
0116             qCWarning(DIGIKAM_FACEDB_LOG) << "Not recognized classifying method";
0117         }
0118     }
0119 
0120     return id;
0121 }
0122 
0123 QVector<int> OpenCVDNNFaceRecognizer::recognize(const QList<QImage*>& inputImages)
0124 {
0125     QVector<int> ids;
0126 
0127     cv::parallel_for_(cv::Range(0, inputImages.size()), Private::ParallelRecognizer(d, inputImages, ids));
0128 
0129     return ids;
0130 }
0131 
0132 
0133 void OpenCVDNNFaceRecognizer::clearTraining(const QList<int>& idsToClear, const QString& trainingContext)
0134 {
0135     if (idsToClear.isEmpty())
0136     {
0137         FaceDbAccess().db()->clearDNNTraining(trainingContext);
0138     }
0139     else
0140     {
0141         FaceDbAccess().db()->clearDNNTraining(idsToClear, trainingContext);
0142     }
0143 /*
0144     FaceDbAccess().db()->clearTreeDb();
0145 */
0146 }
0147 
0148 bool OpenCVDNNFaceRecognizer::registerTrainingData(const cv::Mat& preprocessedImage, int label)
0149 {
0150     cv::Mat faceEmbedding = d->extractors[0]->getFaceEmbedding(preprocessedImage);
0151 
0152     if (d->method == Tree)
0153     {
0154         KDNode* const newNode = d->tree->add(faceEmbedding, label);
0155 
0156         if (!newNode)
0157         {
0158             qCWarning(DIGIKAM_FACEDB_LOG) << "Error insert new node";
0159 
0160             return false;
0161         }
0162     }
0163 
0164     return true;
0165 }
0166 
0167 int OpenCVDNNFaceRecognizer::verifyTestData(const cv::Mat& preprocessedImage)
0168 {
0169     int id = -1;
0170 
0171     if (d->method == Tree)
0172     {
0173         id = d->predictKDTree(d->extractors[0]->getFaceEmbedding(preprocessedImage));
0174     }
0175 
0176     return id;
0177 }
0178 
0179 } // namespace Digikam