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-10-09 0007 * Description : Widget to choose options for face scanning 0008 * 0009 * SPDX-FileCopyrightText: 2010-2012 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 // NOTE: Uncomment this line to enable detect and recognize option 0017 // Currently this option is hidden, since it's not handled properly and provides confusing functionality => Fix it later 0018 //#define ENABLE_DETECT_AND_RECOGNIZE 0019 0020 #include "facescanwidget_p.h" 0021 0022 namespace Digikam 0023 { 0024 0025 FaceScanWidget::FaceScanWidget(QWidget* const parent) 0026 : QTabWidget (parent), 0027 StateSavingObject(this), 0028 d (new Private) 0029 { 0030 setObjectName(d->configName); 0031 setupUi(); 0032 setupConnections(); 0033 } 0034 0035 FaceScanWidget::~FaceScanWidget() 0036 { 0037 delete d; 0038 } 0039 0040 void FaceScanWidget::doLoadState() 0041 { 0042 KConfigGroup group = getConfigGroup(); 0043 QString mainTask = group.readEntry(entryName(d->configMainTask), 0044 d->configValueDetect); 0045 0046 if (mainTask == d->configValueRecognizedMarkedFaces) 0047 { 0048 d->reRecognizeButton->setChecked(true); 0049 } 0050 else if (mainTask == d->configValueDetectAndRecognize) 0051 { 0052 0053 #ifdef ENABLE_DETECT_AND_RECOGNIZE 0054 0055 d->detectAndRecognizeButton->setChecked(true); 0056 0057 #else 0058 0059 d->detectButton->setChecked(true); 0060 0061 #endif 0062 0063 } 0064 else 0065 { 0066 d->detectButton->setChecked(true); 0067 } 0068 0069 FaceScanSettings::AlreadyScannedHandling handling; 0070 handling = (FaceScanSettings::AlreadyScannedHandling)group.readEntry(entryName(d->configAlreadyScannedHandling), 0071 (int)FaceScanSettings::Skip); 0072 0073 d->alreadyScannedBox->setCurrentIndex(d->alreadyScannedBox->findData(handling)); 0074 0075 d->accuracyInput->setValue(ApplicationSettings::instance()->getFaceDetectionAccuracy() * 100); 0076 0077 d->albumSelectors->loadState(); 0078 0079 d->useYoloV3Button->setChecked(ApplicationSettings::instance()->getFaceDetectionYoloV3()); 0080 0081 d->useFullCpuButton->setChecked(group.readEntry(entryName(d->configUseFullCpu), false)); 0082 } 0083 0084 void FaceScanWidget::doSaveState() 0085 { 0086 KConfigGroup group = getConfigGroup(); 0087 0088 QString mainTask; 0089 0090 if (d->detectButton->isChecked()) 0091 { 0092 mainTask = d->configValueDetect; 0093 } 0094 0095 #ifdef ENABLE_DETECT_AND_RECOGNIZE 0096 0097 else if (d->detectAndRecognizeButton->isChecked()) 0098 { 0099 mainTask = d->configValueDetectAndRecognize; 0100 } 0101 0102 #endif 0103 0104 else // d->reRecognizeButton 0105 { 0106 mainTask = d->configValueRecognizedMarkedFaces; 0107 } 0108 0109 group.writeEntry(entryName(d->configMainTask), mainTask); 0110 group.writeEntry(entryName(d->configAlreadyScannedHandling), 0111 d->alreadyScannedBox->itemData(d->alreadyScannedBox->currentIndex()).toInt()); 0112 0113 ApplicationSettings::instance()->setFaceDetectionAccuracy(double(d->accuracyInput->value()) / 100); 0114 d->albumSelectors->saveState(); 0115 0116 ApplicationSettings::instance()->setFaceDetectionYoloV3(d->useYoloV3Button->isChecked()); 0117 0118 group.writeEntry(entryName(d->configUseFullCpu), d->useFullCpuButton->isChecked()); 0119 } 0120 0121 void FaceScanWidget::setupUi() 0122 { 0123 // ---- Workflow tab -------- 0124 0125 d->workflowWidget = new QWidget(this); 0126 d->workflowWidget->setToolTip(i18nc("@info:tooltip", 0127 "digiKam can search for faces in your photos.\n" 0128 "When you have identified your friends on a number of photos,\n" 0129 "it can also recognize the people shown on your photos.")); 0130 0131 QVBoxLayout* const optionLayout = new QVBoxLayout; 0132 0133 QHBoxLayout* const scanOptionLayout = new QHBoxLayout; 0134 0135 d->alreadyScannedBox = new SqueezedComboBox; 0136 d->alreadyScannedBox->addSqueezedItem(i18nc("@label:listbox", "Skip images already scanned"), FaceScanSettings::Skip); 0137 d->alreadyScannedBox->addSqueezedItem(i18nc("@label:listbox", "Scan again and merge results"), FaceScanSettings::Merge); 0138 d->alreadyScannedBox->addSqueezedItem(i18nc("@label:listbox", "Clear unconfirmed results and rescan"), FaceScanSettings::Rescan); 0139 d->alreadyScannedBox->addSqueezedItem(i18nc("@label:listbox", "Clear all previous results and rescan"), FaceScanSettings::ClearAll); 0140 0141 QString buttonText; 0142 d->helpButton = new QPushButton(QIcon::fromTheme(QLatin1String("help-browser")), buttonText); 0143 d->helpButton->setToolTip(i18nc("@info", "Help")); 0144 0145 connect(d->helpButton, &QPushButton::clicked, 0146 this, []() 0147 { 0148 openOnlineDocumentation(QLatin1String("main_window"), QLatin1String("people_view")); 0149 } 0150 ); 0151 0152 scanOptionLayout->addWidget(d->alreadyScannedBox, 9); 0153 scanOptionLayout->addWidget(d->helpButton, 1); 0154 0155 optionLayout->addLayout(scanOptionLayout); 0156 0157 d->alreadyScannedBox->setCurrentIndex(FaceScanSettings::Skip); 0158 0159 d->detectButton = new QRadioButton(i18nc("@option:radio", "Detect faces")); 0160 d->detectButton->setToolTip(i18nc("@info", "Find all faces in your photos")); 0161 0162 #ifdef ENABLE_DETECT_AND_RECOGNIZE 0163 0164 d->detectAndRecognizeButton = new QRadioButton(i18nc("@option:radio", "Detect and recognize faces")); 0165 d->detectAndRecognizeButton->setToolTip(i18nc("@info", "Find all faces in your photos and\n" 0166 "try to recognize which person is depicted")); 0167 #endif 0168 0169 d->reRecognizeButton = new QRadioButton(i18nc("@option:radio", "Recognize faces")); 0170 d->reRecognizeButton->setToolTip(i18nc("@info", "Try again to recognize the people depicted\n" 0171 "on marked but yet unconfirmed faces.")); 0172 0173 optionLayout->addWidget(d->detectButton); 0174 0175 #ifdef ENABLE_DETECT_AND_RECOGNIZE 0176 0177 optionLayout->addWidget(d->detectAndRecognizeButton); 0178 0179 #endif 0180 0181 optionLayout->addWidget(d->reRecognizeButton); 0182 0183 #ifdef ENABLE_DETECT_AND_RECOGNIZE 0184 0185 QStyleOptionButton buttonOption; 0186 buttonOption.initFrom(d->detectAndRecognizeButton); 0187 int indent = style()->subElementRect(QStyle::SE_RadioButtonIndicator, &buttonOption, d->detectAndRecognizeButton).width(); 0188 optionLayout->setColumnMinimumWidth(0, indent); 0189 0190 #endif 0191 0192 d->workflowWidget->setLayout(optionLayout); 0193 addTab(d->workflowWidget, i18nc("@title:tab", "Workflow")); 0194 0195 // ---- Album tab --------- 0196 0197 d->albumSelectors = new AlbumSelectors(QString(), d->configName, 0198 this, AlbumSelectors::AlbumType::All, true); 0199 addTab(d->albumSelectors, i18nc("@title:tab", "Search in")); 0200 0201 // ---- Settings tab ------ 0202 0203 QWidget* const settingsTab = new QWidget(this); 0204 QVBoxLayout* const settingsLayout = new QVBoxLayout(settingsTab); 0205 0206 QGroupBox* const accuracyBox = new QGroupBox(i18nc("@label", "Face Accuracy"), settingsTab); 0207 QGridLayout* const accuracyGrid = new QGridLayout(accuracyBox); 0208 0209 QLabel* const sensitivityLabel = new QLabel(i18nc("@label left extremities of a scale", "Sensitivity"), settingsTab); 0210 sensitivityLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); 0211 0212 QLabel* const specificityLabel = new QLabel(i18nc("@label right extremities of a scale", "Specificity"), settingsTab); 0213 specificityLabel->setAlignment(Qt::AlignTop | Qt::AlignRight); 0214 0215 d->accuracyInput = new DIntNumInput(settingsTab); 0216 d->accuracyInput->setDefaultValue(70); 0217 d->accuracyInput->setRange(0, 100, 10); 0218 d->accuracyInput->setToolTip(i18nc("@info:tooltip", 0219 "Adjust sensitivity versus specificity: the higher the value, the more accurately faces will\n" 0220 "be recognized, but less faces will be recognized\n" 0221 "(only faces that are very similar to pre-tagged faces are recognized).")); 0222 0223 accuracyGrid->addWidget(d->accuracyInput, 0, 0, 1, 3); 0224 accuracyGrid->addWidget(sensitivityLabel, 1, 0, 1, 1); 0225 accuracyGrid->addWidget(specificityLabel, 1, 2, 1, 1); 0226 accuracyGrid->setColumnStretch(1, 10); 0227 0228 d->useYoloV3Button = new QCheckBox(settingsTab); 0229 d->useYoloV3Button->setText(i18nc("@option:check", "Use YOLO v3 detection model")); 0230 d->useYoloV3Button->setToolTip(i18nc("@info:tooltip", 0231 "Face detection with YOLO v3 data model. Better results but slower.")); 0232 0233 d->useFullCpuButton = new QCheckBox(settingsTab); 0234 d->useFullCpuButton->setText(i18nc("@option:check", "Work on all processor cores")); 0235 d->useFullCpuButton->setToolTip(i18nc("@info:tooltip", 0236 "Face detection and recognition are time-consuming tasks.\n" 0237 "You can choose if you wish to employ all processor cores\n" 0238 "on your system, or work in the background only on one core.")); 0239 0240 settingsLayout->addWidget(accuracyBox); 0241 settingsLayout->addWidget(d->useYoloV3Button); 0242 settingsLayout->addWidget(d->useFullCpuButton); 0243 0244 settingsLayout->addStretch(10); 0245 0246 addTab(settingsTab, i18nc("@title:tab", "Settings")); 0247 } 0248 0249 void FaceScanWidget::setupConnections() 0250 { 0251 /* 0252 connect(d->detectButton, SIGNAL(toggled(bool)), 0253 d->alreadyScannedBox, SLOT(setEnabled(bool))); 0254 */ 0255 0256 #ifdef ENABLE_DETECT_AND_RECOGNIZE 0257 0258 connect(d->detectAndRecognizeButton, SIGNAL(toggled(bool)), 0259 d->alreadyScannedBox, SLOT(setEnabled(bool))); 0260 0261 #endif 0262 0263 connect(d->detectButton, SIGNAL(toggled(bool)), 0264 this, SLOT(slotPrepareForDetect(bool))); 0265 0266 connect(d->reRecognizeButton, SIGNAL(toggled(bool)), 0267 this, SLOT(slotPrepareForRecognize(bool))); 0268 0269 connect(d->accuracyInput, &DIntNumInput::valueChanged, 0270 this, [=](int value) 0271 { 0272 ApplicationSettings::instance()->setFaceDetectionAccuracy(double(value) / 100); 0273 } 0274 ); 0275 0276 connect(d->useYoloV3Button, &QCheckBox::toggled, 0277 this, [=](bool yolo) 0278 { 0279 ApplicationSettings::instance()->setFaceDetectionYoloV3(yolo); 0280 } 0281 ); 0282 } 0283 0284 void FaceScanWidget::slotPrepareForDetect(bool status) 0285 { 0286 d->alreadyScannedBox->setEnabled(status); 0287 } 0288 0289 void FaceScanWidget::slotPrepareForRecognize(bool /*status*/) 0290 { 0291 d->alreadyScannedBox->setEnabled(false); 0292 } 0293 0294 bool FaceScanWidget::settingsConflicted() const 0295 { 0296 return d->settingsConflicted; 0297 } 0298 0299 FaceScanSettings FaceScanWidget::settings() const 0300 { 0301 FaceScanSettings settings; 0302 0303 d->settingsConflicted = false; 0304 0305 if (d->detectButton->isChecked()) 0306 { 0307 settings.task = FaceScanSettings::Detect; 0308 } 0309 else 0310 { 0311 0312 #ifdef ENABLE_DETECT_AND_RECOGNIZE 0313 0314 if (d->detectAndRecognizeButton->isChecked()) 0315 { 0316 settings.task = FaceScanSettings::DetectAndRecognize; 0317 } 0318 else // recognize only 0319 0320 #endif 0321 0322 { 0323 settings.task = FaceScanSettings::RecognizeMarkedFaces; 0324 0325 // preset settingsConflicted as True, since by default there are no tags to recognize 0326 0327 d->settingsConflicted = true; 0328 } 0329 } 0330 0331 settings.alreadyScannedHandling = (FaceScanSettings::AlreadyScannedHandling) 0332 d->alreadyScannedBox->itemData(d->alreadyScannedBox->currentIndex()).toInt(); 0333 0334 settings.albums = d->albumSelectors->selectedAlbumsAndTags(); 0335 settings.accuracy = double(d->accuracyInput->value()) / 100; 0336 settings.wholeAlbums = d->albumSelectors->wholeAlbumsChecked(); 0337 0338 if (d->settingsConflicted) 0339 { 0340 int numberOfIdentities = FaceDbAccess().db()->getNumberOfIdentities(); 0341 d->settingsConflicted = (numberOfIdentities == 0); 0342 } 0343 0344 settings.useYoloV3 = d->useYoloV3Button->isChecked(); 0345 settings.useFullCpu = d->useFullCpuButton->isChecked(); 0346 0347 return settings; 0348 } 0349 0350 } // namespace Digikam 0351 0352 #include "moc_facescanwidget.cpp"