diff --git a/core/app/views/sidebar/peoplesidebarwidget.cpp b/core/app/views/sidebar/peoplesidebarwidget.cpp index a6a1f87016..f624612e13 100644 --- a/core/app/views/sidebar/peoplesidebarwidget.cpp +++ b/core/app/views/sidebar/peoplesidebarwidget.cpp @@ -1,239 +1,240 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-12-05 * Description : Side Bar Widget for People * * Copyright (C) 2009-2010 by Johannes Wienke * Copyright (C) 2010-2020 by Gilles Caulier * Copyright (C) 2012 by Andi Clemens * Copyright (C) 2014 by Mohamed_Anwer * Copyright (C) 2010 by Aditya Bhatt * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "peoplesidebarwidget.h" // Qt includes #include #include #include #include #include #include #include #include // KDE includes #include #include // Local includes #include "digikam_debug.h" #include "searchtextbar.h" #include "tagfolderview.h" #include "timelinewidget.h" #include "facescanwidget.h" #include "facesdetector.h" #include "dnotificationwidget.h" namespace Digikam { class Q_DECL_HIDDEN PeopleSideBarWidget::Private { public: explicit Private() : personIcon(nullptr), textLabel(nullptr), rescanButton(nullptr), searchModificationHelper(nullptr), settingsWdg(nullptr), tagFolderView(nullptr), tagSearchBar(nullptr) { } QLabel* personIcon; QLabel* textLabel; QPushButton* rescanButton; SearchModificationHelper* searchModificationHelper; FaceScanWidget* settingsWdg; TagFolderView* tagFolderView; SearchTextBar* tagSearchBar; }; PeopleSideBarWidget::PeopleSideBarWidget(QWidget* const parent, TagModel* const model, SearchModificationHelper* const searchModificationHelper) : SidebarWidget(parent), d(new Private) { setObjectName(QLatin1String("People Sidebar")); setProperty("Shortcut", Qt::CTRL + Qt::SHIFT + Qt::Key_F9); d->searchModificationHelper = searchModificationHelper; const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); QWidget* const mainView = new QWidget(this); QScrollArea* const scrollArea = new QScrollArea(this); QVBoxLayout* const mainLayout = new QVBoxLayout(this); mainLayout->addWidget(scrollArea); mainLayout->setContentsMargins(0, 0, spacing, 0); scrollArea->setWidget(mainView); scrollArea->setWidgetResizable(true); QVBoxLayout* const vlay = new QVBoxLayout; QHBoxLayout* const hlay = new QHBoxLayout; d->tagFolderView = new TagFolderView(this, model); d->tagFolderView->setConfigGroup(getConfigGroup()); d->tagFolderView->setExpandNewCurrentItem(true); d->tagFolderView->setAlbumManagerCurrentAlbum(true); d->tagFolderView->setShowDeleteFaceTagsAction(true); d->tagFolderView->filteredModel()->listOnlyTagsWithProperty(TagPropertyName::person()); d->tagFolderView->filteredModel()->setFilterBehavior(AlbumFilterModel::StrictFiltering); d->tagSearchBar = new SearchTextBar(this, QLatin1String("ItemIconViewPeopleSearchBar")); d->tagSearchBar->setHighlightOnResult(true); d->tagSearchBar->setModel(d->tagFolderView->filteredModel(), AbstractAlbumModel::AlbumIdRole, AbstractAlbumModel::AlbumTitleRole); d->tagSearchBar->setFilterModel(d->tagFolderView->albumFilterModel()); d->personIcon = new QLabel; d->personIcon->setPixmap(QIcon::fromTheme(QLatin1String("edit-image-face-show")).pixmap(48)); d->textLabel = new QLabel; d->textLabel->setWordWrap(true); d->textLabel->setText(i18nc("@info", "digiKam can search for faces in your photos. " "When you have identified your friends on a number of photos, " "it can also recognize the people shown on your photos.")); d->settingsWdg = new FaceScanWidget(this); d->rescanButton = new QPushButton; d->rescanButton->setText(i18n("Scan collection for faces")); hlay->addWidget(d->personIcon); hlay->addWidget(d->textLabel); vlay->addLayout(hlay); vlay->addWidget(d->tagFolderView, 10); vlay->addWidget(d->tagSearchBar); vlay->addWidget(d->settingsWdg); vlay->addWidget(d->rescanButton); vlay->setContentsMargins(0, 0, spacing, 0); mainView->setLayout(vlay); connect(d->tagFolderView, SIGNAL(signalFindDuplicates(QList)), this, SIGNAL(signalFindDuplicates(QList))); connect(d->rescanButton, SIGNAL(pressed()), this, SLOT(slotScanForFaces()) ); } PeopleSideBarWidget::~PeopleSideBarWidget() { delete d; } void PeopleSideBarWidget::slotInit() { loadState(); } void PeopleSideBarWidget::setActive(bool active) { emit requestFaceMode(active); if (active) { d->tagFolderView->setCurrentAlbums(QList() << d->tagFolderView->currentAlbum()); } } void PeopleSideBarWidget::doLoadState() { d->tagFolderView->loadState(); d->settingsWdg->loadState(); } void PeopleSideBarWidget::doSaveState() { d->tagFolderView->saveState(); d->settingsWdg->saveState(); } void PeopleSideBarWidget::applySettings() { } void PeopleSideBarWidget::changeAlbumFromHistory(const QList& album) { d->tagFolderView->setCurrentAlbums(album); } void PeopleSideBarWidget::slotScanForFaces() { FaceScanSettings faceScanSettings = d->settingsWdg->settings(); if (!d->settingsWdg->settingsConflicted()) { FacesDetector* const tool = new FacesDetector(faceScanSettings); tool->start(); d->settingsWdg->setEnabled(false); d->rescanButton->setEnabled(false); connect(tool, SIGNAL(signalComplete()), this, SLOT(slotScanComplete())); connect(tool, SIGNAL(signalCanceled()), this, SLOT(slotScanComplete())); } else { emit signalNofificationError(i18n("Face recognition is aborted, because " "there are no identities to recognize. " "Please add new identities."), DNotificationWidget::Information); } } void PeopleSideBarWidget::slotScanComplete() { d->settingsWdg->setEnabled(true); d->rescanButton->setEnabled(true); + d->settingsWdg->resetRetrainAllButton(); } const QIcon PeopleSideBarWidget::getIcon() { return QIcon::fromTheme(QLatin1String("edit-image-face-show")); } const QString PeopleSideBarWidget::getCaption() { return i18nc("Browse images sorted by depicted people", "People"); } } // namespace Digikam diff --git a/core/utilities/facemanagement/widgets/facescanwidget.cpp b/core/utilities/facemanagement/widgets/facescanwidget.cpp index 91e0789b71..f28b1590f7 100644 --- a/core/utilities/facemanagement/widgets/facescanwidget.cpp +++ b/core/utilities/facemanagement/widgets/facescanwidget.cpp @@ -1,379 +1,384 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-10-09 * Description : Widget to choose options for face scanning * * Copyright (C) 2010-2012 by Marcel Wiesweg * Copyright (C) 2012-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ // NOTE: Uncomment this line to enable detect and recognize option // Currently this option is hiden, since it's not handled properly and provides confusing functionality => Fix it later //#define ENABLE_DETECT_AND_RECOGNIZE #include "facescanwidget_p.h" namespace Digikam { FaceScanWidget::FaceScanWidget(QWidget* const parent) : QWidget(parent), StateSavingObject(this), d(new Private) { setObjectName(d->configName); setupUi(); setupConnections(); } FaceScanWidget::~FaceScanWidget() { delete d; } void FaceScanWidget::doLoadState() { qCDebug(DIGIKAM_GENERAL_LOG) << getConfigGroup().name(); KConfigGroup group = getConfigGroup(); QString mainTask = group.readEntry(entryName(d->configMainTask), d->configValueDetect); if (mainTask == d->configValueRecognizedMarkedFaces) { d->reRecognizeButton->setChecked(true); } else if (mainTask == d->configValueDetectAndRecognize) { #ifdef ENABLE_DETECT_AND_RECOGNIZE d->detectAndRecognizeButton->setChecked(true); #endif d->detectButton->setChecked(true); } else { d->detectButton->setChecked(true); } FaceScanSettings::AlreadyScannedHandling handling; QString skipHandling = group.readEntry(entryName(d->configAlreadyScannedHandling), QString::fromLatin1("Skip")); if (skipHandling == QLatin1String("Rescan")) { handling = FaceScanSettings::Rescan; } else if (skipHandling == QLatin1String("Merge")) { handling = FaceScanSettings::Merge; } else // Skip { handling = FaceScanSettings::Skip; } d->alreadyScannedBox->setCurrentIndex(d->alreadyScannedBox->findData(handling)); d->accuracyInput->setValue(ApplicationSettings::instance()->getFaceDetectionAccuracy() * 100); d->albumSelectors->loadState(); d->useFullCpuButton->setChecked(group.readEntry(entryName(d->configUseFullCpu), false)); // do not load retrainAllButton state from config, dangerous } void FaceScanWidget::doSaveState() { qCDebug(DIGIKAM_GENERAL_LOG) << getConfigGroup().name(); KConfigGroup group = getConfigGroup(); QString mainTask; if (d->detectButton->isChecked()) { mainTask = d->configValueDetect; } #ifdef ENABLE_DETECT_AND_RECOGNIZE else if (d->detectAndRecognizeButton->isChecked()) { mainTask = d->configValueDetectAndRecognize; } #endif else // d->reRecognizeButton { mainTask = d->configValueRecognizedMarkedFaces; } group.writeEntry(entryName(d->configMainTask), mainTask); QString handling; switch ((FaceScanSettings::AlreadyScannedHandling)(d->alreadyScannedBox->itemData(d->alreadyScannedBox->currentIndex()).toInt())) { case FaceScanSettings::Skip: handling = QLatin1String("Skip"); break; case FaceScanSettings::Rescan: handling = QLatin1String("Rescan"); break; case FaceScanSettings::Merge: handling = QLatin1String("Merge"); break; } group.writeEntry(entryName(d->configAlreadyScannedHandling), handling); ApplicationSettings::instance()->setFaceDetectionAccuracy(double(d->accuracyInput->value()) / 100); d->albumSelectors->saveState(); group.writeEntry(entryName(d->configUseFullCpu), d->useFullCpuButton->isChecked()); } void FaceScanWidget::setupUi() { // ---- Main option box -------- d->optionGroupBox = new QGroupBox(i18n("Workflow")); QVBoxLayout* const optionLayout = new QVBoxLayout; d->alreadyScannedBox = new SqueezedComboBox; d->alreadyScannedBox->addSqueezedItem(i18nc("@label:listbox", "Skip images already scanned"), FaceScanSettings::Skip); d->alreadyScannedBox->addSqueezedItem(i18nc("@label:listbox", "Scan again and merge results"), FaceScanSettings::Merge); d->alreadyScannedBox->addSqueezedItem(i18nc("@label:listbox", "Clear unconfirmed results and rescan"), FaceScanSettings::Rescan); d->alreadyScannedBox->setCurrentIndex(FaceScanSettings::Skip); d->detectButton = new QRadioButton(i18nc("@option:radio", "Detect faces")); d->detectButton->setToolTip(i18nc("@info", "Find all faces in your photos")); #ifdef ENABLE_DETECT_AND_RECOGNIZE d->detectAndRecognizeButton = new QRadioButton(i18nc("@option:radio", "Detect and recognize faces")); d->detectAndRecognizeButton->setToolTip(i18nc("@info", "Find all faces in your photos and try to recognize which person is depicted")); #endif d->reRecognizeButton = new QRadioButton(i18nc("@option:radio", "Recognize faces")); d->reRecognizeButton->setToolTip(i18nc("@info", "Try again to recognize the people depicted on marked but yet unconfirmed faces.")); optionLayout->addWidget(d->alreadyScannedBox); optionLayout->addWidget(d->detectButton); #ifdef ENABLE_DETECT_AND_RECOGNIZE optionLayout->addWidget(d->detectAndRecognizeButton); #endif optionLayout->addWidget(d->reRecognizeButton); #ifdef ENABLE_DETECT_AND_RECOGNIZE QStyleOptionButton buttonOption; buttonOption.initFrom(d->detectAndRecognizeButton); int indent = style()->subElementRect(QStyle::SE_RadioButtonIndicator, &buttonOption, d->detectAndRecognizeButton).width(); optionLayout->setColumnMinimumWidth(0, indent); #endif d->optionGroupBox->setLayout(optionLayout); // ---- Album tab --------- d->tabWidget = new QTabWidget; QWidget* const selWidget = new QWidget(d->tabWidget); QVBoxLayout* const selLayout = new QVBoxLayout; d->albumSelectors = new AlbumSelectors(i18nc("@label", "Search in:"), d->configName, selWidget); selLayout->addWidget(d->albumSelectors); selWidget->setLayout(selLayout); d->tabWidget->addTab(selWidget, i18nc("@title:tab", "Albums")); // ---- Settings tab ------ QWidget* const settingsTab = new QWidget(d->tabWidget); QVBoxLayout* const settingsLayout = new QVBoxLayout(settingsTab); QGroupBox* const accuracyBox = new QGroupBox(i18nc("@groupbox", "Face Accuracy"), settingsTab); QGridLayout* const accuracyGrid = new QGridLayout(accuracyBox); QLabel* const sensitivityLabel = new QLabel(i18nc("@label left extremities of a scale", "Sensitivity"), settingsTab); sensitivityLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); QLabel* const specificityLabel = new QLabel(i18nc("@label right extremities of a scale", "Specificity"), settingsTab); specificityLabel->setAlignment(Qt::AlignTop | Qt::AlignRight); d->accuracyInput = new DIntNumInput(settingsTab); d->accuracyInput->setDefaultValue(70); d->accuracyInput->setRange(0, 100, 10); d->accuracyInput->setToolTip(i18nc("@info:tooltip", "Adjust sensitivity versus specificity: the higher the value, the more accurately faces will " "be recognized, but less faces will be recognized " "(only faces that are very similar to pre-tagged faces are recognized).")); accuracyGrid->addWidget(d->accuracyInput, 0, 0, 1, 3); accuracyGrid->addWidget(sensitivityLabel, 1, 0, 1, 1); accuracyGrid->addWidget(specificityLabel, 1, 2, 1, 1); accuracyGrid->setColumnStretch(1, 10); d->useFullCpuButton = new QCheckBox(settingsTab); d->useFullCpuButton->setText(i18nc("@option:check", "Work on all processor cores")); d->useFullCpuButton->setToolTip(i18nc("@info:tooltip", "Face detection and recognition are time-consuming tasks. " "You can choose if you wish to employ all processor cores " "on your system, or work in the background only on one core.")); d->retrainAllButton = new QCheckBox(settingsTab); d->retrainAllButton->setText(i18nc("@option:check", "Clear and rebuild all training data")); d->retrainAllButton->setToolTip(i18nc("@info:tooltip", "This will clear all training data for recognition " "and rebuild it from all available faces.")); settingsLayout->addWidget(accuracyBox); settingsLayout->addWidget(d->useFullCpuButton); settingsLayout->addWidget(d->retrainAllButton); settingsLayout->addStretch(10); d->tabWidget->addTab(settingsTab, i18nc("@title:tab", "Settings")); // ------------------------ QVBoxLayout* const vbx = new QVBoxLayout(this); vbx->addWidget(d->optionGroupBox); vbx->addWidget(d->tabWidget); vbx->setContentsMargins(QMargins()); setLayout(vbx); } void FaceScanWidget::setupConnections() { /* connect(d->detectButton, SIGNAL(toggled(bool)), d->alreadyScannedBox, SLOT(setEnabled(bool))); */ #ifdef ENABLE_DETECT_AND_RECOGNIZE connect(d->detectAndRecognizeButton, SIGNAL(toggled(bool)), d->alreadyScannedBox, SLOT(setEnabled(bool))); #endif connect(d->detectButton, SIGNAL(toggled(bool)), this, SLOT(slotPrepareForDetect(bool))); connect(d->reRecognizeButton, SIGNAL(toggled(bool)), this, SLOT(slotPrepareForRecognize(bool))); connect(d->retrainAllButton, SIGNAL(toggled(bool)), this, SLOT(retrainAllButtonToggled(bool))); } void FaceScanWidget::slotPrepareForDetect(bool status) { d->alreadyScannedBox->setEnabled(status); // Set default for Tag tab as all unchecked d->albumSelectors->resetTAlbumSelection(); } void FaceScanWidget::slotPrepareForRecognize(bool /*status*/) { /** * TODO: * reRecognizeButton always disables d->alreadyScannedBox, while it should be * Find out why and fix it. */ d->alreadyScannedBox->setEnabled(false); // First we set all tags unchecked d->albumSelectors->resetTAlbumSelection(); // Set default for Tag tab so that People and its children are checked AlbumList tagAlbums = AlbumManager::instance()->allTAlbums(); QString people = i18nc("People on your photos", "People"); for (int i = 0 ; i < tagAlbums.size() ; ++i) { Album* const album = tagAlbums[i]; if (album->title() == people || ((album->parent() != nullptr) && (album->parent()->title() == people))) { d->albumSelectors->setTagSelected(album, false); } } } void FaceScanWidget::retrainAllButtonToggled(bool on) { d->optionGroupBox->setEnabled(!on); d->albumSelectors->setEnabled(!on); } bool FaceScanWidget::settingsConflicted() const { return d->settingsConflicted; } FaceScanSettings FaceScanWidget::settings() const { FaceScanSettings settings; d->settingsConflicted = false; if (d->retrainAllButton->isChecked()) { settings.task = FaceScanSettings::RetrainAll; } else if (d->detectButton->isChecked()) { settings.task = FaceScanSettings::Detect; } else { #ifdef ENABLE_DETECT_AND_RECOGNIZE if (d->detectAndRecognizeButton->isChecked()) { settings.task = FaceScanSettings::DetectAndRecognize; } else // recognize only #endif { settings.task = FaceScanSettings::RecognizeMarkedFaces; // preset settingsConflicted as True, since by default there are no tags to recognize d->settingsConflicted = true; } } settings.alreadyScannedHandling = (FaceScanSettings::AlreadyScannedHandling) d->alreadyScannedBox->itemData(d->alreadyScannedBox->currentIndex()).toInt(); settings.accuracy = double(d->accuracyInput->value()) / 100; // TODO: why does the original code append but not assign here??? // settings.albums << d->albumSelectors->selectedAlbumsAndTags(); settings.albums = d->albumSelectors->selectedAlbumsAndTags(); if (d->settingsConflicted) { int numberOfIdentities = FaceDbAccess().db()->getNumberOfIdentities(); d->settingsConflicted = (numberOfIdentities == 0); } settings.useFullCpu = d->useFullCpuButton->isChecked(); return settings; } +void FaceScanWidget::resetRetrainAllButton() +{ + d->retrainAllButton->setChecked(false); +} + } // namespace Digikam diff --git a/core/utilities/facemanagement/widgets/facescanwidget.h b/core/utilities/facemanagement/widgets/facescanwidget.h index 0b623afe01..6687066b96 100644 --- a/core/utilities/facemanagement/widgets/facescanwidget.h +++ b/core/utilities/facemanagement/widgets/facescanwidget.h @@ -1,80 +1,82 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-10-09 * Description : Widget to choose options for face scanning * * Copyright (C) 2010-2012 by Marcel Wiesweg * Copyright (C) 2012-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_FACE_SCAN_WIDGET_H #define DIGIKAM_FACE_SCAN_WIDGET_H // Qt includes #include // Local includes #include "statesavingobject.h" #include "facescansettings.h" namespace Digikam { class FaceScanWidget : public QWidget, public StateSavingObject { Q_OBJECT public: explicit FaceScanWidget(QWidget* const parent = nullptr); ~FaceScanWidget(); bool settingsConflicted() const; FaceScanSettings settings() const; + void resetRetrainAllButton(); + protected: void doLoadState(); void doSaveState(); protected Q_SLOTS: void retrainAllButtonToggled(bool on); private: void setupUi(); void setupConnections(); private Q_SLOTS: void slotPrepareForDetect(bool status); void slotPrepareForRecognize(bool status); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_FACE_SCAN_WIDGET_H